pyegeria 5.4.3.3__py3-none-any.whl → 5.4.3.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. commands/cat/debug_log.2025-09-02_07-44-39_567276.log.zip +0 -0
  2. commands/cat/debug_log.2025-09-03_07-45-21_986388.log.zip +0 -0
  3. commands/cat/debug_log.log +5314 -4140
  4. commands/cat/list_format_set.py +2 -2
  5. commands/tech/list_information_supply_chains.py +1 -1
  6. commands/tech/list_solution_blueprints.py +1 -1
  7. commands/tech/list_solution_components.py +1 -1
  8. commands/tech/list_solution_roles.py +1 -1
  9. md_processing/__init__.py +0 -4
  10. md_processing/data/commands.json +4 -4
  11. md_processing/dr_egeria.py +6 -9
  12. md_processing/dr_egeria_inbox/data_spec_test.md +38 -364
  13. md_processing/dr_egeria_inbox/gov_def.md +3 -3
  14. md_processing/dr_egeria_inbox/product.md +10 -2
  15. md_processing/md_commands/data_designer_commands.py +90 -425
  16. md_processing/md_processing_utils/common_md_utils.py +50 -1
  17. md_processing/md_processing_utils/extraction_utils.py +14 -7
  18. md_processing/md_processing_utils/md_processing_constants.py +1 -1
  19. pyegeria/___external_references.py +2943 -2955
  20. pyegeria/__init__.py +1 -1
  21. pyegeria/_client_new.py +9 -7
  22. pyegeria/_output_formats.py +124 -3
  23. pyegeria/collection_manager.py +17 -56
  24. pyegeria/config.py +10 -1
  25. pyegeria/data_designer.py +166 -117
  26. pyegeria/egeria_client.py +1 -1
  27. pyegeria/egeria_tech_client.py +1 -1
  28. pyegeria/glossary_manager.py +71 -85
  29. pyegeria/governance_officer.py +26 -29
  30. pyegeria/output_formatter.py +127 -1
  31. pyegeria/project_manager.py +33 -36
  32. pyegeria/{solution_architect_omvs.py → solution_architect.py} +443 -388
  33. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.3.4.dist-info}/METADATA +1 -1
  34. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.3.4.dist-info}/RECORD +37 -39
  35. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 13:07-gov_def.md +0 -492
  36. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 13:25-gov_def.md +0 -520
  37. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 16:43-gov_def.md +0 -636
  38. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 16:46-gov_def.md +0 -636
  39. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.3.4.dist-info}/LICENSE +0 -0
  40. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.3.4.dist-info}/WHEEL +0 -0
  41. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.3.4.dist-info}/entry_points.txt +0 -0
pyegeria/data_designer.py CHANGED
@@ -18,7 +18,8 @@ from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequ
18
18
  TemplateRequestBody,
19
19
  UpdateElementRequestBody, NewRelationshipRequestBody,
20
20
  DeleteRequestBody)
21
- from pyegeria.output_formatter import (extract_mermaid_only, extract_basic_dict)
21
+ from pyegeria.output_formatter import (extract_mermaid_only, extract_basic_dict, populate_columns_from_properties,
22
+ get_required_relationships, populate_common_columns)
22
23
  from pyegeria.output_formatter import (generate_output,
23
24
  _extract_referenceable_properties)
24
25
  from pyegeria.utils import body_slimmer, dynamic_catch
@@ -1296,11 +1297,10 @@ class DataDesigner(Client2):
1296
1297
 
1297
1298
  member_of_collections = el_struct.get("memberOfCollections", {})
1298
1299
  for collection in member_of_collections:
1299
- c_type = collection["relatedElement"]["properties"].get("collectionType", "") or ""
1300
+ type_name = collection["relatedElement"]["elementHeader"]["type"].get("typeName", "") or ""
1300
1301
  guid = collection["relatedElement"]["elementHeader"]["guid"]
1301
- name = collection["relatedElement"]["properties"].get("name", "") or ""
1302
+ name = collection["relatedElement"]["properties"].get("displayName", "") or ""
1302
1303
  qualifiedName = collection['relatedElement']["properties"].get("qualifiedName", "") or ""
