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,1525 @@
1
+ """
2
+ SPDX-License-Identifier: Apache-2.0
3
+ Copyright Contributors to the ODPi Egeria project.
4
+
5
+ Manage locations in Egeria.
6
+
7
+ """
8
+
9
+ import asyncio
10
+ from typing import Optional
11
+
12
+ from pyegeria.core._server_client import ServerClient
13
+ from pyegeria.view.base_report_formats import get_report_spec_match
14
+ from pyegeria.view.base_report_formats import select_report_spec
15
+ from pyegeria.core.config import settings as app_settings
16
+ from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequestBody, NewElementRequestBody,
17
+ TemplateRequestBody, UpdateElementRequestBody,
18
+ NewRelationshipRequestBody, DeleteElementRequestBody, DeleteRelationshipRequestBody)
19
+ from pyegeria.view.output_formatter import generate_output, populate_columns_from_properties, \
20
+ _extract_referenceable_properties, get_required_relationships
21
+ from pyegeria.core.utils import dynamic_catch
22
+
23
+ EGERIA_LOCAL_QUALIFIER = app_settings.User_Profile.egeria_local_qualifier
24
+ from loguru import logger
25
+
26
+
27
+ class Location(ServerClient):
28
+ """
29
+ Manage Locations in Egeria..
30
+
31
+ This client provides asynchronous and synchronous helpers to create, update, search,
32
+ and relate Location elements and their subtypes (Campaign, StudyProject, Task, PersonalProject).
33
+
34
+ References
35
+
36
+
37
+ Parameters
38
+ -----------
39
+ view_server : str
40
+ The name of the View Server to connect to.
41
+ platform_url : str
42
+ URL of the server platform to connect to.
43
+ user_id : str
44
+ Default user identity for calls (can be overridden per call).
45
+ user_pwd : str, optional
46
+ Password for the user_id. If a token is supplied, this may be None.
47
+
48
+ Notes
49
+ -----
50
+ - Most high-level list/report methods accept an `output_format` and an optional `report_spec` and
51
+ delegate rendering to `pyegeria.output_formatter.generate_output` along with shared helpers such as
52
+ `populate_common_columns`.
53
+ - Private extractor methods follow the convention: `_extract_<entity>_properties(element, columns_struct)` and
54
+ must return the same `columns_struct` with per-column `value` fields populated.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ view_server: str,
60
+ platform_url: str,
61
+ user_id: str,
62
+ user_pwd: Optional[str] = None,
63
+ token: Optional[str] = None,
64
+ ):
65
+ self.view_server = view_server
66
+ self.platform_url = platform_url
67
+ self.user_id = user_id
68
+ self.user_pwd = user_pwd
69
+ self.ref_location_command_base: str = (
70
+ f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/location-arena"
71
+ )
72
+ self.url_marker = 'locations'
73
+ ServerClient.__init__(self, view_server, platform_url, user_id, user_pwd, token)
74
+
75
+ @dynamic_catch
76
+ async def _async_create_location(self, body: Optional[dict | NewElementRequestBody] = None) -> str:
77
+ """Create a new location. Async version.
78
+
79
+ Parameters
80
+ ----------
81
+ body : dict | NewElementRequestBody, optional
82
+ The properties for the location.
83
+
84
+ Returns
85
+ -------
86
+ str
87
+ The unique identifier of the newly created location.
88
+
89
+ Raises
90
+ ------
91
+ PyegeriaException
92
+ If there are issues in communications, message format, or Egeria errors.
93
+
94
+ Notes
95
+ -----
96
+ Sample JSON body:
97
+ ```json
98
+ {
99
+ "class" : "NewElementRequestBody",
100
+ "properties": {
101
+ "class" : "LocationProperties",
102
+ "qualifiedName": "Location::Add name here",
103
+ "displayName": "Add short name here",
104
+ "description": "Add description here"
105
+ }
106
+ }
107
+ ```
108
+ """
109
+ url = f"{self.ref_location_command_base}/locations"
110
+ return await self._async_create_element_body_request(url, ["LocationProperties"], body)
111
+
112
+ @dynamic_catch
113
+ def create_location(self, body: Optional[dict | NewElementRequestBody] = None) -> str:
114
+ """Create a new location.
115
+
116
+ Parameters
117
+ ----------
118
+ body : dict | NewElementRequestBody, optional
119
+ The properties for the location.
120
+
121
+ Returns
122
+ -------
123
+ str
124
+ The unique identifier of the newly created location.
125
+
126
+ Raises
127
+ ------
128
+ PyegeriaException
129
+ If there are issues in communications, message format, or Egeria errors.
130
+
131
+ Notes
132
+ -----
133
+ Sample JSON body:
134
+ ```json
135
+ {
136
+ "class" : "NewElementRequestBody",
137
+ "properties": {
138
+ "class" : "LocationProperties",
139
+ "qualifiedName": "Location::Add name here",
140
+ "displayName": "Add short name here",
141
+ "description": "Add description here"
142
+ }
143
+ }
144
+ ```
145
+ """
146
+ return asyncio.get_event_loop().run_until_complete(self._async_create_location(body))
147
+
148
+ #######
149
+
150
+ @dynamic_catch
151
+ async def _async_create_location_from_template(self, body: Optional[dict | TemplateRequestBody] = None) -> str:
152
+ """ Create a new metadata element to represent a Location using an existing metadata element as a template.
153
+ The template defines additional classifications and relationships that should be added to the new element.
154
+ Async version.
155
+
156
+ Parameters
157
+ ----------
158
+ body: dict
159
+ A dict representing the details of the Location to create.
160
+
161
+ Returns
162
+ -------
163
+ str - the guid of the created Location
164
+
165
+ Raises
166
+ ------
167
+ PyegeriaInvalidParameterException
168
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
169
+ PyegeriaAPIException
170
+ Raised by the server when an issue arises in processing a valid request
171
+ PyegeriaUnAuthorizedException
172
+ The principle specified by the user_id does not have authorization for the requested action
173
+
174
+ Notes
175
+ -----
176
+ JSON Structure looks like:
177
+
178
+ {
179
+ "class": "TemplateRequestBody",
180
+ "anchorGUID": "anchor GUID, if set then isOwnAnchor=false",
181
+ "isOwnAnchor": false,
182
+ "parentGUID": "parent GUID, if set, set all parameters beginning 'parent'",
183
+ "parentRelationshipTypeName": "open metadata type name",
184
+ "parentAtEnd1": true,
185
+ "templateGUID": "template GUID",
186
+ "replacementProperties": {
187
+ "class": "ElementProperties",
188
+ "propertyValueMap" : {
189
+ "propertyName" : {
190
+ "class": "PrimitiveTypePropertyValue",
191
+ "typeName": "string",
192
+ "primitiveTypeCategory" : "OM_PRIMITIVE_TYPE_STRING",
193
+ "primitiveValue" : "value of property"
194
+ }
195
+ }
196
+ },
197
+ "placeholderPropertyValues" : {
198
+ "placeholderProperty1Name" : "property1Value",
199
+ "placeholderProperty2Name" : "property2Value"
200
+ }
201
+ }
202
+
203
+ """
204
+ url = f"{self.ref_location_command_base}/locations/from-template"
205
+
206
+ return await self._async_create_element_from_template("POST", url, body)
207
+
208
+ @dynamic_catch
209
+ def create_location_from_template(self, body: Optional[dict | TemplateRequestBody] = None) -> str:
210
+ """ Create a new metadata element to represent a Location using an existing metadata element as a template.
211
+ The template defines additional classifications and relationships that should be added to the new element.
212
+
213
+ Parameters
214
+ ----------
215
+ body: dict
216
+ A dict representing the details of the Location to create.
217
+
218
+ Returns
219
+ -------
220
+ str - the guid of the created Location
221
+
222
+ Raises
223
+ ------
224
+ PyegeriaInvalidParameterException
225
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
226
+ PyegeriaAPIException
227
+ Raised by the server when an issue arises in processing a valid request
228
+ PyegeriaUnAuthorizedException
229
+ The principle specified by the user_id does not have authorization for the requested action
230
+
231
+ Notes
232
+ -----
233
+ JSON Structure looks like:
234
+
235
+ {
236
+ "class": "TemplateRequestBody",
237
+ "anchorGUID": "anchor GUID, if set then isOwnAnchor=false",
238
+ "isOwnAnchor": false,
239
+ "parentGUID": "parent GUID, if set, set all parameters beginning 'parent'",
240
+ "parentRelationshipTypeName": "open metadata type name",
241
+ "parentAtEnd1": true,
242
+ "templateGUID": "template GUID",
243
+ "replacementProperties": {
244
+ "class": "ElementProperties",
245
+ "propertyValueMap" : {
246
+ "propertyName" : {
247
+ "class": "PrimitiveTypePropertyValue",
248
+ "typeName": "string",
249
+ "primitiveTypeCategory" : "OM_PRIMITIVE_TYPE_STRING",
250
+ "primitiveValue" : "value of property"
251
+ }
252
+ }
253
+ },
254
+ "placeholderPropertyValues" : {
255
+ "placeholderProperty1Name" : "property1Value",
256
+ "placeholderProperty2Name" : "property2Value"
257
+ }
258
+ }
259
+
260
+ """
261
+ loop = asyncio.get_event_loop()
262
+ resp = loop.run_until_complete(self._async_create_location_from_template(body))
263
+ return resp
264
+
265
+ @dynamic_catch
266
+ async def _async_update_location(self, location_guid: str,
267
+ body: dict | UpdateElementRequestBody) -> None:
268
+ """Update an Location. Async version.
269
+
270
+ Parameters
271
+ ----------
272
+ location_guid: str
273
+ The guid of the Location to update.
274
+ body: dict | UpdateElementRequestBody, optional
275
+ A dict or UpdateElementRequestBody representing the updates to apply.
276
+
277
+ Returns
278
+ -------
279
+ None
280
+
281
+ Raises
282
+ ------
283
+ PyegeriaException
284
+ If there are issues in communications, message format, or Egeria errors.
285
+
286
+ Notes
287
+ -----
288
+ Sample JSON body:
289
+ ```json
290
+ {
291
+ "class" : "UpdateElementRequestBody",
292
+ "properties": {
293
+ "class" : "LocationProperties",
294
+ "displayName": "Updated Name"
295
+ }
296
+ }
297
+ ```
298
+ """
299
+ url = f"{self.ref_location_command_base}/locations/{location_guid}/update"
300
+ await self._async_update_element_body_request(url, ["LocationProperties"], body)
301
+
302
+ @dynamic_catch
303
+ def update_location(self, location_guid: str, body: dict | UpdateElementRequestBody) -> None:
304
+ """Update an Location.
305
+
306
+ Parameters
307
+ ----------
308
+ location_guid: str
309
+ The guid of the Location to update.
310
+ body: dict | UpdateElementRequestBody, optional
311
+ A dict or UpdateElementRequestBody representing the updates to apply.
312
+
313
+ Returns
314
+ -------
315
+ None
316
+
317
+ Raises
318
+ ------
319
+ PyegeriaException
320
+ If there are issues in communications, message format, or Egeria errors.
321
+
322
+ Notes
323
+ -----
324
+ Sample JSON body:
325
+ ```json
326
+ {
327
+ "class" : "UpdateElementRequestBody",
328
+ "properties": {
329
+ "class" : "LocationProperties",
330
+ "displayName": "Updated Name"
331
+ }
332
+ }
333
+ ```
334
+ """
335
+ return asyncio.get_event_loop().run_until_complete(
336
+ self._async_update_location(location_guid, body))
337
+
338
+ @dynamic_catch
339
+ async def _async_link_peer_locations(self, location1_guid: str, location2_guid: str,
340
+ body: Optional[dict | NewRelationshipRequestBody] = None) -> None:
341
+ """Link peer locations. Async version.
342
+
343
+ Parameters
344
+ ----------
345
+ location1_guid : str
346
+ The unique identifier of the first location.
347
+ location2_guid : str
348
+ The unique identifier of the second location.
349
+ body : dict | NewRelationshipRequestBody, optional
350
+ The properties for the relationship.
351
+
352
+ Returns
353
+ -------
354
+ None
355
+
356
+ Raises
357
+ ------
358
+ PyegeriaException
359
+ If there are issues in communications, message format, or Egeria errors.
360
+
361
+ Notes
362
+ -----
363
+ Sample JSON body:
364
+ ```json
365
+ {
366
+ "class" : "NewRelationshipRequestBody",
367
+ "properties": {
368
+ "class": "AdjacentLocationProperties",
369
+ "label": "add label here",
370
+ "description": "add description here"
371
+ }
372
+ }
373
+ ```
374
+ """
375
+ url = f"{self.ref_location_command_base}/locations/{location1_guid}/adjacent-locations/{location2_guid}/attach"
376
+ await self._async_new_relationship_request(url, ["AdjacentLocationProperties"], body)
377
+ logger.info(f"Linking location {location1_guid} to location {location2_guid}")
378
+
379
+ @dynamic_catch
380
+ def link_peer_locations(self, location1_guid: str, location2_guid: str,
381
+ body: Optional[dict | NewRelationshipRequestBody] = None):
382
+ """Link peer locations.
383
+
384
+ Parameters
385
+ ----------
386
+ location1_guid : str
387
+ The unique identifier of the first location.
388
+ location2_guid : str
389
+ The unique identifier of the second location.
390
+ body : dict | NewRelationshipRequestBody, optional
391
+ The properties for the relationship.
392
+
393
+ Returns
394
+ -------
395
+ None
396
+
397
+ Raises
398
+ ------
399
+ PyegeriaException
400
+ If there are issues in communications, message format, or Egeria errors.
401
+
402
+ Notes
403
+ -----
404
+ Sample JSON body:
405
+ ```json
406
+ {
407
+ "class" : "NewRelationshipRequestBody",
408
+ "properties": {
409
+ "class": "AdjacentLocationProperties",
410
+ "label": "add label here",
411
+ "description": "add description here"
412
+ }
413
+ }
414
+ ```
415
+ """
416
+ loop = asyncio.get_event_loop()
417
+ loop.run_until_complete(self._async_link_peer_locations(location1_guid, location2_guid, body))
418
+
419
+ @dynamic_catch
420
+ async def _async_detach_peer_locations(self, location1_guid: str, location2_guid: str,
421
+ body: Optional[dict | DeleteRelationshipRequestBody] = None) -> None:
422
+ """ Unlink peer locations. Async version.
423
+
424
+ Parameters
425
+ ----------
426
+ location1_guid: str
427
+ The unique identifier of the subscriber.
428
+ location2_guid: str
429
+ The unique identifier of the subscription.
430
+ body: dict | DeleteRelationshipRequestBody, optional, default = None
431
+ A structure representing the details of the relationship.
432
+
433
+ Returns
434
+ -------
435
+ Nothing
436
+
437
+ Raises
438
+ ------
439
+ PyegeriaInvalidParameterException
440
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
441
+ PyegeriaAPIException
442
+ Raised by the server when an issue arises in processing a valid request
443
+ NotAuthorizedException
444
+ The principle specified by the user_id does not have authorization for the requested action
445
+
446
+ Notes
447
+ -----
448
+ JSON Structure looks like:
449
+ {
450
+ "class": "DeleteRelationshipRequestBody",
451
+ "deleteMethod": "LOOK_FOR_LINEAGE",
452
+ "externalSourceGUID": "add guid here",
453
+ "externalSourceName": "add qualified name here",
454
+ "effectiveTime": "{{$isoTimestamp}}",
455
+ "forLineage": false,
456
+ "forDuplicateProcessing": false
457
+ }
458
+ """
459
+ url = (f"{self.command_root}/elements/{location1_guid}/locations/{location2_guid}/detach")
460
+
461
+ await self._async_delete_element_request(url, body)
462
+ logger.info(f"Unlink {location1_guid} from location {location2_guid}")
463
+
464
+ def detach_peer_locations(self, location1_guid: str, location2_guid: str,
465
+ body: Optional[dict | DeleteRelationshipRequestBody] = None):
466
+ """ Unlink peer locations.
467
+
468
+ Parameters
469
+ ----------
470
+ location1_guid: str
471
+ The unique identifier of the subscriber.
472
+ location2_guid: str
473
+ The unique identifier of the subscription.
474
+ body: dict | DeleteRelationshipRequestBody, optional, default = None
475
+ A structure representing the details of the relationship.
476
+
477
+ Returns
478
+ -------
479
+ Nothing
480
+
481
+ Raises
482
+ ------
483
+ PyegeriaInvalidParameterException
484
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
485
+ PyegeriaAPIException
486
+ Raised by the server when an issue arises in processing a valid request
487
+ NotAuthorizedException
488
+ The principle specified by the user_id does not have authorization for the requested action
489
+
490
+ Notes
491
+ -----
492
+ JSON Structure looks like:
493
+ {
494
+ "class": "DeleteRelationshipRequestBody",
495
+ "deleteMethod": "LOOK_FOR_LINEAGE",
496
+ "externalSourceGUID": "add guid here",
497
+ "externalSourceName": "add qualified name here",
498
+ "effectiveTime": "{{$isoTimestamp}}",
499
+ "forLineage": false,
500
+ "forDuplicateProcessing": false
501
+ }
502
+ """
503
+ loop = asyncio.get_event_loop()
504
+ loop.run_until_complete(self._async_detach_peer_locations(location1_guid, location2_guid, body))
505
+
506
+ @dynamic_catch
507
+ async def _async_link_nested_location(self, location_guid: str, nested_location_guid: str,
508
+ body: Optional[dict | NewRelationshipRequestBody] = None) -> None:
509
+ """ AAttach a super location to a nested location.
510
+ Async version.
511
+
512
+ Parameters
513
+ ----------
514
+ location_guid: str
515
+ The unique identifier of the location.
516
+ nested_location_guid: str
517
+ The identifier of the nested location.
518
+ body: dict | NewRelationshipRequestBody, optional, default = None
519
+ A structure representing the details of the relationship.
520
+
521
+ Returns
522
+ -------
523
+ Nothing
524
+
525
+ Raises
526
+ ------
527
+ PyegeriaInvalidParameterException
528
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
529
+ PyegeriaAPIException
530
+ Raised by the server when an issue arises in processing a valid request
531
+ NotAuthorizedException
532
+ The principle specified by the user_id does not have authorization for the requested action
533
+
534
+ Notes
535
+ -----
536
+ JSON Structure looks like:
537
+ {
538
+ "class" : "NewRelationshipRequestBody",
539
+ "externalSourceGUID": "add guid here",
540
+ "externalSourceName": "add qualified name here",
541
+ "effectiveTime" : "{{$isoTimestamp}}",
542
+ "forLineage" : false,
543
+ "forDuplicateProcessing" : false,
544
+ "properties": {
545
+ "class": "NestedLocationProperties",
546
+ "label": "add label here",
547
+ "description": "add description here",
548
+ "effectiveFrom": "{{$isoTimestamp}}",
549
+ "effectiveTo": "{{$isoTimestamp}}"
550
+ }
551
+ }
552
+
553
+ """
554
+
555
+ url = f"{self.ref_location_command_base}/locations/{location_guid}/nested-locations/{nested_location_guid}/attach"
556
+ await self._async_new_relationship_request(url, ["NestedLocationProperties"], body)
557
+ logger.info(f"Linking element {location_guid} to nested location {nested_location_guid}")
558
+
559
+ @dynamic_catch
560
+ def link_nested_location(self, location_guid: str, nested_location_guid: str,
561
+ body: Optional[dict | NewRelationshipRequestBody] = None):
562
+ """ AAttach a super location to a nested location.
563
+
564
+ Parameters
565
+ ----------
566
+ location_guid: str
567
+ The unique identifier of the location.
568
+ nested_location_guid: str
569
+ The identifier of the nested location.
570
+ body: dict | NewRelationshipRequestBody, optional, default = None
571
+ A structure representing the details of the relationship.
572
+
573
+ Returns
574
+ -------
575
+ Nothing
576
+
577
+ Raises
578
+ ------
579
+ PyegeriaInvalidParameterException
580
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
581
+ PyegeriaAPIException
582
+ Raised by the server when an issue arises in processing a valid request
583
+ NotAuthorizedException
584
+ The principle specified by the user_id does not have authorization for the requested action
585
+
586
+ Notes
587
+ -----
588
+ JSON Structure looks like:
589
+ {
590
+ "class" : "NewRelationshipRequestBody",
591
+ "externalSourceGUID": "add guid here",
592
+ "externalSourceName": "add qualified name here",
593
+ "effectiveTime" : "{{$isoTimestamp}}",
594
+ "forLineage" : false,
595
+ "forDuplicateProcessing" : false,
596
+ "properties": {
597
+ "class": "NestedLocationProperties",
598
+ "label": "add label here",
599
+ "description": "add description here",
600
+ "effectiveFrom": "{{$isoTimestamp}}",
601
+ "effectiveTo": "{{$isoTimestamp}}"
602
+ }
603
+ }
604
+
605
+ """
606
+ loop = asyncio.get_event_loop()
607
+ loop.run_until_complete(self._async_link_nested_location(location_guid, nested_location_guid, body))
608
+
609
+ @dynamic_catch
610
+ async def _async_detach_nested_location(self, location_guid: str, nested_location_guid: str,
611
+ body: Optional[dict | DeleteRelationshipRequestBody] = None) -> None:
612
+ """ Detach a nested location from a location. Async version.
613
+
614
+ Parameters
615
+ ----------
616
+ location_guid: str
617
+ The unique identifier of the location.
618
+ nested_location_guid: str
619
+ The unique identifier of the nested location.
620
+ body: dict | DeleteRelationshipRequestBody, optional, default = None
621
+ A structure representing the details of the relationship.
622
+
623
+ Returns
624
+ -------
625
+ Nothing
626
+
627
+ Raises
628
+ ------
629
+ PyegeriaInvalidParameterException
630
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
631
+ PyegeriaAPIException
632
+ Raised by the server when an issue arises in processing a valid request
633
+ NotAuthorizedException
634
+ The principle specified by the user_id does not have authorization for the requested action
635
+
636
+ Notes
637
+ -----
638
+ JSON Structure looks like:
639
+ {
640
+ "class": "DeleteRelationshipRequestBody",
641
+ "deleteMethod": "LOOK_FOR_LINEAGE",
642
+ "externalSourceGUID": "add guid here",
643
+ "externalSourceName": "add qualified name here",
644
+ "effectiveTime": "{{$isoTimestamp}}",
645
+ "forLineage": false,
646
+ "forDuplicateProcessing": false
647
+ }
648
+ """
649
+ url = (
650
+ f"{self.ref_location_command_base}/locations{location_guid}/nested-locations/{nested_location_guid}/detach")
651
+
652
+ await self._async_delete_element_request(url, body)
653
+ logger.info(f"Detached location {location_guid} from nested location {nested_location_guid}")
654
+
655
+ def detach_nested_location(self, location_guid: str, nested_location_guid: str,
656
+ body: Optional[dict | DeleteRelationshipRequestBody] = None):
657
+ """ Detach a nested location from a location.
658
+
659
+ Parameters
660
+ ----------
661
+ location_guid: str
662
+ The unique identifier of the location.
663
+ nested_location_guid: str
664
+ The unique identifier of the nested location.
665
+ body: dict | DeleteRelationshipRequestBody, optional, default = None
666
+ A structure representing the details of the relationship.
667
+
668
+ Returns
669
+ -------
670
+ Nothing
671
+
672
+ Raises
673
+ ------
674
+ PyegeriaInvalidParameterException
675
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
676
+ PyegeriaAPIException
677
+ Raised by the server when an issue arises in processing a valid request
678
+ NotAuthorizedException
679
+ The principle specified by the user_id does not have authorization for the requested action
680
+
681
+ Notes
682
+ -----
683
+ JSON Structure looks like:
684
+ {
685
+ "class": "DeleteRelationshipRequestBody",
686
+ "deleteMethod": "LOOK_FOR_LINEAGE",
687
+ "externalSourceGUID": "add guid here",
688
+ "externalSourceName": "add qualified name here",
689
+ "effectiveTime": "{{$isoTimestamp}}",
690
+ "forLineage": false,
691
+ "forDuplicateProcessing": false
692
+ }
693
+ """
694
+ loop = asyncio.get_event_loop()
695
+ loop.run_until_complete(self._async_detach_nested_location(location_guid, nested_location_guid, body))
696
+
697
+ @dynamic_catch
698
+ async def _async_link_known_location(self, element_guid: str, location_guid: str,
699
+ body: Optional[dict | NewRelationshipRequestBody] = None) -> None:
700
+ """ Attach an element to its location.
701
+ Async version.
702
+
703
+ Parameters
704
+ ----------
705
+ element_guid: str
706
+ The unique identifier of the element.
707
+ location_guid: str
708
+ The identifier of the location.
709
+ body: dict | NewRelationshipRequestBody, optional, default = None
710
+ A structure representing the details of the relationship.
711
+
712
+ Returns
713
+ -------
714
+ Nothing
715
+
716
+ Raises
717
+ ------
718
+ PyegeriaInvalidParameterException
719
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
720
+ PyegeriaAPIException
721
+ Raised by the server when an issue arises in processing a valid request
722
+ NotAuthorizedException
723
+ The principle specified by the user_id does not have authorization for the requested action
724
+
725
+ Notes
726
+ -----
727
+ JSON Structure looks like:
728
+ {
729
+ "class" : "NewRelationshipRequestBody",
730
+ "externalSourceGUID": "add guid here",
731
+ "externalSourceName": "add qualified name here",
732
+ "effectiveTime" : "{{$isoTimestamp}}",
733
+ "forLineage" : false,
734
+ "forDuplicateProcessing" : false,
735
+ "properties": {
736
+ "class": "KnownLocationProperties",
737
+ "label": "add label here",
738
+ "description": "add description here",
739
+ "effectiveFrom": "{{$isoTimestamp}}",
740
+ "effectiveTo": "{{$isoTimestamp}}"
741
+ }
742
+ }
743
+
744
+ """
745
+
746
+ url = f"{self.ref_location_command_base}elements/{element_guid}/known-locations/{location_guid}/attach"
747
+ await self._async_new_relationship_request(url, ["KnownLocationProperties"], body)
748
+ logger.info(f"Linking element {element_guid} to location {location_guid}")
749
+
750
+ @dynamic_catch
751
+ def link_known_location(self, element_guid: str, location_guid: str,
752
+ body: Optional[dict | NewRelationshipRequestBody] = None):
753
+ """ Attach an element to its location.
754
+ Async version.
755
+
756
+ Parameters
757
+ ----------
758
+ element_guid: str
759
+ The unique identifier of the element.
760
+ location_guid: str
761
+ The identifier of the location.
762
+ body: dict | NewRelationshipRequestBody, optional, default = None
763
+ A structure representing the details of the relationship.
764
+
765
+ Returns
766
+ -------
767
+ Nothing
768
+
769
+ Raises
770
+ ------
771
+ PyegeriaInvalidParameterException
772
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
773
+ PyegeriaAPIException
774
+ Raised by the server when an issue arises in processing a valid request
775
+ NotAuthorizedException
776
+ The principle specified by the user_id does not have authorization for the requested action
777
+
778
+ Notes
779
+ -----
780
+ JSON Structure looks like:
781
+ {
782
+ "class" : "NewRelationshipRequestBody",
783
+ "externalSourceGUID": "add guid here",
784
+ "externalSourceName": "add qualified name here",
785
+ "effectiveTime" : "{{$isoTimestamp}}",
786
+ "forLineage" : false,
787
+ "forDuplicateProcessing" : false,
788
+ "properties": {
789
+ "class": "KnownLocationProperties",
790
+ "label": "add label here",
791
+ "description": "add description here",
792
+ "effectiveFrom": "{{$isoTimestamp}}",
793
+ "effectiveTo": "{{$isoTimestamp}}"
794
+ }
795
+ }
796
+
797
+ """
798
+ loop = asyncio.get_event_loop()
799
+ loop.run_until_complete(self._async_link_known_location(element_guid, location_guid, body))
800
+
801
+ @dynamic_catch
802
+ async def _async_detach_known_location(self, element_guid: str, location_guid: str,
803
+ body: Optional[dict | DeleteRelationshipRequestBody] = None) -> None:
804
+ """ Detach an element from an known location. Async version.
805
+
806
+ Parameters
807
+ ----------
808
+ element_guid: str
809
+ The unique identifier of the element.
810
+ location_guid: str
811
+ The unique identifier of the location.
812
+ body: dict | DeleteRelationshipRequestBody, optional, default = None
813
+ A structure representing the details of the relationship.
814
+
815
+ Returns
816
+ -------
817
+ Nothing
818
+
819
+ Raises
820
+ ------
821
+ PyegeriaInvalidParameterException
822
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
823
+ PyegeriaAPIException
824
+ Raised by the server when an issue arises in processing a valid request
825
+ NotAuthorizedException
826
+ The principle specified by the user_id does not have authorization for the requested action
827
+
828
+ Notes
829
+ -----
830
+ JSON Structure looks like:
831
+ {
832
+ "class": "DeleteRelationshipRequestBody",
833
+ "deleteMethod": "LOOK_FOR_LINEAGE",
834
+ "externalSourceGUID": "add guid here",
835
+ "externalSourceName": "add qualified name here",
836
+ "effectiveTime": "{{$isoTimestamp}}",
837
+ "forLineage": false,
838
+ "forDuplicateProcessing": false
839
+ }
840
+ """
841
+ url = (f"{self.command_root}/location_arena/elements/{element_guid}/known-locations/{location_guid}/detach")
842
+
843
+ await self._async_delete_element_request(url, body)
844
+ logger.info(f"Detached element {element_guid} from location {location_guid}")
845
+
846
+ def detach_known_location(self, element_guid: str, location_guid: str,
847
+ body: Optional[dict | DeleteRelationshipRequestBody] = None):
848
+ """ Detach an element from an known location. Async version.
849
+
850
+ Parameters
851
+ ----------
852
+ element_guid: str
853
+ The unique identifier of the element.
854
+ location_guid: str
855
+ The unique identifier of the location.
856
+ body: dict | DeleteRelationshipRequestBody, optional, default = None
857
+ A structure representing the details of the relationship.
858
+
859
+ Returns
860
+ -------
861
+ Nothing
862
+
863
+ Raises
864
+ ------
865
+ PyegeriaInvalidParameterException
866
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
867
+ PyegeriaAPIException
868
+ Raised by the server when an issue arises in processing a valid request
869
+ NotAuthorizedException
870
+ The principle specified by the user_id does not have authorization for the requested action
871
+
872
+ Notes
873
+ -----
874
+ JSON Structure looks like:
875
+ {
876
+ "class": "DeleteRelationshipRequestBody",
877
+ "deleteMethod": "LOOK_FOR_LINEAGE",
878
+ "externalSourceGUID": "add guid here",
879
+ "externalSourceName": "add qualified name here",
880
+ "effectiveTime": "{{$isoTimestamp}}",
881
+ "forLineage": false,
882
+ "forDuplicateProcessing": false
883
+ }
884
+ """
885
+ loop = asyncio.get_event_loop()
886
+ loop.run_until_complete(self._async_detach_known_location(element_guid, location_guid, body))
887
+
888
+ @dynamic_catch
889
+ async def _async_delete_location(self, location_guid: str,
890
+ body: Optional[dict | DeleteElementRequestBody] = None,
891
+ cascade: bool = False) -> None:
892
+ """Delete a location. Async version.
893
+
894
+ Parameters
895
+ ----------
896
+ location_guid : str
897
+ The GUID of the location to delete.
898
+ body : dict | DeleteElementRequestBody, optional
899
+ Request body for deletion.
900
+ cascade : bool, optional, default=False
901
+ If true, performs a cascade delete.
902
+
903
+ Returns
904
+ -------
905
+ None
906
+
907
+ Raises
908
+ ------
909
+ PyegeriaException
910
+ If there are issues in communications, message format, or Egeria errors.
911
+
912
+ Notes
913
+ -----
914
+ Sample JSON body:
915
+ ```json
916
+ {
917
+ "class" : "DeleteElementRequestBody",
918
+ "cascadedDelete" : false
919
+ }
920
+ ```
921
+ """
922
+ url = f"{self.ref_location_command_base}/locations/{location_guid}/delete"
923
+
924
+ await self._async_delete_element_request(url, body, cascade)
925
+ logger.info(f"Deleted location {location_guid} with cascade {cascade}")
926
+
927
+ @dynamic_catch
928
+ def delete_location(self, location_guid: str, body: Optional[dict | DeleteElementRequestBody] = None,
929
+ cascade: bool = False) -> None:
930
+ """Delete a location.
931
+
932
+ Parameters
933
+ ----------
934
+ location_guid : str
935
+ The GUID of the location to delete.
936
+ body : dict | DeleteElementRequestBody, optional
937
+ Request body for deletion.
938
+ cascade : bool, optional, default=False
939
+ If true, performs a cascade delete.
940
+
941
+ Returns
942
+ -------
943
+ None
944
+
945
+ Raises
946
+ ------
947
+ PyegeriaException
948
+ If there are issues in communications, message format, or Egeria errors.
949
+
950
+ Notes
951
+ -----
952
+ Sample JSON body:
953
+ ```json
954
+ {
955
+ "class" : "DeleteElementRequestBody",
956
+ "cascadedDelete" : false
957
+ }
958
+ ```
959
+ """
960
+ loop = asyncio.get_event_loop()
961
+ loop.run_until_complete(self._async_delete_location(location_guid, body, cascade))
962
+
963
+ @dynamic_catch
964
+ async def _async_find_locations(self, search_string: str = "*",
965
+ starts_with: bool = True, ends_with: bool = False,
966
+ ignore_case: bool = False,
967
+ anchor_domain: Optional[str] = None,
968
+ metadata_element_type: Optional[str] = None,
969
+ metadata_element_subtypes: Optional[list[str]] = None,
970
+ skip_relationships: Optional[list[str]] = None,
971
+ include_only_relationships: Optional[list[str]] = None,
972
+ skip_classified_elements: Optional[list[str]] = None,
973
+ include_only_classified_elements: Optional[list[str]] = None,
974
+ graph_query_depth: int = 3,
975
+ governance_zone_filter: Optional[list[str]] = None, as_of_time: Optional[str] = None,
976
+ effective_time: Optional[str] = None, relationship_page_size: int = 0,
977
+ limit_results_by_status: Optional[list[str]] = None, sequencing_order: Optional[str] = None,
978
+ sequencing_property: Optional[str] = None,
979
+ output_format: str = "JSON",
980
+ report_spec: str | dict = "Referenceable",
981
+ start_from: int = 0, page_size: int = 100,
982
+ property_names: Optional[list[str]] = None,
983
+ body: Optional[dict | SearchStringRequestBody] = None) -> list | str:
984
+ """ Retrieve the list of location metadata elements that contain the search string. Async Version.
985
+
986
+ Parameters
987
+ ----------
988
+ search_string: str
989
+ Search string to match against - None or '*' indicate match against all locations.
990
+ starts_with : bool, [default=True], optional
991
+ Starts with the supplied string.
992
+ ends_with : bool, [default=False], optional
993
+ Ends with the supplied string
994
+ ignore_case : bool, [default=False], optional
995
+ Ignore case when searching
996
+ anchor_domain: str, optional
997
+ The anchor domain to search in.
998
+ metadata_element_type: str, optional
999
+ The type of metadata element to search for.
1000
+ metadata_element_subtypes: list[str], optional
1001
+ The subtypes of metadata element to search for.
1002
+ skip_relationships: list[str], optional
1003
+ The types of relationships to skip.
1004
+ include_only_relationships: list[str], optional
1005
+ The types of relationships to include.
1006
+ skip_classified_elements: list[str], optional
1007
+ The types of classified elements to skip.
1008
+ include_only_classified_elements: list[str], optional
1009
+ The types of classified elements to include.
1010
+ graph_query_depth: int, [default=3], optional
1011
+ The depth of the graph query.
1012
+ governance_zone_filter: list[str], optional
1013
+ The governance zones to search in.
1014
+ as_of_time: str, optional
1015
+ The time to search as of.
1016
+ effective_time: str, optional
1017
+ The effective time to search at.
1018
+ relationship_page_size: int, [default=0], optional
1019
+ The page size for relationships.
1020
+ limit_results_by_status: list[str], optional
1021
+ The statuses to limit results by.
1022
+ sequencing_order: str, optional
1023
+ The order to sequence results by.
1024
+ sequencing_property: str, optional
1025
+ The property to sequence results by.
1026
+ output_format: str, default = "JSON"
1027
+ - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
1028
+ report_spec: str | dict , optional, default = "Referenceable"
1029
+ - The desired output columns/fields to include.
1030
+ start_from: int, [default=0], optional
1031
+ When multiple pages of results are available, the page number to start from.
1032
+ page_size: int, [default=100]
1033
+ The number of items to return in a single page.
1034
+ property_names: list[str], optional
1035
+ The names of properties to search for.
1036
+ body: dict | SearchStringRequestBody, optional, default = None
1037
+ - if provided, the search parameters in the body will supercede other attributes, such as "search_string"
1038
+
1039
+ Returns
1040
+ -------
1041
+ List | str
1042
+
1043
+ Output depends on the output format specified.
1044
+
1045
+ Raises
1046
+ ------
1047
+
1048
+ ValidationError
1049
+ If the client passes incorrect parameters on the request that don't conform to the data model.
1050
+ PyegeriaException
1051
+ Issues raised in communicating or server side processing.
1052
+ NotAuthorizedException
1053
+ The principle specified by the user_id does not have authorization for the requested action
1054
+
1055
+ """
1056
+ url = f"{self.ref_location_command_base}/locations/by-search-string"
1057
+ response = await self._async_find_request(url, _type="Location", _gen_output=self._generate_location_output,
1058
+ search_string=search_string, starts_with=starts_with,
1059
+ ends_with=ends_with, ignore_case=ignore_case,
1060
+ anchor_domain=anchor_domain,
1061
+ metadata_element_type=metadata_element_type,
1062
+ metadata_element_subtypes=metadata_element_subtypes,
1063
+ skip_relationships=skip_relationships,
1064
+ include_only_relationships=include_only_relationships,
1065
+ skip_classified_elements=skip_classified_elements,
1066
+ include_only_classified_elements=include_only_classified_elements,
1067
+ graph_query_depth=graph_query_depth,
1068
+ governance_zone_filter=governance_zone_filter,
1069
+ as_of_time=as_of_time, effective_time=effective_time,
1070
+ relationship_page_size=relationship_page_size,
1071
+ limit_results_by_status=limit_results_by_status,
1072
+ sequencing_order=sequencing_order,
1073
+ sequencing_property=sequencing_property,
1074
+ output_format=output_format, report_spec=report_spec,
1075
+ start_from=start_from, page_size=page_size,
1076
+ property_names=property_names, body=body)
1077
+
1078
+ return response
1079
+
1080
+ @dynamic_catch
1081
+ def find_locations(self, search_string: str = "*",
1082
+ starts_with: bool = True, ends_with: bool = False,
1083
+ ignore_case: bool = False,
1084
+ anchor_domain: Optional[str] = None,
1085
+ metadata_element_type: Optional[str] = None,
1086
+ metadata_element_subtypes: Optional[list[str]] = None,
1087
+ skip_relationships: Optional[list[str]] = None,
1088
+ include_only_relationships: Optional[list[str]] = None,
1089
+ skip_classified_elements: Optional[list[str]] = None,
1090
+ include_only_classified_elements: Optional[list[str]] = None,
1091
+ graph_query_depth: int = 3,
1092
+ governance_zone_filter: Optional[list[str]] = None, as_of_time: Optional[str] = None,
1093
+ effective_time: Optional[str] = None, relationship_page_size: int = 0,
1094
+ limit_results_by_status: Optional[list[str]] = None, sequencing_order: Optional[str] = None,
1095
+ sequencing_property: Optional[str] = None,
1096
+ output_format: str = "JSON",
1097
+ report_spec: str | dict = "Referenceable",
1098
+ start_from: int = 0, page_size: int = 100,
1099
+ property_names: Optional[list[str]] = None,
1100
+ body: Optional[dict | SearchStringRequestBody] = None) -> list | str:
1101
+ """ Retrieve the list of location metadata elements that contain the search string.
1102
+
1103
+ Parameters
1104
+ ----------
1105
+ search_string: str
1106
+ Search string to match against - None or '*' indicate match against all locations.
1107
+ starts_with : bool, [default=True], optional
1108
+ Starts with the supplied string.
1109
+ ends_with : bool, [default=False], optional
1110
+ Ends with the supplied string
1111
+ ignore_case : bool, [default=False], optional
1112
+ Ignore case when searching
1113
+ anchor_domain: str, optional
1114
+ The anchor domain to search in.
1115
+ metadata_element_type: str, optional
1116
+ The type of metadata element to search for.
1117
+ metadata_element_subtypes: list[str], optional
1118
+ The subtypes of metadata element to search for.
1119
+ skip_relationships: list[str], optional
1120
+ The types of relationships to skip.
1121
+ include_only_relationships: list[str], optional
1122
+ The types of relationships to include.
1123
+ skip_classified_elements: list[str], optional
1124
+ The types of classified elements to skip.
1125
+ include_only_classified_elements: list[str], optional
1126
+ The types of classified elements to include.
1127
+ graph_query_depth: int, [default=3], optional
1128
+ The depth of the graph query.
1129
+ governance_zone_filter: list[str], optional
1130
+ The governance zones to search in.
1131
+ as_of_time: str, optional
1132
+ The time to search as of.
1133
+ effective_time: str, optional
1134
+ The effective time to search at.
1135
+ relationship_page_size: int, [default=0], optional
1136
+ The page size for relationships.
1137
+ limit_results_by_status: list[str], optional
1138
+ The statuses to limit results by.
1139
+ sequencing_order: str, optional
1140
+ The order to sequence results by.
1141
+ sequencing_property: str, optional
1142
+ The property to sequence results by.
1143
+ output_format: str, default = "JSON"
1144
+ - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
1145
+ report_spec: str | dict , optional, default = "Referenceable"
1146
+ - The desired output columns/fields to include.
1147
+ start_from: int, [default=0], optional
1148
+ When multiple pages of results are available, the page number to start from.
1149
+ page_size: int, [default=100]
1150
+ The number of items to return in a single page.
1151
+ property_names: list[str], optional
1152
+ The names of properties to search for.
1153
+ body: dict | SearchStringRequestBody, optional, default = None
1154
+ - if provided, the search parameters in the body will supercede other attributes, such as "search_string"
1155
+
1156
+ Returns
1157
+ -------
1158
+ List | str
1159
+
1160
+ Output depends on the output format specified.
1161
+
1162
+ Raises
1163
+ ------
1164
+
1165
+ ValidationError
1166
+ If the client passes incorrect parameters on the request that don't conform to the data model.
1167
+ PyegeriaException
1168
+ Issues raised in communicating or server side processing.
1169
+ NotAuthorizedException
1170
+ The principle specified by the user_id does not have authorization for the requested action
1171
+
1172
+ """
1173
+ loop = asyncio.get_event_loop()
1174
+ return loop.run_until_complete(self._async_find_locations(search_string=search_string,
1175
+ starts_with=starts_with,
1176
+ ends_with=ends_with,
1177
+ ignore_case=ignore_case,
1178
+ anchor_domain=anchor_domain,
1179
+ metadata_element_type=metadata_element_type,
1180
+ metadata_element_subtypes=metadata_element_subtypes,
1181
+ skip_relationships=skip_relationships,
1182
+ include_only_relationships=include_only_relationships,
1183
+ skip_classified_elements=skip_classified_elements,
1184
+ include_only_classified_elements=include_only_classified_elements,
1185
+ graph_query_depth=graph_query_depth,
1186
+ governance_zone_filter=governance_zone_filter,
1187
+ as_of_time=as_of_time,
1188
+ effective_time=effective_time,
1189
+ relationship_page_size=relationship_page_size,
1190
+ limit_results_by_status=limit_results_by_status,
1191
+ sequencing_order=sequencing_order,
1192
+ sequencing_property=sequencing_property,
1193
+ output_format=output_format,
1194
+ report_spec=report_spec,
1195
+ start_from=start_from,
1196
+ page_size=page_size,
1197
+ property_names=property_names,
1198
+ body=body))
1199
+
1200
+ @dynamic_catch
1201
+ async def _async_get_locations_by_name(self, filter_string: Optional[str] = None,
1202
+ classification_names: Optional[list[str]] = None,
1203
+ body: Optional[dict | FilterRequestBody] = None,
1204
+ start_from: int = 0, page_size: int = 0,
1205
+ output_format: str = 'JSON',
1206
+ report_spec: str | dict = "Locations") -> list | str:
1207
+ """ Returns the list of Locations with a particular name. Async version.
1208
+
1209
+ Parameters
1210
+ ----------
1211
+ filter_string: str,
1212
+ name to use to find matching locations.
1213
+ classification_names: list[str], optional, default = None
1214
+ type of collection to filter by - e.g., DataDict, Folder, Root
1215
+ body: dict, optional, default = None
1216
+ Provides, a full request body. If specified, the body supercedes the name parameter.
1217
+ start_from: int, [default=0], optional
1218
+ When multiple pages of results are available, the page number to start from.
1219
+ page_size: int, [default=None]
1220
+ The number of items to return in a single page. If not specified, the default will be taken from
1221
+ the class instance.
1222
+ output_format: str, default = "JSON"
1223
+ - one of "DICT", "MERMAID" or "JSON"
1224
+ report_spec: dict , optional, default = None
1225
+ The desired output columns/fields to include.
1226
+
1227
+ Returns
1228
+ -------
1229
+ List | str
1230
+
1231
+ A list of collections match matching the name. Returns a string if none found.
1232
+
1233
+ Raises
1234
+ ------
1235
+
1236
+ PyegeriaInvalidParameterException
1237
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1238
+ PyegeriaAPIException
1239
+ Raised by the server when an issue arises in processing a valid request
1240
+ NotAuthorizedException
1241
+ The principle specified by the user_id does not have authorization for the requested action
1242
+ """
1243
+ url = f"{self.ref_location_command_base}/locations/by-name"
1244
+ response = await self._async_get_name_request(url, _type="ExternalReference",
1245
+ _gen_output=self._generate_location_output,
1246
+ filter_string=filter_string,
1247
+ classification_names=classification_names,
1248
+ start_from=start_from, page_size=page_size,
1249
+ output_format=output_format, report_spec=report_spec,
1250
+ body=body)
1251
+
1252
+ return response
1253
+
1254
+ def get_locations_by_name(self, filter_string: Optional[str] = None, classification_names: Optional[list[str]] = None,
1255
+ body: Optional[dict | FilterRequestBody] = None,
1256
+ start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
1257
+ report_spec: str | dict = "Locations") -> list | str:
1258
+ """ Returns the list of Locations with a particular name.
1259
+
1260
+ Parameters
1261
+ ----------
1262
+ filter_string: str,
1263
+ name to use to find matching locations.
1264
+ classification_names: list[str], optional, default = None
1265
+ type of collection to filter by - e.g., DataDict, Folder, Root
1266
+ body: dict, optional, default = None
1267
+ Provides, a full request body. If specified, the body supercedes the name parameter.
1268
+ start_from: int, [default=0], optional
1269
+ When multiple pages of results are available, the page number to start from.
1270
+ page_size: int, [default=None]
1271
+ The number of items to return in a single page. If not specified, the default will be taken from
1272
+ the class instance.
1273
+ output_format: str, default = "JSON"
1274
+ - one of "DICT", "MERMAID" or "JSON"
1275
+ report_spec: dict , optional, default = None
1276
+ The desired output columns/fields to include.
1277
+
1278
+ Returns
1279
+ -------
1280
+ List | str
1281
+
1282
+ A list of collections match matching the name. Returns a string if none found.
1283
+
1284
+ Raises
1285
+ ------
1286
+
1287
+ PyegeriaInvalidParameterException
1288
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1289
+ PyegeriaAPIException
1290
+ Raised by the server when an issue arises in processing a valid request
1291
+ NotAuthorizedException
1292
+ The principle specified by the user_id does not have authorization for the requested action
1293
+ """
1294
+ return asyncio.get_event_loop().run_until_complete(
1295
+ self._async_get_locations_by_name(filter_string, classification_names, body, start_from, page_size,
1296
+ output_format, report_spec))
1297
+
1298
+ @dynamic_catch
1299
+ async def _async_get_location_by_guid(self, location_guid: str, element_type: Optional[str] = None,
1300
+ body: Optional[dict | GetRequestBody] = None, output_format: str = 'JSON',
1301
+ report_spec: str | dict = "Locations") -> dict | str:
1302
+ """Return the properties of a specific location. Async version.
1303
+
1304
+ Parameters
1305
+ ----------
1306
+ location_guid: str,
1307
+ unique identifier of the location to retrieve.
1308
+ element_type: str, default = None, optional
1309
+ type of element, etc.
1310
+ body: dict | GetRequestBody, optional, default = None
1311
+ full request body.
1312
+ output_format: str, default = "JSON"
1313
+ - one of "DICT", "MERMAID" or "JSON"
1314
+ report_spec: str | dict, optional, default = None
1315
+ The desired output columns/fields to include.
1316
+
1317
+ Returns
1318
+ -------
1319
+ dict | str
1320
+
1321
+ A JSON dict representing the specified collection. Returns a string if none found.
1322
+
1323
+ Raises
1324
+ ------
1325
+
1326
+ PyegeriaInvalidParameterException
1327
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1328
+ PyegeriaAPIException
1329
+ Raised by the server when an issue arises in processing a valid request
1330
+ NotAuthorizedException
1331
+ The principle specified by the user_id does not have authorization for the requested action
1332
+
1333
+ Notes
1334
+ ----
1335
+ Body sample:
1336
+ {
1337
+ "class": "GetRequestBody",
1338
+ "asOfTime": "{{$isoTimestamp}}",
1339
+ "effectiveTime": "{{$isoTimestamp}}",
1340
+ "forLineage": false,
1341
+ "forDuplicateProcessing": false
1342
+ }
1343
+ """
1344
+
1345
+ url = f"{self.ref_location_command_base}/locations/{location_guid}/retrieve"
1346
+ type = element_type if element_type else "Location"
1347
+
1348
+ response = await self._async_get_guid_request(url, _type=type,
1349
+ _gen_output=self._generate_location_output,
1350
+ output_format=output_format, report_spec=report_spec,
1351
+ body=body)
1352
+
1353
+ return response
1354
+
1355
+ @dynamic_catch
1356
+ def get_location_by_guid(self, location_guid: str, element_type: Optional[str] = None, body: Optional[dict | GetRequestBody] = None,
1357
+ output_format: str = 'JSON', report_spec: str | dict = "Locations") -> dict | str:
1358
+ """Return the properties of a specific location. Async version.
1359
+
1360
+ Parameters
1361
+ ----------
1362
+ location_guid: str,
1363
+ unique identifier of the location to retrieve.
1364
+ element_type: str, default = None, optional
1365
+ type of element, etc.
1366
+ body: dict | GetRequestBody, optional, default = None
1367
+ full request body.
1368
+ output_format: str, default = "JSON"
1369
+ - one of "DICT", "MERMAID" or "JSON"
1370
+ report_spec: str | dict, optional, default = None
1371
+ The desired output columns/fields to include.
1372
+
1373
+ Returns
1374
+ -------
1375
+ dict | str
1376
+
1377
+ A JSON dict representing the specified collection. Returns a string if none found.
1378
+
1379
+ Raises
1380
+ ------
1381
+
1382
+ PyegeriaInvalidParameterException
1383
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1384
+ PyegeriaAPIException
1385
+ Raised by the server when an issue arises in processing a valid request
1386
+ NotAuthorizedException
1387
+ The principle specified by the user_id does not have authorization for the requested action
1388
+
1389
+ Notes
1390
+ ----
1391
+ Body sample:
1392
+ {
1393
+ "class": "FilterRequestBody",
1394
+ "filter": "Add name here",
1395
+ "startFrom": 0,
1396
+ "pageSize": 10,
1397
+ "asOfTime": "{{$isoTimestamp}}",
1398
+ "effectiveTime": "{{$isoTimestamp}}",
1399
+ "forLineage": false,
1400
+ "forDuplicateProcessing": false,
1401
+ "limitResultsByStatus": ["ACTIVE"],
1402
+ "sequencingOrder": "PROPERTY_ASCENDING",
1403
+ "sequencingProperty": "qualifiedName"
1404
+ }
1405
+ """
1406
+ return asyncio.get_event_loop().run_until_complete(
1407
+ self._async_get_location_by_guid(location_guid, element_type, body, output_format, report_spec))
1408
+
1409
+ @dynamic_catch
1410
+ def _extract_location_properties(self, element: dict, columns_struct: dict) -> dict:
1411
+ """
1412
+ Extract common properties from a location element and populate into the provided columns_struct.
1413
+
1414
+ Args:
1415
+ element (dict): The location element
1416
+ columns_struct (dict): The columns structure to populate
1417
+
1418
+ Returns:
1419
+ dict: columns_struct with column 'value' fields populated
1420
+ """
1421
+ # First, populate from element.properties using the utility
1422
+ col_data = populate_columns_from_properties(element, columns_struct)
1423
+
1424
+ columns_list = col_data.get("formats", {}).get("attributes", [])
1425
+
1426
+ # Populate header-derived values
1427
+ header_props = _extract_referenceable_properties(element)
1428
+ for column in columns_list:
1429
+ key = column.get('key')
1430
+ if key in header_props:
1431
+ column['value'] = header_props.get(key)
1432
+ elif isinstance(key, str) and key.lower() == 'guid':
1433
+ column['value'] = header_props.get('GUID')
1434
+
1435
+ # Derived/computed fields
1436
+ # locationCategories are classifications
1437
+ classification_names = ""
1438
+ classifications = element.get('elementHeader', {}).get("locationCategories", [])
1439
+ for classification in classifications:
1440
+ classification_names += f"{classification['classificationName']}, "
1441
+ if classification_names:
1442
+ for column in columns_list:
1443
+ if column.get('key') == 'classifications':
1444
+ column['value'] = classification_names[:-2]
1445
+ break
1446
+
1447
+ # Populate requested relationship-based columns generically from top-level keys
1448
+ col_data = get_required_relationships(element, col_data)
1449
+
1450
+ # Subject area classification
1451
+ subject_area = element.get('elementHeader', {}).get("subjectArea", "") or ""
1452
+ subj_val = ""
1453
+ if isinstance(subject_area, dict):
1454
+ subj_val = subject_area.get("classificationProperties", {}).get("subjectAreaName", "")
1455
+ for column in columns_list:
1456
+ if column.get('key') == 'subject_area':
1457
+ column['value'] = subj_val
1458
+ break
1459
+
1460
+ # Mermaid graph
1461
+ mermaid_val = element.get('mermaidGraph', "") or ""
1462
+ for column in columns_list:
1463
+ if column.get('key') == 'mermaid':
1464
+ column['value'] = mermaid_val
1465
+ break
1466
+
1467
+ logger.trace(f"Extracted/Populated columns: {col_data}")
1468
+
1469
+ return col_data
1470
+
1471
+ @dynamic_catch
1472
+ def _generate_location_output(self, elements: dict | list[dict], filter_string: Optional[str],
1473
+ element_type_name: Optional[str], output_format: str = "DICT",
1474
+ report_spec: dict | str = None) -> str | list[dict]:
1475
+ """ Generate output for locations in the specified format.
1476
+
1477
+ Args:
1478
+ elements (Union[Dict, List[Dict]]): Dictionary or list of dictionaries containing data field elements
1479
+ filter (Optional[str]): The search string used to find the elements
1480
+ element_type_name (Optional[str]): The type of location
1481
+ output_format (str): The desired output format (MD, FORM, REPORT, LIST, DICT, MERMAID, HTML)
1482
+ report_spec (Optional[dict], optional): List of dictionaries containing column data. Defaults
1483
+ to None.
1484
+
1485
+ Returns:
1486
+ Union[str, List[Dict]]: Formatted output as a string or list of dictionaries
1487
+ """
1488
+ if element_type_name is None:
1489
+ entity_type = "Locations"
1490
+ else:
1491
+ entity_type = element_type_name
1492
+ # First see if the user has specified an report_spec - either a label or a dict
1493
+ get_additional_props_func = None
1494
+ if report_spec:
1495
+ if isinstance(report_spec, str):
1496
+ output_formats = select_report_spec(report_spec, output_format)
1497
+ elif isinstance(report_spec, dict):
1498
+ output_formats = get_report_spec_match(report_spec, output_format)
1499
+
1500
+ # If no output_format was set, then use the element_type_name to lookup the output format
1501
+ elif element_type_name:
1502
+ output_formats = select_report_spec(element_type_name, output_format)
1503
+ else:
1504
+ # fallback to locations or entity type
1505
+ output_formats = select_report_spec(entity_type, output_format)
1506
+ if output_formats is None:
1507
+ output_formats = select_report_spec("Default", output_format)
1508
+
1509
+ if output_formats:
1510
+ get_additional_props_name = output_formats.get("get_additional_props", {}).get("function", None)
1511
+ if isinstance(get_additional_props_name, str):
1512
+ class_name, method_name = get_additional_props_name.split(".")
1513
+ if hasattr(self, method_name):
1514
+ get_additional_props_func = getattr(self, method_name)
1515
+
1516
+ logger.trace(f"Executing generate_location_output for {entity_type}: {output_formats}")
1517
+ return generate_output(
1518
+ elements,
1519
+ filter,
1520
+ entity_type,
1521
+ output_format,
1522
+ self._extract_location_properties,
1523
+ get_additional_props_func,
1524
+ output_formats,
1525
+ )