pyegeria 5.4.0.13__py3-none-any.whl → 5.4.0.15__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,194 +10,322 @@ 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
+
179
+ "Collections": FormatSet(
180
+ heading="Common Collection Information",
181
+ description="Attributes generic to all Collections.",
182
+ aliases=["Collection", "RootCollection", "Folder", "ReferenceList", "HomeCollection",
183
+ "ResultSet", "RecentAccess", "WorkItemList", "Namespace"],
184
+ annotations=COMMON_ANNOTATIONS,
185
+ formats=[COLLECTION_DICT, COLLECTION_TABLE, COMMON_FORMATS_ALL], # Reusing common formats
186
+ action=[ActionParameter(
187
+ function="CollectionManager.find_collections",
188
+ user_params=["search_string"],
189
+ spec_params={},
190
+ )]
191
+ ),
192
+
193
+ "CollectionMembers": FormatSet(
194
+ heading="Collection Membership Information",
195
+ description="Attributes about all CollectionMembers.",
196
+ aliases=["CollectionMember", "Member", "Members"],
197
+ annotations= {"wikilinks": ["[[CollectionMembers]]"]},
198
+ formats = [COLLECTION_DICT, COLLECTION_TABLE],
199
+ action=[ActionParameter(
200
+ function="CollectionManager.get_collection_members",
201
+ user_params=["collection_guid"],
202
+ spec_params={"output_format": "DICT"},
203
+ )]
204
+ ),
205
+ "DigitalProducts": FormatSet(
206
+ heading="Digital Product Information",
207
+ description="Attributes useful to Digital Products.",
208
+ aliases=["DigitalProduct", "DataProducts"],
209
+ annotations={},
210
+ formats=[
211
+ Format(
212
+ types=["REPORT"],
213
+ columns=COMMON_COLUMNS + [
214
+ Column(name="Status", key='status'),
215
+ Column(name='Product Name', key='product_name'),
216
+ Column(name='Identifier', key='identifier'),
217
+ Column(name='Maturity', key='maturity'),
218
+ Column(name='Service Life', key='service_life'),
219
+ Column(name='Next Version', key='next_version'),
220
+ Column(name='Withdraw Date', key='withdraw_date'),
221
+ Column(name='Members', key='members', format=True),
123
222
  ],
124
- }
223
+ )
125
224
  ],
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
- }
225
+ action=[ActionParameter(
226
+ function="CollectionManager.find_collections",
227
+ user_params=["search_string"],
228
+ spec_params={"classification_name": "DigitalProducts"},
229
+ )]
230
+ ),
231
+
232
+ "Agreements": FormatSet(
233
+ heading="General Agreement Information",
234
+ description="Attributes generic to all Agreements.",
235
+ aliases=["DataSharingAgreement"],
236
+ annotations={"wikilinks": ["[[Agreements]]", "[[Egeria]]"]},
237
+ formats=[COMMON_FORMATS_ALL] # Reusing common formats and columns
238
+ ),
239
+
240
+ "DataDictionary": FormatSet(
241
+ heading="Data Dictionary Information",
242
+ description="Attributes useful to Data Dictionary.",
243
+ aliases=["Data Dict", "Data Dictionary"],
244
+ annotations={"wikilinks": ["[[Data Dictionary]]"]},
245
+ formats=[COMMON_FORMATS_ALL], # Reusing common formats and columns
246
+ action=[ActionParameter(
247
+ function="CollectionManager.find_collections",
248
+ user_params=["search_string"],
249
+ spec_params={"classification_name": "DataDictionary"},
250
+ )]
251
+ ),
252
+
253
+ "Data Specification": FormatSet(
254
+ heading="Data Specification Information",
255
+ description="Attributes useful to Data Specification.",
256
+ aliases=["Data Spec", "DataSpec", "DataSpecification"],
257
+ annotations={"wikilinks": ["[[Data Specification]]"]},
258
+ formats=[
259
+ Format(types=["REPORT", "HTML"],columns=COMMON_COLUMNS + [Column(name="Mermaid", key='mermaid'),]),
260
+ Format(types=["MERMAID"], columns=[
261
+ Column(name="Display Name", key='display_name'),
262
+ Column(name="Mermaid", key='mermaid'),
263
+ ]),
264
+ Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
265
+ action=[ActionParameter(
266
+ function="CollectionManager.find_collections",
267
+ user_params=["search_string"],
268
+ spec_params={"classification_name": "DataSpec"},
269
+ )]
270
+ ),
271
+
272
+ "DataStruct": FormatSet(
273
+ heading="Data Structure Information",
274
+ description="Attributes useful to Data Structures.",
275
+ aliases=["Data Structure", "DataStructures", "Data Structures", "Data Struct", "DataStructure"],
276
+ annotations={"wikilinks": ["[[Data Structure]]"]},
277
+ formats=[Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
278
+ action=[ActionParameter(
279
+ function="DataDesigner.find_data_structures",
280
+ user_params=["search_string"],
281
+ spec_params={},
282
+ )]
283
+ ),
284
+
285
+ "DataField": FormatSet(
286
+ heading="Data Structure Information",
287
+ description="Attributes useful to Data Structures.",
288
+ aliases=["Data Field", "Data Fields", "DataFields"],
289
+ annotations={"wikilinks": ["[[Data Field]]"]},
290
+ formats=[Format(types=["ALL"], columns=COMMON_COLUMNS)], # Reusing common formats and columns
291
+ action=[ActionParameter(
292
+ function="DataDesigner.find_data_fields",
293
+ user_params=["search_string"],
294
+ spec_params={},
295
+ )]
296
+ ),
297
+
298
+ "Mandy-DataStruct": FormatSet(
299
+ heading="Puddy Approves",
300
+ description="This is a tutorial on how to use a data struct description",
301
+ aliases=[],
302
+ annotations={"wikilinks": ["[[Data Structure]]"]},
303
+ formats=[
304
+ Format(types=["TABLE"], columns=COMMON_COLUMNS + [Column(name='GUID', key='GUID')]),
305
+ Format(types=[ "DICT","LIST", ], columns=COMMON_COLUMNS + [Column(name='GUID', key='GUID')]),
306
+ Format(types=["REPORT","MERMAID", "HTML"], columns=[Column(name='Display Name', key='display_name'),
307
+ Column(name='Mermaid', key='mermaid'),]),
308
+ ],
309
+ action=[ActionParameter(
310
+ function="DataDesigner.find_data_structures",
311
+ user_params=["search_string"],
312
+ spec_params={"output_format":"DICT"},
313
+ )]
314
+ ),
315
+
316
+ "Governance Definitions": FormatSet(
317
+ heading="Governance-Definitions Information",
318
+ description="Attributes useful to Governance-Definitions.",
319
+ aliases=["GovernanceDefinitions"],
320
+ annotations={"wikilinks": ["[[Governance]]"]},
321
+ formats=[Format(types=["ALL"], columns=GOVERNANCE_DEFINITIONS_COLUMNS)],
322
+ action=[ActionParameter(
323
+ function="GovernanceOfficer.find_governance_definitions",
324
+ user_params=["search_filter"],
325
+ spec_params={"output_format":"DICT"},
326
+ )]
327
+ ),
328
+ })
201
329
 
