pyegeria 5.3.8.1__py3-none-any.whl → 5.3.8.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.
@@ -10,7 +10,7 @@ added in subsequent versions of the glossary_omvs module.
10
10
  import asyncio
11
11
  from datetime import datetime
12
12
 
13
- from pyegeria import NO_GLOSSARIES_FOUND, NO_CATEGORIES_FOUND, NO_TERMS_FOUND
13
+ from pyegeria import NO_GLOSSARIES_FOUND, NO_CATEGORIES_FOUND, NO_TERMS_FOUND, max_paging_size
14
14
  import json
15
15
  from pyegeria._client import Client
16
16
  from pyegeria._validators import validate_guid, validate_name, validate_search_string
@@ -102,132 +102,785 @@ class GlossaryBrowser(Client):
102
102
  output = f"## {attribute_title}\n{attribute_value}\n\n"
103
103
  return output
104
104
 
105
+ def _format_for_markdown_table(self, text: str) -> str:
106
+ """
107
+ Format text for markdown tables by replacing newlines with spaces and escaping pipe characters.
105
108
 
106
- def generate_glossaries_md(self, elements: list | dict, search_string: str, output_format: str = 'MD')-> str:
107
- elements_md, elements_action = self.make_preamble(obj_type="Glossary", search_string=search_string,
108
- output_format=output_format)
109
- if isinstance(elements, dict):
110
- elements = [elements]
109
+ Args:
110
+ text (str): The text to format
111
111
 
112
- for element in elements:
113
- guid = element['elementHeader'].get("guid", None)
114
- properties = element['glossaryProperties']
115
- display_name = properties.get("displayName", None)
116
- description = properties.get("description", None)
117
- language = properties.get("language", None)
118
- usage = properties.get("usage", None)
119
- qualified_name = properties.get("qualifiedName", None)
120
-
121
- if output_format in ['FORM','MD']:
122
- elements_md += f"# {elements_action}\n\n"
123
- elements_md += f"## Glossary Name \n\n{display_name}\n\n"
112
+ Returns:
113
+ str: Formatted text safe for markdown tables
114
+ """
115
+ if not text:
116
+ return ""
117
+ # Replace newlines with spaces and escape pipe characters
118
+ return text.replace("\n", " ").replace("|", "\\|")
124
119
 
125
- elif output_format == 'REPORT':
126
- elements_md += f"# Glossary Name: {display_name}\n\n"
127
- else:
128
- elements_md += f"## Glossary Name \n\n{display_name}\n\n"
129
120
 
130
- elements_md += self.make_md_attribute( "description", description, output_format)
131
- elements_md += self.make_md_attribute("language", language, output_format)
132
- elements_md += self.make_md_attribute("usage", usage, output_format)
133
- elements_md += self.make_md_attribute("qualified name", qualified_name, output_format)
134
- elements_md += self.make_md_attribute("GUID", guid, output_format)
135
- # elements_md += MD_SEPERATOR
121
+ def _extract_glossary_properties(self, element: dict) -> dict:
122
+ """
123
+ Extract common properties from a glossary element.
136
124
 
137
- return elements_md
125
+ Args:
126
+ element (dict): The glossary element
138
127
 
139
- def generate_terms_md(self, elements: list | dict, search_string: str, output_format: str = 'MD') -> str:
140
- elements_md, elements_action = self.make_preamble(obj_type="Term", search_string=search_string, output_format=output_format)
141
- if isinstance(elements, dict):
142
- elements = [elements]
128
+ Returns:
129
+ dict: Dictionary of extracted properties
130
+ """
131
+ guid = element['elementHeader'].get("guid", None)
132
+ properties = element['glossaryProperties']
133
+ display_name = properties.get("displayName", "") or ""
134
+ description = properties.get("description", "") or ""
135
+ language = properties.get("language", "") or ""
136
+ usage = properties.get("usage", "") or ""
137
+ qualified_name = properties.get("qualifiedName", "") or ""
138
+
139
+ return {
140
+ 'guid': guid,
141
+ 'properties': properties,
142
+ 'display_name': display_name,
143
+ 'description': description,
144
+ 'language': language,
145
+ 'usage': usage,
146
+ 'qualified_name': qualified_name
147
+ }
148
+
149
+ def _generate_entity_md(self, elements: list, elements_action: str, output_format: str,
150
+ entity_type: str, extract_properties_func, get_additional_props_func=None) -> str:
151
+ """
152
+ Generic method to generate markdown for entities (glossaries, terms, categories).
153
+
154
+ Args:
155
+ elements (list): List of entity elements
156
+ elements_action (str): Action description for elements
157
+ output_format (str): Output format
158
+ entity_type (str): Type of entity (Glossary, Term, Category)
159
+ extract_properties_func: Function to extract properties from an element
160
+ get_additional_props_func: Optional function to get additional properties
161
+
162
+ Returns:
163
+ str: Markdown representation
164
+ """
165
+ elements_md = ""
143
166
 
144
167
  for element in elements:
