pyegeria 5.4.4.1__py3-none-any.whl → 5.4.4.2__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.
@@ -0,0 +1,33 @@
1
+ # Business-Imperative-DrE Attributes
2
+ Auto-generated format for Business Imperative (Create).
3
+
4
+ # Business-Imperative-DrE Report - created at 2025-09-11 21:21
5
+ Business-Imperative-DrE found from the search string: `All`
6
+
7
+ <a id="d85814c3-dac7-4915-bdcf-751ef037d83e"></a>
8
+ # Business-Imperative-DrE Name: Sustainability Reporting Imperative
9
+
10
+ ## Display Name
11
+ Sustainability Reporting Imperative
12
+
13
+ ## Summary
14
+ Comply with Legal and Expected Sustainability Norms and Regulations
15
+
16
+ ## Description
17
+ As an international company, Coco Pharmaceuticals must comply with local regulatory requirements as well as meeting the expectations of our customers and shareholders. Sustainability reporting and improvements are important across stakeholders. We must therefore have and communicate an explicit and robust sustainability strategy with demonstrated outcomes.
18
+
19
+ ## Scope
20
+ Enterprise
21
+
22
+ ## Importance
23
+ Very important, with several implications.
24
+
25
+ ## Outcomes
26
+ 1. Ability to operate in each location.2. Market position3. Satisfied shareholders4. Happier and engaged employees
27
+
28
+ ## Qualified Name
29
+ BusinessImperative::Sustainability Reporting Imperative
30
+
31
+ ## GUID
32
+ d85814c3-dac7-4915-bdcf-751ef037d83e
33
+
@@ -147,11 +147,13 @@ def process_gov_definition_upsert_command(egeria_client: EgeriaTech, txt: str, d
147
147
  # Proceed with the update
148
148
  update_body = set_update_body(object_type, parsed_output['attributes'])
149
149
  update_body['properties'] = set_gov_prop_body(object_type, qualified_name, parsed_output['attributes'])
150
- egeria_client.update_governance_definition(guid, body_slimmer(update_body))
150
+ update_body = body_slimmer(update_body)
151
+ egeria_client.update_governance_definition(guid, update_body)
151
152
  if status := parsed_output['attributes'].get('Status', {}).get('value', None):
152
153
  egeria_client.update_governance_definition_status(guid, status)
153
154
  logger.success(f"Updated {object_type} `{display_name}` with GUID {guid}")
154
- return egeria_client.get_governance_definition_by_guid(guid, output_format='MD')
155
+ return egeria_client.get_governance_definition_by_guid(guid, output_format='MD',
156
+ output_format_set = 'Business-Imperative-DrE')
155
157
 
156
158
  elif object_action == "Create":
157
159
  if valid is False and exists:
@@ -187,6 +189,7 @@ def process_gov_definition_upsert_command(egeria_client: EgeriaTech, txt: str, d
187
189
 
188
190
  except Exception as e:
189
191
  logger.exception(f"Unexpected error: {e}")
192
+ print("Unexepected error occurred:\n {e}")
190
193
  return None
191
194
 
192
195
 
@@ -8,9 +8,11 @@ import re
8
8
  from typing import List, Optional
9
9
 
10
10
  from loguru import logger
11
+ from pydantic import ValidationError
11
12
  from rich import print
12
13
  from rich.markdown import Markdown
13
14
  from rich.console import Console
15
+ from pyegeria.utils import parse_to_dict
14
16
 
15
17
  from md_processing.md_processing_utils.common_md_utils import (get_current_datetime_string, get_element_dictionary,
16
18
  update_element_dictionary,
@@ -26,7 +28,8 @@ from md_processing.md_processing_utils.common_md_utils import (update_element_di
26
28
  from md_processing.md_processing_utils.extraction_utils import (extract_command_plus, update_a_command)
27
29
  from md_processing.md_processing_utils.md_processing_constants import (get_command_spec)
28
30
  from md_processing.md_processing_utils.message_constants import (ERROR, INFO, WARNING, ALWAYS, EXISTS_REQUIRED)
29
- from pyegeria import EgeriaTech, select_output_format_set
31
+ from pyegeria import EgeriaTech, select_output_format_set, PyegeriaException, print_basic_exception, \
32
+ print_validation_error
30
33
 
31
34
  from pyegeria._globals import DEBUG_LEVEL
32
35
 
@@ -93,131 +96,157 @@ def parse_upsert_command(egeria_client: EgeriaTech, object_type: str, object_act
93
96
 
94
97
  for attr in attributes:
95
98
  for key in attr:
96
- # Run some checks to see if the attribute is appropriate to the operation and usage level
97
- for_update = attr[key].get('inUpdate', True)
98
- level = attr[key].get('level', 'Basic')
99
- msg = (f"___\nProcessing `{key}` in `{object_action}` on a `{object_type}` "
100
- f"\n\twith usage level: `{EGERIA_USAGE_LEVEL}` and attribute level `{level}` and for_update `"
101
- f"{for_update}`\n")
102
- logger.trace(msg)
103
- if for_update is False and object_action == "Update":
104
- logger.trace(f"Attribute `{key}`is not allowed for `Update`", highlight=True)
105
- continue
106
- if EGERIA_USAGE_LEVEL == "Basic" and level != "Basic":
107
- logger.trace(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.",
108
- highlight=True)
109
- continue
110
- if EGERIA_USAGE_LEVEL == "Advanced" and level in ["Expert", "Invisible"]:
111
- logger.trace(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.",
112
- highlight=True)
113
- continue
114
- if EGERIA_USAGE_LEVEL == "Expert" and level == "Invisible":
115
- logger.trace(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.",
116
- highlight=True)
117
- continue
118
-
119
- if attr[key].get('input_required', False) is True:
120
- if_missing = ERROR
121
- else:
122
- if_missing = INFO
123
-
124
- # lab = [item.strip() for item in re.split(r'[;,\n]+',attr[key]['attr_labels'])]
125
- lab = split_tb_string(attr[key]['attr_labels'])
126
- labels: set = set()
127
- labels.add(key.strip())
128
- if key == 'Display Name':
129
- labels.add(object_type.strip())
130
- if lab is not None and lab != [""]:
131
- labels.update(lab)
132
-
133
- default_value = attr[key].get('default_value', None)
134
-
135
- style = attr[key]['style']
136
- if style in ['Simple', 'Comment']:
137
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
138
- elif style == 'Dictionary':
139
- parsed_attributes[key] = proc_dictionary_attribute(txt, object_action, labels, if_missing, default_value)
140
- parsed_attributes[key]['name_list'] = json.dumps(parsed_attributes[key]['value'], indent=2)
141
-
142
- elif style == 'Valid Value':
143
- parsed_attributes[key] = proc_valid_value(txt, object_action, labels,
144
- attr[key].get('valid_values', None), if_missing,
145
- default_value)
146
- elif style == 'QN':
147
- parsed_attributes[key] = proc_el_id(egeria_client, command_display_name, command_qn_prefix, labels, txt,
148
- object_action, version, if_missing)
149
- if key == 'Qualified Name' and parsed_attributes[key]['value'] and parsed_attributes[key][
150
- 'exists'] is False:
151
- parsed_output['exists'] = False
152
- elif style == 'ID':
153
- parsed_attributes[key] = proc_el_id(egeria_client, command_display_name, command_qn_prefix, labels, txt,
154
- object_action, version, if_missing)
155
-
156
- parsed_output['guid'] = parsed_attributes[key].get('guid', None)
157
- parsed_output['qualified_name'] = parsed_attributes[key].get('qualified_name', None)
158
- parsed_output['exists'] = parsed_attributes[key]['exists']
159
- if parsed_attributes[key]['valid'] is False:
160
- parsed_output['valid'] = False
161
- parsed_output['reason'] += parsed_attributes[key]['reason']
162
-
163
- elif style == 'Reference Name':
164
- parsed_attributes[key] = proc_ids(egeria_client, key, labels, txt, object_action, if_missing)
165
- if ((if_missing == ERROR) and parsed_attributes[key].get("value", None) and parsed_attributes[key][
166
- 'exists'] is False):
167
- msg = f"Reference Name `{parsed_attributes[key]['value']}` is specified but does not exist"
168
- logger.error(msg)
169
- parsed_output['valid'] = False
170
- parsed_output['reason'] += msg
171
- elif parsed_attributes[key]['valid'] is False:
172
- parsed_output['valid'] = False
173
- parsed_output['reason'] += parsed_attributes[key]['reason']
174
-
175
- elif style == 'GUID':
176
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing)
177
- elif style == 'Ordered Int':
178
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing)
179
- elif style == 'Simple Int':
180
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value, "int")
181
- elif style == 'Simple List':
182
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
183
- name_list = parsed_attributes[key]['value']
184
- # attribute = re.split(r'[;,\n]+', name_list) if name_list is not None else None
185
- attribute = split_tb_string(name_list)
186
- parsed_attributes[key]['value'] = attribute
187
- parsed_attributes[key]['name_list'] = name_list
188
- elif style == 'Parent':
189
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
190
- elif style == 'Bool':
191
- parsed_attributes[key] = proc_bool_attribute(txt, object_action, labels, if_missing, default_value)
192
- elif style == "Dictionary List":
193
- parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
194
- parsed_attributes[key]['list'] = json.loads(parsed_attributes[key]['value'])
195
-
99
+ try:
100
+ # Run some checks to see if the attribute is appropriate to the operation and usage level
101
+ for_update = attr[key].get('inUpdate', True)
102
+ level = attr[key].get('level', 'Basic')
103
+ msg = (f"___\nProcessing `{key}` in `{object_action}` on a `{object_type}` "
104
+ f"\n\twith usage level: `{EGERIA_USAGE_LEVEL}` and attribute level `{level}` and for_update `"
105
+ f"{for_update}`\n")
106
+ logger.trace(msg)
107
+ if for_update is False and object_action == "Update":
108
+ logger.trace(f"Attribute `{key}`is not allowed for `Update`", highlight=True)
109
+ continue
110
+ if EGERIA_USAGE_LEVEL == "Basic" and level != "Basic":
111
+ logger.trace(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.",
112
+ highlight=True)
113
+ continue
114
+ if EGERIA_USAGE_LEVEL == "Advanced" and level in ["Expert", "Invisible"]:
115
+ logger.trace(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.",
116
+ highlight=True)
117
+ continue
118
+ if EGERIA_USAGE_LEVEL == "Expert" and level == "Invisible":
119
+ logger.trace(f"Attribute `{key}` is not supported for `{EGERIA_USAGE_LEVEL}` usage level. Skipping.",
120
+ highlight=True)
121
+ continue
196
122
 
197
- elif style == 'Reference Name List':
198
- parsed_attributes[key] = proc_name_list(egeria_client, key, txt, labels, if_missing)
199
- if (parsed_attributes[key].get("value", None) and (
200
- parsed_attributes[key]['exists'] is False or parsed_attributes[key]['valid'] is False)):
201
- msg = (f"A Reference Name in `{parsed_attributes[key].get('name_list', None)}` is specified but "
202
- f"does not exist")
123
+ if attr[key].get('input_required', False) is True:
124
+ if_missing = ERROR
125
+ else:
126
+ if_missing = INFO
127
+
128
+ # lab = [item.strip() for item in re.split(r'[;,\n]+',attr[key]['attr_labels'])]
129
+ lab = split_tb_string(attr[key]['attr_labels'])
130
+ labels: set = set()
131
+ labels.add(key.strip())
132
+ if key == 'Display Name':
133
+ labels.add(object_type.strip())
134
+ if lab is not None and lab != [""]:
135
+ labels.update(lab)
136
+
137
+ default_value = attr[key].get('default_value', None)
138
+
139
+ style = attr[key]['style']
140
+ if style in ['Simple', 'Comment']:
141
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
142
+ elif style == 'Dictionary':
143
+ parsed_attributes[key] = proc_dictionary_attribute(txt, object_action, labels, if_missing, default_value)
144
+ if key in parsed_attributes and parsed_attributes[key] is not None:
145
+ if parsed_attributes[key].get('value', None) is not None:
146
+ if isinstance(parsed_attributes[key]['value'], dict):
147
+ parsed_attributes[key]['dict'] = json.dumps(parsed_attributes[key]['value'], indent=2)
148
+ else:
149
+ continue
150
+ else:
151
+ continue
152
+
153
+ elif style == 'Valid Value':
154
+ parsed_attributes[key] = proc_valid_value(txt, object_action, labels,
155
+ attr[key].get('valid_values', None), if_missing,
156
+ default_value)
157
+ elif style == 'QN':
158
+ parsed_attributes[key] = proc_el_id(egeria_client, command_display_name, command_qn_prefix, labels, txt,
159
+ object_action, version, if_missing)
160
+ if key == 'Qualified Name' and parsed_attributes[key]['value'] and parsed_attributes[key][
161
+ 'exists'] is False:
162
+ parsed_output['exists'] = False
163
+ elif style == 'ID':
164
+ parsed_attributes[key] = proc_el_id(egeria_client, command_display_name, command_qn_prefix, labels, txt,
165
+ object_action, version, if_missing)
166
+
167
+ parsed_output['guid'] = parsed_attributes[key].get('guid', None)
168
+ parsed_output['qualified_name'] = parsed_attributes[key].get('qualified_name', None)
169
+ parsed_output['exists'] = parsed_attributes[key]['exists']
170
+ if parsed_attributes[key]['valid'] is False:
171
+ parsed_output['valid'] = False
172
+ parsed_output['reason'] += parsed_attributes[key]['reason']
173
+
174
+ elif style == 'Reference Name':
175
+ parsed_attributes[key] = proc_ids(egeria_client, key, labels, txt, object_action, if_missing)
176
+ if ((if_missing == ERROR) and parsed_attributes[key].get("value", None) and parsed_attributes[key][
177
+ 'exists'] is False):
178
+ msg = f"Reference Name `{parsed_attributes[key]['value']}` is specified but does not exist"
179
+ logger.error(msg)
180
+ parsed_output['valid'] = False
181
+ parsed_output['reason'] += msg
182
+ elif parsed_attributes[key]['valid'] is False:
183
+ parsed_output['valid'] = False
184
+ parsed_output['reason'] += parsed_attributes[key]['reason']
185
+
186
+ elif style == 'GUID':
187
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing)
188
+ elif style == 'Ordered Int':
189
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing)
190
+ elif style == 'Simple Int':
191
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value, "int")
192
+ elif style == 'Simple List':
193
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value, "list")
194
+ name_list = parsed_attributes[key]['value']
195
+ # attribute = re.split(r'[;,\n]+', name_list) if name_list is not None else None
196
+ attribute = split_tb_string(name_list)
197
+ parsed_attributes[key]['value'] = attribute
198
+ parsed_attributes[key]['name_list'] = name_list
199
+ elif style == 'Parent':
200
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
201
+ elif style == 'Bool':
202
+ parsed_attributes[key] = proc_bool_attribute(txt, object_action, labels, if_missing, default_value)
203
+ elif style == "Dictionary List":
204
+ parsed_attributes[key] = proc_simple_attribute(txt, object_action, labels, if_missing, default_value)
205
+ parsed_attributes[key]['list'] = json.loads(parsed_attributes[key]['value'])
206
+
207
+
208
+ elif style == 'Reference Name List':
209
+ parsed_attributes[key] = proc_name_list(egeria_client, key, txt, labels, if_missing)
210
+ if (parsed_attributes[key].get("value", None) and (
211
+ parsed_attributes[key]['exists'] is False or parsed_attributes[key]['valid'] is False)):
212
+ msg = (f"A Reference Name in `{parsed_attributes[key].get('name_list', None)}` is specified but "
213
+ f"does not exist")
214
+ logger.error(msg)
215
+ parsed_output['valid'] = False
216
+ parsed_output['reason'] += msg
217
+ else:
218
+ msg = f"Unknown attribute style: {style} for key `{key}`"
203
219
  logger.error(msg)
