pyegeria 5.4.0.14__py3-none-any.whl → 5.4.0.16__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 (118) hide show
  1. commands/cat/.DS_Store +0 -0
  2. commands/cat/.env +8 -0
  3. commands/cat/debug_log +550 -0
  4. commands/cat/debug_log.log +0 -0
  5. commands/cat/list_format_set.py +10 -4
  6. commands/cat/logs/pyegeria.log +0 -0
  7. commands/cli/debug_log.log +0 -0
  8. commands/ops/load_archive.py +26 -22
  9. commands/ops/logs/pyegeria.log +0 -0
  10. md_processing/.DS_Store +0 -0
  11. md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +8 -0
  12. md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +873 -0
  13. md_processing/dr_egeria_inbox/arch_test.md +57 -0
  14. md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
  15. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
  16. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
  17. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
  18. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
  19. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
  20. md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
  21. md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
  22. md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
  23. md_processing/dr_egeria_inbox/cat_test.md +100 -0
  24. md_processing/dr_egeria_inbox/collections.md +39 -0
  25. md_processing/dr_egeria_inbox/data_designer_debug.log +6 -0
  26. md_processing/dr_egeria_inbox/data_designer_out.md +60 -0
  27. md_processing/dr_egeria_inbox/data_designer_search_test.md +11 -0
  28. md_processing/dr_egeria_inbox/data_field.md +54 -0
  29. md_processing/dr_egeria_inbox/data_spec.md +77 -0
  30. md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
  31. md_processing/dr_egeria_inbox/data_test.md +179 -0
  32. md_processing/dr_egeria_inbox/data_test2.md +429 -0
  33. md_processing/dr_egeria_inbox/data_test3.md +462 -0
  34. md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +124 -0
  35. md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
  36. md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
  37. md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +313 -0
  38. md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
  39. md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
  40. md_processing/dr_egeria_inbox/generated_help_report.md +9 -0
  41. md_processing/dr_egeria_inbox/glossary_list.md +5 -0
  42. md_processing/dr_egeria_inbox/glossary_search_test.md +40 -0
  43. md_processing/dr_egeria_inbox/glossary_test1.md +324 -0
  44. md_processing/dr_egeria_inbox/gov_def.md +424 -0
  45. md_processing/dr_egeria_inbox/gov_def2.md +447 -0
  46. md_processing/dr_egeria_inbox/product.md +50 -0
  47. md_processing/dr_egeria_inbox/rel.md +8 -0
  48. md_processing/dr_egeria_inbox/sb.md +119 -0
  49. md_processing/dr_egeria_inbox/solution-components.md +136 -0
  50. md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
  51. md_processing/dr_egeria_inbox/synonym_test.md +42 -0
  52. md_processing/dr_egeria_inbox/t2.md +268 -0
  53. md_processing/dr_egeria_outbox/.obsidian/app.json +1 -0
  54. md_processing/dr_egeria_outbox/.obsidian/appearance.json +1 -0
  55. md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +6 -0
  56. md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +31 -0
  57. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +10 -0
  58. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +4459 -0
  59. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +10 -0
  60. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +3 -0
  61. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  62. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  63. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  64. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +500 -0
  65. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  66. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  67. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +37 -0
  68. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
  69. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +220 -0
  70. md_processing/dr_egeria_outbox/.obsidian/types.json +28 -0
  71. md_processing/dr_egeria_outbox/.obsidian/workspace.json +220 -0
  72. md_processing/dr_egeria_outbox/Untitled.canvas +1 -0
  73. md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:00-product.md +62 -0
  74. md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:13-product.md +62 -0
  75. md_processing/dr_egeria_outbox/friday/processed-2025-07-20 13:23-product.md +47 -0
  76. md_processing/dr_egeria_outbox/friday/processed-2025-08-01 11:55-data_test3.md +503 -0
  77. md_processing/dr_egeria_outbox/monday/processed-2025-07-14 12:38-data_designer_out.md +663 -0
  78. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 10:52-generated_help_report.md +2744 -0
  79. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 18:38-collections.md +62 -0
  80. md_processing/dr_egeria_outbox/monday/processed-2025-08-01 11:34-gov_def.md +444 -0
  81. md_processing/dr_egeria_outbox/processed-2025-08-03 16:05-glossary_list.md +37 -0
  82. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 14:55-product.md +77 -0
  83. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:05-product.md +75 -0
  84. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:11-product.md +74 -0
  85. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 20:40-collections.md +49 -0
  86. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +719 -0
  87. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +41 -0
  88. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +33 -0
  89. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +192 -0
  90. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +486 -0
  91. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +486 -0
  92. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +486 -0
  93. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +486 -0
  94. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +486 -0
  95. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +486 -0
  96. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +486 -0
  97. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-16 19:15-gov_def2.md +527 -0
  98. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 12:08-gov_def2.md +527 -0
  99. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 14:27-gov_def2.md +485 -0
  100. md_processing/md_processing_utils/debug_log.log +0 -0
  101. md_processing/md_processing_utils/solution_architect_log.log +0 -0
  102. pyegeria/.DS_Store +0 -0
  103. pyegeria/_client_new.py +31 -1
  104. pyegeria/_output_format_models.py +373 -0
  105. pyegeria/_output_formats.py +501 -190
  106. pyegeria/collection_manager_omvs.py +1 -1
  107. pyegeria/glossary_browser_omvs.py +178 -65
  108. pyegeria/governance_officer_omvs.py +2 -2
  109. pyegeria/load_config.py +38 -13
  110. pyegeria/logging_configuration.py +2 -2
  111. {pyegeria-5.4.0.14.dist-info → pyegeria-5.4.0.16.dist-info}/METADATA +1 -1
  112. {pyegeria-5.4.0.14.dist-info → pyegeria-5.4.0.16.dist-info}/RECORD +115 -17
  113. md_processing/dr-egeria-outbox/DataStruct-2025-07-29-20-49-16.py +0 -8
  114. md_processing/dr-egeria-outbox/Mandy-DataStruct-2025-07-29-15-54-45.md +0 -19
  115. pyegeria/dr.egeria spec.md +0 -9
  116. {pyegeria-5.4.0.14.dist-info → pyegeria-5.4.0.16.dist-info}/LICENSE +0 -0
  117. {pyegeria-5.4.0.14.dist-info → pyegeria-5.4.0.16.dist-info}/WHEEL +0 -0
  118. {pyegeria-5.4.0.14.dist-info → pyegeria-5.4.0.16.dist-info}/entry_points.txt +0 -0