145
- guid = element['elementHeader'].get("guid", None)
146
- element_properties = element['glossaryTermProperties']
147
- display_name = element_properties.get("displayName", None)
148
- summary = element_properties.get("summary", None)
149
- description = element_properties.get("description", None)
150
- examples = element_properties.get("examples", None)
151
- usage = element_properties.get("usage", None)
152
- pub_version = element_properties.get("publishfinVersionIdentifier", None)
153
- qualified_name = element_properties.get("qualifiedName", None)
154
- status = element['elementHeader'].get('status', None)
155
-
156
- glossary_guid = element['elementHeader'].get('classifications', [{}])[0].get('classificationProperties', {}).get('anchorGUID', None)
157
- glossary_qualified_name = self.get_glossary_by_guid(glossary_guid)['glossaryProperties']['qualifiedName']
158
-
159
- category_list_md = "\n"
160
- category_list = self.get_categories_for_term(guid)
161
- if type(category_list) is str and category_list == NO_CATEGORIES_FOUND:
162
- category_list_md = ['---']
163
- elif isinstance(category_list, list) and len(category_list) > 0:
164
- first_cat = True
165
- for category in category_list:
166
- category_name = category["glossaryCategoryProperties"].get("qualifiedName", '---')
167
- if first_cat:
168
- category_list_md += f" {category_name}\n"
169
- first_cat = False
170
- else:
171
- category_list_md += f", {category_name}\n"
168
+ props = extract_properties_func(element)
169
+
170
+ # Get additional properties if function is provided
171
+ additional_props = {}
172
+ if get_additional_props_func:
173
+ additional_props = get_additional_props_func(element, props['guid'])
172
174
 
175
+ # Format header based on output format
173
176
  if output_format in ['FORM', 'MD']:
174
177
  elements_md += f"# {elements_action}\n\n"
175
- elements_md += f"## Term Name \n\n{display_name}\n\n"
178
+ elements_md += f"## {entity_type} Name \n\n{props['display_name']}\n\n"
176
179
  elif output_format == 'REPORT':
177
- elements_md += f"# Term Name: {display_name}\n\n"
180
+ elements_md += f"# {entity_type} Name: {props['display_name']}\n\n"
178
181
  else:
179
- elements_md += f"## Term Name \n\n{display_name}\n\n"
180
-
181
- elements_md += self.make_md_attribute("summary", summary, output_format)
182
- elements_md += self.make_md_attribute("in glossary", glossary_qualified_name, output_format)
183
- elements_md += self.make_md_attribute( "categories", category_list_md, output_format)
184
- elements_md += self.make_md_attribute( "status", status, output_format)
185
- elements_md += self.make_md_attribute( "description", description, output_format)
186
- elements_md += self.make_md_attribute( "examples", examples, output_format)
187
- elements_md += self.make_md_attribute("usage", usage, output_format)
188
- elements_md += self.make_md_attribute("version", pub_version, output_format)
189
- elements_md += self.make_md_attribute("qualified name", qualified_name, output_format)
190
- elements_md += self.make_md_attribute("GUID", guid, output_format)
191
- elements_md += MD_SEPERATOR
182
+ elements_md += f"## {entity_type} Name \n\n{props['display_name']}\n\n"
183
+
184
+ # Add common attributes
185
+ for key, value in props.items():
186
+ if key not in ['guid', 'properties', 'display_name']:
187
+ elements_md += self.make_md_attribute(key.replace('_', ' '), value, output_format)
188
+
189
+ # Add additional properties
190
+ for key, value in additional_props.items():
191
+ elements_md += self.make_md_attribute(key.replace('_', ' '), value, output_format)
192
+
193
+ # Add GUID
194
+ elements_md += self.make_md_attribute("qualified name", props['qualified_name'], output_format)
195
+ elements_md += self.make_md_attribute("GUID", props['guid'], output_format)
196
+
197
+ # Add separator if not the last element
198
+ if element != elements[-1]:
199
+ elements_md += MD_SEPERATOR
192
200
 
193
201
  return elements_md
194
202
 
