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
@@ -421,7 +421,7 @@ def get_element_by_name(egeria_client, element_type: str, element_name: str) ->
421
421
  return q_name, guid, unique, exists
422
422
 
423
423
  # Haven't seen this element before
424
- property_names = ['qualifiedName', 'name', 'displayName', 'title']
424
+ property_names = ['qualifiedName', 'displayName', 'title']
425
425
  open_metadata_type_name = None
426
426
  details = egeria_client.get_elements_by_property_value(element_name, property_names, open_metadata_type_name)
427
427
  if isinstance(details, str):
@@ -430,12 +430,19 @@ def get_element_by_name(egeria_client, element_type: str, element_name: str) ->
430
430
  exists = False
431
431
  return None, None, unique, exists
432
432
  if len(details) > 1:
433
- msg = (f"More than one element with name {element_name} found, please specify a "
434
- f"**Qualified Name**")
435
- print_msg("DEBUG-ERROR", msg, debug_level)
436
- unique = False
437
- exists = None
438
- return element_name, None, unique, exists
433
+ if q_name is None:
434
+ q_name = egeria_client.__create_qualified_name__(element_type, element_name)
435
+ guid = egeria_client.__get_guid__(qualified_name=q_name)
436
+ update_element_dictionary(q_name, {'guid': guid})
437
+ exists = True if guid != "No elements found" else False
438
+ return q_name, guid, unique, exists
439
+ else:
440
+ msg = (f"More than one element with name {element_name} found, please specify a "
441
+ f"**Qualified Name**")
442
+ print_msg("DEBUG-ERROR", msg, debug_level)
443
+ unique = False
444
+ exists = None
445
+ return element_name, None, unique, exists
439
446
 
440
447
  el_qname = details[0]["properties"].get('qualifiedName', None)
441
448
  el_guid = details[0]['elementHeader']['guid']