@@ -10,194 +10,345 @@ This file manages output format sets.
10
10
  pyegeria allows find and get requests to generate output in different output formats -
11
11
  including DICT, MD, FORM, REPORT, LIST, MERMAID, TABLE, and perhaps additional ones in the future.
12
12
 
13
- Some of the formats, such as REPORT and LIST, allow the user to filter which attributes to
13
+ It is important to be able to filter which attributes to
14
14
  display, and the order in which they appear. However, many, if not most users will likely not want to customize
15
15
  the column list and so we need a sensible set of defaults for each type of output. These defaults are used
16
16
  by the find and get methods if the user doesn't provide a value for the columns parameter.
17
17
 
18
- This file contains these defaults and a function to return the right default dict for a given type.
18
+ This file contains these defaults and functions to work with them. The output format sets are now implemented
19
+ using Pydantic models defined in `_output_format_models.py`, which provide several advantages:
20
+ - Type validation: The models ensure that the data has the correct types and structure.
21
+ - Composition: The models support composition of formats, allowing formats to be reused and combined.
22
+ - Documentation: The models provide clear documentation of the data structure.
23
+ - IDE support: The models provide better IDE support, including autocompletion and type hints.
24
+
25
+ The functions in this module are designed to be backward compatible with code that expects the old
26
+ dictionary-based format. They convert between Pydantic models and dictionaries as needed.
27
+
28
+ Example usage:
29
+ ```python
30
+ # Get a format set by name and output type
31
+ format_set = select_output_format_set("Collections", "TABLE")
32
+
33
+ # Get a list of all available format sets
34
+ format_sets = output_format_set_list()
35
+
36
+ # Get the heading and description of a format set
37
+ heading = get_output_format_set_heading("Collections")
38
+ description = get_output_format_set_description("Collections")
39
+
40
+ # Match a format set with a specific output type
41
+ matched_format_set = get_output_format_type_match(format_set, "DICT")
42
+ ```
43
+
44
+ For more advanced usage, you can work directly with the Pydantic models:
45
+ ```python
46
+ from pyegeria._output_format_models import Column, Format, FormatSet
47
+
48
+ # Create a new format set
49
+ format_set = FormatSet(
50
+ heading="Example Format Set",
51
+ description="An example format set",
52
+ formats=[
53
+ Format(
54
+ types=["TABLE", "DICT"],
55
+ columns=[
56
+ Column(name="Display Name", key="display_name"),
57
+ Column(name="Description", key="description", format=True),
58
+ ],
59
+ ),
60
+ ],
61
+ )
62
+
63
+ # Add the format set to the output_format_sets dictionary
64
+ output_format_sets["Example"] = format_set
65
+ ```
19
66
  """
20
67
 
68
+ import os
69
+ from pathlib import Path
70
+ from typing import Dict, List, Optional, Union, Any
21
71
  from loguru import logger
22
72
 
73
+ from pyegeria._output_format_models import Column, Format, ActionParameter, FormatSet, FormatSetDict, save_format_sets_to_json, load_format_sets_from_json
74
+ from pyegeria.load_config import get_app_config
75
+
76
+ # Get the configured value for the user format sets directory
77
+ app_config = get_app_config()
78
+ USER_FORMAT_SETS_DIR = os.path.expanduser(app_config.Environment.pyegeria_user_format_sets_dir)
79
+
23
80
  # Constants
24
81
  MD_SEPARATOR = "\n---\n\n"
25
82
 
26
83
  # Define shared elements
27
84
  COMMON_COLUMNS = [
28
- {'name': 'Display Name', 'key': 'display_name'},
29
- {'name': 'Qualified Name', 'key': 'qualified_name', 'format': True},
30
- {'name': 'Category', 'key': 'category'},
31
- {'name': 'Description', 'key': 'description', 'format': True},
85
+ Column(name='Display Name', key='display_name'),
86
+ Column(name='Qualified Name', key='qualified_name', format=True),
87
+ Column(name='Category', key='category'),
88
+ Column(name='Description', key='description', format=True),
32
89
  ]
33
90
 
34
91
  COMMON_METADATA_COLUMNS = [
35
- {'name': 'GUID', 'key': 'guid', 'format': True},
36
- {'name': 'Metadata Collection ID', 'key': 'metadata_collection_id', 'format': True},
37
- {'name': 'Metadata Collection Name', 'key': 'metadata_collection_name', 'format': True},
92
+ Column(name='GUID', key='guid', format=True),
93
+ Column(name='Metadata Collection ID', key='metadata_collection_id', format=True),
94
+ Column(name='Metadata Collection Name', key='metadata_collection_name', format=True),
38
95
  ]
39
96
 
40
- COMMON_FORMATS_ALL = {
41
- "types": ["ALL"],
42
- "columns": COMMON_COLUMNS,
43
- }
97
+ COMMON_HEADER_COLUMNS = [
98
+ Column(name="Classifications", key='classifications'),
99
+ Column(name="Created By", key='created_by'),
100
+ Column(name="Create Time", key='create_time'),
101
+ Column(name="Updated By", key='updated_by'),
102
+ Column(name="Update Time", key='update_time'),
103
+ Column(name="Effective From", key='effective_from'),
104
+ Column(name="Effective To", key='effective_to'),
105
+ Column(name="Version", key='version'),
106
+ Column(name="Open Metadata Type Name", key='type_name'),
107
+ ]
108
+
109
+
110
+ REFERNCEABLE_COLUMNS = COMMON_COLUMNS + [
111
+ Column(name='Version Identifier', key='version_identifier'),
112
+ Column(name='Additional Properties', key='additional_properties')
113
+ ]
114
+
115
+
116
+ COMMON_FORMATS_ALL = Format(
117
+ types=["ALL"],
118
+ columns=COMMON_COLUMNS,
119
+ )
120
+
121
+
122
+ COLLECTIONS_COLUMNS = COMMON_COLUMNS + [
123
+ Column(name="Created By", key='created_by'),
124
+ Column(name="Create Time", key='create_time'),
125
+ Column(name="Updated By", key='updated_by'),
126
+ Column(name="Update Time", key='update_time'),
127
+ ]
44
128
 
129
+ COLLECTIONS_MEMBERS_COLUMNS = COLLECTIONS_COLUMNS + [
130
+ Column(name="Members", key='members'),
131
+ ]
45
132
 
46
- COLLECTIONS_COLUMNS = COMMON_COLUMNS + [
47
- {'name': "Created By", 'key': 'created_by'},
48
- {'name': "Create Time", 'key': 'create_time'},
49
- {'name': "Updated By", 'key': 'updated_by'},
50
- {'name': "Update Time", 'key': 'update_time'},
51
- ]
133
+ COLLECTION_DICT = Format(
134
+ types=["DICT"],
135
+ columns=COLLECTIONS_COLUMNS,
136
+ )
52
137
 
53
- COLLECTION_DICT = {
54
- "types": ["DICT"],
55
- "columns": COLLECTIONS_COLUMNS,
56
- }
138
+ COLLECTION_TABLE = Format(
139
+ types=["TABLE"],
140
+ columns=COMMON_COLUMNS,
141
+ )
57
142
 
58
- COLLECTION_TABLE = {
59
- "types": ["TABLE"],
60
- "columns": COMMON_COLUMNS,
61
- }
143
+ GOVERNANCE_DEFINITIONS_COLUMNS = COMMON_COLUMNS + [
144
+ Column(name="Document Identifier", key='document_identifier'),
145
+ Column(name="Title", key='title'),
146
+ Column(name="Scope", key='scope'),
147
+ ]
62
148
 
63
149
  COMMON_ANNOTATIONS = {
64
150
  "wikilinks": ["[[Commons]]"]
65
151
  }
66
152
 
67
153
  # Modularized output_format_sets
68
- output_format_sets = {
69
- "Referenceable": {
70
- "heading": "Common Attributes",
71
- "description": "Attributes that apply to all Referenceables.",
72
- "annotations": {}, # No specific annotations
73
- "formats": [
74
- {
75
- "types": ["ALL"],
76
- "columns": COMMON_COLUMNS + COMMON_METADATA_COLUMNS + [
77
- {'name': 'Version Identifier', 'key': 'version_identifier'},
78
- {'name': "Classifications", 'key': 'classifications'},
79
- {'name': "Additional Properties", 'key': 'additional_properties'},
80
- {'name': "Created By", 'key': 'created_by'},
81
- {'name': "Create Time", 'key': 'create_time'},
82
- {'name': "Updated By", 'key': 'updated_by'},
83
- {'name': "Update Time", 'key': 'update_time'},
84
- {'name': "Effective From", 'key': 'effective_from'},
85
- {'name': "Effective To", 'key': 'effective_to'},
86
- {'name': "Version", 'key': 'version'},
87
- {'name': "Open Metadata Type Name", 'key': 'type_name'},
154
+ output_format_sets = FormatSetDict({
155
+ "Referenceable": FormatSet(
156
+ heading="Common Attributes",
157
+ description="Attributes that apply to all Referenceables.",
158
+ annotations={}, # No specific annotations
159
+ formats=[
160
+ Format(
161
+ types=["ALL"],
162
+ columns=COMMON_COLUMNS + COMMON_METADATA_COLUMNS + [
163
+ Column(name='Version Identifier', key='version_identifier'),
164
+ Column(name="Classifications", key='classifications'),
165
+ Column(name="Additional Properties", key='additional_properties'),
166
+ Column(name="Created By", key='created_by'),
167
+ Column(name="Create Time", key='create_time'),
168
+ Column(name="Updated By", key='updated_by'),
169
+ Column(name="Update Time", key='update_time'),
170
+ Column(name="Effective From", key='effective_from'),
171
+ Column(name="Effective To", key='effective_to'),
172
+ Column(name="Version", key='version'),
173
+ Column(name="Open Metadata Type Name", key='type_name'),
88
174
  ],
89
- }
175
+ )
90
176
  ],
91
- },
92
-
93
- "Collections": {
94
- "heading": "Common Collection Information",
95
- "description": "Attributes generic to all Collections.",
96
- "aliases": ["Collection", "RootCollection", "Folder", "ReferenceList", "HomeCollection",
97
- "ResultSet", "RecentAccess", "WorkItemList", "Namespace"],
98
- "annotations": COMMON_ANNOTATIONS,
99
- "formats": [ COLLECTION_DICT, COLLECTION_TABLE,COMMON_FORMATS_ALL], # Reusing common formats
100
- "action": [{"function": "CollectionManager.find_collections",
101
- "user_params": [ "search_string"],
102
- "spec_params": { },
103
- }]
104
- },
105
-
106
- "DigitalProducts": {
107
- "heading": "Digital Product Information",
108
- "description": "Attributes useful to Digital Products.",
109
- "aliases": ["DigitalProduct", "DataProducts"],
110
- "annotations": {},
111
- "formats": [
112
- {
113
- "types": ["REPORT"],
114
- "columns": COMMON_COLUMNS + [
115
- {'name': "Status", 'key': 'status'},
116
- {'name': 'Product Name', 'key': 'product_name'},
117
- {'name': 'Identifier', 'key': 'identifier'},
118
- {'name': 'Maturity', 'key': 'maturity'},
119
- {'name': 'Service Life', 'key': 'service_life'},
120
- {'name': 'Next Version', 'key': 'next_version'},
121
- {'name': 'Withdraw Date', 'key': 'withdraw_date'},
122
- {'name': 'Members', 'key': 'members', 'format': True},
177
+ ),
178
+ "Basic-Terms": FormatSet(
179
+ heading="Basic Glossary Term Attributes",
180
+ description="Attributes that apply to all Basic Glossary Terms.",
181
+ annotations={},
182
+ formats=[
183
+ Format(
184
+ types=["ALL"],
185
+ columns=COMMON_COLUMNS + COMMON_METADATA_COLUMNS + [
186
+ Column(name='Version Identifier', key='version_identifier'),
187
+ Column(name="Summary", key='summary'),
188
+ Column(name="Additional Properties", key='additional_properties'),
189
+ Column(name="Example", key='example'),
190
+ Column(name="Usage", key='usage'),
191
+ Column(name="Updated By", key='updated_by'),
192
+ Column(name="Update Time", key='update_time'),
193
+ Column(name="Effective From", key='effective_from'),
194
+ Column(name="Effective To", key='effective_to'),
195
+ Column(name="GUID", key='guid'),
196
+ Column(name="Open Metadata Type Name", key='type_name'),
123
197
  ],
124
- }
198
+ )
199
+ ]
200
+ ),
201
+
202
+ "Collections": FormatSet(
203
+ heading="Common Collection Information",
204
+ description="Attributes generic to all Collections.",
205
+ aliases=["Collection", "RootCollection", "Folder", "ReferenceList", "HomeCollection",
206
+ "ResultSet", "RecentAccess", "WorkItemList", "Namespace"],
207
+ annotations=COMMON_ANNOTATIONS,
208
+ formats=[COLLECTION_DICT, COLLECTION_TABLE, COMMON_FORMATS_ALL], # Reusing common formats
209
+ action=[ActionParameter(
210
+ function="CollectionManager.find_collections",
211
+ user_params=["search_string"],
212
+ spec_params={},
213
+ )]
214
+ ),
215
+
216
+ "CollectionMembers": FormatSet(
217
+ heading="Collection Membership Information",
218
+ description="Attributes about all CollectionMembers.",
219
+ aliases=["CollectionMember", "Member", "Members"],
220
+ annotations= {"wikilinks": ["[[CollectionMembers]]"]},
221
+ formats = [COLLECTION_DICT, COLLECTION_TABLE],
222
+ action=[ActionParameter(
223
+ function="CollectionManager.get_collection_members",
224
+ user_params=["collection_guid"],
225
+ spec_params={"output_format": "DICT"},
226
+ )]
227
+ ),
228
+ "DigitalProducts": FormatSet(
229
+ heading="Digital Product Information",
230
+ description="Attributes useful to Digital Products.",
231
+ aliases=["DigitalProduct", "DataProducts"],
232
+ annotations={},
233
+ formats=[
234
+ Format(
235
+ types=["REPORT"],
236
+ columns=COMMON_COLUMNS + [
237
+ Column(name="Status", key='status'),
238
+ Column(name='Product Name', key='product_name'),
239
+ Column(name='Identifier', key='identifier'),
240
+ Column(name='Maturity', key='maturity'),
241
+ Column(name='Service Life', key='service_life'),
242
+ Column(name='Next Version', key='next_version'),
243
+ Column(name='Withdraw Date', key='withdraw_date'),
244
+ Column(name='Members', key='members', format=True),
245
+ ],
246
+ )
125
247
  ],
126
- "action": [{"function": "CollectionManager.find_collections",
127
- "user_params": [ "search_string"],
128
- "spec_params": { "classification_name":"DigitalProducts"}
129
- }
130
- ]
131
- },
132
-
133
- "Agreements": {
134
- "heading": "General Agreement Information",
135
- "description": "Attributes generic to all Agreements.",
136
- "aliases": ["DataSharingAgreement"],
137
- "annotations": {"wikilinks": ["[[Agreements]]", "[[Egeria]]"]},
138
- "formats": [COMMON_FORMATS_ALL] # Reusing common formats and columns
139
- },
140
-
141
- "DataDictionary": {
142
- "heading": "Data Dictionary Information",
143
- "description": "Attributes useful to Data Dictionary.",
144
- "aliases": ["Data Dict", "Data Dictionary"],
145
- "annotations": {"wikilinks": ["[[Data Dictionary]]"]},
146
- "formats": [COMMON_FORMATS_ALL], # Reusing common formats and columns
147
- "action": [{"function": "CollectionManager.find_collections",
148
- "user_params": [ "search_string"],
149
- "spec_params": { "classification_name": "DataDictionary" },
150
- }]
151
- },
152
-
153
- "Data Specification": {
154
- "heading": "Data Specification Information",
155
- "description": "Attributes useful to Data Specification.",
156
- "aliases": ["Data Spec", "DataSpec", "DataSpecification"],
157
- "annotations": {"wikilinks": ["[[Data Specification]]"]},
158
- "formats": [{"types": ["DICT"], "columns": COMMON_COLUMNS,}], # Reusing common formats and columns
159
- "action": [{"function": "CollectionManager.find_collections",
160
- "user_params": [ "search_string"],
161
- "spec_params": { "classification_name": "DataSpec" },
162
- }]
163
- },
164
- "DataStruct": {
165
- "heading": "Data Structure Information",
166
- "description": "Attributes useful to Data Structures.",
167
- "aliases": ["Data Structure", "DataStructures", "Data Structures", "Data Struct", "DataStructure"],
168
- "annotations": {"wikilinks": ["[[Data Structure]]"]},
169
- "formats": [{"types": ["ALL"], "columns" : COMMON_COLUMNS}], # Reusing common formats and columns
170
- "action": [{"function": "DataDesigner.find_data_structures",
171
- "user_params": ["search_string" ],
172
- "spec_params": { },
173
- }]
174
- },
175
- "DataField": {
176
- "heading": "Data Structure Information",
177
- "description": "Attributes useful to Data Structures.",
178
- "aliases": ["Data Field", "Data Fields", "DataFields"],
179
- "annotations": {"wikilinks": ["[[Data Field]]"]},
180
- "formats": [{"types": ["ALL"], "columns" : COMMON_COLUMNS}], # Reusing common formats and columns
181
- "action": [{"function": "DataDesigner.find_data_fields",
182
- "user_params": ["search_string" ],
183
- "spec_params": { },
184
- }]
185
- },
186
- "Mandy-DataStruct": {
187
- "heading": "Puddy Approves",
188
- "description": "This is a tutorial on how to use a data struct description",
189
- "aliases": [],
190
- "annotations": {"wikilinks": ["[[Data Structure]]"]},
191
- "formats": [{"types": ["TABLE"], "columns": COMMON_COLUMNS + [{'name': 'GUID', 'key': 'GUID'}]},
192
- { "types": ["DICT", "LIST", "REPORT"], "columns": COMMON_COLUMNS + [{'name': 'GUID', 'key': 'GUID'}]}
193
- ],
194
- "action": [{
195
- "function": "DataDesigner.find_data_structures",
196
- "user_params": ["filter"],
197
- "spec_params": {},
198
- }]
199
- },
200
- }
248
+ action=[ActionParameter(
249
+ function="CollectionManager.find_collections",
250
+ user_params=["search_string"],
251
+ spec_params={"classification_name": "DigitalProducts"},
252
+ )]
253
+ ),
254
+
255
+ "Agreements": FormatSet(
256
+ heading="General Agreement Information",
257
+ description="Attributes generic to all Agreements.",
258
+ aliases=["DataSharingAgreement"],
259
+ annotations={"wikilinks": ["[[Agreements]]", "[[Egeria]]"]},
260
+ formats=[COMMON_FORMATS_ALL] # Reusing common formats and columns
261
+ ),
262
+
263
+ "DataDictionary": FormatSet(
264
+ heading="Data Dictionary Information",
265
+ description="Attributes useful to Data Dictionary.",
266
+ aliases=["Data Dict", "Data Dictionary"],
267
+ annotations={"wikilinks": ["[[Data Dictionary]]"]},
268
+ formats=[COMMON_FORMATS_ALL], # Reusing common formats and columns
269
+ action=[ActionParameter(
270
+ function="CollectionManager.find_collections",
271
+ user_params=["search_string"],
272
+ spec_params={"classification_name": "DataDictionary"},
273
+ )]
274
+ ),
275
+
276
+ "Data Specification": FormatSet(
277
+ heading="Data Specification Information",
278
+ description="Attributes useful to Data Specification.",
279
+ aliases=["Data Spec", "DataSpec", "DataSpecification"],
280
+ annotations={"wikilinks": ["[[Data Specification]]"]},
281
+ formats=[
282
+ Format(types=["REPORT", "HTML"],columns=COMMON_COLUMNS + [Column(name="Mermaid", key='mermaid'),]),
283
+ Format(types=["MERMAID"], columns=[
284
+ Column(name="Display Name", key='display_name'),
285
+ Column(name="Mermaid", key='mermaid'),
286
+ ]),
287
+ Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
288
+ action=[ActionParameter(
289
+ function="CollectionManager.find_collections",
290
+ user_params=["search_string"],
291
+ spec_params={"classification_name": "DataSpec"},
292
+ )]
293
+ ),
294
+
295
+ "DataStruct": FormatSet(
296
+ heading="Data Structure Information",
297
+ description="Attributes useful to Data Structures.",
298
+ aliases=["Data Structure", "DataStructures", "Data Structures", "Data Struct", "DataStructure"],
299
+ annotations={"wikilinks": ["[[Data Structure]]"]},
300
+ formats=[Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
301
+ action=[ActionParameter(
302
+ function="DataDesigner.find_data_structures",
303
+ user_params=["search_string"],
304
+ spec_params={},
305
+ )]
306
+ ),
307
+
308
+ "DataField": FormatSet(
309
+ heading="Data Structure Information",
310
+ description="Attributes useful to Data Structures.",
311
+ aliases=["Data Field", "Data Fields", "DataFields"],
312
+ annotations={"wikilinks": ["[[Data Field]]"]},
313
+ formats=[Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
314
+ action=[ActionParameter(
315
+ function="DataDesigner.find_data_fields",
316
+ user_params=["search_string"],
317
+ spec_params={},
318
+ )]
319
+ ),
320
+
321
+ "Mandy-DataStruct": FormatSet(
322
+ heading="Puddy Approves",
323
+ description="This is a tutorial on how to use a data struct description",
324
+ aliases=[],
325
+ annotations={"wikilinks": ["[[Data Structure]]"]},
326
+ formats=[
327
+ Format(types=["TABLE"], columns=COMMON_COLUMNS + [Column(name='GUID', key='GUID')]),
328
+ Format(types=[ "DICT","LIST", ], columns=COMMON_COLUMNS + [Column(name='GUID', key='GUID')]),
329
+ Format(types=["REPORT","MERMAID", "HTML"], columns=[Column(name='Display Name', key='display_name'),
330
+ Column(name='Mermaid', key='mermaid'),]),
331
+ ],
332
+ action=[ActionParameter(
333
+ function="DataDesigner.find_data_structures",
334
+ user_params=["search_string"],
335
+ spec_params={"output_format":"DICT"},
336
+ )]
337
+ ),
338
+
339
+ "Governance Definitions": FormatSet(
340
+ heading="Governance-Definitions Information",
341
+ description="Attributes useful to Governance-Definitions.",
342
+ aliases=["GovernanceDefinitions"],
343
+ annotations={"wikilinks": ["[[Governance]]"]},
344
+ formats=[Format(types=["ALL"], columns=GOVERNANCE_DEFINITIONS_COLUMNS)],
345
+ action=[ActionParameter(
346
+ function="GovernanceOfficer.find_governance_definitions",
347
+ user_params=["search_filter"],
348
+ spec_params={"output_format":"DICT"},
349
+ )]
350
+ ),
351
+ })
201
352
 
202
353
  def select_output_format_set(kind: str, output_type: str) -> dict | None:
203
354
  """
