pyegeria 5.3.6.2.4__py3-none-any.whl → 5.3.6.3__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 (31) hide show
  1. pyegeria/__init__.py +3 -0
  2. pyegeria/commands/cat/{freddies-inbox → dr_egeria_inbox}/freddie_intro.md +64 -16
  3. pyegeria/commands/cat/{freddie_jupyter.py → dr_egeria_jupyter.py} +8 -16
  4. pyegeria/commands/cat/{freddie_md.py → dr_egeria_md.py} +29 -32
  5. pyegeria/commands/cat/glossary_actions.py +59 -1
  6. pyegeria/commands/cat/list_categories.py +185 -0
  7. pyegeria/commands/cat/list_glossaries.py +26 -33
  8. pyegeria/commands/cat/list_terms.py +47 -47
  9. pyegeria/commands/cli/egeria.py +51 -61
  10. pyegeria/commands/cli/egeria_cat.py +53 -31
  11. pyegeria/create_tech_guid_lists.py +2 -5
  12. pyegeria/glossary_browser_omvs.py +257 -105
  13. pyegeria/glossary_manager_omvs.py +148 -1591
  14. pyegeria/md_processing_utils.py +816 -0
  15. {pyegeria-5.3.6.2.4.dist-info → pyegeria-5.3.6.3.dist-info}/METADATA +1 -1
  16. {pyegeria-5.3.6.2.4.dist-info → pyegeria-5.3.6.3.dist-info}/RECORD +21 -29
  17. {pyegeria-5.3.6.2.4.dist-info → pyegeria-5.3.6.3.dist-info}/entry_points.txt +5 -0
  18. pyegeria/commands/cat/freddie_jup.py +0 -124
  19. pyegeria/commands/cat/freddie_utils.py +0 -590
  20. pyegeria/commands/cat/freddies-outbox/Terms-2025-03-06-13-19-29-Report.md +0 -69
  21. pyegeria/commands/cat/freddies-outbox/Terms-2025-03-06-13-20-30-Update-Form.md +0 -78
  22. pyegeria/commands/cat/glossary_creation_experiment.ipynb +0 -235
  23. pyegeria/test_j.html +0 -0
  24. pyegeria/test_m.html +0 -213
  25. pyegeria/test_m1.html +0 -273
  26. pyegeria/test_mer.ipynb +0 -596
  27. pyegeria/test_w.html +0 -213
  28. /pyegeria/commands/cat/{freddies-inbox → dr_egeria_inbox}/glossary_creation_experiment.ipynb +0 -0
  29. /pyegeria/commands/cat/{freddies-inbox → dr_egeria_inbox}/glossary_exp.md +0 -0
  30. {pyegeria-5.3.6.2.4.dist-info → pyegeria-5.3.6.3.dist-info}/LICENSE +0 -0
  31. {pyegeria-5.3.6.2.4.dist-info → pyegeria-5.3.6.3.dist-info}/WHEEL +0 -0