@@ -0,0 +1,422 @@
1
+ import argparse
2
+ import json
3
+ import re
4
+ import argparse
5
+ from pathlib import Path
6
+ from typing import Iterable, List, Optional
7
+
8
+ from loguru import logger
9
+ from rich.prompt import Prompt
10
+
11
+ from pyegeria._output_format_models import Column, Format, ActionParameter, FormatSet, FormatSetDict
12
+ from pyegeria._output_formats import OPTIONAL_PARAMS # ["page_size","start_from","starts_with","ends_with","ignore_case"]
13
+
14
+ def _is_create_command(cmd_key: str, cmd_obj: dict) -> bool:
15
+ name = str(cmd_obj.get("display_name", "")).strip()
16
+ return (isinstance(cmd_key, str) and cmd_key.strip().lower().startswith("create")) or \
17
+ (name.lower().startswith("create"))
18
+
19
+ def _derive_set_name_from_display_name(display_name: str) -> str:
20
+ """From 'Create Governance Strategy Definition' -> 'GovernanceStrategyDefinition'."""
21
+ if not display_name:
22
+ return ""
23
+ parts = display_name.strip().split()
24
+ if len(parts) <= 1:
25
+ return re.sub(r"\s+", "", display_name.strip())+"-DrE"
26
+ rest = " ".join(parts[1:]).strip()
27
+ rest = rest+"-DrE"
28
+ return re.sub(r"\s+", "", rest)
29
+
30
+ def _safe_parse_constraints(s) -> dict:
31
+ """Parse find_constraints (often an escaped JSON string) into a dict."""
32
+ if not s:
33
+ return {}
34
+ if isinstance(s, dict):
35
+ return s
36
+ txt = str(s).strip()
37
+ # Attempt 1: direct JSON
38
+ try:
39
+ return json.loads(txt)
40
+ except Exception:
41
+ logger.error(f"Error parsing constraints: {s!r}")
42
+ pass
43
+ # Attempt 2: unwrap quotes
44
+ try:
45
+ txt2 = txt.strip('"').strip("'")
46
+ return json.loads(txt2)
47
+ except Exception:
48
+ pass
49
+ # Attempt 3: heuristic single->double quotes for dict-like strings
50
+ if ("{" in txt and "}" in txt) and ("'" in txt and '"' not in txt):
51
+ try:
52
+ return json.loads(txt.replace("'", '"'))
53
+ except Exception:
54
+ pass
55
+ logger.debug(f"Could not parse find_constraints: {s!r} -> {{}}")
56
+ return {}
57
+
58
+ def _extract_basic_columns_from_attributes(attributes: Iterable[dict]) -> List[Column]:
59
+ """
60
+ attributes: list of single-key dicts, e.g.
61
+ {"Display Name": {"variable_name": "display_name", "level": "Basic", ...}}
62
+ """
63
+
64
+ cols: List[Column] = []
65
+ seen: set[str] = set()
66
+ for entry in attributes or []:
67
+ if not isinstance(entry, dict) or not entry:
68
+ continue
69
+ # Some entries may contain more than one key; scan all items
70
+ for label, details in entry.items():
71
+ if not isinstance(details, dict):
72
+ continue
73
+ if details.get("level") != "Basic":
74
+ continue
75
+ key = details.get("variable_name")
76
+ if not key or key in seen:
77
+ continue
78
+ seen.add(key)
79
+ cols.append(Column(name=str(label), key=str(key)))
80
+ return cols
81
+
82
+ def build_format_sets_from_commands(
83
+ commands_json_path: str | Path,
84
+ *,
85
+ include_only_create: bool = True,
86
+ default_types: Optional[List[str]] = None
87
+ ) -> FormatSetDict:
88
+ """
89
+ Convert commands.json into a FormatSetDict following your mappings.
90
+ """
91
+ path = Path(commands_json_path)
92
+ data = json.loads(path.read_text(encoding="utf-8"))
93
+
94
+ if isinstance(data, dict):
95
+ items_iter = data.items()
96
+ elif isinstance(data, list):
97
+ # If it’s a list of command objects, synthesize keys from display_name or index
98
+ def _key_for(idx, obj):
99
+ dn = str(obj.get("display_name", "")).strip()
100
+ return dn or f"cmd_{idx}"
101
+
102
+ items_iter = ((_key_for(i, obj), obj) for i, obj in enumerate(data))
103
+ else:
104
+ raise ValueError("commands.json root must be an object/dict or an array of command objects")
105
+ data = dict(items_iter).get("Command Specifications", {})
106
+ logger.info(
107
+ f"Loaded commands.json from {path} with {len(data)} commands"
108
+ )
109
+
110
+
111
+ results = FormatSetDict()
112
+ types = default_types or ["ALL"]
113
+
114
+ for cmd_key, cmd_obj in data.items():
115
+ if not isinstance(cmd_obj, dict):
116
+ continue
117
+ if include_only_create and not _is_create_command(cmd_key, cmd_obj):
118
+ continue
119
+
120
+ display_name = str(cmd_obj.get("display_name", "")).strip()
121
+ if not display_name:
122
+ logger.debug(f"Skip {cmd_key!r}: missing display_name")
123
+ continue
124
+ set_name = re.sub(r"\s+", "-", display_name)
125
+ set_name = set_name.strip() + "-DrE"
126
+ # set_name = _derive_set_name_from_display_name(display_name)
127
+ if not set_name:
128
+ logger.debug(f"Skip {cmd_key!r}: could not derive set name from display_name={display_name!r}")
129
+ continue
130
+
131
+ columns = _extract_basic_columns_from_attributes(cmd_obj.get("Attributes", []))
132
+ if not columns:
133
+ logger.debug(f"Skip {set_name}: no Basic attributes")
134
+ continue
135
+
136
+ find_method = cmd_obj.get("find_method") or ""
137
+ constraints = _safe_parse_constraints(cmd_obj.get("find_constraints"))
138
+ action = None
139
+ if find_method:
140
+ action = ActionParameter(
141
+ function=find_method,
142
+ required_params=["search_string"],
143
+ optional_params=OPTIONAL_PARAMS,
144
+ spec_params=constraints or {},
145
+ )
146
+
147
+ fs = FormatSet(
148
+ target_type=set_name,
149
+ heading=f"{set_name} Attributes",
150
+ description=f"Auto-generated format for {display_name} (Create).",
151
+ formats=[Format(types=types, columns=columns)],
152
+ action=action
153
+ )
154
+ results[set_name] = fs
155
+
156
+ return results
157
+
158
+ def save_generated_format_sets(
159
+ commands_json_path: str | Path,
160
+ out_file: str | Path,
161
+ *,
162
+ include_only_create: bool = True,
163
+ default_types: Optional[List[str]] = None
164
+ ) -> Path:
165
+ """Build and save to a JSON file loadable by _output_formats.load_output_format_sets."""
166
+ sets = build_format_sets_from_commands(
167
+ commands_json_path,
168
+ include_only_create=include_only_create,
169
+ default_types=default_types,
170
+ )
171
+ out = Path(out_file)
172
+ out.parent.mkdir(parents=True, exist_ok=True)
173
+ sets.save_to_json(str(out))
174
+ logger.info(f"Saved {len(sets)} generated format sets to {out}")
175
+ return out
176
+
177
+ # New: expose a simple alias that returns a FormatSetDict directly
178
+ # for callers that want the in-memory object rather than JSON.
179
+ def generate_format_sets(
180
+ commands_json_path: str | Path,
181
+ *,
182
+ include_only_create: bool = True,
183
+ default_types: Optional[List[str]] = None,
184
+ ):
185
+ """Return a FormatSetDict built from commands.json (no file I/O)."""
186
+ return build_format_sets_from_commands(
187
+ commands_json_path,
188
+ include_only_create=include_only_create,
189
+ default_types=default_types,
190
+ )
191
+
192
+ # New: merge generated sets into the builtin registry
193
+
194
+ def merge_generated_format_sets(
195
+ commands_json_path: str | Path,
196
+ *,
197
+ include_only_create: bool = True,
198
+ default_types: Optional[List[str]] = None,
199
+ ) -> int:
200
+ """Build a FormatSetDict and merge into pyegeria._output_formats.output_format_sets.
201
+ Returns the number of sets merged/added.
202
+ """
203
+ from pyegeria._output_formats import output_format_sets # local import to avoid cycles on tooling
204
+
205
+ gen = build_format_sets_from_commands(
206
+ commands_json_path,
207
+ include_only_create=include_only_create,
208
+ default_types=default_types,
209
+ )
210
+ count = 0
211
+ for name, fs in gen.items():
212
+ output_format_sets[name] = fs
213
+ count += 1
214
+ logger.info(f"Merged {count} generated format sets into built-ins")
215
+ return count
216
+
217
+
218
+ def _py_literal(value):
219
+ """Render a Python literal safely for strings, lists, dicts, bools, None, numbers."""
220
+ if isinstance(value, str):
221
+ return repr(value)
222
+ if isinstance(value, (int, float)):
223
+ return str(value)
224
+ if value is True:
225
+ return "True"
226
+ if value is False:
227
+ return "False"
228
+ if value is None:
229
+ return "None"
230
+ if isinstance(value, list):
231
+ return "[" + ", ".join(_py_literal(v) for v in value) + "]"
232
+ if isinstance(value, dict):
233
+ # Render dict with stable key ordering
234
+ items = ", ".join(f"{_py_literal(k)}: {_py_literal(v)}" for k, v in sorted(value.items(), key=lambda kv: str(kv[0]).lower()))
235
+ return "{" + items + "}"
236
+ # Fallback
237
+ return repr(value)
238
+
239
+
240
+ def _format_column(col: Column) -> str:
241
+ parts = [f"name={_py_literal(getattr(col, 'name', ''))}", f"key={_py_literal(getattr(col, 'key', ''))}"]
242
+ fmt = getattr(col, 'format', None)
243
+ if fmt not in (None, False):
244
+ parts.append(f"format={_py_literal(bool(fmt))}")
245
+ return "Column(" + ", ".join(parts) + ")"
246
+
247
+
248
+ def _format_action(ap: ActionParameter | None) -> str:
249
+ if not ap:
250
+ return "None"
251
+ parts = [
252
+ f"function={_py_literal(getattr(ap, 'function', ''))}",
253
+ ]
254
+ req = getattr(ap, 'required_params', None)
255
+ if req:
256
+ parts.append(f"required_params={_py_literal(list(req))}")
257
+ opt = getattr(ap, 'optional_params', None)
258
+ if opt:
259
+ parts.append(f"optional_params={_py_literal(list(opt))}")
260
+ spec = getattr(ap, 'spec_params', None)
261
+ if spec:
262
+ parts.append(f"spec_params={_py_literal(dict(spec))}")
263
+ return "ActionParameter(" + ", ".join(parts) + ")"
264
+
265
+
266
+ def _format_format(fmt: Format) -> str:
267
+ cols = getattr(fmt, 'columns', []) or []
268
+ types = getattr(fmt, 'types', []) or []
269
+ col_code = ", ".join(_format_column(c) for c in cols)
270
+ parts = [f"types={_py_literal(list(types))}", f"columns=[{col_code}]"]
271
+ return "Format(" + ", ".join(parts) + ")"
272
+
273
+
274
+ def _format_formatset(fs: FormatSet) -> str:
275
+ parts = []
276
+ if getattr(fs, 'target_type', None):
277
+ parts.append(f"target_type={_py_literal(fs.target_type)}")
278
+ if getattr(fs, 'heading', None):
279
+ parts.append(f"heading={_py_literal(fs.heading)}")
280
+ if getattr(fs, 'description', None):
281
+ parts.append(f"description={_py_literal(fs.description)}")
282
+ aliases = getattr(fs, 'aliases', None)
283
+ if aliases:
284
+ parts.append(f"aliases={_py_literal(list(aliases))}")
285
+ ann = getattr(fs, 'annotations', None)
286
+ if ann:
287
+ parts.append(f"annotations={_py_literal(dict(ann))}")
288
+ # formats
289
+ fmts = getattr(fs, 'formats', []) or []
290
+ fmt_code = ", ".join(_format_format(f) for f in fmts)
291
+ parts.append(f"formats=[{fmt_code}]")
292
+ # actions
293
+ action = getattr(fs, 'action', None)
294
+ if action:
295
+ parts.append(f"action={_format_action(action)}")
296
+ get_add = getattr(fs, 'get_additional_props', None)
297
+ if get_add:
298
+ parts.append(f"get_additional_props={_format_action(get_add)}")
299
+ return "FormatSet(" + ", ".join(parts) + ")"
300
+
301
+
302
+ def format_sets_to_python_code(sets: FormatSetDict, var_name: str = "generated_format_sets") -> str:
303
+ """Render a FormatSetDict into a Python module string defining `var_name`."""
304
+ header = (
305
+ "# Auto-generated by gen_format_sets.py\n"
306
+ "from pyegeria._output_format_models import Column, Format, ActionParameter, FormatSet, FormatSetDict\n\n"
307
+ )
308
+ # Stable ordering by key
309
+ entries = []
310
+ for name in sorted(sets.keys(), key=lambda s: s.lower()):
311
+ fs = sets[name]
312
+ entries.append(f" {_py_literal(name)}: {_format_formatset(fs)}")
313
+ body = f"{var_name} = FormatSetDict({{\n" + ",\n".join(entries) + "\n})\n"
314
+ return header + body
315
+
316
+
317
+ def save_generated_format_sets_code(
318
+ commands_json_path: str | Path,
319
+ out_file: str | Path,
320
+ *,
321
+ include_only_create: bool = True,
322
+ default_types: Optional[List[str]] = None,
323
+ var_name: str = "generated_format_sets",
324
+ ) -> Path:
325
+ """Build FormatSetDict and save as Python code to out_file."""
326
+ sets = build_format_sets_from_commands(
327
+ commands_json_path,
328
+ include_only_create=include_only_create,
329
+ default_types=default_types,
330
+ )
331
+ code = format_sets_to_python_code(sets, var_name=var_name)
332
+ out = Path(out_file)
333
+ out.parent.mkdir(parents=True, exist_ok=True)
334
+ out.write_text(code, encoding="utf-8")
335
+ logger.info(f"Saved Python code for {len(sets)} generated format sets to {out}")
336
+ return out
337
+
338
+
339
+ def main():
340
+ parser = argparse.ArgumentParser(description="Generate FormatSets from commands.json")
341
+ parser.add_argument("commands_json", nargs="?", help="Path to commands.json")
342
+ parser.add_argument("output_json", nargs="?", help="Path to output JSON file (when --emit json)")
343
+ parser.add_argument("--emit", choices=["json", "dict", "code"], default="json",
344
+ help="Choose output mode: save JSON file, write Python code, or work with in-memory FormatSetDict")
345
+ parser.add_argument("--merge", action="store_true",
346
+ help="Merge generated sets into built-in registry (output_format_sets)")
347
+ parser.add_argument("--list", action="store_true",
348
+ help="When emitting dict (and/or after merge), list set names to stdout")
349
+ args = parser.parse_args()
350
+
351
+ try:
352
+ input_file = args.commands_json or Prompt.ask(
353
+ "Enter commands.json:", default="md_processing/data/commands.json"
354
+ )
355
+
356
+ if args.emit == "json":
357
+ # JSON output path
358
+ output_file = args.output_json or Prompt.ask(
359
+ "Output File:", default="md_processing/data/generated_format_sets.json"
360
+ )
361
+ save_generated_format_sets(input_file, output_file)
362
+ if args.merge:
363
+ # Also load and merge saved JSON into registry for convenience
364
+ from pyegeria._output_formats import load_output_format_sets
365
+ load_output_format_sets(output_file, merge=True)
366
+ logger.info("Merged saved JSON into built-in registry")
367
+ elif args.emit == "code":
368
+ # Python code output path
369
+ output_file = args.output_json or Prompt.ask(
370
+ "Output Python File:", default="md_processing/data/generated_format_sets.py"
371
+ )
372
+ save_generated_format_sets_code(input_file, output_file)
373
+ if args.merge:
374
+ # Dynamically import and merge the generated variable
375
+ try:
376
+ import importlib.util
377
+ import sys as _sys
378
+ spec = importlib.util.spec_from_file_location("_gen_fs_module", output_file)
379
+ mod = importlib.util.module_from_spec(spec)
380
+ assert spec and spec.loader
381
+ spec.loader.exec_module(mod) # type: ignore
382
+ generated = getattr(mod, "generated_format_sets", None)
383
+ if generated:
384
+ from pyegeria._output_formats import output_format_sets
385
+ merged = 0
386
+ for name, fs in generated.items():
387
+ output_format_sets[name] = fs
388
+ merged += 1
389
+ logger.info(f"Merged {merged} generated format sets from code into built-ins")
390
+ else:
391
+ logger.warning("No 'generated_format_sets' found in generated code module")
392
+ except Exception as e:
393
+ logger.error(f"Failed to import and merge generated code: {e}")
394
+ else:
395
+ # Emit dict path: build in memory
396
+ sets = generate_format_sets(input_file)
397
+ logger.info(f"Generated {len(sets)} format sets (in-memory FormatSetDict)")
398
+ if args.merge:
399
+ from pyegeria._output_formats import output_format_sets
400
+ merged = 0
401
+ for name, fs in sets.items():
402
+ output_format_sets[name] = fs
403
+ merged += 1
404
+ logger.info(f"Merged {merged} generated format sets into built-ins")
405
+ if args.list:
406
+ # If merged, show names from the global registry that match the generated ones
407
+ names = sorted(list(sets.keys()))
408
+ print("Merged set names:")
409
+ for n in names:
410
+ print(f"- {n}")
411
+ else:
412
+ if args.list:
413
+ names = sorted(list(sets.keys()))
414
+ print(f"Generated set names ({len(names)}):")
415
+ for n in names:
416
+ print(f"- {n}")
417
+ except (KeyboardInterrupt, EOFError):
418
+ # Graceful exit when user cancels or stdin is not interactive
419
+ pass
420
+
421
+ if __name__ == "__main__":
422
+ main()
@@ -123,13 +123,30 @@ COLLECTIONS_LIST = ["List Collections", "View Collections", "List Digital Produc
123
123
  "List Context Event Collections", "View Context Event Collections",
124
124
  "List Name Space Collections", "View Name Space Collections",
125
125
  "List Event Set Collections", "View Event Set Collections",
126
- "List Naming Standard Rulesets", "View Naming Standard Rulesets",
126
+ "List Naming Standard Rulesets", "View Naming Standard Rulesets", "List External Reference",
127
+ "List Related Media", "List Cited Document", "List External Data Source", "List External Model Source",
127
128
  ]
