pyegeria 5.4.0.23__py3-none-any.whl → 5.4.0.25__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 (54) hide show
  1. commands/cat/debug_log +7967 -465
  2. commands/cat/debug_log.2025-08-15_09-14-07_444802.zip +0 -0
  3. commands/cat/debug_log.2025-08-16_10-21-59_388912.zip +0 -0
  4. commands/cat/debug_log.2025-08-17_11-34-27_981852.zip +0 -0
  5. commands/cat/dr_egeria_md.py +36 -6
  6. commands/cat/logs/pyegeria.log +3 -135
  7. md_processing/.DS_Store +0 -0
  8. md_processing/__init__.py +12 -6
  9. md_processing/data/commands.json +8523 -2234
  10. md_processing/dr_egeria_inbox/gov_def.md +76 -18
  11. md_processing/dr_egeria_inbox/img.png +0 -0
  12. md_processing/dr_egeria_inbox/product.md +185 -24
  13. md_processing/dr_egeria_outbox/.obsidian/workspace.json +5 -5
  14. md_processing/dr_egeria_outbox/monday/processed-2025-08-19 07:05-product.md +426 -0
  15. md_processing/dr_egeria_outbox/monday/processed-2025-08-19 07:56-product.md +212 -0
  16. md_processing/dr_egeria_outbox/monday/processed-2025-08-19 09:43-product.md +201 -0
  17. md_processing/dr_egeria_outbox/tuesday/processed-2025-08-19 10:55-product.md +209 -0
  18. md_processing/md_commands/governance_officer_commands.py +247 -178
  19. md_processing/md_commands/product_manager_commands.py +730 -580
  20. md_processing/md_processing_utils/common_md_proc_utils.py +170 -12
  21. md_processing/md_processing_utils/common_md_utils.py +126 -28
  22. md_processing/md_processing_utils/extraction_utils.py +2 -2
  23. md_processing/md_processing_utils/md_processing_constants.py +14 -10
  24. pyegeria/.DS_Store +0 -0
  25. pyegeria/__init__.py +1 -1
  26. pyegeria/_client_new.py +61 -12
  27. pyegeria/_exceptions_new.py +6 -0
  28. pyegeria/_output_formats.py +42 -2
  29. pyegeria/collection_manager.py +79 -14
  30. pyegeria/{data_designer_omvs.py → data_designer.py} +1171 -1675
  31. pyegeria/glossary_browser.py +1259 -0
  32. pyegeria/{glossary_manager_omvs.py → glossary_manager.py} +1181 -1099
  33. pyegeria/governance_officer.py +1 -3
  34. pyegeria/load_config.py +1 -1
  35. pyegeria/models.py +37 -4
  36. pyegeria/output_formatter.py +2 -1
  37. pyegeria/project_manager.py +1952 -0
  38. pyegeria/utils.py +5 -2
  39. {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.25.dist-info}/METADATA +1 -1
  40. {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.25.dist-info}/RECORD +43 -44
  41. md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:00-product.md +0 -62
  42. md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:13-product.md +0 -62
  43. md_processing/dr_egeria_outbox/friday/processed-2025-07-20 13:23-product.md +0 -47
  44. md_processing/dr_egeria_outbox/friday/processed-2025-08-01 11:55-data_test3.md +0 -503
  45. md_processing/dr_egeria_outbox/monday/processed-2025-07-14 12:38-data_designer_out.md +0 -663
  46. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 10:52-generated_help_report.md +0 -2744
  47. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 18:38-collections.md +0 -62
  48. md_processing/dr_egeria_outbox/monday/processed-2025-08-01 11:34-gov_def.md +0 -444
  49. md_processing/dr_egeria_outbox/processed-2025-08-03 16:05-glossary_list.md +0 -37
  50. pyegeria/glossary_browser_omvs.py +0 -3840
  51. pyegeria/governance_officer_omvs.py +0 -2367
  52. {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.25.dist-info}/LICENSE +0 -0
  53. {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.25.dist-info}/WHEEL +0 -0
  54. {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.25.dist-info}/entry_points.txt +0 -0
@@ -4,10 +4,11 @@ This file contains general utility functions for processing Egeria Markdown
4
4
  import json
5
5
  import os
6
6
  import sys
7
- from typing import List
7
+ from typing import List, Optional
8
8
 
9
9
  from loguru import logger
10
10
  from rich import print
11
+ from rich.markdown import Markdown
11
12
  from rich.console import Console
12
13
 
