pyegeria 5.4.3.3__py3-none-any.whl → 5.4.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 (230) 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.2025-09-04_08-21-58_788009.log.zip +0 -0
  4. commands/cat/debug_log.2025-09-05_09-37-53_062579.log.zip +0 -0
  5. commands/cat/list_format_set.py +5 -3
  6. commands/tech/list_information_supply_chains.py +1 -1
  7. commands/tech/list_solution_blueprints.py +1 -1
  8. commands/tech/list_solution_components.py +1 -1
  9. commands/tech/list_solution_roles.py +1 -1
  10. md_processing/__init__.py +9 -5
  11. md_processing/data/commands.json +7182 -1401
  12. md_processing/data/generated_format_sets.json +4137 -0
  13. md_processing/data/generated_format_sets.py +51 -0
  14. md_processing/dr_egeria.py +20 -11
  15. md_processing/md_commands/data_designer_commands.py +90 -425
  16. md_processing/md_commands/ext_ref_commands.py +543 -0
  17. md_processing/md_commands/old_solution_architect_commands.py +1139 -0
  18. md_processing/md_commands/solution_architect_commands.py +26 -59
  19. md_processing/md_processing_utils/common_md_utils.py +50 -1
  20. md_processing/md_processing_utils/debug_log +1 -3
  21. md_processing/md_processing_utils/dr-egeria-help-2025-09-09T11:10:03.md +3305 -0
  22. md_processing/md_processing_utils/extraction_utils.py +14 -7
  23. md_processing/md_processing_utils/gen_format_sets.py +422 -0
  24. md_processing/md_processing_utils/md_processing_constants.py +20 -2
  25. pyegeria/__init__.py +1 -1
  26. pyegeria/_client_new.py +9 -7
  27. pyegeria/_output_formats.py +278 -3
  28. pyegeria/collection_manager.py +20 -59
  29. pyegeria/config.py +10 -1
  30. pyegeria/data_designer.py +166 -117
  31. pyegeria/egeria_client.py +1 -1
  32. pyegeria/egeria_tech_client.py +4 -1
  33. pyegeria/external_references.py +1794 -0
  34. pyegeria/glossary_manager.py +71 -85
  35. pyegeria/governance_officer.py +26 -29
  36. pyegeria/output_formatter.py +127 -1
  37. pyegeria/project_manager.py +33 -36
  38. pyegeria/{solution_architect_omvs.py → solution_architect.py} +733 -873
  39. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.4.dist-info}/METADATA +1 -1
  40. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.4.dist-info}/RECORD +43 -219
  41. commands/.DS_Store +0 -0
  42. commands/cat/.DS_Store +0 -0
  43. commands/cat/.env +0 -8
  44. commands/cat/debug_log.2025-08-29_07-07-27_848189.log.zip +0 -0
  45. commands/cat/debug_log.2025-08-30_21-15-48_528443.log.zip +0 -0
  46. commands/cat/debug_log.log +0 -6102
  47. commands/cat/logs/pyegeria.log +0 -90
  48. commands/cli/debug_log.log +0 -0
  49. commands/doc/.DS_Store +0 -0
  50. commands/ops/logs/pyegeria.log +0 -0
  51. md_processing/.DS_Store +0 -0
  52. md_processing/.idea/.gitignore +0 -8
  53. md_processing/.idea/inspectionProfiles/Project_Default.xml +0 -59
  54. md_processing/.idea/md_processing.iml +0 -15
  55. md_processing/.idea/modules.xml +0 -8
  56. md_processing/.idea/sonarlint/issuestore/index.pb +0 -0
  57. md_processing/.idea/sonarlint/securityhotspotstore/index.pb +0 -0
  58. md_processing/.idea/vcs.xml +0 -6
  59. md_processing/.idea/workspace.xml +0 -107
  60. md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +0 -8
  61. md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +0 -873
  62. md_processing/dr_egeria_inbox/arch_test.md +0 -57
  63. md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +0 -254
  64. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +0 -696
  65. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +0 -254
  66. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +0 -298
  67. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +0 -608
  68. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +0 -94
  69. md_processing/dr_egeria_inbox/archive/freddie_intro.md +0 -284
  70. md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +0 -275
  71. md_processing/dr_egeria_inbox/archive/test-term.md +0 -110
  72. md_processing/dr_egeria_inbox/cat_test.md +0 -100
  73. md_processing/dr_egeria_inbox/collections.md +0 -39
  74. md_processing/dr_egeria_inbox/data_designer_debug.log +0 -6
  75. md_processing/dr_egeria_inbox/data_designer_out.md +0 -60
  76. md_processing/dr_egeria_inbox/data_designer_search_test.md +0 -11
  77. md_processing/dr_egeria_inbox/data_field.md +0 -54
  78. md_processing/dr_egeria_inbox/data_spec.md +0 -77
  79. md_processing/dr_egeria_inbox/data_spec_test.md +0 -2340
  80. md_processing/dr_egeria_inbox/data_test.md +0 -179
  81. md_processing/dr_egeria_inbox/data_test2.md +0 -429
  82. md_processing/dr_egeria_inbox/data_test3.md +0 -462
  83. md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +0 -124
  84. md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +0 -168
  85. md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +0 -280
  86. md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +0 -318
  87. md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +0 -1073
  88. md_processing/dr_egeria_inbox/dr_egeria_isc1.md +0 -44
  89. md_processing/dr_egeria_inbox/generated_help_report.md +0 -9
  90. md_processing/dr_egeria_inbox/generated_help_terms.md +0 -842
  91. md_processing/dr_egeria_inbox/glossary_list.md +0 -5
  92. md_processing/dr_egeria_inbox/glossary_search_test.md +0 -40
  93. md_processing/dr_egeria_inbox/glossary_test1.md +0 -378
  94. md_processing/dr_egeria_inbox/gov_def.md +0 -718
  95. md_processing/dr_egeria_inbox/gov_def2.md +0 -447
  96. md_processing/dr_egeria_inbox/img.png +0 -0
  97. md_processing/dr_egeria_inbox/output_tests.md +0 -114
  98. md_processing/dr_egeria_inbox/product.md +0 -211
  99. md_processing/dr_egeria_inbox/rel.md +0 -8
  100. md_processing/dr_egeria_inbox/sb.md +0 -119
  101. md_processing/dr_egeria_inbox/solution-components.md +0 -136
  102. md_processing/dr_egeria_inbox/solution_blueprints.md +0 -118
  103. md_processing/dr_egeria_inbox/synonym_test.md +0 -42
  104. md_processing/dr_egeria_inbox/t2.md +0 -268
  105. md_processing/dr_egeria_outbox/.DS_Store +0 -0
  106. md_processing/dr_egeria_outbox/.obsidian/app.json +0 -1
  107. md_processing/dr_egeria_outbox/.obsidian/appearance.json +0 -1
  108. md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +0 -7
  109. md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +0 -33
  110. md_processing/dr_egeria_outbox/.obsidian/plugins/buttons/main.js +0 -5164
  111. md_processing/dr_egeria_outbox/.obsidian/plugins/buttons/manifest.json +0 -10
  112. md_processing/dr_egeria_outbox/.obsidian/plugins/buttons/styles.css +0 -624
  113. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +0 -10
  114. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +0 -4459
  115. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +0 -10
  116. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +0 -3
  117. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +0 -153
  118. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +0 -11
  119. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +0 -1
  120. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +0 -500
  121. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +0 -12
  122. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +0 -1
  123. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/data.json +0 -38
  124. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +0 -37
  125. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +0 -11
  126. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +0 -220
  127. md_processing/dr_egeria_outbox/.obsidian/types.json +0 -28
  128. md_processing/dr_egeria_outbox/.obsidian/workspace.json +0 -270
  129. md_processing/dr_egeria_outbox/Button Test.md +0 -11
  130. md_processing/dr_egeria_outbox/Scripts/.DS_Store +0 -0
  131. md_processing/dr_egeria_outbox/Scripts/sendRest.js +0 -24
  132. md_processing/dr_egeria_outbox/Templates/sendToApi.md.md +0 -17
  133. md_processing/dr_egeria_outbox/Untitled.canvas +0 -1
  134. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 09:26-product.md +0 -210
  135. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 14:03-product.md +0 -209
  136. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 14:24-product.md +0 -263
  137. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 16:03-data_spec_test.md +0 -2374
  138. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 16:05-data_spec_test.md +0 -2374
  139. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 08:28-data_spec_test.md +0 -2321
  140. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 08:37-data_spec_test.md +0 -2304
  141. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 08:56-data_spec_test.md +0 -2324
  142. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 09:00-data_spec_test.md +0 -2324
  143. md_processing/dr_egeria_outbox/processed-2025-08-30 16:56-generated_help_terms.md +0 -795
  144. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +0 -719
  145. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +0 -41
  146. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +0 -33
  147. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +0 -192
  148. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +0 -486
  149. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +0 -486
  150. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +0 -486
  151. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +0 -486
  152. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +0 -486
  153. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +0 -486
  154. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +0 -486
  155. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 13:07-gov_def.md +0 -492
  156. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 13:25-gov_def.md +0 -520
  157. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 16:43-gov_def.md +0 -636
  158. md_processing/dr_egeria_outbox/tuesday/processed-2025-09-02 16:46-gov_def.md +0 -636
  159. md_processing/family_docs/Data Designer/Create_Data_Class.md +0 -164
  160. md_processing/family_docs/Data Designer/Create_Data_Dictionary.md +0 -30
  161. md_processing/family_docs/Data Designer/Create_Data_Field.md +0 -162
  162. md_processing/family_docs/Data Designer/Create_Data_Specification.md +0 -36
  163. md_processing/family_docs/Data Designer/Create_Data_Structure.md +0 -38
  164. md_processing/family_docs/Data Designer/View_Data_Classes.md +0 -78
  165. md_processing/family_docs/Data Designer/View_Data_Dictionaries.md +0 -78
  166. md_processing/family_docs/Data Designer/View_Data_Fields.md +0 -78
  167. md_processing/family_docs/Data Designer/View_Data_Specifications.md +0 -78
  168. md_processing/family_docs/Data Designer/View_Data_Structures.md +0 -78
  169. md_processing/family_docs/Data Designer.md +0 -842
  170. md_processing/family_docs/Digital Product Manager/Add_Member->Collection.md +0 -42
  171. md_processing/family_docs/Digital Product Manager/Attach_Collection->Resource.md +0 -36
  172. md_processing/family_docs/Digital Product Manager/Create_Agreement.md +0 -96
  173. md_processing/family_docs/Digital Product Manager/Create_Data_Sharing_Agreement.md +0 -72
  174. md_processing/family_docs/Digital Product Manager/Create_DigitalSubscription.md +0 -102
  175. md_processing/family_docs/Digital Product Manager/Create_Digital_Product.md +0 -134
  176. md_processing/family_docs/Digital Product Manager/Link_Agreement_Items.md +0 -60
  177. md_processing/family_docs/Digital Product Manager/Link_Contracts.md +0 -26
  178. md_processing/family_docs/Digital Product Manager/Link_Digital_Product_-_Digital_Product.md +0 -30
  179. md_processing/family_docs/Digital Product Manager/Link_Subscribers.md +0 -48
  180. md_processing/family_docs/Digital Product Manager.md +0 -668
  181. md_processing/family_docs/Glossary/Attach_Category_Parent.md +0 -18
  182. md_processing/family_docs/Glossary/Attach_Term-Term_Relationship.md +0 -26
  183. md_processing/family_docs/Glossary/Create_Category.md +0 -38
  184. md_processing/family_docs/Glossary/Create_Glossary.md +0 -42
  185. md_processing/family_docs/Glossary/Create_Term.md +0 -70
  186. md_processing/family_docs/Glossary.md +0 -206
  187. md_processing/family_docs/Governance Officer/Create_Business_Imperative.md +0 -106
  188. md_processing/family_docs/Governance Officer/Create_Certification_Type.md +0 -112
  189. md_processing/family_docs/Governance Officer/Create_Governance_Approach.md +0 -114
  190. md_processing/family_docs/Governance Officer/Create_Governance_Obligation.md +0 -114
  191. md_processing/family_docs/Governance Officer/Create_Governance_Principle.md +0 -114
  192. md_processing/family_docs/Governance Officer/Create_Governance_Procedure.md +0 -128
  193. md_processing/family_docs/Governance Officer/Create_Governance_Process.md +0 -122
  194. md_processing/family_docs/Governance Officer/Create_Governance_Processing_Purpose.md +0 -106
  195. md_processing/family_docs/Governance Officer/Create_Governance_Responsibility.md +0 -122
  196. md_processing/family_docs/Governance Officer/Create_Governance_Rule.md +0 -122
  197. md_processing/family_docs/Governance Officer/Create_Governance_Strategy.md +0 -106
  198. md_processing/family_docs/Governance Officer/Create_License_Type.md +0 -112
  199. md_processing/family_docs/Governance Officer/Create_Naming_Standard_Rule.md +0 -122
  200. md_processing/family_docs/Governance Officer/Create_Regulation_Article.md +0 -106
  201. md_processing/family_docs/Governance Officer/Create_Regulation_Definition.md +0 -118
  202. md_processing/family_docs/Governance Officer/Create_Security_Access_Control.md +0 -114
  203. md_processing/family_docs/Governance Officer/Create_Security_Group.md +0 -120
  204. md_processing/family_docs/Governance Officer/Create_Service_Level_Objectives.md +0 -122
  205. md_processing/family_docs/Governance Officer/Create_Threat_Definition.md +0 -106
  206. md_processing/family_docs/Governance Officer/Link_Governance_Controls.md +0 -32
  207. md_processing/family_docs/Governance Officer/Link_Governance_Drivers.md +0 -32
  208. md_processing/family_docs/Governance Officer/Link_Governance_Policies.md +0 -32
  209. md_processing/family_docs/Governance Officer/View_Governance_Definitions.md +0 -82
  210. md_processing/family_docs/Governance Officer.md +0 -2412
  211. md_processing/family_docs/Solution Architect/Create_Information_Supply_Chain.md +0 -70
  212. md_processing/family_docs/Solution Architect/Create_Solution_Blueprint.md +0 -44
  213. md_processing/family_docs/Solution Architect/Create_Solution_Component.md +0 -96
  214. md_processing/family_docs/Solution Architect/Create_Solution_Role.md +0 -66
  215. md_processing/family_docs/Solution Architect/Link_Information_Supply_Chain_Peers.md +0 -32
  216. md_processing/family_docs/Solution Architect/Link_Solution_Component_Peers.md +0 -32
  217. md_processing/family_docs/Solution Architect/View_Information_Supply_Chains.md +0 -32
  218. md_processing/family_docs/Solution Architect/View_Solution_Blueprints.md +0 -32
  219. md_processing/family_docs/Solution Architect/View_Solution_Components.md +0 -32
  220. md_processing/family_docs/Solution Architect/View_Solution_Roles.md +0 -32
  221. md_processing/family_docs/Solution Architect.md +0 -490
  222. md_processing/md_commands/old_project_commands.py +0 -164
  223. md_processing/md_processing_utils/debug_log.log +0 -5580
  224. md_processing/md_processing_utils/generated_help_terms.md +0 -842
  225. md_processing/md_processing_utils/logs/pyegeria.log +0 -56
  226. pyegeria/.DS_Store +0 -0
  227. pyegeria/___external_references.py +0 -3267
  228. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.4.dist-info}/LICENSE +0 -0
  229. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.4.dist-info}/WHEEL +0 -0
  230. {pyegeria-5.4.3.3.dist-info → pyegeria-5.4.4.dist-info}/entry_points.txt +0 -0