195
- def generate_categories_md(self, elements: list | dict, search_string: str, output_format: str = 'MD')-> str:
196
- elements_md, elements_action = self.make_preamble(obj_type="Categories", search_string=search_string,
203
+ def _generate_glossary_md(self, elements: list, elements_action: str, output_format: str) -> str:
204
+ """
205
+ Generate markdown for glossaries.
206
+
207
+ Args:
208
+ elements (list): List of glossary elements
209
+ elements_action (str): Action description for elements
210
+ output_format (str): Output format
211
+
212
+ Returns:
213
+ str: Markdown representation
214
+ """
215
+ return self._generate_entity_md(
216
+ elements=elements,
217
+ elements_action=elements_action,
218
+ output_format=output_format,
219
+ entity_type="Glossary",
220
+ extract_properties_func=self._extract_glossary_properties
221
+ )
222
+
223
+ def _generate_entity_md_table(self, elements: list, search_string: str, entity_type: str,
224
+ extract_properties_func, columns: list, get_additional_props_func=None) -> str:
225
+ """
226
+ Generic method to generate a markdown table for entities (glossaries, terms, categories).
227
+
228
+ Args:
229
+ elements (list): List of entity elements
230
+ search_string (str): The search string used
231
+ entity_type (str): Type of entity (Glossary, Term, Category)
232
+ extract_properties_func: Function to extract properties from an element
233
+ columns: List of column definitions, each containing 'name', 'key', and 'format' (optional)
234
+ get_additional_props_func: Optional function to get additional properties
235
+
236
+ Returns:
237
+ str: Markdown table
238
+ """
239
+ # Create table header
240
+ elements_md = f"# {entity_type}s Table\n\n"
241
+ elements_md += f"{entity_type}s found from the search string: `{search_string}`\n\n"
242
+
243
+ # Add column headers
244
+ header_row = "| "
245
+ separator_row = "|"
246
+ for column in columns:
247
+ header_row += f"{column['name']} | "
248
+ separator_row += "-------------|"
249
+
250
+ elements_md += header_row + "\n"
251
+ elements_md += separator_row + "\n"
252
+
253
+ # Add rows
254
+ for element in elements:
255
+ props = extract_properties_func(element)
256
+
257
+ # Get additional properties if function is provided
258
+ additional_props = {}
259
+ if get_additional_props_func:
260
+ additional_props = get_additional_props_func(element, props['guid'])
261
+
262
+ # Build row
263
+ row = "| "
264
+ for column in columns:
265
+ key = column['key']
266
+ value = ""
267
+
268
+ # Check if the key is in props or additional_props
269
+ if key in props:
270
+ value = props[key]
271
+ elif key in additional_props:
272
+ value = additional_props[key]
273
+
274
+ # Format the value if needed
275
+ if 'format' in column and column['format']:
276
+ value = self._format_for_markdown_table(value)
277
+
278
+ row += f"{value} | "
279
+
280
+ elements_md += row + "\n"
281
+
282
+ return elements_md
283
+
284
+ def _generate_glossary_md_table(self, elements: list, search_string: str) -> str:
285
+ """
286
+ Generate a markdown table for glossaries.
287
+
288
+ Args:
289
+ elements (list): List of glossary elements
290
+ search_string (str): The search string used
291
+
292
+ Returns:
293
+ str: Markdown table
294
+ """
295
+ columns = [
296
+ {'name': 'Glossary Name', 'key': 'display_name'},
297
+ {'name': 'Qualified Name', 'key': 'qualified_name'},
298
+ {'name': 'Language', 'key': 'language', 'format': True},
299
+ {'name': 'Description', 'key': 'description', 'format': True},
300
+ {'name': 'Usage', 'key': 'usage', 'format': True}
301
+ ]
302
+
303
+ return self._generate_entity_md_table(
304
+ elements=elements,
305
+ search_string=search_string,
306
+ entity_type="Glossary",
307
+ extract_properties_func=self._extract_glossary_properties,
308
+ columns=columns
309
+ )
310
+
311
+ def _generate_entity_dict(self, elements: list, extract_properties_func, get_additional_props_func=None,
312
+ include_keys=None, exclude_keys=None) -> list:
313
+ """
314
+ Generic method to generate a dictionary representation of entities (glossaries, terms, categories).
315
+
316
+ Args:
317
+ elements (list): List of entity elements
318
+ extract_properties_func: Function to extract properties from an element
319
+ get_additional_props_func: Optional function to get additional properties
320
+ include_keys: Optional list of keys to include in the result (if None, include all)
321
+ exclude_keys: Optional list of keys to exclude from the result (if None, exclude none)
322
+
323
+ Returns:
324
+ list: List of entity dictionaries
325
+ """
326
+ result = []
327
+
328
+ for element in elements:
329
+ props = extract_properties_func(element)
330
+
331
+ # Get additional properties if function is provided
332
+ additional_props = {}
333
+ if get_additional_props_func:
334
+ additional_props = get_additional_props_func(element, props['guid'])
335
+
336
+ # Create entity dictionary
337
+ entity_dict = {}
338
+
339
+ # Add properties based on include/exclude lists
340
+ for key, value in props.items():
341
+ if key != 'properties': # Skip the raw properties object
342
+ if (include_keys is None or key in include_keys) and (exclude_keys is None or key not in exclude_keys):
343
+ entity_dict[key] = value
344
+
345
+ # Add additional properties
346
+ for key, value in additional_props.items():
347
+ if (include_keys is None or key in include_keys) and (exclude_keys is None or key not in exclude_keys):
348
+ entity_dict[key] = value
349
+
350
+ result.append(entity_dict)
351
+
352
+ return result
353
+
354
+ def _generate_glossary_dict(self, elements: list) -> list:
355
+ """
356
+ Generate a dictionary representation of glossaries.
357
+
358
+ Args:
359
+ elements (list): List of glossary elements
360
+
361
+ Returns:
362
+ list: List of glossary dictionaries
363
+ """
364
+ return self._generate_entity_dict(
365
+ elements=elements,
366
+ extract_properties_func=self._extract_glossary_properties,
367
+ exclude_keys=['properties']
368
+ )
369
+
370
+ def generate_glossaries_md(self, elements: list | dict, search_string: str, output_format: str = 'MD')-> str | list:
371
+ """
372
+ Generate markdown or dictionary representation of glossaries.
373
+
374
+ Args:
375
+ elements (list | dict): List or dictionary of glossary elements
376
+ search_string (str): The search string used
377
+ output_format (str): Output format (MD, FORM, REPORT, LIST, DICT)
378
+
379
+ Returns:
380
+ str | list: Markdown string or list of dictionaries depending on output_format
381
+ """
382
+ elements_md, elements_action = self.make_preamble(obj_type="Glossary", search_string=search_string,
197
383
  output_format=output_format)
198
384
  if isinstance(elements, dict):
199
385
  elements = [elements]
200
386
 
201
- for element in elements:
202
- guid = element['elementHeader'].get("guid", None)
203
- properties = element['glossaryCategoryProperties']
204
- display_name = properties.get("displayName", None)
205
- description = properties.get("description", None)
206
- qualified_name = properties.get("qualifiedName", None)
207
-
208
- classification_props = element["elementHeader"]['classifications'][0].get('classificationProperties', None)
209
- glossary_qualified_name = '---'
210
- if classification_props is not None:
211
- glossary_guid = classification_props.get('anchorGUID', '---')
212
- glossary_qualified_name = (
213
- self.get_glossary_by_guid(glossary_guid))['glossaryProperties']['qualifiedName']
387
+ # If output format is LIST, create a markdown table
388
+ if output_format == 'LIST':
389
+ return self._generate_glossary_md_table(elements, search_string)
214
390
 
215
- if output_format in ['FORM', 'MD']:
216
- elements_md += f"# {elements_action}\n\n"
217
- elements_md += f"## Category Name \n\n{display_name}\n\n"
391
+ # If output format is DICT, return a dictionary structure
392
+ elif output_format == 'DICT':
393
+ return self._generate_glossary_dict(elements)
218
394
 
219
- elif output_format == 'REPORT':
220
- elements_md += f"# Category Name: {display_name}\n\n"
221
- else:
222
- elements_md += f"## Category Name \n\n{display_name}\n\n"
395
+ # Original implementation for other formats (MD, FORM, REPORT)
396
+ elements_md += self._generate_glossary_md(elements, elements_action, output_format)
397
+ return elements_md
398
+
399
+ def _extract_term_properties(self, element: dict) -> dict:
400
+ """
401
+ Extract common properties from a term element.
402
+
403
+ Args:
404
+ element (dict): The term element
405
+
406
+ Returns:
407
+ dict: Dictionary of extracted properties
408
+ """
409
+ guid = element['elementHeader'].get("guid", None)
410
+ properties = element['glossaryTermProperties']
411
+ display_name = properties.get("displayName", "") or ""
412
+ summary = properties.get("summary", "") or ""
413
+ description = properties.get("description", "") or ""
414
+ examples = properties.get("examples", "") or ""
415
+ usage = properties.get("usage", "") or ""
416
+ pub_version = properties.get("publishfinVersionIdentifier", "") or ""
417
+ qualified_name = properties.get("qualifiedName", "") or ""
418
+ status = element['elementHeader'].get('status', "") or ""
419
+
420
+ return {
421
+ 'guid': guid,
422
+ 'properties': properties,
423
+ 'display_name': display_name,
424
+ 'summary': summary,
425
+ 'description': description,
426
+ 'examples': examples,
427
+ 'usage': usage,
428
+ 'pub_version': pub_version,
429
+ 'qualified_name': qualified_name,
430
+ 'status': status
431
+ }
432
+
433
+ def _get_categories_for_term(self, term_guid: str) -> tuple[list, str]:
434
+ """
435
+ Get a list of categories for a given term.
436
+
437
+ Args:
438
+ term_guid (str): The GUID of the term
439
+
440
+ Returns:
441
+ tuple: A tuple containing:
442
+ - list: List of category names
443
+ - str: Formatted string of category names for markdown
444
+ """
445
+ category_names = []
446
+ category_list_md = "\n"
447
+
448
+ category_list = self.get_categories_for_term(term_guid)
449
+ if type(category_list) is str and category_list == NO_CATEGORIES_FOUND:
450
+ category_list_md = '---'
451
+ elif isinstance(category_list, list) and len(category_list) > 0:
452
+ first_cat = True
453
+ for category in category_list:
454
+ category_name = category["glossaryCategoryProperties"].get("qualifiedName", '---')
455
+ if category_name:
456
+ category_names.append(category_name)
457
+ if first_cat:
458
+ category_list_md += f" {category_name}\n"
459
+ first_cat = False
460
+ else:
461
+ category_list_md += f", {category_name}\n"
462
+ else:
463
+ category_list_md = '---'
464
+
465
+ return category_names, category_list_md
466
+
467
+ def _get_term_table_properties(self, element: dict, term_guid: str) -> dict:
468
+ """
469
+ Get properties for a term table row.
470
+
471
+ Args:
472
+ element (dict): The term element
473
+ term_guid (str): The GUID of the term
474
+
475
+ Returns:
476
+ dict: Dictionary of properties for the table row
477
+ """
478
+ # Get glossary information
479
+ glossary_qualified_name = self._get_glossary_name_for_element(element)
480
+
481
+ # Get categories
482
+ category_names, _ = self._get_categories_for_term(term_guid)
483
+ categories_str = ", ".join(category_names) if category_names else "---"
484
+
485
+ return {
486
+ 'glossary': glossary_qualified_name,
487
+ 'categories_str': categories_str
488
+ }
489
+
490
+ def _generate_term_md_table(self, elements: list, search_string: str) -> str:
491
+ """
492
+ Generate a markdown table for terms.
493
+
494
+ Args:
495
+ elements (list): List of term elements
496
+ search_string (str): The search string used
497
+
498
+ Returns:
499
+ str: Markdown table
500
+ """
501
+ columns = [
502
+ {'name': 'Term Name', 'key': 'display_name'},
503
+ {'name': 'Qualified Name', 'key': 'qualified_name'},
504
+ {'name': 'Summary', 'key': 'summary', 'format': True},
505
+ {'name': 'Glossary', 'key': 'glossary'},
506
+ {'name': 'Categories', 'key': 'categories_str', 'format': True}
507
+ ]
508
+
509
+ return self._generate_entity_md_table(
510
+ elements=elements,
511
+ search_string=search_string,
512
+ entity_type="Term",
513
+ extract_properties_func=self._extract_term_properties,
514
+ columns=columns,
515
+ get_additional_props_func=self._get_term_table_properties
516
+ )
517
+
518
+ def _get_term_dict_properties(self, element: dict, term_guid: str) -> dict:
519
+ """
520
+ Get additional properties for a term dictionary.
521
+
522
+ Args:
523
+ element (dict): The term element
524
+ term_guid (str): The GUID of the term
525
+
526
+ Returns:
527
+ dict: Dictionary of additional properties
528
+ """
529
+ # Get glossary information
530
+ glossary_qualified_name = self._get_glossary_name_for_element(element)
531
+
532
+ # Get categories
533
+ category_names, _ = self._get_categories_for_term(term_guid)
534
+
535
+ return {
536
+ 'in_glossary': glossary_qualified_name,
537
+ 'categories': category_names,
538
+ 'version': element['glossaryTermProperties'].get('publishfinVersionIdentifier', '')
539
+ }
540
+
541
+ def _generate_term_dict(self, elements: list) -> list:
542
+ """
543
+ Generate a dictionary representation of terms.
544
+
545
+ Args:
546
+ elements (list): List of term elements
547
+
548
+ Returns:
549
+ list: List of term dictionaries
550
+ """
551
+ return self._generate_entity_dict(
552
+ elements=elements,
553
+ extract_properties_func=self._extract_term_properties,
554
+ get_additional_props_func=self._get_term_dict_properties,
555
+ exclude_keys=['properties', 'pub_version'] # Exclude raw properties and pub_version (renamed to version)
556
+ )
557
+
558
+ def _get_term_additional_properties(self, element: dict, term_guid: str) -> dict:
559
+ """
560
+ Get additional properties for a term.
561
+
562
+ Args:
563
+ element (dict): The term element
564
+ term_guid (str): The GUID of the term
565
+
566
+ Returns:
567
+ dict: Dictionary of additional properties
568
+ """
569
+ # Get glossary information
570
+ glossary_qualified_name = self._get_glossary_name_for_element(element)
571
+
572
+ # Get categories
573
+ _, category_list_md = self._get_categories_for_term(term_guid)
574
+
575
+ return {
576
+ 'in_glossary': glossary_qualified_name,
577
+ 'categories': category_list_md
578
+ }
579
+
580
+ def _generate_term_md(self, elements: list, elements_action: str, output_format: str) -> str:
581
+ """
582
+ Generate markdown for terms.
583
+
584
+ Args:
585
+ elements (list): List of term elements
586
+ elements_action (str): Action description for elements
587
+ output_format (str): Output format
588
+
589
+ Returns:
590
+ str: Markdown representation
591
+ """
592
+ return self._generate_entity_md(
593
+ elements=elements,
594
+ elements_action=elements_action,
595
+ output_format=output_format,
596
+ entity_type="Term",
597
+ extract_properties_func=self._extract_term_properties,
598
+ get_additional_props_func=self._get_term_additional_properties
599
+ )
600
+
601
+ def generate_terms_md(self, elements: list | dict, search_string: str, output_format: str = 'MD') -> str | list:
602
+ """
603
+ Generate markdown or dictionary representation of terms.
604
+
605
+ Args:
606
+ elements (list | dict): List or dictionary of term elements
607
+ search_string (str): The search string used
608
+ output_format (str): Output format (MD, MD-TABLE, DICT, FORM, REPORT)
609
+
610
+ Returns:
611
+ str | list: Markdown string or list of dictionaries depending on output_format
612
+ """
613
+ elements_md, elements_action = self.make_preamble(obj_type="Term", search_string=search_string, output_format=output_format)
614
+ if isinstance(elements, dict):
615
+ elements = [elements]
616
+
617
+ # If output format is MD-TABLE, create a markdown table
618
+ if output_format == 'LIST':
619
+ return self._generate_term_md_table(elements, search_string)
620
+
621
+ # If output format is DICT, return a dictionary structure
622
+ elif output_format == 'DICT':
623
+ return self._generate_term_dict(elements)
624
+
625
+ # Original implementation for other formats (MD, FORM, REPORT)
626
+ elements_md += self._generate_term_md(elements, elements_action, output_format)
627
+ return elements_md
223
628
 
629
+ def _get_parent_category_name(self, category_guid: str) -> str:
630
+ """
631
+ Get the parent category name for a given category.
632
+
633
+ Args:
634
+ category_guid (str): The GUID of the category
635
+
636
+ Returns:
637
+ str: The parent category name or '---' if no parent
638
+ """
639
+ parent_cat = self.get_category_parent(category_guid)
640
+ if isinstance(parent_cat, str):
641
+ return '---'
642
+ return parent_cat['glossaryCategoryProperties']['qualifiedName']
643
+
644
+ def _get_subcategories_list(self, category_guid: str) -> tuple[list, str]:
645
+ """
646
+ Get a list of subcategories for a given category.
647
+
648
+ Args:
649
+ category_guid (str): The GUID of the category
650
+
651
+ Returns:
652
+ tuple: A tuple containing:
653
+ - list: List of subcategory names
654
+ - str: Formatted string of subcategory names for markdown
655
+ """
656
+ subcategories = self.get_glossary_subcategories(category_guid)
657
+ subcategory_list = []
658
+
659
+ if isinstance(subcategories, str) and subcategories == NO_CATEGORIES_FOUND:
660
+ subcategory_list_md = '---'
661
+ elif isinstance(subcategories, list) and len(subcategories) > 0:
662
+ for subcat in subcategories:
663
+ subcat_name = subcat["glossaryCategoryProperties"].get("qualifiedName", '')
664
+ if subcat_name:
665
+ subcategory_list.append(subcat_name)
666
+ subcategory_list_md = ", ".join(subcategory_list)
667
+ else:
668
+ subcategory_list_md = '---'
669
+
670
+ return subcategory_list, subcategory_list_md
671
+
672
+ def _get_glossary_name_for_element(self, element: dict) -> str:
673
+ """
674
+ Get the glossary name for a given element.
675
+
676
+ Args:
677
+ element (dict): The element dictionary
678
+
679
+ Returns:
680
+ str: The glossary name or '---' if not found
681
+ """
682
+ classification_props = element["elementHeader"]['classifications'][0].get('classificationProperties', None)
683
+ if classification_props is None:
684
+ return '---'
685
+
686
+ glossary_guid = classification_props.get('anchorGUID', '---')
687
+ if glossary_guid == '---':
688
+ return '---'
689
+
690
+ glossary = self.get_glossary_by_guid(glossary_guid)
691
+ return glossary['glossaryProperties']['qualifiedName']
692
+
693
+ def _extract_category_properties(self, element: dict) -> dict:
694
+ """
695
+ Extract common properties from a category element.
696
+
697
+ Args:
698
+ element (dict): The category element
699
+
700
+ Returns:
701
+ dict: Dictionary of extracted properties
702
+ """
703
+ guid = element['elementHeader'].get("guid", None)
704
+ properties = element['glossaryCategoryProperties']
705
+ display_name = properties.get("displayName", "") or ""
706
+ description = properties.get("description", "") or ""
707
+ qualified_name = properties.get("qualifiedName", "") or ""
708
+
709
+ return {
710
+ 'guid': guid,
711
+ 'properties': properties,
712
+ 'display_name': display_name,
713
+ 'description': description,
714
+ 'qualified_name': qualified_name
715
+ }
716
+
717
+ def _get_category_table_properties(self, element: dict, category_guid: str) -> dict:
718
+ """
719
+ Get properties for a category table row.
720
+
721
+ Args:
722
+ element (dict): The category element
723
+ category_guid (str): The GUID of the category
724
+
725
+ Returns:
726
+ dict: Dictionary of properties for the table row
727
+ """
728
+ # Get parent category
729
+ parent_cat_md = self._get_parent_category_name(category_guid)
730
+
731
+ # Get subcategories
732
+ _, subcategory_list_md = self._get_subcategories_list(category_guid)
733
+
734
+ return {
735
+ 'parent_category': parent_cat_md,
736
+ 'subcategories': subcategory_list_md
737
+ }
738
+
739
+ def _generate_category_md_table(self, elements: list, search_string: str) -> str:
740
+ """
741
+ Generate a markdown table for categories.
742
+
743
+ Args:
744
+ elements (list): List of category elements
745
+ search_string (str): The search string used
746
+
747
+ Returns:
748
+ str: Markdown table
749
+ """
750
+ columns = [
751
+ {'name': 'Display Name', 'key': 'display_name'},
752
+ {'name': 'Description', 'key': 'description', 'format': True},
753
+ {'name': 'Qualified Name', 'key': 'qualified_name'},
754
+ {'name': 'Parent Category', 'key': 'parent_category'},
755
+ {'name': 'Subcategories', 'key': 'subcategories', 'format': True}
756
+ ]
757
+
758
+ return self._generate_entity_md_table(
759
+ elements=elements,
760
+ search_string=search_string,
761
+ entity_type="Category",
762
+ extract_properties_func=self._extract_category_properties,
763
+ columns=columns,
764
+ get_additional_props_func=self._get_category_table_properties
765
+ )
766
+
767
+ def _get_category_dict_properties(self, element: dict, category_guid: str) -> dict:
768
+ """
769
+ Get additional properties for a category dictionary.
770
+
771
+ Args:
772
+ element (dict): The category element
773
+ category_guid (str): The GUID of the category
774
+
775
+ Returns:
776
+ dict: Dictionary of additional properties
777
+ """
778
+ # Get parent category
779
+ parent_cat_md = self._get_parent_category_name(category_guid)
780
+
781
+ # Get subcategories
782
+ subcategory_list, _ = self._get_subcategories_list(category_guid)
783
+
784
+ # Get glossary information
785
+ glossary_qualified_name = self._get_glossary_name_for_element(element)
786
+
787
+ return {
788
+ 'parent_category': parent_cat_md,
789
+ 'subcategories': subcategory_list,
790
+ 'in_glossary': glossary_qualified_name
791
+ }
792
+
793
+ def _generate_category_dict(self, elements: list) -> list:
794
+ """
795
+ Generate a dictionary representation of categories.
796
+
797
+ Args:
798
+ elements (list): List of category elements
799
+
800
+ Returns:
801
+ list: List of category dictionaries
802
+ """
803
+ return self._generate_entity_dict(
804
+ elements=elements,
805
+ extract_properties_func=self._extract_category_properties,
806
+ get_additional_props_func=self._get_category_dict_properties,
807
+ exclude_keys=['properties'] # Exclude raw properties
808
+ )
809
+
810
+ def _get_category_additional_properties(self, element: dict, category_guid: str) -> dict:
811
+ """
812
+ Get additional properties for a category.
224
813
 
225
- elements_md += self.make_md_attribute("description", description, output_format)
226
- elements_md += self.make_md_attribute("in glossary", glossary_qualified_name, output_format)
227
- elements_md += self.make_md_attribute("qualified name", qualified_name, output_format)
228
- elements_md += self.make_md_attribute("GUID", guid, output_format)
229
- elements_md += MD_SEPERATOR
814
+ Args:
815
+ element (dict): The category element
816
+ category_guid (str): The GUID of the category
817
+
818
+ Returns:
819
+ dict: Dictionary of additional properties
820
+ """
821
+ # Get parent category
822
+ parent_cat_md = self._get_parent_category_name(category_guid)
823
+
824
+ # Get subcategories
825
+ _, subcategory_list_md = self._get_subcategories_list(category_guid)
826
+
827
+ # Get glossary information
828
+ glossary_qualified_name = self._get_glossary_name_for_element(element)
829
+
830
+ return {
831
+ 'in_glossary': glossary_qualified_name,
832
+ 'parent_category': parent_cat_md,
833
+ 'subcategories': subcategory_list_md
834
+ }
835
+
836
+ def _generate_category_md(self, elements: list, elements_action: str, output_format: str) -> str:
837
+ """
838
+ Generate markdown for categories.
839
+
840
+ Args:
841
+ elements (list): List of category elements
842
+ elements_action (str): Action description for elements
843
+ output_format (str): Output format
230
844
 
845
+ Returns:
846
+ str: Markdown representation
847
+ """
848
+ return self._generate_entity_md(
849
+ elements=elements,
850
+ elements_action=elements_action,
851
+ output_format=output_format,
852
+ entity_type="Category",
853
+ extract_properties_func=self._extract_category_properties,
854
+ get_additional_props_func=self._get_category_additional_properties
855
+ )
856
+
857
+ def generate_categories_md(self, elements: list | dict, search_string: str, output_format: str = 'MD')-> str | list:
858
+ """
859
+ Generate markdown or dictionary representation of categories.
860
+
861
+ Args:
862
+ elements (list | dict): List or dictionary of category elements
863
+ search_string (str): The search string used
864
+ output_format (str): Output format (MD, LIST, DICT, FORM, REPORT)
865
+
866
+ Returns:
867
+ str | list: Markdown string or list of dictionaries depending on output_format
868
+ """
869
+ elements_md, elements_action = self.make_preamble(obj_type="Categories", search_string=search_string,
870
+ output_format=output_format)
871
+ if isinstance(elements, dict):
872
+ elements = [elements]
873
+
874
+ # If output format is LIST, create a markdown table
875
+ if output_format == 'LIST':
876
+ return self._generate_category_md_table(elements, search_string)
877
+
878
+ # If output format is DICT, return a dictionary structure
879
+ elif output_format == 'DICT':
880
+ return self._generate_category_dict(elements)
881
+
882
+ # Original implementation for other formats (MD, FORM, REPORT)
883
+ elements_md += self._generate_category_md(elements, elements_action, output_format)
231
884
  return elements_md
232
885
 
233
886
  #
@@ -518,6 +1171,8 @@ class GlossaryBrowser(Client):
518
1171
  MD - output standard markdown with no preamble
519
1172
  FORM - output markdown with a preamble for a form
520
1173
  REPORT - output markdown with a preamble for a report
1174
+ LIST - output a markdown table with columns for Glossary Name, Qualified Name, Language, Description, Usage
1175
+ DICT - output a dictionary structure containing all attributes
521
1176
  Returns
522
1177
  -------
523
1178
  List | str
@@ -571,6 +1226,8 @@ class GlossaryBrowser(Client):
571
1226
  MD - output standard markdown with no preamble
572
1227
  FORM - output markdown with a preamble for a form
573
1228
  REPORT - output markdown with a preamble for a report
1229
+ LIST - output a markdown table with columns for Glossary Name, Qualified Name, Language, Description, Usage
1230
+ DICT - output a dictionary structure containing all attributes
574
1231
 
575
1232
  Returns
576
1233
  -------
@@ -625,6 +1282,8 @@ class GlossaryBrowser(Client):
625
1282
  MD - output standard markdown with no preamble
626
1283
  FORM - output markdown with a preamble for a form
627
1284
  REPORT - output markdown with a preamble for a report
1285
+ LIST - output a markdown table with columns for Glossary Name, Qualified Name, Language, Description, Usage
1286
+ DICT - output a dictionary structure containing all attributes
628
1287
 
629
1288
  Returns
630
1289
  -------
@@ -852,6 +1511,127 @@ class GlossaryBrowser(Client):
852
1511
  )
