From e33cf9e0ae2b1bb1f20aa2893272a3283ca98913 Mon Sep 17 00:00:00 2001 From: wolandscat Date: Thu, 1 Feb 2024 13:32:04 -0700 Subject: [PATCH] Basic reports. --- .../src/gui/export/report_dialog.e | 12 +- .../src/interface/Archetype_library_report.e | 18 ++- .../src/interface/Id_code_report.e | 122 ++++++++++++++++ .../interface/Loinc_archetype_map_report.e | 133 +++++++++++++----- .../src/interface/arch_id_to_tpl_id_report.e | 69 +++++++++ .../src/interface/archetype_reporter.e | 45 ++++-- .../src/am/archetype/archetype_definitions.e | 43 ++++-- 7 files changed, 376 insertions(+), 66 deletions(-) create mode 100644 components/adl_compiler/src/interface/Id_code_report.e create mode 100644 components/adl_compiler/src/interface/arch_id_to_tpl_id_report.e diff --git a/apps/adl_workbench/src/gui/export/report_dialog.e b/apps/adl_workbench/src/gui/export/report_dialog.e index 0f69558e6..56f558813 100644 --- a/apps/adl_workbench/src/gui/export/report_dialog.e +++ b/apps/adl_workbench/src/gui/export/report_dialog.e @@ -31,10 +31,6 @@ feature -- Definitions Min_width_in_chars: INTEGER = 160 - Flat_path_segment_name: STRING = "flat" - - Differential_path_segment_name: STRING = "differential" - feature {NONE} -- Initialization make @@ -44,6 +40,8 @@ feature {NONE} -- Initialization create_interface_objects -- Initialize `Current'. + local + title_label: STRING do create gui_controls.make (0) @@ -61,7 +59,11 @@ feature {NONE} -- Initialization ev_root_container.disable_item_expand (ev_cell_1) create ev_label_1 - ev_label_1.set_text (get_text ({ADL_MESSAGES_IDS}.ec_report_description)) + create title_label.make_from_string (get_text ({ADL_MESSAGES_IDS}.ec_report_description) + "%N") + across builder.reports as rpts_csr loop + title_label.append (rpts_csr.item.title + "%N") + end + ev_label_1.set_text (title_label) ev_root_container.extend (ev_label_1) ev_root_container.disable_item_expand (ev_label_1) diff --git a/components/adl_compiler/src/interface/Archetype_library_report.e b/components/adl_compiler/src/interface/Archetype_library_report.e index a6416fc99..d7d00a8c1 100644 --- a/components/adl_compiler/src/interface/Archetype_library_report.e +++ b/components/adl_compiler/src/interface/Archetype_library_report.e @@ -24,6 +24,10 @@ inherit feature -- Definitions + Text_field_delimiter: STRING = "/" + +feature -- Access + id: STRING deferred end @@ -32,7 +36,7 @@ feature -- Definitions deferred end - output_table: HASH_TABLE[TUPLE, STRING] + output_table: ARRAYED_LIST[TUPLE] once create Result.make(0) end @@ -45,8 +49,10 @@ feature {NONE} -- Initialisation feature {ARCHETYPE_REPORTER} -- Processing - initialise + initialise (a_text_quote_agent: like text_quote_agent) do + output_table.wipe_out + text_quote_agent := a_text_quote_agent end finalise @@ -59,4 +65,12 @@ feature {ARCHETYPE_REPORTER} -- Processing deferred end +feature {NONE} + + text_quote_agent: FUNCTION [ANY, TUPLE[STRING], STRING] + -- function to use to quote output format + attribute + Result := default_text_quoting_agent + end + end diff --git a/components/adl_compiler/src/interface/Id_code_report.e b/components/adl_compiler/src/interface/Id_code_report.e new file mode 100644 index 000000000..c689ed4a2 --- /dev/null +++ b/components/adl_compiler/src/interface/Id_code_report.e @@ -0,0 +1,122 @@ +note + component: "openEHR ADL Tools" + description: "Id-code extractor" + keywords: "export, archetype, ADL" + author: "Thomas Beale " + support: "openEHR AWB project " + copyright: "Copyright (c) 2024- openEHR International" + license: "Apache 2.0 License " + +class ID_CODE_REPORT + +inherit + ARCHETYPE_LIBRARY_REPORT + export + {NONE} all + redefine + output_table + end + + SHARED_ARCHETYPE_LIBRARIES + export + {NONE} all + end + + EXCEPTIONS + export + {NONE} all + end + +create + make + +feature -- Access + + id: STRING + do + Result := "Id_code_report" + end + + title: STRING + do + Result := "Id-codes in library" + end + + output_table: ARRAYED_LIST[TUPLE [arch_id, id_code, path, description, loinc_code: STRING]] + once + create Result.make(0) + end + +feature {ARCHETYPE_REPORTER} -- Processing + + process_archetype (auth_ara: ARCH_LIB_AUTHORED_ARCHETYPE) + -- Generate serialised output under `output_dir' from `ara', optionally building it first if necessary. + local + id_code, loinc_code: STRING + terminology: ARCHETYPE_TERMINOLOGY + path: OG_PATH + binding_key, item_text: STRING + finished: BOOLEAN + locatable_descriptions: HASH_TABLE [STRING, STRING] + concept_desc: IMMUTABLE_STRING +-- save_flag: BOOLEAN + do + if not attached {ARCH_LIB_TEMPLATE} auth_ara then + terminology := auth_ara.differential_archetype.terminology + create concept_desc.make_from_string (terminology.term_definition (Default_language, terminology.concept_code).text) + + -- find the language key + check terminology.languages_available.has (Default_language) end + + locatable_descriptions := auth_ara.locatable_descriptions (True, False, Default_language, Text_field_delimiter) + + -- create a report row for each path, of the form: archetype_id, path, meaning + across auth_ara.locatable_paths (True, False) as path_csr loop + -- first work out if the path terminal node has a terminology defined code; if not, ignore it + create path.make_from_string (path_csr.item) + if path.is_root then + id_code := terminology.concept_code + else + id_code := path.last.object_id + end + + -- if the terminal id-code in the path is defined in the terminology, + -- we keep it, else we ignore this path + if terminology.has_code (id_code) then + create loinc_code.make_empty + create binding_key.make_empty + if terminology.has_term_binding (loinc_terminology_id, id_code) then + binding_key := id_code + elseif terminology.has_term_binding (loinc_terminology_id, path_csr.item) then + binding_key := path_csr.item + end + + if not binding_key.is_empty then + loinc_code.append (terminology.term_binding (loinc_terminology_id, binding_key).as_string) + end + + -- build the text: start with archetype concept + create item_text.make_from_string (concept_desc) + if not path.is_root then + if attached locatable_descriptions.item (path_csr.item) as desc then + item_text.append (Text_field_delimiter + desc) + else + item_text.append (Text_field_delimiter + "(ITEM NOT FOUND}") + end + end + + output_table.extend ([auth_ara.id.as_string, id_code, path_csr.item, text_quote_agent.item([item_text]), loinc_code]) +--if not loinc_code.is_empty then +-- terminology.remove_term_binding (binding_key, loinc_terminology_id) +-- terminology.put_term_binding (create {URI}.make_from_string (loinc_code), loinc_terminology_id, id_code) +-- save_flag := True +--end + end + end +--if save_flag then +-- auth_ara.save_differential_text +--end + end + end + +end diff --git a/components/adl_compiler/src/interface/Loinc_archetype_map_report.e b/components/adl_compiler/src/interface/Loinc_archetype_map_report.e index 10e0a0b68..5e05659b4 100644 --- a/components/adl_compiler/src/interface/Loinc_archetype_map_report.e +++ b/components/adl_compiler/src/interface/Loinc_archetype_map_report.e @@ -14,7 +14,7 @@ inherit export {NONE} all redefine - output_table, initialise + output_table end SHARED_ARCHETYPE_LIBRARIES @@ -30,7 +30,7 @@ inherit create make -feature -- Definitions +feature -- Access id: STRING do @@ -42,65 +42,124 @@ feature -- Definitions Result := "LOINC to Archetype id map" end - output_table: HASH_TABLE[TUPLE [arch_id, path, description: STRING], STRING] + output_table: ARRAYED_LIST [TUPLE [loinc_code, arch_id, id_code, path, description: STRING]] once create Result.make(0) end feature {ARCHETYPE_REPORTER} -- Processing - initialise + process_archetype (auth_ara: ARCH_LIB_AUTHORED_ARCHETYPE) + -- Generate serialised output under `output_dir' from `ara', optionally building it first if necessary. + local + id_code, loinc_code: STRING + terminology: ARCHETYPE_TERMINOLOGY + path: OG_PATH + binding_key, item_text: STRING + finished: BOOLEAN + locatable_descriptions: HASH_TABLE [STRING, STRING] + concept_desc: IMMUTABLE_STRING do - output_table.wipe_out + if not attached {ARCH_LIB_TEMPLATE} auth_ara then + terminology := auth_ara.differential_archetype.terminology + create concept_desc.make_from_string (terminology.term_definition (Default_language, terminology.concept_code).text) + + -- find the language key + check terminology.languages_available.has (Default_language) end + + locatable_descriptions := auth_ara.locatable_descriptions (True, False, Default_language, Text_field_delimiter) + + -- create a report row for each path, of the form: archetype_id, path, meaning + across auth_ara.locatable_paths (True, False) as path_csr loop + -- first work out if the path terminal node has a terminology defined code; if not, ignore it + create path.make_from_string (path_csr.item) + if path.is_root then + id_code := terminology.concept_code + else + id_code := path.last.object_id + end + + -- if the terminal id-code in the path is defined in the terminology, + -- or else the whole path is bound + create binding_key.make_empty + create loinc_code.make_empty + if terminology.has_term_binding (loinc_terminology_id, id_code) then + binding_key := id_code + elseif terminology.has_term_binding (loinc_terminology_id, path_csr.item) then + binding_key := path_csr.item + end + + if not binding_key.is_empty then + loinc_code.append (terminology.term_binding (loinc_terminology_id, binding_key).as_string) + + -- build the text: start with archetype concept + create item_text.make_from_string (concept_desc) + if not path.is_root then + if attached locatable_descriptions.item (path_csr.item) as desc then + item_text.append (Text_field_delimiter + desc) + else + item_text.append (Text_field_delimiter + "(ITEM NOT FOUND}") + end + end + + output_table.extend ([loinc_code, auth_ara.id.as_string, id_code, path_csr.item, text_quote_agent.item([item_text])]) + end + end + end end - process_archetype (auth_ara: ARCH_LIB_AUTHORED_ARCHETYPE) + process_archetype2 (auth_ara: ARCH_LIB_AUTHORED_ARCHETYPE) -- Generate serialised output under `output_dir' from `ara', optionally building it first if necessary. local - loinc_code: STRING flat_terminology: ARCHETYPE_TERMINOLOGY binding_key, binding_text: STRING binding_path: OG_PATH + locatable_descriptions: HASH_TABLE [STRING, STRING] do flat_terminology := auth_ara.flat_archetype.terminology -- find the language key check flat_terminology.languages_available.has (Default_language) end - -- Get the LOINC code for the archetype as a whole (= root node = concept code) --- loinc_code := flat_terminology.term_binding_for_concept_code (Loinc_terminology_id) --- if not loinc_code.is_empty then --- output_table.put ([auth_ara.id.as_string, flat_terminology.concept_code, flat_terminology.term_definition (Default_language, flat_terminology.concept_code).text], loinc_code) --- end + locatable_descriptions := auth_ara.locatable_descriptions (True, False, Default_language, Text_field_delimiter) -- process any LOINC codes inside terminology across flat_terminology.term_bindings_for_terminology (Loinc_terminology_id) as bindings_csr loop - -- here we figure out which id-code the binding key corresponds to. There are 3 possibilities: - -- * an id-code (DEPRECATED) - -- * the root path '/', which means the code is the concept code from the terminology - -- * a path containing one or more id-codes - binding_key := bindings_csr.key - if is_id_code (binding_key) then - binding_text := flat_terminology.term_definition (Default_language, bindings_csr.key).text - else - -- assume path - create binding_path.make_from_string (binding_key) - if binding_path.is_root then - binding_text := flat_terminology.term_definition (Default_language, flat_terminology.concept_code).text - else - create binding_text.make_empty - across binding_path as path_csr loop - if is_id_code (path_csr.item.object_id) then - binding_text.append (flat_terminology.term_definition (Default_language, path_csr.item.object_id).text) - end - if not path_csr.is_last then - binding_text.append ("/") - end - end - end - end - output_table.put ([auth_ara.id.as_string, bindings_csr.key, binding_text], bindings_csr.item.as_string) + -- we ignore at-codes +-- if not is_value_code (binding_key) then + +-- -- here we figure out which id-code the binding key corresponds to. There are 3 possibilities: +-- -- * an id-code +-- -- * the root path '/', which means the code is the concept code from the terminology +-- -- * a path containing one or more id-codes +-- binding_key := bindings_csr.key +-- if is_id_code (binding_key) then +-- binding_text := flat_terminology.term_definition (Default_language, bindings_csr.key).text + +-- -- assume path +-- else +-- create binding_path.make_from_string (binding_key) +-- if binding_path.is_root then +-- binding_text := flat_terminology.term_definition (Default_language, flat_terminology.concept_code).text +-- else +-- create binding_text.make_empty +-- across binding_path as path_csr loop +-- if is_id_code (path_csr.item.object_id) then +-- if flat_terminology.has_code (path_csr.item.object_id) then +-- binding_text.append (flat_terminology.term_definition (Default_language, path_csr.item.object_id).text) +-- else +-- binding_text.append (path_csr.item.attr_name) +-- end +-- end +-- if not path_csr.is_last then +-- binding_text.append ("/") +-- end +-- end +-- end +-- end +-- output_table.extend ([bindings_csr.item.as_string, auth_ara.id.as_string, bindings_csr.key, text_quote_agent.item([binding_text])]) +-- end end end diff --git a/components/adl_compiler/src/interface/arch_id_to_tpl_id_report.e b/components/adl_compiler/src/interface/arch_id_to_tpl_id_report.e new file mode 100644 index 000000000..dccc3d0f7 --- /dev/null +++ b/components/adl_compiler/src/interface/arch_id_to_tpl_id_report.e @@ -0,0 +1,69 @@ +note + component: "openEHR ADL Tools" + description: "Id-code extractor" + keywords: "export, archetype, ADL" + author: "Thomas Beale " + support: "openEHR AWB project " + copyright: "Copyright (c) 2024- openEHR International" + license: "Apache 2.0 License " + +class ARCH_ID_TO_TPL_ID_REPORT + +inherit + ARCHETYPE_LIBRARY_REPORT + export + {NONE} all + redefine + output_table + end + + SHARED_ARCHETYPE_LIBRARIES + export + {NONE} all + end + + EXCEPTIONS + export + {NONE} all + end + +create + make + +feature -- Access + + id: STRING + do + Result := "Arch_id_to_tpl_id_report" + end + + title: STRING + do + Result := "Archetype ID to Template ID map" + end + + output_table: ARRAYED_LIST[TUPLE [arch_id, tpl_id: STRING]] + once + create Result.make(0) + end + +feature {ARCHETYPE_REPORTER} -- Processing + + process_archetype (auth_ara: ARCH_LIB_AUTHORED_ARCHETYPE) + -- Generate serialised output under `output_dir' from `ara', optionally building it first if necessary. + local + arch_id: STRING + do + if attached {ARCH_LIB_TEMPLATE} auth_ara then + across auth_ara.suppliers_index as suppliers_csr loop + if attached {ARCH_LIB_TEMPLATE_OVERLAY} suppliers_csr.item as tpl_ovl then + arch_id := tpl_ovl.parent_id.as_string + else + arch_id := suppliers_csr.key + end + output_table.extend ([arch_id, auth_ara.id.as_string]) + end + end + end + +end diff --git a/components/adl_compiler/src/interface/archetype_reporter.e b/components/adl_compiler/src/interface/archetype_reporter.e index e35419710..300bc85f3 100644 --- a/components/adl_compiler/src/interface/archetype_reporter.e +++ b/components/adl_compiler/src/interface/archetype_reporter.e @@ -55,6 +55,25 @@ feature {NONE} -- Initialisation do end +feature -- Access + + reports: HASH_TABLE [ARCHETYPE_LIBRARY_REPORT, STRING] + local + report1: LOINC_ARCHETYPE_MAP_REPORT + report2: ID_CODE_REPORT + report3: ARCH_ID_TO_TPL_ID_REPORT + once + create Result.make(0) + create report1.make + Result.put (report1, report1.title) + + create report2.make + Result.put (report2, report2.title) + + create report3.make + Result.put (report3, report3.title) + end + feature -- Commands build_artefact (ara: ARCH_LIB_ARCHETYPE) @@ -69,11 +88,14 @@ feature {NONE} -- Commands do output_dir := args.export_dir syntax := args.syntax + if attached text_quoting_agents.item (syntax) as agt then + text_quote_agent := agt + end artefact_count := current_library.archetype_count across reports as rpts_csr loop - rpts_csr.item.initialise + rpts_csr.item.initialise (text_quote_agent) end end @@ -84,7 +106,7 @@ feature {NONE} -- Commands do across reports as rpts_csr loop check attached reporting_file_extensions.item(syntax) as fmt then - output_filename := file_system.pathname (output_dir, rpts_csr.key) + fmt + output_filename := file_system.pathname (output_dir, rpts_csr.item.id) + fmt end if attached file_system.new_output_file (output_filename) as fd then @@ -97,11 +119,11 @@ feature {NONE} -- Commands check attached {STRING} cols_csr.item as s then col_vals.append (s) if not cols_csr.is_last then - col_vals.append (Csv_default_delimiter) + col_vals.append_character (Csv_default_delimiter) end end end - fd.put_string (row_csr.key + Csv_default_delimiter + col_vals + "%N") + fd.put_string (col_vals + "%N") end fd.close end @@ -139,15 +161,6 @@ feature {NONE} -- Commands feature {NONE} -- Build State - reports: HASH_TABLE [ARCHETYPE_LIBRARY_REPORT, STRING] - local - report1: LOINC_ARCHETYPE_MAP_REPORT - once - create Result.make(0) - create report1.make - Result.put (report1, report1.title) - end - output_dir: STRING attribute create Result.make_empty @@ -199,4 +212,10 @@ feature {NONE} -- Implementation create Result end + text_quote_agent: FUNCTION [ANY, TUPLE[STRING], STRING] + -- function to use to quote output format + attribute + Result := default_text_quoting_agent + end + end diff --git a/libraries/openehr/src/am/archetype/archetype_definitions.e b/libraries/openehr/src/am/archetype/archetype_definitions.e index cb2e8321a..773942ea8 100755 --- a/libraries/openehr/src/am/archetype/archetype_definitions.e +++ b/libraries/openehr/src/am/archetype/archetype_definitions.e @@ -61,14 +61,6 @@ feature -- File and artefact types -- also we don't want users to get confused about what kind of files -- these are - Syntax_type_csv: STRING = "csv" - -- Name of CSV syntax type. - - File_ext_csv: STRING = ".csv" - -- Name of CSV syntax type. - - Csv_default_delimiter: STRING = "," - Aom_profile_file_match_regex: STRING once ("PROCESS") Result := ".*\" + Aom_profile_file_extension + "$" @@ -88,6 +80,16 @@ feature -- File and artefact types create Result.make_from_string ("templates") end + Syntax_type_csv: STRING = "csv" + -- Name of CSV syntax type. + + File_ext_csv: STRING = ".csv" + -- Name of CSV syntax type. + + Csv_default_delimiter: CHARACTER = ',' + + Csv_default_quote: STRING = "%"" + feature -- Export Types export_formats: ARRAYED_SET [STRING] @@ -104,7 +106,7 @@ feature -- Export Types once create Result.make (0) Result.compare_objects - Result.extend ({ODIN_DEFINITIONS}.syntax_type_json) + -- Result.extend ({ODIN_DEFINITIONS}.syntax_type_json) Result.extend (Syntax_type_csv) end @@ -118,6 +120,29 @@ feature -- Export Types not_empty: not Result.is_empty end + text_quoting_agents: HASH_TABLE [FUNCTION [ANY, TUPLE[STRING], STRING], STRING] + once + create Result.make(0) + Result.put (agent json_quote_text, {ODIN_DEFINITIONS}.syntax_type_json) + Result.put (agent csv_quote_text, Syntax_type_csv) + end + + json_quote_text (s: STRING): STRING + -- TODO + do + Result := s + end + + csv_quote_text (s: STRING): STRING + do + Result := Csv_default_quote + s + Csv_default_quote + end + + default_text_quoting_agent: FUNCTION [ANY, TUPLE[STRING], STRING] + once + Result := agent csv_quote_text + end + feature -- Archetype identifiers archetype_id_checker: ARCHETYPE_HRID_PARSER