pyegeria 5.3.9.9.3__py3-none-any.whl → 5.3.9.9.5__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.
- md_processing/__init__.py +49 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
- md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
- md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
- md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
- md_processing/dr_egeria_inbox/cat_test.md +100 -0
- md_processing/dr_egeria_inbox/commands.json +3252 -0
- md_processing/dr_egeria_inbox/data_field.md +54 -0
- md_processing/dr_egeria_inbox/data_spec.md +77 -0
- md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
- md_processing/dr_egeria_inbox/data_test.md +86 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +313 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
- md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
- md_processing/dr_egeria_inbox/glossary_creation_experiment.ipynb +341 -0
- md_processing/dr_egeria_inbox/glossary_test1.md +324 -0
- md_processing/dr_egeria_inbox/rel.md +8 -0
- md_processing/dr_egeria_inbox/sb.md +119 -0
- md_processing/dr_egeria_inbox/search_test.md +39 -0
- md_processing/dr_egeria_inbox/solution-components.md +154 -0
- md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
- md_processing/dr_egeria_inbox/synonym_test.md +42 -0
- md_processing/dr_egeria_inbox/t1.md +0 -0
- md_processing/dr_egeria_inbox/t2.md +268 -0
- md_processing/dr_egeria_outbox/processed-2025-05-15 19:52-data_test.md +94 -0
- md_processing/dr_egeria_outbox/processed-2025-05-16 07:39-data_test.md +88 -0
- md_processing/dr_egeria_outbox/processed-2025-05-17 16:01-data_field.md +56 -0
- md_processing/dr_egeria_outbox/processed-2025-05-18 15:51-data_test.md +103 -0
- md_processing/dr_egeria_outbox/processed-2025-05-18 16:47-data_test.md +94 -0
- md_processing/dr_egeria_outbox/processed-2025-05-19 07:14-data_test.md +96 -0
- md_processing/dr_egeria_outbox/processed-2025-05-19 07:20-data_test.md +100 -0
- md_processing/dr_egeria_outbox/processed-2025-05-19 07:22-data_test.md +88 -0
- md_processing/dr_egeria_outbox/processed-2025-05-19 09:26-data_test.md +91 -0
- md_processing/dr_egeria_outbox/processed-2025-05-19 10:27-data_test.md +91 -0
- md_processing/md_commands/__init__.py +3 -0
- md_processing/md_commands/blueprint_commands.py +303 -0
- md_processing/md_commands/data_designer_commands.py +1183 -0
- md_processing/md_commands/glossary_commands.py +1144 -0
- md_processing/md_commands/project_commands.py +163 -0
- md_processing/md_processing_utils/__init__.py +4 -0
- md_processing/md_processing_utils/common_md_proc_utils.py +724 -0
- md_processing/md_processing_utils/common_md_utils.py +172 -0
- md_processing/md_processing_utils/extraction_utils.py +486 -0
- md_processing/md_processing_utils/md_processing_constants.py +122 -0
- md_processing/md_processing_utils/message_constants.py +19 -0
- pyegeria/data_designer_omvs.py +4 -3
- {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.3.9.9.5.dist-info}/METADATA +1 -2
- {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.3.9.9.5.dist-info}/RECORD +58 -6
- {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.3.9.9.5.dist-info}/LICENSE +0 -0
- {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.3.9.9.5.dist-info}/WHEEL +0 -0
- {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.3.9.9.5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,724 @@
|
|
1
|
+
"""
|
2
|
+
This file contains general utility functions for processing Egeria Markdown
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
from typing import List
|
7
|
+
|
8
|
+
from rich.console import Console
|
9
|
+
from rich import print
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
from md_processing.md_processing_utils.common_md_utils import (get_current_datetime_string, print_msg,
|
14
|
+
get_element_dictionary, update_element_dictionary,
|
15
|
+
split_tb_string, str_to_bool, )
|
16
|
+
from md_processing.md_processing_utils.extraction_utils import (process_simple_attribute, extract_attribute,
|
17
|
+
get_element_by_name)
|
18
|
+
from md_processing.md_processing_utils.md_processing_constants import (get_command_spec)
|
19
|
+
from md_processing.md_processing_utils.message_constants import (ERROR, INFO, WARNING, ALWAYS, EXISTS_REQUIRED)
|
20
|
+
from pyegeria import EgeriaTech
|
21
|
+
from pyegeria._globals import DEBUG_LEVEL
|
22
|
+
|
23
|
+
# Constants
|
24
|
+
EGERIA_WIDTH = int(os.environ.get("EGERIA_WIDTH", "200"))
|
25
|
+
EGERIA_USAGE_LEVEL = os.environ.get("EGERIA_USAGE_LEVEL", "Basic")
|
26
|
+
console = Console(width=EGERIA_WIDTH)
|
27
|
+
|
28
|
+
debug_level = DEBUG_LEVEL
|
29
|
+
global COMMAND_DEFINITIONS
|
30
|
+
|
31
|
+
|
32
|
+
def process_provenance_command(file_path: str, txt: [str]) -> str:
|
33
|
+
"""
|
34
|
+
Processes a provenance object_action by extracting the file path and current datetime.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
file_path: The path to the file being processed.
|
38
|
+
txt: The text containing the provenance object_action.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
A string containing the provenance information.
|
42
|
+
"""
|
43
|
+
now = get_current_datetime_string()
|
44
|
+
file_name = os.path.basename(file_path)
|
45
|
+
provenance = f"\n\n\n# Provenance:\n \n* Derived from processing file {file_name} on {now}\n"
|
46
|
+
return provenance
|
47
|
+
|
48
|
+
|
49
|
+
def parse_user_command(egeria_client: EgeriaTech, object_type: str, object_action: str, txt: str,
|
50
|
+
directive: str = "display") -> dict:
|
51
|
+
parsed_attributes, parsed_output = {}, {}
|
52
|
+
|
53
|
+
parsed_output['valid'] = True
|
54
|
+
parsed_output['exists'] = False
|
55
|
+
parsed_output['display'] = ""
|
56
|
+
display_name = ""
|
57
|
+
labels = {}
|
58
|
+
|
59
|
+
command_spec = get_command_spec(object_type)
|
60
|
+
attributes = command_spec.get('Attributes', [])
|
61
|
+
command_display_name = command_spec.get('display_name', None)
|
62
|
+
command_qn_prefix = command_spec.get('qn_prefix', None)
|
63
|
+
|
64
|
+
parsed_output['display_name'] = command_display_name
|
65
|
+
parsed_output['qn_prefix'] = command_qn_prefix
|
66
|
+
|
67
|
+
parsed_output['is_own_anchor'] = command_spec.get('isOwnAnchor', True)
|
68
|
+
|
69
|
+
parsed_output['reason'] = ""
|
70
|
+
|
71
|
+
msg = f"\tProcessing {object_action} on a {object_type} \n"
|
72
|
+
print_msg(ALWAYS, msg, debug_level)
|
73
|
+
|
74
|
+
# get the version early because we may need it to construct qualified names.
|
75
|
+
version = process_simple_attribute(txt, {'Version', "Version Identifier", "Published Version"}, INFO)
|
76
|
+
parsed_output['version'] = version
|
77
|
+
|
78
|
+
for attr in attributes:
|
79
|
+
for key in attr:
|
80
|
+
# Run some checks to see if the attribute is appropriate to the operation and usage level
|
81
|
+
for_update = attr[key].get('inUpdate', True)
|
82
|
+
level = attr[key].get('level', 'Basic')
|
83
|
+
msg = (f"___\nProcessing `{key}` in `{object_action}` on a `{object_type}` "
|
84
|
+
f"\n\twith usage level: `{EGERIA_USAGE_LEVEL}` and attribute level `{level}` and for_update `{for_update}`\n")
|
85
|
+
print_msg(INFO, msg, debug_level)
|
86
|
+
if for_update is False and object_action == "Update":
|
87
|
+
console.print(f"Attribute `{key}`is not allowed for `Update`", highlight=True)
|
88
|
+
continue
|
89
|
+
if EGERIA_USAGE_LEVEL == "Basic" and level != "Basic":
|
90
|
+
console.print(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.", highlight=True)
|
91
|
+
continue
|
92
|
+
if EGERIA_USAGE_LEVEL == "Advanced" and level in [ "Expert", "Invisible"]:
|
93
|
+
console.print(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.", highlight=True)
|
94
|
+
continue
|
95
|
+
if EGERIA_USAGE_LEVEL == "Expert" and level == "Invisible":
|
96
|
+
console.print(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.", highlight=True)
|
97
|
+
continue
|
98
|
+
|
99
|
+
|
100
|
+
if attr[key].get('input_required', False) is True:
|
101
|
+
if_missing = ERROR
|
102
|
+
else:
|
103
|
+
if_missing = INFO
|
104
|
+
|
105
|
+
# lab = [item.strip() for item in re.split(r'[;,\n]+',attr[key]['attr_labels'])]
|
106
|
+
lab =split_tb_string(attr[key]['attr_labels'] )
|
107
|
+
labels: set = set()
|
108
|
+
labels.add(key.strip())
|
109
|
+
if key == 'Display Name':
|
110
|
+
labels.add(object_type.strip())
|
111
|
+
if lab is not None and lab != [""]:
|
112
|
+
labels.update(lab)
|
113
|
+
|
114
|
+
|
115
|
+
default_value = attr[key].get('default_value', None)
|
116
|
+
|
117
|
+
style = attr[key]['style']
|
118
|
+
if style in ['Simple', 'Dictionary', 'Comment']:
|
119
|
+
parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
|
120
|
+
elif style == 'Valid Value':
|
121
|
+
parsed_attributes[key] = proc_valid_value(txt, object_action, labels,
|
122
|
+
attr[key].get('valid_values', None), if_missing,
|
123
|
+
default_value)
|
124
|
+
elif style == 'QN':
|
125
|
+
parsed_attributes[key] = proc_el_id(egeria_client, command_display_name, command_qn_prefix, labels, txt,
|
126
|
+
object_action, version, if_missing)
|
127
|
+
if key == 'Qualified Name' and parsed_attributes[key]['value'] and parsed_attributes[key]['exists'] is False:
|
128
|
+
parsed_output['exists'] = False
|
129
|
+
elif style == 'ID':
|
130
|
+
parsed_attributes[key] = proc_el_id(egeria_client, command_display_name, command_qn_prefix, labels, txt,
|
131
|
+
object_action, version, if_missing)
|
132
|
+
|
133
|
+
parsed_output['guid'] = parsed_attributes[key].get('guid', None)
|
134
|
+
parsed_output['qualified_name'] = parsed_attributes[key].get('qualified_name', None)
|
135
|
+
parsed_output['exists'] = parsed_attributes[key]['exists']
|
136
|
+
if parsed_attributes[key]['valid'] is False:
|
137
|
+
parsed_output['valid'] = False
|
138
|
+
parsed_output['reason'] += parsed_attributes[key]['reason']
|
139
|
+
|
140
|
+
elif style == 'Reference Name':
|
141
|
+
parsed_attributes[key] = proc_ids(egeria_client, key, labels, txt, object_action, if_missing)
|
142
|
+
if ((if_missing==ERROR) and parsed_attributes[key].get("value", None) and
|
143
|
+
parsed_attributes[key]['exists'] is False):
|
144
|
+
msg = f"Reference Name `{parsed_attributes[key]['value']}` is specified but does not exist"
|
145
|
+
print_msg(ERROR, msg, debug_level)
|
146
|
+
parsed_output['valid'] = False
|
147
|
+
parsed_output['reason'] += msg
|
148
|
+
|
149
|
+
elif style == 'GUID':
|
150
|
+
parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing)
|
151
|
+
elif style == 'Ordered Int':
|
152
|
+
parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing)
|
153
|
+
elif style == 'Simple Int':
|
154
|
+
parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
|
155
|
+
elif style == 'Simple List':
|
156
|
+
parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
|
157
|
+
name_list = parsed_attributes[key]['value']
|
158
|
+
# attribute = re.split(r'[;,\n]+', name_list) if name_list is not None else None
|
159
|
+
attribute = split_tb_string(name_list)
|
160
|
+
parsed_attributes[key]['value'] = attribute
|
161
|
+
parsed_attributes[key]['name_list'] = name_list
|
162
|
+
elif style == 'Parent':
|
163
|
+
parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
|
164
|
+
elif style == 'Bool':
|
165
|
+
parsed_attributes[key] = proc_bool_attribute(txt, object_action, labels, if_missing, default_value)
|
166
|
+
|
167
|
+
|
168
|
+
elif style == 'Reference Name List':
|
169
|
+
parsed_attributes[key] = proc_name_list(egeria_client, key, txt, labels, if_missing)
|
170
|
+
|
171
|
+
else:
|
172
|
+
msg = f"Unknown attribute style: {style}"
|
173
|
+
print_msg(ERROR, msg, debug_level)
|
174
|
+
sys.exit(1)
|
175
|
+
parsed_attributes[key]['valid'] = False
|
176
|
+
parsed_attributes[key]['value'] = None
|
177
|
+
if key == "Display Name":
|
178
|
+
display_name = parsed_attributes[key]['value']
|
179
|
+
|
180
|
+
|
181
|
+
|
182
|
+
value = parsed_attributes[key].get('value',None)
|
183
|
+
|
184
|
+
if value is not None:
|
185
|
+
# if the value is a dict, get the flattened name list
|
186
|
+
value = parsed_attributes[key].get('name_list', None) if isinstance(value,(dict, list)) else value
|
187
|
+
|
188
|
+
parsed_output['display'] += f"\n\t* {key}: `{value}`\n\t"
|
189
|
+
|
190
|
+
|
191
|
+
if parsed_attributes.get('Parent ID', {}).get('value', None) is not None:
|
192
|
+
if (parsed_attributes['Parent Relationship Type Name']['value'] is None) or (
|
193
|
+
parsed_attributes['Parent at End1']['value'] is None):
|
194
|
+
msg = "Parent ID was found but either Parent `Relationship Type Name` or `Parent at End1` are missing"
|
195
|
+
print_msg(ERROR, msg, debug_level)
|
196
|
+
parsed_output['valid'] = False
|
197
|
+
parsed_output['reason'] = msg
|
198
|
+
if parsed_attributes['Parent Relationship Type Name'].get('exists', False) is False:
|
199
|
+
msg = "Parent ID was found but does not exist"
|
200
|
+
print_msg(ERROR, msg, debug_level)
|
201
|
+
parsed_output['valid'] = False
|
202
|
+
parsed_output['reason'] = msg
|
203
|
+
|
204
|
+
if directive in ["validate", "process"] and object_action == "Update" and not parsed_output[
|
205
|
+
'exists']: # check to see if provided information exists and is consistent with existing info
|
206
|
+
msg = f"Update request invalid, Term {display_name} does not exist\n"
|
207
|
+
print_msg(ERROR, msg, debug_level)
|
208
|
+
parsed_output['valid'] = False
|
209
|
+
if directive in ["validate", "process"] and not parsed_output['valid'] and object_action == "Update":
|
210
|
+
msg = f"Request is invalid, `{object_action} {object_type}` is not valid - see previous messages\n"
|
211
|
+
print_msg(ERROR, msg, debug_level)
|
212
|
+
|
213
|
+
elif directive in ["validate",
|
214
|
+
"process"] and object_action == 'Create': # if the object_action is create, check that it
|
215
|
+
# doesn't already exist
|
216
|
+
if parsed_output['exists']:
|
217
|
+
msg = f"Element `{display_name}` cannot be created since it already exists\n"
|
218
|
+
print_msg(ERROR, msg, debug_level)
|
219
|
+
parsed_output['valid'] = False
|
220
|
+
else:
|
221
|
+
msg = f"It is valid to create Element `{display_name}`"
|
222
|
+
print_msg(ALWAYS, msg, debug_level)
|
223
|
+
|
224
|
+
parsed_output['attributes'] = parsed_attributes
|
225
|
+
return parsed_output
|
226
|
+
|
227
|
+
|
228
|
+
def proc_simple_attribute(txt: str, action: str, labels: set, if_missing: str = INFO, default_value=None) -> dict:
|
229
|
+
"""Process a simple attribute based on the provided labels and if_missing value.
|
230
|
+
Extract the attribute value from the text and store it in a dictionary along with valid.
|
231
|
+
If it doesn`t exist, mark the dictionary entry as invalid and print an error message with severity of if_missing.
|
232
|
+
|
233
|
+
Parameters:
|
234
|
+
----------
|
235
|
+
txt: str
|
236
|
+
The block of object_action text to extract attributes from.
|
237
|
+
labels: list
|
238
|
+
The possible attribute labels to search for. The first label will be used in messages.
|
239
|
+
if_missing: str, default is INFO
|
240
|
+
Can be one of "WARNING", "ERROR", "INFO". The severity of the missing attribute.
|
241
|
+
default_value: default is None
|
242
|
+
The default value to return if the attribute is missing.
|
243
|
+
"""
|
244
|
+
valid = True
|
245
|
+
|
246
|
+
if if_missing not in ["WARNING", "ERROR", "INFO"]:
|
247
|
+
msg = "Invalid severity for missing attribute"
|
248
|
+
print_msg("ERROR", msg, debug_level)
|
249
|
+
return {"status": ERROR, "reason": msg, "value": None, "valid": False}
|
250
|
+
|
251
|
+
attribute = extract_attribute(txt, labels)
|
252
|
+
if default_value == "":
|
253
|
+
default_value = None
|
254
|
+
attribute = default_value if attribute is None else attribute
|
255
|
+
|
256
|
+
if attribute is None:
|
257
|
+
|
258
|
+
if if_missing == INFO or if_missing == WARNING:
|
259
|
+
msg = f"Optional attribute with labels: `{labels}` missing"
|
260
|
+
valid = True
|
261
|
+
else:
|
262
|
+
msg = f"Missing attribute with labels `{labels}` "
|
263
|
+
valid = False
|
264
|
+
print_msg(if_missing, msg, debug_level)
|
265
|
+
return {"status": if_missing, "reason": msg, "value": None, "valid": valid, "exists": False}
|
266
|
+
return {"status": INFO, "OK": None, "value": attribute, "valid": valid, "exists": True}
|
267
|
+
|
268
|
+
|
269
|
+
def proc_valid_value(txt: str, action: str, labels: set, valid_values: [], if_missing: str = INFO,
|
270
|
+
default_value=None) -> dict:
|
271
|
+
"""Process a string attribute to check that it is a member of the associated value values list.
|
272
|
+
Extract the attribute value from the text and store it in a dictionary along with valid.
|
273
|
+
If it doesn`t exist, mark the dictionary entry as invalid and print an error message with severity of if_missing.
|
274
|
+
|
275
|
+
Parameters:
|
276
|
+
----------
|
277
|
+
txt: str
|
278
|
+
The block of object_action text to extract attributes from.
|
279
|
+
labels: list
|
280
|
+
The possible attribute labels to search for. The first label will be used in messages.
|
281
|
+
if_missing: str, default is INFO
|
282
|
+
Can be one of "WARNING", "ERROR", "INFO". The severity of the missing attribute.
|
283
|
+
default_value: default is None
|
284
|
+
The default value to return if the attribute is missing.
|
285
|
+
"""
|
286
|
+
valid = True
|
287
|
+
v_values = []
|
288
|
+
|
289
|
+
if if_missing not in ["WARNING", "ERROR", "INFO"]:
|
290
|
+
msg = "Invalid severity for missing attribute"
|
291
|
+
print_msg("ERROR", msg, debug_level)
|
292
|
+
return {"status": ERROR, "reason": msg, "value": None, "valid": False}
|
293
|
+
if valid_values is None:
|
294
|
+
msg = "Missing valid values list"
|
295
|
+
print_msg("ERROR", msg, debug_level)
|
296
|
+
return {"status": WARNING, "reason": msg, "value": None, "valid": False}
|
297
|
+
if isinstance(valid_values, str):
|
298
|
+
# v_values = [item.strip() for item in re.split(r'[;,\n]+', valid_values)]
|
299
|
+
v_values = split_tb_string(valid_values)
|
300
|
+
if not isinstance(v_values, list):
|
301
|
+
msg = "Valid values list is not a list"
|
302
|
+
print_msg("ERROR", msg, debug_level)
|
303
|
+
return {"status": WARNING, "reason": msg, "value": None, "valid": False}
|
304
|
+
if len(v_values) == 0:
|
305
|
+
msg = "Valid values list is empty"
|
306
|
+
print_msg("ERROR", msg, debug_level)
|
307
|
+
return {"status": WARNING, "reason": msg, "value": None, "valid": False}
|
308
|
+
|
309
|
+
attribute = extract_attribute(txt, labels)
|
310
|
+
if default_value == "":
|
311
|
+
default_value = None
|
312
|
+
attribute = default_value if attribute is None else attribute
|
313
|
+
|
314
|
+
if attribute is None:
|
315
|
+
if if_missing == INFO or if_missing == WARNING:
|
316
|
+
msg = f"Optional attribute with labels: `{labels}` missing"
|
317
|
+
valid = True
|
318
|
+
else:
|
319
|
+
msg = f"Missing attribute with labels `{labels}` "
|
320
|
+
valid = False
|
321
|
+
print_msg(if_missing, msg, debug_level)
|
322
|
+
return {"status": if_missing, "reason": msg, "value": None, "valid": valid, "exists": False}
|
323
|
+
else:
|
324
|
+
if attribute not in v_values:
|
325
|
+
msg = f"Invalid value for attribute `{labels}`"
|
326
|
+
print_msg(WARNING, msg, debug_level)
|
327
|
+
return {"status": WARNING, "reason": msg, "value": attribute, "valid": False, "exists": True}
|
328
|
+
|
329
|
+
return {"status": INFO, "OK": "OK", "value": attribute, "valid": valid, "exists": True}
|
330
|
+
|
331
|
+
|
332
|
+
def proc_bool_attribute(txt: str, action: str, labels: set, if_missing: str = INFO, default_value=None) -> dict:
|
333
|
+
"""Process a boolean attribute based on the provided labels and if_missing value.
|
334
|
+
Extract the attribute value from the text and store it in a dictionary along with valid.
|
335
|
+
If it doesn`t exist, mark the dictionary entry as invalid and print an error message with severity of if_missing.
|
336
|
+
|
337
|
+
Parameters:
|
338
|
+
----------
|
339
|
+
txt: str
|
340
|
+
The block of object_action text to extract attributes from.
|
341
|
+
labels: list
|
342
|
+
The possible attribute labels to search for. The first label will be used in messages.
|
343
|
+
if_missing: str, default is INFO
|
344
|
+
Can be one of "WARNING", "ERROR", "INFO". The severity of the missing attribute.
|
345
|
+
default_value: default is None
|
346
|
+
The default value to return if the attribute is missing.
|
347
|
+
"""
|
348
|
+
valid = True
|
349
|
+
|
350
|
+
if if_missing not in ["WARNING", "ERROR", "INFO"]:
|
351
|
+
msg = "Invalid severity for missing attribute"
|
352
|
+
print_msg("ERROR", msg, debug_level)
|
353
|
+
return {"status": ERROR, "reason": msg, "value": None, "valid": False}
|
354
|
+
|
355
|
+
attribute = extract_attribute(txt, labels)
|
356
|
+
if default_value == "":
|
357
|
+
default = None
|
358
|
+
else:
|
359
|
+
default = str_to_bool(default_value)
|
360
|
+
attribute = default if attribute is None else attribute
|
361
|
+
|
362
|
+
if attribute is None:
|
363
|
+
if if_missing == INFO or if_missing == WARNING:
|
364
|
+
msg = f"Optional attribute with labels: `{labels}` missing"
|
365
|
+
valid = True
|
366
|
+
else:
|
367
|
+
msg = f"Missing attribute with labels `{labels}` "
|
368
|
+
valid = False
|
369
|
+
print_msg(if_missing, msg, debug_level)
|
370
|
+
return {"status": if_missing, "reason": msg, "value": None, "valid": valid, "exists": False}
|
371
|
+
|
372
|
+
if isinstance(attribute, str):
|
373
|
+
attribute = attribute.strip().lower()
|
374
|
+
if attribute in ["true", "yes", "1"]:
|
375
|
+
attribute = True
|
376
|
+
elif attribute in ["false", "no", "0"]:
|
377
|
+
attribute = False
|
378
|
+
else:
|
379
|
+
msg = f"Invalid value for boolean attribute `{labels}`"
|
380
|
+
print_msg("ERROR", msg, debug_level)
|
381
|
+
return {"status": ERROR, "reason": msg, "value": attribute, "valid": False, "exists": True}
|
382
|
+
|
383
|
+
return {"status": INFO, "OK": None, "value": attribute, "valid": valid, "exists": True}
|
384
|
+
|
385
|
+
|
386
|
+
def proc_el_id(egeria_client: EgeriaTech, element_type: str, qn_prefix: str, element_labels: list[str], txt: str,
|
387
|
+
action: str, version: str = None, if_missing: str = INFO) -> dict:
|
388
|
+
"""
|
389
|
+
Processes display_name and qualified_name by extracting them from the input text,
|
390
|
+
checking if the element exists in Egeria, and validating the information. If a qualified
|
391
|
+
name isn't found, one will be created.
|
392
|
+
|
393
|
+
Parameters
|
394
|
+
----------
|
395
|
+
egeria_client: EgeriaTech
|
396
|
+
Client object for interacting with Egeria.
|
397
|
+
element_type: str
|
398
|
+
type of element to process (e.g., 'blueprint', 'category', 'term')
|
399
|
+
element_labels: a list of equivalent label names to use in processing the element.
|
400
|
+
txt: str
|
401
|
+
A string representing the input text to be processed for extracting element identifiers.
|
402
|
+
action: str
|
403
|
+
The action object_action to be executed (e.g., 'Create', 'Update', 'Display', ...)
|
404
|
+
version: str, optional = None
|
405
|
+
An optional version identifier used if we need to construct the qualified name
|
406
|
+
|
407
|
+
Returns: dict with keys:
|
408
|
+
status
|
409
|
+
reason
|
410
|
+
value - value of display name
|
411
|
+
valid - name we parse out
|
412
|
+
exists
|
413
|
+
qualified_name - qualified name - either that we find or the one we construct
|
414
|
+
guid - guid of the element if it already exists
|
415
|
+
"""
|
416
|
+
valid = True
|
417
|
+
exists = False
|
418
|
+
identifier_output = {}
|
419
|
+
|
420
|
+
element_name = extract_attribute(txt, element_labels)
|
421
|
+
qualified_name = extract_attribute(txt, ["Qualified Name"])
|
422
|
+
|
423
|
+
if element_name is None:
|
424
|
+
msg = f"Optional attribute with label`{element_type}` missing"
|
425
|
+
print_msg("INFO", msg, debug_level)
|
426
|
+
identifier_output = {"status": INFO, "reason": msg, "value": None, "valid": False, "exists": False, }
|
427
|
+
return identifier_output
|
428
|
+
|
429
|
+
if qualified_name:
|
430
|
+
q_name, guid, unique, exists = get_element_by_name(egeria_client, element_type,
|
431
|
+
qualified_name) # Qualified name could be different if it
|
432
|
+
# is being updated
|
433
|
+
else:
|
434
|
+
q_name, guid, unique, exists = get_element_by_name(egeria_client, element_type, element_name)
|
435
|
+
qualified_name = q_name
|
436
|
+
|
437
|
+
if unique is False:
|
438
|
+
msg = f"Multiple elements named {element_name} found"
|
439
|
+
print_msg("DEBUG-ERROR", msg, debug_level)
|
440
|
+
identifier_output = {"status": ERROR, "reason": msg, "value": element_name, "valid": False, "exists": True, }
|
441
|
+
valid = False
|
442
|
+
|
443
|
+
if action == "Update" and not exists:
|
444
|
+
msg = f"Element {element_name} does not exist"
|
445
|
+
print_msg("DEBUG-ERROR", msg, debug_level)
|
446
|
+
identifier_output = {"status": ERROR, "reason": msg, "value": element_name, "valid": False, "exists": False, }
|
447
|
+
|
448
|
+
elif action == "Update" and exists:
|
449
|
+
msg = f"Element {element_name} exists"
|
450
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
451
|
+
identifier_output = {
|
452
|
+
"status": INFO, "reason": msg, "value": element_name, "valid": True, "exists": True,
|
453
|
+
'qualified_name': q_name, 'guid': guid
|
454
|
+
}
|
455
|
+
|
456
|
+
elif action == "Create" and exists:
|
457
|
+
msg = f"Element {element_name} already exists"
|
458
|
+
print_msg("DEBUG-ERROR", msg, debug_level)
|
459
|
+
identifier_output = {
|
460
|
+
"status": ERROR, "reason": msg, "value": element_name, "valid": False, "exists": True,
|
461
|
+
'qualified_name': qualified_name, 'guid': guid,
|
462
|
+
}
|
463
|
+
|
464
|
+
elif action == "Create" and not exists and valid:
|
465
|
+
msg = f"{element_type} `{element_name}` does not exist"
|
466
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
467
|
+
|
468
|
+
if q_name is None and qualified_name is None:
|
469
|
+
q_name = egeria_client.__create_qualified_name__(qn_prefix, element_name, version_identifier=version)
|
470
|
+
update_element_dictionary(q_name, {'display_name': element_name})
|
471
|
+
|
472
|
+
elif qualified_name:
|
473
|
+
update_element_dictionary(qualified_name, {'display_name': element_name})
|
474
|
+
q_name = qualified_name
|
475
|
+
|
476
|
+
identifier_output = {
|
477
|
+
"status": INFO, "reason": msg, "value": element_name, "valid": True, "exists": False,
|
478
|
+
'qualified_name': q_name
|
479
|
+
}
|
480
|
+
|
481
|
+
return identifier_output
|
482
|
+
|
483
|
+
|
484
|
+
def proc_ids(egeria_client: EgeriaTech, element_type: str, element_labels: set, txt: str, action: str,
|
485
|
+
if_missing: str = INFO, version: str = None) -> dict:
|
486
|
+
"""
|
487
|
+
Processes element identifiers from the input text using the labels supplied,
|
488
|
+
checking if the element exists in Egeria, and validating the information.
|
489
|
+
|
490
|
+
Parameters
|
491
|
+
----------
|
492
|
+
egeria_client: EgeriaTech
|
493
|
+
Client object for interacting with Egeria.
|
494
|
+
element_type: str
|
495
|
+
type of element to process (e.g., 'blueprint', 'category', 'term')
|
496
|
+
element_labels: a list of equivalent label names to use in processing the element.
|
497
|
+
txt: str
|
498
|
+
A string representing the input text to be processed for extracting element identifiers.
|
499
|
+
action: str
|
500
|
+
The action object_action to be executed (e.g., 'Create', 'Update', 'Display', ...)
|
501
|
+
if_missing: str, optional = None
|
502
|
+
Optional version identifier used if we need to construct the qualified name
|
503
|
+
version: str, optional = INFO
|
504
|
+
What to do if the element doesn't exist. Default is INFO. Can be one of "WARNING", "ERROR", "INFO".
|
505
|
+
|
506
|
+
Returns: dict with keys:
|
507
|
+
status
|
508
|
+
reason
|
509
|
+
value
|
510
|
+
valid - name we parse out
|
511
|
+
exists
|
512
|
+
qualified_name - what we find exists
|
513
|
+
guid
|
514
|
+
"""
|
515
|
+
valid = True
|
516
|
+
exists = False
|
517
|
+
identifier_output = {}
|
518
|
+
unique = True
|
519
|
+
value = None
|
520
|
+
|
521
|
+
element_name = extract_attribute(txt, element_labels)
|
522
|
+
if element_name:
|
523
|
+
q_name, guid, unique, exists = get_element_by_name(egeria_client, element_type, element_name)
|
524
|
+
value = element_name
|
525
|
+
else:
|
526
|
+
exists = False
|
527
|
+
q_name = None
|
528
|
+
unique = None
|
529
|
+
|
530
|
+
if exists is True and unique is False:
|
531
|
+
# Multiple elements found - so need to respecify with qualified name
|
532
|
+
msg = f"Multiple elements named {element_name} found"
|
533
|
+
print_msg("DEBUG-ERROR", msg, debug_level)
|
534
|
+
identifier_output = {"status": ERROR, "reason": msg, "value": element_name, "valid": False, "exists": True, }
|
535
|
+
|
536
|
+
|
537
|
+
elif action == EXISTS_REQUIRED or if_missing == ERROR and not exists:
|
538
|
+
# a required identifier doesn't exist
|
539
|
+
msg = f"Required {element_type} `{element_name}` does not exist"
|
540
|
+
print_msg("DEBUG-ERROR", msg, debug_level)
|
541
|
+
identifier_output = {"status": ERROR, "reason": msg, "value": element_name, "valid": False, "exists": False, }
|
542
|
+
elif value is None and if_missing == INFO:
|
543
|
+
# an optional identifier is empty
|
544
|
+
msg = f"Optional attribute with label`{element_type}` missing"
|
545
|
+
print_msg("INFO", msg, debug_level)
|
546
|
+
identifier_output = {"status": INFO, "reason": msg, "value": None, "valid": True, "exists": False, }
|
547
|
+
elif value and exists is False:
|
548
|
+
# optional identifier specified but doesn't exist
|
549
|
+
msg = f"Optional attribute with label`{element_type}` specified but doesn't exist"
|
550
|
+
print_msg("ERROR", msg, debug_level)
|
551
|
+
identifier_output = {"status": ERROR, "reason": msg, "value": value, "valid": False, "exists": False, }
|
552
|
+
|
553
|
+
else:
|
554
|
+
# all good.
|
555
|
+
msg = f"Element {element_type} `{element_name}` exists"
|
556
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
557
|
+
identifier_output = {
|
558
|
+
"status": INFO, "reason": msg, "value": element_name, "valid": True, "exists": True,
|
559
|
+
"qualified_name": q_name, 'guid': guid
|
560
|
+
}
|
561
|
+
|
562
|
+
return identifier_output
|
563
|
+
|
564
|
+
|
565
|
+
def proc_name_list(egeria_client: EgeriaTech, element_type: str, txt: str, element_labels: set,
|
566
|
+
if_missing: str = INFO) -> dict:
|
567
|
+
"""
|
568
|
+
Processes a list of names specified in the given text, retrieves details for each
|
569
|
+
element based on the provided type, and generates a list of valid qualified names.
|
570
|
+
|
571
|
+
The function reads a text block, extracts a list of element names according to the specified
|
572
|
+
element type, looks them up using the provided Egeria client, and classifies them as valid or
|
573
|
+
invalid. It returns the processed names, a list of qualified names, and validity and existence
|
574
|
+
flags.
|
575
|
+
|
576
|
+
Args:
|
577
|
+
|
578
|
+
egeria_client (EgeriaTech): The client instance to connect and query elements from an
|
579
|
+
external system.
|
580
|
+
Element_type (str): The type of element, such as schema or attribute, to process.
|
581
|
+
Txt (str): The raw input text containing element names to be processed.
|
582
|
+
element_labels: a list of equivalent label names to use in processing the element.
|
583
|
+
required: bool, default is False
|
584
|
+
- indicates whether the list of names is required to be present in the input text.
|
585
|
+
|
586
|
+
Returns:
|
587
|
+
Dict containing:
|
588
|
+
'names' - Concatenated valid input names as a single string (or None if empty).
|
589
|
+
'name_list' - A list of known qualified names extracted from the processed elements.
|
590
|
+
'valid' - A boolean indicating whether all elements are valid.
|
591
|
+
'exists' - A boolean indicating whether all elements exist.
|
592
|
+
"""
|
593
|
+
valid = True
|
594
|
+
exists = True
|
595
|
+
id_list_output = {}
|
596
|
+
elements = ""
|
597
|
+
new_element_list = []
|
598
|
+
|
599
|
+
elements_txt = extract_attribute(txt, element_labels)
|
600
|
+
|
601
|
+
if elements_txt is None:
|
602
|
+
msg = f"Attribute with labels `{{element_type}}` missing"
|
603
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
604
|
+
return {"status": if_missing, "reason": msg, "value": None, "valid": False, "exists": False, }
|
605
|
+
else:
|
606
|
+
# element_list = re.split(r'[;,\n]+', elements_txt)
|
607
|
+
element_list = split_tb_string(elements_txt)
|
608
|
+
element_details = {}
|
609
|
+
for element in element_list:
|
610
|
+
# Get the element using the generalized function
|
611
|
+
known_q_name, known_guid, el_valid, el_exists = get_element_by_name(egeria_client, element_type, element)
|
612
|
+
details = {"known_q_name": known_q_name, "known_guid": known_guid, "el_valid": el_valid}
|
613
|
+
elements = f"{element} {elements}" # list of the input names
|
614
|
+
|
615
|
+
if el_exists and el_valid:
|
616
|
+
new_element_list.append(known_q_name) # list of qualified names
|
617
|
+
elif not el_exists:
|
618
|
+
msg = f"No {element_type} `{element}` found"
|
619
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
620
|
+
valid = False
|
621
|
+
valid = valid if el_valid is None else (valid and el_valid)
|
622
|
+
exists = exists and el_exists
|
623
|
+
element_details[element] = details
|
624
|
+
|
625
|
+
if elements:
|
626
|
+
msg = f"Found {element_type}: {elements}"
|
627
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
628
|
+
id_list_output = {
|
629
|
+
"status": INFO, "reason": msg, "value": element_details, "valid": valid, "exists": exists,
|
630
|
+
"name_list": new_element_list,
|
631
|
+
}
|
632
|
+
else:
|
633
|
+
msg = f" Name list contains one or more invalid qualified names."
|
634
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
635
|
+
id_list_output = {"status": if_missing, "reason": msg, "value": elements, "valid": valid,
|
636
|
+
"exists": exists, "dict_list" : element_details}
|
637
|
+
return id_list_output
|
638
|
+
|
639
|
+
|
640
|
+
def update_term_categories(egeria_client: EgeriaTech, term_guid: str, categories_exist: bool,
|
641
|
+
categories_list: List[str]) -> None:
|
642
|
+
"""
|
643
|
+
|
644
|
+
Adds or removes a term to/from specified categories in a glossary.
|
645
|
+
|
646
|
+
This function associates a term, identified by its GUID, with one or more
|
647
|
+
categories. It uses the provided EgeriaTech client to assign the term
|
648
|
+
to the categories. If the GUID of a category is not readily available, it
|
649
|
+
is retrieved either from a pre-loaded dictionary or through a client lookup.
|
650
|
+
|
651
|
+
Args:
|
652
|
+
egeria_client (EgeriaTech): The client to interact with the glossary.
|
653
|
+
term_guid (str): The GUID of the term to be associated with categories.
|
654
|
+
categories_exist (bool): Flag indicating whether the categories already
|
655
|
+
exist.
|
656
|
+
categories_list (List[str]): A list of category names to associate with
|
657
|
+
the term.
|
658
|
+
|
659
|
+
Returns:
|
660
|
+
None
|
661
|
+
"""
|
662
|
+
to_be_cat_guids: list[str] = []
|
663
|
+
# find the categories a term is currently in.
|
664
|
+
existing_categories = egeria_client.get_categories_for_term(term_guid)
|
665
|
+
if type(existing_categories) is str:
|
666
|
+
current_categories = []
|
667
|
+
else:
|
668
|
+
current_categories = [cat['elementHeader']['guid'] for cat in existing_categories]
|
669
|
+
|
670
|
+
if categories_exist is True and categories_list is not None:
|
671
|
+
if type(categories_list) is str:
|
672
|
+
# categories_list = re.split(r'[;,\n]+', categories_list)
|
673
|
+
# categories_list = categories_list.split(";,").trim()
|
674
|
+
categories_list = split_tb_string(categories_list)
|
675
|
+
for category in categories_list:
|
676
|
+
cat_guid = None
|
677
|
+
cat_el = category.strip()
|
678
|
+
element_dict = get_element_dictionary()
|
679
|
+
if cat_el in element_dict:
|
680
|
+
cat = element_dict.get(cat_el, None)
|
681
|
+
cat_guid = cat.get('guid', None) if cat else None
|
682
|
+
if cat_guid is None:
|
683
|
+
cat_guid = egeria_client.__get_guid__(qualified_name=cat_el)
|
684
|
+
update_element_dictionary(cat_el, {'guid': cat_guid})
|
685
|
+
to_be_cat_guids.append(cat_guid)
|
686
|
+
|
687
|
+
for cat in to_be_cat_guids:
|
688
|
+
if cat not in current_categories:
|
689
|
+
egeria_client.add_term_to_category(term_guid, cat)
|
690
|
+
current_categories.append(cat)
|
691
|
+
msg = f"Added term {term_guid} to category {cat}"
|
692
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
693
|
+
|
694
|
+
for cat in current_categories:
|
695
|
+
if cat not in to_be_cat_guids:
|
696
|
+
egeria_client.remove_term_from_category(term_guid, cat)
|
697
|
+
msg = f"Removed term {term_guid} from category {cat}"
|
698
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
699
|
+
else: # No categories specified - so remove any categories a term is in
|
700
|
+
for cat in current_categories:
|
701
|
+
egeria_client.remove_term_from_category(term_guid, cat)
|
702
|
+
msg = f"Removed term {term_guid} from category {cat}"
|
703
|
+
print_msg("DEBUG-INFO", msg, debug_level)
|
704
|
+
|
705
|
+
def sync_data_dict_membership(egeria_client: EgeriaTech, in_data_dictionary_names: list , in_data_dictionary: dict,
|
706
|
+
guid:str, object_type:str)->dict:
|
707
|
+
pass
|
708
|
+
|
709
|
+
def sync_data_structure_membership(egeria_client: EgeriaTech, in_data_structure_names: list , in_data_structure: dict,
|
710
|
+
guid:str, object_type:str)->dict:
|
711
|
+
|
712
|
+
pass
|
713
|
+
|
714
|
+
def sync_data_spec_membership(egeria_client: EgeriaTech, in_data_spec_names: list , in_data_spec: dict,
|
715
|
+
guid:str, object_type:str)->dict:
|
716
|
+
pass
|
717
|
+
|
718
|
+
def sync_term_links(egeria_client: EgeriaTech, term_guid: str, links_exist: bool,
|
719
|
+
links_list: List[str]) -> None:
|
720
|
+
pass
|
721
|
+
|
722
|
+
def sync_parent_data_field(egeria_client: EgeriaTech, term_guid: str, links_exist: bool,
|
723
|
+
links_list: List[str]) -> None:
|
724
|
+
pass
|