@@ -0,0 +1,816 @@
1
+ """
2
+
3
+ This file contains functions to parse and process Egeria Markdown (Freddie)
4
+
5
+
6
+ """
7
+
8
+ import json
9
+ from jupyter_notebook_parser import JupyterNotebookParser
10
+ import nbformat
11
+ import os
12
+ import re
13
+ from pyegeria import EgeriaTech, NO_CATEGORIES_FOUND
14
+ from rich import box, print
15
+ from rich.console import Console
16
+ from rich.markdown import Markdown
17
+ from rich.prompt import Prompt
18
+ from rich.table import Table
19
+ import click
20
+ from pyegeria import EgeriaTech, body_slimmer, NO_GLOSSARIES_FOUND, NO_TERMS_FOUND, NO_ELEMENTS_FOUND, NO_PROJECTS_FOUND
21
+ from pyegeria._exceptions import (InvalidParameterException, PropertyServerException, print_exception_response, )
22
+ import datetime
23
+
24
+ console = Console(width=120)
25
+
26
+ commands = ["Create Glossary", "Update Glossary", "Create Term", "Update Term", "Create Personal Project",
27
+ "Update Personal Project", "Create Category", "Update Category"]
28
+ ERROR = "ERROR-> "
29
+ INFO = "INFO- "
30
+ WARNING = "WARNING-> "
31
+ pre_command = "\n---\n==> Processing command:"
32
+ element_dictionary = {}
33
+
34
+
35
+ def is_valid_iso_date(date_text) -> bool:
36
+ """Checks if the given string is a valid ISO date."""
37
+ try:
38
+ datetime.datetime.strptime(date_text, '%Y-%m-%d')
39
+ return True
40
+ except ValueError:
41
+ return False
42
+
43
+
44
+ def get_current_datetime_string():
45
+ """Returns the current date and time as a human-readable string."""
46
+ now = datetime.datetime.now()
47
+ return now.strftime("%Y%m%d%H%M%S")
48
+
49
+ def add_term_to_categories(egeria_client: EgeriaTech, term_guid: str, categories_exist: bool, categories_list: [str], element_dictionary: dict)-> None:
50
+ if categories_exist is True and categories_list is not None:
51
+ for category in categories_list:
52
+ cat_guid = None
53
+ cat_el = f"category.{category.strip()}"
54
+ if cat_el in element_dictionary:
55
+ cat= element_dictionary.get(cat_el, None)
56
+ cat_guid = cat.get('guid', None) if cat else None
57
+ if cat_guid is None:
58
+ cat_guid = egeria_client.__get_guid__(property_name='displayName',display_name=category)
59
+ egeria_client.add_term_to_category(term_guid, cat_guid)
60
+
61
+
62
+
63
+ def extract_command(block: str) -> str | None:
64
+ match = re.search(r"#(.*?)(?:##|\n|$)", block) # Using a non capturing group
65
+ if match:
66
+ return match.group(1).strip()
67
+ return None
68
+
69
+
70
+ def extract_attribute(text: str, label: str) -> str | None:
71
+ """
72
+ Extracts the glossary name from a string.
73
+
74
+ Args:
75
+ text: The input string.
76
+ label: The label to search for.
77
+
78
+ Returns:
79
+ The glossary name, or None if not found.
80
+ """
81
+ pattern = r"## " + re.escape(label) + r"\n(.*?)(?:##|$)" # Construct pattern
82
+ match = re.search(pattern, text, re.DOTALL)
83
+ if match and not match.group(1).isspace():
84
+ txt = match.group(1).strip()
85
+ return txt.strip()
86
+ return None
87
+
88
+
89
+ def update_a_command(txt: str, command: str, obj_type: str, q_name: str, u_guid: str) -> str:
90
+ u_guid = u_guid if u_guid else " "
91
+ verb = command.split(' ')[0].strip()
92
+ action = "Update" if (verb == "Create" and u_guid is not None) else "Create"
93
+ txt = txt.replace(f"{command}", f'**{action} {obj_type}**\n') # update the command
94
+ txt = txt.replace('<GUID>', f'**GUID**\n{u_guid}') # update with GUID
95
+ txt = txt.replace('<Qualified Name>', f"**Qualified Name**\n{q_name}")
96
+ if "Qualified Name" not in txt:
97
+ txt += f"\n## **Qualified Name**\n{q_name}\n"
98
+ if "GUID" not in txt:
99
+ txt += f"\n## **GUID**\n{u_guid}\n"
100
+
101
+ # if (command in {"Update Term", "Update Category", 'Update Glossary'}) and ("Update Description" not in txt):
102
+ # txt += '\n** Update Description\n\n\n'
103
+ # elif "Update Description" in txt:
104
+ # pattern = r"(## Update Description\n).*?(#)"
105
+ # replacement = r"\1\n\n\2"
106
+ # txt += re.sub(pattern, replacement, txt)
107
+
108
+ status = extract_attribute(txt, "Status")
109
+ if command in ["Create Term", "Update Term"] and status is None:
110
+ pattern = r"(## Status\s*\n)(.*?)(#)"
111
+ replacement = r"\1\n DRAFT\n\n\3"
112
+ txt = re.sub(pattern, replacement, txt)
113
+ return txt
114
+
115
+
116
+ def process_glossary_upsert_command(egeria_client: EgeriaTech, element_dictionary: dict, txt: str,
117
+ directive: str = "display") -> str | None:
118
+ """
119
+ Processes a glossary create or update command by extracting key attributes such as
120
+ glossary name, language, description, and usage from the given text.
121
+
122
+ :param txt: A string representing the input cell to be processed for
123
+ extracting glossary-related attributes.
124
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
125
+ :return: A string summarizing the outcome of the processing.
126
+ """
127
+ command = extract_command(txt)
128
+ object_type = command.split(' ')[1].strip()
129
+ object_action = command.split(' ')[0].strip()
130
+
131
+ glossary_name = extract_attribute(txt, 'Glossary Name')
132
+ print(Markdown(f"{pre_command} `{command}` for glossary: `\'{glossary_name}\'` with directive: `{directive}` "))
133
+ language = extract_attribute(txt, 'Language')
134
+ description = extract_attribute(txt, 'Description')
135
+ usage = extract_attribute(txt, 'Usage')
136
+
137
+ glossary_display = (f"\n* Command: {command}\n\t* Glossary Name: {glossary_name}\n\t"
138
+ f"* Language: {language}\n\t* Description:\n{description}\n"
139
+ f"* Usage: {usage}\n")
140
+
141
+ if object_action == 'Update':
142
+ q_name = extract_attribute(txt, 'Qualified Name')
143
+ guid = extract_attribute(txt, 'GUID')
144
+ glossary_display += f"* Qualified Name: {q_name}\n\t* GUID: {guid}\n\n"
145
+
146
+ def validate_glossary(obj_action: str) -> tuple[bool, bool, str | None, str | None]:
147
+ valid = True
148
+ msg = ""
149
+ known_glossary_guid = None
150
+ known_q_name = None
151
+
152
+ glossary_details = egeria_client.get_glossaries_by_name(glossary_name)
153
+ if glossary_details == NO_GLOSSARIES_FOUND:
154
+ glossary_exists = False
155
+ else:
156
+ glossary_exists = True
157
+
158
+ if glossary_name is None:
159
+ msg = f"* {ERROR}Glossary name is missing\n"
160
+ valid = False
161
+ if language is None:
162
+ msg += f"* {ERROR}Language is missing\n"
163
+ valid = False
164
+ if description is None:
165
+ msg += f"* {INFO}Description is missing\n"
166
+
167
+ if len(glossary_details) > 1 and glossary_exists:
168
+ msg += f"* {ERROR}More than one glossary with name {glossary_name} found\n"
169
+ valid = False
170
+ if len(glossary_details) == 1:
171
+ known_glossary_guid = glossary_details[0]['elementHeader'].get('guid', None)
172
+ known_q_name = glossary_details[0]['glossaryProperties'].get('qualifiedName', None)
173
+
174
+ if obj_action == "Update":
175
+
176
+ if not glossary_exists:
177
+ msg += f"* {ERROR}Glossary {glossary_name} does not exist\n"
178
+ valid = False
179
+ # if len(glossary_details) > 1 and glossary_exists:
180
+ # msg += f"* {ERROR}More than one glossary with name {glossary_name} found\n"
181
+ # valid = False
182
+ # if len(glossary_details) == 1:
183
+ # known_glossary_guid = glossary_details[0]['elementHeader'].get('guid', None)
184
+ # known_q_name = glossary_details[0]['glossaryProperties'].get('qualifiedName',None)
185
+ if q_name is None:
186
+ msg += f"* {INFO}Qualified Name is missing => can use known qualified name of {known_q_name}\n"
187
+ valid = True
188
+ elif q_name != known_q_name:
189
+ msg += (
190
+ f"* {ERROR}Glossary `{glossary_name}` qualifiedName mismatch between {q_name} and {known_q_name}\n")
191
+ valid = False
192
+ if valid:
193
+ msg += glossary_display
194
+ msg += f"* -->Glossary `{glossary_name}` exists and can be updated\n"
195
+ else:
196
+ msg += f"* --> validation failed\n"
197
+
198
+ print(Markdown(msg))
199
+ return valid, glossary_exists, known_glossary_guid, known_q_name
200
+
201
+ elif obj_action == "Create":
202
+ if glossary_exists:
203
+ msg += f"{ERROR}Glossary {glossary_name} already exists\n"
204
+
205
+ elif valid:
206
+ msg += f"-->It is valid to create Glossary \'{glossary_name}\' with:\n"
207
+ msg += glossary_display
208
+
209
+ print(Markdown(msg))
210
+ return valid, glossary_exists, known_glossary_guid, known_q_name
211
+
212
+ if directive == "display":
213
+ print(Markdown(glossary_display))
214
+ return None
215
+
216
+ elif directive == "validate":
217
+ is_valid, exists, known_guid, known_q_name = validate_glossary(object_action)
218
+ valid = is_valid if is_valid else None
219
+ return valid
220
+
221
+ elif directive == "process":
222
+ is_valid, exists, known_guid, known_q_name = validate_glossary(object_action)
223
+ if not is_valid:
224
+ return None
225
+ if object_action == "Update":
226
+ if not exists:
227
+ print(
228
+ f"\n{ERROR}Glossary {glossary_name} does not exist! Updating result document with Create command\n")
229
+ return update_a_command(txt, command, object_type, known_q_name, known_guid)
230
+
231
+ body = {
232
+ "class": "ReferenceableRequestBody", "elementProperties": {
233
+ "class": "GlossaryProperties", "qualifiedName": known_q_name, "description": description,
234
+ "language": language, "usage": usage
235
+ }
236
+ }
237
+ egeria_client.update_glossary(known_guid, body)
238
+ print(f"\n-->Updated Glossary {glossary_name} with GUID {known_guid}")
239
+ element_dictionary[f"glossary.{glossary_name}"] = {
240
+ 'guid': known_guid, 'q_name': known_q_name
241
+ }
242
+ # return update_a_command(txt, command, object_type, known_q_name, known_guid)
243
+ return egeria_client.get_glossary_by_guid(known_guid, output_format='md')
244
+ elif object_action == "Create":
245
+ glossary_guid = None
246
+
247
+ if exists:
248
+ print(f"\nGlossary {glossary_name} already exists and result document updated\n")
249
+ return update_a_command(txt, command, object_type, known_q_name, known_guid)
250
+ else:
251
+ glossary_guid = egeria_client.create_glossary(glossary_name, description, language, usage)
252
+ glossary = egeria_client.get_glossary_by_guid(glossary_guid)
253
+ if glossary == NO_GLOSSARIES_FOUND:
254
+ print(f"{ERROR}Just created with GUID {glossary_guid} but Glossary not found\n")
255
+ return None
256
+ qualified_name = glossary['glossaryProperties']["qualifiedName"]
257
+ element_dictionary[f"glossary.{glossary_name}"] = {
258
+ 'guid': glossary_guid, 'q_name': qualified_name
259
+ }
260
+ # return update_a_command(txt, command, object_type, qualified_name, glossary_guid)
261
+ return egeria_client.get_glossary_by_guid(glossary_guid, output_format = 'md')
262
+
263
+
264
+ def process_categories_upsert_command(egeria_client: EgeriaTech, element_dictionary: dict, txt: str,
265
+ directive: str = "display") -> str | None:
266
+ """
267
+ Processes a glossary category create or update command by extracting key attributes such as
268
+ category name, qualified, description, and anchor glossary from the given txt..
269
+
270
+ :param txt: A string representing the input cell to be processed for
271
+ extracting category-related attributes.
272
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
273
+ :return: A string summarizing the outcome of the processing.
274
+ """
275
+ command = extract_command(txt)
276
+ object_type = command.split(' ')[1].strip()
277
+ object_action = command.split(' ')[0].strip()
278
+
279
+ category_name = extract_attribute(txt, 'Category Name')
280
+ print(Markdown(f"{pre_command} `{command}` for category: `\'{category_name}\'` with directive: `{directive}` "))
281
+ owning_glossary = extract_attribute(txt, 'Owning Glossary')
282
+ description = extract_attribute(txt, 'Description')
283
+ category_display = (f"\n* Command: {command}\n\t* Category: {category_name}\n\t"
284
+ f"* In Glossary: {owning_glossary}\n\t* Description:\n{description}\n")
285
+ update_description = None
286
+
287
+ if object_action == 'Update':
288
+ q_name = extract_attribute(txt, 'Qualified Name')
289
+ guid = extract_attribute(txt, 'GUID')
290
+ update_description = extract_attribute(txt, 'Update Description')
291
+ category_display += (f"* Qualified Name: {q_name}\n\t* GUID: {guid}\n\n"
292
+ f"* Update Description: \n {update_description}\n\t")
293
+
294
+ def validate_category(obj_action: str) -> tuple[bool, bool, str | None, str | None, str | None]:
295
+ valid = True
296
+ msg = ""
297
+ known_category_guid = None
298
+ known_q_name = None
299
+ glossary_guid = None
300
+
301
+ category_details = egeria_client.get_categories_by_name(category_name)
302
+ if category_details == NO_CATEGORIES_FOUND:
303
+ category_exists = False
304
+ else:
305
+ category_exists = True
306
+ el_glossary = f"glossary.{owning_glossary}"
307
+ if owning_glossary is None:
308
+ msg += f"* {ERROR}Owning Glossary is missing\n"
309
+ valid = False
310
+
311
+ elif el_glossary in element_dictionary: # Check to see if we already know about this glossary
312
+ owning_glossary_el = f"glossary.{owning_glossary}"
313
+ glossary_guid = element_dictionary[owning_glossary_el].get('guid', None)
314
+ glossary_q_name = element_dictionary[owning_glossary_el].get('q_name', None)
315
+ else:
316
+ # need to ask Egeria if it knows the Glossary Name
317
+ glossary = egeria_client.get_glossaries_by_name(owning_glossary)
318
+ if glossary == NO_GLOSSARIES_FOUND:
319
+ msg += f"* {ERROR}Glossary `{owning_glossary}` does not exist\n\n"
320
+ valid = False
321
+ else:
322
+ msg += f"* {INFO}Glossary `{owning_glossary}` exists\n\n"
323
+ glossary_guid = glossary[0]['elementHeader'].get('guid', None)
324
+ glossary_q_name = glossary[0]['glossaryProperties'].get('qualifiedName', None)
325
+ element_dictionary[el_glossary] = {
326
+ 'guid': glossary_guid, 'q_name': glossary_q_name
327
+ }
328
+
329
+ if category_name is None:
330
+ msg = f"* {ERROR}Category name is missing\n"
331
+ valid = False
332
+
333
+ if description is None:
334
+ msg += f"* {INFO}Description is missing\n"
335
+
336
+ if len(category_details) > 1 and category_exists:
337
+ msg += f"* {ERROR}More than one category with name `{category_name}` found\n"
338
+ valid = False
339
+ if len(category_details) == 1:
340
+ known_category_guid = category_details[0]['elementHeader'].get('guid', None)
341
+ known_q_name = category_details[0]['glossaryCategoryProperties'].get('qualifiedName', None)
342
+
343
+ if obj_action == "Update":
344
+ if not category_exists:
345
+ msg += f"* {ERROR}category `{category_name}` does not exist\n"
346
+ valid = False
347
+ if q_name is None:
348
+ msg += f"* {INFO}Qualified Name is missing => can use known qualified name of {known_q_name}\n"
349
+ valid = True
350
+ elif q_name != known_q_name:
351
+ msg += (
352
+ f"* {ERROR}category `{category_name}` qualifiedName mismatch between {q_name} and {known_q_name}\n")
353
+ valid = False
354
+ if valid:
355
+ msg += category_display
356
+ msg += f"* -->category `{category_name}` exists and can be updated\n"
357
+ else:
358
+ msg += f"* --> validation failed\n"
359
+
360
+ print(Markdown(msg))
361
+ return valid, category_exists, known_category_guid, known_q_name, glossary_guid
362
+
363
+ elif obj_action == "Create":
364
+ if category_exists:
365
+ msg += f"{ERROR}category `{category_name}` already exists\n"
366
+
367
+ elif valid:
368
+ msg += f"-->It is valid to create category `{category_name}` with:\n"
369
+ msg += category_display
370
+
371
+ print(Markdown(msg))
372
+ return valid, category_exists, known_category_guid, known_q_name, glossary_guid
373
+
374
+ if directive == "display":
375
+ print(Markdown(category_display))
376
+ return None
377
+
378
+ elif directive == "validate":
379
+ is_valid, exists, known_guid, known_q_name, glossary_guid = validate_category(object_action)
380
+ valid = is_valid if is_valid else None
381
+ return valid
382
+
383
+ elif directive == "process":
384
+ is_valid, exists, known_guid, known_q_name, glossary_guid = validate_category(object_action)
385
+ if not is_valid:
386
+ print(f"{ERROR}Validation checks failed in creating category `{category_name}`")
387
+ return None
388
+
389
+ if object_action == "Update":
390
+ if not exists:
391
+ print(
392
+ f"\n{ERROR}category `{category_name}` does not exist! Updating result document with Create "
393
+ f"command\n")
394
+ return update_a_command(txt, command, object_type, known_q_name, known_guid)
395
+
396
+ egeria_client.update_category(glossary_guid, category_name, description, known_q_name, None,
397
+ update_description)
398
+ print(f"\n-->Updated category `{category_name}`with GUID {known_guid}")
399
+ element_dictionary[f"category.{category_name}"] = {
400
+ 'guid': known_guid, 'q_name': known_q_name
401
+ }
402
+ # return update_a_command(txt, command, object_type, known_q_name, known_guid)
403
+ return egeria_client.get_categories_by_guid(known_guid, output_format='md')
404
+
405
+ elif object_action == "Create":
406
+ is_root = False
407
+
408
+ if exists:
409
+ print(f"\ncategory `{category_name}` already exists and result document updated\n")
410
+ return update_a_command(txt, command, object_type, known_q_name, known_guid)
411
+ else:
412
+ category_guid = egeria_client.create_category(glossary_guid, category_name, description, is_root)
413
+ category = egeria_client.get_categories_by_guid(category_guid)
414
+
415
+ if category == NO_CATEGORIES_FOUND:
416
+ print(f"{ERROR}Just created with GUID {category_guid} but category not found\n")
417
+ return None
418
+ qualified_name = category['glossaryCategoryProperties']["qualifiedName"]
419
+ element_dictionary[f"category.{category_name}"] = {
420
+ 'guid': category_guid, 'q_name': qualified_name
421
+ }
422
+ # return update_a_command(txt, command, object_type, qualified_name, category_guid)
423
+ return egeria_client.get_categories_by_guid(category_guid, output_format='md')
424
+
425
+ def process_term_upsert_command(egeria_client: EgeriaTech, element_dictionary: dict, txt: str,
426
+ directive: str = "display") -> str | None:
427
+ """
428
+ Processes a term create or update command by extracting key attributes such as
429
+ term name, summary, description, abbreviation, examples, usage, version, and status from the given cell.
430
+
431
+ :param txt: A string representing the input cell to be processed for
432
+ extracting glossary-related attributes.
433
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
434
+ :return: A string summarizing the outcome of the processing.
435
+ """
436
+
437
+ command = extract_command(txt)
438
+ object_type = command.split(' ')[1].strip()
439
+ object_action = command.split(' ')[0].strip()
440
+
441
+ term_name = extract_attribute(txt, 'Term Name')
442
+ summary = extract_attribute(txt, 'Summary')
443
+ description = extract_attribute(txt, 'Description')
444
+ abbreviation = extract_attribute(txt, 'Abbreviation')
445
+ examples = extract_attribute(txt, 'Examples')
446
+ usage = extract_attribute(txt, 'Usage')
447
+ status = extract_attribute(txt, 'Status')
448
+ version = extract_attribute(txt, 'Version')
449
+ categories = extract_attribute(txt, 'Categories')
450
+ categories_list = None
451
+ cats_exist = True
452
+
453
+ glossary_name = extract_attribute(txt, 'Glossary Name')
454
+
455
+ print(Markdown(f"{pre_command} `{command}` for term: `\'{term_name}\'` with directive: `{directive}`"))
456
+
457
+ def validate_term(obj_action: str) -> tuple[bool, bool, str | None, str | None]:
458
+ nonlocal version, status, categories, categories_list, cats_exist
459
+ valid = True
460
+ msg = ""
461
+ known_term_guid = None
462
+ known_q_name = None
463
+
464
+ term_details = egeria_client.get_terms_by_name(term_name)
465
+ if term_details == NO_TERMS_FOUND:
466
+ term_exists = False
467
+ else:
468
+ term_exists = True
469
+
470
+ if status is None:
471
+ msg += f"* {INFO}Term status is missing - will default to DRAFT\n"
472
+ status = 'DRAFT'
473
+
474
+
475
+ if term_name is None:
476
+ msg = f"* {ERROR}Term name is missing\n"
477
+ valid = False
478
+ if glossary_name is None:
479
+ msg += f"* {ERROR}Glossary name is missing\n"
480
+ valid = False
481
+ else:
482
+ glossary_el = f"glossary.{glossary_name}"
483
+ if glossary_el not in element_dictionary:
484
+ glossary = egeria_client.get_glossaries_by_name(glossary_name)
485
+ if isinstance(glossary,str):
486
+ msg += f"* {ERROR}Glossary `{glossary_name}` is unknown\n "
487
+ valid = False
488
+
489
+ if categories is None:
490
+ msg += f"* {INFO}Categories are missing\n"
491
+ else:
492
+ categories_list = re.split(r'[,\n]+', categories)
493
+ categories = ""
494
+ for category in categories_list:
495
+ category_el = f"category.{category.strip()}"
496
+ if category_el not in element_dictionary:
497
+ cat = egeria_client.get_categories_by_name(category)
498
+ if isinstance(cat,str):
499
+ msg += (f"* {WARNING}Category `{category}` is unknown to validate -> "
500
+ f"categories for this term won't be processed!\n")
501
+ cats_exist = False
502
+ categories = f"{category}, {categories}"
503
+ if cats_exist:
504
+ categories +='\n'
505
+ else:
506
+ categories = None
507
+
508
+
509
+ if summary is None:
510
+ msg += f"* {INFO}Term summary is missing\n"
511
+
512
+ if description is None:
513
+ msg += f"* {INFO}Term description is missing\n"
514
+
515
+ if abbreviation is None:
516
+ msg += f"* {INFO}Term abbreviation is missing\n"
517
+ if examples is None:
518
+ msg += f"* {INFO}Term examples is missing\n"
519
+ if usage is None:
520
+ msg += f"* {INFO}Term usage is missing\n"
521
+ if version is None:
522
+ msg += f"* {INFO}Term version is missing - will default to 0.0.1\n"
523
+ version = "0.0.1"
524
+
525
+ if obj_action == "Update": # check to see if provided information exists and is consistent with existing info
526
+ if not term_exists:
527
+ msg += f"* {ERROR}Term {term_name} does not exist\n"
528
+ valid = False
529
+
530
+ if len(term_details) > 1 and term_exists:
531
+ msg += f"* {ERROR}More than one term with name {term_name} found\n"
532
+ valid = False
533
+ elif len(term_details) == 1:
534
+ known_term_guid = term_details[0]['elementHeader'].get('guid', None)
535
+ known_q_name = term_details[0]['glossaryTermProperties'].get('qualifiedName', None)
536
+ if q_name != known_q_name:
537
+ msg += (f"* {ERROR}Term {term_name} qualifiedName mismatch between {q_name} and {known_q_name}\n")
538
+ valid = False
539
+ else:
540
+ msg += f"--> * Term {term_name} exists and can be updated\n"
541
+ msg += term_display
542
+
543
+ print(Markdown(msg))
544
+ return valid, term_exists, known_term_guid, known_q_name
545
+
546
+ elif obj_action == 'Create': # if the command is create, check that it doesn't already exist
547
+ if term_exists:
548
+ msg += f"\n{WARNING}Term \'{term_name}\' already exists.\n"
549
+ elif not valid:
550
+ msg += f"\n-->Validation checks failed in creating Term \'{term_name}\' with: {term_display}\n"
551
+ else:
552
+ msg += f"\n-->It is valid to create Term \'{term_name}\' with: {term_display}\n"
553
+
554
+ print(Markdown(msg))
555
+ return valid, term_exists, known_term_guid, known_q_name
556
+
557
+ if object_action == "Update":
558
+ term_guid = extract_attribute(txt, 'GUID')
559
+ term_guid = term_guid if term_guid else " "
560
+ q_name = extract_attribute(txt, 'Qualified Name')
561
+ q_name = q_name if q_name else " "
562
+
563
+ update_description = extract_attribute(txt, 'Update Description')
564
+ update_description = update_description if update_description else " "
565
+ term_display = (f"\n* Command: {command}\n\t* Glossary: {glossary_name}\n\t"
566
+ f"* Term Name: {term_name}\n\t* Categories: {categories}\n\t* Summary: {summary}"
567
+ f"\n\t* Description: {description}\n\t"
568
+ f"* Abbreviation: {abbreviation}\n\t* Examples: {examples}\n\t* Usage: {usage}\n\t"
569
+ f"* Version: {version}\n\t* Status: {status}\n\t* GUID: {term_guid}\n\t* Qualified Name: "
570
+ f"{q_name}"
571
+ f"\n\t* Update Description: {update_description}\n")
572
+ else:
573
+ term_display = (f"\n* Command: {command}\n\t* Glossary: {glossary_name}\n\t"
574
+ f"* Term Name: {term_name}\n\t* Categories: {categories}\n\t* Summary: {summary}\n\t"
575
+ f"* Description: {description}\n\t"
576
+ f"* Abbreviation: {abbreviation}\n\t* Examples: {examples}\n\t* Usage: {usage}\n\t"
577
+ f"* Version: {version}\n\t* Status: {status}\n")
578
+
579
+ if directive == "display":
580
+ print(Markdown(term_display))
581
+ return None
582
+ elif directive == "validate":
583
+ is_valid, exists, known_guid, known_q_name = validate_term(object_action)
584
+ valid = is_valid if is_valid else None
585
+ return valid
586
+ elif directive == "process":
587
+ try:
588
+ is_valid, exists, known_guid, known_q_name = validate_term(object_action)
589
+ if not is_valid: # First validate the term before we process it
590
+ return None
591
+
592
+ if object_action == "Update" and directive == "process":
593
+ if not exists:
594
+ print(f"\n-->Term {term_name} does not exist")
595
+ return None
596
+ body = {
597
+ "class": "ReferenceableRequestBody", "elementProperties": {
598
+ "class": "GlossaryTermProperties", "qualifiedName": known_q_name, "summary": summary,
599
+ "description": description, "abbreviation": abbreviation, "examples": examples, "usage": usage,
600
+ "publishVersionIdentifier": version, "status": status
601
+ }, "updateDescription": update_description
602
+ }
603
+ egeria_client.update_term(known_guid, body)
604
+ # if cats_exist is True and categories_list is not None:
605
+ # for category in categories_list:
606
+ # cat_guid = element_dictionary.get(f"category.{category}", None)
607
+ # if cat_guid is None:
608
+ # cat_guid = egeria_client.__get_guid__(display_name=category)
609
+ # egeria_client.add_term_to_category(known_guid, cat_guid)
610
+ add_term_to_categories(
611
+ egeria_client, known_guid, cats_exist , categories_list,
612
+ element_dictionary)
613
+ print(f"\n-->Updated Term {term_name} with GUID {known_guid} and categories {categories_list}")
614
+ return egeria_client.get_terms_by_guid(known_guid, 'md')
615
+ # return update_a_command(txt, command, object_type, known_q_name, known_guid)
616
+ elif object_action == "Update" and directive == "validate":
617
+ return egeria_client.get_terms_by_guid(known_guid, 'md')
618
+
619
+ elif object_action == "Create":
620
+ guid = None
621
+ q_name = f"GlossaryTerm:{term_name}:{get_current_datetime_string()}"
622
+ if exists:
623
+ print(f"\n{WARNING}Term {term_name} exists and result document updated")
624
+ return update_a_command(txt, command, object_type, q_name, known_guid)
625
+ else:
626
+ ## get the guid for the glossary from the name - first look locally
627
+ glossary = element_dictionary.get(f"glossary.{glossary_name}", None)
628
+
629
+ if glossary is not None:
630
+ glossary_guid = glossary.get('guid', None)
631
+ if glossary_guid is None:
632
+ print(f"{ERROR}Glossary reference {glossary_name} not found")
633
+ return None
634
+ else:
635
+ glossary_guid = egeria_client.__get_guid__(property_name="displayName", display_name=glossary_name)
636
+ if glossary_guid == NO_ELEMENTS_FOUND:
637
+ print(f"{ERROR}Glossary {glossary_name} not found")
638
+ return None
639
+ term_body = {
640
+ "class": "ReferenceableRequestBody", "elementProperties": {
641
+ "class": "GlossaryTermProperties", "qualifiedName": q_name, "displayName": term_name,
642
+ "summary": summary, "description": description, "abbreviation": abbreviation,
643
+ "examples": examples, "usage": usage, "publishVersionIdentifier": version
644
+ # "additionalProperties":
645
+ # {
646
+ # "propertyName1": "xxxx",
647
+ # "propertyName2": "xxxx"
648
+ # }
649
+ }, "initialStatus": status
650
+ }
651
+ term_guid = egeria_client.create_controlled_glossary_term(glossary_guid, term_body)
652
+ if term_guid == NO_ELEMENTS_FOUND:
653
+ print(f"{ERROR}Term {term_name} not created")
654
+ return None
655
+ if cats_exist and categories is not None:
656
+ add_term_to_categories(
657
+ egeria_client, term_guid, cats_exist, categories_list,
658
+ element_dictionary)
659
+ print(f"\n-->Created Term {term_name} with GUID {term_guid}")
660
+ # element = egeria_client.find_glossary_terms('term_name')
661
+ element_dictionary[f"term.{term_name}"] = {'guid': term_guid, 'q_name': q_name}
662
+ return egeria_client.get_terms_by_guid(term_guid, 'md')
663
+ # return update_a_command(txt, command, object_type, q_name, term_guid)
664
+ except Exception as e:
665
+ print(f"{ERROR}Error creating term {term_name}: {e}")
666
+ console.print_exception(show_locals=True)
667
+ return None
668
+
669
+ def process_per_proj_upsert_command(egeria_client: EgeriaTech, element_dictionary: dict, txt: str,
670
+ directive: str = "display") -> str | None:
671
+ """
672
+ Processes a personal project create or update command by extracting key attributes such as
673
+ glossary name, language, description, and usage from the given cell.
674
+
675
+ :param txt: A string representing the input cell to be processed for
676
+ extracting glossary-related attributes.
677
+ :param directive: an optional string indicating the directive to be used - display, validate or execute
678
+ :return: A string summarizing the outcome of the processing.
679
+ """
680
+ command = extract_command(txt)
681
+ object = command.split()
682
+ object_type = f"{object[1]} {object[2]}"
683
+ object_action = object[0]
684
+
685
+ project_name = extract_attribute(txt, 'Project Name')
686
+ description = extract_attribute(txt, 'Description')
687
+ project_identifier = extract_attribute(txt, 'Project Identifier')
688
+ project_status = extract_attribute(txt, 'Project Status')
689
+ project_phase = extract_attribute(txt, 'Project Phase')
690
+ project_health = extract_attribute(txt, 'Project Health')
691
+ start_date = extract_attribute(txt, 'Start Date')
692
+ planned_end_date = extract_attribute(txt, 'Planned End Date')
693
+ print(Markdown(f"{pre_command} `\'{command}\'` for project: `{project_name}` with directive: `{directive}` "))
694
+
695
+ project_display = (f"\n* Command: {command}\n\t* Project: {project_name}\n\t"
696
+ f"* Status: {project_status}\n\t* Description: {description}\n\t"
697
+ f"* Phase: {project_phase}\n\t* Health: {project_health}\n\t"
698
+ f"* Start Date: {start_date}\n\t* Planned End Date: {planned_end_date}\n")
699
+
700
+ def validate_project(obj_action: str) -> tuple[bool, bool, str, str]:
701
+ valid = True
702
+ msg = ""
703
+ known_guid = None
704
+ known_q_name = None
705
+
706
+ project_details = egeria_client.get_projects_by_name(project_name)
707
+ if project_details == NO_PROJECTS_FOUND:
708
+ project_exists = False
709
+ else:
710
+ project_exists = True
711
+
712
+ if project_name is None:
713
+ msg = f"* {ERROR}Project name is missing\n"
714
+ valid = False
715
+ if project_status is None:
716
+ msg += f"* {INFO}No Project status found\n"
717
+
718
+ if description is None:
719
+ msg += f"* {INFO}No Description found\n"
720
+
721
+ if project_identifier is None:
722
+ msg += f"* {INFO}No Project Identifier found\n"
723
+
724
+ if project_phase is None:
725
+ msg += f"* {INFO}No Project Phase found\n"
726
+
727
+ if project_health is None:
728
+ msg += f"* {INFO}No Project Health found\n"
729
+
730
+ if start_date is None:
731
+ msg += f"* {INFO}No Start Date found\n"
732
+ elif not is_valid_iso_date(start_date):
733
+ msg += f"* {ERROR}Start Date is not a valid ISO date of form YYYY-MM-DD\n"
734
+ valid = False
735
+
736
+ if planned_end_date is None:
737
+ msg += f"* {INFO} No Planned End Date found\n"
738
+ elif not is_valid_iso_date(planned_end_date):
739
+ msg += f"* {ERROR}Planned End Date is not a valid ISO date of form YYYY-MM-DD\n"
740
+ valid = False
741
+
742
+ if obj_action == "Update":
743
+ q_name = extract_attribute(txt, 'Qualified Name')
744
+
745
+ if not project_exists:
746
+ msg += f"* {ERROR}Project {project_name} does not exist\n"
747
+ valid = False
748
+ if len(project_details) > 1 and project_exists:
749
+ msg += f"* {ERROR}More than one project with name {project_name} found\n"
750
+ valid = False
751
+ if len(project_details) == 1:
752
+ known_guid = project_details[0]['elementHeader'].get('guid', None)
753
+ known_q_name = project_details[0]['glossaryProperties'].get('qualifiedName', None)
754
+ if q_name is None:
755
+ msg += f"* {INFO}Qualified Name is missing => can use known qualified name of {known_q_name}\n"
756
+ valid = True
757
+ elif q_name != known_q_name:
758
+ msg += (f"* {ERROR}Project {project_name} qualifiedName mismatch between {q_name} and {known_q_name}\n")
759
+ valid = False
760
+ if valid:
761
+ msg += project_display
762
+ msg += f"* -->Project {project_name} exists and can be updated\n"
763
+ else:
764
+ msg += f"* --> validation failed\n"
765
+ msg += '---'
766
+ print(Markdown(msg))
767
+ return valid, project_exists, known_guid, known_q_name
768
+
769
+ elif obj_action == "Create":
770
+ if project_exists:
771
+ msg += f"\n{ERROR}Project {project_name} already exists"
772
+ else:
773
+ msg += f"\n-->It is valid to create Project \'{project_name}\' with:\n"
774
+ print(Markdown(msg))
775
+ return valid, project_exists, known_guid, known_q_name
776
+
777
+ if directive == "display":
778
+ print(Markdown(project_display))
779
+ return None
780
+
781
+ elif directive == "validate":
782
+ is_valid, exists, known_guid, known_q_name = validate_project(object_action)
783
+ valid = is_valid if is_valid else None
784
+ return valid
785
+
786
+ elif directive == "process":
787
+ is_valid, exists, known_guid, known_q_name = validate_project(object_action)
788
+ if not is_valid:
789
+ return None
790
+ if object_action == "Update":
791
+ if not exists:
792
+ print(f"\n\n-->Project {project_name} does not exist")
793
+ return None
794
+
795
+ egeria_client.update_project(known_guid, known_q_name, project_identifier, project_name, description,
796
+ project_status, project_phase, project_health, start_date, planned_end_date,
797
+ False)
798
+ print(f"\n-->Updated Project {project_name} with GUID {known_guid}")
799
+ return update_a_command(txt, command, object_type, known_q_name, known_guid)
800
+ elif object_action == "Create":
801
+ guid = None
802
+ if exists:
803
+ print(f"Project {project_name} already exists and update document created")
804
+ return update_a_command(txt, command, object_type, known_q_name, known_guid)
805
+ else:
806
+ guid = egeria_client.create_project(None, None, None, False, project_name, description,
807
+ "PersonalProject", project_identifier, True, project_status,
808
+ project_phase, project_health, start_date, planned_end_date)
809
+ project_g = egeria_client.get_project(guid)
810
+ if project_g == NO_GLOSSARIES_FOUND:
811
+ print(f"Just created with GUID {guid} but Project not found")
812
+ return None
813
+
814
+ q_name = project_g['projectProperties']["qualifiedName"]
815
+ element_dictionary[f"project.{project_name}"] = {'guid': guid, 'q_name': q_name}
816
+ return update_a_command(txt, command, object_type, q_name, guid)