1303
- type_name = collection["relatedElement"]["elementHeader"].get("classificationProperties",{}).get('anchorTypeName', None)
1304
1304
  if type_name:
1305
1305
  if type_name == "DataDictionary":
1306
1306
  member_of_data_dicts_guids.append(guid)
@@ -2516,7 +2516,7 @@ class DataDesigner(Client2):
2516
2516
 
2517
2517
  loop = asyncio.get_event_loop()
2518
2518
  response = loop.run_until_complete(
2519
- self._async_get_data_fields_by_name(filter, classification_names, body, start_from, page_size,
2519
+ self._async_get_data_fields_by_name(filter_string, classification_names, body, start_from, page_size,
2520
2520
  output_format, output_format_set))
2521
2521
  return response
2522
2522
 
@@ -4681,118 +4681,161 @@ class DataDesigner(Client2):
4681
4681
 
4682
4682
 
4683
4683
 
4684
- def _extract_data_structure_properties(self, element: dict) -> dict:
4685
- """
4686
- Extract common properties from a data structure element.
4687
-
4688
- Args:
4689
- element (dict): The data structure element
4690
-
4691
- Returns:
4692
- dict: Dictionary of extracted properties
4693
- """
4694
- props = _extract_referenceable_properties(element)
4695
-
4696
- props['properties'] = element.get('properties', {})
4697
-
4698
- props['namespace'] = props['properties'].get("namespace", "") or ""
4699
-
4700
- classification_names = []
4701
- for c in props['classifications']:
4702
- classification_names.append(c.get("classificationName", None))
4703
- props['classifications'] = classification_names
4704
-
4705
- # Now lets get the related elements
4706
- associated_elements = self.get_data_rel_elements_dict(element)
4707
- props['data_specs'] = associated_elements.get("member_of_data_spec_qnames", [])
4708
-
4709
- # data_structures = associated_elements.get("member_of_data_struct_qnames", [])
4710
- props['assigned_meanings'] = associated_elements.get("assigned_meanings_qnames", [])
4711
- props['parent_names'] = associated_elements.get("parent_qnames", [])
4712
- props['member_data_fields'] = associated_elements.get("member_data_field_qnames", [])
4713
-
4714
- props['mermaid'] = element.get('mermaidGraph', "") or ""
4715
-
4716
- return props
4717
-
4718
- def _extract_data_class_properties(self, element: dict) -> dict:
4719
- """
4720
- Extract common properties from a data class element.
4721
-
4722
- Args:
4723
- element (dict): The data class element
4724
-
4725
- Returns:
4726
- dict: Dictionary of extracted properties
4727
- """
4728
- props = _extract_referenceable_properties(element)
4729
- properties = element.get('properties', {})
4730
- props['properties'] = properties
4731
-
4732
- classification_names = []
4733
- for c in props['classifications']:
4734
- classification_names.append(c.get("classificationName", None))
4735
- props['classifications'] = classification_names
4736
-
4737
- props['namespace'] = props['properties'].get("namespace", "") or ""
4738
-
4739
- props['data_type'] = properties.get('dataType', "") or ""
4740
- props['match_property_names'] = properties.get('matchPropertyNames', []) or []
4741
- props['match_threshold'] = properties.get('matchThreshold', 0)
4742
- props['allow_duplicate_values'] = properties.get('allowDuplicateValues', False)
4743
- props['is_case_sensitive'] = properties.get('isCaseSensitive', False)
4744
- props['is_nullable'] = properties.get('isNullable', False)
4745
-
4746
- # Now lets get the related elements
4747
- associated_elements = self.get_data_rel_elements_dict(element)
4748
- props['data_dictionaries'] = associated_elements.get("member_of_data_dicts_qnames", [])
4749
- props['assigned_meanings'] = associated_elements.get("assigned_meanings_qnames", [])
4750
- props['parent_names'] = associated_elements.get("parent_qnames", [])
4751
- props['nested_data_classes'] = associated_elements.get("nested_data_class_qnames", [])
4752
- props['specialized_data_classes'] = associated_elements.get("specialized_data_class_qnames", [])
4753
- props['mermaid'] = element.get('mermaidGraph', "") or ""
4754
-
4755
- return props
4756
-
4757
- def _extract_data_field_properties(self, element: dict) -> dict:
4758
- """
4759
- Extract common properties from a data field element.
4760
-
4761
- Args:
4762
- element (dict): The data field element
4763
-
4764
- Returns:
4765
- dict: Dictionary of extracted properties
4766
- """
4767
- props = _extract_referenceable_properties(element)
4768
-
4769
- props['properties'] = element.get('properties', {})
4770
- props['namespace'] = props['properties'].get("namespace", "") or ""
4771
- properties = element.get('properties', {})
4772
-
4773
- classification_names = []
4774
- for c in props['classifications']:
4775
- classification_names.append(c.get("classificationName", None))
4776
- props['classifications'] = classification_names
4777
-
4778
- props['is_nullable'] = properties.get('isNullable', False)
4779
- props['data_type'] = properties.get('dataType', "") or ""
4780
- props['minimum_length'] = properties.get('minimumLength', 0)
4781
- props['length'] = properties.get('length', 0)
4782
- props['precision'] = properties.get('precision', 0)
4783
- props['ordered_values'] = properties.get('orderedValues', False)
4784
- props['sort_order'] = properties.get('sortOrder', "") or ""
4785
-
4786
- # Now lets get the related elements
4787
- associated_elements = self.get_data_rel_elements_dict(element)
4788
- props['data_dictionaries'] = associated_elements.get("member_of_data_dicts_qnames", [])
4789
- props['data_structures'] = associated_elements.get("data_structure_qnames", [])
4790
- props['assigned_meanings'] = associated_elements.get("assigned_meanings_qnames", [])
4791
- props['parent_names'] = associated_elements.get("parent_qnames", [])
4792
- props['data_class'] = associated_elements.get("data_class_qnames", [])
4793
- props['mermaid'] = element.get('mermaidGraph', "") or ""
4794
-
4795
- return props
4684
+ def _extract_data_structure_properties(self, element: dict, columns_struct: dict) -> dict:
4685
+ """Extractor for Data Structure elements with related overlay.
4686
+
4687
+ Pattern:
4688
+ - Populate common columns via populate_common_columns.
4689
+ - Derive related properties using get_data_rel_elements_dict from the element body.
4690
+ - Overlay values into matching columns' 'value' fields, handling formats as list or dict.
4691
+ - Return the enriched columns_struct.
4692
+ """
4693
+ col_data = populate_common_columns(element, columns_struct)
4694
+
4695
+ try:
4696
+ related_map = self.get_data_rel_elements_dict(element)
4697
+ except Exception:
4698
+ related_map = {}
4699
+
4700
+ if isinstance(related_map, dict) and related_map:
4701
+ try:
4702
+ formats = col_data.get("formats") if isinstance(col_data, dict) else None
4703
+ if isinstance(formats, list):
4704
+ targets = formats
4705
+ elif isinstance(formats, dict):
4706
+ inner = formats.get("formats") if isinstance(formats.get("formats"), (dict, list)) else None
4707
+ if isinstance(inner, list):
4708
+ targets = inner
4709
+ elif isinstance(inner, dict):
4710
+ targets = [inner]
4711
+ else:
4712
+ targets = [formats]
4713
+ else:
4714
+ targets = []
4715
+
4716
+ if targets:
4717
+ for fmt in targets:
4718
+ cols = fmt.get("columns", []) if isinstance(fmt, dict) else []
4719
+ for col in cols:
4720
+ key = col.get("key") if isinstance(col, dict) else None
4721
+ if key and key in related_map:
4722
+ col["value"] = related_map.get(key)
4723
+ else:
4724
+ cols = col_data.get("columns", []) if isinstance(col_data, dict) else []
4725
+ for col in cols:
4726
+ key = col.get("key") if isinstance(col, dict) else None
4727
+ if key and key in related_map:
4728
+ col["value"] = related_map.get(key)
4729
+ except Exception:
4730
+ pass
4731
+
4732
+ return col_data
4733
+
4734
+
4735
+ def _extract_data_class_properties(self, element: dict,columns_struct: dict) -> dict:
4736
+ """Extractor for Data Class elements with related overlay, mirroring Data Field pattern."""
4737
+ col_data = populate_common_columns(element, columns_struct)
4738
+
4739
+ try:
4740
+ related_map = self.get_data_rel_elements_dict(element)
4741
+ except Exception:
4742
+ related_map = {}
4743
+
4744
+ if isinstance(related_map, dict) and related_map:
4745
+ try:
4746
+ formats = col_data.get("formats") if isinstance(col_data, dict) else None
4747
+ if isinstance(formats, list):
4748
+ targets = formats
4749
+ elif isinstance(formats, dict):
4750
+ inner = formats.get("formats") if isinstance(formats.get("formats"), (dict, list)) else None
4751
+ if isinstance(inner, list):
4752
+ targets = inner
4753
+ elif isinstance(inner, dict):
4754
+ targets = [inner]
4755
+ else:
4756
+ targets = [formats]
4757
+ else:
4758
+ targets = []
4759
+
4760
+ if targets:
4761
+ for fmt in targets:
4762
+ cols = fmt.get("columns", []) if isinstance(fmt, dict) else []
4763
+ for col in cols:
4764
+ key = col.get("key") if isinstance(col, dict) else None
4765
+ if key and key in related_map:
4766
+ col["value"] = related_map.get(key)
4767
+ else:
4768
+ cols = col_data.get("columns", []) if isinstance(col_data, dict) else []
4769
+ for col in cols:
4770
+ key = col.get("key") if isinstance(col, dict) else None
4771
+ if key and key in related_map:
4772
+ col["value"] = related_map.get(key)
4773
+ except Exception:
4774
+ pass
4775
+
4776
+ return col_data
4777
+
4778
+ def _extract_data_field_properties(self, element: dict, columns_struct: dict) -> dict:
4779
+ """Extractor for Data Field elements.
4780
+
4781
+ Steps:
4782
+ - Populate base/referenceable/common properties into columns_struct via populate_common_columns.
4783
+ - Derive related properties using get_data_rel_elements_dict from the element body.
4784
+ - For each column in columns_struct, if its 'key' matches a key from the related dict, set its 'value'.
4785
+ - Return the enriched columns_struct.
4786
+ """
4787
+ # 1) Populate common columns first (header, properties, basic relationships, mermaid)
4788
+ col_data = populate_common_columns(element, columns_struct)
4789
+
4790
+ # 2) Build a map of related properties/elements from the body. The Data Designer methods
4791
+ # return a body that may include keys like assignedMeanings, otherRelatedElements,
4792
+ # memberOfCollections, memberDataFields, assignedDataClasses, nestedDataClasses, etc.
4793
+ try:
4794
+ related_map = self.get_data_rel_elements_dict(element)
4795
+ except Exception:
4796
+ related_map = {}
4797
+
4798
+ if isinstance(related_map, dict) and related_map:
4799
+ # 3) Walk the configured columns and overlay values when the key matches an entry from related_map
4800
+ try:
4801
+ formats = col_data.get("formats") if isinstance(col_data, dict) else None
4802
+ if isinstance(formats, list):
4803
+ targets = formats
4804
+ elif isinstance(formats, dict):
4805
+ # Handle dict variant. It may be a single format dict or a wrapper containing 'formats'.
4806
+ # Examples seen:
4807
+ # { 'columns': [...] }
4808
+ # { 'types': 'ALL', 'columns': [...] }
4809
+ # { 'formats': { 'columns': [...] } }
4810
+ inner = formats.get("formats") if isinstance(formats.get("formats"), dict | list) else None
4811
+ if isinstance(inner, list):
4812
+ targets = inner
4813
+ elif isinstance(inner, dict):
4814
+ targets = [inner]
4815
+ else:
4816
+ targets = [formats]
4817
+ else:
4818
+ targets = []
4819
+
4820
+ if targets:
4821
+ for fmt in targets:
4822
+ cols = fmt.get("columns", []) if isinstance(fmt, dict) else []
4823
+ for col in cols:
4824
+ key = col.get("key") if isinstance(col, dict) else None
4825
+ if key and key in related_map:
4826
+ col["value"] = related_map.get(key)
4827
+ else:
4828
+ # If columns are on the top-level (non-standard), attempt to handle gracefully
4829
+ cols = col_data.get("columns", []) if isinstance(col_data, dict) else []
4830
+ for col in cols:
4831
+ key = col.get("key") if isinstance(col, dict) else None
4832
+ if key and key in related_map:
4833
+ col["value"] = related_map.get(key)
4834
+ except Exception:
4835
+ # Do not fail rendering due to overlay issues; keep the base columns
4836
+ pass
4837
+
4838
+ return col_data
4796
4839
 
4797
4840
  def _generate_basic_structured_output(self, elements: dict, filter: str, type: str = None ,output_format: str = 'DICT',
4798
4841
  columns_struct: dict = None) -> str | list:
@@ -4942,6 +4985,12 @@ class DataDesigner(Client2):
4942
4985
  output_formats,
4943
4986
  )
