pyegeria 5.4.0.22__py3-none-any.whl → 5.4.0.24__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 (130) hide show
  1. commands/cat/.DS_Store +0 -0
  2. commands/cat/.env +8 -0
  3. commands/cat/debug_log +2046 -465
  4. commands/cat/debug_log.2025-08-15_09-14-07_444802.zip +0 -0
  5. commands/cat/debug_log.2025-08-16_10-21-59_388912.zip +0 -0
  6. commands/cat/debug_log.log +0 -0
  7. commands/cat/dr_egeria_md.py +16 -3
  8. commands/cat/list_collections.py +15 -6
  9. commands/cat/list_format_set.py +90 -85
  10. commands/cli/debug_log.log +0 -0
  11. commands/ops/logs/pyegeria.log +0 -0
  12. md_processing/.DS_Store +0 -0
  13. md_processing/__init__.py +5 -3
  14. md_processing/data/commands.json +8310 -903
  15. md_processing/dr-egeria-outbox/Collections-2025-08-12-13-30-37.md +163 -0
  16. md_processing/dr-egeria-outbox/Collections-2025-08-12-13-35-58.md +474 -0
  17. md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +8 -0
  18. md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +873 -0
  19. md_processing/dr_egeria_inbox/arch_test.md +57 -0
  20. md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
  21. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
  22. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
  23. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
  24. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
  25. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
  26. md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
  27. md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
  28. md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
  29. md_processing/dr_egeria_inbox/cat_test.md +100 -0
  30. md_processing/dr_egeria_inbox/collections.md +39 -0
  31. md_processing/dr_egeria_inbox/data_designer_debug.log +6 -0
  32. md_processing/dr_egeria_inbox/data_designer_out.md +60 -0
  33. md_processing/dr_egeria_inbox/data_designer_search_test.md +11 -0
  34. md_processing/dr_egeria_inbox/data_field.md +54 -0
  35. md_processing/dr_egeria_inbox/data_spec.md +77 -0
  36. md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
  37. md_processing/dr_egeria_inbox/data_test.md +179 -0
  38. md_processing/dr_egeria_inbox/data_test2.md +429 -0
  39. md_processing/dr_egeria_inbox/data_test3.md +462 -0
  40. md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +124 -0
  41. md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
  42. md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
  43. md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +313 -0
  44. md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
  45. md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
  46. md_processing/dr_egeria_inbox/generated_help_report.md +9 -0
  47. md_processing/dr_egeria_inbox/glossary_list.md +5 -0
  48. md_processing/dr_egeria_inbox/glossary_search_test.md +40 -0
  49. md_processing/dr_egeria_inbox/glossary_test1.md +324 -0
  50. md_processing/dr_egeria_inbox/gov_def.md +482 -0
  51. md_processing/dr_egeria_inbox/gov_def2.md +447 -0
  52. md_processing/dr_egeria_inbox/img.png +0 -0
  53. md_processing/dr_egeria_inbox/product.md +162 -0
  54. md_processing/dr_egeria_inbox/rel.md +8 -0
  55. md_processing/dr_egeria_inbox/sb.md +119 -0
  56. md_processing/dr_egeria_inbox/solution-components.md +136 -0
  57. md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
  58. md_processing/dr_egeria_inbox/synonym_test.md +42 -0
  59. md_processing/dr_egeria_inbox/t2.md +268 -0
  60. md_processing/dr_egeria_outbox/.obsidian/app.json +1 -0
  61. md_processing/dr_egeria_outbox/.obsidian/appearance.json +1 -0
  62. md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +6 -0
  63. md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +31 -0
  64. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +10 -0
  65. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +4459 -0
  66. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +10 -0
  67. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +3 -0
  68. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  69. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  70. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  71. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +500 -0
  72. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  73. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  74. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +37 -0
  75. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
  76. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +220 -0
  77. md_processing/dr_egeria_outbox/.obsidian/types.json +28 -0
  78. md_processing/dr_egeria_outbox/.obsidian/workspace.json +220 -0
  79. md_processing/dr_egeria_outbox/Untitled.canvas +1 -0
  80. md_processing/dr_egeria_outbox/monday/processed-2025-07-14 12:38-data_designer_out.md +663 -0
  81. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 10:52-generated_help_report.md +2744 -0
  82. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 18:38-collections.md +62 -0
  83. md_processing/dr_egeria_outbox/monday/processed-2025-08-01 11:34-gov_def.md +444 -0
  84. md_processing/dr_egeria_outbox/monday/processed-2025-08-17 21:04-product.md +97 -0
  85. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 14:55-product.md +77 -0
  86. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:05-product.md +75 -0
  87. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:11-product.md +74 -0
  88. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 20:40-collections.md +49 -0
  89. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +719 -0
  90. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +41 -0
  91. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +33 -0
  92. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +192 -0
  93. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +486 -0
  94. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +486 -0
  95. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +486 -0
  96. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +486 -0
  97. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +486 -0
  98. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +486 -0
  99. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +486 -0
  100. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-16 19:15-gov_def2.md +527 -0
  101. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 12:08-gov_def2.md +527 -0
  102. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 14:27-gov_def2.md +485 -0
  103. md_processing/md_commands/governance_officer_commands.py +291 -150
  104. md_processing/md_commands/product_manager_commands.py +309 -401
  105. md_processing/md_processing_utils/common_md_proc_utils.py +110 -7
  106. md_processing/md_processing_utils/common_md_utils.py +112 -26
  107. md_processing/md_processing_utils/debug_log.log +0 -0
  108. md_processing/md_processing_utils/md_processing_constants.py +8 -5
  109. md_processing/md_processing_utils/solution_architect_log.log +0 -0
  110. pyegeria/.DS_Store +0 -0
  111. pyegeria/__init__.py +3 -3
  112. pyegeria/_client_new.py +48 -51
  113. pyegeria/_exceptions_new.py +6 -0
  114. pyegeria/_output_format_models.py +22 -17
  115. pyegeria/_output_formats.py +122 -34
  116. pyegeria/collection_manager.py +154 -50
  117. pyegeria/collection_manager_omvs.py +47 -18
  118. pyegeria/egeria_cat_client.py +1 -1
  119. pyegeria/egeria_client.py +6 -0
  120. pyegeria/egeria_tech_client.py +6 -1
  121. pyegeria/governance_officer.py +2513 -0
  122. pyegeria/load_config.py +1 -1
  123. pyegeria/models.py +48 -5
  124. pyegeria/output_formatter.py +298 -79
  125. pyegeria/utils.py +1 -1
  126. {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.24.dist-info}/METADATA +1 -1
  127. {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.24.dist-info}/RECORD +130 -30
  128. {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.24.dist-info}/LICENSE +0 -0
  129. {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.24.dist-info}/WHEEL +0 -0
  130. {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.24.dist-info}/entry_points.txt +0 -0
pyegeria/_client_new.py CHANGED
@@ -476,7 +476,8 @@ class Client2:
476
476
  except (HTTPStatusError, httpx.HTTPStatusError, httpx.RequestError) as e:
477
477
  # context["caught_exception"] = e
478
478
  # context['HTTPStatusCode'] = e.response.status_code
479
- additional_info = {"userid": self.user_id}
479
+ additional_info = {"userid": self.user_id, "reason": response.text}
480
+
480
481
  raise PyegeriaClientException(response, context, additional_info, e)
481
482
  #
482
483
  # except json.JSONDecodeError as e:
@@ -736,20 +737,20 @@ class Client2:
736
737
  return elements
737
738
 
738
739
  def validate_new_element_request(self, body: dict | NewElementRequestBody,
739
- prop: str) -> NewElementRequestBody | None:
740
+ prop: list[str]) -> NewElementRequestBody | None:
740
741
  if isinstance(body, NewElementRequestBody):
741
- if body.properties.class_ == prop:
742
- validated_body = body
743
- else:
744
- raise PyegeriaInvalidParameterException(additional_info=
745
- {"reason": "unexpected property class name"})
742
+ # if body.properties.class_ in prop:
743
+ validated_body = body
744
+ # else:
745
+ # raise PyegeriaInvalidParameterException(additional_info=
746
+ # {"reason": "unexpected property class name"})
746
747
 
747
748
  elif isinstance(body, dict):
748
- if body.get("properties", {}).get("class", "") == prop:
749
- validated_body = self._new_element_request_adapter.validate_python(body)
750
- else:
751
- raise PyegeriaInvalidParameterException(additional_info=
752
- {"reason": "unexpected property class name"})
749
+ # if body.get("properties", {}).get("class", "") == prop:
750
+ validated_body = self._new_element_request_adapter.validate_python(body)
751
+ # else:
752
+ # raise PyegeriaInvalidParameterException(additional_info=
753
+ # {"reason": "unexpected property class name"})
753
754
  else:
754
755
  return None
755
756
  return validated_body
@@ -780,12 +781,12 @@ class Client2:
780
781
  validated_body = body
781
782
  elif isinstance(body, dict):
782
783
  validated_body = self._delete_request_adapter.validate_python(body)
783
- else:
784
+ else: # handle case where body not provided
784
785
  body= {
785
786
  "class": "DeleteRequestBody",
786
787
  "cascadeDelete": cascade_delete
787
788
  }
788
- validated_body= DeleteRequestBody.validate_python(body)
789
+ validated_body= DeleteRequestBody.model_validate(body)
789
790
  return validated_body
790
791
 
791
792
  def validate_update_element_request(self, body: dict | UpdateElementRequestBody,
@@ -798,17 +799,17 @@ class Client2:
798
799
  {"reason": "unexpected property class name"})
799
800
 
800
801
  elif isinstance(body, dict):
801
- if body.get("properties", {}).get("class", "") in prop:
802
- validated_body = self._update_element_request_adapter.validate_python(body)
803
- else:
804
- raise PyegeriaInvalidParameterException(additional_info=
805
- {"reason": "unexpected property class name"})
802
+ # if body.get("properties", {}).get("class", "") in prop:
803
+ validated_body = self._update_element_request_adapter.validate_python(body)
804
+ # else:
805
+ # raise PyegeriaInvalidParameterException(additional_info=
806
+ # {"reason": "unexpected property class name"})
806
807
  else:
807
808
  validated_body = None
808
809
  return validated_body
809
810
 
810
811
  def validate_update_status_request(self, status: str = None, body: dict | UpdateStatusRequestBody = None,
811
- prop: str = None) -> UpdateStatusRequestBody | None:
812
+ prop: list[str] = None) -> UpdateStatusRequestBody | None:
812
813
  if isinstance(body, UpdateStatusRequestBody):
813
814
  validated_body = body
814
815
 
@@ -820,33 +821,34 @@ class Client2:
820
821
  "class": "UpdateStatusRequestBody",
821
822
  "status": status
822
823
  }
823
- validated_body = UpdateStatusRequestBody.validate_python(body)
824
+ validated_body = UpdateStatusRequestBody.model_validate(body)
824
825
  else:
825
826
  raise PyegeriaInvalidParameterException(additional_info={"reason": "invalid parameters"})
826
827
 
827
828
  return validated_body
828
829
 
829
830
  def validate_update_relationship_request(self, body: dict | UpdateRelationshipRequestBody,
830
- prop: str) -> UpdateRelationshipRequestBody | None:
831
+ prop: [str]) -> UpdateRelationshipRequestBody | None:
831
832
  if isinstance(body, UpdateRelationshipRequestBody):
832
- if body.properties.class_ == prop:
833
- validated_body = body
834
- else:
835
- raise PyegeriaInvalidParameterException(additional_info=
836
- {"reason": "unexpected property class name"})
833
+ # if body.properties.class_ == prop:
834
+ validated_body = body
835
+ # else:
836
+ # raise PyegeriaInvalidParameterException(additional_info=
837
+ # {"reason": "unexpected property class name"})
837
838
 
838
839
  elif isinstance(body, dict):
839
- if body.get("properties", {}).get("class", "") == prop:
840
- validated_body = self._update_relationship_request_adapter.validate_python(body)
841
- else:
842
- raise PyegeriaInvalidParameterException(additional_info=
843
- {"reason": "unexpected property class name"})
840
+ # if body.get("properties", {}).get("class", "") == prop:
841
+ validated_body = self._update_relationship_request_adapter.validate_python(body)
842
+ # else:
843
+ # raise PyegeriaInvalidParameterException(additional_info=
844
+ # {"reason": "unexpected property class name"})
844
845
  else:
845
846
  validated_body = None
846
847
  return validated_body
847
848
 
848
849
  async def _async_find_request(self, url: str, _type: str, _gen_output: Callable[..., Any],
849
850
  search_string: str = '*', classification_names: list[str] = None,
851
+ metadata_element_types: list[str] = None,
850
852
  starts_with: bool = True, ends_with: bool = False, ignore_case: bool = False,
851
853
  start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
852
854
  output_format_set: str | dict = None,
@@ -867,13 +869,14 @@ class Client2:
867
869
  "start_from": start_from,
868
870
  "page_size": page_size,
869
871
  "include_only_classified_elements": classification_names,
872
+ "metadata_element_subtype_names": metadata_element_types,
870
873
  }
871
874
  validated_body = SearchStringRequestBody.model_validate(body)
872
875
 
873
- classification_names = validated_body.include_only_classified_elements
874
- classification_name = classification_names[0] if classification_names else _type
876
+ # classification_names = validated_body.include_only_classified_elements
877
+ # element_type_name = classification_names[0] if classification_names else _type
875
878
 
876
- json_body = validated_body.model_dump_json(indent=2)
879
+ json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
877
880
 
878
881
  response = await self._async_make_request("POST", url, json_body)
879
882
  elements = response.json().get("elements", NO_ELEMENTS_FOUND)
@@ -883,7 +886,7 @@ class Client2:
883
886
 
884
887
  if output_format != 'JSON': # return a simplified markdown representation
885
888
  # logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
886
- return _gen_output(elements, search_string, classification_name,
889
+ return _gen_output(elements, search_string, _type,
887
890
  output_format, output_format_set)
888
891
  return elements
889
892
 
@@ -908,8 +911,8 @@ class Client2:
908
911
  }
909
912
  validated_body = FilterRequestBody.model_validate(body)
910
913
 
911
- classification_names = validated_body.include_only_classified_elements
912
- classification_name = classification_names[0] if classification_names else _type
914
+ # classification_names = validated_body.include_only_classified_elements
915
+ # element_type_name = classification_names[0] if classification_names else _type
913
916
 
914
917
  json_body = validated_body.model_dump_json(indent=2)
915
918
 
@@ -921,7 +924,7 @@ class Client2:
921
924
 
922
925
  if output_format != 'JSON': # return a simplified markdown representation
923
926
  logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
924
- return _gen_output(elements, filter_string, classification_name,
927
+ return _gen_output(elements, filter_string, _type,
925
928
  output_format, output_format_set)
926
929
  return elements
927
930
 
@@ -972,7 +975,10 @@ class Client2:
972
975
  json_body = validated_body.model_dump_json(indent=2)
973
976
 
974
977
  response = await self._async_make_request("POST", url, json_body)
975
- elements = response.json().get("elements", NO_ELEMENTS_FOUND)
978
+ elements = response.json().get("elements", None)
979
+ if elements is None:
980
+ elements = response.json().get("element", NO_ELEMENTS_FOUND)
981
+
976
982
  if type(elements) is str:
977
983
  logger.info(NO_ELEMENTS_FOUND)
978
984
  return NO_ELEMENTS_FOUND
@@ -983,7 +989,7 @@ class Client2:
983
989
  output_format, output_format_set)
984
990
  return elements
985
991
 
986
- async def _async_create_element_body_request(self, url: str, prop: str,
992
+ async def _async_create_element_body_request(self, url: str, prop: list[str],
987
993
  body: dict | NewElementRequestBody = None) -> str:
988
994
  validated_body = self.validate_new_element_request(body, prop)
989
995
  json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
@@ -1008,7 +1014,7 @@ class Client2:
1008
1014
  response = await self._async_make_request("POST", url, json_body)
1009
1015
  logger.info(response.json())
1010
1016
 
1011
- async def _async_new_relationship_request(self, url: str, prop: str,
1017
+ async def _async_new_relationship_request(self, url: str, prop: list[str],
1012
1018
  body: dict | NewRelationshipRequestBody = None) -> None:
1013
1019
  validated_body = self.validate_new_relationship_request(body, prop)
1014
1020
  if validated_body:
@@ -1029,15 +1035,6 @@ class Client2:
1029
1035
  await self._async_make_request("POST", url)
1030
1036
 
1031
1037
 
1032
- async def _async_new_relationship_request(self, url: str, prop: str,
1033
- body: dict | NewRelationshipRequestBody = None) -> None:
1034
- validated_body = self.validate_new_relationship_request(body, prop)
1035
- if validated_body:
1036
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
1037
- logger.info(json_body)
1038
- await self._async_make_request("POST", url, json_body)
1039
- else:
1040
- await self._async_make_request("POST", url)
1041
1038
 
1042
1039
 
1043
1040
 
@@ -374,6 +374,11 @@ def print_exception_table(e: PyegeriaException):
374
374
  def print_basic_exception(e: PyegeriaException):
375
375
  """Prints the exception response"""
376
376
  related_code = e.related_http_code if hasattr(e, "related_http_code") else ""
377
+ http_reason = e.response.text if e.response else ""
378
+ table = Table(title=f"Exception: {e.__class__.__name__}", show_lines=True, header_style="bold", box=box.HEAVY_HEAD)
379
+ table.caption = e.pyegeria_code
380
+ table.add_column("Facet", justify="center")
381
+ table.add_column("Item", justify="left", width=80)
377
382
  related_response = e.response.json() if e.response else ""
378
383
  table = Table(title=f"Exception: {e.__class__.__name__}", show_lines=True, header_style="bold", box=box.HEAVY_HEAD)
379
384
  table.caption = e.pyegeria_code
@@ -382,6 +387,7 @@ def print_basic_exception(e: PyegeriaException):
382
387
 
383
388
  if isinstance(e, PyegeriaException):
384
389
  table.add_row("HTTP Code", str(e.response_code))
390
+ table.add_row("HTTP Reason", str(http_reason))
385
391
  table.add_row("Egeria Code", str(related_code))
386
392
  table.add_row("Caller Method", e.context.get("caller method", "---")) if e.context else ""
387
393
  table.add_row("Request URL", str(e.response_url))
@@ -175,14 +175,16 @@ class FormatSet(BaseModel):
175
175
  aliases: Alternative names that can be used to reference this format set
176
176
  annotations: Additional metadata, like wiki links
177
177
  formats: A list of format configurations
178
- action: Optional actions associated with the format set
178
+ action: Optional action associated with the format set
179
+ get_additional_props: Optional action used to retrieve additional properties for a format set
179
180
  """
180
181
  heading: str
181
182
  description: str
182
183
  aliases: List[str] = Field(default_factory=list)
183
184
  annotations: Dict[str, List[str]] = Field(default_factory=dict)
184
185
  formats: List[Union[Format, Dict[str, Any]]]
185
- action: Optional[List[Union[ActionParameter, Dict[str, Any]]]] = None
186
+ action: Optional[Union[ActionParameter, Dict[str, Any]]] = None
187
+ get_additional_props: Optional[Union[ActionParameter, Dict[str, Any]]] = None
186
188
 
187
189
  @validator('formats', pre=True)
188
190
  def validate_formats(cls, v):
@@ -195,18 +197,20 @@ class FormatSet(BaseModel):
195
197
  result.append(item)
196
198
  return result
197
199
 
198
- @validator('action', pre=True)
199
- def validate_action(cls, v):
200
- """Convert dictionary actions to ActionParameter objects."""
200
+ @validator('action', 'get_additional_props', pre=True)
201
+ def validate_action_like(cls, v):
202
+ """Convert dictionary action-like fields to ActionParameter objects. Accepts legacy list shape."""
201
203
  if v is None:
202
204
  return None
203
- result = []
204
- for item in v:
205
- if isinstance(item, dict):
206
- result.append(ActionParameter(**item))
207
- else:
208
- result.append(item)
209
- return result
205
+ # Backward compatibility: if a list is provided, use the first element
206
+ if isinstance(v, list):
207
+ if not v:
208
+ return None
209
+ logger.warning("FormatSet.action/get_additional_props provided as a list; coercing first element to dict. This shape is deprecated.")
210
+ v = v[0]
211
+ if isinstance(v, dict):
212
+ return ActionParameter(**v)
213
+ return v
210
214
 
211
215
  def dict(self, *args, **kwargs):
212
216
  """Override dict method to convert nested objects back to dictionaries."""
@@ -215,11 +219,12 @@ class FormatSet(BaseModel):
215
219
  format if isinstance(format, dict) else format.dict()
216
220
  for format in self.formats
217
221
  ]
218
- if self.action:
219
- result['action'] = [
220
- action if isinstance(action, dict) else action.dict()
221
- for action in self.action
222
- ]
222
+ if self.action is not None:
223
+ result['action'] = self.action if isinstance(self.action, dict) else self.action.dict()
224
+ if self.get_additional_props is not None:
225
+ result['get_additional_props'] = (
226
+ self.get_additional_props if isinstance(self.get_additional_props, dict) else self.get_additional_props.dict()
227
+ )
223
228
  return result
224
229
 
225
230
  def get(self, key, default=None):
@@ -70,9 +70,11 @@ from pathlib import Path
70
70
  from typing import Dict, List, Optional, Union, Any
71
71
  from loguru import logger
72
72
 
73
- from pyegeria._output_format_models import Column, Format, ActionParameter, FormatSet, FormatSetDict, save_format_sets_to_json, load_format_sets_from_json
73
+ from pyegeria._output_format_models import (Column, Format, ActionParameter, FormatSet, FormatSetDict,
74
+ save_format_sets_to_json, load_format_sets_from_json)
74
75
  from pyegeria.load_config import get_app_config
75
76
 
77
+
76
78
  # Get the configured value for the user format sets directory
77
79
  app_config = get_app_config()
78
80
  USER_FORMAT_SETS_DIR = os.path.expanduser(app_config.Environment.pyegeria_user_format_sets_dir)
@@ -90,6 +92,7 @@ COMMON_COLUMNS = [
90
92
 
91
93
  COMMON_METADATA_COLUMNS = [
92
94
  Column(name='GUID', key='guid', format=True),
95
+ Column(name='Type Name', key='type_name'),
93
96
  Column(name='Metadata Collection ID', key='metadata_collection_id', format=True),
94
97
  Column(name='Metadata Collection Name', key='metadata_collection_name', format=True),
95
98
  ]
@@ -120,6 +123,8 @@ COMMON_FORMATS_ALL = Format(
120
123
 
121
124
 
122
125
  COLLECTIONS_COLUMNS = COMMON_COLUMNS + [
126
+ Column(name='Type Name', key='type_name'),
127
+ Column(name='Classifications', key='classifications'),
123
128
  Column(name="Created By", key='created_by'),
124
129
  Column(name="Create Time", key='create_time'),
125
130
  Column(name="Updated By", key='updated_by'),
@@ -127,31 +132,71 @@ COLLECTIONS_COLUMNS = COMMON_COLUMNS + [
127
132
  ]
128
133
 
129
134
  COLLECTIONS_MEMBERS_COLUMNS = COLLECTIONS_COLUMNS + [
130
- Column(name="Members", key='members'),
135
+ Column(name="Containing Members", key='collection_members'),
136
+ Column(name="Member Of", key='member_of_collections')
131
137
  ]
132
138
 
133
139
  COLLECTION_DICT = Format(
134
140
  types=["DICT"],
135
- columns=COLLECTIONS_COLUMNS,
141
+ columns=COLLECTIONS_MEMBERS_COLUMNS + [
142
+ Column(name="GUID", key='GUID'),
143
+ ],
144
+ )
145
+
146
+ COLLECTION_REPORT = Format(
147
+ types=["REPORT"],
148
+ columns=COLLECTIONS_MEMBERS_COLUMNS + [
149
+ Column(name="GUID", key='GUID'),
150
+ Column(name="Mermaid", key='mermaid'),
151
+ ],
136
152
  )
137
153
 
138
154
  COLLECTION_TABLE = Format(
139
155
  types=["TABLE"],
140
- columns=COMMON_COLUMNS,
156
+ columns=COLLECTIONS_MEMBERS_COLUMNS,
141
157
  )
142
158
 
143
159
  GOVERNANCE_DEFINITIONS_COLUMNS = COMMON_COLUMNS + [
144
- Column(name="Document Identifier", key='document_identifier'),
145
- Column(name="Title", key='title'),
160
+ Column(name="Summary", key='summary'),
161
+ Column(name="Usage", key='usage'),
162
+ Column(name="Importance", key='importance'),
146
163
  Column(name="Scope", key='scope'),
164
+ Column(name="Type", key='type_name'),
165
+ ]
166
+ GOVERNANCE_DEFINITIONS_BASIC = [
167
+ Column(name="Type", key='type_name'),
168
+ Column(name='Qualified Name', key='qualified_name', format=True),
169
+ Column(name="GUID", key='guid', format=True),
147
170
  ]
148
-
149
171
  COMMON_ANNOTATIONS = {
150
172
  "wikilinks": ["[[Commons]]"]
151
173
  }
152
174
 
153
175
  # Modularized output_format_sets
154
176
  output_format_sets = FormatSetDict({
177
+ "Default": FormatSet(
178
+ heading="Default Base Attributes",
179
+ description="Was a valid combination of output_format_set and output_format provided?",
180
+ annotations={}, # No specific annotations
181
+ formats=[
182
+ Format(
183
+ types=["ALL"],
184
+ columns=COMMON_COLUMNS + COMMON_METADATA_COLUMNS + [
185
+ Column(name='Version Identifier', key='version_identifier'),
186
+ Column(name="Classifications", key='classifications'),
187
+ Column(name="Additional Properties", key='additional_properties'),
188
+ Column(name="Created By", key='created_by'),
189
+ Column(name="Create Time", key='create_time'),
190
+ Column(name="Updated By", key='updated_by'),
191
+ Column(name="Update Time", key='update_time'),
192
+ Column(name="Effective From", key='effective_from'),
193
+ Column(name="Effective To", key='effective_to'),
194
+ Column(name="Version", key='version'),
195
+ Column(name="Open Metadata Type Name", key='type_name'),
196
+ ],
197
+ )
198
+ ],
199
+ ),
155
200
  "Referenceable": FormatSet(
156
201
  heading="Common Attributes",
157
202
  description="Attributes that apply to all Referenceables.",
@@ -205,12 +250,12 @@ output_format_sets = FormatSetDict({
205
250
  aliases=["Collection", "RootCollection", "Folder", "ReferenceList", "HomeCollection",
206
251
  "ResultSet", "RecentAccess", "WorkItemList", "Namespace"],
207
252
  annotations=COMMON_ANNOTATIONS,
208
- formats=[COLLECTION_DICT, COLLECTION_TABLE, COMMON_FORMATS_ALL], # Reusing common formats
209
- action=[ActionParameter(
253
+ formats=[COLLECTION_DICT, COLLECTION_TABLE, COLLECTION_REPORT, COMMON_FORMATS_ALL], # Reusing common formats
254
+ action=ActionParameter(
210
255
  function="CollectionManager.find_collections",
211
256
  user_params=["search_string"],
212
257
  spec_params={},
213
- )]
258
+ )
214
259
  ),
215
260
 
216
261
  "CollectionMembers": FormatSet(
@@ -219,11 +264,11 @@ output_format_sets = FormatSetDict({
219
264
  aliases=["CollectionMember", "Member", "Members"],
220
265
  annotations= {"wikilinks": ["[[CollectionMembers]]"]},
221
266
  formats = [COLLECTION_DICT, COLLECTION_TABLE],
222
- action=[ActionParameter(
267
+ action=ActionParameter(
223
268
  function="CollectionManager.get_collection_members",
224
269
  user_params=["collection_guid"],
225
270
  spec_params={"output_format": "DICT"},
226
- )]
271
+ )
227
272
  ),
228
273
  "DigitalProducts": FormatSet(
229
274
  heading="Digital Product Information",
@@ -232,7 +277,7 @@ output_format_sets = FormatSetDict({
232
277
  annotations={},
233
278
  formats=[
234
279
  Format(
235
- types=["REPORT"],
280
+ types=["REPORT", "DICT", "TABLE"],
236
281
  columns=COMMON_COLUMNS + [
237
282
  Column(name="Status", key='status'),
238
283
  Column(name='Product Name', key='product_name'),
@@ -242,35 +287,65 @@ output_format_sets = FormatSetDict({
242
287
  Column(name='Next Version', key='next_version'),
243
288
  Column(name='Withdraw Date', key='withdraw_date'),
244
289
  Column(name='Members', key='members', format=True),
290
+ Column(name='Uses Products', key='uses_digital_products'),
291
+ Column(name='Used by Products', key='used_by_digital_products'),
245
292
  ],
246
293
  )
247
294
  ],
248
- action=[ActionParameter(
295
+ action=ActionParameter(
249
296
  function="CollectionManager.find_collections",
250
297
  user_params=["search_string"],
251
- spec_params={"initial_classifications": "DigitalProducts"},
252
- )]
298
+ spec_params={},
299
+ ),
300
+ get_additional_props=ActionParameter(
301
+ function="CollectionManager._extract_digital_product_properties",
302
+ user_params = [],
303
+ spec_params={},
304
+ )
253
305
  ),
254
306
 
255
307
  "Agreements": FormatSet(
256
308
  heading="General Agreement Information",
257
309
  description="Attributes generic to all Agreements.",
258
- aliases=["DataSharingAgreement"],
310
+ aliases=["DataSharingAgreement", "Agreement"],
259
311
  annotations={"wikilinks": ["[[Agreements]]", "[[Egeria]]"]},
260
- formats=[COMMON_FORMATS_ALL] # Reusing common formats and columns
312
+ formats=[
313
+ Format(
314
+ types=["REPORT", "DICT", "TABLE"],
315
+ columns=COMMON_COLUMNS + COMMON_HEADER_COLUMNS + [
316
+ Column(name='Identifier', key='identifier'),
317
+ Column(name='Support Level', key='support_level'),
318
+ Column(name='service Levels', key='service_levels'),
319
+ Column(name='Agreement Items', key='agreement_items', format=True),
320
+ Column(name='Members', key='members', format=True),
321
+ ]
322
+ )
323
+ ],
324
+ action=ActionParameter(
325
+ function="CollectionManager.find_collections",
326
+ user_params=["search_string"],
327
+ spec_params={"metadata_element_types": ["Agreement"]},
328
+ # spec_params={},
329
+ ),
330
+ get_additional_props=ActionParameter(
331
+ function="CollectionManager._extract_agreement_properties",
332
+ user_params=[],
333
+ spec_params={},
334
+ ),
261
335
  ),
262
336
 
337
+
263
338
  "DataDictionary": FormatSet(
264
339
  heading="Data Dictionary Information",
265
340
  description="Attributes useful to Data Dictionary.",
266
341
  aliases=["Data Dict", "Data Dictionary"],
267
342
  annotations={"wikilinks": ["[[Data Dictionary]]"]},
268
343
  formats=[COMMON_FORMATS_ALL], # Reusing common formats and columns
269
- action=[ActionParameter(
344
+ action=ActionParameter(
270
345
  function="CollectionManager.find_collections",
271
346
  user_params=["search_string"],
272
347
  spec_params={"initial_classifications": "DataDictionary"},
273
- )]
348
+ )
274
349
  ),
275
350
 
276
351
  "Data Specification": FormatSet(
@@ -285,11 +360,11 @@ output_format_sets = FormatSetDict({
285
360
  Column(name="Mermaid", key='mermaid'),
286
361
  ]),
287
362
  Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
288
- action=[ActionParameter(
363
+ action=ActionParameter(
289
364
  function="CollectionManager.find_collections",
290
365
  user_params=["search_string"],
291
366
  spec_params={"initial_classifications": "DataSpec"},
292
- )]
367
+ )
293
368
  ),
294
369
 
295
370
  "DataStruct": FormatSet(
@@ -298,11 +373,11 @@ output_format_sets = FormatSetDict({
298
373
  aliases=["Data Structure", "DataStructures", "Data Structures", "Data Struct", "DataStructure"],
299
374
  annotations={"wikilinks": ["[[Data Structure]]"]},
300
375
  formats=[Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
301
- action=[ActionParameter(
376
+ action=ActionParameter(
302
377
  function="DataDesigner.find_data_structures",
303
378
  user_params=["search_string"],
304
379
  spec_params={},
305
- )]
380
+ )
306
381
  ),
307
382
 
308
383
  "DataField": FormatSet(
@@ -311,11 +386,11 @@ output_format_sets = FormatSetDict({
311
386
  aliases=["Data Field", "Data Fields", "DataFields"],
312
387
  annotations={"wikilinks": ["[[Data Field]]"]},
313
388
  formats=[Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
314
- action=[ActionParameter(
389
+ action=ActionParameter(
315
390
  function="DataDesigner.find_data_fields",
316
391
  user_params=["search_string"],
317
392
  spec_params={},
318
- )]
393
+ )
319
394
  ),
320
395
 
321
396
  "Mandy-DataStruct": FormatSet(
@@ -329,24 +404,35 @@ output_format_sets = FormatSetDict({
329
404
  Format(types=["REPORT","MERMAID", "HTML"], columns=[Column(name='Display Name', key='display_name'),
330
405
  Column(name='Mermaid', key='mermaid'),]),
331
406
  ],
332
- action=[ActionParameter(
407
+ action=ActionParameter(
333
408
  function="DataDesigner.find_data_structures",
334
409
  user_params=["search_string"],
335
410
  spec_params={"output_format":"DICT"},
336
- )]
411
+ )
412
+ ),
413
+ "Governance Basics": FormatSet(
414
+ heading="Basic Governance-Definitions Information",
415
+ description="Core Attributes useful to Governance-Definitions.",
416
+ aliases=["BasicGovernance"],
417
+ annotations={"wikilinks": ["[[Governance]]"]},
418
+ formats=[Format(types=["ALL"], columns=GOVERNANCE_DEFINITIONS_BASIC)],
419
+ action=ActionParameter(
420
+ function="GovernanceOfficer.find_governance_definitions",
421
+ user_params=["search_string"],
422
+ spec_params={},
423
+ )
337
424
  ),
338
-
339
425
  "Governance Definitions": FormatSet(
340
426
  heading="Governance-Definitions Information",
341
427
  description="Attributes useful to Governance-Definitions.",
342
428
  aliases=["GovernanceDefinitions"],
343
429
  annotations={"wikilinks": ["[[Governance]]"]},
344
430
  formats=[Format(types=["ALL"], columns=GOVERNANCE_DEFINITIONS_COLUMNS)],
345
- action=[ActionParameter(
431
+ action=ActionParameter(
346
432
  function="GovernanceOfficer.find_governance_definitions",
347
- user_params=["search_filter"],
348
- spec_params={"output_format":"DICT"},
349
- )]
433
+ user_params=["search_string"],
434
+ spec_params={},
435
+ )
350
436
  ),
351
437
  })
352
438
 
@@ -390,7 +476,9 @@ def select_output_format_set(kind: str, output_type: str) -> dict | None:
390
476
  output_struct["annotations"] = element.annotations
391
477
  if element.action:
392
478
  # Convert ActionParameter to dictionary for backward compatibility
393
- output_struct["action"] = [action.dict() for action in element.action]
479
+ output_struct["action"] = element.action.dict()
480
+ if element.get_additional_props:
481
+ output_struct["get_additional_props"] = element.get_additional_props.dict()
394
482
 
395
483
  # If this was just a validation that the format set could be found then the output type is ANY - so just return.
396
484
  if output_type == "ANY":