13
14
  from md_processing.md_processing_utils.common_md_utils import (get_current_datetime_string, get_element_dictionary,
@@ -15,16 +16,20 @@ from md_processing.md_processing_utils.common_md_utils import (get_current_datet
15
16
  split_tb_string, str_to_bool, )
16
17
  from md_processing.md_processing_utils.extraction_utils import (process_simple_attribute, extract_attribute,
17
18
  get_element_by_name)
19
+ from md_processing.md_processing_utils.common_md_utils import (update_element_dictionary, set_gov_prop_body, \
20
+ set_update_body, set_create_body,
21
+ set_peer_gov_def_request_body, set_rel_request_body,
22
+ set_delete_request_body,set_rel_request_body,
23
+ set_filter_request_body, setup_log,
24
+ ALL_GOVERNANCE_DEFINITIONS, set_find_body)
25
+ from md_processing.md_processing_utils.extraction_utils import (extract_command_plus, update_a_command)
18
26
  from md_processing.md_processing_utils.md_processing_constants import (get_command_spec)
19
27
  from md_processing.md_processing_utils.message_constants import (ERROR, INFO, WARNING, ALWAYS, EXISTS_REQUIRED)
20
- from pyegeria import EgeriaTech
28
+ from pyegeria import EgeriaTech, select_output_format_set
29
+
21
30
  from pyegeria._globals import DEBUG_LEVEL
22
31
 
23
- log_format = "P {time} | {level} | {function} | {line} | {message} | {extra}"
24
- logger.remove()
25
- logger.add(sys.stderr, level="INFO", format=log_format, colorize=True)
26
- logger.add("debug_log.log", rotation="1 day", retention="1 week", compression="zip", level="TRACE", format=log_format,
27
- colorize=True)
32
+
28
33
  # Constants
29
34
  EGERIA_WIDTH = int(os.environ.get("EGERIA_WIDTH", "200"))
30
35
  EGERIA_USAGE_LEVEL = os.environ.get("EGERIA_USAGE_LEVEL", "Basic")
@@ -124,8 +129,12 @@ def parse_upsert_command(egeria_client: EgeriaTech, object_type: str, object_act
124
129
  default_value = attr[key].get('default_value', None)
125
130
 
126
131
  style = attr[key]['style']
127
- if style in ['Simple', 'Dictionary', 'Comment']:
132
+ if style in ['Simple', 'Comment']:
128
133
  parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
134
+ elif style == 'Dictionary':
135
+ parsed_attributes[key] = proc_dictionary_attribute(txt, object_action, labels, if_missing, default_value)
136
+ parsed_attributes[key]['name_list'] = json.dumps(parsed_attributes[key]['value'], indent=2)
137
+
129
138
  elif style == 'Valid Value':
130
139
  parsed_attributes[key] = proc_valid_value(txt, object_action, labels,
131
140
  attr[key].get('valid_values', None), if_missing,
@@ -325,8 +334,12 @@ def parse_view_command(egeria_client: EgeriaTech, object_type: str, object_actio
325
334
  default_value = attr[key].get('default_value', None)
326
335
 
327
336
  style = attr[key]['style']
328
- if style in ['Simple', 'Dictionary', 'Comment']:
337
+ if style in ['Simple', 'Comment']:
329
338
  parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
339
+ elif style == 'Dictionary':
340
+ parsed_attributes[key] = proc_dictionary_attribute(txt, object_action, labels, if_missing,
341
+ default_value)
342
+ parsed_attributes[key]['name_list'] = json.dumps(parsed_attributes[key]['value'], indent=2)
330
343
  elif style == 'Valid Value':
331
344
  parsed_attributes[key] = proc_valid_value(txt, object_action, labels,
332
345
  attr[key].get('valid_values', None), if_missing,
@@ -401,7 +414,7 @@ def parse_view_command(egeria_client: EgeriaTech, object_type: str, object_actio
401
414
  value = parsed_attributes[key].get('value', None)
402
415
 
403
416
  if value is not None:
404
- # if the value is a dict, get the flattened name list
417
+ # if the value is a dict or list, get the stringifiedt
405
418
  value = parsed_attributes[key].get('name_list', None) if isinstance(value, (dict, list)) else value
406
419
  parsed_output['display'] += f"\n\t* {key}: `{value}`\n\t"
407
420
 
@@ -464,6 +477,50 @@ def proc_simple_attribute(txt: str, action: str, labels: set, if_missing: str =
464
477
 
465
478
  return {"status": INFO, "OK": None, "value": attribute, "valid": valid, "exists": True}
466
479
 
480
+ @logger.catch
481
+ def proc_dictionary_attribute(txt: str, action: str, labels: set, if_missing: str = INFO, default_value=None,
482
+ simp_type: str = None) -> dict:
483
+ """Process a dictionary attribute based on the provided labels and if_missing value.
484
+ Extract the attribute value from the text and store it in a dictionary along with valid.
485
+ If it doesn`t exist, mark the dictionary entry as invalid and print an error message with severity of if_missing.
486
+
487
+ Parameters:
488
+ ----------
489
+ txt: str
490
+ The block of object_action text to extract attributes from.
491
+ labels: list
492
+ The possible attribute labels to search for. The first label will be used in messages.
493
+ if_missing: str, default is INFO
494
+ Can be one of "WARNING", "ERROR", "INFO". The severity of the missing attribute.
495
+ default_value: default is None
496
+ The default value to return if the attribute is missing.
497
+ """
498
+ valid = True
499
+
500
+ if if_missing not in ["WARNING", "ERROR", "INFO"]:
501
+ msg = "Invalid severity for missing attribute"
502
+ logger.error(msg)
503
+ return {"status": ERROR, "reason": msg, "value": None, "valid": False}
504
+
505
+ if default_value == "":
506
+ default_value = None
507
+
508
+ attr = extract_attribute(txt, labels)
509
+ attribute = json.loads(attr) if attr is not None else default_value
510
+
511
+
512
+ if attribute is None:
513
+ if if_missing == INFO or if_missing == WARNING:
514
+ msg = f"Optional attribute with labels: `{labels}` missing"
515
+ valid = True
516
+ logger.info(msg)
517
+ else:
518
+ msg = f"Missing attribute with labels `{labels}` "
519
+ valid = False
520
+ logger.error(msg)
521
+ return {"status": if_missing, "reason": msg, "value": None, "valid": valid, "exists": False}
522
+
523
+ return {"status": INFO, "OK": None, "value": attribute, "valid": valid, "exists": True}
467
524
 
468
525
  @logger.catch
469
526
  def proc_valid_value(txt: str, action: str, labels: set, valid_values: [], if_missing: str = INFO,
@@ -524,8 +581,11 @@ def proc_valid_value(txt: str, action: str, labels: set, valid_values: [], if_mi
524
581
  logger.error(msg)
525
582
  return {"status": if_missing, "reason": msg, "value": None, "valid": valid, "exists": False}
526
583
  else:
584
+ # Todo: look at moving validation into pydantic or another style...
585
+ if "Status" in labels:
586
+ attribute = attribute.upper()
527
587
  if attribute not in v_values:
528
- msg = f"Invalid value for attribute `{labels}`"
588
+ msg = f"Invalid value for attribute `{labels}` attribute is `{attribute}`"
529
589
  logger.warning(msg)
530
590
  return {"status": WARNING, "reason": msg, "value": attribute, "valid": False, "exists": True}
531
591
 
@@ -651,7 +711,7 @@ def proc_el_id(egeria_client: EgeriaTech, element_type: str, qn_prefix: str, ele
651
711
  logger.error(msg)
652
712
  identifier_output = {"status": ERROR, "reason": msg, "value": element_name, "valid": False, "exists": False, }
653
713
 
654
- elif action in ["Update", "View"] and exists:
714
+ elif action in ["Update", "View", "Link", "Detach"] and exists:
655
715
  msg = f"Element {element_name} exists"
656
716
  logger.info(msg)
657
717
  identifier_output = {
@@ -923,3 +983,101 @@ def update_term_categories(egeria_client: EgeriaTech, term_guid: str, categories
923
983
 
924
984
 
925
985
 
986
+
987
+ @logger.catch
988
+ def process_output_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
989
+ """
990
+ Processes a generic output request by extracting attributes (including Output Format and
991
+ Output Format Set) and dynamically invoking the find function specified by the
992
+ output_format_set, following the approach used in commands/cat/list_format_set.
993
+
994
+ This is modeled on process_gov_definition_list_command but uses the dynamic
995
+ dispatch via the output format set rather than directly calling a specific
996
+ egeria_client method.
997
+
998
+ :param egeria_client: EgeriaTech composite client instance
999
+ :param txt: The command text (e.g., parsed from a markdown cell)
1000
+ :param directive: display | validate | process
1001
+ :return: Markdown string for processed output or None
1002
+ """
1003
+ command, object_type, object_action = extract_command_plus(txt)
1004
+ print(Markdown(f"# {command}\n"))
1005
+
1006
+ parsed_output = parse_view_command(egeria_client, object_type, object_action, txt, directive)
1007
+
1008
+ valid = parsed_output['valid']
1009
+ print(Markdown(f"Performing {command}"))
1010
+ print(Markdown(parsed_output['display']))
1011
+
1012
+ attr = parsed_output.get('attributes', {})
1013
+
1014
+ search_string = attr.get('Search String', {}).get('value', '*')
1015
+ output_format = attr.get('Output Format', {}).get('value', 'LIST')
1016
+ output_format_set = attr.get('Output Format Set', {}).get('value', object_type)
1017
+
1018
+ if directive == "display":
1019
+ return None
1020
+ elif directive == "validate":
1021
+ # Validate that the format set exists and has an action
1022
+ fmt = select_output_format_set(output_format_set, "ANY") if valid else None
1023
+ if valid and fmt and fmt.get("action"):
1024
+ print(Markdown(f"==> Validation of {command} completed successfully!\n"))
1025
+ return True
1026
+ else:
1027
+ msg = f"Validation failed for object_action `{command}`"
1028
+ logger.error(msg)
1029
+ return False
1030
+
1031
+ elif directive == "process":
1032
+ try:
1033
+ if not valid:
1034
+ msg = f"Validation failed for {object_action} `{object_type}`"
1035
+ logger.error(msg)
1036
+ return None
1037
+
1038
+ # Resolve the find function from the output format set
1039
+ fmt = select_output_format_set(output_format_set, output_format)
1040
+ if not fmt:
1041
+ logger.error(f"Output format set '{output_format_set}' not found or not compatible with '{output_format}'.")
1042
+ return None
1043
+ action = fmt.get("action", {})
1044
+ func_spec = action.get("function")
1045
+ if not func_spec or "." not in func_spec:
1046
+ func_spec = f"EgeriaTech.find_{object_type.replace(' ', '_').lower()}"
1047
+
1048
+
1049
+ # Extract method name and get it from the composite client
1050
+ _, method_name = func_spec.split(".", 1)
1051
+ if not hasattr(egeria_client, method_name):
1052
+ logger.error(f"Method '{method_name}' not available on EgeriaTech client.")
1053
+ return None
1054
+ method = getattr(egeria_client, method_name)
1055
+
1056
+ # Build body and params
1057
+ list_md = f"\n# `{object_type}` with filter: `{search_string}`\n\n"
1058
+ body = set_find_body(object_type, attr)
1059
+
1060
+ params = {
1061
+ 'search_string': search_string,
1062
+ 'body': body,
1063
+ 'output_format': output_format,
1064
+ 'output_format_set': output_format_set,
1065
+ }
1066
+
1067
+ # Call the resolved method
1068
+ struct = method(**params)
1069
+
1070
+ if output_format.upper() == "DICT":
1071
+ list_md += f"```\n{json.dumps(struct, indent=4)}\n```\n"
1072
+ else:
1073
+ list_md += struct
1074
+ logger.info(f"Wrote `{object_type}` for search string: `{search_string}` using format set '{output_format_set}'")
1075
+
1076
+ return list_md
1077
+
1078
+ except Exception as e:
1079
+ logger.error(f"Error performing {command}: {e}")
1080
+ console.print_exception(show_locals=True)
1081
+ return None
1082
+ else:
1083
+ return None
@@ -11,7 +11,7 @@ from loguru import logger
11
11
  from rich import print
12
12
  from rich.console import Console
13
13
  from rich.markdown import Markdown
14
- from pyegeria.utils import (camel_to_title_case)
14
+ from pyegeria.utils import (camel_to_title_case, body_slimmer)
15
15
  from pyegeria._globals import DEBUG_LEVEL
16
16
  from md_processing.md_processing_utils.message_constants import message_types
17
17
 
@@ -38,11 +38,22 @@ LOG_FORMAT = "D <green> {time} </green> | {level} | {function} | {line} | {messa
38
38
  CONSOLE_LOG_FORMAT = "<green>{time}</green> | {message}"
39
39
 
40
40
  console = Console(width=EGERIA_WIDTH)
41
- GENERAL_GOVERNANCE_DEFINITIONS = ["Business Imperative", "Regulation Article", "Threat", "Governance Principle",
42
- "Governance Obligation", "Governance Approach", "Governance Processing Purpose"]
41
+ GENERAL_GOVERNANCE_DEFINITIONS = ["Governance Definition", "Business Imperative", "Regulation Article", "Threat",
42
+ "Governance Policy", "Governance Principle", "Governance Obligation",
43
+ "Governance Approach",
44
+ "Governance Processing Purpose"]
45
+ GOVERNANCE_DRIVERS = ["Governance Driver", "Governance Strategy", "Governance Imperative", "Regulation",
46
+ "Regulation Article", "Threat"]
47
+ GOVERNANCE_POLICIES = ["Governance Policy", "Governance Principle", "Governance Obligation", "Governance Approach"]
43
48
 
44
- GOVERNANCE_CONTROLS = ["Governance Rule", "Service Level Objective", "Governance Process",
45
- "Governance Responsibility", "Governance Procedure", "Security Access Control"]
49
+
50
+ GOVERNANCE_CONTROLS = ["Governance Control", "Governance Rule", "Service Level Objective", "Governance Action",
51
+ "Security Access Control", "Governance Procedure","Governance Responsibility",
52
+ "Subject Area Definition", "Data Processing Purposes", "Methodology"]
53
+
54
+ ALL_GOVERNANCE_DEFINITIONS = GENERAL_GOVERNANCE_DEFINITIONS + GOVERNANCE_CONTROLS + ["Governance Strategy", "Regulation",
55
+ "Security Group", "GovernanceMetric",
56
+ "Naming Standard Rule", "TermsAndConditions", "Certification Type", "License Type"]
46
57
 
47
58
  debug_level = DEBUG_LEVEL
48
59
  global COMMAND_DEFINITIONS
@@ -216,13 +227,46 @@ def find_key_with_value(value: str) -> str | None:
216
227
  return None # If value not found
217
228
 
218
229
 
230
+ def set_find_body(object_type: str, attributes: dict)->dict:
231
+ prop_name = object_type.replace(" ", "")
232
+
233
+ start = attributes.get('Start From', {}).get('value', 0)
234
+ start_from = int(start) if start else 0
235
+ page = attributes.get('Page Size', {}).get('value', 0)
236
+ page_size = int(page) if page else 0
237
+ depth = attributes.get('Graph Query Depth', {}).get('value', 0)
238
+ depth = int(depth) if depth else 0
239
+
240
+
241
+
242
+ body = {
243
+ "class": "SearchStringRequestBody",
244
+ "searchString": attributes.get('Search String', {}).get('value', None),
245
+ "startsWith": attributes.get('Start With', {}).get('value', True),
246
+ "endWith": attributes.get('End With', {}).get('value', False),
247
+ "ignoreCase": attributes.get('Ignore Case', {}).get('value', False),
248
+ "limitResultsByStatus": attributes.get('Limit Results By Status', {}).get('value', False),
249
+ "startFrom": int(attributes.get('Start From', {}).get('value', 0)),
250
+ "pageSize": int(attributes.get('Page Size', {}).get('value', 0)),
251
+ # "metadataElementSubtypeNames": attributes.get('Metadata Element Subtype Name', {}).get('value', None),
252
+ "metadataElementTypeName": attributes.get('Metadata Element Type Name', {}).get('value', None),
253
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
254
+ "governanceZoneFilter" : attributes.get('Governance Zone Filter', {}).get('value', None),
255
+ "graphQueryDepth": int(attributes.get('Graph Query Depth', {}).get('value', 0)),
256
+ "initialStatus": attributes.get('Status', {}).get('value', "ACTIVE"),
257
+ "initialClassifications": {}}
258
+
259
+ return body
260
+
261
+
219
262
  def set_create_body(object_type: str, attributes: dict)->dict:
220
263
  prop_name = object_type.replace(" ", "")
221
264
  body = {
222
- "class": "NewGovernanceDefinitionRequestBody",
265
+ "class": "NewElementRequestBody",
223
266
  "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
224
267
  "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
225
268
  "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
269
+ "governanceZoneFilter" : attributes.get('Governance Zone Filter', {}).get('value', None),
226
270
  "forLineage": attributes.get('For Lineage', {}).get('value', False),
227
271
  "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
228
272
  "anchorGUID": attributes.get('Anchor ID', {}).get('guid', None),
@@ -245,6 +289,7 @@ def set_update_body(object_type: str, attributes: dict)->dict:
245
289
  "class" : "UpdateElementRequestBody",
246
290
  "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
247
291
  "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
292
+ "governanceZoneFilter": attributes.get('Governance Zone Filter', {}).get('value', None),
248
293
  "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
249
294
  "forLineage": attributes.get('For Lineage', {}).get('value', False),
250
295
  "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
@@ -261,35 +306,66 @@ def set_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict
261
306
  "displayName": attributes['Display Name'].get('value', None),
262
307
  "qualifiedName" : qualified_name,
263
308
  "description": attributes['Description'].get('value', None),
264
- "status": attributes.get('Status', {}).get('value', "ACTIVE"),
309
+ "category": attributes.get('Category', {}).get('value', None),
310
+ "identifier": attributes.get('Identifier', {}).get('value', None),
265
311
  "userDefinedStatus": attributes.get('User Defined Status', {}).get('value', None),
266
312
  "versionIdentifier": attributes.get('Version Identifier', {}).get('value', None),
267
313
  "effectiveFrom": attributes.get('Effective From', {}).get('value', None),
268
314
  "effectiveTo": attributes.get('Effective To', {}).get('value', None),
269
315
  "additionalProperties": attributes.get('Additional Properties', {}).get('value', None),
270
- "extendedProperties": attributes.get('Extended Properties', {}).get('value', None)
316
+ "extendedProperties": attributes.get('Extended Properties', {}).get('value', None),
317
+ "supportLevel": attributes.get('Support Level', {}).get('value', None),
318
+ "serviceLevels": attributes.get('Service Levels', {}).get('value', None),
271
319
  }
272
320
 
321
+ def set_product_body(object_type: str, qualified_name: str, attributes: dict)->dict:
322
+ prop_bod = set_prop_body(object_type, qualified_name, attributes)
323
+ prop_bod["identifier"] = attributes.get('Identifier', {}).get('value', None)
324
+ prop_bod["productName"] = attributes.get('Product Name', {}).get('value', None)
325
+ prop_bod["maturity"] = attributes.get('Maturity', {}).get('value', None)
326
+ prop_bod["serviceLife"] = attributes.get('Service Life', {}).get('value', None)
327
+ prop_bod["introductionDate"] = attributes.get('Introduction Date', {}).get('value', [])
328
+ prop_bod["withdrawalDate"] = attributes.get('Withdrawal Date', {}).get('value', [])
329
+ prop_bod["nextVersion"] = attributes.get('Next Version Date', {}).get('value', [])
330
+ return prop_bod
331
+
332
+
333
+
334
+ def set_update_status_body(object_type: str, attributes: dict)->dict:
335
+ return {
336
+ "class" : "UpdateStatusRequestBody",
337
+ "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
338
+ "forLineage": attributes.get('For Lineage', {}).get('value', False),
339
+ "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
340
+ "mergeUpdate": attributes.get('Merge Update', {}).get('value', True),
341
+ }
342
+
273
343
  def set_gov_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict:
274
344
  prop_name = object_type.replace(" ", "")
275
345
  prop_bod = set_prop_body(object_type, qualified_name, attributes)
276
346
  prop_bod["domainIdentifier"] = attributes.get('Domain Identifier', {}).get('value', None)
277
- # prop_bod["documentIdentifier"] = attributes.get('Document Identifier', {}).get('value', None)
278
- prop_bod["title"]= attributes.get('Display Name', {}).get('value', None)
279
- prop_bod['documentIdentifier'] = qualified_name
347
+ prop_bod["displayName"]= attributes.get('Display Name', {}).get('value', None)
348
+ prop_bod['qualifiedName'] = qualified_name
280
349
  prop_bod["versionIdentifier"] = attributes.get('Version Identifier', {}).get('value', None)
281
350
  prop_bod["summary"] = attributes.get('Summary', {}).get('value', None)
351
+ prop_bod["description"] = attributes.get('Description', {}).get('value', None)
352
+
282
353
  prop_bod["scope"] = attributes.get('Scope', {}).get('value', None)
283
354
  prop_bod["importance"] = attributes.get('Importance', {}).get('value', None)
284
355
  prop_bod["implications"] = attributes.get('Implication', {}).get('value', [])
285
356
  prop_bod["outcomes"] = attributes.get('Outcomes', {}).get('value', [])
286
357
  prop_bod["results"] = attributes.get('Results', {}).get('value', [])
358
+ prop_bod["effectiveFrom"] = attributes.get('Effective From', {}).get('value', None),
359
+ prop_bod["effectiveTo"] = attributes.get('Effective To', {}).get('value', None),
360
+ prop_bod["additionalProperties"] = attributes.get('Additional Properties', {}).get('value', None),
361
+ prop_bod["extendedProperties"] = attributes.get('Extended Properties', {}).get('value', None)
287
362
 
288
- body = update_body_for_type(object_type, prop_bod, attributes)
363
+
364
+ body = update_gov_body_for_type(object_type, prop_bod, attributes)
289
365
  return body
290
366
 
291
367
 
292
- def update_body_for_type(object_type: str, body: dict, attributes: dict) -> dict:
368
+ def update_gov_body_for_type(object_type: str, body: dict, attributes: dict) -> dict:
293
369
  gov_def_name = object_type.replace(" ", "")
294
370
  if object_type in GENERAL_GOVERNANCE_DEFINITIONS:
295
371
  return body
@@ -298,7 +374,7 @@ def update_body_for_type(object_type: str, body: dict, attributes: dict) -> dict
298
374
  return body
299
375
 
300
376
  elif object_type == "Regulation":
301
- body['source'] = attributes.get('Source', {}).get('value', None)
377
+ body['regulationSource'] = attributes.get('Regulation Source', {}).get('value', None)
302
378
  body['regulators'] = attributes.get('Regulators', {}).get('value', [])
303
379
  return body
304
380
 
@@ -308,17 +384,24 @@ def update_body_for_type(object_type: str, body: dict, attributes: dict) -> dict
308
384
  elif object_type == "Security Group":
309
385
  body['distinguishedName'] = attributes.get('Distinguished Name', {}).get('value', None)
310
386
  return body
387
+ elif object_type == "GovernanceMetric":
388
+ body['measurement'] = attributes.get('Measurement', {}).get('value', None)
389
+ body['target'] = attributes.get('Target', {}).get('value', None)
390
+ return body
311
391
  elif object_type == "Naming Standard Rule":
312
392
  body['namePatterns'] = attributes.get('Name Patterns', {}).get('value', [])
313
393
  return body
314
- elif object_type in ["Certification Type", "License Type"]:
315
- body['details'] = attributes.get('Details', {}).get('value', None)
394
+ elif object_type in ["TermsAndConditions", "Certification Type", "License Type"]:
395
+ body['entitlements'] = attributes.get('Entitlementss', {}).get('value', {})
396
+ body['restrictions'] = attributes.get('Restrictions', {}).get('value', {})
397
+ body['obligations'] = attributes.get('Obligations', {}).get('value', {})
398
+
316
399
  return body
317
400
 
318
401
 
319
402
  def set_rel_request_body(object_type: str, attributes: dict)->dict:
320
403
  return {
321
- "class" : "RelationshipRequestBody",
404
+ "class" : "NewRelationshipRequestBody",
322
405
  "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
323
406
  "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
324
407
  "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
@@ -338,9 +421,9 @@ def set_peer_gov_def_request_body(object_type: str, attributes: dict)->dict:
338
421
  }
339
422
  return rel_body
340
423
 
341
- def set_metadata_source_request_body(object_type: str, attributes: dict)->dict:
424
+ def set_delete_request_body(object_type: str, attributes: dict)->dict:
342
425
  return {
343
- "class": "MetadataSourceRequestBody",
426
+ "class": "DeleteRequestBody",
344
427
  "externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
345
428
  "externalSourceName": attributes.get('External Source Name', {}).get('value', None),
346
429
  "effectiveTime": attributes.get('Effective Time', {}).get('value', None),
@@ -348,6 +431,8 @@ def set_metadata_source_request_body(object_type: str, attributes: dict)->dict:
348
431
  "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False)
349
432
  }
350
433
 
434
+
435
+
351
436
  def set_filter_request_body(object_type: str, attributes: dict)->dict:
352
437
  return {
353
438
  "class": "FilterRequestBody",
@@ -371,14 +456,27 @@ def set_element_status_request_body(object_type: str, attributes: dict)->dict:
371
456
  "forLineage": attributes.get('For Lineage', {}).get('value', False),
372
457
  "forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False)
373
458
  }
374
- def set_collection_property_body(object_type: str, qualified_name:str, attributes: dict)->dict:
375
- body = set_prop_body("Collection", qualified_name,attributes)
376
- body["category"] = attributes.get('Category', {}).get('value', None)
377
- return body
378
459
 
379
- def set_collection_classifications(object_type: str, attributes: dict)->dict:
380
- collection_classifications = attributes.get('Collection Classifications', {}).get('name_list', None)
381
- if collection_classifications is None:
382
- collection_classifications = object_type
383
- body = {classification: {} for classification in collection_classifications} if collection_classifications else {}
460
+
461
+ def set_classifications(object_type: str, attributes: dict)->dict:
462
+ classifications = attributes.get('Classifications', {}).get('name_list', None)
463
+ body = None
464
+ if classifications:
465
+ body = {classification: {} for classification in classifications} if cclassifications else {}
384
466
  return body
467
+
468
+ def set_collection_classifications(object_type: str, attributes: dict, obj_types: list[str])->dict:
469
+ classifications = attributes.get('Classifications', {}).get('name_list', None)
470
+ obj = object_type.replace(" ", "")
471
+ if object_type in obj_types:
472
+ if classifications:
473
+ if object_type not in classifications:
474
+ classifications.append(obj)
475
+ else:
476
+ classifications = [obj]
477
+
478
+ body = {}
479
+ if classifications:
480
+ for classification in classifications:
481
+ body[classification] = {"class" : f"{classification}Properties"}
482
+ return body
@@ -91,7 +91,7 @@ def extract_attribute(text: str, labels: set) -> str | None:
91
91
  # Iterate over the list of labels
92
92
  for label in labels:
93
93
  # Construct pattern for the current label
94
- # text = re.sub(r'\s+', ' ', text).strip()
94
+ # text = re.sub(r'\s+', ' ', text).strip() # just added
95
95
  text = re.sub(r'\n\n+', '\n\n', text).strip()
96
96
 
97
97
  label = label.strip()
@@ -395,7 +395,7 @@ def get_element_by_name(egeria_client, element_type: str, element_name: str) ->
395
395
  return q_name, guid, unique, exists
396
396
 
397
397
  else: # Missing guid from element_dictionary
398
- guid = egeria_client.get_element_guid_by_unique_name(element_name)
398
+ guid = egeria_client.__get_guid__(qualified_name=q_name)
399
399
  if guid == NO_ELEMENTS_FOUND:
400
400
  guid = None
401
401
  exists = False
@@ -51,9 +51,10 @@ TERM_RELATIONSHPS = ["Synonym", "Translation", "PreferredTerm", "TermISATYPEOFRe
51
51
  "ISARelationship"]
52
52
 
53
53
  # List of supported md_commands
54
- GOV_LINK_LIST = [ "Link Governance Drivers", "Detach Governance Drivers",
55
- "Link Governance Policies", "Detach Governance Policies",
56
- "Link Governance Controls", "Detach Governance Controls",]
54
+ GOV_LINK_LIST = [ "Link Governance Drivers", "Link Drivers", "Detach Governance Drivers", "Detach Drivers",
55
+ "Link Governance Policies", "Link Policies", "Detach Governance Policies", "Detach Policies",
56
+ "Link Governance Controls", "Link Controls", "Detach Governance Controls", "Detach Controls",
57
+ ]
57
58
 
58
59
  GOV_COM_LIST = [ "Create Business Imperative", "Update Business Imperative",
59
60
  "Create Regulation Article Definition", "Update Regulation Article Definition",
@@ -130,20 +131,21 @@ command_list = ["Provenance", "Create Glossary", "Update Glossary", "Create Term
130
131
  "View Dataa Classes", "View Data Class", "Create Data Class", "Update Data Class",
131
132
  "Create Digital Product", "Create Data Product", "Update Digital Product", "Update Data Product",
132
133
  "Create Agreement", "Update Agreement",
133
- "Link Digital Products", "Link Data Products", "Detach Digital Products", "Detach Data Products",
134
- # "Create Data Sharing Agreement", "Update Data Sharing Agreement",
134
+ "Link Digital Products", "Link Product-Product", "Detach Digital Products", "Detach Product-Product",
135
+ "Create Data Sharing Agreement", "Update Data Sharing Agreement",
135
136
  "Create Digital Subscription", "Create Product Subscription", "Update Digital Subscription", "Update Product Subscription",
136
- "Attach Agreement Items", "Detach Agreement Items",
137
+ "Link Agreement->Item", "Detach Agreement->Item",
137
138
  "Attach Contract", "Detach Contract",
138
- "Attach Subscriber", "Detach Subscriber",
139
- "Link Collection to Resource", "Attach Collection to Resource",
140
- "Unlink Collection From Resource", "Detach Collection From Resource",
139
+ "Link Subscriber->Subscription", "Detach Subscriber->Subscription",
140
+ "Link Collection->Resource", "Attach Collection->Resource",
141
+ "Unlink Collection->Resource", "Detach Collection->Resource",
141
142
  "Add Member to Collection", "Add Member", "Member->Collection",
142
- "Remove Member from Collection","Remove Member from Collection",
143
+ "Remove Member from Collection","Remove Member->Collection",
143
144
  "View Governance Definitions", "View Gov Definitions",
144
145
  "List Governance Definitions", "List Gov Definitions",
145
146
  "View Governance Definition Context","List Governance Definition Context",
146
147
  "View Governance Def Context", "List Governance Def Context",
148
+ "View Report",
147
149
  # "Create Business Imperative", "Update Business Imperative",
148
150
  # "Create Regulation Article Definition", "Update Regulation Article Definition",
149
151
  # "Create Threat Definition", "Update Threat Definition",
@@ -173,6 +175,8 @@ command_list.extend(GOV_COM_LIST)
173
175
  command_list.extend(GOV_LINK_LIST)
174
176
  command_list.extend(COLLECTIONS_LIST)
175
177
  command_list.extend(SIMPLE_COLLECTIONS)
178
+ command_list.extend(["Link Governance Response", "Detach Governance Response",
179
+ "Link Governance Mechanism", "Detach Governance Mechanism"])
176
180
 
177
181
  pre_command = "\n---\n==> Processing object_action:"
178
182
  command_seperator = Markdown("\n---\n")
pyegeria/.DS_Store CHANGED
Binary file
pyegeria/__init__.py CHANGED
@@ -30,7 +30,7 @@ from ._client_new import Client2
30
30
  from ._exceptions_new import (PyegeriaInvalidParameterException,PyegeriaAPIException, PyegeriaException,
31
31
  PyegeriaUnauthorizedException, PyegeriaClientException, PyegeriaUnknownException,
32
32
  PyegeriaConnectionException, PyegeriaNotFoundException,
33
- print_exception_table, print_basic_exception)
33
+ print_exception_table, print_basic_exception, print_validation_error)
34
34
  from .load_config import load_app_config, get_app_config
35
35
  from .logging_configuration import config_logging, console_log_filter, init_logging
36
36
  from ._exceptions import (InvalidParameterException, PropertyServerException, UserNotAuthorizedException,