202
330
  def select_output_format_set(kind: str, output_type: str) -> dict | None:
203
331
  """
@@ -212,75 +340,235 @@ def select_output_format_set(kind: str, output_type: str) -> dict | None:
212
340
  dict | None:
213
341
  """
214
342
  # Normalize the output type to uppercase for consistency
215
- output_sets = output_format_sets
216
-
217
343
  output_type = output_type.upper()
218
- output_struct:dict = {}
344
+ output_struct: dict = {}
219
345
 
220
346
  # Step 1: Check if `kind` exists in the `output_format_sets` dictionary
221
- element = output_sets.get(kind)
347
+ element = output_format_sets.get(kind)
222
348
 
223
349
  # Step 2: If not found, attempt to match `kind` in aliases
224
350
  if element is None:
225
- for value in output_format_sets.values():
226
- aliases = value.get("aliases", [])
351
+ for key, value in output_format_sets.items():
352
+ aliases = value.aliases
227
353
  if kind in aliases:
228
354
  element = value
229
355
  break
230
356
 
231
-
232
357
  # Step 3: If still not found, return None
233
358
  if element is None:
234
359
  msg = f"No matching column set found for kind='{kind}' and output type='{output_type}'."
235
360
  logger.error(msg)
236
361
  return None
237
362
  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", [])