204
- parsed_output['valid'] = False
205
- parsed_output['reason'] += msg
206
- else:
207
- msg = f"Unknown attribute style: {style} for key `{key}`"
208
- logger.error(msg)
209
- sys.exit(1)
220
+ sys.exit(1)
221
+ parsed_attributes[key]['valid'] = False
222
+ parsed_attributes[key]['value'] = None
223
+ if key == "Display Name":
224
+ display_name = parsed_attributes[key]['value']
225
+
226
+ value = parsed_attributes[key].get('value', None)
227
+
228
+ if value is not None:
229
+ if isinstance(value, list):
230
+ list_out = f"\n\t* {key}:\n\n"
231
+ for item in value:
232
+ list_out += f"\t{item}\n"
233
+ parsed_output['display'] += list_out
234
+ elif isinstance(value, dict):
235
+ list_out = f"\n\t* {key}:\n\n"
236
+ for k, v in value.items():
237
+ list_out += f"\t{k}: \n\t\t{v}\n"
238
+ parsed_output['display'] += list_out
239
+ else:
240
+ parsed_output['display'] += f"\n\t* {key}: \n`{value}`\n\t"
241
+ except PyegeriaException as e:
242
+ logger.error(f"PyegeriaException occurred: {e}")
243
+
244
+ print_basic_exception(e)
245
+
246
+ except ValidationError as e:
210
247
  parsed_attributes[key]['valid'] = False
