pyegeria 5.3.9.9.3__py3-none-any.whl → 5.5.3.3__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.

Potentially problematic release.


This version of pyegeria might be problematic. Click here for more details.

Files changed (272) hide show
  1. commands/__init__.py +24 -0
  2. commands/cat/Dr-Egeria_md-orig.py +2 -2
  3. commands/cat/__init__.py +1 -17
  4. commands/cat/collection_actions.py +197 -0
  5. commands/cat/dr_egeria_command_help.py +372 -0
  6. commands/cat/dr_egeria_jupyter.py +7 -7
  7. commands/cat/dr_egeria_md.py +27 -182
  8. commands/cat/exp_list_glossaries.py +11 -14
  9. commands/cat/get_asset_graph.py +37 -267
  10. commands/cat/{get_collection.py → get_collection_tree.py} +10 -18
  11. commands/cat/get_project_dependencies.py +14 -14
  12. commands/cat/get_project_structure.py +15 -14
  13. commands/cat/get_tech_type_elements.py +16 -116
  14. commands/cat/glossary_actions.py +145 -298
  15. commands/cat/list_assets.py +3 -11
  16. commands/cat/list_cert_types.py +17 -63
  17. commands/cat/list_collections.py +46 -138
  18. commands/cat/list_deployed_catalogs.py +15 -27
  19. commands/cat/list_deployed_database_schemas.py +27 -43
  20. commands/cat/list_deployed_databases.py +16 -31
  21. commands/cat/list_deployed_servers.py +35 -54
  22. commands/cat/list_glossaries.py +18 -17
  23. commands/cat/list_projects.py +10 -12
  24. commands/cat/list_tech_type_elements.py +21 -37
  25. commands/cat/list_tech_types.py +13 -25
  26. commands/cat/list_terms.py +38 -79
  27. commands/cat/list_todos.py +4 -11
  28. commands/cat/list_user_ids.py +3 -10
  29. commands/cat/my_reports.py +559 -0
  30. commands/cat/run_report.py +394 -0
  31. commands/cat/run_report_orig.py +528 -0
  32. commands/cli/egeria.py +222 -247
  33. commands/cli/egeria_cat.py +68 -81
  34. commands/cli/egeria_my.py +13 -0
  35. commands/cli/egeria_ops.py +69 -74
  36. commands/cli/egeria_tech.py +17 -93
  37. commands/cli/ops_config.py +3 -6
  38. commands/{cat/list_categories.py → deprecated/list_data_designer.py} +53 -64
  39. commands/{cat/list_data_structures.py → deprecated/list_data_structures_full.py} +3 -6
  40. commands/deprecated/old_get_asset_graph.py +315 -0
  41. commands/my/__init__.py +0 -2
  42. commands/my/list_my_profile.py +27 -34
  43. commands/my/list_my_roles.py +1 -7
  44. commands/my/monitor_my_todos.py +1 -7
  45. commands/my/monitor_open_todos.py +6 -7
  46. commands/my/todo_actions.py +4 -5
  47. commands/ops/__init__.py +0 -2
  48. commands/ops/gov_server_actions.py +17 -21
  49. commands/ops/list_archives.py +17 -38
  50. commands/ops/list_catalog_targets.py +33 -40
  51. commands/ops/load_archive.py +35 -26
  52. commands/ops/{monitor_engine_activity_c.py → monitor_active_engine_activity.py} +51 -82
  53. commands/ops/{monitor_integ_daemon_status.py → monitor_daemon_status.py} +35 -55
  54. commands/ops/monitor_engine_activity.py +79 -77
  55. commands/ops/{monitor_gov_eng_status.py → monitor_engine_status.py} +10 -7
  56. commands/ops/monitor_platform_status.py +38 -50
  57. commands/ops/monitor_server_startup.py +6 -11
  58. commands/ops/monitor_server_status.py +7 -11
  59. commands/ops/orig_monitor_server_list.py +8 -8
  60. commands/ops/orig_monitor_server_status.py +1 -5
  61. commands/ops/refresh_integration_daemon.py +5 -5
  62. commands/ops/restart_integration_daemon.py +5 -5
  63. commands/ops/table_integ_daemon_status.py +6 -6
  64. commands/ops/x_engine_actions.py +7 -7
  65. commands/tech/__init__.py +0 -2
  66. commands/tech/{generic_actions.py → element_actions.py} +6 -11
  67. commands/tech/get_element_info.py +20 -29
  68. commands/tech/get_guid_info.py +23 -42
  69. commands/tech/get_tech_details.py +20 -35
  70. commands/tech/get_tech_type_template.py +28 -39
  71. commands/tech/list_all_om_type_elements.py +24 -30
  72. commands/tech/list_all_om_type_elements_x.py +22 -28
  73. commands/tech/list_all_related_elements.py +19 -28
  74. commands/tech/list_anchored_elements.py +22 -30
  75. commands/tech/list_asset_types.py +19 -24
  76. commands/tech/list_elements_by_classification_by_property_value.py +26 -32
  77. commands/tech/list_elements_by_property_value.py +19 -25
  78. commands/tech/list_elements_by_property_value_x.py +20 -28
  79. commands/tech/list_elements_for_classification.py +28 -41
  80. commands/tech/list_gov_action_processes.py +16 -27
  81. commands/tech/list_information_supply_chains.py +22 -30
  82. commands/tech/list_registered_services.py +14 -26
  83. commands/tech/list_related_elements_with_prop_value.py +15 -25
  84. commands/tech/list_related_specification.py +1 -4
  85. commands/tech/list_relationship_types.py +15 -25
  86. commands/tech/list_relationships.py +20 -36
  87. commands/tech/list_solution_blueprints.py +28 -33
  88. commands/tech/list_solution_components.py +23 -29
  89. commands/tech/list_solution_roles.py +21 -32
  90. commands/tech/list_tech_templates.py +51 -54
  91. commands/tech/list_valid_metadata_values.py +5 -9
  92. commands/tech/table_tech_templates.py +2 -6
  93. commands/tech/x_list_related_elements.py +1 -4
  94. examples/GeoSpatial Products Example.py +524 -0
  95. examples/Jupyter Notebooks/P-egeria-server-config.ipynb +2137 -0
  96. examples/Jupyter Notebooks/README.md +2 -0
  97. examples/Jupyter Notebooks/common/P-environment-check.ipynb +115 -0
  98. examples/Jupyter Notebooks/common/__init__.py +14 -0
  99. examples/Jupyter Notebooks/common/common-functions.ipynb +4694 -0
  100. examples/Jupyter Notebooks/common/environment-check.ipynb +52 -0
  101. examples/Jupyter Notebooks/common/globals.ipynb +184 -0
  102. examples/Jupyter Notebooks/common/globals.py +154 -0
  103. examples/Jupyter Notebooks/common/orig_globals.py +152 -0
  104. examples/format_sets/all_format_sets.json +910 -0
  105. examples/format_sets/custom_format_sets.json +268 -0
  106. examples/format_sets/subset_format_sets.json +187 -0
  107. examples/format_sets_save_load_example.py +291 -0
  108. examples/jacquard_data_sets.py +129 -0
  109. examples/output_formats_example.py +193 -0
  110. examples/test_jacquard_data_sets.py +54 -0
  111. examples/test_jacquard_data_sets_scenarios.py +94 -0
  112. md_processing/__init__.py +90 -0
  113. md_processing/command_dispatcher.py +33 -0
  114. md_processing/command_mapping.py +221 -0
  115. md_processing/data/commands/commands_data_designer.json +537 -0
  116. md_processing/data/commands/commands_external_reference.json +733 -0
  117. md_processing/data/commands/commands_feedback.json +155 -0
  118. md_processing/data/commands/commands_general.json +204 -0
  119. md_processing/data/commands/commands_glossary.json +218 -0
  120. md_processing/data/commands/commands_governance.json +3678 -0
  121. md_processing/data/commands/commands_product_manager.json +865 -0
  122. md_processing/data/commands/commands_project.json +642 -0
  123. md_processing/data/commands/commands_solution_architect.json +366 -0
  124. md_processing/data/commands.json +17568 -0
  125. md_processing/data/commands_working.json +30641 -0
  126. md_processing/data/gened_report_specs.py +6584 -0
  127. md_processing/data/generated_format_sets.json +6533 -0
  128. md_processing/data/generated_format_sets_old.json +4137 -0
  129. md_processing/data/generated_format_sets_old.py +45 -0
  130. md_processing/dr_egeria.py +182 -0
  131. md_processing/md_commands/__init__.py +3 -0
  132. md_processing/md_commands/data_designer_commands.py +1276 -0
  133. md_processing/md_commands/ext_ref_commands.py +530 -0
  134. md_processing/md_commands/feedback_commands.py +726 -0
  135. md_processing/md_commands/glossary_commands.py +684 -0
  136. md_processing/md_commands/governance_officer_commands.py +600 -0
  137. md_processing/md_commands/product_manager_commands.py +1266 -0
  138. md_processing/md_commands/project_commands.py +383 -0
  139. md_processing/md_commands/solution_architect_commands.py +1184 -0
  140. md_processing/md_commands/view_commands.py +295 -0
  141. md_processing/md_processing_utils/__init__.py +4 -0
  142. md_processing/md_processing_utils/common_md_proc_utils.py +1249 -0
  143. md_processing/md_processing_utils/common_md_utils.py +578 -0
  144. md_processing/md_processing_utils/determine_width.py +103 -0
  145. md_processing/md_processing_utils/extraction_utils.py +547 -0
  146. md_processing/md_processing_utils/gen_report_specs.py +643 -0
  147. md_processing/md_processing_utils/generate_dr_help.py +193 -0
  148. md_processing/md_processing_utils/generate_md_cmd_templates.py +144 -0
  149. md_processing/md_processing_utils/generate_md_templates.py +83 -0
  150. md_processing/md_processing_utils/md_processing_constants.py +1228 -0
  151. md_processing/md_processing_utils/message_constants.py +19 -0
  152. pyegeria/__init__.py +201 -443
  153. pyegeria/core/__init__.py +40 -0
  154. pyegeria/core/_base_platform_client.py +574 -0
  155. pyegeria/core/_base_server_client.py +573 -0
  156. pyegeria/core/_exceptions.py +457 -0
  157. pyegeria/core/_globals.py +60 -0
  158. pyegeria/core/_server_client.py +6073 -0
  159. pyegeria/core/_validators.py +257 -0
  160. pyegeria/core/config.py +654 -0
  161. pyegeria/{create_tech_guid_lists.py → core/create_tech_guid_lists.py} +0 -1
  162. pyegeria/core/load_config.py +37 -0
  163. pyegeria/core/logging_configuration.py +207 -0
  164. pyegeria/core/mcp_adapter.py +144 -0
  165. pyegeria/core/mcp_server.py +212 -0
  166. pyegeria/core/utils.py +405 -0
  167. pyegeria/deprecated/__init__.py +0 -0
  168. pyegeria/{_client.py → deprecated/_client.py} +62 -24
  169. pyegeria/{_deprecated_gov_engine.py → deprecated/_deprecated_gov_engine.py} +16 -16
  170. pyegeria/{classification_manager_omvs.py → deprecated/classification_manager_omvs.py} +1988 -1878
  171. pyegeria/deprecated/output_formatter_with_machine_keys.py +1127 -0
  172. pyegeria/{runtime_manager_omvs.py → deprecated/runtime_manager_omvs.py} +216 -229
  173. pyegeria/{valid_metadata_omvs.py → deprecated/valid_metadata_omvs.py} +93 -93
  174. pyegeria/{x_action_author_omvs.py → deprecated/x_action_author_omvs.py} +2 -3
  175. pyegeria/egeria_cat_client.py +25 -51
  176. pyegeria/egeria_client.py +140 -98
  177. pyegeria/egeria_config_client.py +48 -24
  178. pyegeria/egeria_tech_client.py +170 -83
  179. pyegeria/models/__init__.py +150 -0
  180. pyegeria/models/collection_models.py +168 -0
  181. pyegeria/models/models.py +654 -0
  182. pyegeria/omvs/__init__.py +84 -0
  183. pyegeria/omvs/action_author.py +342 -0
  184. pyegeria/omvs/actor_manager.py +5980 -0
  185. pyegeria/omvs/asset_catalog.py +842 -0
  186. pyegeria/omvs/asset_maker.py +2736 -0
  187. pyegeria/omvs/automated_curation.py +4403 -0
  188. pyegeria/omvs/classification_manager.py +11213 -0
  189. pyegeria/omvs/collection_manager.py +5780 -0
  190. pyegeria/omvs/community_matters_omvs.py +468 -0
  191. pyegeria/{core_omag_server_config.py → omvs/core_omag_server_config.py} +157 -157
  192. pyegeria/{data_designer_omvs.py → omvs/data_designer.py} +1991 -1691
  193. pyegeria/omvs/data_discovery.py +869 -0
  194. pyegeria/omvs/data_engineer.py +372 -0
  195. pyegeria/omvs/digital_business.py +1133 -0
  196. pyegeria/omvs/external_links.py +1752 -0
  197. pyegeria/omvs/feedback_manager.py +834 -0
  198. pyegeria/{full_omag_server_config.py → omvs/full_omag_server_config.py} +73 -69
  199. pyegeria/omvs/glossary_manager.py +3231 -0
  200. pyegeria/omvs/governance_officer.py +3009 -0
  201. pyegeria/omvs/lineage_linker.py +314 -0
  202. pyegeria/omvs/location_arena.py +1525 -0
  203. pyegeria/omvs/metadata_expert.py +668 -0
  204. pyegeria/omvs/metadata_explorer_omvs.py +2943 -0
  205. pyegeria/omvs/my_profile.py +1042 -0
  206. pyegeria/omvs/notification_manager.py +358 -0
  207. pyegeria/omvs/people_organizer.py +394 -0
  208. pyegeria/{platform_services.py → omvs/platform_services.py} +113 -193
  209. pyegeria/omvs/product_manager.py +1825 -0
  210. pyegeria/omvs/project_manager.py +1907 -0
  211. pyegeria/omvs/reference_data.py +1140 -0
  212. pyegeria/omvs/registered_info.py +334 -0
  213. pyegeria/omvs/runtime_manager.py +2817 -0
  214. pyegeria/omvs/schema_maker.py +446 -0
  215. pyegeria/{server_operations.py → omvs/server_operations.py} +27 -26
  216. pyegeria/omvs/solution_architect.py +6490 -0
  217. pyegeria/omvs/specification_properties.py +37 -0
  218. pyegeria/omvs/subject_area.py +1042 -0
  219. pyegeria/omvs/template_manager_omvs.py +236 -0
  220. pyegeria/omvs/time_keeper.py +1761 -0
  221. pyegeria/omvs/valid_metadata.py +3221 -0
  222. pyegeria/omvs/valid_metadata_lists.py +37 -0
  223. pyegeria/omvs/valid_type_lists.py +37 -0
  224. pyegeria/view/__init__.py +28 -0
  225. pyegeria/view/_output_format_models.py +514 -0
  226. pyegeria/view/_output_formats.py +14 -0
  227. pyegeria/view/base_report_formats.py +2719 -0
  228. pyegeria/view/dr_egeria_reports.py +56 -0
  229. pyegeria/view/format_set_executor.py +397 -0
  230. pyegeria/{md_processing_utils.py → view/md_processing_utils.py} +5 -5
  231. pyegeria/{mermaid_utilities.py → view/mermaid_utilities.py} +2 -154
  232. pyegeria/view/output_formatter.py +1297 -0
  233. pyegeria-5.5.3.3.dist-info/METADATA +218 -0
  234. pyegeria-5.5.3.3.dist-info/RECORD +241 -0
  235. {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.5.3.3.dist-info}/WHEEL +2 -1
  236. pyegeria-5.5.3.3.dist-info/entry_points.txt +103 -0
  237. pyegeria-5.5.3.3.dist-info/top_level.txt +4 -0
  238. commands/cat/.DS_Store +0 -0
  239. commands/cat/README.md +0 -16
  240. commands/cli/txt_custom_v2.tcss +0 -19
  241. commands/my/README.md +0 -17
  242. commands/ops/README.md +0 -24
  243. commands/ops/monitor_asset_events.py +0 -108
  244. commands/tech/README.md +0 -24
  245. pyegeria/.DS_Store +0 -0
  246. pyegeria/README.md +0 -35
  247. pyegeria/_globals.py +0 -47
  248. pyegeria/_validators.py +0 -385
  249. pyegeria/asset_catalog_omvs.py +0 -864
  250. pyegeria/automated_curation_omvs.py +0 -3765
  251. pyegeria/collection_manager_omvs.py +0 -2744
  252. pyegeria/dr.egeria spec.md +0 -9
  253. pyegeria/egeria_my_client.py +0 -56
  254. pyegeria/feedback_manager_omvs.py +0 -4573
  255. pyegeria/glossary_browser_omvs.py +0 -3728
  256. pyegeria/glossary_manager_omvs.py +0 -2440
  257. pyegeria/m_test.py +0 -118
  258. pyegeria/md_processing_helpers.py +0 -58
  259. pyegeria/md_processing_utils_orig.py +0 -1103
  260. pyegeria/metadata_explorer_omvs.py +0 -2326
  261. pyegeria/my_profile_omvs.py +0 -1022
  262. pyegeria/output_formatter.py +0 -389
  263. pyegeria/project_manager_omvs.py +0 -1933
  264. pyegeria/registered_info.py +0 -167
  265. pyegeria/solution_architect_omvs.py +0 -2156
  266. pyegeria/template_manager_omvs.py +0 -1414
  267. pyegeria/utils.py +0 -197
  268. pyegeria-5.3.9.9.3.dist-info/METADATA +0 -72
  269. pyegeria-5.3.9.9.3.dist-info/RECORD +0 -143
  270. pyegeria-5.3.9.9.3.dist-info/entry_points.txt +0 -99
  271. /pyegeria/{_exceptions.py → deprecated/_exceptions.py} +0 -0
  272. {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.5.3.3.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,578 @@
1
+ """
2
+ This file contains general utility functions for processing Egeria Markdown
3
+ """
4
+ import os
5
+ import re
6
+ from datetime import datetime
7
+ from typing import Any
8
+ from loguru import logger
9
+ from rich import print
10
+ from rich.console import Console
11
+ from rich.markdown import Markdown
12
+
13
+ from pyegeria import ServerClient, PyegeriaException
14
+ from pyegeria.core.utils import (camel_to_title_case)
15
+ from pyegeria.core._globals import DEBUG_LEVEL
16
+ from md_processing.md_processing_utils.message_constants import message_types
17
+
18
+ # Constants
19
+ EGERIA_METADATA_STORE = os.environ.get("EGERIA_METADATA_STORE", "active-metadata-store")
20
+ EGERIA_KAFKA_ENDPOINT = os.environ.get("KAFKA_ENDPOINT", "localhost:9092")
21
+ EGERIA_PLATFORM_URL = os.environ.get("EGERIA_PLATFORM_URL", "https://localhost:9443")
22
+ EGERIA_VIEW_SERVER = os.environ.get("EGERIA_VIEW_SERVER", "view-server")
23
+ EGERIA_VIEW_SERVER_URL = os.environ.get("EGERIA_VIEW_SERVER_URL", "https://localhost:9443")
24
+ EGERIA_INTEGRATION_DAEMON = os.environ.get("EGERIA_INTEGRATION_DAEMON", "integration-daemon")
25
+ EGERIA_INTEGRATION_DAEMON_URL = os.environ.get("EGERIA_INTEGRATION_DAEMON_URL", "https://localhost:9443")
26
+ EGERIA_ADMIN_USER = os.environ.get("ADMIN_USER", "garygeeke")
27
+ EGERIA_ADMIN_PASSWORD = os.environ.get("ADMIN_PASSWORD", "secret")
28
+ EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
29
+ EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
30
+ EGERIA_WIDTH = os.environ.get("EGERIA_WIDTH", 220)
31
+ EGERIA_JUPYTER = os.environ.get("EGERIA_JUPYTER", False)
32
+ EGERIA_HOME_GLOSSARY_GUID = os.environ.get("EGERIA_HOME_GLOSSARY_GUID", None)
33
+ EGERIA_GLOSSARY_PATH = os.environ.get("EGERIA_GLOSSARY_PATH", None)
34
+ EGERIA_ROOT_PATH = os.environ.get("EGERIA_ROOT_PATH", "../../")
35
+ EGERIA_INBOX_PATH = os.environ.get("EGERIA_INBOX_PATH", "md_processing/dr_egeria_inbox")
36
+ EGERIA_OUTBOX_PATH = os.environ.get("EGERIA_OUTBOX_PATH", "md_processing/dr_egeria_outbox")
37
+
38
+ GENERAL_GOVERNANCE_DEFINITIONS = ["Governance Definition", "Business Imperative", "Regulation Article", "Threat",
39
+ "Governance Policy", "Governance Principle", "Governance Obligation",
40
+ "Governance Approach",
41
+ "Governance Processing Purpose"]
42
+ GOVERNANCE_DRIVERS = ["Governance Driver", "Governance Strategy", "Governance Imperative", "Regulation",
43
+ "Regulation Article", "Threat"]
44
+ GOVERNANCE_POLICIES = ["Governance Policy", "Governance Principle", "Governance Obligation", "Governance Approach"]
45
+
46
+
47
+ GOVERNANCE_CONTROLS = ["Governance Control", "Governance Rule", "Service Level Objective", "Governance Action",
48
+ "Security Access Control", "Governance Procedure","Governance Responsibility",
49
+ "Subject Area Definition", "Data Processing Purposes", "Methodology"]
50
+
51
+ ALL_GOVERNANCE_DEFINITIONS = GENERAL_GOVERNANCE_DEFINITIONS + GOVERNANCE_CONTROLS + ["Governance Strategy", "Regulation",
52
+ "Security Group", "GovernanceMetric",
53
+ "Naming Standard Rule", "TermsAndConditions", "Certification Type", "License Type"]
54
+ console = Console(width=EGERIA_WIDTH)
55
+ debug_level = DEBUG_LEVEL
56
+ global COMMAND_DEFINITIONS
57
+
58
+ def split_tb_string(input: str)-> [Any]:
59
+ """Split the string and trim the items"""
60
+ l = [item.strip() for item in re.split(r'[;,\n]+',input)] if input is not None else None
61
+ return l
62
+
63
+ def str_to_bool(value: str) -> bool:
64
+ """Converts a string to a boolean value."""
65
+ return value.lower() in ("yes", "true", "t", "1")
66
+
67
+ def render_markdown(markdown_text: str) -> None:
68
+ """Renders the given markdown text in the console."""
69
+ console.print(Markdown(markdown_text))
70
+
71
+
72
+ def is_valid_iso_date(date_text) -> bool:
73
+ """Checks if the given string is a valid ISO date."""
74
+ try:
75
+ datetime.strptime(date_text, '%Y-%m-%d')
76
+ return True
77
+ except ValueError:
78
+ return False
79
+
80
+
81
+ def set_debug_level(directive: str) -> None:
82
+ """Sets the debug level for the script."""
83
+ global debug_level
84
+ if directive == "display":
85
+ debug_level = "display-only"
86
+
87
+
88
+ def get_current_datetime_string():
89
+ """Returns the current date and time as a human-readable string."""
90
+ now = datetime.now().strftime('%Y-%m-%d %H:%M')
91
+ return now
92
+
93
+
94
+ def print_msg(msg_level: str, msg: str, verbosity: str):
95
+ """
96
+ Prints a message based on its type and verbosity level.
97
+
98
+ This function handles the output of messages depending on the specified
99
+ verbosity level and message type. It uses predefined message types and
100
+ formats the output accordingly.
101
+
102
+ Args:
103
+ msg_level: The type of the message, such as 'WARNING', 'ERROR', 'INFO', or
104
+ 'ALWAYS'.
105
+ msg: The content of the message to display.
106
+ verbosity: The verbosity level, which determines how the message is
107
+ displayed ('verbose', 'quiet', or 'debug').
108
+ """
109
+ if msg_level == "ALWAYS":
110
+ print(f"{message_types.get(msg_level, '')}{msg}")
111
+ # else:
112
+ # logger.info(f"{message_types.get(msg_level, '')}{msg}")
113
+ # elif verbosity == "verbose" and msg_level in ["INFO", "WARNING", "ERROR"]:
114
+ # print(f"{message_types.get(msg_level, '')}{msg}")
115
+ # elif verbosity == "quiet" and msg_level in ["WARNING", "ERROR"]:
116
+ # print(f"{message_types.get(msg_level, '')}{msg}")
117
+ # elif verbosity == "debug" and msg_level in ["INFO", "WARNING", "ERROR", "DEBUG-INFO", "DEBUG-WARNING",
118
+ # "DEBUG-ERROR"]:
119
+ # print(f"{message_types.get(msg_level, '')}{msg}")
120
+ # elif verbosity == "display-only" and msg_level in ["ALWAYS", "ERROR"]:
121
+ # print(f"{message_types.get(msg_level, '')}{msg}")
122
+ elif msg_level == "ERROR":
123
+ logger.error(f"{message_types.get(msg_level, '')}{msg}")
124
+ elif msg_level == "WARNING":
125
+ logger.warning(f"{message_types.get(msg_level, '')}{msg}")
126
+ elif msg_level == "DEBUG":
127
+ logger.debug(f"{message_types.get(msg_level, '')}{msg}")
128
+ else:
129
+ logger.info(f"{message_types.get(msg_level, '')}{msg}")
130
+
131
+ def process_provenance_command(file_path: str, txt: [str]) -> str:
132
+ """
133
+ Processes a provenance object_action by extracting the file path and current datetime.
134
+
135
+ Args:
136
+ file_path: The path to the file being processed.
137
+ txt: The text containing the provenance object_action.
138
+
139
+ Returns:
140
+ A string containing the provenance information.
141
+ """
142
+ now = get_current_datetime_string()
143
+ file_name = os.path.basename(file_path)
144
+ provenance = f"\n\n\n# Provenance:\n \n* Derived from processing file {file_name} on {now}\n"
145
+ return provenance
146
+
147
+
148
+ # Dictionary to store element information to avoid redundant API calls
149
+ element_dictionary = {}
150
+
151
+
152
+ def get_element_dictionary():
153
+ """
154
+ Get the shared element dictionary.
155
+
156
+ Returns:
157
+ dict: The shared element dictionary
158
+ """
159
+ global element_dictionary
160
+ return element_dictionary
161
+
162
+
163
+ def update_element_dictionary(key, value):
164
+ """
165
+ Update the shared element dictionary with a new key-value pair.
166
+
167
+ Args:
168
+ key (str): The key to update
169
+ value (dict): The value to associate with the key
170
+ """
171
+ global element_dictionary
172
+ if (key is None or value is None):
173
+ print(f"===>ERROR Key is {key} and value is {value}")
174
+ return
175
+ element_dictionary[key] = value
176
+
177
+
178
+ def clear_element_dictionary():
179
+ """
180
+ Clear the shared element dictionary.
181
+ """
182
+ global element_dictionary
183
+ element_dictionary.clear()
184
+
185
+
186
+ def is_present(value: str) -> bool:
187
+ global element_dictionary
188
+ present = value in element_dictionary.keys() or any(
189
+ value in inner_dict.values() for inner_dict in element_dictionary.values())
190
+ return present
191
+
192
+
193
+ def find_key_with_value(value: str) -> str | None:
194
+ """
195
+ Finds the top-level key whose nested dictionary contains the given value.
196
+
197
+ Args:
198
+ data (dict): A dictionary where keys map to nested dictionaries.
199
+ value (str): The value to search for.
200
+
201
+ Returns:
202
+ str | None: The top-level key that contains the value, or None if not found.
203
+ """
204
+ global element_dictionary
205
+ # Check if the value matches a top-level key
206
+ if value in element_dictionary.keys():
207
+ return value
208
+
209
+ # Check if the value exists in any of the nested dictionaries
210
+ for key, inner_dict in element_dictionary.items():
211
+ if value in inner_dict.values():
212
+ return key
213
+
214
+ return None # If value not found
215
+
216
+
217
+ def set_find_body(object_type: str, attributes: dict)->dict:
218
+ prop_name = object_type.replace(" ", "")
219
+ s = attributes.get('Search String', {}).get('value', None)
220
+ search_string = None if s =='*' else s
221
+
222
+
223
+ body = {
224
+ "class": "SearchStringRequestBody",
225
+ "searchString": search_string,
226
+ "startsWith": attributes.get('Start With', {}).get('value', True),
227
+ "endWith": attributes.get('End With', {}).get('value', False),
228
+ "ignoreCase": attributes.get('Ignore Case', {}).get('value', False),
229
+ "limitResultsByStatus": attributes.get('Limit Results By Status', {}).get('value', []),
230
+ "startFrom": int(attributes.get('Start From', {}).get('value', 0)),
231
+ "pageSize": int(attributes.get('Page Size', {}).get('value', 0)),
232
+ # "metadataElementSubtypeNames": attributes.get('Metadata Element Subtype Name', {}).get('value', None),
233
+ "metadataElementTypeName": attributes.get('Metadata Element Type Name', {}).get('value', None),
234
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
235
+ "governanceZoneFilter" : attributes.get('Governance Zone Filter', {}).get('value', None),
236
+ "graphQueryDepth": int(attributes.get('Graph Query Depth', {}).get('value', 0)),
237
+ }
238
+
239
+ return body
240
+
241
+
242
+ def set_create_body(object_type: str, attributes: dict)->dict:
243
+ """
244
+ Build the OUTER request body for a create action (NewElementRequestBody).
245
+
246
+ Notes on two-layer convention:
247
+ - Outer layer (this function): action wrapper with metadata like externalSource*, effectiveTime, anchor/parent hints, and an empty "properties" field.
248
+ - Inner layer: an element-type-specific Properties structure built by set_element_prop_body, set_product_body, set_data_field_body, etc.
249
+ Callers should build the inner body separately with the appropriate helper and then assign it to body["properties"].
250
+ """
251
+ prop_name = object_type.replace(" ", "")
252
+ body = {
253
+ "class": "NewElementRequestBody",
254
+ "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
255
+ "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
256
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
257
+ "governanceZoneFilter" : attributes.get('Governance Zone Filter', {}).get('value', None),
258
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
259
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
260
+ "anchorGUID": attributes.get('Anchor ID', {}).get('guid', None),
261
+ "isOwnAnchor": attributes.get('Is Own Anchor', {}).get('value', True),
262
+ "parentGUID": attributes.get('Parent ID', {}).get('guid', None),
263
+ "parentRelationshipTypeName": attributes.get('Parent Relationship Type Name', {}).get('value', None),
264
+ "parentRelationshipProperties": attributes.get('Parent Relationship Properties', {}).get('value', None),
265
+ "parentAtEnd1": attributes.get('Parent at End1', {}).get('value', True),
266
+ "anchorScopeGUID": attributes.get('Anchor Scope GUID', {}).get('guid', None),
267
+ "properties": "",
268
+ "initialStatus": attributes.get('Status', {}).get('value', "ACTIVE"),
269
+ "initialClassifications": {}}
270
+
271
+ return body
272
+
273
+
274
+
275
+
276
+ def set_update_body(object_type: str, attributes: dict)->dict:
277
+ """
278
+ Build the OUTER request body for an update action (UpdateElementRequestBody).
279
+
280
+ Two-layer convention:
281
+ - Outer layer (this function) provides action metadata and an empty "properties" slot.
282
+ - Inner layer must be constructed via element-specific helpers (e.g., set_element_prop_body) and assigned to the returned dict's "properties" key by the caller before invoking the client.
283
+ """
284
+ return {
285
+ "class" : "UpdateElementRequestBody",
286
+ "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
287
+ "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
288
+ "governanceZoneFilter": attributes.get('Governance Zone Filter', {}).get('value', None),
289
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
290
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
291
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
292
+ "mergeUpdate": attributes.get('Merge Update', {}).get('value', True),
293
+ "properties": "",
294
+ }
295
+ def set_rel_prop_body(object_type: str, attributes: dict)->dict:
296
+ prop_name = object_type.replace(" ", "")
297
+ display_name = attributes.get('Display Name', {}).get('value', None)
298
+
299
+ return {
300
+ "class": prop_name + "Properties",
301
+ "description": attributes['Description'].get('value', None),
302
+ "label": attributes.get('Label', {}).get('value', None) or attributes.get('Link Label', {}).get('value', None),
303
+ "typeName" : attributes.get('Type Name', {}).get('value', None),
304
+ "effectiveFrom": attributes.get('Effective From', {}).get('value', None),
305
+ "effectiveTo": attributes.get('Effective To', {}).get('value', None),
306
+ "extendedProperties": attributes.get('Extended Properties', {}).get('value', None),
307
+ }
308
+
309
+ def set_element_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict:
310
+ """
311
+ Build the INNER element-specific Properties body to be placed under the outer body's "properties" key.
312
+
313
+ This returns the typed properties structure (e.g., "ReferenceableProperties" subtypes) appropriate for the object_type.
314
+ Usage example:
315
+ - outer = set_create_body(object_type, attributes)
316
+ - props = set_element_prop_body(object_type, qualified_name, attributes)
317
+ - outer["properties"] = props
318
+ - client.create_xxx(outer)
319
+ """
320
+ prop_name = object_type.replace(" ", "")
321
+ display_name = attributes.get('Display Name', {}).get('value', None)
322
+
323
+ return {
324
+ "class": prop_name + "Properties",
325
+ "displayName": attributes.get('Display Name', {}).get('value', None),
326
+ "qualifiedName" : qualified_name,
327
+ "description": attributes['Description'].get('value', None),
328
+ "category": attributes.get('Category', {}).get('value', None),
329
+ "identifier": attributes.get('Identifier', {}).get('value', None),
330
+ "userDefinedStatus": attributes.get('User Defined Status', {}).get('value', None),
331
+ "versionIdentifier": attributes.get('Version Identifier', {}).get('value', None),
332
+ "effectiveFrom": attributes.get('Effective From', {}).get('value', None),
333
+ "effectiveTo": attributes.get('Effective To', {}).get('value', None),
334
+ "additionalProperties": attributes.get('Additional Properties', {}).get('value', None),
335
+ "extendedProperties": attributes.get('Extended Properties', {}).get('value', None),
336
+ "supportLevel": attributes.get('Support Level', {}).get('value', None),
337
+ "serviceLevels": attributes.get('Service Levels', {}).get('value', None),
338
+ }
339
+
340
+ def set_product_body(object_type: str, qualified_name: str, attributes: dict)->dict:
341
+ prop_bod = set_element_prop_body(object_type, qualified_name, attributes)
342
+ prop_bod["identifier"] = attributes.get('Identifier', {}).get('value', None)
343
+ prop_bod["productName"] = attributes.get('Product Name', {}).get('value', None)
344
+ prop_bod["maturity"] = attributes.get('Maturity', {}).get('value', None)
345
+ prop_bod["serviceLife"] = attributes.get('Service Life', {}).get('value', None)
346
+ prop_bod["introductionDate"] = attributes.get('Introduction Date', {}).get('value', [])
347
+ prop_bod["withdrawalDate"] = attributes.get('Withdrawal Date', {}).get('value', [])
348
+ prop_bod["nextVersion"] = attributes.get('Next Version Date', {}).get('value', [])
349
+ return prop_bod
350
+
351
+ def set_data_field_body(object_type: str, qualified_name: str, attributes: dict)->dict:
352
+ prop_bod = set_element_prop_body(object_type, qualified_name, attributes)
353
+ prop_bod["namespace"] = attributes.get('Namespace', {}).get('value', None)
354
+ prop_bod["aliases"] = attributes.get('Aliases', {}).get('value', [])
355
+ prop_bod["namePatterns"] = attributes.get('Name Patterns', {}).get('value', [])
356
+ prop_bod["defaultValue"] = attributes.get('Default Value', {}).get('value', None)
357
+ prop_bod["isNullable"] = attributes.get('Is Nullable', {}).get('value', None)
358
+ prop_bod["dataType"] = attributes.get('Data Type', {}).get('value', None)
359
+ prop_bod["units"] = attributes.get('Units', {}).get('value', None)
360
+ prop_bod["minimumLength"] = attributes.get('Minimum Length', {}).get('value', None)
361
+ prop_bod["length"] = attributes.get('Length', {}).get('value', None)
362
+ prop_bod["precision"] = attributes.get('Precision', {}).get('value', None)
363
+ prop_bod["orderedValues"] = attributes.get('Ordered Values', {}).get('value', [])
364
+ prop_bod["sortOrder"] = attributes.get('Sort Order', {}).get('value', None)
365
+ return prop_bod
366
+
367
+ def set_update_status_body(object_type: str, attributes: dict)->dict:
368
+ return {
369
+ "class" : "UpdateStatusRequestBody",
370
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
371
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
372
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
373
+ "mergeUpdate": attributes.get('Merge Update', {}).get('value', True),
374
+ }
375
+
376
+ def set_gov_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict:
377
+ prop_name = object_type.replace(" ", "")
378
+ prop_bod = set_element_prop_body(object_type, qualified_name, attributes)
379
+ prop_bod["domainIdentifier"] = attributes.get('Domain Identifier', {}).get('value', None)
380
+ prop_bod["displayName"]= attributes.get('Display Name', {}).get('value', None)
381
+ prop_bod['qualifiedName'] = qualified_name
382
+ prop_bod["versionIdentifier"] = attributes.get('Version Identifier', {}).get('value', None)
383
+ prop_bod["summary"] = attributes.get('Summary', {}).get('value', None)
384
+ prop_bod["description"] = attributes.get('Description', {}).get('value', None)
385
+
386
+ prop_bod["scope"] = attributes.get('Scope', {}).get('value', None)
387
+ prop_bod["importance"] = attributes.get('Importance', {}).get('value', None)
388
+ prop_bod["implications"] = attributes.get('Implication', {}).get('value', [])
389
+ prop_bod["outcomes"] = attributes.get('Outcomes', {}).get('value', [])
390
+ prop_bod["results"] = attributes.get('Results', {}).get('value', []) or []
391
+ prop_bod["effectiveFrom"] = attributes.get('Effective From', {}).get('value', None),
392
+ prop_bod["effectiveTo"] = attributes.get('Effective To', {}).get('value', None),
393
+ prop_bod["additionalProperties"] = attributes.get('Additional Properties', {}).get('value', None),
394
+ prop_bod["extendedProperties"] = attributes.get('Extended Properties', {}).get('value', None)
395
+
396
+
397
+ body = update_gov_body_for_type(object_type, prop_bod, attributes)
398
+ return body
399
+
400
+
401
+ def update_gov_body_for_type(object_type: str, body: dict, attributes: dict) -> dict:
402
+ gov_def_name = object_type.replace(" ", "")
403
+ if object_type in GENERAL_GOVERNANCE_DEFINITIONS:
404
+ return body
405
+ elif object_type == "Governance Strategy":
406
+ body['businessImperatives'] = attributes.get('Business Imperatives', {}).get('value', [])
407
+ return body
408
+
409
+ elif object_type == "Regulation":
410
+ body['regulationSource'] = attributes.get('Regulation Source', {}).get('value', None)
411
+ body['regulators'] = attributes.get('Regulators', {}).get('value', [])
412
+ return body
413
+
414
+ elif object_type in GOVERNANCE_CONTROLS:
415
+ body['implementationDescription'] = attributes.get('Implementation Description', {}).get('value', None)
416
+ return body
417
+ elif object_type == "Security Group":
418
+ body['distinguishedName'] = attributes.get('Distinguished Name', {}).get('value', None)
419
+ return body
420
+ elif object_type == "GovernanceMetric":
421
+ body['measurement'] = attributes.get('Measurement', {}).get('value', None)
422
+ body['target'] = attributes.get('Target', {}).get('value', None)
423
+ return body
424
+ elif object_type == "Naming Standard Rule":
425
+ body['namePatterns'] = attributes.get('Name Patterns', {}).get('value', [])
426
+ return body
427
+ elif object_type in ["TermsAndConditions", "Certification Type", "License Type"]:
428
+ entitlements = attributes.get('Entitlementss', {}).get('value', {}) if attributes.get('Entitlementss',None) else None
429
+ restrictions = attributes.get('Restrictions', {}).get('value', {}) if attributes.get('Restrictions',None) else None
430
+ obligations = attributes.get('Obligations', {}).get('value', {}) if attributes.get('Obligations',None) else None
431
+ body['entitlements'] = entitlements
432
+ body['restrictions'] = restrictions
433
+ body['obligations'] = obligations
434
+
435
+ return body
436
+
437
+
438
+ def set_rel_request_body(object_type: str, attributes: dict)->dict:
439
+ """
440
+ Build the OUTER request body for creating a relationship (NewRelationshipRequestBody).
441
+ The inner relationship properties must be assigned to the returned dict under the "properties" key,
442
+ commonly via set_rel_prop_body or set_rel_request_body_for_type.
443
+ """
444
+ return {
445
+ "class" : "NewRelationshipRequestBody",
446
+ "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
447
+ "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
448
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
449
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
450
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
451
+ "properties": "",
452
+ }
453
+
454
+ def set_peer_gov_def_request_body(object_type: str, attributes: dict)->dict:
455
+ rel_body = set_rel_request_body(object_type, attributes)
456
+ rel_body["properties"] = {
457
+ "class" : "PeerDefinitionProperties",
458
+ "description": attributes.get('Description', {}).get('value', None),
459
+ "effectiveFrom": attributes.get('Effective From', {}).get('value', None),
460
+ "effectiveTo": attributes.get('Effective To', {}).get('value', None),
461
+ "label": attributes.get('Label', {}).get('value', None),
462
+ }
463
+ return rel_body
464
+
465
+ def set_rel_request_body_for_type(object_type: str, attributes: dict)->dict:
466
+ """
467
+ Convenience helper that builds both layers (outer + inner) for a relationship of a known type.
468
+ It creates the outer NewRelationshipRequestBody via set_rel_request_body and fills rel_body["properties"]
469
+ with a typed properties structure under the "class" of f"{object_type}Properties".
470
+ """
471
+ rel_body = set_rel_request_body(object_type, attributes)
472
+ # class_prop = camel_to_title_case(object_type) + "Properties"
473
+ class_prop = f"{object_type}Properties"
474
+ rel_body["properties"] = {
475
+ "class" : class_prop,
476
+ "description": attributes.get('Description', {}).get('value', None),
477
+ "effectiveFrom": attributes.get('Effective From', {}).get('value', None),
478
+ "effectiveTo": attributes.get('Effective To', {}).get('value', None),
479
+ "label": attributes.get('Label', {}).get('value', None),
480
+ }
481
+ return rel_body
482
+
483
+ def set_delete_request_body(object_type: str, attributes: dict)->dict:
484
+ return {
485
+ "class": "DeleteElementRequestBody",
486
+ "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
487
+ "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
488
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
489
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
490
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False)
491
+ }
492
+
493
+
494
+
495
+ def set_filter_request_body(object_type: str, attributes: dict)->dict:
496
+ return {
497
+ "class": "FilterRequestBody",
498
+ "asOfTime": attributes.get('AsOfTime', {}).get('value', None),
499
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
500
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
501
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
502
+ "limitResultsByStatus": attributes.get('Limit Result by Status', {}).get('value', None),
503
+ "sequencingOrder": attributes.get('Sequencing Order', {}).get('value', None),
504
+ "sequencingProperty": attributes.get('Sequencing Property', {}).get('value', None),
505
+ "filter": attributes.get('Search String', {}).get('value', None),
506
+ }
507
+
508
+ def set_element_status_request_body(object_type: str, attributes: dict)->dict:
509
+ return {
510
+ "class": f"{camel_to_title_case(object_type)}StatusRequestBody",
511
+ "status": attributes.get('Status', {}).get('value', None),
512
+ "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
513
+ "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
514
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
515
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
516
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False)
517
+ }
518
+
519
+
520
+ def set_classifications(object_type: str, attributes: dict)->dict:
521
+ classifications = attributes.get('Classifications', {}).get('name_list', None)
522
+ body = None
523
+ if classifications:
524
+ body = {classification: {} for classification in classifications} if classifications else {}
525
+ return body
526
+
527
+ def set_object_classifications(object_type: str, attributes: dict, obj_types: list[str])->dict:
528
+ classifications = attributes.get('Classifications', {}).get('name_list', None)
529
+ obj = object_type.replace(" ", "")
530
+ if object_type in obj_types:
531
+ if classifications:
532
+ if object_type not in classifications:
533
+ classifications.append(obj)
534
+ else:
535
+ classifications = [obj]
536
+
537
+ body = {}
538
+ if classifications:
539
+ for classification in classifications:
540
+ body[classification] = {"class" : f"{classification}Properties"}
541
+ return body
542
+
543
+ def add_search_keywords(client: ServerClient, element_guid: str, keywords: list[str]):
544
+ """Add a search keyword to an element. Throw an exception if a problem is encountered.
545
+
546
+ Args:
547
+ client (ServerClient): The Egeria client instance.
548
+ element_guid (str): The GUID of the element to add the keyword to.
549
+ keyword (str): The search keyword to add.
550
+
551
+ Returns:
552
+ None
553
+ """
554
+ try:
555
+ for keyword in keywords:
556
+ client.add_search_keyword_to_element(element_guid, keyword)
557
+ print("Added keyword `{}` to element `{}`".format(keyword, element_guid))
558
+
559
+ except PyegeriaException as e:
560
+ context = {
561
+ "readon" : "Exception encountered executing add_search_keyword",
562
+ "element_guid": element_guid,
563
+ "keyword": keyword,
564
+ "exception": str(e)
565
+ }
566
+ raise PyegeriaException(context = context)
567
+
568
+ def add_note_in_dr_e(client: ServerClient, qualified_name: str, display_name: str, journal_entry: str)-> str:
569
+ if journal_entry:
570
+ note_log_qn = f"{qualified_name}-NoteLog"
571
+ note_log_display_name = f"{display_name}-NoteLog"
572
+ note_display_name = f"{qualified_name}-Journal-Entry-{datetime.now().strftime('%Y-%m-%d %H:%M')}"
573
+ journal_entry_guid = client.add_journal_entry(note_log_qn, qualified_name, note_log_display_name,
574
+ note_display_name, journal_entry)
575
+ logger.info(f"Added journal entry `{journal_entry_guid}` to `{qualified_name}`")
576
+ return journal_entry_guid
577
+ else:
578
+ return None