363
+ # Convert FormatSet to dictionary for backward compatibility
364
+ output_struct["aliases"] = element.aliases
365
+ output_struct["heading"] = element.heading
366
+ output_struct["description"] = element.description
367
+ output_struct["annotations"] = element.annotations
368
+ if element.action:
369
+ # Convert ActionParameter to dictionary for backward compatibility
370
+ output_struct["action"] = [action.dict() for action in element.action]
244
371
 
245
372
  # If this was just a validation that the format set could be found then the output type is ANY - so just return.
246
373
  if output_type == "ANY":
247
374
  return output_struct
248
375
 
249
376
  # 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
377
+ for format in element.formats:
378
+ if output_type in format.types:
379
+ # Convert Format to dictionary for backward compatibility
380
+ output_struct["formats"] = format.dict()
253
381
  return output_struct
254
382
 
255
383
  # 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
384
+ for format in element.formats:
385
+ if "ALL" in format.types:
386
+ # Convert Format to dictionary for backward compatibility
387
+ output_struct["formats"] = format.dict()
259
388
  return output_struct
260
389
 
261
390
  # Step 6: If no match is found, return None
262
391
  logger.error(f"No matching format found for kind='{kind}' with output type='{output_type}'.")
263
392
  return None
264
393
 
265
- def output_format_set_list()->list[str]:
394
+ def output_format_set_list() -> list[str]:
395
+ """
396
+ Returns a list of all available format set names.
397
+
398
+ Returns:
399
+ list[str]: A list of format set names
400
+ """
266
401
  return list(output_format_sets.keys())
267
402
 
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")
403
+ def get_output_format_set_heading(format_set: str) -> str:
404
+ """
405
+ Gets the heading of a format set.
406
+
407
+ Args:
408
+ format_set: The name of the format set
409
+
410
+ Returns:
411
+ str: The heading of the format set
412
+ """
413
+ return output_format_sets[format_set].heading
414
+
415
+ def get_output_format_set_description(format_set: str) -> str:
416
+ """
417
+ Gets the description of a format set.
418
+
419
+ Args:
420
+ format_set: The name of the format set
421
+
422
+ Returns:
423
+ str: The description of the format set
424
+ """
425
+ return output_format_sets[format_set].description
272
426
 
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", []):
427
+ def get_output_format_type_match(format_set: Union[dict, FormatSet], output_format: str) -> dict:
428
+ """
429
+ Matches a format set with a specific output format.
430
+
431
+ Args:
432
+ format_set: The format set to match, either a FormatSet instance or a dictionary
433
+ output_format: The output format to match
434
+
435
+ Returns:
436
+ dict: The format set with the matching format
437
+ """
438
+ # Convert FormatSet to dictionary if needed
439
+ if isinstance(format_set, FormatSet):
440
+ format_set_dict = format_set.dict()
441
+ else:
442
+ format_set_dict = format_set
443
+
444
+ # Handle the case where format_set is a list (legacy code)
445
+ if isinstance(format_set_dict, list):
446
+ for format in format_set_dict.get("formats", []):
276
447
  if output_format in format.get("types", []):
277
- format_set["formats"] = format
278
- return format_set
448
+ format_set_dict["formats"] = format
449
+ return format_set_dict
279
450
 
280
- # Step 5: Handle the fallback case of "ALL"
281
- for format in format_set.get("formats", []):
451
+ # Handle the fallback case of "ALL"
452
+ for format in format_set_dict.get("formats", []):
282
453
  if "ALL" in format.get("types", []):