211
248
  parsed_attributes[key]['value'] = None
212
- if key == "Display Name":
213
- display_name = parsed_attributes[key]['value']
214
-
215
- value = parsed_attributes[key].get('value', None)
216
-
217
- if value is not None:
218
- # if the value is a dict, get the flattened name list
219
- value = parsed_attributes[key].get('name_list', None) if isinstance(value, (dict, list)) else value
220
- parsed_output['display'] += f"\n\t* {key}: `{value}`\n\t"
249
+ print_validation_error(e)
221
250
 
222
251
 
223
252
  parsed_output['attributes'] = parsed_attributes
@@ -504,7 +533,8 @@ def proc_simple_attribute(txt: str, action: str, labels: set, if_missing: str =
504
533
 
505
534
  attribute = extract_attribute(txt, labels)
506
535
 
507
- attribute = default_value if attribute is None else attribute.replace('\n', '')
536
+ # attribute = default_value if attribute is None else attribute.replace('\n', '')
537
+ attribute = default_value if attribute is None else attribute
508
538
 
509
539
  if attribute is None:
510
540
  if if_missing == INFO or if_missing == WARNING:
@@ -519,8 +549,10 @@ def proc_simple_attribute(txt: str, action: str, labels: set, if_missing: str =
519
549
 
520
550
  if attribute and simp_type == "int" :
521
551
  attribute = int(attribute)
522
- elif attribute and simp_type == "list":
523
- attribute = list(attribute)
552
+ # elif attribute and simp_type == "list":
553
+ # if isinstance(attribute, str):
554
+ # attribute = [piece.strip() for piece in re.split(r'[,\n]', attribute) if piece.strip()]
555
+
524
556
 
525
557
 
526
558
  return {"status": INFO, "OK": None, "value": attribute, "valid": valid, "exists": True}
@@ -554,8 +586,8 @@ def proc_dictionary_attribute(txt: str, action: str, labels: set, if_missing: st
554
586
  default_value = None
555
587
 
556
588
  attr = extract_attribute(txt, labels)
557
- attribute = json.loads(attr) if attr is not None else default_value
558
-
589
+ # attribute = json.loads(attr) if attr is not None else default_value
590
+ attribute = parse_to_dict(attr)
559
591
 
560
592
  if attribute is None:
561
593
  if if_missing == INFO or if_missing == WARNING:
@@ -387,7 +387,7 @@ def set_gov_prop_body(object_type: str, qualified_name: str, attributes: dict)->
387
387
  prop_bod["importance"] = attributes.get('Importance', {}).get('value', None)
388
388
  prop_bod["implications"] = attributes.get('Implication', {}).get('value', [])
389
389
  prop_bod["outcomes"] = attributes.get('Outcomes', {}).get('value', [])
390
- prop_bod["results"] = attributes.get('Results', {}).get('value', [])
390
+ prop_bod["results"] = attributes.get('Results', {}).get('value', []) or []
391
391
  prop_bod["effectiveFrom"] = attributes.get('Effective From', {}).get('value', None),
392
392
  prop_bod["effectiveTo"] = attributes.get('Effective To', {}).get('value', None),
393
393
  prop_bod["additionalProperties"] = attributes.get('Additional Properties', {}).get('value', None),
@@ -425,9 +425,12 @@ def update_gov_body_for_type(object_type: str, body: dict, attributes: dict) ->
425
425
  body['namePatterns'] = attributes.get('Name Patterns', {}).get('value', [])
426
426
  return body
427
427
  elif object_type in ["TermsAndConditions", "Certification Type", "License Type"]:
428
- body['entitlements'] = attributes.get('Entitlementss', {}).get('value', {})
429
- body['restrictions'] = attributes.get('Restrictions', {}).get('value', {})
430
- body['obligations'] = attributes.get('Obligations', {}).get('value', {})
428
+ entitlements = attributes.get('Entitlementss', {}).get('value', {}) if attributes.get('Entitlementss',None) else None
429
+ restrictions = attributes.get('Restrictions', {}).get('value', {}) if attributes.get('Restrictions',None) else None
430
+ obligations = attributes.get('Obligations', {}).get('value', {}) if attributes.get('Obligations',None) else None
431
+ body['entitlements'] = entitlements
432
+ body['restrictions'] = restrictions
433
+ body['obligations'] = obligations
431
434
 
432
435
  return body
433
436
 
@@ -2,6 +2,7 @@
2
2
  This file contains functions for extracting data from text for Egeria Markdown processing
3
3
  """
4
4
  import re
5
+ import json
5
6
  from typing import Any
6
7
 
7
8
  from md_processing.md_processing_utils.common_md_utils import (print_msg, find_key_with_value, get_element_dictionary,
@@ -74,56 +75,94 @@ def extract_command(block: str) -> str | None:
74
75
  return None
75
76
 
76
77
 
77
- def extract_attribute(text: str, labels: set) -> str | None:
78
+ # def extract_attribute(text: str, labels: set) -> str | None:
79
+ # """
80
+ # Extracts the attribute value from a string.
81
+ #
82
+ # Args:
83
+ # text: The input string.
84
+ # labels: List of equivalent labels to search for
85
+ #
86
+ # Returns:
87
+ # The value of the attribute, or None if not found.
88
+ #
89
+ # Note:
90
+ # Lines beginning with '>' are ignored.
91
+ # """
92
+ # # Iterate over the list of labels
93
+ # for label in labels:
94
+ # # Construct pattern for the current label
95
+ # # text = re.sub(r'\s+', ' ', text).strip() # just added
96
+ # # text = re.sub(r'\n\n+', '\n\n', text).strip()
97
+ #
98
+ # # Replace multiple spaces or tabs with a single space
99
+ # normalized = re.sub(r'\s+', ' ', text)
100
+ # # Collapse multiple blank lines into a single one
101
+ # normalized = re.sub(r'\n\s*\n', '\n', normalized).strip()
102
+ #
103
+ # # label = label.strip()
104
+ # # pattern = rf"##\s*{re.escape(label)}\s*\n(?:\s*\n)*?(.*?)(?:#|___|$)"
105
+ # # Normalize the label
106
+ # normalized_label = re.sub(r'\s+', ' ', label.strip())
107
+ #
108
+ # # Construct the regex pattern
109
+ # pattern = rf"##\s*{re.escape(normalized_label)}\s*\n(?:\s*\n)*?(.*?)(?:#|___|$)"
110
+ # # pattern = rf"##\s+{re.escape(label)}\n(.*?)(?:#|___|$)" # modified from --- to enable embedded tables
111
+ # match = re.search(pattern, text, re.DOTALL)
112
+ # if match:
113
+ # # Extract matched text
114
+ # matched_text = match.group(1)
115
+ #
116
+ # # Filter out lines beginning with '>'
117
+ # filtered_lines = [line for line in matched_text.split('\n') if not line.strip().startswith('>')]
118
+ # filtered_text = '\n'.join(filtered_lines)
119
+ #
120
+ # # Replace consecutive \n with a single \n
121
+ # extracted_text = re.sub(r'\n+', '\n', filtered_text)
122
+ # if not extracted_text.isspace() and extracted_text:
123
+ # return extracted_text.strip() # Return the cleaned text - I removed the title casing
124
+ #
125
+ # return None
126
+
127
+
128
+ from typing import Optional, List
129
+
130
+ def extract_attribute(text: str, labels: List[str]) -> Optional[str]:
78
131
  """
79
- Extracts the attribute value from a string.
132
+ def extract_attribute(text: str, labels: List[str]) -> Optional[str]:
80
133
 
81
- Args:
82
- text: The input string.
83
- labels: List of equivalent labels to search for
134
+ Extracts the attribute value from a string while:
135
+ - Preserving single newlines within the matched text.
136
+ - Removing lines starting with '>'.
84
137
 
85
- Returns:
86
- The value of the attribute, or None if not found.
138
+ Args:
139
+ text: The input string containing labeled sections.
140
+ labels: List of equivalent labels to search for.
87
141
 
88
- Note:
89
- Lines beginning with '>' are ignored.
90
- """
91
- # Iterate over the list of labels
142
+ Returns:
143
+ The cleaned value of the attribute, or None if not found.
144
+ """
92
145
  for label in labels:
93
146
  # Construct pattern for the current label
94
- # text = re.sub(r'\s+', ' ', text).strip() # just added
95
- # text = re.sub(r'\n\n+', '\n\n', text).strip()
96
-
97
- # Replace multiple spaces or tabs with a single space
98
- normalized = re.sub(r'\s+', ' ', text)
99
- # Collapse multiple blank lines into a single one
100
- normalized = re.sub(r'\n\s*\n', '\n', normalized).strip()
101
-
102
- # label = label.strip()
103
- # pattern = rf"##\s*{re.escape(label)}\s*\n(?:\s*\n)*?(.*?)(?:#|___|$)"
104
- # Normalize the label
105
- normalized_label = re.sub(r'\s+', ' ', label.strip())
106
-
107
- # Construct the regex pattern
108
- pattern = rf"##\s*{re.escape(normalized_label)}\s*\n(?:\s*\n)*?(.*?)(?:#|___|$)"
109
- # pattern = rf"##\s+{re.escape(label)}\n(.*?)(?:#|___|$)" # modified from --- to enable embedded tables
110
- match = re.search(pattern, text, re.DOTALL)
147
+ pattern = rf"## {re.escape(label)}\n(.*?)(?=^##|\Z)" # Captures content until the next '##' or end of text
148
+ match = re.search(pattern, text, re.DOTALL | re.MULTILINE)
111
149
  if match:
112
150
  # Extract matched text
113
- matched_text = match.group(1)
151
+ extracted_text = match.group(1)
114
152
 
115
- # Filter out lines beginning with '>'
116
- filtered_lines = [line for line in matched_text.split('\n') if not line.strip().startswith('>')]
117
- filtered_text = '\n'.join(filtered_lines)
153
+ # Remove lines starting with '>'
154
+ filtered_lines = [line for line in extracted_text.splitlines() if not line.lstrip().startswith(">")]
118
155
 
119
- # Replace consecutive \n with a single \n
120
- extracted_text = re.sub(r'\n+', '\n', filtered_text)
121
- if not extracted_text.isspace() and extracted_text:
122
- return extracted_text.strip() # Return the cleaned text - I removed the title casing
156
+ # Join the lines back, preserving single newlines
157
+ cleaned_text = "\n".join(filtered_lines).strip()
123
158
 
159
+ if cleaned_text:
160
+ return cleaned_text # Return the cleaned and formatted text
124
161
  return None
125
162
 
126
163
 
164
+
165
+
127
166
  def process_simple_attribute(txt: str, labels: set, if_missing: str = INFO) -> str | None:
128
167
  """Process a simple attribute based on the provided labels and if_missing value.
129
168
  Extract the attribute value from the text and return it if it exists.
@@ -787,9 +787,6 @@ class GovernanceOfficer(Client2):
787
787
  f"{guid}/update")
788
788
  await self._async_update_element_body_request(url, GOV_DEF_PROPERTIES_LIST, body)
789
789
 
790
- @dynamic_catch
791
- async def _async_update_governance_definition(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
792
- """ Update a governance definition. Async Version."""
793
790
 
794
791
  def update_governance_definition(self, guid: str, body: dict | UpdateStatusRequestBody) -> None:
795
792
  """ Update the properties of a governance definition.
@@ -899,7 +896,7 @@ class GovernanceOfficer(Client2):
899
896
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/{self.url_marker}/governance-defnitions/"
900
897
  f"{guid}/update-status")
901
898
 
902
- await self._async_update_status_request(url, status, body)
899
+ # await self._async_update_status_request(url, status, body)
903
900
 
904
901
  def update_governance_definition_status(self, guid: str, status: str = None,
905
902
  body: dict | UpdateStatusRequestBody = None) -> None:
pyegeria/utils.py CHANGED
@@ -246,6 +246,116 @@ def flatten_dict_to_string(d: dict) -> str:
246
246
  # The decorator logic, which applies @logger.catch dynamically
247
247
 
248
248
 
249
+ import json
250
+ import re
251
+
252
+
253
+ # def parse_to_dict(input_str: str):
254
+ # """
255
+ # Check if a string is valid JSON or a name:value list without braces and convert to a dictionary.
256
+ #
257
+ # Args:
258
+ # input_str: The input string to parse.
259
+ #
260
+ # Returns:
261
+ # dict: A dictionary converted from the input string.
262
+ # None: If the input is neither valid JSON nor a valid name:value list.
263
+ # """
264
+ #
265
+ # if input_str is None:
266
+ # return None
267
+ #
268
+ # # Check if the input string is valid JSON
269
+ # try:
270
+ # result = json.loads(input_str)
271
+ # if isinstance(result, dict): # Ensure it's a dictionary
272
+ # return result
273
+ # except json.JSONDecodeError:
274
+ # pass
275
+ #
276
+ # # Check if input string looks like a name:value list
277
+ # # Supports both comma and newline as separators
278
+ # pattern = r'^(\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9_-]+)\s*:\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9 _-]*)\s*)' \
279
+ # r'(\s*[,|\n]\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9_-]+)\s*:\s*("[^"]+"|\'[^\']+\'|[a-zA-Z0-9 _-]*)\s*)*$'
280
+ # if re.match(pattern, input_str.strip()):
281
+ # try:
282
+ # # Split by ',' or '\n' and process key-value pairs
283
+ # pairs = [pair.split(":", 1) for pair in re.split(r'[,|\n]+', input_str.strip())]
284
+ # return {key.strip().strip('\'"'): value.strip().strip('\'"') for key, value in pairs}
285
+ # except Exception:
286
+ # return None
287
+ #
288
+ # # If neither pattern matches, return None
289
+ # return None
290
+
291
+
292
+ def parse_to_dict(input_str: str) -> dict | None:
293
+ """
294
+ Parse input strings into a dictionary, handling both JSON and key-value pairs.
295
+ Recovers from malformed JSON (e.g., where commas are missing between key-value pairs)
296
+ and supports multiline values.
297
+
298
+ Args:
299
+ input_str (str): The input string to parse.
300
+
301
+ Returns:
302
+ dict: A parsed dictionary if validation is successful, or None if the string cannot be parsed.
303
+ """
304
+ if not input_str:
305
+ return None
306
+
307
+ # Attempt to parse valid JSON
308
+ try:
309
+ result = json.loads(input_str)
310
+ if isinstance(result, dict):
311
+ return result
312
+ except json.JSONDecodeError:
313
+ pass
314
+
315
+ # Fix malformed JSON or attempt alternate parsing for "key: value" patterns
316
+ try:
317
+ # Step 1: Inject missing commas where they are omitted between key-value pairs
318
+ fixed_input = re.sub(
319
+ r'("\s*:[^,}\n]+)\s*("(?![:,}\n]))', # Find missing commas (key-value-value sequences)
320
+ r'\1,\2', # Add a comma between the values
321
+ input_str
322
+ )
323
+
324
+ # Attempt to parse the fixed string as JSON
325
+ try:
326
+ result = json.loads(fixed_input)
327
+ if isinstance(result, dict):
328
+ return result
329
+ except json.JSONDecodeError:
330
+ pass
331
+
332
+ # Step 2: Handle key-value format fallback (supports multiline strings)
333
+ # Matches `key: value` pairs, including multiline quoted values
334
+ key_value_pattern = re.compile(r'''
335
+ (?:"([^"]+)"|'([^']+)'|([a-zA-Z0-9_-]+)) # Key: quoted "key", 'key', or unquoted key
336
+ \s*:\s* # Key-value separator
337
+ (?:"((?:\\.|[^"\\])*?)"|'((?:\\.|[^'\\])*?)'|([^\n,]+)) # Value: quoted or unquoted
338
+ ''', re.VERBOSE | re.DOTALL)
339
+
340
+ matches = key_value_pattern.findall(input_str)
341
+
342
+ # Build dictionary from matches
343
+ result_dict = {}
344
+ for match in matches:
345
+ key = next((group for group in match[:3] if group), "").strip()
346
+ value = next((group for group in match[3:] if group), "").strip()
347
+ result_dict[key] = value
348
+
349
+ if result_dict:
350
+ return result_dict
351
+ except Exception as e:
352
+ # Log or handle parsing exception if needed
353
+ pass
354
+
355
+ # If all parsing attempts fail, return None
356
+ return None
357
+
358
+
249
359
  def dynamic_catch(func: T) -> T:
250
360
  if app_settings.get("enable_logger_catchh", False):
251
361
  return logger.catch(func) # Apply the logger.catch decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyegeria
3
- Version: 5.4.4.1
3
+ Version: 5.4.4.2
4
4
  Summary: A python client for Egeria
5
5
  License: Apache 2.0
6
6
  Keywords: egeria,metadata,governance
@@ -3,6 +3,7 @@ commands/__init__.py,sha256=-_1ApX0GIzkVNl1NajyGMIz7fHbwZfN5DVnBQAOiScA,1174
3
3
  commands/cat/Dr-Egeria_md-orig.py,sha256=ZX20BvRo0fIFisvy5Z-VJDLVyKbQoud89-CUV2S66tU,7336
4
4
  commands/cat/README.md,sha256=-aaAnIT2fcfU63vajgB-RzQk4l4yFdhkyVfSaTPiqRY,967
5
5
  commands/cat/__init__.py,sha256=l4CImkjEiTQKS7QR-RQwzHIgRpfP032Mn_NZEuIILwg,98
6
+ commands/cat/debug_log.2025-09-10_13-48-37_153090.log.zip,sha256=Wx_eCPrVj_km3sNYZu6fe4KlMUTkiuGJ3SSWmD4mOyU,13471
6
7
  commands/cat/dr_egeria_command_help.py,sha256=_HOJd1IplnYhoQm1cwsROS9SxVKILLUf9YNlGMvvdF4,15359
7
8
  commands/cat/dr_egeria_jupyter.py,sha256=rfLVV_84Q8Pqcq1flmijKvZ7sEZFy7JAcAP_NAbb46Y,5656
8
9
  commands/cat/dr_egeria_md.py,sha256=zz-XtRW0sfBOG8kA51OFR5uKQ7JULGoOgXeJBXQBqTM,4889
@@ -4291,28 +4292,29 @@ md_processing/data/commands-working.json,sha256=uCo_azcuuYqGm7QffJeCGj7PyZuZRGdu
4291
4292
  md_processing/data/commands.json,sha256=RIHRggidx9qPwBDVmXagoY0AkVcG-sL0yobl7aiGmHI,1712477
4292
4293
  md_processing/data/generated_format_sets.json,sha256=TXvGK_Gm5ieq9i6u9M1j4CaNPzoV2m0hisKK2fWCtIM,98502
4293
4294
  md_processing/data/generated_format_sets.py,sha256=ZUWlUi5BHdetUQP-Hx8nQqzd3mCEubsJQXjvPmqhY3M,54980
4295
+ md_processing/dr-egeria-outbox/Business-Imperative-DrE-2025-09-11-21-21-15.md,sha256=2_SfdjR5fJXNTzM4Yx2po7vf5t4VmX2GsUedPaRujrQ,1176
4294
4296
  md_processing/dr_egeria.py,sha256=OlzP8LkXqZ79lxsWOnz8zOpcOdhpLp7m_PDE0fi-YPs,19963
4295
4297
  md_processing/dr_egeria_inbox/glossary_creation_experiment.ipynb,sha256=dbzNu90fCKNohOWVSRBOB1GLyd95x8Qw51I5AkaPtso,11552
4296
4298
  md_processing/md_commands/__init__.py,sha256=ssEojzFlSYtY2bHqqOoKo8PFaANZ_kq_gIbtlXnuc2s,93
4297
4299
  md_processing/md_commands/data_designer_commands.py,sha256=oUXM6wvyictQZVzSH43GJdmy5Zm7nWogWuoCNo8Yskw,64389
4298
4300
  md_processing/md_commands/ext_ref_commands.py,sha256=uxVMz3QSJbMxMVYYaDQs9YlBH65Jsw0Wa0oJFcn5GnQ,24546
4299
4301
  md_processing/md_commands/glossary_commands.py,sha256=BEWcJYCWybTvbqcZD7_IFbBKiMpX6b55avrn1nckqO4,33811
4300
- md_processing/md_commands/governance_officer_commands.py,sha256=NCYrx6k1q_duVoF0bMdbyLMjHOn7Xvc0-c50a4NWcGA,21990
4302
+ md_processing/md_commands/governance_officer_commands.py,sha256=ixM-Su3q6XF-MKkVJb8R0wXfgM_ZAiucCINS_LkjgXY,22178
4301
4303
  md_processing/md_commands/old_solution_architect_commands.py,sha256=Hk-_-2aJWoG8LYzTmf84z3rakN9kIQWEM9HM9_lz6xw,55157
4302
4304
  md_processing/md_commands/product_manager_commands.py,sha256=aLagu4Tljd0gITdmvxXIe_sMGcJVkNTrncigG5TXFmo,45749
4303
4305
  md_processing/md_commands/project_commands.py,sha256=s9-n_a0lUU-oAZMYeaW6Nq_Tii9nG4hVIuBuf3q-heI,17762
4304
4306
  md_processing/md_commands/solution_architect_commands.py,sha256=4Ghb8j2wlDdWtHoSCrx5jCJFEJfqII4mnFJ_tqduRKI,52569
4305
4307
  md_processing/md_commands/view_commands.py,sha256=dvRD0Vjv1w9FTfV5W-4EMQBTk2NAUJlIP2jQ411kHS4,11815
4306
4308
  md_processing/md_processing_utils/__init__.py,sha256=LxAmxlcji26ziKV4gGar01d95gL9vgToRIeJW8N-Ifs,80
4307
- md_processing/md_processing_utils/common_md_proc_utils.py,sha256=TWQyypm9xNSf1sipnjEWQqyuaShRSldbooBBd42gODo,56637
4308
- md_processing/md_processing_utils/common_md_utils.py,sha256=dWoJEpp-yF-CPThZ1I5hMM0CSXkqfXBkgxH8rklZz8A,26452
4309
+ md_processing/md_processing_utils/common_md_proc_utils.py,sha256=wZQvmFLryko-Of-MaZ9BspejTSW27V9y6Azoaeb-w0o,58607
4310
+ md_processing/md_processing_utils/common_md_utils.py,sha256=X8Kzva-sRyM6RZ6RbkUeP4fJaVhif0q6z7WhP-hhdm8,26711
4309
4311
  md_processing/md_processing_utils/debug_log,sha256=dDkHai9YpY-9k9-DSFzbaMgZ-AavFw-Vxk2Q1r_34Ls,43746
4310
4312
  md_processing/md_processing_utils/debug_log.2025-09-09_11-10-03_528597.zip,sha256=2hK1jdCdG7aR9FPxFdtmU_OxHBNYLA2vlr-OPqR6sXs,1863
4311
4313
  md_processing/md_processing_utils/determine_width.py,sha256=nzinSuSF9SeuVOfKXsg-l1cqLkNYKZnF6HYfJpft44A,4236
4312
4314
  md_processing/md_processing_utils/dr-egeria-help-2025-09-09T11:10:03.md,sha256=x_J_baA18EsMHW_O-EZiNkXAQ3MEwta8ZLsKPil55O8,166018
4313
4315
  md_processing/md_processing_utils/dr-egeria-help-2025-09-10T14:49:49.md,sha256=qnu8YS-7Ra0GQtPPiCKb4PpQBcJAmfaG50ufFpr9b-s,175356
4314
4316
  md_processing/md_processing_utils/dr-egeria-help-2025-09-10T14:57:46.md,sha256=1gBIR4iuBsAXpGdg4nPgFHOGcLcDCZbNvvg-9cHD4fM,55188
4315
- md_processing/md_processing_utils/extraction_utils.py,sha256=bTeoiAC0DcNl5LjYuXkyk8V3cLhAshp_IFPMeROq7Qk,20930
4317
+ md_processing/md_processing_utils/extraction_utils.py,sha256=nCtsnx_iSNV-h1StZ54GQLzSIqfx3hCNnmdDTWUKC10,22350
4316
4318
  md_processing/md_processing_utils/gen_format_sets.py,sha256=R5IvrajER0Xj9kZ99UxRS5Zoa5dAVPcLwCI7kf2iUak,16476
4317
4319
  md_processing/md_processing_utils/generate_dr_help.py,sha256=MJWlr_22Y9pjWqQbfSLb6C-t1GlQNlbWXkCmDnphfME,7419
4318
4320
  md_processing/md_processing_utils/generate_md_cmd_templates.py,sha256=SVdtlA6Nc9JIN-pORGbf-_shEP7egReuVejEcMjxRYM,5797
@@ -4348,7 +4350,7 @@ pyegeria/external_references.py,sha256=XPJxE57d4e7ooasSZiYTpoppo43z1oLYvthD58__v
4348
4350
  pyegeria/feedback_manager_omvs.py,sha256=0xBs0p54vmdfVYYgQ8pOanLC4fxfgTk1Z61Y6D1U7_I,152978
4349
4351
  pyegeria/full_omag_server_config.py,sha256=CQqLCy_3DZFvJZEOcGf50HWdFaWpiAIs6z-kKyjvpDA,47464
4350
4352
  pyegeria/glossary_manager.py,sha256=LsemH2bC1jj6mquLxZMXsoZY77D8gTdWKXNs9Gee6mQ,110173
4351
- pyegeria/governance_officer.py,sha256=3c-8PFUzWzPHc7OOX-Kt7vVaj4rSttCvOsgXSDX3POw,100217
4353
+ pyegeria/governance_officer.py,sha256=d-hflvrMt-7hH6j47OicoolpU8rf6uzV-IZRgWL485Y,100023
4352
4354
  pyegeria/load_config.py,sha256=XDwPAHB3MvGRuoP8kg1lJJAI4BgMWZ3TYxfxYROgJj4,1188
4353
4355
  pyegeria/load_config_orig.py,sha256=lOM37vdIBcYfLQFTLP5bDuNc7vTFGBNYPfqHtWGNvA4,11624
4354
4356
  pyegeria/logging_configuration.py,sha256=BxTQRN-7OOdk5t1f1xSn8gKU8iT-MfWEgbn6cYWrRsY,7674
@@ -4367,11 +4369,11 @@ pyegeria/runtime_manager_omvs.py,sha256=bVAFJPRnIbTxdzmDHx7XgJBlyh_ZyHiKeUGqFT1O
4367
4369
  pyegeria/server_operations.py,sha256=dTqUEmX1B77b0x61OSU0aonsW8KwIpfzb3eioRpwaiI,16832
4368
4370
  pyegeria/solution_architect.py,sha256=hZQOEtenUGfTGYtI7kD0zI_Nf2IBy-RMNoasfp8BgGs,236903
4369
4371
  pyegeria/template_manager_omvs.py,sha256=chBljs1vy5wr9DRAtbvIt4Cob_7HxGfxLkCNlDTM-rQ,42755
4370
- pyegeria/utils.py,sha256=qgiYEdCRrrL6SpX1sceZQVYR40-rfFAhUJEhsubcx80,6889
4372
+ pyegeria/utils.py,sha256=dh7PyRPjMZkVSGodRwJxqgCndL4TvKK4g891f0rZ3HI,10762
4371
4373
  pyegeria/valid_metadata_omvs.py,sha256=Xq9DqBQvBFFJzaFIRKcVZ2k4gJvSh9yeXs_j-O3vn1w,65050
4372
4374
  pyegeria/x_action_author_omvs.py,sha256=RcqSzahUKCtvb_3u_wyintAlc9WFkC_2v0E12TZs8lQ,6433
4373
- pyegeria-5.4.4.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
4374
- pyegeria-5.4.4.1.dist-info/METADATA,sha256=YzOue9FT_FkpqffG-4ZxbbZbWq-A0lzdbjpbjz4Sxbw,6292
4375
- pyegeria-5.4.4.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
4376
- pyegeria-5.4.4.1.dist-info/entry_points.txt,sha256=HAS-LHaaBfkaZ19XU9g5mXwn2uj2HK99isdijI-VIDk,6353
4377
- pyegeria-5.4.4.1.dist-info/RECORD,,
4375
+ pyegeria-5.4.4.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
4376
+ pyegeria-5.4.4.2.dist-info/METADATA,sha256=7akG9oixZL63LJlSGv_hLTNy2lbfLvvtcA_PJ1hKmr8,6292
4377
+ pyegeria-5.4.4.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
4378
+ pyegeria-5.4.4.2.dist-info/entry_points.txt,sha256=HAS-LHaaBfkaZ19XU9g5mXwn2uj2HK99isdijI-VIDk,6353
4379
+ pyegeria-5.4.4.2.dist-info/RECORD,,