4944
4987
 
4988
+ def _extract_additional_data_struct_properties(self, element, columns_struct):
4989
+ return None
4990
+ def _extract_additional_data_field_properties(self, element, columns_struct):
4991
+ return None
4992
+ def _extract_additional_data_class_properties(self, element, columns_struct):
4993
+ return None
4945
4994
 
4946
4995
  if __name__ == "__main__":
4947
4996
  print("Data Designer")
pyegeria/egeria_client.py CHANGED
@@ -24,7 +24,7 @@ from pyegeria.full_omag_server_config import FullServerConfig
24
24
  from pyegeria.metadata_explorer_omvs import MetadataExplorer
25
25
  from pyegeria.my_profile_omvs import MyProfile
26
26
  from pyegeria.feedback_manager_omvs import FeedbackManager
27
- from pyegeria.solution_architect_omvs import SolutionArchitect
27
+ from pyegeria.solution_architect import SolutionArchitect
28
28
  from pyegeria.server_operations import ServerOps
29
29
  from pyegeria.registered_info import RegisteredInfo
30
30
  from pyegeria.valid_metadata_omvs import ValidMetadataManager
@@ -12,7 +12,7 @@ from pyegeria.egeria_cat_client import EgeriaCat
12
12
  from pyegeria.metadata_explorer_omvs import MetadataExplorer
