pyegeria 5.4.0.25__py3-none-any.whl → 5.4.0.27__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/debug_log +868 -7794
- commands/cat/debug_log.2025-08-18_11-34-38_088636.zip +0 -0
- commands/cat/list_collections.py +1 -1
- commands/cat/list_format_set.py +6 -8
- commands/cli/egeria.py +2 -2
- commands/cli/egeria_cat.py +3 -2
- commands/ops/load_archive.py +2 -2
- md_processing/data/commands.json +7 -7
- md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +7 -7
- md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +36 -31
- md_processing/dr_egeria_outbox/friday/processed-2025-08-22 21:22-dr_egeria_intro_part1.md +312 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-22 21:23-dr_egeria_intro_part1.md +265 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 15:06-dr_egeria_intro_part1.md +230 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 15:30-dr_egeria_intro_part1.md +296 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 15:31-dr_egeria_intro_part1.md +253 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 16:08-dr_egeria_intro_part2.md +343 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 16:12-dr_egeria_intro_part2.md +343 -0
- md_processing/md_commands/glossary_commands.py +888 -951
- md_processing/md_commands/product_manager_commands.py +8 -270
- md_processing/md_commands/project_commands.py +1 -1
- md_processing/md_processing_utils/common_md_proc_utils.py +138 -64
- md_processing/md_processing_utils/common_md_utils.py +2 -1
- pyegeria/__init__.py +5 -302
- pyegeria/_client_new.py +5 -4
- pyegeria/_output_formats.py +23 -3
- pyegeria/collection_manager.py +31 -28
- pyegeria/{load_config.py → config.py} +7 -2
- pyegeria/data_designer.py +154 -194
- pyegeria/egeria_cat_client.py +48 -30
- pyegeria/egeria_client.py +74 -75
- pyegeria/egeria_config_client.py +37 -7
- pyegeria/egeria_my_client.py +45 -10
- pyegeria/egeria_tech_client.py +69 -58
- pyegeria/glossary_manager.py +494 -122
- pyegeria/governance_officer.py +2 -2
- pyegeria/logging_configuration.py +1 -4
- pyegeria/models.py +1 -1
- pyegeria/project_manager.py +381 -741
- pyegeria/solution_architect_omvs.py +1 -1
- pyegeria/utils.py +1 -3
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/METADATA +1 -1
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/RECORD +45 -44
- commands/cat/debug_log.2025-08-15_09-14-07_444802.zip +0 -0
- commands/cat/debug_log.2025-08-16_10-21-59_388912.zip +0 -0
- commands/cat/debug_log.2025-08-17_11-34-27_981852.zip +0 -0
- md_processing/md_processing_utils/solution_architect_log.log +0 -0
- pyegeria/collection_manager_omvs.py +0 -6541
- pyegeria/glossary_browser.py +0 -1259
- pyegeria/project_manager_omvs.py +0 -1933
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/entry_points.txt +0 -0
@@ -21,7 +21,7 @@ from md_processing.md_processing_utils.md_processing_constants import (load_comm
|
|
21
21
|
from pyegeria import DEBUG_LEVEL, body_slimmer, to_pascal_case, PyegeriaException, print_basic_exception, print_exception_table
|
22
22
|
from pyegeria.egeria_tech_client import EgeriaTech
|
23
23
|
|
24
|
-
|
24
|
+
EGERIA_METADATA_STORE = os.environ.get("EGERIA_METADATA_STORE", "active-metadata-store")
|
25
25
|
EGERIA_KAFKA_ENDPOINT = os.environ.get("KAFKA_ENDPOINT", "localhost:9092")
|
26
26
|
EGERIA_PLATFORM_URL = os.environ.get("EGERIA_PLATFORM_URL", "https://localhost:9443")
|
27
27
|
EGERIA_VIEW_SERVER = os.environ.get("EGERIA_VIEW_SERVER", "view-server")
|
@@ -142,270 +142,6 @@ def update_data_collection_memberships(egeria_client: EgeriaTech, entity_type: s
|
|
142
142
|
add_member_to_collections(egeria_client, guid_list, display_name, guid)
|
143
143
|
|
144
144
|
|
145
|
-
# @logger.catch
|
146
|
-
|
147
|
-
|
148
|
-
# @logger.catch
|
149
|
-
# def add_field_to_data_structures(egeria_client: EgeriaTech, display_name: str, struct_list: list, guid) -> None:
|
150
|
-
# """
|
151
|
-
# Add data field to data structures.
|
152
|
-
# """
|
153
|
-
#
|
154
|
-
# try:
|
155
|
-
# for structure_guid in struct_list:
|
156
|
-
# egeria_client.link_member_data_field(structure_guid, guid, None)
|
157
|
-
# msg = f"Added `{display_name}` to structure `{structure_guid}`"
|
158
|
-
# logger.info(msg)
|
159
|
-
# return
|
160
|
-
#
|
161
|
-
# except Exception as e:
|
162
|
-
# console.print_exception()
|
163
|
-
#
|
164
|
-
#
|
165
|
-
# @logger.catch
|
166
|
-
# def remove_field_from_data_structures(egeria_client: EgeriaTech, display_name: str, struct_list: list,
|
167
|
-
# guid: str) -> None:
|
168
|
-
# """Remove a data field from a list of data structures."""
|
169
|
-
# try:
|
170
|
-
# for structure_guid in struct_list:
|
171
|
-
# egeria_client.detach_member_data_field(structure_guid, guid, None)
|
172
|
-
# msg = f"Removed `{display_name}` from structure `{structure_guid}`"
|
173
|
-
# logger.info(msg)
|
174
|
-
# return
|
175
|
-
#
|
176
|
-
# except Exception as e:
|
177
|
-
# console.print_exception()
|
178
|
-
#
|
179
|
-
|
180
|
-
# @logger.catch
|
181
|
-
# def sync_data_field_rel_elements(egeria_client: EgeriaTech, structure_list: list, parent_field_list: list, terms: list,
|
182
|
-
# data_class_guid: str, guid: str, display_name: str,
|
183
|
-
# replace_all_props: bool = True) -> None:
|
184
|
-
# """Sync a field's related elements.
|
185
|
-
#
|
186
|
-
# TODO: Need to add data class support when ready and may need to revisit bodies.
|
187
|
-
#
|
188
|
-
# """
|
189
|
-
# if terms:
|
190
|
-
# terms = [terms]
|
191
|
-
#
|
192
|
-
# if replace_all_props:
|
193
|
-
# rel_el_list = egeria_client.get_data_field_rel_elements(guid)
|
194
|
-
# # should I throw an exception if empty?
|
195
|
-
# if rel_el_list is None:
|
196
|
-
# logger.warning("Unexpected -> the list was None - assigning empty list")
|
197
|
-
# rel_el_list = {}
|
198
|
-
#
|
199
|
-
# as_is_data_structs = set(rel_el_list.get("data_structure_guids", []))
|
200
|
-
# as_is_parent_fields = set(rel_el_list.get("parent_guids", []))
|
201
|
-
# as_is_assigned_meanings = set(rel_el_list.get("assigned_meanings_guids", []))
|
202
|
-
# as_is_data_classes = set(rel_el_list.get("data_class_guids", []))
|
203
|
-
#
|
204
|
-
# to_be_data_structs = set(structure_list) if structure_list is not None else set()
|
205
|
-
# to_be_parent_fields = set(parent_field_list) if parent_field_list is not None else set()
|
206
|
-
# to_be_assigned_meanings = set(terms) if terms is not None else set()
|
207
|
-
# to_be_data_classes = set([data_class_guid]) if data_class_guid is not None else set()
|
208
|
-
#
|
209
|
-
# logger.trace(f"as_is_data_structs: {list(as_is_data_structs)} to_be_data_struct: {list(to_be_data_structs)}")
|
210
|
-
# logger.trace(
|
211
|
-
# f"as_is_parent_fields: {list(as_is_parent_fields)} to_be_parent_fields: {list(to_be_parent_fields)}")
|
212
|
-
# logger.trace(f"as_is_assigned_meanings: {list(as_is_assigned_meanings)} to_be_assigned_meanings: "
|
213
|
-
# f"{list(to_be_assigned_meanings)}")
|
214
|
-
# logger.trace(f"as_is_data_classes: {list(as_is_data_classes)} to_be_assigned_data_classes: "
|
215
|
-
# f"{list(to_be_data_classes)}")
|
216
|
-
#
|
217
|
-
# data_struct_to_remove = as_is_data_structs - to_be_data_structs
|
218
|
-
# logger.trace(f"data_struct_to_remove: {list(data_struct_to_remove)}")
|
219
|
-
# if len(data_struct_to_remove) > 0:
|
220
|
-
# for ds in data_struct_to_remove:
|
221
|
-
# egeria_client.detach_member_data_field(ds, guid, None)
|
222
|
-
# msg = f"Removed `{display_name}` from structure `{ds}`"
|
223
|
-
# logger.trace(msg)
|
224
|
-
# data_struct_to_add = to_be_data_structs - as_is_data_structs
|
225
|
-
# logger.trace(f"data_struct_to_add: {list(data_struct_to_add)}")
|
226
|
-
# if len(data_struct_to_add) > 0:
|
227
|
-
# for ds in data_struct_to_add:
|
228
|
-
# egeria_client.link_member_data_field(ds, guid, None)
|
229
|
-
# msg = f"Added `{display_name}` to structure `{ds}`"
|
230
|
-
# logger.trace(msg)
|
231
|
-
#
|
232
|
-
# parent_field_to_remove = to_be_parent_fields - as_is_parent_fields
|
233
|
-
# logger.trace(f"parent_field_to_remove: {list(parent_field_to_remove)}")
|
234
|
-
# if len(parent_field_to_remove) > 0:
|
235
|
-
# for field in parent_field_to_remove:
|
236
|
-
# egeria_client.detach_nested_data_field(field, guid, None)
|
237
|
-
# msg = f"Removed `{display_name}` from field `{field}`"
|
238
|
-
# logger.trace(msg)
|
239
|
-
# parent_field_to_add = to_be_parent_fields - as_is_parent_fields
|
240
|
-
# logger.trace(f"parent_field_to_add: {list(parent_field_to_add)}")
|
241
|
-
# if len(parent_field_to_add) > 0:
|
242
|
-
# for field in parent_field_to_add:
|
243
|
-
# egeria_client.link_nested_data_field(field, guid, None)
|
244
|
-
# msg = f"Added `{display_name}` to field `{field}`"
|
245
|
-
# logger.trace(msg)
|
246
|
-
#
|
247
|
-
# terms_to_remove = as_is_assigned_meanings - to_be_assigned_meanings
|
248
|
-
# logger.trace(f"terms_to_remove: {list(terms_to_remove)}")
|
249
|
-
# if terms:
|
250
|
-
# for term in terms_to_remove:
|
251
|
-
# egeria_client.detach_semantic_definition(guid, term, None)
|
252
|
-
# msg = f"Removed `{term}` from `{display_name}`"
|
253
|
-
# logger.trace(msg)
|
254
|
-
# terms_to_add = to_be_assigned_meanings - as_is_assigned_meanings
|
255
|
-
# logger.trace(f"terms_to_add: {list(terms_to_add)}")
|
256
|
-
# if len(terms_to_add) > 0:
|
257
|
-
# for term in terms_to_add:
|
258
|
-
# egeria_client.link_semantic_definition(guid, term, None)
|
259
|
-
# msg = f"Added `{term}` to`{display_name}`"
|
260
|
-
# logger.trace(msg)
|
261
|
-
#
|
262
|
-
# classes_to_remove = as_is_data_classes - to_be_data_classes
|
263
|
-
# logger.trace(f"classes_to_remove: {list(classes_to_remove)}")
|
264
|
-
# if len(terms_to_remove) > 0:
|
265
|
-
# for dc in classes_to_remove:
|
266
|
-
# body = {
|
267
|
-
# "class": "MetadataSourceRequestBody", "forLineage": False, "forDuplicateProcessing": False
|
268
|
-
# }
|
269
|
-
# egeria_client.detach_data_class_definition(guid, dc, body)
|
270
|
-
# msg = f"Removed `{dc}` from `{display_name}`"
|
271
|
-
# logger.trace(msg)
|
272
|
-
# classes_to_add = to_be_data_classes - as_is_data_classes
|
273
|
-
# logger.trace(f"classes_to_add: {list(classes_to_add)}")
|
274
|
-
# if len(terms_to_add) > 0:
|
275
|
-
# for dc in classes_to_add:
|
276
|
-
# body = {
|
277
|
-
# "class": "RelationshipRequestBody", "forLineage": False, "forDuplicateProcessing": False
|
278
|
-
# }
|
279
|
-
# egeria_client.link_data_class_definition(guid, dc, body)
|
280
|
-
# msg = f"Added `{dc}` to`{display_name}`"
|
281
|
-
# logger.trace(msg)
|
282
|
-
#
|
283
|
-
#
|
284
|
-
# else: # merge - add field to related elements
|
285
|
-
# if structure_list:
|
286
|
-
# add_field_to_data_structures(egeria_client, display_name, structure_list, guid)
|
287
|
-
# msg = f"Added `{display_name}` to `{structure_list}`"
|
288
|
-
# logger.trace(msg)
|
289
|
-
#
|
290
|
-
# if parent_field_list:
|
291
|
-
# for field in parent_field_list:
|
292
|
-
# egeria_client.link_nested_data_field(field, guid, None)
|
293
|
-
# msg = f"Added `{display_name}` to `{field}`"
|
294
|
-
# logger.trace(msg)
|
295
|
-
# if terms:
|
296
|
-
# for term in terms:
|
297
|
-
# egeria_client.link_semantic_definition(guid, term, None)
|
298
|
-
# msg = f"Added `{term}` to `{display_name}`"
|
299
|
-
# logger.trace(msg)
|
300
|
-
#
|
301
|
-
# if data_class_guid:
|
302
|
-
# egeria_client.link_data_class_definition(guid, data_class_guid)
|
303
|
-
# msg = f"Added `{data_class_guid}` to `{display_name}`"
|
304
|
-
# logger.trace(msg)
|
305
|
-
|
306
|
-
#
|
307
|
-
# @logger.catch
|
308
|
-
# def sync_data_class_rel_elements(egeria_client: EgeriaTech, containing_data_class_guids: list, terms: list,
|
309
|
-
# specializes_data_classes: list, guid: str, display_name: str,
|
310
|
-
# replace_all_props: bool = True) -> None:
|
311
|
-
# """Sync a data class' related elements.
|
312
|
-
#
|
313
|
-
# """
|
314
|
-
# if terms:
|
315
|
-
# terms = [terms]
|
316
|
-
#
|
317
|
-
# if replace_all_props:
|
318
|
-
# rel_el_list = egeria_client.get_data_class_rel_elements(guid)
|
319
|
-
# if rel_el_list is None:
|
320
|
-
# logger.warning("Unexpected -> the list was None - assigning empty list")
|
321
|
-
# rel_el_list = {}
|
322
|
-
# if terms:
|
323
|
-
# terms = [terms]
|
324
|
-
#
|
325
|
-
# as_is_nested_classes = set(rel_el_list.get("nested_data_class_guids", []))
|
326
|
-
# as_is_assigned_meanings = set(rel_el_list.get("assigned_meanings_guids", []))
|
327
|
-
# as_is_specialized_classes = set(rel_el_list.get("specialized_data_class_guids", []))
|
328
|
-
#
|
329
|
-
# to_be_nested_classes = set(containing_data_class_guids) if containing_data_class_guids is not None else set()
|
330
|
-
# to_be_assigned_meanings = set(terms) if terms is not None else set()
|
331
|
-
# to_be_specialized_classes = set([specializes_data_classes]) if specializes_data_classes is not None else set()
|
332
|
-
#
|
333
|
-
# logger.trace(
|
334
|
-
# f"as_is_nested_classes: {list(as_is_nested_classes)} to_be_nested_classes: {list(to_be_nested_classes)}")
|
335
|
-
# logger.trace(f"as_is_assigned_meanings: {list(as_is_assigned_meanings)} to_be_assigned_meanings: "
|
336
|
-
# f"{list(to_be_assigned_meanings)}")
|
337
|
-
# logger.trace(f"as_is_specialized_classes: {list(as_is_specialized_classes)} to_be_specizialized_data_classes: "
|
338
|
-
# f"{list(to_be_specialized_classes)}")
|
339
|
-
#
|
340
|
-
# nested_classes_to_remove = to_be_nested_classes - as_is_nested_classes
|
341
|
-
# logger.trace(f"nested_classes_to_remove: {list(nested_classes_to_remove)}")
|
342
|
-
# if len(nested_classes_to_remove) > 0:
|
343
|
-
# for field in nested_classes_to_remove:
|
344
|
-
# egeria_client.detach_nested_data_class(field, guid, None)
|
345
|
-
# msg = f"Removed `{display_name}` from field `{field}`"
|
346
|
-
# logger.trace(msg)
|
347
|
-
# nested_classes_to_add = to_be_nested_classes - as_is_nested_classes
|
348
|
-
# logger.trace(f"nested_classes_to_add: {list(nested_classes_to_add)}")
|
349
|
-
# if len(nested_classes_to_add) > 0:
|
350
|
-
# for field in nested_classes_to_add:
|
351
|
-
# egeria_client.link_nested_data_class(field, guid, None)
|
352
|
-
# msg = f"Added `{display_name}` to field `{field}`"
|
353
|
-
# logger.trace(msg)
|
354
|
-
#
|
355
|
-
# terms_to_remove = as_is_assigned_meanings - to_be_assigned_meanings
|
356
|
-
# logger.trace(f"terms_to_remove: {list(terms_to_remove)}")
|
357
|
-
# if len(terms_to_remove) > 0:
|
358
|
-
# for term in terms_to_remove:
|
359
|
-
# egeria_client.detach_semantic_definition(guid, term, None)
|
360
|
-
# msg = f"Removed `{term}` from `{display_name}`"
|
361
|
-
# logger.trace(msg)
|
362
|
-
# terms_to_add = to_be_assigned_meanings - as_is_assigned_meanings
|
363
|
-
# logger.trace(f"terms_to_add: {list(terms_to_add)}")
|
364
|
-
# if len(terms_to_add) > 0:
|
365
|
-
# for term in terms_to_add:
|
366
|
-
# egeria_client.link_semantic_definition(guid, term, None)
|
367
|
-
# msg = f"Added `{term}` to`{display_name}`"
|
368
|
-
# logger.trace(msg)
|
369
|
-
#
|
370
|
-
# specialized_classes_to_remove = as_is_specialized_classes - to_be_specialized_classes
|
371
|
-
# logger.trace(f"classes_to_remove: {list(specialized_classes_to_remove)}")
|
372
|
-
# if len(terms_to_remove) > 0:
|
373
|
-
# for dc in specialized_classes_to_remove:
|
374
|
-
# body = {
|
375
|
-
# "class": "MetadataSourceRequestBody", "forLineage": False, "forDuplicateProcessing": False
|
376
|
-
# }
|
377
|
-
# egeria_client.detach_specialist_data_class(guid, dc, body)
|
378
|
-
# msg = f"Removed `{dc}` from `{display_name}`"
|
379
|
-
# logger.trace(msg)
|
380
|
-
# specialized_classes_to_add = to_be_specialized_classes - as_is_specialized_classes
|
381
|
-
# logger.trace(f"classes_to_add: {list(specialized_classes_to_add)}")
|
382
|
-
# if len(specialized_classes_to_add) > 0:
|
383
|
-
# for dc in specialized_classes_to_add:
|
384
|
-
# body = {
|
385
|
-
# "class": "RelationshipRequestBody", "forLineage": False, "forDuplicateProcessing": False
|
386
|
-
# }
|
387
|
-
# egeria_client.link_specialist_data_class(guid, dc, body)
|
388
|
-
# msg = f"Added `{dc}` to`{display_name}`"
|
389
|
-
# logger.trace(msg)
|
390
|
-
#
|
391
|
-
#
|
392
|
-
# else: # merge - add field to related elements
|
393
|
-
# if containing_data_class_guids:
|
394
|
-
# for field in containing_data_class_guids:
|
395
|
-
# egeria_client.link_nested_data_class(field, guid, None)
|
396
|
-
# msg = f"Added `{display_name}` to `{field}`"
|
397
|
-
# logger.trace(msg)
|
398
|
-
#
|
399
|
-
# if terms:
|
400
|
-
# for term in terms:
|
401
|
-
# egeria_client.link_semantic_definition(guid, term, None)
|
402
|
-
# msg = f"Added `{term}` to `{display_name}`"
|
403
|
-
# logger.trace(msg)
|
404
|
-
# if specializes_data_classes:
|
405
|
-
# for el in specializes_data_classes:
|
406
|
-
# egeria_client.link_specialist_data_class(guid, el)
|
407
|
-
# msg = f"Linked `{el}` to `{display_name}`"
|
408
|
-
# logger.trace(msg)
|
409
145
|
#
|
410
146
|
# Product Manager Commands
|
411
147
|
#
|
@@ -465,14 +201,14 @@ def process_collection_upsert_command(egeria_client: EgeriaTech, txt: str, direc
|
|
465
201
|
if object_action == "Update":
|
466
202
|
if not exists:
|
467
203
|
msg = (f" Element `{display_name}` does not exist! Updating result document with Create "
|
468
|
-
f"object_action
|
204
|
+
f"`{object_action}`\n")
|
469
205
|
logger.error(msg)
|
470
206
|
return update_a_command(txt, object_action, object_type, qualified_name, guid)
|
471
207
|
elif not valid:
|
472
208
|
return None
|
473
209
|
else:
|
474
210
|
print(Markdown(
|
475
|
-
f"==> Validation of {command} completed successfully! Proceeding to apply the changes.\n"))
|
211
|
+
f"==> Validation of `{command}` completed successfully! Proceeding to apply the changes.\n"))
|
476
212
|
prop_body = set_prop_body(obj,qualified_name,attributes)
|
477
213
|
|
478
214
|
body = set_update_body(obj, attributes)
|
@@ -573,6 +309,8 @@ def process_digital_product_upsert_command(egeria_client: EgeriaTech, txt: str,
|
|
573
309
|
|
574
310
|
elif directive == "process":
|
575
311
|
try:
|
312
|
+
prop_body = set_product_body(object_type, qualified_name, attributes)
|
313
|
+
|
576
314
|
if object_action == "Update":
|
577
315
|
if not exists:
|
578
316
|
msg = (f" Element `{display_name}` does not exist! Updating result document with Create "
|
@@ -584,9 +322,9 @@ def process_digital_product_upsert_command(egeria_client: EgeriaTech, txt: str,
|
|
584
322
|
else:
|
585
323
|
print(Markdown(
|
586
324
|
f"==> Validation of {command} completed successfully! Proceeding to apply the changes.\n"))
|
587
|
-
|
325
|
+
|
588
326
|
body = set_update_body(object_type, attributes)
|
589
|
-
body['properties'] =
|
327
|
+
body['properties'] = prop_body
|
590
328
|
# Todo: Update product manager later?
|
591
329
|
|
592
330
|
egeria_client.update_digital_product(guid, body)
|
@@ -612,7 +350,7 @@ def process_digital_product_upsert_command(egeria_client: EgeriaTech, txt: str,
|
|
612
350
|
else:
|
613
351
|
body = set_create_body(object_type, attributes)
|
614
352
|
body["initialClassifications"] = set_collection_classifications(object_type, attributes,[])
|
615
|
-
|
353
|
+
|
616
354
|
body["properties"] = prop_body
|
617
355
|
|
618
356
|
guid = egeria_client.create_digital_product(body_slimmer(body))
|
@@ -8,7 +8,7 @@ from md_processing.md_processing_utils.common_md_utils import (debug_level, prin
|
|
8
8
|
from md_processing.md_processing_utils.extraction_utils import (extract_command, process_simple_attribute)
|
9
9
|
from md_processing.md_processing_utils.md_processing_constants import ALWAYS, ERROR, INFO, pre_command
|
10
10
|
from pyegeria._globals import NO_PROJECTS_FOUND
|
11
|
-
from pyegeria.
|
11
|
+
from pyegeria.project_manager import ProjectManager
|
12
12
|
|
13
13
|
setup_log()
|
14
14
|
|
@@ -474,6 +474,9 @@ def proc_simple_attribute(txt: str, action: str, labels: set, if_missing: str =
|
|
474
474
|
|
475
475
|
if attribute and simp_type == "int" :
|
476
476
|
attribute = int(attribute)
|
477
|
+
elif attribute and simp_type == "list":
|
478
|
+
attribute = list(attribute)
|
479
|
+
|
477
480
|
|
478
481
|
return {"status": INFO, "OK": None, "value": attribute, "valid": valid, "exists": True}
|
479
482
|
|
@@ -915,74 +918,145 @@ def proc_name_list(egeria_client: EgeriaTech, element_type: str, txt: str, eleme
|
|
915
918
|
|
916
919
|
|
917
920
|
@logger.catch
|
918
|
-
def
|
919
|
-
|
921
|
+
def sync_collection_memberships(egeria_client: EgeriaTech, guid: str, get_method: callable, collection_types: list,
|
922
|
+
to_be_collection_guids:list, merge_update: bool = True)-> None:
|
920
923
|
"""
|
924
|
+
Synchronize collection memberships for an element.
|
921
925
|
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
the term.
|
936
|
-
|
937
|
-
Returns:
|
938
|
-
None
|
926
|
+
Parameters
|
927
|
+
- egeria_client: EgeriaTech composite client used to call add/remove operations.
|
928
|
+
- guid: the GUID of the element (e.g., GlossaryTerm) whose memberships we sync.
|
929
|
+
- get_method: callable to fetch the element details by guid; must accept (guid, output_format="JSON").
|
930
|
+
- collection_types: list of collection type identifiers to consider when syncing (e.g., ["Glossary", "Folder"]).
|
931
|
+
- to_be_collection_guids: list of lists of GUIDs corresponding positionally to collection_types; may contain None.
|
932
|
+
- merge_update: if True, only add missing memberships; if False, remove existing memberships for the
|
933
|
+
specified collection_types and then add the desired memberships.
|
934
|
+
|
935
|
+
Behavior
|
936
|
+
- When merge_update is True: determine the element's current memberships and add the missing ones only.
|
937
|
+
- When merge_update is False: remove the element from all collections of the specified types, then add the
|
938
|
+
provided target memberships for those types.
|
939
939
|
"""
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
940
|
+
try:
|
941
|
+
# Defensive defaults and shape normalization
|
942
|
+
collection_types = collection_types or []
|
943
|
+
to_be_collection_guids = to_be_collection_guids or []
|
944
|
+
# Ensure the lists align by index; pad to length
|
945
|
+
max_len = max(len(collection_types), len(to_be_collection_guids)) if (collection_types or to_be_collection_guids) else 0
|
946
|
+
if len(collection_types) < max_len:
|
947
|
+
collection_types = collection_types + [None] * (max_len - len(collection_types))
|
948
|
+
if len(to_be_collection_guids) < max_len:
|
949
|
+
to_be_collection_guids = to_be_collection_guids + [None] * (max_len - len(to_be_collection_guids))
|
950
|
+
|
951
|
+
# Get current element details with raw JSON to inspect relationships
|
952
|
+
element = None
|
953
|
+
try:
|
954
|
+
element = get_method(guid, output_format="JSON")
|
955
|
+
except TypeError:
|
956
|
+
# Some get methods require element_type parameter; fallback best-effort
|
957
|
+
element = get_method(guid, element_type=None, output_format="JSON")
|
958
|
+
if isinstance(element, str):
|
959
|
+
# e.g., "No elements found"; nothing to do
|
960
|
+
logger.debug(f"sync_collection_memberships: element lookup returned: {element}")
|
961
|
+
return
|
962
|
+
if not isinstance(element, dict):
|
963
|
+
logger.debug("sync_collection_memberships: element lookup did not return a dict; skipping")
|
964
|
+
return
|
965
|
+
|
966
|
+
member_rels = element.get("memberOfCollections", []) or []
|
967
|
+
|
968
|
+
# Build current membership maps
|
969
|
+
# - by GUID: set of current collection guids
|
970
|
+
# - by type name (classification names found on related collection): map type->set(guids)
|
971
|
+
current_all_guids: set[str] = set()
|
972
|
+
current_by_type: dict[str, set[str]] = {}
|
973
|
+
|
974
|
+
for rel in member_rels:
|
975
|
+
try:
|
976
|
+
related = (rel or {}).get("relatedElement", {})
|
977
|
+
rel_guid = ((related.get("elementHeader") or {}).get("guid"))
|
978
|
+
if not rel_guid:
|
979
|
+
continue
|
980
|
+
current_all_guids.add(rel_guid)
|
981
|
+
|
982
|
+
# Collect type hints from classifications and from properties.collectionType
|
983
|
+
type_names: set[str] = set()
|
984
|
+
classifications = ((related.get("elementHeader") or {}).get("classifications")) or []
|
985
|
+
for cls in classifications:
|
986
|
+
tname = (((cls or {}).get("type") or {}).get("typeName"))
|
987
|
+
if tname:
|
988
|
+
type_names.add(tname)
|
989
|
+
ctype = ((related.get("properties") or {}).get("collectionType"))
|
990
|
+
if isinstance(ctype, str) and ctype:
|
991
|
+
type_names.add(ctype)
|
992
|
+
|
993
|
+
if not type_names:
|
994
|
+
# Fallback: try elementHeader.type.typeName
|
995
|
+
tname2 = (((related.get("elementHeader") or {}).get("type") or {}).get("typeName"))
|
996
|
+
if tname2:
|
997
|
+
type_names.add(tname2)
|
998
|
+
|
999
|
+
for tn in type_names:
|
1000
|
+
s = current_by_type.setdefault(tn, set())
|
1001
|
+
s.add(rel_guid)
|
1002
|
+
except Exception as e:
|
1003
|
+
logger.debug(f"sync_collection_memberships: skipping malformed relationship: {e}")
|
1004
|
+
continue
|
985
1005
|
|
1006
|
+
# Helper to coerce incoming desired list entry to a set of guids
|
1007
|
+
def to_guid_set(maybe_list) -> set[str]:
|
1008
|
+
if not maybe_list:
|
1009
|
+
return set()
|
1010
|
+
if isinstance(maybe_list, list):
|
1011
|
+
return {g for g in maybe_list if isinstance(g, str) and g}
|
1012
|
+
# Sometimes a single guid may slip through
|
1013
|
+
if isinstance(maybe_list, str):
|
1014
|
+
return {maybe_list}
|
1015
|
+
return set()
|
1016
|
+
|
1017
|
+
# If merge_update is False: remove all existing memberships for the specified types
|
1018
|
+
if not merge_update:
|
1019
|
+
# Build a set of guids to remove across specified types
|
1020
|
+
to_remove: set[str] = set()
|
1021
|
+
for t in collection_types:
|
1022
|
+
if not t:
|
1023
|
+
continue
|
1024
|
+
# Match by exact type name as seen in current_by_type
|
1025
|
+
guids_for_type = current_by_type.get(t) or set()
|
1026
|
+
if not guids_for_type and t.lower() in {k.lower() for k in current_by_type.keys()}:
|
1027
|
+
# Case-insensitive fallback
|
1028
|
+
for k, v in current_by_type.items():
|
1029
|
+
if k.lower() == t.lower():
|
1030
|
+
guids_for_type = v
|
1031
|
+
break
|
1032
|
+
to_remove.update(guids_for_type)
|
1033
|
+
|
1034
|
+
for coll_guid in to_remove:
|
1035
|
+
try:
|
1036
|
+
egeria_client.remove_from_collection(coll_guid, guid)
|
1037
|
+
logger.info(f"Removed element {guid} from collection {coll_guid}")
|
1038
|
+
except Exception as e:
|
1039
|
+
logger.debug(f"Failed to remove element {guid} from collection {coll_guid}: {e}")
|
1040
|
+
|
1041
|
+
# Now add desired memberships (for both merge and replace flows)
|
1042
|
+
for idx, t in enumerate(collection_types):
|
1043
|
+
desired_set = to_guid_set(to_be_collection_guids[idx] if idx < len(to_be_collection_guids) else None)
|
1044
|
+
if not desired_set:
|
1045
|
+
continue
|
1046
|
+
for coll_guid in desired_set:
|
1047
|
+
# If merge_update True, skip if already a member; if False, we removed earlier so can re-add
|
1048
|
+
if merge_update and coll_guid in current_all_guids:
|
1049
|
+
continue
|
1050
|
+
try:
|
1051
|
+
egeria_client.add_to_collection(coll_guid, guid)
|
1052
|
+
logger.info(f"Added element {guid} to collection {coll_guid}")
|
1053
|
+
except Exception as e:
|
1054
|
+
logger.debug(f"Failed to add element {guid} to collection {coll_guid}: {e}")
|
1055
|
+
|
1056
|
+
return
|
1057
|
+
except Exception as e:
|
1058
|
+
logger.error(f"sync_collection_memberships: unexpected error: {e}")
|
1059
|
+
return
|
986
1060
|
|
987
1061
|
@logger.catch
|
988
1062
|
def process_output_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
|
@@ -300,10 +300,11 @@ def set_update_body(object_type: str, attributes: dict)->dict:
|
|
300
300
|
def set_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict:
|
301
301
|
|
302
302
|
prop_name = object_type.replace(" ", "")
|
303
|
+
display_name = attributes.get('Display Name', {}).get('value', None)
|
303
304
|
|
304
305
|
return {
|
305
306
|
"class": prop_name + "Properties",
|
306
|
-
"displayName": attributes
|
307
|
+
"displayName": attributes.get('Display Name', {}).get('value', None),
|
307
308
|
"qualifiedName" : qualified_name,
|
308
309
|
"description": attributes['Description'].get('value', None),
|
309
310
|
"category": attributes.get('Category', {}).get('value', None),
|