pyegeria 5.4.0.23__py3-none-any.whl → 5.4.0.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- commands/cat/debug_log +2046 -465
- commands/cat/debug_log.2025-08-15_09-14-07_444802.zip +0 -0
- commands/cat/debug_log.2025-08-16_10-21-59_388912.zip +0 -0
- commands/cat/dr_egeria_md.py +16 -3
- md_processing/__init__.py +5 -3
- md_processing/data/commands.json +8310 -903
- md_processing/dr_egeria_inbox/gov_def.md +76 -18
- md_processing/dr_egeria_inbox/img.png +0 -0
- md_processing/dr_egeria_inbox/product.md +132 -20
- md_processing/dr_egeria_outbox/monday/processed-2025-08-17 21:04-product.md +97 -0
- md_processing/md_commands/governance_officer_commands.py +291 -150
- md_processing/md_commands/product_manager_commands.py +309 -401
- md_processing/md_processing_utils/common_md_proc_utils.py +110 -7
- md_processing/md_processing_utils/common_md_utils.py +112 -26
- md_processing/md_processing_utils/md_processing_constants.py +8 -5
- pyegeria/__init__.py +1 -1
- pyegeria/_client_new.py +3 -2
- pyegeria/_exceptions_new.py +6 -0
- pyegeria/_output_formats.py +17 -2
- pyegeria/governance_officer.py +1 -3
- pyegeria/load_config.py +1 -1
- pyegeria/models.py +28 -1
- pyegeria/utils.py +1 -1
- {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.24.dist-info}/METADATA +1 -1
- {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.24.dist-info}/RECORD +28 -30
- commands/cat/logs/pyegeria.log +0 -136
- md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:00-product.md +0 -62
- md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:13-product.md +0 -62
- md_processing/dr_egeria_outbox/friday/processed-2025-07-20 13:23-product.md +0 -47
- md_processing/dr_egeria_outbox/friday/processed-2025-08-01 11:55-data_test3.md +0 -503
- md_processing/dr_egeria_outbox/processed-2025-08-03 16:05-glossary_list.md +0 -37
- {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.24.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.24.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.0.23.dist-info → pyegeria-5.4.0.24.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
|
-
|
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")
|
@@ -923,3 +928,101 @@ def update_term_categories(egeria_client: EgeriaTech, term_guid: str, categories
|
|
923
928
|
|
924
929
|
|
925
930
|
|
931
|
+
|
932
|
+
@logger.catch
|
933
|
+
def process_output_command(egeria_client: EgeriaTech, txt: str, directive: str = "display") -> Optional[str]:
|
934
|
+
"""
|
935
|
+
Processes a generic output request by extracting attributes (including Output Format and
|
936
|
+
Output Format Set) and dynamically invoking the find function specified by the
|
937
|
+
output_format_set, following the approach used in commands/cat/list_format_set.
|
938
|
+
|
939
|
+
This is modeled on process_gov_definition_list_command but uses the dynamic
|
940
|
+
dispatch via the output format set rather than directly calling a specific
|
941
|
+
egeria_client method.
|
942
|
+
|
943
|
+
:param egeria_client: EgeriaTech composite client instance
|
944
|
+
:param txt: The command text (e.g., parsed from a markdown cell)
|
945
|
+
:param directive: display | validate | process
|
946
|
+
:return: Markdown string for processed output or None
|
947
|
+
"""
|
948
|
+
command, object_type, object_action = extract_command_plus(txt)
|
949
|
+
print(Markdown(f"# {command}\n"))
|
950
|
+
|
951
|
+
parsed_output = parse_view_command(egeria_client, object_type, object_action, txt, directive)
|
952
|
+
|
953
|
+
valid = parsed_output['valid']
|
954
|
+
print(Markdown(f"Performing {command}"))
|
955
|
+
print(Markdown(parsed_output['display']))
|
956
|
+
|
957
|
+
attr = parsed_output.get('attributes', {})
|
958
|
+
|
959
|
+
search_string = attr.get('Search String', {}).get('value', '*')
|
960
|
+
output_format = attr.get('Output Format', {}).get('value', 'LIST')
|
961
|
+
output_format_set = attr.get('Output Format Set', {}).get('value', object_type)
|
962
|
+
|
963
|
+
if directive == "display":
|
964
|
+
return None
|
965
|
+
elif directive == "validate":
|
966
|
+
# Validate that the format set exists and has an action
|
967
|
+
fmt = select_output_format_set(output_format_set, "ANY") if valid else None
|
968
|
+
if valid and fmt and fmt.get("action"):
|
969
|
+
print(Markdown(f"==> Validation of {command} completed successfully!\n"))
|
970
|
+
return True
|
971
|
+
else:
|
972
|
+
msg = f"Validation failed for object_action `{command}`"
|
973
|
+
logger.error(msg)
|
974
|
+
return False
|
975
|
+
|
976
|
+
elif directive == "process":
|
977
|
+
try:
|
978
|
+
if not valid:
|
979
|
+
msg = f"Validation failed for {object_action} `{object_type}`"
|
980
|
+
logger.error(msg)
|
981
|
+
return None
|
982
|
+
|
983
|
+
# Resolve the find function from the output format set
|
984
|
+
fmt = select_output_format_set(output_format_set, output_format)
|
985
|
+
if not fmt:
|
986
|
+
logger.error(f"Output format set '{output_format_set}' not found or not compatible with '{output_format}'.")
|
987
|
+
return None
|
988
|
+
action = fmt.get("action", {})
|
989
|
+
func_spec = action.get("function")
|
990
|
+
if not func_spec or "." not in func_spec:
|
991
|
+
func_spec = f"EgeriaTech.find_{object_type.replace(' ', '_').lower()}"
|
992
|
+
|
993
|
+
|
994
|
+
# Extract method name and get it from the composite client
|
995
|
+
_, method_name = func_spec.split(".", 1)
|
996
|
+
if not hasattr(egeria_client, method_name):
|
997
|
+
logger.error(f"Method '{method_name}' not available on EgeriaTech client.")
|
998
|
+
return None
|
999
|
+
method = getattr(egeria_client, method_name)
|
1000
|
+
|
1001
|
+
# Build body and params
|
1002
|
+
list_md = f"\n# `{object_type}` with filter: `{search_string}`\n\n"
|
1003
|
+
body = set_find_body(object_type, attr)
|
1004
|
+
|
1005
|
+
params = {
|
1006
|
+
'search_string': search_string,
|
1007
|
+
'body': body,
|
1008
|
+
'output_format': output_format,
|
1009
|
+
'output_format_set': output_format_set,
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
# Call the resolved method
|
1013
|
+
struct = method(**params)
|
1014
|
+
|
1015
|
+
if output_format.upper() == "DICT":
|
1016
|
+
list_md += f"```\n{json.dumps(struct, indent=4)}\n```\n"
|
1017
|
+
else:
|
1018
|
+
list_md += struct
|
1019
|
+
logger.info(f"Wrote `{object_type}` for search string: `{search_string}` using format set '{output_format_set}'")
|
1020
|
+
|
1021
|
+
return list_md
|
1022
|
+
|
1023
|
+
except Exception as e:
|
1024
|
+
logger.error(f"Error performing {command}: {e}")
|
1025
|
+
console.print_exception(show_locals=True)
|
1026
|
+
return None
|
1027
|
+
else:
|
1028
|
+
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",
|
42
|
-
"Governance
|
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
|
-
|
45
|
-
|
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": "
|
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,7 +306,7 @@ 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
|
-
"
|
309
|
+
"category": attributes.get('Category', {}).get('value', None),
|
265
310
|
"userDefinedStatus": attributes.get('User Defined Status', {}).get('value', None),
|
266
311
|
"versionIdentifier": attributes.get('Version Identifier', {}).get('value', None),
|
267
312
|
"effectiveFrom": attributes.get('Effective From', {}).get('value', None),
|
@@ -270,26 +315,51 @@ def set_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict
|
|
270
315
|
"extendedProperties": attributes.get('Extended Properties', {}).get('value', None)
|
271
316
|
}
|
272
317
|
|
318
|
+
def set_product_body(object_type: str, qualified_name: str, attributes: dict)->dict:
|
319
|
+
prop_bod = set_prop_body(object_type, qualified_name, attributes)
|
320
|
+
prop_bod["identifier"] = attributes.get('Identifier', {}).get('value', None)
|
321
|
+
prop_bod["productName"] = attributes.get('Product Name', {}).get('value', None)
|
322
|
+
prop_bod["maturity"] = attributes.get('Maturity', {}).get('value', None)
|
323
|
+
prop_bod["serviceLife"] = attributes.get('Service Life', {}).get('value', None)
|
324
|
+
prop_bod["introductionDate"] = attributes.get('Introduction Date', {}).get('value', [])
|
325
|
+
prop_bod["withdrawalDate"] = attributes.get('Withdrawal Date', {}).get('value', [])
|
326
|
+
prop_bod["nextVersion"] = attributes.get('Next Version Date', {}).get('value', [])
|
327
|
+
|
328
|
+
def set_update_status_body(object_type: str, attributes: dict)->dict:
|
329
|
+
return {
|
330
|
+
"class" : "UpdateStatusRequestBody",
|
331
|
+
"effectiveTime": attributes.get('Effective Time', {}).get('value', None),
|
332
|
+
"forLineage": attributes.get('For Lineage', {}).get('value', False),
|
333
|
+
"forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False),
|
334
|
+
"mergeUpdate": attributes.get('Merge Update', {}).get('value', True),
|
335
|
+
}
|
336
|
+
|
273
337
|
def set_gov_prop_body(object_type: str, qualified_name: str, attributes: dict)->dict:
|
274
338
|
prop_name = object_type.replace(" ", "")
|
275
339
|
prop_bod = set_prop_body(object_type, qualified_name, attributes)
|
276
340
|
prop_bod["domainIdentifier"] = attributes.get('Domain Identifier', {}).get('value', None)
|
277
|
-
|
278
|
-
prop_bod[
|
279
|
-
prop_bod['documentIdentifier'] = qualified_name
|
341
|
+
prop_bod["displayName"]= attributes.get('Display Name', {}).get('value', None)
|
342
|
+
prop_bod['qualifiedName'] = qualified_name
|
280
343
|
prop_bod["versionIdentifier"] = attributes.get('Version Identifier', {}).get('value', None)
|
281
344
|
prop_bod["summary"] = attributes.get('Summary', {}).get('value', None)
|
345
|
+
prop_bod["description"] = attributes.get('Description', {}).get('value', None)
|
346
|
+
|
282
347
|
prop_bod["scope"] = attributes.get('Scope', {}).get('value', None)
|
283
348
|
prop_bod["importance"] = attributes.get('Importance', {}).get('value', None)
|
284
349
|
prop_bod["implications"] = attributes.get('Implication', {}).get('value', [])
|
285
350
|
prop_bod["outcomes"] = attributes.get('Outcomes', {}).get('value', [])
|
286
351
|
prop_bod["results"] = attributes.get('Results', {}).get('value', [])
|
352
|
+
prop_bod["effectiveFrom"] = attributes.get('Effective From', {}).get('value', None),
|
353
|
+
prop_bod["effectiveTo"] = attributes.get('Effective To', {}).get('value', None),
|
354
|
+
prop_bod["additionalProperties"] = attributes.get('Additional Properties', {}).get('value', None),
|
355
|
+
prop_bod["extendedProperties"] = attributes.get('Extended Properties', {}).get('value', None)
|
287
356
|
|
288
|
-
|
357
|
+
|
358
|
+
body = update_gov_body_for_type(object_type, prop_bod, attributes)
|
289
359
|
return body
|
290
360
|
|
291
361
|
|
292
|
-
def
|
362
|
+
def update_gov_body_for_type(object_type: str, body: dict, attributes: dict) -> dict:
|
293
363
|
gov_def_name = object_type.replace(" ", "")
|
294
364
|
if object_type in GENERAL_GOVERNANCE_DEFINITIONS:
|
295
365
|
return body
|
@@ -298,7 +368,7 @@ def update_body_for_type(object_type: str, body: dict, attributes: dict) -> dict
|
|
298
368
|
return body
|
299
369
|
|
300
370
|
elif object_type == "Regulation":
|
301
|
-
body['
|
371
|
+
body['regulationSource'] = attributes.get('Regulation Source', {}).get('value', None)
|
302
372
|
body['regulators'] = attributes.get('Regulators', {}).get('value', [])
|
303
373
|
return body
|
304
374
|
|
@@ -308,17 +378,24 @@ def update_body_for_type(object_type: str, body: dict, attributes: dict) -> dict
|
|
308
378
|
elif object_type == "Security Group":
|
309
379
|
body['distinguishedName'] = attributes.get('Distinguished Name', {}).get('value', None)
|
310
380
|
return body
|
381
|
+
elif object_type == "GovernanceMetric":
|
382
|
+
body['measurement'] = attributes.get('Measurement', {}).get('value', None)
|
383
|
+
body['target'] = attributes.get('Target', {}).get('value', None)
|
384
|
+
return body
|
311
385
|
elif object_type == "Naming Standard Rule":
|
312
386
|
body['namePatterns'] = attributes.get('Name Patterns', {}).get('value', [])
|
313
387
|
return body
|
314
|
-
elif object_type in ["Certification Type", "License Type"]:
|
315
|
-
body['
|
388
|
+
elif object_type in ["TermsAndConditions", "Certification Type", "License Type"]:
|
389
|
+
body['entitlements'] = attributes.get('Entitlementss', {}).get('value', {})
|
390
|
+
body['restrictions'] = attributes.get('Restrictions', {}).get('value', {})
|
391
|
+
body['obligations'] = attributes.get('Obligations', {}).get('value', {})
|
392
|
+
|
316
393
|
return body
|
317
394
|
|
318
395
|
|
319
396
|
def set_rel_request_body(object_type: str, attributes: dict)->dict:
|
320
397
|
return {
|
321
|
-
"class" : "
|
398
|
+
"class" : "NewRelationshipRequestBody",
|
322
399
|
"externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
|
323
400
|
"externalSourceName": attributes.get('External Source Name', {}).get('value', None),
|
324
401
|
"effectiveTime": attributes.get('Effective Time', {}).get('value', None),
|
@@ -338,9 +415,9 @@ def set_peer_gov_def_request_body(object_type: str, attributes: dict)->dict:
|
|
338
415
|
}
|
339
416
|
return rel_body
|
340
417
|
|
341
|
-
def
|
418
|
+
def set_delete_request_body(object_type: str, attributes: dict)->dict:
|
342
419
|
return {
|
343
|
-
"class": "
|
420
|
+
"class": "DeleteRequestBody",
|
344
421
|
"externalSourceGUID": attributes.get('External Source GUID', {}).get('guid', None),
|
345
422
|
"externalSourceName": attributes.get('External Source Name', {}).get('value', None),
|
346
423
|
"effectiveTime": attributes.get('Effective Time', {}).get('value', None),
|
@@ -371,14 +448,23 @@ def set_element_status_request_body(object_type: str, attributes: dict)->dict:
|
|
371
448
|
"forLineage": attributes.get('For Lineage', {}).get('value', False),
|
372
449
|
"forDuplicateProcessing": attributes.get('For Duplicate Processing', {}).get('value', False)
|
373
450
|
}
|
374
|
-
|
375
|
-
|
376
|
-
|
451
|
+
|
452
|
+
|
453
|
+
def set_classifications(object_type: str, attributes: dict)->dict:
|
454
|
+
classifications = attributes.get('Classifications', {}).get('name_list', None)
|
455
|
+
body = None
|
456
|
+
if classifications:
|
457
|
+
body = {classification: {} for classification in classifications} if cclassifications else {}
|
377
458
|
return body
|
378
459
|
|
379
460
|
def set_collection_classifications(object_type: str, attributes: dict)->dict:
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
461
|
+
classifications = attributes.get('Classifications', {}).get('name_list', None)
|
462
|
+
|
463
|
+
if object_type in ["Folder", "Root"]:
|
464
|
+
if object_type not in classifications:
|
465
|
+
classifications.append(object_type)
|
466
|
+
|
467
|
+
body = None
|
468
|
+
if classifications:
|
469
|
+
body = {classification: {} for classification in classifications} if cclassifications else {}
|
470
|
+
return body
|
@@ -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",
|
@@ -131,8 +132,8 @@ command_list = ["Provenance", "Create Glossary", "Update Glossary", "Create Term
|
|
131
132
|
"Create Digital Product", "Create Data Product", "Update Digital Product", "Update Data Product",
|
132
133
|
"Create Agreement", "Update Agreement",
|
133
134
|
"Link Digital Products", "Link Data Products", "Detach Digital Products", "Detach Data Products",
|
134
|
-
|
135
|
-
"Create Digital Subscription", "Create Product Subscription", "Update Digital Subscription", "Update Product Subscription",
|
135
|
+
"Create Data Sharing Agreement", "Update Data Sharing Agreement",
|
136
|
+
# "Create Digital Subscription", "Create Product Subscription", "Update Digital Subscription", "Update Product Subscription",
|
136
137
|
"Attach Agreement Items", "Detach Agreement Items",
|
137
138
|
"Attach Contract", "Detach Contract",
|
138
139
|
"Attach Subscriber", "Detach Subscriber",
|
@@ -173,6 +174,8 @@ command_list.extend(GOV_COM_LIST)
|
|
173
174
|
command_list.extend(GOV_LINK_LIST)
|
174
175
|
command_list.extend(COLLECTIONS_LIST)
|
175
176
|
command_list.extend(SIMPLE_COLLECTIONS)
|
177
|
+
command_list.extend(["Link Governance Response", "Detach Governance Response",
|
178
|
+
"Link Governance Mechanism", "Detach Governance Mechanism"])
|
176
179
|
|
177
180
|
pre_command = "\n---\n==> Processing object_action:"
|
178
181
|
command_seperator = Markdown("\n---\n")
|
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,
|
pyegeria/_client_new.py
CHANGED
@@ -476,7 +476,8 @@ class Client2:
|
|
476
476
|
except (HTTPStatusError, httpx.HTTPStatusError, httpx.RequestError) as e:
|
477
477
|
# context["caught_exception"] = e
|
478
478
|
# context['HTTPStatusCode'] = e.response.status_code
|
479
|
-
additional_info = {"userid": self.user_id}
|
479
|
+
additional_info = {"userid": self.user_id, "reason": response.text}
|
480
|
+
|
480
481
|
raise PyegeriaClientException(response, context, additional_info, e)
|
481
482
|
#
|
482
483
|
# except json.JSONDecodeError as e:
|
@@ -820,7 +821,7 @@ class Client2:
|
|
820
821
|
"class": "UpdateStatusRequestBody",
|
821
822
|
"status": status
|
822
823
|
}
|
823
|
-
validated_body = UpdateStatusRequestBody.
|
824
|
+
validated_body = UpdateStatusRequestBody.model_validate(body)
|
824
825
|
else:
|
825
826
|
raise PyegeriaInvalidParameterException(additional_info={"reason": "invalid parameters"})
|
826
827
|
|
pyegeria/_exceptions_new.py
CHANGED
@@ -374,6 +374,11 @@ def print_exception_table(e: PyegeriaException):
|
|
374
374
|
def print_basic_exception(e: PyegeriaException):
|
375
375
|
"""Prints the exception response"""
|
376
376
|
related_code = e.related_http_code if hasattr(e, "related_http_code") else ""
|
377
|
+
http_reason = e.response.text if e.response else ""
|
378
|
+
table = Table(title=f"Exception: {e.__class__.__name__}", show_lines=True, header_style="bold", box=box.HEAVY_HEAD)
|
379
|
+
table.caption = e.pyegeria_code
|
380
|
+
table.add_column("Facet", justify="center")
|
381
|
+
table.add_column("Item", justify="left", width=80)
|
377
382
|
related_response = e.response.json() if e.response else ""
|
378
383
|
table = Table(title=f"Exception: {e.__class__.__name__}", show_lines=True, header_style="bold", box=box.HEAVY_HEAD)
|
379
384
|
table.caption = e.pyegeria_code
|
@@ -382,6 +387,7 @@ def print_basic_exception(e: PyegeriaException):
|
|
382
387
|
|
383
388
|
if isinstance(e, PyegeriaException):
|
384
389
|
table.add_row("HTTP Code", str(e.response_code))
|
390
|
+
table.add_row("HTTP Reason", str(http_reason))
|
385
391
|
table.add_row("Egeria Code", str(related_code))
|
386
392
|
table.add_row("Caller Method", e.context.get("caller method", "---")) if e.context else ""
|
387
393
|
table.add_row("Request URL", str(e.response_url))
|
pyegeria/_output_formats.py
CHANGED
@@ -163,7 +163,11 @@ GOVERNANCE_DEFINITIONS_COLUMNS = COMMON_COLUMNS + [
|
|
163
163
|
Column(name="Scope", key='scope'),
|
164
164
|
Column(name="Type", key='type_name'),
|
165
165
|
]
|
166
|
-
|
166
|
+
GOVERNANCE_DEFINITIONS_BASIC = [
|
167
|
+
Column(name="Type", key='type_name'),
|
168
|
+
Column(name='Qualified Name', key='qualified_name', format=True),
|
169
|
+
Column(name="GUID", key='guid', format=True),
|
170
|
+
]
|
167
171
|
COMMON_ANNOTATIONS = {
|
168
172
|
"wikilinks": ["[[Commons]]"]
|
169
173
|
}
|
@@ -406,7 +410,18 @@ output_format_sets = FormatSetDict({
|
|
406
410
|
spec_params={"output_format":"DICT"},
|
407
411
|
)
|
408
412
|
),
|
409
|
-
|
413
|
+
"Governance Basics": FormatSet(
|
414
|
+
heading="Basic Governance-Definitions Information",
|
415
|
+
description="Core Attributes useful to Governance-Definitions.",
|
416
|
+
aliases=["BasicGovernance"],
|
417
|
+
annotations={"wikilinks": ["[[Governance]]"]},
|
418
|
+
formats=[Format(types=["ALL"], columns=GOVERNANCE_DEFINITIONS_BASIC)],
|
419
|
+
action=ActionParameter(
|
420
|
+
function="GovernanceOfficer.find_governance_definitions",
|
421
|
+
user_params=["search_string"],
|
422
|
+
spec_params={},
|
423
|
+
)
|
424
|
+
),
|
410
425
|
"Governance Definitions": FormatSet(
|
411
426
|
heading="Governance-Definitions Information",
|
412
427
|
description="Attributes useful to Governance-Definitions.",
|
pyegeria/governance_officer.py
CHANGED
@@ -726,8 +726,6 @@ class GovernanceOfficer(Client2):
|
|
726
726
|
|
727
727
|
@dynamic_catch
|
728
728
|
async def _async_update_governance_definition(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
|
729
|
-
""" Update a governance definition."""
|
730
|
-
|
731
729
|
""" Update the properties of a governance definition. Async Version.
|
732
730
|
|
733
731
|
Parameters
|
@@ -793,7 +791,7 @@ class GovernanceOfficer(Client2):
|
|
793
791
|
await self._async_update_element_body_request(url, GOV_DEF_PROPERTIES_LIST, body)
|
794
792
|
|
795
793
|
@dynamic_catch
|
796
|
-
def _async_update_governance_definition(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
|
794
|
+
async def _async_update_governance_definition(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
|
797
795
|
""" Update a governance definition. Async Version."""
|
798
796
|
|
799
797
|
def update_governance_definition(self, guid: str, body: dict | UpdateStatusRequestBody) -> None:
|
pyegeria/load_config.py
CHANGED
@@ -381,7 +381,7 @@ def load_app_config(env_file: str = None):
|
|
381
381
|
|
382
382
|
# Logging section
|
383
383
|
log = config_dict["Logging"]
|
384
|
-
log["console_filter_levels"] = _parse_list_env("PYEGERIA_CONSOLE_FILTER_LEVELS", log.get("console_filter_levels", ["ERROR"]))
|
384
|
+
log["console_filter_levels"] = _parse_list_env("PYEGERIA_CONSOLE_FILTER_LEVELS", log.get("console_filter_levels", ["ERROR","WARNING","INFO","SUCCESS"]))
|
385
385
|
log["console_logging_enabled"] = _parse_list_env("PYEGERIA_CONSOLE_LOGGING_ENABLED", log.get("console_logging_enabled", ["pyegeria"]))
|
386
386
|
log["console_logging_level"] = os.getenv("PYEGERIA_CONSOLE_LOG_LVL", log.get("console_logging_level", "INFO"))
|
387
387
|
log["enable_logging"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGING", log.get("enable_logging", False))
|
pyegeria/models.py
CHANGED
@@ -177,6 +177,33 @@ class OpenMetadataRootProperties(PyegeriaModel):
|
|
177
177
|
type_name: str | None = None
|
178
178
|
extended_properties: dict | None = None
|
179
179
|
|
180
|
+
@model_validator(mode='before')
|
181
|
+
@classmethod
|
182
|
+
def preprocess_data(cls, values):
|
183
|
+
"""
|
184
|
+
This model validator performs pre-processing on the entire
|
185
|
+
input dictionary before field validation.
|
186
|
+
It converts any empty tuples to None and ensures a metadata key exists.
|
187
|
+
|
188
|
+
This is the modern equivalent of @root_validator(pre=True).
|
189
|
+
"""
|
190
|
+
# Ensure the 'data' key exists before trying to access it.
|
191
|
+
if 'data' in values and isinstance(values['data'], dict):
|
192
|
+
# Convert empty tuples to None
|
193
|
+
processed_data = {}
|
194
|
+
for key, value in values['data'].items():
|
195
|
+
if isinstance(value, tuple) and not value:
|
196
|
+
processed_data[key] = None
|
197
|
+
else:
|
198
|
+
processed_data[key] = value
|
199
|
+
values['data'] = processed_data
|
200
|
+
|
201
|
+
# Ensure a 'metadata' key is always a dictionary.
|
202
|
+
if 'metadata' not in values:
|
203
|
+
values['metadata'] = {}
|
204
|
+
|
205
|
+
return values
|
206
|
+
|
180
207
|
|
181
208
|
class RelationshipBeanProperties(PyegeriaModel):
|
182
209
|
effective_from: datetime | None = None
|
@@ -311,7 +338,7 @@ class UpdateStatusRequestBody(PyegeriaModel):
|
|
311
338
|
|
312
339
|
class GetRequestBody(PyegeriaModel):
|
313
340
|
class_: Annotated[Literal["GetRequestBody"], Field(alias="class")]
|
314
|
-
|
341
|
+
metadata_element_type_name: str | None = None
|
315
342
|
metadata_element_subtype_names: list[str] | None = None
|
316
343
|
skip_relationships: list[str] | None = None
|
317
344
|
include_only_relationships: list[str] | None = None
|
pyegeria/utils.py
CHANGED
@@ -140,7 +140,7 @@ def body_slimmer(body: dict) -> dict:
|
|
140
140
|
|
141
141
|
slimmed = {}
|
142
142
|
for key, value in body.items():
|
143
|
-
if value:
|
143
|
+
if value and not isinstance(value, tuple):
|
144
144
|
if isinstance(value, dict):
|
145
145
|
# Recursively slim embedded dictionaries
|
146
146
|
slimmed_value = body_slimmer(value)
|