13
13
  from pyegeria.registered_info import RegisteredInfo
14
14
  from pyegeria.runtime_manager_omvs import RuntimeManager
15
- from pyegeria.solution_architect_omvs import SolutionArchitect
15
+ from pyegeria.solution_architect import SolutionArchitect
16
16
  from pyegeria.template_manager_omvs import TemplateManager
17
17
  from pyegeria.valid_metadata_omvs import ValidMetadataManager
18
18
  from pyegeria.governance_officer import GovernanceOfficer
@@ -30,7 +30,7 @@ from pyegeria.models import (NewElementRequestBody,
30
30
  from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
31
31
  from pyegeria.output_formatter import (generate_output,
32
32
  _extract_referenceable_properties, populate_columns_from_properties,
33
- get_required_relationships)
33
+ get_required_relationships, populate_common_columns, overlay_additional_values, resolve_output_formats)
34
34
  from pyegeria.utils import body_slimmer, dynamic_catch
35
35
 
36
36
  EGERIA_LOCAL_QUALIFIER = app_settings.User_Profile.egeria_local_qualifier
@@ -2527,84 +2527,90 @@ class GlossaryManager(CollectionManager):
2527
2527
  #
2528
2528
 
2529
2529
  def _extract_glossary_properties(self, element: dict, columns_struct: dict) -> dict:
2530
- props = element.get('properties', {}) or {}
2531
- normalized = {
2532
- 'properties': props,
2533
- 'elementHeader': element.get('elementHeader', {}),
2534
- }
2535
- col_data = populate_columns_from_properties(normalized, columns_struct)
2536
- columns_list = col_data.get('formats', {}).get('columns', [])
2530
+ """Extract glossary columns for rendering.
2531
+
2532
+ This extractor uses `populate_common_columns` for standard fields (properties, header, relationships,
2533
+ subject area, mermaid). It then overlays glossary-specific values such as:
2534
+ - categories_names: comma/newline separated Display Names of categories in the glossary
2535
+ - categories_qualified_names: comma/newline separated Qualified Names of categories in the glossary
2536
+
2537
+ Parameters
2538
+ ----------
2539
+ element : dict
2540
+ Raw element as returned by the OMVS.
2541
+ columns_struct : dict
2542
+ The selected output format structure (from _output_formats), whose columns' `value` fields will be filled.
2543
+
2544
+ Returns
2545
+ -------
2546
+ dict
2547
+ The same columns_struct with values populated. Non-empty values are not overwritten.
2548
+ """
2549
+ # Common population first
2550
+ col_data = populate_common_columns(element, columns_struct)
2551
+ # Overlay glossary-specific extras: categories lists
2537
2552
  header_props = _extract_referenceable_properties(element)
