pyegeria 5.4.0.dev10__py3-none-any.whl → 5.4.0.dev12__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.
Files changed (30) hide show
  1. commands/cat/debug_log +6290 -6042
  2. commands/cat/debug_log.2025-07-01_15-22-20_102237.zip +0 -0
  3. commands/cat/debug_log.2025-07-04_15-43-28_460900.zip +0 -0
  4. commands/cat/debug_log.2025-07-06_20-48-04_338314.zip +0 -0
  5. commands/cat/debug_log.2025-07-09_10-17-09_526262.zip +0 -0
  6. commands/cat/dr_egeria_md.py +24 -14
  7. commands/cat/list_collections.py +11 -4
  8. md_processing/__init__.py +3 -1
  9. md_processing/data/commands.json +7842 -2231
  10. md_processing/md_commands/data_designer_commands.py +67 -80
  11. md_processing/md_commands/glossary_commands.py +3 -1
  12. md_processing/md_commands/product_manager_commands.py +1746 -0
  13. md_processing/md_commands/solution_architect_commands.py +390 -236
  14. md_processing/md_processing_utils/common_md_proc_utils.py +8 -6
  15. md_processing/md_processing_utils/md_processing_constants.py +25 -4
  16. pyegeria/__init__.py +1 -0
  17. pyegeria/_client.py +0 -1
  18. pyegeria/collection_manager_omvs.py +504 -546
  19. pyegeria/data_designer_omvs.py +16 -8
  20. pyegeria/egeria_tech_client.py +9 -25
  21. pyegeria/governance_officer_omvs.py +1446 -1343
  22. pyegeria/output_formatter.py +96 -8
  23. pyegeria/solution_architect_omvs.py +2278 -1728
  24. {pyegeria-5.4.0.dev10.dist-info → pyegeria-5.4.0.dev12.dist-info}/METADATA +1 -1
  25. {pyegeria-5.4.0.dev10.dist-info → pyegeria-5.4.0.dev12.dist-info}/RECORD +28 -25
  26. commands/cat/debug_log.2025-06-24_15-51-28_769553.zip +0 -0
  27. commands/cat/debug_log.2025-06-26_11-18-40_644650.zip +0 -0
  28. {pyegeria-5.4.0.dev10.dist-info → pyegeria-5.4.0.dev12.dist-info}/LICENSE +0 -0
  29. {pyegeria-5.4.0.dev10.dist-info → pyegeria-5.4.0.dev12.dist-info}/WHEEL +0 -0
  30. {pyegeria-5.4.0.dev10.dist-info → pyegeria-5.4.0.dev12.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1746 @@
1
+ """
2
+ This file contains product manager commands for processing Egeria Markdown
3
+ """
4
+ import json
5
+ import os
6
+ import sys
7
+ from typing import Optional
8
+
9
+ from loguru import logger
10
+ from rich import print
11
+ from rich.console import Console
12
+ from rich.markdown import Markdown
13
+
14
+ from md_processing.md_processing_utils.common_md_proc_utils import (parse_upsert_command, parse_view_command)
15
+ from md_processing.md_processing_utils.common_md_utils import update_element_dictionary
16
+ from md_processing.md_processing_utils.extraction_utils import (extract_command_plus, update_a_command)
17
+ from md_processing.md_processing_utils.md_processing_constants import (load_commands, ERROR)
18
+ from pyegeria import DEBUG_LEVEL, body_slimmer
19
+ from pyegeria.egeria_tech_client import EgeriaTech
20
+
21
+ GERIA_METADATA_STORE = os.environ.get("EGERIA_METADATA_STORE", "active-metadata-store")
22
+ EGERIA_KAFKA_ENDPOINT = os.environ.get("KAFKA_ENDPOINT", "localhost:9092")
23
+ EGERIA_PLATFORM_URL = os.environ.get("EGERIA_PLATFORM_URL", "https://localhost:9443")
24
+ EGERIA_VIEW_SERVER = os.environ.get("EGERIA_VIEW_SERVER", "view-server")
25
+ EGERIA_VIEW_SERVER_URL = os.environ.get("EGERIA_VIEW_SERVER_URL", "https://localhost:9443")
26
+ EGERIA_INTEGRATION_DAEMON = os.environ.get("EGERIA_INTEGRATION_DAEMON", "integration-daemon")
27
+ EGERIA_INTEGRATION_DAEMON_URL = os.environ.get("EGERIA_INTEGRATION_DAEMON_URL", "https://localhost:9443")
28
+ EGERIA_ADMIN_USER = os.environ.get("ADMIN_USER", "garygeeke")
29
+ EGERIA_ADMIN_PASSWORD = os.environ.get("ADMIN_PASSWORD", "secret")
30
+ EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
31
+ EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
32
+ EGERIA_WIDTH = os.environ.get("EGERIA_WIDTH", 220)
33
+ EGERIA_JUPYTER = os.environ.get("EGERIA_JUPYTER", False)
34
+ EGERIA_HOME_GLOSSARY_GUID = os.environ.get("EGERIA_HOME_GLOSSARY_GUID", None)
35
+ EGERIA_GLOSSARY_PATH = os.environ.get("EGERIA_GLOSSARY_PATH", None)
36
+ EGERIA_ROOT_PATH = os.environ.get("EGERIA_ROOT_PATH", "../../")
37
+ EGERIA_INBOX_PATH = os.environ.get("EGERIA_INBOX_PATH", "md_processing/dr_egeria_inbox")
38
+ EGERIA_OUTBOX_PATH = os.environ.get("EGERIA_OUTBOX_PATH", "md_processing/dr_egeria_outbox")
39
+
40
+ load_commands('commands.json')
41
+ debug_level = DEBUG_LEVEL
42
+
43
+ console = Console(width=int(200))
44
+
45
+ log_format = "D {time} | {level} | {function} | {line} | {message} | {extra}"
46
+ logger.remove()
47
+ logger.add(sys.stderr, level="INFO", format=log_format, colorize=True)
48
+ full_file_path = os.path.join(EGERIA_ROOT_PATH, EGERIA_INBOX_PATH, "data_designer_debug.log")
49
+ # logger.add(full_file_path, rotation="1 day", retention="1 week", compression="zip", level="TRACE", format=log_format,
50
+ # colorize=True)
51
+ logger.add("debug_log", rotation="1 day", retention="1 week", compression="zip", level="TRACE", format=log_format,
52
+ colorize=True)
53
+
54
+
55
+ #
56
+ # Helper functions for the data designer commands
57
+ #
58
+ @logger.catch
59
+ def add_member_to_collections(egeria_client: EgeriaTech, collection_list: list, display_name: str,
60
+ guid: str) -> None:
61
+ """
62
+ Add member to data dictionaries and data specifications.
63
+ """
64
+ body = {
65
+ "class": "RelationshipRequestBody", "properties": {
66
+ "class": "CollectionMembershipProperties", "membershipRationale": "User Specified",
67
+ "notes": "Added by Dr.Egeria"
68
+ }
69
+ }
70
+ try:
71
+ if collection_list is not None:
72
+ for collection in collection_list:
73
+ egeria_client.add_to_collection(collection, guid, body)
74
+ msg = f"Added `{display_name}` member to `{collection}`"
75
+ logger.info(msg)
76
+ else:
77
+ logger.info("There were no data collections to add.")
78
+ return
79
+
80
+ except Exception as e:
81
+ console.print_exception()
82
+
83
+
84
+ @logger.catch
85
+ def remove_member_from_collections(egeria_client: EgeriaTech, collection_list: list, display_name: str,
86
+ guid: str) -> None:
87
+ try:
88
+ for collection in collection_list:
89
+ egeria_client.remove_from_collection(collection, guid)
90
+ msg = f"Removed `{display_name}` member from `{collection}`"
91
+ logger.info(msg)
92
+ return
93
+
94
+ except Exception as e:
95
+ console.print_exception()
96
+
97
+
98
+ @logger.catch
99
+ def update_data_collection_memberships(egeria_client: EgeriaTech, entity_type: str, guid_list: list,
100
+ collection_class: str, guid: str, display_name: str,
101
+ replace_all_props: bool = True) -> None:
102
+ """ update the collection membership of the element
103
+
104
+ If replace_all_props is set to True, all existing memberships are removed and new memberships are added.
105
+ If replace_all_props is set to False, only the new memberships are added.
106
+ """
107
+
108
+ if replace_all_props:
109
+ match entity_type:
110
+ case "Data Specification":
111
+ get_command = egeria_client.get_collection_by_guid
112
+ case "Data Structure":
113
+ get_command = egeria_client.get_data_structure_by_guid
114
+ case "Data Field":
115
+ get_command = egeria_client.get_data_field_by_guid
116
+ case "Data Class":
117
+ get_command = egeria_client.get_data_class_by_guid
118
+
119
+ coll_list = egeria_client.get_data_memberships(get_command, guid)
120
+ if coll_list is None:
121
+ logger.warning("Unexpected -> the collection list was None - assigning empty dict")
122
+ coll_list = {}
123
+ # compare the existing collections to desired collections
124
+ if collection_class == "DataDictionary":
125
+ as_is = set(coll_list.get("DictList", {}))
126
+ elif collection_class == "DataSpec":
127
+ as_is = set(coll_list.get("SpecList", {}))
128
+
129
+ dict_set = set(coll_list.get("DictList", {}))
130
+ spec_set = set(coll_list.get("SpecList", {}))
131
+ to_be_set = set(guid_list) if guid_list is not None else set()
132
+ logger.debug(f"as_is: {as_is}")
133
+ logger.debug(f"to_be_set: {to_be_set}")
134
+
135
+ # Remove membership for collections that are in the as-is but not in the to-be
136
+ to_remove = as_is - to_be_set
137
+ logger.debug(f"to_remove: {to_remove}")
138
+ if len(to_remove) > 0:
139
+ remove_member_from_collections(egeria_client, to_remove, display_name, guid)
140
+
141
+ # add membership for collections that are in the to-be but are not in the as-is
142
+ to_add = to_be_set - as_is
143
+ logger.debug(f"to_add: {to_add}")
144
+ if len(to_add) > 0:
145
+ add_member_to_collections(egeria_client, to_add, display_name, guid)
146
+ else:
147
+ add_member_to_collections(egeria_client, guid_list, display_name, guid)
148
+
149
+
150
+ # @logger.catch
151
+
152
+
153
+ @logger.catch
154
+ def add_field_to_data_structures(egeria_client: EgeriaTech, display_name: str, struct_list: list, guid) -> None:
155
+ """
156
+ Add data field to data structures.
157
+ """
158
+
159
+ try:
160
+ for structure_guid in struct_list:
161
+ egeria_client.link_member_data_field(structure_guid, guid, None)
162
+ msg = f"Added `{display_name}` to structure `{structure_guid}`"
163
+ logger.info(msg)
164
+ return
165
+
166
+ except Exception as e:
167
+ console.print_exception()
168
+
169
+
170
+ @logger.catch
171
+ def remove_field_from_data_structures(egeria_client: EgeriaTech, display_name: str, struct_list: list,
172
+ guid: str) -> None:
173
+ """Remove a data field from a list of data structures."""
174
+ try:
175
+ for structure_guid in struct_list:
176
+ egeria_client.detach_member_data_field(structure_guid, guid, None)
177
+ msg = f"Removed `{display_name}` from structure `{structure_guid}`"
178
+ logger.info(msg)
179
+ return
180
+
181
+ except Exception as e:
182
+ console.print_exception()
183
+
184
+
185
+ @logger.catch
186
+ def sync_data_field_rel_elements(egeria_client: EgeriaTech, structure_list: list, parent_field_list: list, terms: list,
187
+ data_class_guid: str, guid: str, display_name: str,
188
+ replace_all_props: bool = True) -> None:
189
+ """Sync a field's related elements.
190
+
191
+ TODO: Need to add data class support when ready and may need to revisit bodies.
192
+
193
+ """
194
+ if terms:
195
+ terms = [terms]
196
+
197
+ if replace_all_props:
198
+ rel_el_list = egeria_client.get_data_field_rel_elements(guid)
199
+ # should I throw an exception if empty?
200
+ if rel_el_list is None:
201
+ logger.warning("Unexpected -> the list was None - assigning empty list")
202
+ rel_el_list = {}
203
+
204
+ as_is_data_structs = set(rel_el_list.get("data_structure_guids", []))
205
+ as_is_parent_fields = set(rel_el_list.get("parent_guids", []))
206
+ as_is_assigned_meanings = set(rel_el_list.get("assigned_meanings_guids", []))
207
+ as_is_data_classes = set(rel_el_list.get("data_class_guids", []))
208
+
209
+ to_be_data_structs = set(structure_list) if structure_list is not None else set()
210
+ to_be_parent_fields = set(parent_field_list) if parent_field_list is not None else set()
211
+ to_be_assigned_meanings = set(terms) if terms is not None else set()
212
+ to_be_data_classes = set([data_class_guid]) if data_class_guid is not None else set()
213
+
214
+ logger.trace(f"as_is_data_structs: {list(as_is_data_structs)} to_be_data_struct: {list(to_be_data_structs)}")
215
+ logger.trace(
216
+ f"as_is_parent_fields: {list(as_is_parent_fields)} to_be_parent_fields: {list(to_be_parent_fields)}")
217
+ logger.trace(f"as_is_assigned_meanings: {list(as_is_assigned_meanings)} to_be_assigned_meanings: "
218
+ f"{list(to_be_assigned_meanings)}")
219
+ logger.trace(f"as_is_data_classes: {list(as_is_data_classes)} to_be_assigned_data_classes: "
220
+ f"{list(to_be_data_classes)}")
221
+
222
+ data_struct_to_remove = as_is_data_structs - to_be_data_structs
223
+ logger.trace(f"data_struct_to_remove: {list(data_struct_to_remove)}")
224
+ if len(data_struct_to_remove) > 0:
225
+ for ds in data_struct_to_remove:
226
+ egeria_client.detach_member_data_field(ds, guid, None)
227
+ msg = f"Removed `{display_name}` from structure `{ds}`"
228
+ logger.trace(msg)
229
+ data_struct_to_add = to_be_data_structs - as_is_data_structs
230
+ logger.trace(f"data_struct_to_add: {list(data_struct_to_add)}")
231
+ if len(data_struct_to_add) > 0:
232
+ for ds in data_struct_to_add:
233
+ egeria_client.link_member_data_field(ds, guid, None)
234
+ msg = f"Added `{display_name}` to structure `{ds}`"
235
+ logger.trace(msg)
236
+
237
+ parent_field_to_remove = to_be_parent_fields - as_is_parent_fields
238
+ logger.trace(f"parent_field_to_remove: {list(parent_field_to_remove)}")
239
+ if len(parent_field_to_remove) > 0:
240
+ for field in parent_field_to_remove:
241
+ egeria_client.detach_nested_data_field(field, guid, None)
242
+ msg = f"Removed `{display_name}` from field `{field}`"
243
+ logger.trace(msg)
244
+ parent_field_to_add = to_be_parent_fields - as_is_parent_fields
245
+ logger.trace(f"parent_field_to_add: {list(parent_field_to_add)}")
246
+ if len(parent_field_to_add) > 0:
247
+ for field in parent_field_to_add:
248
+ egeria_client.link_nested_data_field(field, guid, None)
249
+ msg = f"Added `{display_name}` to field `{field}`"
250
+ logger.trace(msg)
251
+
252
+ terms_to_remove = as_is_assigned_meanings - to_be_assigned_meanings
253
+ logger.trace(f"terms_to_remove: {list(terms_to_remove)}")
254
+ if terms:
255
+ for term in terms_to_remove:
256
+ egeria_client.detach_semantic_definition(guid, term, None)
257
+ msg = f"Removed `{term}` from `{display_name}`"
258
+ logger.trace(msg)
259
+ terms_to_add = to_be_assigned_meanings - as_is_assigned_meanings
260
+ logger.trace(f"terms_to_add: {list(terms_to_add)}")
261
+ if len(terms_to_add) > 0:
262
+ for term in terms_to_add:
263
+ egeria_client.link_semantic_definition(guid, term, None)
264
+ msg = f"Added `{term}` to`{display_name}`"
265
+ logger.trace(msg)
266
+
267
+ classes_to_remove = as_is_data_classes - to_be_data_classes
268
+ logger.trace(f"classes_to_remove: {list(classes_to_remove)}")
269
+ if len(terms_to_remove) > 0:
270
+ for dc in classes_to_remove:
271
+ body = {
272
+ "class": "MetadataSourceRequestBody", "forLineage": False, "forDuplicateProcessing": False
273
+ }
274
+ egeria_client.detach_data_class_definition(guid, dc, body)
275
+ msg = f"Removed `{dc}` from `{display_name}`"
276
+ logger.trace(msg)
277
+ classes_to_add = to_be_data_classes - as_is_data_classes
278
+ logger.trace(f"classes_to_add: {list(classes_to_add)}")
279
+ if len(terms_to_add) > 0:
280
+ for dc in classes_to_add:
281
+ body = {
282
+ "class": "RelationshipRequestBody", "forLineage": False, "forDuplicateProcessing": False
283
+ }
284
+ egeria_client.link_data_class_definition(guid, dc, body)
285
+ msg = f"Added `{dc}` to`{display_name}`"
286
+ logger.trace(msg)
287
+
288
+
289
+ else: # merge - add field to related elements
290
+ if structure_list:
291
+ add_field_to_data_structures(egeria_client, display_name, structure_list, guid)
292
+ msg = f"Added `{display_name}` to `{structure_list}`"
293
+ logger.trace(msg)
294
+
295
+ if parent_field_list:
296
+ for field in parent_field_list:
297
+ egeria_client.link_nested_data_field(field, guid, None)
298
+ msg = f"Added `{display_name}` to `{field}`"
299
+ logger.trace(msg)
300
+ if terms:
301
+ for term in terms:
302
+ egeria_client.link_semantic_definition(guid, term, None)
303
+ msg = f"Added `{term}` to `{display_name}`"
304
+ logger.trace(msg)
305
+
306
+ if data_class_guid:
307
+ egeria_client.link_data_class_definition(guid, data_class_guid)
308
+ msg = f"Added `{data_class_guid}` to `{display_name}`"
309
+ logger.trace(msg)
310
+
311
+
312
+ @logger.catch
313
+ def sync_data_class_rel_elements(egeria_client: EgeriaTech, containing_data_class_guids: list, terms: list,
314
+ specializes_data_classes: list, guid: str, display_name: str,
315
+ replace_all_props: bool = True) -> None:
316
+ """Sync a data class' related elements.
317
+
318
+ """
319
+ if terms:
320
+ terms = [terms]
321
+
322
+ if replace_all_props:
323
+ rel_el_list = egeria_client.get_data_class_rel_elements(guid)
324
+ if rel_el_list is None:
325
+ logger.warning("Unexpected -> the list was None - assigning empty list")
326
+ rel_el_list = {}
327
+ if terms:
328
+ terms = [terms]
329
+
330
+ as_is_nested_classes = set(rel_el_list.get("nested_data_class_guids", []))
331
+ as_is_assigned_meanings = set(rel_el_list.get("assigned_meanings_guids", []))
332
+ as_is_specialized_classes = set(rel_el_list.get("specialized_data_class_guids", []))
333
+
334
+ to_be_nested_classes = set(containing_data_class_guids) if containing_data_class_guids is not None else set()
335
+ to_be_assigned_meanings = set(terms) if terms is not None else set()
336
+ to_be_specialized_classes = set([specializes_data_classes]) if specializes_data_classes is not None else set()
337
+
338
+ logger.trace(
339
+ f"as_is_nested_classes: {list(as_is_nested_classes)} to_be_nested_classes: {list(to_be_nested_classes)}")
340
+ logger.trace(f"as_is_assigned_meanings: {list(as_is_assigned_meanings)} to_be_assigned_meanings: "
341
+ f"{list(to_be_assigned_meanings)}")
342
+ logger.trace(f"as_is_specialized_classes: {list(as_is_specialized_classes)} to_be_specizialized_data_classes: "
343
+ f"{list(to_be_specialized_classes)}")
344
+
345
+ nested_classes_to_remove = to_be_nested_classes - as_is_nested_classes
346
+ logger.trace(f"nested_classes_to_remove: {list(nested_classes_to_remove)}")
347
+ if len(nested_classes_to_remove) > 0:
348
+ for field in nested_classes_to_remove:
349
+ egeria_client.detach_nested_data_class(field, guid, None)
350
+ msg = f"Removed `{display_name}` from field `{field}`"
351
+ logger.trace(msg)
352
+ nested_classes_to_add = to_be_nested_classes - as_is_nested_classes
353
+ logger.trace(f"nested_classes_to_add: {list(nested_classes_to_add)}")
354
+ if len(nested_classes_to_add) > 0:
355
+ for field in nested_classes_to_add:
356
+ egeria_client.link_nested_data_class(field, guid, None)
357
+ msg = f"Added `{display_name}` to field `{field}`"
358
+ logger.trace(msg)
359
+
360
+ terms_to_remove = as_is_assigned_meanings - to_be_assigned_meanings
361
+ logger.trace(f"terms_to_remove: {list(terms_to_remove)}")
362
+ if len(terms_to_remove) > 0:
363
+ for term in terms_to_remove:
364
+ egeria_client.detach_semantic_definition(guid, term, None)
365
+ msg = f"Removed `{term}` from `{display_name}`"
366
+ logger.trace(msg)
367
+ terms_to_add = to_be_assigned_meanings - as_is_assigned_meanings
368
+ logger.trace(f"terms_to_add: {list(terms_to_add)}")
369
+ if len(terms_to_add) > 0:
370
+ for term in terms_to_add:
371
+ egeria_client.link_semantic_definition(guid, term, None)
372
+ msg = f"Added `{term}` to`{display_name}`"
373
+ logger.trace(msg)
374
+
375
+ specialized_classes_to_remove = as_is_specialized_classes - to_be_specialized_classes
376
+ logger.trace(f"classes_to_remove: {list(specialized_classes_to_remove)}")
377
+ if len(terms_to_remove) > 0:
378
+ for dc in specialized_classes_to_remove:
379
+ body = {
380
+ "class": "MetadataSourceRequestBody", "forLineage": False, "forDuplicateProcessing": False
381
+ }
382
+ egeria_client.detach_specialist_data_class(guid, dc, body)
383
+ msg = f"Removed `{dc}` from `{display_name}`"
384
+ logger.trace(msg)
385
+ specialized_classes_to_add = to_be_specialized_classes - as_is_specialized_classes
386
+ logger.trace(f"classes_to_add: {list(specialized_classes_to_add)}")
387
+ if len(specialized_classes_to_add) > 0:
388
+ for dc in specialized_classes_to_add:
389
+ body = {
390
+ "class": "RelationshipRequestBody", "forLineage": False, "forDuplicateProcessing": False
391
+ }
392
+ egeria_client.link_specialist_data_class(guid, dc, body)
393
+ msg = f"Added `{dc}` to`{display_name}`"
394
+ logger.trace(msg)
395
+
396
+
397
+ else: # merge - add field to related elements
398
+ if containing_data_class_guids:
399
+ for field in containing_data_class_guids:
400
+ egeria_client.link_nested_data_class(field, guid, None)
401
+ msg = f"Added `{display_name}` to `{field}`"
402
+ logger.trace(msg)
403
+
404
+ if terms:
405
+ for term in terms:
406
+ egeria_client.link_semantic_definition(guid, term, None)
407
+ msg = f"Added `{term}` to `{display_name}`"
408
+ logger.trace(msg)
409
+ if specializes_data_classes:
410
+ for el in specializes_data_classes:
411
+ egeria_client.link_specialist_data_class(guid, el)
412
+ msg = f"Linked `{el}` to `{display_name}`"
413
+ logger.trace(msg)
414
+
415
+
416
+ @logger.catch
417
+ def process_digital_product_upsert_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
418
+ """
419
+ Processes a data specification create or update object_action by extracting key attributes such as
420
+ spec name, parent_guid, parent_relationship_type, parent_at_end_1, collection_type
421
+
422
+ :param txt: A string representing the input cell to be processed for
423
+ extracting glossary-related attributes.
424
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
425
+ :return: A string summarizing the outcome of the processing.
426
+ """
427
+
428
+ command, object_type, object_action = extract_command_plus(txt)
429
+
430
+ parsed_output = parse_upsert_command(egeria_client, object_type, object_action, txt, directive)
431
+
432
+ valid = parsed_output['valid']
433
+ exists = parsed_output['exists']
434
+
435
+ qualified_name = parsed_output.get('qualified_name', None)
436
+ guid = parsed_output.get('guid', None)
437
+
438
+ print(Markdown(parsed_output['display']))
439
+
440
+ logger.debug(json.dumps(parsed_output, indent=4))
441
+
442
+ attributes = parsed_output['attributes']
443
+
444
+ display_name = attributes['Display Name'].get('value', None)
445
+ description = attributes['Description'].get('value', None)
446
+ user_defined_status = attributes['User Defined Status'].get('value', None)
447
+ product_identifier = attributes['Product Identifier'].get('value', None)
448
+ product_name = attributes['Product Name'].get('value', None)
449
+ product_type = attributes['Product Type'].get('value', None)
450
+
451
+
452
+
453
+ product_description = attributes['Product Description'].get('value', None)
454
+ maturity = attributes['Maturity'].get('value', None)
455
+ service_life = attributes['Service Life'].get('value', None)
456
+ introduction_date = attributes['Introduction Date'].get('value', None)
457
+ next_version_date = attributes['Next Version Date'].get('value', None)
458
+ withdrawal_date = attributes['Withdrawal Date'].get('value', None)
459
+
460
+ collection_type = attributes.get('Collection Type', {}).get('value', None)
461
+ current_version = attributes['Current Version'].get('value', None)
462
+ product_manager = attributes['Product Manager'].get('value', None)
463
+
464
+ agreements = attributes['Agreements'].get('value', None)
465
+ agreement_names = attributes['Agreements Names'].get('name_list', None)
466
+ agreement_guids = attributes['Agreements Guids'].get('guid_list', None)
467
+
468
+ subscriptions = attributes['Digital Subscriptions'].get('value', None)
469
+ subscription_names = attributes['Digital Subscriptions Names'].get('name_list', None)
470
+ subscription_guids = attributes['Digital Subscriptions Guids'].get('guid_list', None)
471
+
472
+ product_status = attributes['Product Status'].get('value', "ACTIVE")
473
+
474
+ anchor_guid = attributes.get('Anchor ID', {}).get('guid', None)
475
+ parent_guid = attributes.get('Parent ID', {}).get('guid', None)
476
+ parent_relationship_type_name = attributes.get('Parent Relationship Type Name', {}).get('value', "CollectionMembership")
477
+ parent_at_end1 = attributes.get('Parent at End1', {}).get('value', True)
478
+ anchor_scope_guid = attributes.get('Anchor Scope GUID', {}).get('value', None)
479
+ is_own_anchor = attributes.get('Is Own Anchor', {}).get('value', True)
480
+ if parent_guid is None:
481
+ is_own_anchor = True
482
+
483
+
484
+ additional_prop = attributes.get('Additional Properties', {}).get('value', None)
485
+ additional_properties = json.loads(additional_prop) if additional_prop is not None else None
486
+ extended_prop = attributes.get('Extended Properties', {}).get('value', None)
487
+ extended_properties = json.loads(extended_prop) if extended_prop is not None else None
488
+ external_source_guid = attributes.get('External Source Name', {}).get('guid', None)
489
+ external_source_name = attributes.get('External Source Name', {}).get('value', None)
490
+ effective_time = attributes.get('Effective Time', {}).get('value', None)
491
+ for_lineage = attributes.get('For Lineage', {}).get('value', None)
492
+ for_duplicate_processing = attributes.get('For Duplicate Processing', {}).get('value', None)
493
+
494
+ replace_all_props = not attributes.get('Merge Update', {}).get('value', True)
495
+
496
+ if directive == "display":
497
+
498
+ return None
499
+ elif directive == "validate":
500
+ if valid:
501
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
502
+ else:
503
+ msg = f"Validation failed for object_action `{command}`\n"
504
+ return valid
505
+
506
+ elif directive == "process":
507
+ try:
508
+ if object_action == "Update":
509
+ if not exists:
510
+ msg = (f" Element `{display_name}` does not exist! Updating result document with Create "
511
+ f"object_action\n")
512
+ logger.error(msg)
513
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
514
+ elif not valid:
515
+ return None
516
+ else:
517
+ print(Markdown(
518
+ f"==> Validation of {command} completed successfully! Proceeding to apply the changes.\n"))
519
+
520
+ body ={
521
+ "class": "UpdateElementRequestBody",
522
+ "properties": {
523
+ "class": "DigitalProductProperties",
524
+ "qualifiedName": qualified_name,
525
+ "userDefinedStatus": user_defined_status,
526
+ "name": display_name,
527
+ "description": description,
528
+ "identifier": product_identifier,
529
+ "productName": product_name,
530
+ "productType": product_type,
531
+ "maturity": maturity,
532
+ "serviceLife": service_life,
533
+ "introductionDate": introduction_date,
534
+ "nextVersionDate": next_version_date,
535
+ "withdrawDate": withdrawal_date,
536
+ "currentVersion": current_version,
537
+
538
+ "additionalProperties": additional_properties,
539
+ "extendedProperties": extended_properties,
540
+ },
541
+ "externalSourceGUID": external_source_guid,
542
+ "externalSourceName": external_source_name,
543
+ "effectiveTime": effective_time,
544
+ "forLineage": for_lineage,
545
+ "forDuplicateProcessing": for_duplicate_processing
546
+ }
547
+
548
+ egeria_client.update_digital_product(guid, body)
549
+ egeria_client.update_digital_product_status(guid, product_status)
550
+
551
+ logger.success(f"Updated {object_type} `{display_name}` with GUID {guid}\n\n___")
552
+ update_element_dictionary(qualified_name, {
553
+ 'guid': guid, 'display_name': display_name
554
+ })
555
+ return egeria_client.get_collection_by_guid(guid, collection_type='Data Specification',
556
+ output_format='MD')
557
+
558
+
559
+ elif object_action == "Create":
560
+ if valid is False and exists:
561
+ msg = (f" Digital Product `{display_name}` already exists and result document updated changing "
562
+ f"`Create` to `Update` in processed output\n\n___")
563
+ logger.error(msg)
564
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
565
+
566
+ else:
567
+ body = {
568
+ "class": "NewDigitalProductRequestBody",
569
+ "isOwnAnchor": is_own_anchor,
570
+ "parentGUID": parent_guid,
571
+ "parentRelationshipTypeName": parent_relationship_type_name,
572
+ "parentAtEnd1": parent_at_end1,
573
+ "properties": {
574
+ "class": "DigitalProductProperties",
575
+ "qualifiedName": qualified_name,
576
+ "userDefinedStatus": user_defined_status,
577
+ "name": display_name,
578
+ "description" : description,
579
+ "identifier": product_identifier,
580
+ "productName": product_name,
581
+ "productType": product_type,
582
+ "maturity": maturity,
583
+ "serviceLife": service_life,
584
+ "introductionDate": introduction_date,
585
+ "nextVersionDate": next_version_date,
586
+ "withdrawDate": withdrawal_date,
587
+ "currentVersion": current_version,
588
+
589
+ "additionalProperties": additional_properties,
590
+ "extendedProperties": extended_properties,
591
+ },
592
+ "initialStatus": product_status,
593
+ "externalSourceGUID": external_source_guid,
594
+ "externalSourceName": external_source_name,
595
+ "effectiveTime" : effective_time,
596
+ "forLineage": for_lineage,
597
+ "forDuplicateProcessing": for_duplicate_processing
598
+ }
599
+
600
+
601
+
602
+ guid = egeria_client.create_digital_product(body)
603
+ if guid:
604
+ update_element_dictionary(qualified_name, {
605
+ 'guid': guid, 'display_name': display_name
606
+ })
607
+ msg = f"Created Element `{display_name}` with GUID {guid}\n\n___"
608
+ logger.success(msg)
609
+ return egeria_client.get_collection_by_guid(guid, collection_type='Digital Product',
610
+ output_format='MD')
611
+ else:
612
+ msg = f"Failed to create element `{display_name}` with GUID {guid}\n\n___"
613
+ logger.error(msg)
614
+ return None
615
+
616
+ except Exception as e:
617
+ logger.error(f"Error performing {command}: {e}")
618
+ return None
619
+ else:
620
+ return None
621
+
622
+
623
+ @logger.catch
624
+ def process_data_dict_upsert_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
625
+ """
626
+ Processes a data dictionary create or update object_action by extracting key attributes such as
627
+ spec name, parent_guid, parent_relationship_type, parent_at_end_1, collection_type
628
+
629
+ :param txt: A string representing the input cell to be processed for
630
+ extracting glossary-related attributes.
631
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
632
+ :return: A string summarizing the outcome of the processing.
633
+ """
634
+
635
+ command, object_type, object_action = extract_command_plus(txt)
636
+
637
+ parsed_output = parse_upsert_command(egeria_client, object_type, object_action, txt, directive)
638
+
639
+ valid = parsed_output['valid']
640
+ exists = parsed_output['exists']
641
+
642
+ qualified_name = parsed_output.get('qualified_name', None)
643
+ guid = parsed_output.get('guid', None)
644
+
645
+ print(Markdown(parsed_output['display']))
646
+ logger.debug(json.dumps(parsed_output, indent=4))
647
+
648
+ attributes = parsed_output['attributes']
649
+ description = attributes['Description'].get('value', None)
650
+ display_name = attributes.get('Display Name', {}).get('value', "None Found")
651
+ display_name = display_name if display_name is not None else "None Found"
652
+ anchor_guid = attributes.get('Anchor ID', {}).get('guid', None)
653
+ parent_guid = attributes.get('Parent ID', {}).get('guid', None)
654
+ parent_relationship_type_name = attributes.get('Parent Relationship Type Name', {}).get('value',
655
+ "CollectionMembership")
656
+ parent_at_end1 = attributes.get('Parent at End1', {}).get('value', True)
657
+
658
+ anchor_scope_guid = attributes.get('Anchor Scope GUID', {}).get('value', None)
659
+ is_own_anchor = attributes.get('Is Own Anchor', {}).get('value', True)
660
+ if parent_guid is None:
661
+ is_own_anchor = True
662
+ collection_type = attributes.get('Collection Type', {}).get('value', None)
663
+ replace_all_props = not attributes.get('Merge Update', {}).get('value', True)
664
+
665
+ additional_prop = attributes.get('Additional Properties', {}).get('value', None)
666
+ additional_properties = json.loads(additional_prop) if additional_prop is not None else None
667
+ extended_prop = attributes.get('Extended Properties', {}).get('value', None)
668
+ extended_properties = json.loads(extended_prop) if extended_prop is not None else None
669
+
670
+ if directive == "display":
671
+ return None
672
+ elif directive == "validate":
673
+ if valid:
674
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
675
+ else:
676
+ msg = f"Validation failed for object_action `{command}`\n"
677
+ return valid
678
+
679
+ elif directive == "process":
680
+
681
+ try:
682
+ if object_action == "Update":
683
+
684
+ if not exists:
685
+ logger.error(f"Element `{display_name}` does not exist! Updating result document with Create "
686
+ f"object_action\n\n___")
687
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
688
+ elif not valid:
689
+ logger.error(f"Element `{display_name}` does not have a valid specification? Review..\n\n___ ")
690
+ return None
691
+ else:
692
+ print(Markdown(
693
+ f"==> Validation of {command} completed successfully! Proceeding to apply the changes."))
694
+
695
+ egeria_client.update_collection(guid, qualified_name, display_name, description, collection_type,
696
+ additional_properties,
697
+ extended_properties, replace_all_props)
698
+ logger.success(f"Updated {object_type} `{display_name}` with GUID {guid}\n\n___")
699
+ update_element_dictionary(qualified_name, {
700
+ 'guid': guid, 'display_name': display_name
701
+ })
702
+ return egeria_client.get_collection_by_guid(guid, collection_type='Data Dictionary', output_format='MD')
703
+
704
+ elif object_action == "Create":
705
+ if valid is False and exists:
706
+ logger.error(f"\nElement `{display_name}` already exists and result document updated changing "
707
+ f"`Create` to `Update` in processed output\n\n___")
708
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
709
+ else:
710
+ guid = egeria_client.create_data_dictionary_collection(display_name, description, qualified_name,
711
+ is_own_anchor, anchor_guid, parent_guid,
712
+ parent_relationship_type_name,
713
+ parent_at_end1, collection_type,
714
+ anchor_scope_guid, additional_properties,
715
+ extended_properties)
716
+ if guid:
717
+ update_element_dictionary(qualified_name, {
718
+ 'guid': guid, 'display_name': display_name
719
+ })
720
+ logger.success(f"Created Element `{display_name}` with GUID {guid}\n\n___")
721
+
722
+ return egeria_client.get_collection_by_guid(guid, collection_type='Data Dictionary',
723
+ output_format='MD')
724
+ else:
725
+ logger.error(f"Failed to create Term `{display_name}`\n\n___")
726
+ return None
727
+
728
+ except Exception as e:
729
+ logger.error(f"{ERROR}Error performing {command}: {e}")
730
+ Console().print_exception(show_locals=True)
731
+ return None
732
+ else:
733
+ return None
734
+
735
+
736
+ @logger.catch
737
+ def process_data_structure_upsert_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[
738
+ str]:
739
+ """
740
+ Processes a data structure create or update object_action by extracting key attributes such as
741
+ spec name, parent_guid, parent_relationship_type, parent_at_end_1, collection_type
742
+
743
+ :param txt: A string representing the input cell to be processed for
744
+ extracting glossary-related attributes.
745
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
746
+ :return: A string summarizing the outcome of the processing.
747
+ """
748
+ from md_processing.md_processing_utils.common_md_utils import set_debug_level
749
+
750
+ set_debug_level(directive)
751
+
752
+ command, object_type, object_action = extract_command_plus(txt)
753
+
754
+ parsed_output = parse_upsert_command(egeria_client, object_type, object_action, txt, directive)
755
+
756
+ valid = parsed_output['valid']
757
+ exists = parsed_output['exists']
758
+
759
+ qualified_name = parsed_output.get('qualified_name', None)
760
+ guid = parsed_output.get('guid', None)
761
+
762
+ print(Markdown(parsed_output['display']))
763
+
764
+ if directive == "display":
765
+ return None
766
+
767
+ elif directive == "validate":
768
+ if valid:
769
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
770
+ else:
771
+ msg = f"Validation failed for object_action `{command}`\n"
772
+ return valid
773
+
774
+ elif directive == "process":
775
+ logger.debug(json.dumps(parsed_output, indent=4))
776
+ attributes = parsed_output['attributes']
777
+
778
+ external_source_guid = attributes.get('External Source Name', {}).get('guid', None)
779
+ external_source_name = attributes.get('External Source Name', {}).get('value', None)
780
+ effective_time = attributes.get('Effective Time', {}).get('value', None)
781
+ for_lineage = attributes.get('For Lineage', {}).get('value', None)
782
+ for_duplicate_processing = attributes.get('For Duplicate Processing', {}).get('value', None)
783
+ anchor_guid = attributes.get('Anchor ID', {}).get('guid', None)
784
+ is_own_anchor = attributes.get('Is Own Anchor', {}).get('value', None)
785
+ parent_id = attributes.get('Parent ID', {}).get('value', None)
786
+ parent_guid = attributes.get('Parent ID', {}).get('guid', None)
787
+ parent_relationship_type_name = attributes.get('Parent Relationship Type Name', {}).get('value', None)
788
+ parent_relationship_properties = attributes.get('Parent Relationship Properties', {}).get('value', None)
789
+ parent_at_end1 = attributes.get('Parent at End1', {}).get('value', None)
790
+
791
+ display_name = attributes['Display Name'].get('value', None)
792
+
793
+ namespace = attributes.get('Namespace', {}).get('value', None)
794
+ description = attributes.get('Description', {}).get('value', None)
795
+ version_id = attributes.get('Version Identifier', {}).get('value', None)
796
+ aliases = attributes.get('Aliases', {}).get('value', None)
797
+ name_patterns = attributes.get('Name Patterns', {}).get('value', None)
798
+ is_nullable = attributes.get('Is Nullable', {}).get('value', None)
799
+ default_value = attributes.get('Default Value', {}).get('value', None)
800
+ data_type = attributes.get('Data Type', {}).get('value', None)
801
+ min_length = attributes.get('Minimum Length', {}).get('value', None)
802
+ length = attributes.get('Length', {}).get('value', None)
803
+ precision = attributes.get('Precision', {}).get('value', None)
804
+ ordered_values = attributes.get('Ordered Values', {}).get('value', None)
805
+ sort_order = attributes.get('Sort Order', {}).get('value', None)
806
+ additional_properties = attributes.get('Additional Properties', {}).get('value', None)
807
+ effective_from = attributes.get('Effective From', {}).get('value', None)
808
+ effective_to = attributes.get('Effective To', {}).get('value', None)
809
+
810
+ position = attributes.get('Position', {}).get('value', None)
811
+ min_cardinality = attributes.get('Minimum Cardinality', {}).get('value', None)
812
+ max_cardinality = attributes.get('Maximum Cardinality', {}).get('value', None)
813
+ in_data_structure = attributes.get('In Data Structure', {}).get('value', None)
814
+ data_class = attributes.get('Data Class', {}).get('value', None)
815
+ glossary_term = attributes.get('Glossary Term', {}).get('value', None)
816
+ glossary_term_guid = attributes.get('Glossary Term', {}).get('guid', None)
817
+
818
+ # name_details_list = attributes.get("dict_list", None)
819
+
820
+ data_spec_name_list = attributes.get("In Data Specification", {}).get("name_list", "")
821
+ data_spec_value = attributes.get("In Data Specification", {}).get("value", None)
822
+ data_spec_guid_list = attributes.get("In Data Specification", {}).get("guid_list", None)
823
+
824
+ in_data_dictionary = attributes.get('In Data Dictionary', {}).get('dict_list', None)
825
+ data_dict_name_list = attributes.get('In Data Dictionary', {}).get('name_list', "")
826
+ data_dict_value_list = attributes.get('In Data Dictionary', {}).get('value', None)
827
+ data_dict_guid_list = attributes.get("In Data Dictionary", {}).get("guid_list", None)
828
+
829
+ parent_data_field = attributes.get('Parent Data Field', {}).get('value', None)
830
+ parent_data_field_guid = attributes.get('Parent Data Field', {}).get('guid', None)
831
+
832
+ anchor_scope_guid = attributes.get('Anchor Scope GUID', {}).get('value', None)
833
+
834
+ collection_type = object_type
835
+ replace_all_props = True
836
+ if not valid:
837
+ if exists and object_action == "Create":
838
+ msg = (f"Create failed because Element `{display_name}` exists - changing `Create` to `Update` in "
839
+ f"processed output \n\n___")
840
+ logger.error(msg)
841
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
842
+ else:
843
+ return None
844
+ elif object_action == "Update" and not exists:
845
+ logger.error(f"Element `{display_name}` does not exist! Updating result document with Create "
846
+ f"object_action\n\n___")
847
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
848
+
849
+ else:
850
+ print(Markdown(f"==> Validation of {command} completed successfully! Proceeding to apply the changes.\n"))
851
+
852
+ try:
853
+ if object_action == "Update":
854
+ body = {
855
+ "class": "UpdateElementRequestBody", "externalSourceGUID": external_source_guid,
856
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
857
+ "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing, "properties": {
858
+ "class": "DataStructureProperties", "qualifiedName": qualified_name,
859
+ "displayName": display_name, "description": description, "namespace": namespace,
860
+ "versionIdentifier": version_id, "additionalProperties": additional_properties,
861
+ "effectiveFrom": effective_from, "effectiveTo": effective_to
862
+ }
863
+ }
864
+ egeria_client.update_data_structure_w_body(guid, body, replace_all_props)
865
+ logger.info(f"Updated element `{display_name}` with GUID {guid}")
866
+ core_props = egeria_client.get_data_structure_by_guid(guid, output_format='MD')
867
+
868
+ update_element_dictionary(qualified_name, {
869
+ 'guid': guid, 'display_name': display_name
870
+ })
871
+
872
+ update_data_collection_memberships(egeria_client, object_type, data_spec_guid_list, "DataSpec", guid,
873
+ display_name, replace_all_props)
874
+ core_props += f"## In Data Dictionary\n\n{data_dict_name_list}\n\n"
875
+ core_props += f"## In Data Specification\n\n{data_spec_name_list}\n\n"
876
+ logger.success(f"Updated {object_type} `{display_name}` with GUID {guid}\n\n___")
877
+ return core_props
878
+
879
+ elif object_action == "Create":
880
+ if exists:
881
+ logger.warning(f"\nTerm `{display_name}` already exists and result document updated\n\n___")
882
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
883
+ else:
884
+
885
+ body = {
886
+ "class": "NewElementRequestBody", "externalSourceGUID": external_source_guid,
887
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
888
+ "forLineage": False, "forDuplicateProcessing": False, "anchorGUID": anchor_guid,
889
+ "isOwnAnchor": is_own_anchor, "parentGUID": parent_guid,
890
+ "parentRelationshipTypeName": parent_relationship_type_name,
891
+ "parentRelationshipProperties": parent_relationship_properties, "parentAtEnd1": parent_at_end1,
892
+ "properties": {
893
+ "class": "DataStructureProperties", "qualifiedName": qualified_name,
894
+ "displayName": display_name, "description": description, "namespace": namespace,
895
+ "versionIdentifier": version_id, "additionalProperties": additional_properties,
896
+ "effectiveFrom": effective_from, "effectiveTo": effective_to
897
+ }
898
+ }
899
+
900
+ guid = egeria_client.create_data_structure_w_body(body_slimmer(body))
901
+ if guid:
902
+ update_element_dictionary(qualified_name, {
903
+ 'guid': guid, 'display_name': display_name
904
+ })
905
+
906
+ core_props = egeria_client.get_data_structure_by_guid(guid, output_format='MD')
907
+
908
+ if in_data_dictionary:
909
+ logger.info(f"Will add to data dictionary(s) `{in_data_dictionary}`")
910
+ result = add_member_to_collections(egeria_client, in_data_dictionary, display_name,
911
+ guid)
912
+ core_props += f"## In Data Dictionary\n\n{data_dict_name_list}\n\n"
913
+
914
+ if data_spec_guid_list:
915
+ result = add_member_to_collections(egeria_client, data_spec_guid_list, display_name,
916
+ guid)
917
+ core_props += f"## In Data Specifications\n\n`{data_spec_name_list}`\n\n"
918
+
919
+ logger.info(f"Created Element `{display_name}` with GUID {guid}\n\n___")
920
+
921
+ return core_props
922
+ else:
923
+ logger.error(f"Failed to create Data Structure `{display_name}`\n\n___")
924
+ return None
925
+
926
+
927
+ except Exception as e:
928
+ logger.error(f"Error performing {object_action}: {e}\n\n___")
929
+ return None
930
+ else:
931
+ return None
932
+
933
+
934
+ @logger.catch
935
+ def process_data_field_upsert_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
936
+ """
937
+ Processes a data field create or update object_action by extracting key attributes such as
938
+ spec name, parent_guid, parent_relationship_type, parent_at_end_1, collection_type
939
+
940
+ :param txt: A string representing the input cell to be processed for
941
+ extracting glossary-related attributes.
942
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
943
+ :return: A string summarizing the outcome of the processing.
944
+ """
945
+ from md_processing.md_processing_utils.common_md_utils import set_debug_level
946
+
947
+ set_debug_level(directive)
948
+
949
+ command, object_type, object_action = extract_command_plus(txt)
950
+
951
+ parsed_output = parse_upsert_command(egeria_client, object_type, object_action, txt, directive)
952
+ attributes = parsed_output['attributes']
953
+ display_name = attributes['Display Name'].get('value', None)
954
+ qualified_name = parsed_output.get('qualified_name', None)
955
+ guid = parsed_output.get('guid', None)
956
+ valid = parsed_output['valid']
957
+ exists = parsed_output['exists']
958
+
959
+ print(Markdown(parsed_output['display']))
960
+
961
+ if directive == "display":
962
+
963
+ return None
964
+ elif directive == "validate":
965
+ if valid:
966
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
967
+ else:
968
+ msg = f"Validation failed for object_action `{command}`\n"
969
+ logger.error(msg)
970
+ return valid
971
+
972
+ elif directive == "process":
973
+ logger.debug(json.dumps(parsed_output, indent=4))
974
+
975
+ external_source_guid = attributes.get('External Source Name', {}).get('guid', None)
976
+ external_source_name = attributes.get('External Source Name', {}).get('value', None)
977
+ effective_time = attributes.get('Effective Time', {}).get('value', None)
978
+ for_lineage = attributes.get('For Lineage', {}).get('value', None)
979
+ for_duplicate_processing = attributes.get('For Duplicate Processing', {}).get('value', None)
980
+ anchor_guid = attributes.get('Anchor ID', {}).get('guid', None)
981
+ is_own_anchor = attributes.get('Is Own Anchor', {}).get('value', None)
982
+ # parent_id = attributes.get('Parent ID', {}).get('value', None)
983
+ # parent_guid = attributes['Parent ID'].get('guid', None)
984
+ # parent_relationship_type_name = attributes.get('Parent Relationship Type Name', {}).get('value', None)
985
+ # parent_relationship_properties = attributes.get('Parent Relationship Properties',{}).get('value', None)
986
+ # parent_at_end1 = attributes.get('Parent at End1', {}).get('value', None)
987
+
988
+ namespace = attributes.get('Namespace', {}).get('value', None)
989
+ description = attributes.get('Description', {}).get('value', None)
990
+ version_id = attributes.get('Version Identifier', {}).get('value', None)
991
+ aliases = attributes.get('Aliases', {}).get('value', None)
992
+ name_patterns = attributes.get('Name Patterns', {}).get('value', None)
993
+ is_nullable = attributes.get('Is Nullable', {}).get('value', None)
994
+ default_value = attributes.get('Default Value', {}).get('value', None)
995
+ data_type = attributes.get('Data Type', {}).get('value', None)
996
+ min_length = attributes.get('Minimum Length', {}).get('value', None)
997
+ length = attributes.get('Length', {}).get('value', None)
998
+ precision = attributes.get('Precision', {}).get('value', None)
999
+ ordered_values = attributes.get('Ordered Values', {}).get('value', None)
1000
+ sort_order = attributes.get('Sort Order', {}).get('value', None)
1001
+ additional_properties = attributes.get('Additional Properties', {}).get('value', None)
1002
+ effective_from = attributes.get('Effective From', {}).get('value', None)
1003
+ effective_to = attributes.get('Effective To', {}).get('value', None)
1004
+
1005
+ glossary_term = attributes['Glossary Term'].get('value', None)
1006
+ glossary_term_guid = attributes['Glossary Term'].get('guid', None)
1007
+
1008
+ merge_update = attributes.get('Merge Update', {}).get('value', None)
1009
+
1010
+ position = attributes.get('Position', {}).get('value', None)
1011
+ min_cardinality = attributes.get('Minimum Cardinality', {}).get('value', None)
1012
+ max_cardinality = attributes.get('Maximum Cardinality', {}).get('value', None)
1013
+
1014
+ in_data_structure = attributes.get('In Data Structure', {}).get('value', None)
1015
+ data_structure_guid_list = attributes.get('In Data Structure', {}).get('guid_list', None)
1016
+ in_data_structure_names = attributes.get('In Data Structure Names', {}).get('name_list', None)
1017
+
1018
+ data_class = attributes.get('Data Class', {}).get('value', None)
1019
+ data_class_guid = attributes.get('Data Class', {}).get('guid', None)
1020
+
1021
+ glossary_term_guid = attributes.get('Glossary Term', {}).get('guid', None)
1022
+ if glossary_term_guid:
1023
+ glossary_term_guid = [glossary_term_guid]
1024
+
1025
+ glossary_term_guid = attributes.get('Glossary Term', {}).get('guid', None)
1026
+
1027
+ # name_details_list = attributes.get("dict_list", None)
1028
+
1029
+ in_data_spec = attributes.get("In Data Specification", {}).get("value", None) # this is a [dict]
1030
+ data_spec_name_list = attributes.get("In Data Specification", {}).get("name_list", None)
1031
+ data_spec_guid_list = attributes.get("In Data Specification", {}).get("guid_list", None)
1032
+
1033
+ in_data_dictionary = attributes.get('In Data Dictionary', {}).get('value', None)
1034
+ in_data_dictionary_names = attributes.get('In Data Dictionary', {}).get('name_list', None)
1035
+ data_dict_guid_list = attributes.get("In Data Dictionary", {}).get("guid_list", None)
1036
+
1037
+ parent_data_field = attributes.get('Parent Data Field', {}).get('value', None)
1038
+ parent_data_field_guids = attributes.get('Parent Data Field', {}).get('guid_list', None)
1039
+ parent_data_field_names = attributes.get('Parent Data Field', {}).get('name_list', None)
1040
+
1041
+ anchor_scope_guid = attributes.get('Anchor Scope GUID', {}).get('value', None)
1042
+
1043
+ replace_all_props = not merge_update
1044
+
1045
+ if not valid:
1046
+ if exists and object_action == "Create":
1047
+ msg = (f"Create failed because Element `{display_name}` exists - changing `Create` to `Update` in "
1048
+ f"processed output\n\n___")
1049
+ logger.error(msg)
1050
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
1051
+ else:
1052
+ msg = f"Invalid specification - please review\n\n___"
1053
+ logger.error(msg)
1054
+ return None
1055
+ else:
1056
+ print(Markdown(f"==> Validation of {command} completed successfully! Proceeding to apply the changes.\n"))
1057
+
1058
+ try:
1059
+ if object_action == "Update":
1060
+ if not exists:
1061
+ logger.error(f"Element `{display_name}` does not exist! Updating result document with Create "
1062
+ f"object_action\n\n___")
1063
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
1064
+
1065
+ # first update the base data field
1066
+ body = {
1067
+ "class": "UpdateElementRequestBody", "externalSourceGUID": external_source_guid,
1068
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
1069
+ "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing, "properties": {
1070
+ "class": "DataFieldProperties", "qualifiedName": qualified_name, "displayName": display_name,
1071
+ "namespace": namespace, "description": description, "versionIdentifier": version_id,
1072
+ "aliases": aliases, "namePatterns": name_patterns, "isDeprecated": False,
1073
+ "isNullable": is_nullable, "defaultValue": default_value, "dataType": data_type,
1074
+ "minimumLength": min_length, "length": length, "precision": precision,
1075
+ "orderedValues": ordered_values, "sortOrder": sort_order,
1076
+ "additionalProperties": additional_properties, "effectiveFrom": effective_from,
1077
+ "effectiveTo": effective_to
1078
+ }
1079
+ }
1080
+
1081
+ egeria_client.update_data_field(guid, body, not merge_update)
1082
+ logger.success(f"Updated {object_type} `{display_name}` with GUID {guid}")
1083
+ # Update data dictionary membership
1084
+ update_element_dictionary(qualified_name, {
1085
+ 'guid': guid, 'display_name': display_name
1086
+ })
1087
+ core_props = egeria_client.find_data_fields(qualified_name,
1088
+ output_format='MD') ## update back to by_guid?
1089
+
1090
+ # existing_data_field = egeria_client.get_data_field_by_guid(guid, output_format='JSON')
1091
+
1092
+ # Sync membership in data dictionaries
1093
+ update_data_collection_memberships(egeria_client, object_type, data_dict_guid_list, "DataDictionary",
1094
+ guid, display_name, replace_all_props)
1095
+ logger.success(f"Updating data dictionaries `{in_data_dictionary_names}`")
1096
+ core_props += f"\n\n## In Data Dictionary\n\n{in_data_dictionary_names}\n\n"
1097
+
1098
+ # Sync data field related elements (data structure, parent data fields, terms, data classes
1099
+ sync_data_field_rel_elements(egeria_client, data_structure_guid_list, parent_data_field_guids,
1100
+ glossary_term_guid, data_class_guid, guid, display_name, replace_all_props)
1101
+ core_props += f"\n\n## In Data Structure {in_data_structure_names}\n\n"
1102
+ core_props += f"\n\n## Glossary Term \n\n{glossary_term}\n\n"
1103
+ core_props += f"\n\n## Parent Data Field\n\n{parent_data_field_names}\n\n"
1104
+ core_props += f"\n\n## Data Class\n\n{data_class}\n\n"
1105
+ core_props += "\n_______________________________________________________________________________\n\n"
1106
+
1107
+ # Update data classes
1108
+ logger.success(f"Updated Element `{display_name}`\n\n___")
1109
+ return core_props
1110
+
1111
+ elif object_action == "Create":
1112
+ if valid is False and exists:
1113
+ logger.error(
1114
+ f"\nData Field `{display_name}` already exists and result document updated changing `Create` "
1115
+ f"to `Update` in processed output\n\n___")
1116
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
1117
+ else:
1118
+ # First lets create the data field
1119
+ body = {
1120
+ "class": "NewElementRequestBody", "properties": {
1121
+ "class": "DataFieldProperties", "qualifiedName": qualified_name,
1122
+ "displayName": display_name, "namespace": namespace, "description": description,
1123
+ "versionIdentifier": version_id, "aliases": aliases, "namePatterns": name_patterns,
1124
+ "isDeprecated": False, "isNullable": is_nullable, "defaultValue": default_value,
1125
+ "dataType": data_type, "minimumLength": min_length, "length": length,
1126
+ "precision": precision, "orderedValues": ordered_values, "sortOrder": sort_order,
1127
+ "additionalProperties": additional_properties
1128
+ }
1129
+ }
1130
+ guid = egeria_client.create_data_field(body)
1131
+ if guid:
1132
+ # Now update our element dictionary with the new information
1133
+ update_element_dictionary(qualified_name, {
1134
+ 'guid': guid, 'display_name': display_name
1135
+ })
1136
+ # Start assembling the information we will present back out
1137
+ core_props = egeria_client.get_data_field_by_guid(guid, None, 'MD')
1138
+
1139
+ # Add the field to any data dictionaries
1140
+ if in_data_dictionary:
1141
+ logger.info(f"Will add to data dictionary `{in_data_dictionary}`")
1142
+ add_member_to_collections(egeria_client, data_dict_guid_list, display_name, guid)
1143
+ core_props += f"\n\n## In Data Dictionary\n\n{in_data_dictionary_names}\n\n"
1144
+
1145
+ # Add the field to any data structures
1146
+ if in_data_structure:
1147
+ core_props += f"\n\n## In Data Structure\n\n{in_data_structure_names}\n\n"
1148
+ for ds_guid in data_structure_guid_list:
1149
+ # todo This is too naive? - need to better accommodate the relationship
1150
+ df_body = {
1151
+ "class": "RelationshipRequestBody", "properties": {
1152
+ "class": "MemberDataFieldProperties", "dataFieldPosition": position,
1153
+ "minCardinality": min_cardinality, "maxCardinality": max_cardinality,
1154
+ }
1155
+ }
1156
+
1157
+ msg = f"Adding field to structure {ds_guid}"
1158
+ logger.info(msg)
1159
+ egeria_client.link_member_data_field(ds_guid, guid, df_body)
1160
+ core_props += f"\n\n## In Data Structure {in_data_structure_names}\n\n"
1161
+
1162
+ if glossary_term:
1163
+ if glossary_term_guid:
1164
+ glossary_body = {
1165
+ "class": "RelationshipRequestBody", "externalSourceGUID": external_source_guid,
1166
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
1167
+ "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing
1168
+ }
1169
+ core_props += f"\n\n## Glossary Term \n\n{glossary_term}\n\n"
1170
+ egeria_client.link_semantic_definition(guid, glossary_term_guid, glossary_body)
1171
+
1172
+ if parent_data_field_guids:
1173
+ # parent_df_body = {
1174
+ # "class": "MetadataSourceRequestBody", "externalSourceGUID": external_source_guid,
1175
+ # "externalSourceName": external_source_name, "effectiveTime": effective_time,
1176
+ # "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing
1177
+ # }
1178
+
1179
+ # egeria_client.link_nested_data_field(parent_data_field_guid, guid, parent_df_body)
1180
+ for parent_guid in parent_data_field_guids:
1181
+ egeria_client.link_nested_data_field(parent_guid, guid)
1182
+ core_props += f"\n\n## Parent Data Field\n\n{parent_data_field_names}\n\n"
1183
+
1184
+ # Link data class
1185
+ if data_class:
1186
+ body = {
1187
+ "class": "RelationshipRequestBody", "externalSourceGUID": external_source_guid,
1188
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
1189
+ "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing
1190
+ }
1191
+ egeria_client.link_data_class_definition(guid, data_class_guid, body)
1192
+ msg = f"Adding data class `{data_class}` to data field {display_name}"
1193
+ logger.info(msg)
1194
+
1195
+ logger.success(f"Created Element `{display_name}` with guid `{guid}`")
1196
+ logger.success("=====================================================\n\n")
1197
+ core_props += "\n___\n\n"
1198
+ return core_props
1199
+
1200
+ else:
1201
+ logger.error(f"Failed to create Term `{display_name}`\n\n___")
1202
+ return None
1203
+
1204
+ except Exception as e:
1205
+ logger.error(f"Error performing {command}: {e}\n\n___")
1206
+ return None
1207
+ else:
1208
+ return None
1209
+
1210
+
1211
+ @logger.catch
1212
+ def process_data_class_upsert_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
1213
+ """
1214
+ Processes a data class create or update object_action by extracting key attributes such as
1215
+ spec name, parent_guid, parent_relationship_type, parent_at_end_1, collection_type
1216
+
1217
+ :param txt: A string representing the input cell to be processed for
1218
+ extracting glossary-related attributes.
1219
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
1220
+ :return: A string summarizing the outcome of the processing.
1221
+ """
1222
+
1223
+ command, object_type, object_action = extract_command_plus(txt)
1224
+
1225
+ parsed_output = parse_upsert_command(egeria_client, object_type, object_action, txt, directive)
1226
+
1227
+ attributes = parsed_output['attributes']
1228
+ display_name = attributes['Display Name'].get('value', None)
1229
+ qualified_name = parsed_output.get('qualified_name', None)
1230
+ guid = parsed_output.get('guid', None)
1231
+ valid = parsed_output['valid']
1232
+ exists = parsed_output['exists']
1233
+
1234
+ print(Markdown(parsed_output['display']))
1235
+
1236
+ if directive == "display":
1237
+
1238
+ return None
1239
+ elif directive == "validate":
1240
+ if valid:
1241
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
1242
+ else:
1243
+ msg = f"Validation failed for object_action `{command}`\n"
1244
+ logger.error(msg)
1245
+ return valid
1246
+
1247
+ elif directive == "process":
1248
+ logger.debug(json.dumps(parsed_output, indent=4))
1249
+
1250
+ external_source_guid = attributes.get('External Source Name', {}).get('guid', None)
1251
+ external_source_name = attributes.get('External Source Name', {}).get('value', None)
1252
+ effective_time = attributes.get('Effective Time', {}).get('value', None)
1253
+ for_lineage = attributes.get('For Lineage', {}).get('value', False)
1254
+ for_duplicate_processing = attributes.get('For Duplicate Processing', {}).get('value', False)
1255
+ anchor_guid = attributes.get('Anchor ID', {}).get('guid', None)
1256
+ is_own_anchor = attributes.get('Is Own Anchor', {}).get('value', None)
1257
+ # parent_id = attributes.get('Parent ID', {}).get('value', None)
1258
+ # parent_guid = attributes['Parent ID'].get('guid', None)
1259
+ # parent_relationship_type_name = attributes.get('Parent Relationship Type Name', {}).get('value', None)
1260
+ # parent_relationship_properties = attributes.get('Parent Relationship Properties',{}).get('value', None)
1261
+ # parent_at_end1 = attributes.get('Parent at End1', {}).get('value', None)
1262
+
1263
+ namespace = attributes.get('Namespace', {}).get('value', None)
1264
+ description = attributes.get('Description', {}).get('value', None)
1265
+ version_id = attributes.get('Version Identifier', {}).get('value', None)
1266
+
1267
+ ###############
1268
+ match_property_names = attributes.get('Match Property Names', {}).get('value', [])
1269
+ specification_details = attributes.get('Specification Details', {}).get('value', {})
1270
+ match_threshold = attributes.get('Match Threshold', {}).get('value', 0)
1271
+ specification = attributes.get('Specification', {}).get('value', None)
1272
+ data_type = attributes.get('Data Type', {}).get('value', None)
1273
+ is_nullable = attributes.get('Is Nullable', {}).get('value', True)
1274
+ allow_duplicates = attributes.get('Allow Duplicates', {}).get('value', True)
1275
+ default_value = attributes.get('Default Value', {}).get('value', None)
1276
+ average_value = attributes.get('Average Value', {}).get('value', None)
1277
+ value_list = attributes.get('Value List', {}).get('value', None)
1278
+ value_range_from = attributes.get('Value Range From', {}).get('value', None)
1279
+ value_range_to = attributes.get('Value Range To', {}).get('value', None)
1280
+ sample_values = attributes.get('Sample Values', {}).get('value', [])
1281
+ data_patterns = attributes.get('Data Patterns', {}).get('value', [])
1282
+ additional_properties = attributes.get('Additional Properties', {}).get('value', {})
1283
+
1284
+ ###############
1285
+ aliases = attributes.get('Aliases', {}).get('value', None)
1286
+ name_patterns = attributes.get('Name Patterns', {}).get('value', None)
1287
+
1288
+ min_length = attributes.get('Minimum Length', {}).get('value', None)
1289
+ length = attributes.get('Length', {}).get('value', None)
1290
+ precision = attributes.get('Precision', {}).get('value', None)
1291
+ ordered_values = attributes.get('Ordered Values', {}).get('value', None)
1292
+ sort_order = attributes.get('Sort Order', {}).get('value', None)
1293
+ effective_from = attributes.get('Effective From', {}).get('value', None)
1294
+ effective_to = attributes.get('Effective To', {}).get('value', None)
1295
+
1296
+ glossary_term = attributes.get('Glossary Term', {}).get('value', None)
1297
+ glossary_term_guid = attributes.get('Glossary Term', {}).get('guid', None)
1298
+
1299
+ merge_update = attributes.get('Merge Update', {}).get('value', True)
1300
+
1301
+ position = attributes.get('Position', {}).get('value', None)
1302
+ min_cardinality = attributes.get('Minimum Cardinality', {}).get('value', None)
1303
+ max_cardinality = attributes.get('Maximum Cardinality', {}).get('value', None)
1304
+
1305
+ in_data_structure = attributes.get('In Data Structure', {}).get('value', None)
1306
+ data_structure_guid_list = attributes.get('In Data Structure', {}).get('guid_list', None)
1307
+ in_data_structure_names = attributes.get('In Data Structure Names', {}).get('name_list', None)
1308
+
1309
+ data_class = attributes.get('Data Class', {}).get('value', None)
1310
+ glossary_term = attributes.get('Glossary Term', {}).get('value', None)
1311
+
1312
+ glossary_term_guid = attributes.get('Glossary Term', {}).get('guid', None)
1313
+
1314
+ in_data_dictionary = attributes.get('In Data Dictionary', {}).get('value', None)
1315
+ in_data_dictionary_names = attributes.get('In Data Dictionary', {}).get('name_list', None)
1316
+ data_dict_guid_list = attributes.get("In Data Dictionary", {}).get("guid_list", None)
1317
+
1318
+ containing_data_class = attributes.get('Containing Data Class', {}).get('value', None)
1319
+ containing_data_class_guids = attributes.get('Containing Data Class', {}).get('guid_list', None)
1320
+ containing_data_class_names = attributes.get('Containing Data Class', {}).get('name_list', None)
1321
+
1322
+ specializes_data_class = attributes.get('Specializes Data Class', {}).get('value', None)
1323
+ specializes_data_class_guid = attributes.get('Specializes Data Class', {}).get('guid', None)
1324
+ specializes_data_class_name = attributes.get('Specializes Data Class', {}).get('name', None)
1325
+
1326
+ anchor_scope_guid = attributes.get('Anchor Scope GUID', {}).get('value', None)
1327
+
1328
+ replace_all_props = not merge_update
1329
+
1330
+ if not valid:
1331
+ if exists and object_action == "Create":
1332
+ msg = (f"Create failed because Element `{display_name}` exists - changing `Create` to `Update` in "
1333
+ f"processed output\n\n___")
1334
+ logger.error(msg)
1335
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
1336
+ else:
1337
+ msg = f"Invalid specification - please review\n\n___"
1338
+ return None
1339
+ else:
1340
+ print(Markdown(f"==> Validation of {command} completed successfully! Proceeding to apply the changes.\n"))
1341
+
1342
+ try:
1343
+ if object_action == "Update":
1344
+ if not exists:
1345
+ logger.error(f"Element `{display_name}` does not exist! Updating result document with Create "
1346
+ f"object_action\n\n___")
1347
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
1348
+
1349
+ # first update the base data class
1350
+ body = {
1351
+ "class": "UpdateElementRequestBody", "externalSourceGUID": external_source_guid,
1352
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
1353
+ "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing, "properties": {
1354
+ "class": "DataClassProperties", "qualifiedName": qualified_name, "displayName": display_name,
1355
+ "description": description, "namespace": namespace, "matchPropertyNames": match_property_names,
1356
+ "matchThreshold": match_threshold, "specification": specification,
1357
+ "specificationDetails": specification_details, "dataType": data_type,
1358
+ "allowsDuplicateValues": allow_duplicates, "isNullable": is_nullable,
1359
+ "defaultValue": default_value, "averageValue": average_value, "valueList": value_list,
1360
+ "valueRangeFrom": value_range_from, "valueRangeTo": value_range_to,
1361
+ "sampleValues": sample_values, "dataPatterns": data_patterns,
1362
+ "additionalProperties": additional_properties
1363
+ }
1364
+ }
1365
+
1366
+ egeria_client.update_data_class(guid, body, not merge_update)
1367
+ logger.success(f"Updated {object_type} `{display_name}` with GUID {guid}")
1368
+ # Update data dictionary membership
1369
+ update_element_dictionary(qualified_name, {
1370
+ 'guid': guid, 'display_name': display_name
1371
+ })
1372
+ core_props = egeria_client.get_data_class_by_guid(guid, None, 'MD')
1373
+
1374
+ # Sync membership in data dictionaries
1375
+ update_data_collection_memberships(egeria_client, object_type, data_dict_guid_list, "DataDictionary",
1376
+ guid, display_name, replace_all_props)
1377
+ logger.success(f"Updating data dictionaries `{in_data_dictionary_names}`")
1378
+ core_props += f"\n\n## In Data Dictionary\n\n{in_data_dictionary_names}\n\n"
1379
+
1380
+ # Sync data field related elements (data structure, parent data fields, terms, data classes
1381
+ sync_data_class_rel_elements(egeria_client, containing_data_class_guids, glossary_term_guid,
1382
+ specializes_data_class_guid, guid, display_name, replace_all_props)
1383
+
1384
+ core_props += f"\n\n## Glossary Term \n\n{glossary_term}\n\n"
1385
+ core_props += f"\n\n## Containing Data Class\n\n{containing_data_class_names}\n\n"
1386
+ core_props += "\n___\n\n"
1387
+
1388
+ # Update data classes
1389
+ logger.success(f"Updated Element `{display_name}`\n\n___")
1390
+ return core_props
1391
+
1392
+ elif object_action == "Create":
1393
+ if valid is False and exists:
1394
+ logger.error(
1395
+ f"\nData Class `{display_name}` already exists and result document updated changing `Create` "
1396
+ f"to `Update` in processed output\n\n___")
1397
+ return update_a_command(txt, object_action, object_type, qualified_name, guid)
1398
+ else:
1399
+ # First lets create the data class
1400
+ body = {
1401
+ "class": "NewElementRequestBody", "properties": {
1402
+ "class": "DataClassProperties", "qualifiedName": qualified_name,
1403
+ "displayName": display_name, "description": description, "namespace": namespace,
1404
+ "matchPropertyNames": match_property_names, "matchThreshold": match_threshold,
1405
+ "specification": specification, "specificationDetails": specification_details,
1406
+ "dataType": data_type, "allowsDuplicateValues": allow_duplicates, "isNullable": is_nullable,
1407
+ "defaultValue": default_value, "averageValue": average_value, "valueList": value_list,
1408
+ "valueRangeFrom": value_range_from, "valueRangeTo": value_range_to,
1409
+ "sampleValues": sample_values, "dataPatterns": data_patterns,
1410
+ "additionalProperties": additional_properties
1411
+ }
1412
+ }
1413
+ guid = egeria_client.create_data_class(body)
1414
+ if guid:
1415
+ # Now update our element dictionary with the new information
1416
+ update_element_dictionary(qualified_name, {
1417
+ 'guid': guid, 'display_name': display_name
1418
+ })
1419
+ # Start assembling the information we will present back out
1420
+ core_props = egeria_client.get_data_class_by_guid(guid, None, 'MD')
1421
+
1422
+ # Add the field to any data dictionaries
1423
+ if in_data_dictionary:
1424
+ logger.info(f"Will add to data dictionary `{in_data_dictionary}`")
1425
+ add_member_to_collections(egeria_client, data_dict_guid_list, display_name, guid)
1426
+ core_props += f"\n\n## In Data Dictionary\n\n{in_data_dictionary_names}\n\n"
1427
+
1428
+ if glossary_term:
1429
+ if glossary_term_guid:
1430
+ glossary_body = {
1431
+ "class": "RelationshipRequestBody", "externalSourceGUID": external_source_guid,
1432
+ "externalSourceName": external_source_name, "effectiveTime": effective_time,
1433
+ "forLineage": for_lineage, "forDuplicateProcessing": for_duplicate_processing
1434
+ }
1435
+
1436
+ core_props += f"\n\n## Glossary Term \n\n{glossary_term}\n\n"
1437
+ egeria_client.link_semantic_definition(guid, glossary_term_guid, glossary_body)
1438
+
1439
+ if containing_data_class_guids:
1440
+ for dc_guid in containing_data_class_guids:
1441
+ egeria_client.link_nested_data_class(dc_guid, guid)
1442
+ core_props += f"\n\n## Parent Data Field\n\n{containing_data_class_names}\n\n"
1443
+
1444
+ if specializes_data_class_guid:
1445
+ egeria_client.link_specialist_data_class(specializes_data_class_guid, guid)
1446
+ core_props += f"\n\n## Specialized Data Field\n\n{specializes_data_class_name}\n\n"
1447
+
1448
+ logger.success(f"Created Element `{display_name}`")
1449
+ core_props += "\n___\n\n"
1450
+ return core_props
1451
+
1452
+ else:
1453
+ logger.error(f"Failed to create Term `{display_name}`\n\n___")
1454
+ return None
1455
+
1456
+ except Exception as e:
1457
+ logger.error(f"Error performing {command}: {e}\n\n___")
1458
+ return None
1459
+ else:
1460
+ return None
1461
+
1462
+
1463
+ @logger.catch
1464
+ def process_data_collection_list_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[
1465
+ str]:
1466
+ """
1467
+ Processes a Data Dictionary list object_action by extracting key attributes such as
1468
+ search string from the given text.
1469
+
1470
+ :param txt: A string representing the input cell to be processed for
1471
+ extracting term-related attributes.
1472
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
1473
+ :return: A string summarizing the outcome of the processing.
1474
+ """
1475
+ command, object_type, object_action = extract_command_plus(txt)
1476
+ if object_type in ["Data Dictionary", "Data Dictionaries", "DataDict", "DataDictionary"]:
1477
+ col_type = "DataDictionary"
1478
+ elif object_type in ["Data Specification", "Data Specifications", "Data Specs"]:
1479
+ col_type = "DataSpec"
1480
+ else:
1481
+ col_type = "Collection"
1482
+
1483
+ parsed_output = parse_view_command(egeria_client, object_type, object_action, txt, directive)
1484
+
1485
+
1486
+
1487
+ valid = parsed_output['valid']
1488
+ print(Markdown(f"Performing {command}"))
1489
+ print(Markdown(parsed_output['display']))
1490
+
1491
+ attr = parsed_output.get('attributes',{})
1492
+ effective_time = attr.get('effectiveTime', {}).get('value', None)
1493
+ as_of_time = attr.get('asOfTime', {}).get('value', None)
1494
+ for_duplicate_processing = attr.get('forDuplicateProcessing', {}).get('value', False)
1495
+ for_lineage = attr.get('forLineage',{}).get('value', False)
1496
+ limit_result_by_status = attr.get('limitResultsByStatus',{}).get('value', ['ACTIVE'])
1497
+ sequencing_property = attr.get('sequencingProperty',{}).get('value',"qualifiedName" )
1498
+ sequencing_order = attr.get('sequencingOrder',{}).get('value', "PROPERTY_ASCENDING")
1499
+ search_string = attr.get('Search String', {}).get('value', '*')
1500
+ output_format = attr.get('Output Format', {}).get('value', 'LIST')
1501
+ detailed = attr.get('Detailed', {}).get('value', False)
1502
+
1503
+ if directive == "display":
1504
+ return None
1505
+ elif directive == "validate":
1506
+ if valid:
1507
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
1508
+ else:
1509
+ msg = f"Validation failed for object_action `{command}`\n"
1510
+ logger.error(msg)
1511
+ return valid
1512
+
1513
+ elif directive == "process":
1514
+ try:
1515
+ if not valid: # First validate the command before we process it
1516
+ msg = f"Validation failed for {object_action} `{object_type}`\n"
1517
+ logger.error(msg)
1518
+ return None
1519
+
1520
+ list_md = f"\n# `{col_type}` with filter: `{search_string}`\n\n"
1521
+ body = {
1522
+ "class": "FilterRequestBody",
1523
+ "asOfTime": as_of_time,
1524
+ "effectiveTime": effective_time,
1525
+ "forLineage": for_lineage,
1526
+ "forDuplicateProcessing": for_duplicate_processing,
1527
+ "limitResultsByStatus": limit_result_by_status,
1528
+ "sequencingOrder": sequencing_order,
1529
+ "sequencingProperty": sequencing_property,
1530
+ "filter": search_string,
1531
+ }
1532
+
1533
+ struct = egeria_client.find_collections_w_body(body, col_type, output_format=output_format)
1534
+ if output_format == "DICT":
1535
+ list_md += f"```\n{json.dumps(struct, indent=4)}\n```\n"
1536
+ else:
1537
+ list_md += struct
1538
+ logger.info(f"Wrote `{col_type}` for search string: `{search_string}`")
1539
+
1540
+ return list_md
1541
+
1542
+ except Exception as e:
1543
+ logger.error(f"Error performing {command}: {e}")
1544
+ console.print_exception(show_locals=True)
1545
+ return None
1546
+ else:
1547
+ return None
1548
+
1549
+
1550
+ def process_data_structure_list_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[
1551
+ str]:
1552
+ """
1553
+ Processes a Data Dictionary list object_action by extracting key attributes such as
1554
+ search string from the given text.
1555
+
1556
+ :param txt: A string representing the input cell to be processed for
1557
+ extracting term-related attributes.
1558
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
1559
+ :return: A string summarizing the outcome of the processing.
1560
+ """
1561
+ command, object_type, object_action = extract_command_plus(txt)
1562
+
1563
+ parsed_output = parse_view_command(egeria_client, object_type, object_action, txt, directive)
1564
+
1565
+ attributes = parsed_output['attributes']
1566
+
1567
+ valid = parsed_output['valid']
1568
+ print(Markdown(f"Performing {command}"))
1569
+ print(Markdown(parsed_output['display']))
1570
+
1571
+ if directive == "display":
1572
+ return None
1573
+ elif directive == "validate":
1574
+ if valid:
1575
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
1576
+ else:
1577
+ msg = f"Validation failed for object_action `{command}`\n"
1578
+ logger.error(msg)
1579
+ return valid
1580
+
1581
+ elif directive == "process":
1582
+ attributes = parsed_output['attributes']
1583
+ search_string = attributes.get('Search String', {}).get('value', '*')
1584
+ output_format = attributes.get('Output Format', {}).get('value', 'LIST')
1585
+ detailed = attributes.get('Detailed', {}).get('value', False)
1586
+
1587
+ try:
1588
+ if not valid: # First validate the command before we process it
1589
+ msg = f"Validation failed for {object_action} `{object_type}`\n"
1590
+ logger.error(msg)
1591
+ return None
1592
+
1593
+ list_md = f"\n# `{object_type}` with filter: `{search_string}`\n\n"
1594
+ struct = egeria_client.find_data_structures(search_string, output_format=output_format)
1595
+
1596
+ if output_format == "DICT":
1597
+ list_md += f"```\n{json.dumps(struct, indent=4)}\n```\n"
1598
+ else:
1599
+ list_md += struct
1600
+ logger.info(f"Wrote `{object_type}` for search string: `{search_string}`")
1601
+
1602
+ return list_md
1603
+
1604
+ except Exception as e:
1605
+ logger.error(f"Error performing {command}: {e}")
1606
+ console.print_exception(show_locals=True)
1607
+ return None
1608
+ else:
1609
+ return None
1610
+
1611
+
1612
+ def process_data_field_list_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
1613
+ """
1614
+ Processes a Data Dictionary list object_action by extracting key attributes such as
1615
+ search string from the given text.
1616
+
1617
+ :param txt: A string representing the input cell to be processed for
1618
+ extracting term-related attributes.
1619
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
1620
+ :return: A string summarizing the outcome of the processing.
1621
+ """
1622
+ command, object_type, object_action = extract_command_plus(txt)
1623
+
1624
+ parsed_output = parse_view_command(egeria_client, object_type, object_action, txt, directive)
1625
+
1626
+ attributes = parsed_output['attributes']
1627
+
1628
+ valid = parsed_output['valid']
1629
+ print(Markdown(f"Performing {command}"))
1630
+ print(Markdown(parsed_output['display']))
1631
+
1632
+ if directive == "display":
1633
+ return None
1634
+ elif directive == "validate":
1635
+ if valid:
1636
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
1637
+ else:
1638
+ msg = f"Validation failed for object_action `{command}`\n"
1639
+ logger.error(msg)
1640
+ return valid
1641
+
1642
+ elif directive == "process":
1643
+ attributes = parsed_output['attributes']
1644
+ search_string = attributes.get('Search String', {}).get('value', '*')
1645
+ output_format = attributes.get('Output Format', {}).get('value', 'LIST')
1646
+ detailed = attributes.get('Detailed', {}).get('value', False)
1647
+ as_of_time = attributes.get('AsOfTime', {}).get('value', None)
1648
+ effective_time = attributes.get('Effective Time', {}).get('value', None)
1649
+ sort_order = attributes.get('Sort Order', {}).get('value', None)
1650
+ order_property = attributes.get('Order Property', {}).get('value', None)
1651
+ starts_with = attributes.get('Start With', {}).get('value', True)
1652
+ ends_with = attributes.get('End With', {}).get('value', False)
1653
+ ignore_case = attributes.get('Ignore Case', {}).get('value', False)
1654
+ start_from = attributes.get('Start From', {}).get('value', 0)
1655
+ page_size = attributes.get('Page Size', {}).get('value', None)
1656
+
1657
+ try:
1658
+ if not valid: # First validate the command before we process it
1659
+ msg = f"Validation failed for {object_action} `{object_type}`\n"
1660
+ logger.error(msg)
1661
+ return None
1662
+
1663
+ list_md = f"\n# `{object_type}` with filter: `{search_string}`\n\n"
1664
+ body = {
1665
+ "class": "FilterRequestBody", "asOfTime": as_of_time, "effectiveTime": effective_time,
1666
+ "forLineage": False, "forDuplicateProcessing": False, "limitResultsByStatus": ["ACTIVE"],
1667
+ "sequencingOrder": sort_order, "sequencingProperty": order_property, "filter": search_string,
1668
+ }
1669
+ struct = egeria_client.find_data_fields_w_body(body, start_from, page_size, starts_with, ends_with,
1670
+ ignore_case, output_format)
1671
+
1672
+ if output_format == "DICT":
1673
+ list_md += f"```\n{json.dumps(struct, indent=4)}\n```\n"
1674
+ else:
1675
+ list_md += struct
1676
+ logger.info(f"Wrote `{object_type}` for search string: `{search_string}`")
1677
+
1678
+ return list_md
1679
+
1680
+ except Exception as e:
1681
+ logger.error(f"Error performing {command}: {e}")
1682
+ console.print_exception(show_locals=True)
1683
+ return None
1684
+ else:
1685
+ return None
1686
+
1687
+
1688
+ def process_data_class_list_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
1689
+ """
1690
+ Processes a Data Dictionary list object_action by extracting key attributes such as
1691
+ search string from the given text.
1692
+
1693
+ :param txt: A string representing the input cell to be processed for
1694
+ extracting term-related attributes.
1695
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
1696
+ :return: A string summarizing the outcome of the processing.
1697
+ """
1698
+ command, object_type, object_action = extract_command_plus(txt)
1699
+
1700
+ parsed_output = parse_view_command(egeria_client, object_type, object_action, txt, directive)
1701
+
1702
+ attributes = parsed_output['attributes']
1703
+
1704
+ valid = parsed_output['valid']
1705
+ print(Markdown(f"Performing {command}"))
1706
+ print(Markdown(parsed_output['display']))
1707
+
1708
+ if directive == "display":
1709
+ return None
1710
+ elif directive == "validate":
1711
+ if valid:
1712
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
1713
+ else:
1714
+ msg = f"Validation failed for object_action `{command}`\n"
1715
+ logger.error(msg)
1716
+ return valid
1717
+
1718
+ elif directive == "process":
1719
+ attributes = parsed_output['attributes']
1720
+ search_string = attributes.get('Search String', {}).get('value', '*')
1721
+ output_format = attributes.get('Output Format', {}).get('value', 'LIST')
1722
+ detailed = attributes.get('Detailed', {}).get('value', False)
1723
+
1724
+ try:
1725
+ if not valid: # First validate the command before we process it
1726
+ msg = f"Validation failed for {object_action} `{object_type}`\n"
1727
+ logger.error(msg)
1728
+ return None
1729
+
1730
+ list_md = f"\n# `{object_type}` with filter: `{search_string}`\n\n"
1731
+ struct = egeria_client.find_data_classes(search_string, output_format=output_format)
1732
+
1733
+ if output_format == "DICT":
1734
+ list_md += f"```\n{json.dumps(struct, indent=4)}\n```\n"
1735
+ else:
1736
+ list_md += struct
1737
+ logger.info(f"Wrote `{object_type}` for search string: `{search_string}`")
1738
+
1739
+ return list_md
1740
+
1741
+ except Exception as e:
1742
+ logger.error(f"Error performing {command}: {e}")
1743
+ console.print_exception(show_locals=True)
1744
+ return None
1745
+ else:
1746
+ return None