@@ -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]:
@@ -677,7 +677,7 @@ def generate_entity_dict(elements: List[Dict],
677
677
  value = format_for_markdown_table(value, guid or props.get('GUID'))
678
678
  entity_dict[name] = value
679
679
  else:
680
- props = extract_properties_func(element)
680
+ props = extract_properties_func(element, columns_struct)
681
681
  # Add properties based on include/exclude lists
682
682
  for key, value in props.items():
683
683
  if key not in ['properties', 'mermaid']: # Skip the raw properties object
@@ -721,6 +721,127 @@ def generate_entity_dict(elements: List[Dict],
721
721
 
722
722
  return result
723
723
 
724
+ def resolve_output_formats(entity_type: str,
725
+ output_format: str,
726
+ output_format_set: Optional[Union[str, dict]] = None,
727
+ default_label: Optional[str] = None) -> Optional[dict]:
728
+ """
729
+ Resolve an output format set structure given an entity type, the desired output format
730
+ (e.g., DICT, LIST, MD, REPORT, FORM), and either a label (str) or a dict of format sets.
731
+
732
+ Selection order:
733
+ - If output_format_set is a str: select by label.
734
+ - If output_format_set is a dict: use get_output_format_type_match to pick a matching format.
735
+ - Else: try selecting by entity_type or default_label.
736
+ - Fallback: select "Default".
737
+ """
738
+ from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
739
+
740
+ if isinstance(output_format_set, str):
741
+ return select_output_format_set(output_format_set, output_format)
742
+ if isinstance(output_format_set, dict):
743
+ return get_output_format_type_match(output_format_set, output_format)
744
+
745
+ label = default_label or entity_type
746
+ fmt = select_output_format_set(label, output_format)
747
+ if fmt is None:
748
+ fmt = select_output_format_set("Default", output_format)
749
+ return fmt
750
+
751
+
752
+ def overlay_additional_values(columns_struct: dict, extra: Optional[dict]) -> dict:
753
+ """
754
+ Overlay extra values into columns_struct only where the column's value is empty or missing.
755
+ Returns the modified columns_struct.
756
+ """
757
+ if not isinstance(columns_struct, dict) or not extra:
758
+ return columns_struct
759
+ columns = columns_struct.get('formats', {}).get('columns')
760
+ if not isinstance(columns, list):
761
+ return columns_struct
762
+ for col in columns:
763
+ if not isinstance(col, dict):
764
+ continue
765
+ key = col.get('key')
766
+ if not key:
767
+ continue
768
+ if col.get('value') in (None, "") and key in extra:
769
+ col['value'] = extra[key]
770
+ return columns_struct
771
+
772
+
773
+ def populate_common_columns(
774
+ element: dict,
775
+ columns_struct: dict,
776
+ *,
777
+ include_header: bool = True,
778
+ include_relationships: bool = True,
779
+ include_subject_area: bool = True,
780
+ mermaid_source_key: str = 'mermaidGraph',
781
+ mermaid_dest_key: str = 'mermaid'
782
+ ) -> dict:
783
+ """
784
+ Populate the common columns in columns_struct based on a standard Egeria element shape.
785
+
786
+ Steps:
787
+ - Populate from element.properties (camelCase mapped from snake_case keys)
788
+ - Optionally overlay header-derived values (GUID, type_name, times, etc.)
789
+ - Optionally populate relationship-based columns via get_required_relationships
790
+ - Optionally populate subject_area from element.elementHeader.subjectArea.classificationProperties.subjectAreaName
791
+ - If a column with key == mermaid_dest_key is present, set it from mermaid_source_key
792
+ - Do not overwrite non-empty values already set
793
+ """
794
+ # 1) Base properties
795
+ col_data = populate_columns_from_properties(element, columns_struct)
796
+ columns_list = col_data.get('formats', {}).get('columns', [])
797
+
798
+ # 2) Header overlay
799
+ header_props = _extract_referenceable_properties(element) if include_header else {}
800
+ guid = header_props.get('GUID') if include_header else None
801
+ if include_header:
802
+ for column in columns_list:
803
+ if not isinstance(column, dict):
804
+ continue
805
+ key = column.get('key')
806
+ if not key:
807
+ continue
808
+ if column.get('value') not in (None, ""):
809
+ continue
810
+ if key in header_props:
811
+ column['value'] = header_props.get(key)
812
+ elif isinstance(key, str) and key.lower() == 'guid':
813
+ column['value'] = guid
814
+
815
+ # 3) Relationships
816
+ if include_relationships:
817
+ col_data = get_required_relationships(element, col_data)
818
+
819
+ # 4) Subject area
820
+ if include_subject_area:
821
+ try:
822
+ subject_area = element.get('elementHeader', {}).get('subjectArea') or ""
823
+ subj_val = ""
824
+ if isinstance(subject_area, dict):
825
+ subj_val = subject_area.get('classificationProperties', {}).get('subjectAreaName', '')
826
+ for column in columns_list:
827
+ if column.get('key') == 'subject_area' and column.get('value') in (None, ""):
828
+ column['value'] = subj_val
829
+ except Exception as e:
830
+ logger.debug(f"populate_common_columns: subject_area handling error: {e}")
831
+
832
+ # 5) Mermaid
833
+ try:
834
+ mermaid_val = element.get(mermaid_source_key, '') or ''
835
+ for column in columns_list:
836
+ if column.get('key') == mermaid_dest_key and column.get('value') in (None, ""):
837
+ column['value'] = mermaid_val
838
+ break
839
+ except Exception as e:
840
+ logger.debug(f"populate_common_columns: mermaid handling error: {e}")
841
+
842
+ return col_data
843
+
844
+
724
845
  def extract_mermaid_only(elements: Union[Dict, List[Dict]]) -> Union[str, List[str]]:
725
846
  """
726
847
  Extract mermaid graph data from elements.
@@ -810,6 +931,11 @@ def generate_output(elements: Union[Dict, List[Dict]],
810
931
  Formatted output as string or list of dictionaries
811
932
  """
812
933
  columns = columns_struct['formats'].get('columns',None) if columns_struct else None
934
+ if not columns:
935
+ columns_struct = select_output_format_set("Default",output_format)
936
+ if columns_struct:
937
+ columns = columns_struct.get('formats', {}).get('columns', None)
938
+
813
939
  target_type = columns_struct.get('target_type', entity_type) if columns_struct else entity_type
814
940
  if target_type is None:
815
941
  target_type = entity_type
@@ -17,7 +17,7 @@ from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequ
17
17
  TemplateRequestBody, DeleteRequestBody, UpdateElementRequestBody,
18
18
  NewRelationshipRequestBody)
19
19
  from pyegeria.output_formatter import generate_output, populate_columns_from_properties, \
20
- _extract_referenceable_properties, get_required_relationships
20
+ _extract_referenceable_properties, get_required_relationships, populate_common_columns, overlay_additional_values
21
21
  from pyegeria.utils import body_slimmer, dynamic_catch
22
22
 
23
23
  EGERIA_LOCAL_QUALIFIER = app_settings.User_Profile.egeria_local_qualifier
@@ -28,20 +28,33 @@ PROJECT_TYPES = ["Project", "Campaign", "StudyProject", "Task", "PersonalProject
28
28
 
29
29
  class ProjectManager(Client2):
30
30
  """
31
- Create and manage projects. Projects may be organized in a hierarchy.
32
- See https://egeria-project.org/types/1/0130-Projects
33
-
34
- Attributes:
35
-
36
- server_name: str
37
- The name of the View Server to connect to.
38
- platform_url : str
39
- URL of the server platform to connect to
40
- user_id : str
41
- The identity of the user calling the method - this sets a default optionally used by the methods
42
- when the user doesn't pass the user_id on a method call.
43
- user_pwd: str
44
- The password associated with the user_id. Defaults to None
31
+ Manage Open Metadata Projects via the Project Manager OMVS.
32
+
33
+ This client provides asynchronous and synchronous helpers to create, update, search,
34
+ and relate Project elements and their subtypes (Campaign, StudyProject, Task, PersonalProject).
35
+
36
+ References
37
+ - Egeria concept: https://egeria-project.org/concepts/project
38
+ - Type lineage: https://egeria-project.org/types/1/0130-Projects
39
+
40
+ Parameters
41
+ -----------
42
+ view_server : str
43
+ The name of the View Server to connect to.
44
+ platform_url : str
45
+ URL of the server platform to connect to.
46
+ user_id : str
47
+ Default user identity for calls (can be overridden per call).
48
+ user_pwd : str, optional
49
+ Password for the user_id. If a token is supplied, this may be None.
50
+
51
+ Notes
52
+ -----
53
+ - Most high-level list/report methods accept an `output_format` and an optional `output_format_set` and
54
+ delegate rendering to `pyegeria.output_formatter.generate_output` along with shared helpers such as
55
+ `populate_common_columns`.
56
+ - Private extractor methods follow the convention: `_extract_<entity>_properties(element, columns_struct)` and
57
+ must return the same `columns_struct` with per-column `value` fields populated.
45
58
  """
46
59
 
47
60
  def __init__(
@@ -88,28 +101,12 @@ class ProjectManager(Client2):
88
101
  'properties': props,
89
102
  'elementHeader': element.get('elementHeader', {}),
90
103
  }
91
- col_data = populate_columns_from_properties(element, columns_struct)
92
- # col_data = populate_columns_from_properties(normalized, columns_struct)
104
+ # Common population pipeline
105
+ col_data = populate_common_columns(element, columns_struct)
93
106
  columns_list = col_data.get('formats', {}).get('columns', [])
94
- header_props = _extract_referenceable_properties(element)
95
- # Populate requested relationship-based columns generically
96
- col_data = get_required_relationships(element, col_data)
97
- additional_props = self._extract_additional_project_properties(element, columns_struct)
98
- guid = header_props.get('GUID')
99
-
100
- for column in columns_list:
101
- key = column.get('key')
102
- if key in header_props:
103
- column['value'] = header_props.get(key)
104
- elif key == 'project_roles':
105
- column['value'] = additional_props.get('project_roles', '')
106
- elif isinstance(key, str) and key.lower() == 'guid':
107
- column['value'] = guid
108
-
109
- for column in columns_list:
110
- if column.get('key') == 'mermaid' and not column.get('value'):
111
- column['value'] = element.get('mermaidGraph', '') or ''
112
- break
107
+ # Overlay extras (project roles) only where empty
108
+ extra = self._extract_additional_project_properties(element, columns_struct)
109
+ col_data = overlay_additional_values(col_data, extra)
113
110
  return col_data
114
111
 
115
112