@@ -212,75 +363,235 @@ def select_output_format_set(kind: str, output_type: str) -> dict | None:
212
363
  dict | None:
213
364
  """
214
365
  # Normalize the output type to uppercase for consistency
215
- output_sets = output_format_sets
216
-
217
366
  output_type = output_type.upper()
218
- output_struct:dict = {}
367
+ output_struct: dict = {}
219
368
 
220
369
  # Step 1: Check if `kind` exists in the `output_format_sets` dictionary
221
- element = output_sets.get(kind)
370
+ element = output_format_sets.get(kind)
222
371
 
223
372
  # Step 2: If not found, attempt to match `kind` in aliases
224
373
  if element is None:
225
- for value in output_format_sets.values():
226
- aliases = value.get("aliases", [])
374
+ for key, value in output_format_sets.items():
375
+ aliases = value.aliases
227
376
  if kind in aliases:
228
377
  element = value
229
378
  break
230
379
 
231
-
232
380
  # Step 3: If still not found, return None
233
381
  if element is None:
234
382
  msg = f"No matching column set found for kind='{kind}' and output type='{output_type}'."
235
383
  logger.error(msg)
236
384
  return None
237
385
  else:
238
- output_struct["aliases"] = element.get("aliases", [])
239
- output_struct["heading"] = element.get("heading", [])
240
- output_struct["description"] = element.get("description", [])
241
- output_struct["annotations"] = element.get("annotations", {})
242
- if "action" in element:
243
- output_struct["action"] = element.get("action", [])
386
+ # Convert FormatSet to dictionary for backward compatibility
387
+ output_struct["aliases"] = element.aliases
388
+ output_struct["heading"] = element.heading
389
+ output_struct["description"] = element.description
390
+ output_struct["annotations"] = element.annotations
391
+ if element.action:
392
+ # Convert ActionParameter to dictionary for backward compatibility
393
+ output_struct["action"] = [action.dict() for action in element.action]
244
394
 
245
395
  # If this was just a validation that the format set could be found then the output type is ANY - so just return.
246
396
  if output_type == "ANY":
247
397
  return output_struct
248
398
 
249
399
  # Step 4: Search for a matching format in the `formats` list
250
- for format in element.get("formats", []):
251
- if output_type in format.get("types", []):
252
- output_struct["formats"] = format
400
+ for format in element.formats:
401
+ if output_type in format.types:
402
+ # Convert Format to dictionary for backward compatibility
403
+ output_struct["formats"] = format.dict()
253
404
  return output_struct
254
405
 
255
406
  # Step 5: Handle the fallback case of "ALL"
256
- for format in element.get("formats", []):
257
- if "ALL" in format.get("types", []):
258
- output_struct["formats"] = format
407
+ for format in element.formats:
408
+ if "ALL" in format.types:
409
+ # Convert Format to dictionary for backward compatibility
410
+ output_struct["formats"] = format.dict()
259
411
  return output_struct
260
412
 
261
413
  # Step 6: If no match is found, return None
262
414
  logger.error(f"No matching format found for kind='{kind}' with output type='{output_type}'.")
263
415
  return None
264
416
 
265
- def output_format_set_list()->list[str]:
417
+ def output_format_set_list() -> list[str]:
418
+ """
419
+ Returns a list of all available format set names.
420
+
421
+ Returns:
422
+ list[str]: A list of format set names
423
+ """
266
424
  return list(output_format_sets.keys())
267
425
 
268
- def get_output_format_set_heading(format_set: dict) -> str:
269
- return output_format_sets[format_set].get("heading")
270
- def get_output_format_set_description(format_set: dict) -> str:
271
- return output_format_sets[format_set].get("description")
426
+ def get_output_format_set_heading(format_set: str) -> str:
427
+ """
428
+ Gets the heading of a format set.
429
+
430
+ Args:
431
+ format_set: The name of the format set
432
+
433
+ Returns:
434
+ str: The heading of the format set
435
+ """
436
+ return output_format_sets[format_set].heading
272
437
 
273
- def get_output_format_type_match(format_set: dict, output_format: str) -> dict:
274
- if isinstance(format_set, list):
275
- for format in format_set.get("formats", []):
438
+ def get_output_format_set_description(format_set: str) -> str:
439
+ """
440
+ Gets the description of a format set.
441
+
442
+ Args:
443
+ format_set: The name of the format set
444
+
445
+ Returns:
446
+ str: The description of the format set
447
+ """
448
+ return output_format_sets[format_set].description
449
+
450
+ def get_output_format_type_match(format_set: Union[dict, FormatSet], output_format: str) -> dict:
451
+ """
452
+ Matches a format set with a specific output format.
453
+
454
+ Args:
455
+ format_set: The format set to match, either a FormatSet instance or a dictionary
456
+ output_format: The output format to match
457
+
458
+ Returns:
459
+ dict: The format set with the matching format
460
+ """
461
+ # Convert FormatSet to dictionary if needed
462
+ if isinstance(format_set, FormatSet):
463
+ format_set_dict = format_set.dict()
464
+ else:
465
+ format_set_dict = format_set
466
+
467
+ # Handle the case where format_set is a list (legacy code)
468
+ if isinstance(format_set_dict, list):
469
+ for format in format_set_dict.get("formats", []):
276
470
  if output_format in format.get("types", []):
277
- format_set["formats"] = format
278
- return format_set
471
+ format_set_dict["formats"] = format
472
+ return format_set_dict
279
473
 
280
- # Step 5: Handle the fallback case of "ALL"
281
- for format in format_set.get("formats", []):
474
+ # Handle the fallback case of "ALL"
475
+ for format in format_set_dict.get("formats", []):
282
476
  if "ALL" in format.get("types", []):
283
- format_set["formats"] = format
284
- return format_set
477
+ format_set_dict["formats"] = format
478
+ return format_set_dict
479
+ else:
480
+ # Handle the case where format_set is a dictionary
481
+ if "formats" in format_set_dict:
482
+ formats = format_set_dict["formats"]
483
+ if isinstance(formats, list):
484
+ for format in formats:
485
+ if output_format in format.get("types", []):
486
+ format_set_dict["formats"] = format
487
+ return format_set_dict
488
+
489
+ # Handle the fallback case of "ALL"
490
+ for format in formats:
491
+ if "ALL" in format.get("types", []):
492
+ format_set_dict["formats"] = format
493
+ return format_set_dict
494
+ else:
495
+ # Handle the case where format_set is a dictionary from select_output_format_set with the "ANY" output type
496
+ # In this case, we need to look up the format set by name and get the formats
497
+ if "heading" in format_set_dict and "description" in format_set_dict:
498
+ # Try to find the format set by heading
499
+ for key, value in output_format_sets.items():
500
+ if value.heading == format_set_dict["heading"] and value.description == format_set_dict["description"]:
501
+ # Found the format set, now find the matching format
502
+ for format in value.formats:
503
+ if output_format in format.types:
504
+ format_set_dict["formats"] = format.dict()
505
+ return format_set_dict
506
+
507
+ # Handle the fallback case of "ALL"
508
+ for format in value.formats:
509
+ if "ALL" in format.types:
510
+ format_set_dict["formats"] = format.dict()
511
+ return format_set_dict
512
+
513
+ # If no match is found, return the original format set
514
+ return format_set_dict
515
+
516
+ def save_output_format_sets(file_path: str, format_set_names: List[str] = None) -> None:
517
+ """
518
+ Save output format sets to a JSON file.
519
+
520
+ This function allows saving all format sets or a subset of format sets to a JSON file.
521
+ The saved format sets can later be loaded using the `load_output_format_sets` function.
522
+
523
+ Args:
524
+ file_path: The path to save the file to
525
+ format_set_names: Optional list of format set names to save. If None, all format sets are saved.
526
+ """
527
+ if format_set_names is None:
528
+ # Save all format sets
529
+ output_format_sets.save_to_json(file_path)
530
+ logger.info(f"All format sets saved to {file_path}")
285
531
  else:
286
- return format_set
532
+ # Save only specified format sets
533
+ subset = FormatSetDict()
534
+ for name in format_set_names:
535
+ format_set = output_format_sets.get(name)
536
+ if format_set:
537
+ subset[name] = format_set
538
+ else:
539
+ logger.warning(f"Format set '{name}' not found, skipping")
540
+
541
+ if subset:
542
+ subset.save_to_json(file_path)
543
+ logger.info(f"Selected format sets saved to {file_path}")
544
+ else:
545
+ logger.warning(f"No valid format sets to save, file not created")
546
+
547
+ def load_output_format_sets(file_path: str, merge: bool = True) -> None:
548
+ """
549
+ Load output format sets from a JSON file.
550
+
551
+ This function loads format sets from a JSON file and either merges them with the existing
552
+ format sets or replaces the existing format sets.
553
+
554
+ Args:
555
+ file_path: The path to load the file from
556
+ merge: If True, merge with existing format sets. If False, replace existing format sets.
557
+ """
558
+ global output_format_sets
559
+ try:
560
+ loaded_sets = FormatSetDict.load_from_json(file_path)
561
+
562
+ if merge:
563
+ # Merge with existing format sets
564
+ for key, value in loaded_sets.items():
565
+ output_format_sets[key] = value
566
+ logger.info(f"Format sets from {file_path} merged with existing format sets")
567
+ else:
568
+ # Replace existing format sets
569
+ output_format_sets = loaded_sets
570
+ logger.info(f"Existing format sets replaced with format sets from {file_path}")
571
+ except Exception as e:
572
+ logger.error(f"Error loading format sets from {file_path}: {e}")
573
+ raise
574
+
575
+ def load_user_format_sets() -> None:
576
+ """
577
+ Load all user-defined format sets from the user format sets directory.
578
+
579
+ This function loads all JSON files in the user format sets directory and merges
580
+ the format sets with the existing format sets.
581
+ """
582
+ if not os.path.exists(USER_FORMAT_SETS_DIR):
583
+ logger.debug(f"User format sets directory {USER_FORMAT_SETS_DIR} does not exist")
584
+ return
585
+
586
+ # Load all JSON files in the directory
587
+ for file_path in Path(USER_FORMAT_SETS_DIR).glob("*.json"):
588
+ try:
589
+ load_output_format_sets(str(file_path), merge=True)
590
+ except Exception as e:
591
+ logger.error(f"Error loading format sets from {file_path}: {e}")
592
+
593
+ # Load user-defined format sets at module initialization
594
+ try:
595
+ load_user_format_sets()
596
+ except Exception as e:
597
+ logger.error(f"Error loading user-defined format sets: {e}")