853
1512
  return response
854
1513
 
1514
+ async def _async_get_glossary_subcategories(
1515
+ self,
1516
+ glossary_category_guid: str,
1517
+ effective_time: str = None,
1518
+ start_from: int = 0,
1519
+ page_size: int = max_paging_size,
1520
+ for_lineage: bool = False,
1521
+ for_duplicate_processing: bool = False,
1522
+ ) -> dict | str:
1523
+ """Glossary categories can be organized in a hierarchy. Retrieve the subcategories for the glossary category
1524
+ metadata element with the supplied unique identifier. If the requested category does not have any subcategories,
1525
+ null is returned. The optional request body contain an effective time for the query.
1526
+
1527
+ Parameters
1528
+ ----------
1529
+ glossary_category_guid: str,
1530
+ Unique identifier for the glossary category.
1531
+ effective_time: datetime, [default=None], optional
1532
+ Effective time of the query. If not specified will default to any effective time. Time format is
1533
+ "YYYY-MM-DDTHH:MM:SS" (ISO 8601)
1534
+ start_from: int, [default=0], optional
1535
+ The page to start from.
1536
+ page_size: int, [default=max_paging_size], optional
1537
+ The number of results per page to return.
1538
+ for_lineage: bool, [default=False], optional
1539
+ Indicates the search is for lineage.
1540
+ for_duplicate_processing: bool, [default=False], optional
1541
+ If set to True the user will handle duplicate processing.
1542
+
1543
+ Returns
1544
+ -------
1545
+ A dict list with the glossary metadata element for the requested category.
1546
+
1547
+ Raises
1548
+ ------
1549
+
1550
+ InvalidParameterException
1551
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1552
+ PropertyServerException
1553
+ Raised by the server when an issue arises in processing a valid request
1554
+ NotAuthorizedException
1555
+ The principle specified by the user_id does not have authorization for the requested action
1556
+ ConfigurationErrorException
1557
+ Raised when configuration parameters passed on earlier calls turn out to be
1558
+ invalid or make the new call invalid.
1559
+ """
1560
+ for_lineage_s = str(for_lineage).lower()
1561
+ for_duplicate_processing_s = str(for_duplicate_processing).lower()
1562
+
1563
+ body = {
1564
+ "class": "EffectiveTimeQueryRequestBody",
1565
+ "effectiveTime": effective_time,
1566
+ }
1567
+
1568
+ url = (
1569
+ f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/glossary-browser/glossaries/"
1570
+ f"categories/{glossary_category_guid}/subcategories/retrieve?startFrom={start_from}&pageSize={page_size}&"
1571
+ f"forLineage={for_lineage_s}&forDuplicateProcessing={for_duplicate_processing_s}"
1572
+ )
1573
+ if effective_time:
1574
+ response = await self._async_make_request("POST", url, body_slimmer(body))
1575
+ else:
1576
+ response = await self._async_make_request("POST", url)
1577
+
1578
+ return response.json().get("elementList", "No categories found")
1579
+
1580
+ def get_glossary_subcategories(
1581
+ self,
1582
+ glossary_category_guid: str,
1583
+ effective_time: str = None,
1584
+ start_from: int = 0,
1585
+ page_size: int = max_paging_size,
1586
+ for_lineage: bool = False,
1587
+ for_duplicate_processing: bool = False,
1588
+ ) -> dict | str:
1589
+ """Glossary categories can be organized in a hierarchy. Retrieve the subcategories for the glossary category
1590
+ metadata element with the supplied unique identifier. If the requested category does not have any subcategories,
1591
+ null is returned. The optional request body contain an effective time for the query.
1592
+
1593
+ Parameters
1594
+ ----------
1595
+ glossary_category_guid: str,
1596
+ Unique identifier for the glossary category.
1597
+ effective_time: datetime, [default=None], optional
1598
+ Effective time of the query. If not specified will default to any effective time. Time format is
1599
+ "YYYY-MM-DDTHH:MM:SS" (ISO 8601)
1600
+ start_from: int, [default=0], optional
1601
+ The page to start from.
1602
+ page_size: int, [default=max_paging_size], optional
1603
+ The number of results per page to return.
1604
+ for_lineage: bool, [default=False], optional
1605
+ Indicates the search is for lineage.
1606
+ for_duplicate_processing: bool, [default=False], optional
1607
+ If set to True the user will handle duplicate processing.
1608
+
1609
+ Returns
1610
+ -------
1611
+ A dict list with the glossary metadata element for the requested category.
1612
+
1613
+ Raises
1614
+ ------
1615
+
1616
+ InvalidParameterException
1617
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1618
+ PropertyServerException
1619
+ Raised by the server when an issue arises in processing a valid request
1620
+ NotAuthorizedException
1621
+ The principle specified by the user_id does not have authorization for the requested action
1622
+ ConfigurationErrorException
1623
+ Raised when configuration parameters passed on earlier calls turn out to be
1624
+ invalid or make the new call invalid.
1625
+ """
1626
+ loop = asyncio.get_event_loop()
1627
+ response = loop.run_until_complete(
1628
+ self._async_get_glossary_subcategories(
1629
+ glossary_category_guid, effective_time, start_from,
1630
+ page_size, for_lineage, for_duplicate_processing
1631
+ )
1632
+ )
1633
+ return response
1634
+
855
1635
  async def _async_find_glossary_categories(
856
1636
  self,
857
1637
  search_string: str,
@@ -947,8 +1727,6 @@ class GlossaryBrowser(Client):
947
1727
 
948
1728
 
949
1729
 
950
-
951
-
952
1730
  def find_glossary_categories(
953
1731
  self,
954
1732
  search_string: str,