128
129
 
129
130
  PROJECT_COMMANDS = ["Create Project", "Update Project", "Create Campaign", "Update Campaign",
130
131
  "Create Task", "Update Task", "Create Study Project", "Update Study Project",
131
132
  "Create Personal Project", "Update Personal Project"]
132
133
 
134
+ LINK_EXT_REF = ["Link External Reference", "Link Referenceable->External Reference", "Attach External Reference",
135
+ "Detach External Reference", "Detach External Reference Link", "Link External Data Source",
136
+ "Link External Model Source", "Detach External Data Source", "Detach External Model Source",]
137
+
138
+ LINK_MEDIA = ["Link Related Media", "Link Referenceable->Related Media", "Attach Related Media", "Attach Media Reference Link",
139
+ "Detach Related Media", "Detach Related Media Link", "Detach Media Reference Link"]
140
+
141
+ LINK_CITED_DOC = ["Link Cited Document", "Link Referenceable->Cited Document", "Attach Cited Document", "Attach Cited Document Link",
142
+ "Detach Cited Document", "Detach Cited Document Link",]
143
+
144
+ EXT_REF_UPSERT = ["Create External Reference", "Update External Reference",
145
+ "Create Related Media", "Update Related Media",
146
+ "Create Cited Document", "Update Cited Document",
147
+ "Create External Data Source", "Update External Data Source", "Create External Model Source",
148
+ "Update External Model Source",]
149
+ EXT_REF_COMMANDS = EXT_REF_UPSERT + LINK_EXT_REF + LINK_MEDIA + LINK_CITED_DOC
133
150
  command_list = ["Provenance", "Create Glossary", "Update Glossary", "Create Term", "Update Term", "List Terms",
134
151
  "List Term Details", "List Glossary Terms", "List Term History", "List Term Revision History",
135
152
  "List Term Update History", "List Glossary Structure", "List Glossaries", "List Categories",
@@ -150,7 +167,7 @@ command_list = ["Provenance", "Create Glossary", "Update Glossary", "Create Term
150
167
  "Update Data Structure", "Create Data Dictionary", "Update Data Dictionary", "Create Data Dict",
151
168
  "Update Data Dict",
152
169
  "View Data Structures", "View Data Structure", "View Data Fields", "View Data Field",
153
- "View Dataa Classes", "View Data Class", "Create Data Class", "Update Data Class",
170
+ "View Data Classes", "View Data Class", "Create Data Class", "Update Data Class",
154
171
  "Create Digital Product", "Create Data Product", "Update Digital Product", "Update Data Product",
155
172
  "Create Agreement", "Update Agreement",
156
173
  "Link Digital Products", "Link Product-Product", "Detach Digital Products", "Detach Product-Product",
@@ -198,6 +215,7 @@ command_list.extend(GOV_LINK_LIST)
198
215
  command_list.extend(COLLECTIONS_LIST)
199
216
  command_list.extend(SIMPLE_COLLECTIONS)
200
217
  command_list.extend(PROJECT_COMMANDS)
218
+ command_list.extend(EXT_REF_COMMANDS)
201
219
  command_list.extend(["Link Governance Response", "Detach Governance Response",
202
220
  "Link Governance Mechanism", "Detach Governance Mechanism"])
203
221
 
pyegeria/__init__.py CHANGED
@@ -61,7 +61,7 @@ from .project_manager import ProjectManager
61
61
  from .registered_info import RegisteredInfo
62
62
  from .runtime_manager_omvs import RuntimeManager
63
63
  from .server_operations import ServerOps
64
- from .solution_architect_omvs import SolutionArchitect
64
+ from .solution_architect import SolutionArchitect
65
65
  from .utils import body_slimmer, print_response, to_pascal_case, to_camel_case, camel_to_title_case
66
66
  from .valid_metadata_omvs import ValidMetadataManager
67
67
  from .x_action_author_omvs import ActionAuthor
pyegeria/_client_new.py CHANGED
@@ -360,18 +360,20 @@ class Client2:
360
360
  """Retrieve and return the bearer token"""
361
361
  return self.text_headers["Authorization"]
362
362
 
363
- def get_platform_origin(self):
364
- """Validate platform connectivity"""
363
+ def get_platform_origin(self) -> str:
364
+ """Return the platform origin string if reachable.
365
+
366
+ Historically this method returned a boolean; tests and helpers expect the actual origin text.
367
+ """
365
368
  origin_url = f"{self.platform_url}/open-metadata/platform-services/users/{self.user_id}/server-platform/origin"
366
369
  response = self.make_request("GET", origin_url, is_json=False)
367
370
  if response.status_code == 200:
368
- logger.success(f"Got response from {origin_url}\n Response: {response.text}")
369
- if response.text.split()[0] == "Egeria":
370
- return True
371
- else:
372
- return False
371
+ text = response.text.strip()
372
+ logger.success(f"Got response from {origin_url}\n Response: {text}")
373
+ return text
373
374
  else:
374
375
  logger.info(f"Got response from {origin_url}\n status_code: {response.status_code}")
376
+ return ""
375
377
 
376
378
  # @logger.catch
377
379
  def make_request(