2538
2553
  guid = header_props.get('GUID')
2539
- for column in columns_list:
2540
- key = column.get('key')
2541
- if key in header_props:
2542
- column['value'] = header_props.get(key)
2543
- elif isinstance(key, str) and key.lower() == 'guid':
2544
- column['value'] = guid
2554
+ extra: dict = {}
2545
2555
  if guid:
2546
- categories = None
2547
2556
  try:
2548
2557
  categories = self.get_categories_for_glossary(guid)
2549
2558
  except Exception:
2550
2559
  categories = None
2551
- cat_display_list = []
2552
- cat_qn_list = []
2553
2560
  if isinstance(categories, list):
2561
+ cat_display_list = []
2562
+ cat_qn_list = []
2554
2563
  for category in categories:
2555
2564
  gcp = category.get('glossaryCategoryProperties', {})
2556
- dn = gcp.get('displayName', '') or ''
2557
- qn = gcp.get('qualifiedName', '') or ''
2565
+ dn = (gcp.get('displayName') or '')
2566
+ qn = (gcp.get('qualifiedName') or '')
2558
2567
  if dn:
2559
2568
  cat_display_list.append(dn)
2560
2569
  if qn:
2561
2570
  cat_qn_list.append(qn)
2562
- cat_names_md = (", \n".join(cat_display_list)).rstrip(',') if cat_display_list else ''
2563
- cat_qn_md = (", \n".join(cat_qn_list)).rstrip(',') if cat_qn_list else ''
2564
- for column in columns_list:
2565
- if column.get('key') == 'categories_names' and not column.get('value'):
2566
- column['value'] = cat_names_md
2567
- if column.get('key') == 'categories_qualified_names' and not column.get('value'):
2568
- column['value'] = cat_qn_md
2569
- for column in columns_list:
2570
- if column.get('key') == 'mermaid' and not column.get('value'):
2571
- column['value'] = element.get('mermaidGraph', '') or ''
2572
- break
2573
- return col_data
2571
+ if cat_display_list:
2572
+ extra['categories_names'] = ", \n".join(cat_display_list)
2573
+ if cat_qn_list:
2574
+ extra['categories_qualified_names'] = ", \n".join(cat_qn_list)
2575
+ return overlay_additional_values(col_data, extra)
2574
2576
 
2575
2577
  def _extract_term_properties(self, element: dict, columns_struct: dict) -> dict:
2576
- col_data = populate_columns_from_properties(element, columns_struct)
2577
- columns_list = col_data.get("formats", {}).get("columns", [])
2578
- header_props = _extract_referenceable_properties(element)
2579
- for column in columns_list:
2580
- key = column.get('key')
2581
- if key in header_props:
2582
- column['value'] = header_props.get(key)
2583
- elif isinstance(key, str) and key.lower() == 'guid':
2584
- column['value'] = header_props.get('GUID')
2585
- classification_names = ""
2586
- classifications = element.get('elementHeader', {}).get("collectionCategories", [])
2587
- for classification in classifications:
2588
- classification_names += f"{classification['classificationName']}, "
2589
- if classification_names:
2590
- for column in columns_list:
2591
- if column.get('key') == 'classifications':
2592
- column['value'] = classification_names[:-2]
2593
- break
2594
- col_data = get_required_relationships(element, col_data)
2595
- subject_area = element.get('elementHeader', {}).get("subjectArea", "") or ""
2596
- subj_val = ""
2597
- if isinstance(subject_area, dict):
2598
- subj_val = subject_area.get("classificationProperties", {}).get("subjectAreaName", "")
2599
- for column in columns_list:
2600
- if column.get('key') == 'subject_area':
2601
- column['value'] = subj_val
2602
- break
2603
- mermaid_val = element.get('mermaidGraph', "") or ""
2604
- for column in columns_list:
2605
- if column.get('key') == 'mermaid':
2606
- column['value'] = mermaid_val
2607
- break
2578
+ """Extract glossary term columns for rendering.
2579
+
2580
+ Populates standard columns via `populate_common_columns`, and if requested by the
2581
+ selected columns, derives a classifications string (from `elementHeader.collectionCategories`)
2582
+ into the `classifications` column.
2583
+
2584
+ Parameters
2585
+ ----------
2586
+ element : dict
2587
+ Raw term element returned by the OMVS.
2588
+ columns_struct : dict
2589
+ The chosen format-set structure whose column `value`s will be set.
2590
+
2591
+ Returns
2592
+ -------
2593
+ dict
2594
+ The same `columns_struct` with values populated.
2595
+ """
2596
+ # Use centralized population
2597
+ col_data = populate_common_columns(element, columns_struct)
2598
+ # Term-specific classifications (collectionCategories) to 'classifications' column
2599
+ columns_list = col_data.get('formats', {}).get('columns', [])
2600
+ try:
2601
+ classification_names = ""
2602
+ classifications = element.get('elementHeader', {}).get("collectionCategories", [])
2603
+ for classification in classifications:
2604
+ nm = classification.get('classificationName')
2605
+ if nm:
2606
+ classification_names += f"{nm}, "
2607
+ if classification_names:
2608
+ for column in columns_list:
2609
+ if column.get('key') == 'classifications' and column.get('value') in (None, ""):
2610
+ column['value'] = classification_names[:-2]
2611
+ break
2612
+ except Exception:
2613
+ pass
2608
2614
  return col_data
2609
2615
 
2610
2616
  def _get_term_additional_properties(self, element: dict, term_guid: str, output_format: str = None) -> dict:
@@ -2650,17 +2656,7 @@ class GlossaryManager(CollectionManager):
2650
2656
  output_format: str = 'DICT',
2651
2657
  output_format_set: dict | str = None) -> str | list[dict]:
2652
2658
  entity_type = 'Glossary'
2653
- if output_format_set:
2654
- if isinstance(output_format_set, str):
2655
- output_formats = select_output_format_set(output_format_set, output_format)
2656
- elif isinstance(output_format_set, dict):
2657
- output_formats = get_output_format_type_match(output_format_set, output_format)
2658
- else:
2659
- output_formats = None
2660
- else:
2661
- output_formats = select_output_format_set(entity_type, output_format)
2662
- if output_formats is None:
2663
- output_formats = select_output_format_set('Default', output_format)
2659
+ output_formats = resolve_output_formats(entity_type, output_format, output_format_set)
2664
2660
  return generate_output(
2665
2661
  elements=elements,
2666
2662
  search_string=search_string,
@@ -2676,17 +2672,7 @@ class GlossaryManager(CollectionManager):
2676
2672
  output_format: str = 'DICT',
2677
2673
  output_format_set: dict | str = None) -> str | list[dict]:
2678
2674
  entity_type = 'GlossaryTerm'