283
- format_set["formats"] = format
284
- return format_set
454
+ format_set_dict["formats"] = format
455
+ return format_set_dict
456
+ else:
457
+ # Handle the case where format_set is a dictionary
458
+ if "formats" in format_set_dict:
459
+ formats = format_set_dict["formats"]
460
+ if isinstance(formats, list):
461
+ for format in formats:
462
+ if output_format in format.get("types", []):
463
+ format_set_dict["formats"] = format
464
+ return format_set_dict
465
+
466
+ # Handle the fallback case of "ALL"
467
+ for format in formats:
468
+ if "ALL" in format.get("types", []):
469
+ format_set_dict["formats"] = format
470
+ return format_set_dict
471
+ else:
472
+ # Handle the case where format_set is a dictionary from select_output_format_set with the "ANY" output type
473
+ # In this case, we need to look up the format set by name and get the formats
474
+ if "heading" in format_set_dict and "description" in format_set_dict:
475
+ # Try to find the format set by heading
476
+ for key, value in output_format_sets.items():
477
+ if value.heading == format_set_dict["heading"] and value.description == format_set_dict["description"]:
478
+ # Found the format set, now find the matching format
479
+ for format in value.formats:
480
+ if output_format in format.types:
481
+ format_set_dict["formats"] = format.dict()
482
+ return format_set_dict
483
+
484
+ # Handle the fallback case of "ALL"
485
+ for format in value.formats:
486
+ if "ALL" in format.types:
487
+ format_set_dict["formats"] = format.dict()
488
+ return format_set_dict
489
+
490
+ # If no match is found, return the original format set
491
+ return format_set_dict
492
+
493
+ def save_output_format_sets(file_path: str, format_set_names: List[str] = None) -> None:
494
+ """
495
+ Save output format sets to a JSON file.
496
+
497
+ This function allows saving all format sets or a subset of format sets to a JSON file.
498
+ The saved format sets can later be loaded using the `load_output_format_sets` function.
499
+
500
+ Args:
501
+ file_path: The path to save the file to
502
+ format_set_names: Optional list of format set names to save. If None, all format sets are saved.
503
+ """
504
+ if format_set_names is None:
505
+ # Save all format sets
506
+ output_format_sets.save_to_json(file_path)
507
+ logger.info(f"All format sets saved to {file_path}")
285
508
  else:
286
- return format_set
509
+ # Save only specified format sets
510
+ subset = FormatSetDict()
511
+ for name in format_set_names:
512
+ format_set = output_format_sets.get(name)
513
+ if format_set:
514
+ subset[name] = format_set
515
+ else:
516
+ logger.warning(f"Format set '{name}' not found, skipping")
517
+
518
+ if subset:
519
+ subset.save_to_json(file_path)
520
+ logger.info(f"Selected format sets saved to {file_path}")
521
+ else:
522
+ logger.warning(f"No valid format sets to save, file not created")
523
+
524
+ def load_output_format_sets(file_path: str, merge: bool = True) -> None:
525
+ """
526
+ Load output format sets from a JSON file.
527
+
528
+ This function loads format sets from a JSON file and either merges them with the existing
529
+ format sets or replaces the existing format sets.
530
+
531
+ Args:
532
+ file_path: The path to load the file from
533
+ merge: If True, merge with existing format sets. If False, replace existing format sets.
534
+ """
535
+ global output_format_sets
536
+ try:
537
+ loaded_sets = FormatSetDict.load_from_json(file_path)
538
+
539
+ if merge:
540
+ # Merge with existing format sets
541
+ for key, value in loaded_sets.items():
542
+ output_format_sets[key] = value
543
+ logger.info(f"Format sets from {file_path} merged with existing format sets")
544
+ else:
545
+ # Replace existing format sets
546
+ output_format_sets = loaded_sets
547
+ logger.info(f"Existing format sets replaced with format sets from {file_path}")
548
+ except Exception as e:
549
+ logger.error(f"Error loading format sets from {file_path}: {e}")
550
+ raise
551
+
552
+ def load_user_format_sets() -> None:
553
+ """
554
+ Load all user-defined format sets from the user format sets directory.
555
+
556
+ This function loads all JSON files in the user format sets directory and merges
557
+ the format sets with the existing format sets.
558
+ """
559
+ if not os.path.exists(USER_FORMAT_SETS_DIR):
560
+ logger.debug(f"User format sets directory {USER_FORMAT_SETS_DIR} does not exist")
561
+ return
562
+
563
+ # Load all JSON files in the directory
564
+ for file_path in Path(USER_FORMAT_SETS_DIR).glob("*.json"):
565
+ try:
566
+ load_output_format_sets(str(file_path), merge=True)
567
+ except Exception as e:
568
+ logger.error(f"Error loading format sets from {file_path}: {e}")
569
+
570
+ # Load user-defined format sets at module initialization
571
+ try:
572
+ load_user_format_sets()
573
+ except Exception as e:
574
+ logger.error(f"Error loading user-defined format sets: {e}")