pyegeria 5.4.0.22__py3-none-any.whl → 5.4.0.23__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.
- commands/cat/.DS_Store +0 -0
- commands/cat/.env +8 -0
- commands/cat/debug_log.log +0 -0
- commands/cat/list_collections.py +15 -6
- commands/cat/list_format_set.py +90 -85
- commands/cat/logs/pyegeria.log +136 -0
- commands/cli/debug_log.log +0 -0
- commands/ops/logs/pyegeria.log +0 -0
- md_processing/.DS_Store +0 -0
- md_processing/dr-egeria-outbox/Collections-2025-08-12-13-30-37.md +163 -0
- md_processing/dr-egeria-outbox/Collections-2025-08-12-13-35-58.md +474 -0
- md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +8 -0
- md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +873 -0
- md_processing/dr_egeria_inbox/arch_test.md +57 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
- md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
- md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
- md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
- md_processing/dr_egeria_inbox/cat_test.md +100 -0
- md_processing/dr_egeria_inbox/collections.md +39 -0
- md_processing/dr_egeria_inbox/data_designer_debug.log +6 -0
- md_processing/dr_egeria_inbox/data_designer_out.md +60 -0
- md_processing/dr_egeria_inbox/data_designer_search_test.md +11 -0
- md_processing/dr_egeria_inbox/data_field.md +54 -0
- md_processing/dr_egeria_inbox/data_spec.md +77 -0
- md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
- md_processing/dr_egeria_inbox/data_test.md +179 -0
- md_processing/dr_egeria_inbox/data_test2.md +429 -0
- md_processing/dr_egeria_inbox/data_test3.md +462 -0
- md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +124 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +313 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
- md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
- md_processing/dr_egeria_inbox/generated_help_report.md +9 -0
- md_processing/dr_egeria_inbox/glossary_list.md +5 -0
- md_processing/dr_egeria_inbox/glossary_search_test.md +40 -0
- md_processing/dr_egeria_inbox/glossary_test1.md +324 -0
- md_processing/dr_egeria_inbox/gov_def.md +424 -0
- md_processing/dr_egeria_inbox/gov_def2.md +447 -0
- md_processing/dr_egeria_inbox/product.md +50 -0
- md_processing/dr_egeria_inbox/rel.md +8 -0
- md_processing/dr_egeria_inbox/sb.md +119 -0
- md_processing/dr_egeria_inbox/solution-components.md +136 -0
- md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
- md_processing/dr_egeria_inbox/synonym_test.md +42 -0
- md_processing/dr_egeria_inbox/t2.md +268 -0
- md_processing/dr_egeria_outbox/.obsidian/app.json +1 -0
- md_processing/dr_egeria_outbox/.obsidian/appearance.json +1 -0
- md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +6 -0
- md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +31 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +10 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +4459 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +10 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +3 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +153 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +500 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +37 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +220 -0
- md_processing/dr_egeria_outbox/.obsidian/types.json +28 -0
- md_processing/dr_egeria_outbox/.obsidian/workspace.json +220 -0
- md_processing/dr_egeria_outbox/Untitled.canvas +1 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:00-product.md +62 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:13-product.md +62 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-07-20 13:23-product.md +47 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-01 11:55-data_test3.md +503 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-07-14 12:38-data_designer_out.md +663 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-07-21 10:52-generated_help_report.md +2744 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-07-21 18:38-collections.md +62 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-08-01 11:34-gov_def.md +444 -0
- md_processing/dr_egeria_outbox/processed-2025-08-03 16:05-glossary_list.md +37 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 14:55-product.md +77 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:05-product.md +75 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:11-product.md +74 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 20:40-collections.md +49 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +719 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +41 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +33 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +192 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/tuesday/processed-2025-07-16 19:15-gov_def2.md +527 -0
- md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 12:08-gov_def2.md +527 -0
- md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 14:27-gov_def2.md +485 -0
- md_processing/md_processing_utils/debug_log.log +0 -0
- md_processing/md_processing_utils/solution_architect_log.log +0 -0
- pyegeria/.DS_Store +0 -0
- pyegeria/__init__.py +2 -2
- pyegeria/_client_new.py +45 -49
- pyegeria/_output_format_models.py +22 -17
- pyegeria/_output_formats.py +105 -32
- pyegeria/collection_manager.py +154 -50
- pyegeria/collection_manager_omvs.py +47 -18
- pyegeria/egeria_cat_client.py +1 -1
- pyegeria/egeria_client.py +6 -0
- pyegeria/egeria_tech_client.py +6 -1
- pyegeria/governance_officer.py +2515 -0
- pyegeria/models.py +22 -6
- pyegeria/output_formatter.py +298 -79
- {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.23.dist-info}/METADATA +1 -1
- {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.23.dist-info}/RECORD +120 -18
- {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.23.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.23.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.0.22.dist-info → pyegeria-5.4.0.23.dist-info}/entry_points.txt +0 -0
pyegeria/collection_manager.py
CHANGED
@@ -9,7 +9,7 @@ Copyright Contributors to the ODPi Egeria project.
|
|
9
9
|
import asyncio
|
10
10
|
import inspect
|
11
11
|
from datetime import datetime
|
12
|
-
from typing import Optional, Annotated, Literal
|
12
|
+
from typing import Optional, Annotated, Literal, Union
|
13
13
|
|
14
14
|
from loguru import logger
|
15
15
|
from pydantic import ValidationError, Field, HttpUrl
|
@@ -21,9 +21,11 @@ from pyegeria.load_config import get_app_config
|
|
21
21
|
from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequestBody, NewElementRequestBody,
|
22
22
|
ReferenceableProperties, InitialClassifications, TemplateRequestBody,
|
23
23
|
UpdateElementRequestBody, UpdateStatusRequestBody, NewRelationshipRequestBody,
|
24
|
-
DeleteRequestBody, UpdateRelationshipRequestBody, ResultsRequestBody
|
24
|
+
DeleteRequestBody, UpdateRelationshipRequestBody, ResultsRequestBody,
|
25
|
+
get_defined_field_values, PyegeriaModel)
|
25
26
|
from pyegeria.output_formatter import (generate_output,
|
26
|
-
_extract_referenceable_properties
|
27
|
+
_extract_referenceable_properties, populate_columns_from_properties,
|
28
|
+
get_required_relationships)
|
27
29
|
from pyegeria.utils import body_slimmer, dynamic_catch
|
28
30
|
|
29
31
|
|
@@ -61,28 +63,39 @@ class CollectionProperties(ReferenceableProperties):
|
|
61
63
|
class_: Annotated[Literal["CollectionProperties"], Field(alias="class")]
|
62
64
|
|
63
65
|
|
64
|
-
class DataSpecProperties(
|
66
|
+
class DataSpecProperties(CollectionProperties):
|
65
67
|
class_: Annotated[Literal["DataSpecProperties"], Field(alias="class")]
|
66
68
|
|
67
69
|
|
68
|
-
class DataDictionaryProperties(
|
70
|
+
class DataDictionaryProperties(CollectionProperties):
|
69
71
|
class_: Annotated[Literal["DataDictionaryProperties"], Field(alias="class")]
|
70
72
|
|
71
73
|
|
72
|
-
class AgreementProperties(
|
74
|
+
class AgreementProperties(CollectionProperties):
|
73
75
|
class_: Annotated[Literal["AgreementProperties"], Field(alias="class")]
|
74
76
|
identifier: str | None = None
|
75
77
|
user_defined_status: str | None = None
|
76
78
|
|
79
|
+
class DigitalSubscriptionProperties(AgreementProperties):
|
80
|
+
class_: Annotated[Literal["DigitalSubscriptionProperties"], Field(alias="class")]
|
81
|
+
support_level: str | None = None
|
82
|
+
service_levels: dict | None = None
|
77
83
|
|
78
|
-
|
84
|
+
|
85
|
+
class DigitalProductProperties(CollectionProperties):
|
86
|
+
class_: Annotated[Literal["DigitalProductProperties"], Field(alias="class")]
|
87
|
+
user_defined_status: str | None = None
|
79
88
|
product_name: str | None = None
|
89
|
+
identifier: str | None = None
|
90
|
+
introduction_date: datetime | None = None
|
80
91
|
maturity: str | None = None
|
81
92
|
service_life: str | None = None
|
82
|
-
introduction_date: datetime | None = None
|
83
93
|
next_version_date: datetime | None = None
|
84
94
|
withdrawal_date: datetime | None = None
|
85
|
-
|
95
|
+
|
96
|
+
class Collections(PyegeriaModel):
|
97
|
+
collection: Union[CollectionProperties, DigitalSubscriptionProperties, DigitalProductProperties, AgreementProperties,
|
98
|
+
DataSpecProperties, DataDictionaryProperties] = Field(desciminator="class_")
|
86
99
|
|
87
100
|
|
88
101
|
class CollectionManager(Client2):
|
@@ -127,7 +140,8 @@ class CollectionManager(Client2):
|
|
127
140
|
|
128
141
|
@dynamic_catch
|
129
142
|
async def _async_get_attached_collections(self, parent_guid: str, start_from: int = 0, page_size: int = 0,
|
130
|
-
|
143
|
+
category: str = None, classification_names: list[str]= None,
|
144
|
+
body: dict | FilterRequestBody = None, output_format: str = "JSON",
|
131
145
|
output_format_set: str | dict = None) -> list | str:
|
132
146
|
"""Returns the list of collections that are linked off of the supplied element using the ResourceList
|
133
147
|
relationship. Async version.
|
@@ -180,12 +194,10 @@ class CollectionManager(Client2):
|
|
180
194
|
}
|
181
195
|
|
182
196
|
"""
|
183
|
-
if body is None:
|
184
|
-
body = {}
|
185
197
|
|
186
198
|
url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/"
|
187
|
-
f"metadata-elements/{parent_guid}/collections
|
188
|
-
|
199
|
+
f"metadata-elements/{parent_guid}/collections")
|
200
|
+
return await self._async_get_name_request(url, body, output_format, output_format_set)
|
189
201
|
response = await self._async_make_request("POST", url, body_slimmer(body))
|
190
202
|
elements = response.json().get("elements", NO_ELEMENTS_FOUND)
|
191
203
|
if type(elements) is str:
|
@@ -259,7 +271,8 @@ class CollectionManager(Client2):
|
|
259
271
|
|
260
272
|
|
261
273
|
@dynamic_catch
|
262
|
-
async def _async_find_collections(self, search_string: str =
|
274
|
+
async def _async_find_collections(self, search_string: str = "*", classification_names: list[str] = None,
|
275
|
+
metadata_element_types: list[str] = None,
|
263
276
|
starts_with: bool = True, ends_with: bool = False, ignore_case: bool = False,
|
264
277
|
start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
|
265
278
|
output_format_set: str | dict = None,
|
@@ -273,9 +286,11 @@ class CollectionManager(Client2):
|
|
273
286
|
search_string: str
|
274
287
|
Search string to match against - None or '*' indicate match against all collections (may be filtered by
|
275
288
|
classification).
|
276
|
-
|
289
|
+
classification_names: list[str], optional, default=None
|
277
290
|
A list of classification names to filter on - for example, ["DataSpec"], for data specifications. If none,
|
278
291
|
then all classifications are returned.
|
292
|
+
metadata_element_types: list[str], optional, default=None
|
293
|
+
A list of metadata element types to filter on - for example, ["DataSpec"], for data specifications. If none,
|
279
294
|
starts_with : bool, [default=False], optional
|
280
295
|
Starts with the supplied string.
|
281
296
|
ends_with : bool, [default=False], optional
|
@@ -315,6 +330,7 @@ class CollectionManager(Client2):
|
|
315
330
|
response = await self._async_find_request(url, _type="Collection",
|
316
331
|
_gen_output=self._generate_collection_output,
|
317
332
|
search_string = search_string, classification_names = classification_names,
|
333
|
+
metadata_element_types = metadata_element_types,
|
318
334
|
starts_with = starts_with, ends_with = ends_with, ignore_case = ignore_case,
|
319
335
|
start_from = start_from, page_size = page_size,
|
320
336
|
output_format=output_format, output_format_set=output_format_set,
|
@@ -323,7 +339,8 @@ class CollectionManager(Client2):
|
|
323
339
|
return response
|
324
340
|
|
325
341
|
@dynamic_catch
|
326
|
-
def find_collections(self, search_string: str = '*', classification_names: str = None,
|
342
|
+
def find_collections(self, search_string: str = '*', classification_names: str = None,
|
343
|
+
metadata_element_types: list[str] = None, starts_with: bool = True,
|
327
344
|
ends_with: bool = False, ignore_case: bool = False,
|
328
345
|
start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
|
329
346
|
output_format_set: str | dict = None,
|
@@ -337,9 +354,12 @@ class CollectionManager(Client2):
|
|
337
354
|
search_string: str
|
338
355
|
Search string to match against - None or '*' indicate match against all collections (may be filtered by
|
339
356
|
classification).
|
340
|
-
|
357
|
+
classification_names: list[str], optional, default=None
|
341
358
|
A list of classification names to filter on - for example, ["DataSpec"], for data specifications. If none,
|
342
359
|
then all classifications are returned.
|
360
|
+
metadata_element_types: list[str], optional, default=None
|
361
|
+
A list of metadata element types to filter on - for example, ["DataSpec"], for data specifications. If none,
|
362
|
+
then all metadata element types are returned.
|
343
363
|
starts_with : bool, [default=False], optional
|
344
364
|
Starts with the supplied string.
|
345
365
|
ends_with : bool, [default=False], optional
|
@@ -374,10 +394,16 @@ class CollectionManager(Client2):
|
|
374
394
|
NotAuthorizedException
|
375
395
|
The principle specified by the user_id does not have authorization for the requested action
|
376
396
|
|
397
|
+
Args:
|
398
|
+
classification_names ():
|
399
|
+
metadata_element_types ():
|
400
|
+
|
377
401
|
"""
|
378
402
|
return asyncio.get_event_loop().run_until_complete(
|
379
|
-
self._async_find_collections(search_string, classification_names,
|
380
|
-
|
403
|
+
self._async_find_collections(search_string, classification_names, metadata_element_types,
|
404
|
+
starts_with, ends_with, ignore_case,
|
405
|
+
start_from, page_size, output_format,
|
406
|
+
output_format_set, body))
|
381
407
|
|
382
408
|
|
383
409
|
@dynamic_catch
|
@@ -4843,7 +4869,7 @@ class CollectionManager(Client2):
|
|
4843
4869
|
Parameters
|
4844
4870
|
----------
|
4845
4871
|
collection_guid: str
|
4846
|
-
The guid of the collection to
|
4872
|
+
The guid of the collection to delete.
|
4847
4873
|
|
4848
4874
|
cascade: bool, optional, defaults to True
|
4849
4875
|
If true, a cascade delete is performed.
|
@@ -4885,13 +4911,13 @@ class CollectionManager(Client2):
|
|
4885
4911
|
|
4886
4912
|
def delete_collection(self, collection_guid: str, body: dict | DeleteRequestBody = None,
|
4887
4913
|
cascade: bool = False) -> None:
|
4888
|
-
"""Delete a collection. It is
|
4914
|
+
"""Delete a collection. It is deleted from all parent elements. If members are anchored to the collection
|
4889
4915
|
then they are also deleted.
|
4890
4916
|
|
4891
4917
|
Parameters
|
4892
4918
|
----------
|
4893
4919
|
collection_guid: str
|
4894
|
-
The guid of the collection to
|
4920
|
+
The guid of the collection to delete.
|
4895
4921
|
|
4896
4922
|
cascade: bool, optional, defaults to True
|
4897
4923
|
If true, a cascade delete is performed.
|
@@ -5352,46 +5378,113 @@ class CollectionManager(Client2):
|
|
5352
5378
|
self._async_get_member_list(collection_guid, collection_name, collection_qname))
|
5353
5379
|
return resp
|
5354
5380
|
|
5355
|
-
|
5356
|
-
|
5381
|
+
def _extract_digital_product_properties(self, element: dict, guid: str, output_format: str) -> dict:
|
5382
|
+
props = element["properties"]
|
5383
|
+
digital_prod = DigitalProductProperties.model_validate(props)
|
5384
|
+
added_props = get_defined_field_values(digital_prod)
|
5385
|
+
|
5386
|
+
used_by_digital_products = element.get("usedByDigitalProducts", "")
|
5387
|
+
if isinstance(used_by_digital_products, (list | dict)):
|
5388
|
+
used_by_digital_products_list = ""
|
5389
|
+
for prod in used_by_digital_products:
|
5390
|
+
used_by_digital_products_list += f"{prod["relatedElement"]["properties"]["qualifiedName"]}, "
|
5391
|
+
added_props["used_by_digital_products"] = used_by_digital_products_list[:-2]
|
5392
|
+
|
5393
|
+
uses_digital_products = element.get("usesDigitalProducts", "")
|
5394
|
+
if isinstance(uses_digital_products, (list | dict)):
|
5395
|
+
uses_digital_products_list = ""
|
5396
|
+
for prod in uses_digital_products:
|
5397
|
+
uses_digital_products_list += f"{prod["relatedElement"]["properties"]["qualifiedName"]}, "
|
5398
|
+
added_props["uses_digital_products"] = uses_digital_products_list[:-2]
|
5399
|
+
|
5400
|
+
return added_props
|
5401
|
+
|
5402
|
+
def _extract_agreement_properties(self, element: dict, guid: str, output_format: str) -> dict:
|
5403
|
+
props = element["properties"]
|
5404
|
+
# agreement = Collections.model_validate(props)
|
5405
|
+
# added_props = get_defined_field_values(agreement)
|
5406
|
+
|
5407
|
+
added_props = {}
|
5408
|
+
agreement_items = element.get("agreementItems", "")
|
5409
|
+
if isinstance(agreement_items, (list | dict)):
|
5410
|
+
agreement_items_list = ""
|
5411
|
+
for item in agreement_items:
|
5412
|
+
agreement_items_list += f"{item["relatedElement"]["properties"]["qualifiedName"]}, "
|
5413
|
+
added_props["agreementItems"] = agreement_items_list[:-2]
|
5414
|
+
|
5415
|
+
return added_props
|
5416
|
+
|
5417
|
+
def _extract_collection_properties(self, element: dict, columns_struct: dict) -> dict:
|
5357
5418
|
"""
|
5358
|
-
Extract common properties from a collection element.
|
5419
|
+
Extract common properties from a collection element and populate into the provided columns_struct.
|
5359
5420
|
|
5360
5421
|
Args:
|
5361
5422
|
element (dict): The collection element
|
5423
|
+
columns_struct (dict): The columns structure to populate
|
5362
5424
|
|
5363
5425
|
Returns:
|
5364
|
-
dict:
|
5426
|
+
dict: columns_struct with column 'value' fields populated
|
5365
5427
|
"""
|
5366
|
-
|
5367
|
-
|
5428
|
+
# First, populate from element.properties using the utility
|
5429
|
+
col_data = populate_columns_from_properties(element, columns_struct)
|
5430
|
+
|
5431
|
+
columns_list = col_data.get("formats", {}).get("columns", [])
|
5432
|
+
|
5433
|
+
# Populate header-derived values
|
5434
|
+
header_props = _extract_referenceable_properties(element)
|
5435
|
+
for column in columns_list:
|
5436
|
+
key = column.get('key')
|
5437
|
+
if key in header_props:
|
5438
|
+
column['value'] = header_props.get(key)
|
5439
|
+
elif isinstance(key, str) and key.lower() == 'guid':
|
5440
|
+
column['value'] = header_props.get('GUID')
|
5441
|
+
|
5442
|
+
# Derived/computed fields
|
5443
|
+
# collectionCategories are classifications
|
5368
5444
|
classification_names = ""
|
5369
|
-
|
5370
|
-
for classification in
|
5445
|
+
classifications = element.get('elementHeader', {}).get("collectionCategories", [])
|
5446
|
+
for classification in classifications:
|
5371
5447
|
classification_names += f"{classification['classificationName']}, "
|
5372
|
-
|
5448
|
+
if classification_names:
|
5449
|
+
for column in columns_list:
|
5450
|
+
if column.get('key') == 'classifications':
|
5451
|
+
column['value'] = classification_names[:-2]
|
5452
|
+
break
|
5453
|
+
|
5454
|
+
# Populate requested relationship-based columns generically from top-level keys
|
5455
|
+
col_data = get_required_relationships(element, col_data)
|
5373
5456
|
|
5374
|
-
|
5457
|
+
# Subject area classification
|
5458
|
+
subject_area = element.get('elementHeader', {}).get("subjectArea", "") or ""
|
5459
|
+
subj_val = ""
|
5460
|
+
if isinstance(subject_area, dict):
|
5461
|
+
subj_val = subject_area.get("classificationProperties", {}).get("subjectAreaName", "")
|
5462
|
+
for column in columns_list:
|
5463
|
+
if column.get('key') == 'subject_area':
|
5464
|
+
column['value'] = subj_val
|
5465
|
+
break
|
5375
5466
|
|
5376
|
-
|
5377
|
-
|
5378
|
-
|
5379
|
-
|
5380
|
-
|
5381
|
-
|
5382
|
-
|
5383
|
-
|
5467
|
+
# Mermaid graph
|
5468
|
+
mermaid_val = element.get('mermaidGraph', "") or ""
|
5469
|
+
for column in columns_list:
|
5470
|
+
if column.get('key') == 'mermaid':
|
5471
|
+
column['value'] = mermaid_val
|
5472
|
+
break
|
5473
|
+
|
5474
|
+
logger.trace(f"Extracted/Populated columns: {col_data}")
|
5475
|
+
|
5476
|
+
return col_data
|
5384
5477
|
|
5385
5478
|
|
5386
5479
|
def _generate_collection_output(self, elements: dict|list[dict], filter: Optional[str],
|
5387
|
-
|
5480
|
+
element_type_name: Optional[str], output_format: str = "DICT",
|
5388
5481
|
output_format_set: dict | str = None) -> str| list[dict]:
|
5389
5482
|
""" Generate output for collections in the specified format.
|
5390
5483
|
|
5391
5484
|
Args:
|
5392
5485
|
elements (Union[Dict, List[Dict]]): Dictionary or list of dictionaries containing data field elements
|
5393
5486
|
filter (Optional[str]): The search string used to find the elements
|
5394
|
-
|
5487
|
+
element_type_name (Optional[str]): The type of collection
|
5395
5488
|
output_format (str): The desired output format (MD, FORM, REPORT, LIST, DICT, MERMAID, HTML)
|
5396
5489
|
output_format_set (Optional[dict], optional): List of dictionaries containing column data. Defaults
|
5397
5490
|
to None.
|
@@ -5399,22 +5492,33 @@ class CollectionManager(Client2):
|
|
5399
5492
|
Returns:
|
5400
5493
|
Union[str, List[Dict]]: Formatted output as a string or list of dictionaries
|
5401
5494
|
"""
|
5402
|
-
if
|
5495
|
+
if element_type_name is None:
|
5403
5496
|
entity_type = "Collections"
|
5404
5497
|
else:
|
5405
|
-
entity_type =
|
5498
|
+
entity_type = element_type_name
|
5406
5499
|
# First see if the user has specified an output_format_set - either a label or a dict
|
5500
|
+
get_additional_props_func = None
|
5407
5501
|
if output_format_set:
|
5408
5502
|
if isinstance(output_format_set, str):
|
5409
5503
|
output_formats = select_output_format_set(output_format_set, output_format)
|
5410
|
-
|
5504
|
+
elif isinstance(output_format_set, dict):
|
5411
5505
|
output_formats = get_output_format_type_match(output_format_set, output_format)
|
5412
|
-
|
5413
|
-
|
5414
|
-
|
5506
|
+
|
5507
|
+
# If no output_format was set, then use the element_type_name to lookup the output format
|
5508
|
+
elif element_type_name:
|
5509
|
+
output_formats = select_output_format_set(element_type_name, output_format)
|
5415
5510
|
else:
|
5416
5511
|
# fallback to collections or entity type
|
5417
5512
|
output_formats = select_output_format_set(entity_type,output_format)
|
5513
|
+
if output_formats is None:
|
5514
|
+
output_formats = select_output_format_set("Default", output_format)
|
5515
|
+
|
5516
|
+
if output_formats:
|
5517
|
+
get_additional_props_name = output_formats.get("get_additional_props", {}).get("function", None)
|
5518
|
+
if isinstance(get_additional_props_name, str):
|
5519
|
+
class_name, method_name = get_additional_props_name.split(".")
|
5520
|
+
if hasattr(self, method_name):
|
5521
|
+
get_additional_props_func = getattr(self, method_name)
|
5418
5522
|
|
5419
5523
|
logger.trace(f"Executing generate_collection_output for {entity_type}: {output_formats}")
|
5420
5524
|
return generate_output(
|
@@ -5423,7 +5527,7 @@ class CollectionManager(Client2):
|
|
5423
5527
|
entity_type,
|
5424
5528
|
output_format,
|
5425
5529
|
self._extract_collection_properties,
|
5426
|
-
|
5530
|
+
get_additional_props_func,
|
5427
5531
|
output_formats,
|
5428
5532
|
)
|
5429
5533
|
|
@@ -16,7 +16,7 @@ from pyegeria._client import Client
|
|
16
16
|
from pyegeria._globals import NO_ELEMENTS_FOUND, NO_GUID_RETURNED, NO_MEMBERS_FOUND
|
17
17
|
from pyegeria._validators import validate_guid, validate_search_string
|
18
18
|
from pyegeria.output_formatter import (generate_output,
|
19
|
-
_extract_referenceable_properties)
|
19
|
+
_extract_referenceable_properties, get_required_relationships)
|
20
20
|
from pyegeria.utils import body_slimmer, dynamic_catch
|
21
21
|
|
22
22
|
|
@@ -6431,34 +6431,63 @@ class CollectionManager(Client2):
|
|
6431
6431
|
return resp
|
6432
6432
|
|
6433
6433
|
|
6434
|
-
def _extract_collection_properties(self, element: dict) -> dict:
|
6434
|
+
def _extract_collection_properties(self, element: dict, columns_struct: dict) -> dict:
|
6435
6435
|
"""
|
6436
|
-
Extract common properties from a collection element.
|
6436
|
+
Extract common properties from a collection element and populate into the provided columns_struct.
|
6437
6437
|
|
6438
6438
|
Args:
|
6439
6439
|
element (dict): The collection element
|
6440
|
+
columns_struct (dict): The columns structure to populate
|
6440
6441
|
|
6441
6442
|
Returns:
|
6442
|
-
dict:
|
6443
|
+
dict: columns_struct with column 'value' fields populated
|
6443
6444
|
"""
|
6444
|
-
|
6445
|
-
|
6446
|
-
|
6447
|
-
|
6448
|
-
|
6449
|
-
|
6450
|
-
|
6451
|
-
|
6452
|
-
|
6453
|
-
|
6445
|
+
col_data = columns_struct
|
6446
|
+
# Populate display/qualified/description etc. from properties via output formatter util not available here,
|
6447
|
+
# so we do minimal direct mapping for OMVS variant.
|
6448
|
+
props = element.get('properties', {}) or {}
|
6449
|
+
columns_list = col_data.get('formats', {}).get('columns', [])
|
6450
|
+
|
6451
|
+
# Header props
|
6452
|
+
header_props = _extract_referenceable_properties(element)
|
6453
|
+
for column in columns_list:
|
6454
|
+
key = column.get('key')
|
6455
|
+
# from element.properties (camelCase); keys here are snake_case or GUID
|
6456
|
+
if isinstance(key, str):
|
6457
|
+
if key.lower() == 'guid':
|
6458
|
+
column['value'] = header_props.get('GUID')
|
6459
|
+
elif key in header_props:
|
6460
|
+
column['value'] = header_props.get(key)
|
6461
|
+
else:
|
6462
|
+
# simple camelCase mapping inline to reduce imports here
|
6463
|
+
snake_parts = key.split('_')
|
6464
|
+
camel = snake_parts[0] + ''.join([p.capitalize() if p.lower() != 'guid' else 'GUID' for p in snake_parts[1:]])
|
6465
|
+
if camel in props:
|
6466
|
+
column['value'] = props.get(camel)
|
6467
|
+
|
6468
|
+
# Mermaid graph
|
6469
|
+
for column in columns_list:
|
6470
|
+
if column.get('key') == 'mermaid':
|
6471
|
+
column['value'] = element.get('mermaidGraph', '') or ''
|
6472
|
+
break
|
6473
|
+
|
6474
|
+
# Members list (uses API call)
|
6454
6475
|
member_names = ""
|
6455
|
-
members = self.get_member_list(collection_guid=
|
6476
|
+
members = self.get_member_list(collection_guid=header_props.get('GUID'))
|
6456
6477
|
if isinstance(members, list):
|
6457
6478
|
for member in members:
|
6458
6479
|
member_names += f"{member['qualifiedName']}, "
|
6459
|
-
|
6460
|
-
|
6461
|
-
|
6480
|
+
member_names = member_names[:-2]
|
6481
|
+
for column in columns_list:
|
6482
|
+
if column.get('key') == 'members':
|
6483
|
+
column['value'] = member_names
|
6484
|
+
break
|
6485
|
+
|
6486
|
+
# Populate any additional requested relationship-based columns generically
|
6487
|
+
col_data = get_required_relationships(element, col_data)
|
6488
|
+
|
6489
|
+
logger.trace(f"Extracted/Populated columns: {col_data}")
|
6490
|
+
return col_data
|
6462
6491
|
|
6463
6492
|
|
6464
6493
|
def _generate_collection_output(self, elements: dict|list[dict], filter: Optional[str],
|
pyegeria/egeria_cat_client.py
CHANGED
@@ -8,7 +8,7 @@ AssetCatalog, CollectionManager, GlossaryManager, and ProjectManager.
|
|
8
8
|
"""
|
9
9
|
from pyegeria.egeria_my_client import EgeriaMy
|
10
10
|
from pyegeria.asset_catalog_omvs import AssetCatalog
|
11
|
-
from pyegeria.
|
11
|
+
from pyegeria.collection_manager import CollectionManager
|
12
12
|
from pyegeria.glossary_manager_omvs import GlossaryManager
|
13
13
|
from pyegeria.project_manager_omvs import ProjectManager
|
14
14
|
|
pyegeria/egeria_client.py
CHANGED
@@ -15,6 +15,7 @@ for all use cases..using the more role based clients is often appropriate:
|
|
15
15
|
from pyegeria.asset_catalog_omvs import AssetCatalog
|
16
16
|
from pyegeria.collection_manager_omvs import CollectionManager
|
17
17
|
from pyegeria.glossary_manager_omvs import GlossaryManager
|
18
|
+
from pyegeria.governance_officer import GovernanceOfficer
|
18
19
|
from pyegeria.project_manager_omvs import ProjectManager
|
19
20
|
from pyegeria.automated_curation_omvs import AutomatedCuration
|
20
21
|
from pyegeria.classification_manager_omvs import ClassificationManager
|
@@ -30,6 +31,7 @@ from pyegeria.registered_info import RegisteredInfo
|
|
30
31
|
from pyegeria.valid_metadata_omvs import ValidMetadataManager
|
31
32
|
from pyegeria.egeria_config_client import EgeriaConfig
|
32
33
|
from pyegeria.data_designer_omvs import DataDesigner
|
34
|
+
from pyegeria.governance_officer import GovernanceOfficer
|
33
35
|
# from pyegeria.md_processing_utils import render_markdown
|
34
36
|
|
35
37
|
|
@@ -55,6 +57,7 @@ class Egeria(
|
|
55
57
|
SolutionArchitect,
|
56
58
|
EgeriaConfig,
|
57
59
|
DataDesigner,
|
60
|
+
GovernanceOfficer
|
58
61
|
):
|
59
62
|
"""
|
60
63
|
Client to issue Runtime status requests.
|
@@ -129,4 +132,7 @@ class Egeria(
|
|
129
132
|
DataDesigner.__init__(
|
130
133
|
self, view_server, platform_url, user_id, user_pwd, token
|
131
134
|
)
|
135
|
+
GovernanceOfficer.__init__(
|
136
|
+
self, view_server, platform_url, user_id, user_pwd, token
|
137
|
+
)
|
132
138
|
print(Egeria.mro())
|
pyegeria/egeria_tech_client.py
CHANGED
@@ -15,7 +15,8 @@ from pyegeria.runtime_manager_omvs import RuntimeManager
|
|
15
15
|
from pyegeria.solution_architect_omvs import SolutionArchitect
|
16
16
|
from pyegeria.template_manager_omvs import TemplateManager
|
17
17
|
from pyegeria.valid_metadata_omvs import ValidMetadataManager
|
18
|
-
from pyegeria.
|
18
|
+
from pyegeria.governance_officer import GovernanceOfficer
|
19
|
+
from pyegeria.collection_manager import CollectionManager
|
19
20
|
|
20
21
|
|
21
22
|
class EgeriaTech(
|
@@ -31,6 +32,7 @@ class EgeriaTech(
|
|
31
32
|
DataDesigner,
|
32
33
|
TemplateManager,
|
33
34
|
GovernanceOfficer,
|
35
|
+
CollectionManager
|
34
36
|
):
|
35
37
|
"""
|
36
38
|
Client for technical Egeria users.
|
@@ -94,6 +96,9 @@ class EgeriaTech(
|
|
94
96
|
GovernanceOfficer.__init__(
|
95
97
|
self, view_server, platform_url, user_id, user_pwd, token
|
96
98
|
)
|
99
|
+
CollectionManager.__init__(
|
100
|
+
self, view_server, platform_url, user_id, user_pwd, token
|
101
|
+
)
|
97
102
|
|
98
103
|
if __name__ == "__main__":
|
99
104
|
print("Main-Tech Client")
|