2679
- if output_format_set:
2680
- if isinstance(output_format_set, str):
2681
- output_formats = select_output_format_set(output_format_set, output_format)
2682
- elif isinstance(output_format_set, dict):
2683
- output_formats = get_output_format_type_match(output_format_set, output_format)
2684
- else:
2685
- output_formats = None
2686
- else:
2687
- output_formats = select_output_format_set(entity_type, output_format)
2688
- if output_formats is None:
2689
- output_formats = select_output_format_set('Default', output_format)
2675
+ output_formats = resolve_output_formats(entity_type, output_format, output_format_set)
2690
2676
  return generate_output(
2691
2677
  elements=elements,
2692
2678
  search_string=search_string,
@@ -22,6 +22,7 @@ from pyegeria.output_formatter import (
22
22
  _extract_referenceable_properties,
23
23
  populate_columns_from_properties,
24
24
  get_required_relationships,
25
+ populate_common_columns,
25
26
  )
26
27
  from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
27
28
 
@@ -108,16 +109,26 @@ class GovernanceOfficer(Client2):
108
109
  output_format: str = 'DICT', output_format_set: dict | str = None
109
110
  ) -> str | list:
110
111
  """
111
- Generate output for governance definitions in the specified format, using output format sets.
112
+ Render governance definitions using the shared output pipeline.
112
113
 
113
- Args:
114
- elements: Dictionary or list of dictionaries containing governance definition elements
115
- search_string: The search string used to find the elements
116
- output_format: The desired output format (MD, FORM, REPORT, LIST, DICT, MERMAID, HTML)
117
- output_format_set: Optional format set name or structure to control columns
114
+ Parameters
115
+ ----------
116
+ elements : dict | list[dict]
117
+ Element(s) returned by the OMVS.
118
+ search_string : str
119
+ The search string used to retrieve these elements (shown in preamble for some formats).
120
+ element_type_name : str, optional
121
+ Friendly type label to display (defaults to "Governance Definition").
122
+ output_format : str
123
+ One of: MD, FORM, REPORT, LIST, DICT, MERMAID, HTML.
124
+ output_format_set : dict | str, optional
125
+ Either a label for a format set or a concrete format-set dict. When omitted, a sensible
126
+ default for Governance Definitions is chosen, falling back to "Default".
118
127
 
119
- Returns:
120
- Formatted output as string or list of dictionaries
128
+ Returns
129
+ -------
130
+ str | list
131
+ Rendered output in the requested format.
121
132
  """
122
133
  # Ensure elements handled consistently for MERMAID
123
134
  if output_format == "MERMAID":
@@ -167,27 +178,13 @@ class GovernanceOfficer(Client2):
167
178
  Returns:
168
179
  dict: columns_struct with populated 'value' fields
169
180
  """
170
- col_data = populate_columns_from_properties(element, columns_struct)
171
- columns_list = col_data.get('formats', {}).get('columns', [])
172
-
173
- # Header-derived values (GUID, type_name, etc.)
174
- header_props = _extract_referenceable_properties(element)
175
- for column in columns_list:
176
- key = column.get('key')
177
- if key in header_props:
178
- column['value'] = header_props.get(key)
179
- elif isinstance(key, str) and key.lower() == 'guid':
180
- column['value'] = header_props.get('GUID')
181
-
182
- # Populate requested relationship-based columns generically
183
- col_data = get_required_relationships(element, col_data)
184
-
185
- # Mermaid graph if requested
186
- for column in columns_list:
187
- if column.get('key') == 'mermaid':
188
- column['value'] = element.get('mermaidGraph', '') or ''
189
- break
190
-
181
+ # Use the common population pipeline to reduce duplication
182
+ col_data = populate_common_columns(element, columns_struct,
183
+ include_header=True,
184
+ include_relationships=True,
185
+ include_subject_area=True,
186
+ mermaid_source_key='mermaidGraph',
187
+ mermaid_dest_key='mermaid')
191
188
  return col_data
192
189
 
193
190
  def _extract_gov_def_list(self, element: Union[Dict, List[Dict]]) -> List[Dict]: