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
pyegeria/core/utils.py ADDED
@@ -0,0 +1,405 @@
1
+ """
2
+ SPDX-License-Identifier: Apache-2.0
3
+ Copyright Contributors to the ODPi Egeria project.
4
+
5
+ General utility functions in support of the Egeria Python Client package.
6
+
7
+ """
8
+
9
+ from loguru import logger
10
+ from rich import print, print_json
11
+ from rich.console import Console
12
+ from pyegeria.core.config import settings as app_settings
13
+ from typing import Callable, TypeVar
14
+
15
+ T = TypeVar('T', bound=Callable)
16
+
17
+ console = Console(width=200)
18
+
19
+ def init_log():
20
+ pass
21
+
22
+ def print_rest_request_body(body):
23
+ """
24
+
25
+ Args:
26
+ body:
27
+ """
28
+ pretty_body = json.dumps(body, indent=4)
29
+ print_json(pretty_body, indent=4, sort_keys=True)
30
+
31
+
32
+ def print_rest_response(response):
33
+ """
34
+
35
+ Args:
36
+ response:
37
+ """
38
+ print("Returns:")
39
+ pretty_body = json.dumps(response, indent=4)
40
+ print_json(pretty_body, indent=4, sort_keys=True)
41
+
42
+
43
+ def print_guid_list(guids):
44
+ """Print a list of guids"""
45
+ if guids is None:
46
+ print("No assets created")
47
+ else:
48
+ pretty_guids = json.dumps(guids, indent=4)
49
+ print_json(pretty_guids, indent=4, sort_keys=True)
50
+
51
+
52
+ #
53
+ # OCF Common services
54
+ # Working with assets - this set of functions displays assets returned from the open metadata repositories.
55
+ #
56
+
57
+
58
+
59
+
60
+ def body_slimmer(body: dict) -> dict:
61
+ """body_slimmer is a little function to remove unused keys from a dict
62
+ and recursively slim embedded dicts
63
+
64
+ Parameters
65
+ ----------
66
+ body : the dictionary that you want to slim
67
+
68
+ Returns
69
+ -------
70
+ dict:
71
+ a slimmed body with all embedded dictionaries also slimmed
72
+ """
73
+ if body is None:
74
+ return {}
75
+
76
+ slimmed = {}
77
+ for key, value in body.items():
78
+ if value and not isinstance(value, tuple):
79
+ if isinstance(value, dict):
80
+ # Recursively slim embedded dictionaries
81
+ slimmed_value = body_slimmer(value)
82
+ if slimmed_value: # Only include non-empty dictionaries
83
+ slimmed[key] = slimmed_value
84
+ else:
85
+ slimmed[key] = value
86
+ return slimmed
87
+
88
+
89
+ def camel_to_title_case(input_string):
90
+ # Add a space before uppercase letters and capitalize each word
91
+ result = re.sub(r'([a-z])([A-Z])', r'\1 \2', input_string).title()
92
+ return result
93
+
94
+
95
+ def to_camel_case(input_string):
96
+ """Convert an input string to camelCase, singularizing if plural.
97
+
98
+ This function takes an input string, converts it to singular form if it's plural,
99
+ and then transforms it to camelCase format (first word lowercase, subsequent words
100
+ capitalized with no spaces).
101
+
102
+ Parameters
103
+ ----------
104
+ input_string : str
105
+ The string to convert to camelCase
106
+
107
+ Returns
108
+ -------
109
+ str:
110
+ The input string converted to camelCase, after singularization if needed
111
+
112
+ Examples
113
+ --------
114
+ >>> to_camel_case("data categories")
115
+ 'dataCategory'
116
+ >>> to_camel_case("business terms")
117
+ 'businessTerm'
118
+ >>> to_camel_case("glossary categories")
119
+ 'glossaryCategory'
120
+ """
121
+ if not input_string:
122
+ return ""
123
+
124
+ # Convert to lowercase for consistent processing
125
+ lowercase_input = input_string.lower()
126
+
127
+ # First, convert to singular if plural
128
+ singular = lowercase_input
129
+
130
+ # Handle common plural endings
131
+ if singular.endswith('ies'):
132
+ singular = singular[:-3] + 'y'
133
+ elif singular.endswith('es'):
134
+ # Special cases like 'classes' -> 'class'
135
+ if singular.endswith('sses') or singular.endswith('ches') or singular.endswith('shes') or singular.endswith('xes'):
136
+ singular = singular[:-2]
137
+ else:
138
+ singular = singular[:-1]
139
+ elif singular.endswith('s') and not singular.endswith('ss'):
140
+ singular = singular[:-1]
141
+
142
+ # Split the string into words and convert to camelCase
143
+ words = singular.split()
144
+ if not words:
145
+ return ""
146
+
147
+ # First word is lowercase, rest are capitalized
148
+ result = words[0]
149
+ for word in words[1:]:
150
+ result += word.capitalize()
151
+
152
+ return result
153
+
154
+ def to_pascal_case(input_string)->str:
155
+ """
156
+ Convert input string to PascalCase, singularizing if plural.
157
+ Args:
158
+ input_string ():
159
+
160
+ Returns:
161
+ transformed string
162
+ """
163
+ result = to_camel_case(input_string)
164
+ output_string = result[0].upper() + result[1:]
165
+ return output_string
166
+
167
+ def flatten_dict_to_string(d: dict) -> str:
168
+ """Flatten a dictionary into a string and replace quotes with backticks."""
169
+ try:
170
+ flat_string = ", ".join(
171
+ # Change replace(\"'\", '`') to replace("'", '`')
172
+ f"{key}=`{str(value).replace('\"', '`').replace("'", '`')}`"
173
+ for key, value in d.items()
174
+ )
175
+ return flat_string
176
+ except Exception as e:
177
+ # Corrected syntax for exception chaining
178
+ raise Exception("Error flattening dictionary") from e
179
+ # The decorator logic, which applies @logger.catch dynamically
180
+
181
+ def dict_to_markdown_list(data: dict, level: int = 0) -> str:
182
+ """
183
+ Recursively converts a dictionary into a nested markdown bullet list string.
184
+
185
+ Args:
186
+ data (dict): The dictionary to convert.
187
+ level (int): The current indentation level (default is 0).
188
+
189
+ Returns:
190
+ str: The markdown formatted string.
191
+ """
192
+ markdown_str = ""
193
+ # Standard markdown indent is often 2 or 4 spaces. Using 2 for compactness in recursion.
194
+ indent = " " * level
195
+
196
+ for key, value in data.items():
197
+ if isinstance(value, dict):
198
+ markdown_str += f"{indent}* **{key}**:\n{dict_to_markdown_list(value, level + 1)}"
199
+ elif isinstance(value, list):
200
+ markdown_str += f"{indent}* **{key}**:\n"
201
+ for item in value:
202
+ if isinstance(item, dict):
203
+ markdown_str += f"{indent} * \n{dict_to_markdown_list(item, level + 2)}"
204
+ else:
205
+ markdown_str += f"{indent} * {item}\n"
206
+ else:
207
+ markdown_str += f"{indent}* **{key}**: {value}\n"
208
+
209
+ return markdown_str
210
+
211
+
212
+ import csv
213
+ import io
214
+ from typing import Dict, Any
215
+
216
+
217
+ def transform_json_to_tabular(json_data: Dict[str, Any], output_format: str = 'rich'):
218
+ """
219
+ Transforms Egeria TabularDataSetReportResponse JSON to CSV, Rich table, or Markdown table (LIST).
220
+
221
+ :param json_data: The JSON data as a dictionary.
222
+ :param output_format: 'CSV', 'RICH-TABLE', or 'LIST'.
223
+ """
224
+ report = json_data.json().get('tabularDataSetReport')
225
+ if not report:
226
+ print("No tabularDataSetReport found in JSON.")
227
+ return
228
+
229
+ column_descriptions = report.get('columnDescriptions', [])
230
+ headers = [col.get('columnName') for col in column_descriptions]
231
+ data_records = report.get('dataRecords', {})
232
+
233
+ # Sort keys to ensure correct order if they are numeric strings
234
+ sorted_record_keys = sorted(data_records.keys(), key=lambda x: int(x))
235
+ rows = [data_records[key] for key in sorted_record_keys]
236
+
237
+ if output_format == 'CSV':
238
+ output = io.StringIO()
239
+ writer = csv.writer(output)
240
+ writer.writerow(headers)
241
+ writer.writerows(rows)
242
+ print(output.getvalue())
243
+ elif output_format == 'RICH-TABLE':
244
+ try:
245
+ from rich.console import Console
246
+ from rich.table import Table
247
+
248
+ console = Console()
249
+ table = Table(title=report.get('tableName', 'Report'))
250
+
251
+ for header in headers:
252
+ table.add_column(header)
253
+
254
+ for row in rows:
255
+ # Convert all items to string for Rich
256
+ str_row = [str(item) if item is not None else "" for item in row]
257
+ table.add_row(*str_row)
258
+
259
+ console.print(table)
260
+ except ImportError:
261
+ print("Rich library not installed. Defaulting to simple text output.")
262
+ print(f"Headers: {headers}")
263
+ for row in rows:
264
+ print(row)
265
+ elif output_format == 'LIST':
266
+ # Generate a markdown table
267
+ markdown_output = f"### {report.get('tableName', 'Report')}\n\n"
268
+ markdown_output += "| " + " | ".join(headers) + " |\n"
269
+ markdown_output += "| " + " | ".join(["---"] * len(headers)) + " |\n"
270
+ for row in rows:
271
+ str_row = [str(item) if item is not None else "" for item in row]
272
+ markdown_output += "| " + " | ".join(str_row) + " |\n"
273
+ print(markdown_output)
274
+ else:
275
+ print(f"Unknown output format: {output_format}")
276
+
277
+
278
+ # Example usage:
279
+ # with open('path/to/your/file.json', 'r') as f:
280
+ # data = json.load(f)
281
+ # transform_json_to_tabular(data, 'CSV') # or 'RICH-TABLE' or 'LIST'
282
+
283
+ import json
284
+ import re
285
+
286
+
287
+ # def parse_to_dict(input_str: str):
288
+ # """
289
+ # Check if a string is valid JSON or a name:value list without braces and convert to a dictionary.
290
+ #
291
+ # Args:
292
+ # input_str: The input string to parse.
293
+ #
294
+ # Returns:
295
+ # dict: A dictionary converted from the input string.
296
+ # None: If the input is neither valid JSON nor a valid name:value list.
297
+ # """
298
+ #
299
+ # if input_str is None:
300
+ # return None
301
+ #
302
+ # # Check if the input string is valid JSON
303
+ # try:
304
+ # result = json.loads(input_str)
305
+ # if isinstance(result, dict): # Ensure it's a dictionary
306
+ # return result
307
+ # except json.JSONDecodeError:
308
+ # pass
309
+ #
310
+ # # Check if input string looks like a name:value list
311
+ # # Supports both comma and newline as separators
312
+ # pattern = r'^(\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9_-]+)\s*:\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9 _-]*)\s*)' \
313
+ # r'(\s*[,|\n]\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9_-]+)\s*:\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9 _-]*)\s*)*$'
314
+ # if re.match(pattern, input_str.strip()):
315
+ # try:
316
+ # # Split by ',' or '\n' and process key-value pairs
317
+ # pairs = [pair.split(":", 1) for pair in re.split(r'[,|\n]+', input_str.strip())]
318
+ # return {key.strip().strip('\'"'): value.strip().strip('\'"') for key, value in pairs}
319
+ # except Exception:
320
+ # return None
321
+ #
322
+ # # If neither pattern matches, return None
323
+ # return None
324
+
325
+
326
+ def parse_to_dict(input_str: str) -> dict | None:
327
+ """
328
+ Parse input strings into a dictionary, handling both JSON and key-value pairs.
329
+ Recovers from malformed JSON (e.g., where commas are missing between key-value pairs)
330
+ and supports multiline values.
331
+
332
+ Args:
333
+ input_str (str): The input string to parse.
334
+
335
+ Returns:
336
+ dict: A parsed dictionary if validation is successful, or None if the string cannot be parsed.
337
+ """
338
+ if not input_str:
339
+ return None
340
+
341
+ # Attempt to parse valid JSON
342
+ try:
343
+ result = json.loads(input_str)
344
+ if isinstance(result, dict):
345
+ return result
346
+ except json.JSONDecodeError:
347
+ pass
348
+
349
+ # Fix malformed JSON or attempt alternate parsing for "key: value" patterns
350
+ try:
351
+ # Step 1: Inject missing commas where they are omitted between key-value pairs
352
+ fixed_input = re.sub(
353
+ r'("\s*:[^,}\n]+)\s*("(?![:,}\n]))', # Find missing commas (key-value-value sequences)
354
+ r'\1,\2', # Add a comma between the values
355
+ input_str
356
+ )
357
+
358
+ # Attempt to parse the fixed string as JSON
359
+ try:
360
+ result = json.loads(fixed_input)
361
+ if isinstance(result, dict):
362
+ return result
363
+ except json.JSONDecodeError:
364
+ pass
365
+
366
+ # Step 2: Handle key-value format fallback (supports multiline strings)
367
+ # Matches `key: value` pairs, including multiline quoted values
368
+ key_value_pattern = re.compile(r'''
369
+ (?:"([^"]+)"|'([^']+)'|([a-zA-Z0-9_-]+)) # Key: quoted "key", 'key', or unquoted key
370
+ \s*:\s* # Key-value separator
371
+ (?:"((?:\\.|[^"\\])*?)"|'((?:\\.|[^'\\])*?)'|([^\n,]+)) # Value: quoted or unquoted
372
+ ''', re.VERBOSE | re.DOTALL)
373
+
374
+ matches = key_value_pattern.findall(input_str)
375
+
376
+ # Build dictionary from matches
377
+ result_dict = {}
378
+ for match in matches:
379
+ key = next((group for group in match[:3] if group), "").strip()
380
+ value = next((group for group in match[3:] if group), "").strip()
381
+ result_dict[key] = value
382
+
383
+ if result_dict:
384
+ return result_dict
385
+ except Exception as e:
386
+ # Log or handle parsing exception if needed
387
+ pass
388
+
389
+ # If all parsing attempts fail, return None
390
+ return None
391
+
392
+
393
+ def dynamic_catch(func: T) -> T:
394
+ if app_settings.get("enable_logger_catchh", False):
395
+ return logger.catch(func) # Apply the logger.catch decorator
396
+ else:
397
+ return func # Return the function unwrapped
398
+
399
+ def make_format_set_name_from_type(obj_type: str)-> str:
400
+ formatted_name = obj_type.replace(" ", "-")
401
+ return f"{formatted_name}-DrE"
402
+
403
+
404
+ if __name__ == "__main__":
405
+ print("Main-Utils")
File without changes
@@ -11,28 +11,26 @@ import asyncio
11
11
  import inspect
12
12
  import json
13
13
  import os
14
- import re
15
14
  from datetime import datetime
16
15
 
17
16
  import httpx
18
17
  from httpx import AsyncClient, Response
19
18
 
20
- from pyegeria.utils import body_slimmer
21
- from pyegeria._exceptions import (
19
+ from pyegeria.core.utils import body_slimmer
20
+ from pyegeria.core._exceptions import (
22
21
  InvalidParameterException,
23
22
  OMAGCommonErrorCode,
24
23
  PropertyServerException,
25
24
  UserNotAuthorizedException,
26
25
  )
27
- from pyegeria._globals import enable_ssl_check, max_paging_size, NO_ELEMENTS_FOUND
28
- from pyegeria._validators import (
26
+ from pyegeria.core._globals import enable_ssl_check, max_paging_size, NO_ELEMENTS_FOUND
27
+ from pyegeria.core._validators import (
29
28
  is_json,
30
29
  validate_name,
31
30
  validate_server_name,
32
31
  validate_url,
33
32
  validate_user_id,
34
33
  )
35
- from pyegeria.output_formatter import make_preamble, make_md_attribute
36
34
 
37
35
  ...
38
36
 
@@ -181,9 +179,9 @@ class Client:
181
179
 
182
180
  Raises
183
181
  ------
184
- InvalidParameterException
182
+ PyegeriaInvalidParameterException
185
183
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
186
- PropertyServerException
184
+ PyegeriaAPIException
187
185
  Raised by the server when an issue arises in processing a valid request
188
186
  NotAuthorizedException
189
187
  The principle specified by the user_id does not have authorization for the requested action
@@ -237,9 +235,9 @@ class Client:
237
235
 
238
236
  Raises
239
237
  ------
240
- InvalidParameterException
238
+ PyegeriaInvalidParameterException
241
239
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
242
- PropertyServerException
240
+ PyegeriaAPIException
243
241
  Raised by the server when an issue arises in processing a valid request
244
242
  NotAuthorizedException
245
243
  The principle specified by the user_id does not have authorization for the requested action
@@ -263,7 +261,7 @@ class Client:
263
261
  This method is used to refresh the bearer token used for authentication with Egeria. It checks if the token
264
262
  source is 'Egeria', and if the user ID and password are valid. If all conditions are met, it calls the
265
263
  `create_egeria_bearer_token` method to create a new bearer token. Otherwise,
266
- it raises an `InvalidParameterException`.
264
+ it raises an `PyegeriaInvalidParameterException`.
267
265
 
268
266
  Parameters:
269
267
 
@@ -271,7 +269,7 @@ class Client:
271
269
  None
272
270
 
273
271
  Raises:
274
- InvalidParameterException: If the token source is invalid.
272
+ PyegeriaInvalidParameterException: If the token source is invalid.
275
273
  """
276
274
  if (
277
275
  (self.token_src == "Egeria")
@@ -292,7 +290,7 @@ class Client:
292
290
  This method is used to refresh the bearer token used for authentication with Egeria. It checks if the token
293
291
  source is 'Egeria', and if the user ID and password are valid. If all conditions are met, it calls the
294
292
  `create_egeria_bearer_token` method to create a new bearer token. Otherwise,
295
- it raises an `InvalidParameterException`.
293
+ it raises an `PyegeriaInvalidParameterException`.
296
294
 
297
295
  Parameters:
298
296
 
@@ -300,8 +298,8 @@ class Client:
300
298
  None
301
299
 
302
300
  Raises:
303
- InvalidParameterException: If the token source is invalid.
304
- PropertyServerException
301
+ PyegeriaInvalidParameterException: If the token source is invalid.
302
+ PyegeriaAPIException
305
303
  Raised by the server when an issue arises in processing a valid request
306
304
  NotAuthorizedException
307
305
  The principle specified by the user_id does not have authorization for the requested action
@@ -324,9 +322,9 @@ class Client:
324
322
 
325
323
  Raises
326
324
  ------
327
- InvalidParameterException
325
+ PyegeriaInvalidParameterException
328
326
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
329
- PropertyServerException
327
+ PyegeriaAPIException
330
328
  Raised by the server when an issue arises in processing a valid request
331
329
  NotAuthorizedException
332
330
  The principle specified by the user_id does not have authorization for the requested action
@@ -366,7 +364,7 @@ class Client:
366
364
  ) -> Response | str:
367
365
  """Make a request to the Egeria API - Async Version
368
366
  Function to make an API call via the self.session Library. Raise an exception if the HTTP response code
369
- is not 200/201. IF there is a REST communication exception, raise InvalidParameterException.
367
+ is not 200/201. IF there is a REST communication exception, raise PyegeriaInvalidParameterException.
370
368
 
371
369
  :param request_type: Type of Request.
372
370
  Supported Values - GET, POST, (not PUT, PATCH, DELETE).
@@ -444,7 +442,7 @@ class Client:
444
442
  {
445
443
  "class": "VoidResponse",
446
444
  "relatedHTTPCode": response.status_code,
447
- "exceptionClassName": "InvalidParameterException",
445
+ "exceptionClassName": "PyegeriaInvalidParameterException",
448
446
  "actionDescription": caller_method,
449
447
  "exceptionErrorMessage": msg,
450
448
  "exceptionErrorMessageId": OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
@@ -487,7 +485,7 @@ class Client:
487
485
  {
488
486
  "class": "VoidResponse",
489
487
  "relatedHTTPCode": response.status_code,
490
- "exceptionClassName": "InvalidParameterException",
488
+ "exceptionClassName": "PyegeriaInvalidParameterException",
491
489
  "actionDescription": caller_method,
492
490
  "exceptionErrorMessage": msg,
493
491
  "exceptionErrorMessageId": OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
@@ -537,7 +535,7 @@ class Client:
537
535
  {
538
536
  "class": "VoidResponse",
539
537
  "relatedHTTPCode": response.status_code,
540
- "exceptionClassName": "PropertyServerException",
538
+ "exceptionClassName": "PyegeriaAPIException",
541
539
  "actionDescription": caller_method,
542
540
  "exceptionErrorMessage": msg,
543
541
  "exceptionErrorMessageId": OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
@@ -596,7 +594,7 @@ class Client:
596
594
  {
597
595
  "class": "VoidResponse",
598
596
  "relatedHTTPCode": 400,
599
- "exceptionClassName": "InvalidParameterException",
597
+ "exceptionClassName": "PyegeriaInvalidParameterException",
600
598
  "actionDescription": caller_method,
601
599
  "exceptionErrorMessage": msg,
602
600
  "exceptionErrorMessageId": OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
@@ -771,13 +769,14 @@ class Client:
771
769
  )
772
770
  return result
773
771
 
774
- def __create_qualified_name__(self, type: str, display_name: str, local_qualifier: str = None,
772
+ def __create_qualified_name__(self, type_name: str, display_name: str, local_qualifier: str = None,
775
773
  version_identifier: str = None) -> str:
776
774
  """Helper function to create a qualified name for a given type and display name.
777
775
  If present, the local qualifier will be prepended to the qualified name."""
778
776
  EGERIA_LOCAL_QUALIFIER = os.environ.get("EGERIA_LOCAL_QUALIFIER", local_qualifier)
779
777
  # display_name = re.sub(r'\s','-',display_name.strip()) # This changes spaces between words to -; removing
780
-
778
+ if display_name is None:
779
+ raise InvalidParameterException("display_name is required")
781
780
  q_name = f"{type}::{display_name.strip()}"
782
781
  if EGERIA_LOCAL_QUALIFIER:
783
782
  q_name = f"{EGERIA_LOCAL_QUALIFIER}::{q_name}"
@@ -786,6 +785,45 @@ class Client:
786
785
  return q_name
787
786
 
788
787
 
788
+ async def _async_get_element_by_guid_(self, element_guid: str) -> dict | str:
789
+ """
790
+ Simplified, internal version of get_element_by_guid found in Classification Manager.
791
+ Retrieve an element by its guid. Async version.
792
+
793
+ Parameters
794
+ ----------
795
+ element_guid: str
796
+ - unique identifier for the element
797
+
798
+ Returns
799
+ -------
800
+ dict | str
801
+ Returns a string if no element found; otherwise a dict of the element.
802
+
803
+ Raises
804
+ ------
805
+ PyegeriaInvalidParameterException
806
+ one of the parameters is null or invalid or
807
+ PyegeriaAPIException
808
+ There is a problem adding the element properties to the metadata repository or
809
+ PyegeriaUnauthorizedException
810
+ the requesting user is not authorized to issue this request.
811
+ """
812
+
813
+ body = {
814
+ "class": "EffectiveTimeQueryRequestBody",
815
+ "effectiveTime": None,
816
+ }
817
+
818
+ url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/classification-manager/elements/"
819
+ f"{element_guid}?forLineage=false&forDuplicateProcessing=false")
820
+
821
+ response: Response = await self._async_make_request("POST", url, body_slimmer(body))
822
+
823
+ elements = response.json().get("element", NO_ELEMENTS_FOUND)
824
+
825
+ return elements
826
+
789
827
 
790
828
  if __name__ == "__main__":
791
829
  print("Main-__client")