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.
@@ -59,12 +59,12 @@ from pyegeria._output_formats import select_output_format_set, get_output_format
59
59
  from pyegeria._exceptions_new import PyegeriaException, print_exception_response
60
60
  from pyegeria.egeria_tech_client import EgeriaTech
61
61
 
62
- pydevd_pycharm.settrace('host.docker.internal', # Use 'host.docker.internal' to connect to the host machine
63
- port=5678, # Port to communicate with PyCharm
64
- stdoutToServer=True, # Forward stdout to PyCharm
65
- stderrToServer=True, # Forward stderr to PyCharm
66
- suspend=True) # Suspend execution until the debugger is connected
67
-
62
+ # pydevd_pycharm.settrace('host.docker.internal', # Use 'host.docker.internal' to connect to the host machine
63
+ # port=5678, # Port to communicate with PyCharm
64
+ # stdoutToServer=True, # Forward stdout to PyCharm
65
+ # stderrToServer=True, # Forward stderr to PyCharm
66
+ # suspend=True) # Suspend execution until the debugger is connected
67
+ #
68
68
 
69
69
 
70
70
  EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
@@ -204,6 +204,8 @@ def execute_format_set_action(
204
204
  file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.json"
205
205
  elif output_format == "DICT":
206
206
  file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.py"
207
+ elif output_format == "MERMAID":
208
+ file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.md"
207
209
  else:
208
210
  file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.md"
209
211
  full_file_path = os.path.join(file_path, file_name)
@@ -231,12 +233,16 @@ def execute_format_set_action(
231
233
  return
232
234
  elif isinstance(output, (str, list)) and output_format == "DICT":
233
235
  output = json.dumps(output, indent=4)
234
- elif isinstance(output, (str, list)) and output_format == "REPORT":
236
+ elif isinstance(output, (str, list)) and output_format in[ "REPORT" ]:
235
237
  output = preamble + output
238
+ elif isinstance(output, (str, list)) and output_format == "HTML":
239
+ pass
236
240
 
237
241
  with open(full_file_path, 'w') as f:
238
242
  f.write(output)
239
- print(f"\n==> Output written to [{format_set_name}]({full_file_path})")
243
+ print(f"\n==> Output written to {full_file_path}")
244
+ if output_format == "HTML":
245
+ print(f"\n==> Web link: [{file_name}]({app_config.pyegeria_publishing_root}/{file_name}")
240
246
  return
241
247
 
242
248
  # For TABLE output, add output_format to params
@@ -355,7 +361,7 @@ def main():
355
361
  parser.add_argument("--userid", help="User Id")
356
362
  parser.add_argument("--password", help="User Password")
357
363
  parser.add_argument("--output-format", help="Output format (TABLE, DICT, FORM, REPORT, HTML, LIST)",
358
- choices=["TABLE", "DICT", "FORM", "REPORT", "HTML", "LIST"], default="TABLE")
364
+ choices=["TABLE", "DICT", "FORM", "REPORT", "HTML", "LIST", "MERMAID"], default="TABLE")
359
365
 
360
366
 
361
367
 
@@ -0,0 +1,373 @@
1
+ """
2
+ SPDX-License-Identifier: Apache-2.0
3
+ Copyright Contributors to the ODPi Egeria project.
4
+
5
+ This module defines Pydantic models for output format sets used in pyegeria.
6
+
7
+ These models provide a structured way to define and validate output formats
8
+ for different types of data, supporting composition and reuse of formats.
9
+
10
+ The module defines the following models:
11
+ - Column: Represents a column in an output format with name, key, and format attributes.
12
+ - Format: Represents a format configuration with types and columns.
13
+ - ActionParameter: Represents a parameter for an action with function, user_params, and spec_params.
14
+ - FormatSet: Represents a complete format set with heading, description, aliases, annotations, formats, and actions.
15
+ - FormatSetDict: A dictionary of format sets with methods for backward compatibility.
16
+
17
+ These models are used in the `_output_formats.py` module to replace the dictionary-based
18
+ implementation of output format sets. The models provide several advantages:
19
+ - Type validation: The models ensure that the data has the correct types and structure.
20
+ - Composition: The models support composition of formats, allowing formats to be reused and combined.
21
+ - Documentation: The models provide clear documentation of the data structure.
22
+ - IDE support: The models provide better IDE support, including autocompletion and type hints.
23
+
24
+ Example usage:
25
+ ```python
26
+ from pyegeria._output_format_models import Column, Format, FormatSet
27
+
28
+ # Create columns
29
+ columns = [
30
+ Column(name="Display Name", key="display_name"),
31
+ Column(name="Description", key="description", format=True),
32
+ ]
33
+
34
+ # Create a format
35
+ format = Format(
36
+ types=["TABLE", "DICT"],
37
+ columns=columns,
38
+ )
39
+
40
+ # Create a format set
41
+ format_set = FormatSet(
42
+ heading="Example Format Set",
43
+ description="An example format set",
44
+ formats=[format],
45
+ )
46
+
47
+ # Convert to dictionary for backward compatibility
48
+ format_set_dict = format_set.dict()
49
+ ```
50
+
51
+ The models are designed to be backward compatible with the existing dictionary-based
52
+ implementation. The `FormatSet` class has a `get` method that mimics the behavior of a
53
+ dictionary, and the `FormatSetDict` class provides dictionary-like access to the format sets.
54
+ """
55
+
56
+ import json
57
+ import os
58
+ from pathlib import Path
59
+ from typing import Dict, List, Optional, Union, Any
60
+ from pydantic import BaseModel, Field, validator
61
+ from loguru import logger
62
+
63
+ def save_format_sets_to_json(format_sets: Dict[str, 'FormatSet'], file_path: str) -> None:
64
+ """
65
+ Save format sets to a JSON file.
66
+
67
+ Args:
68
+ format_sets: The format sets to save
69
+ file_path: The path to save the file to
70
+ """
71
+ # Convert FormatSet objects to dictionaries
72
+ serializable_dict = {key: value.dict() for key, value in format_sets.items()}
73
+
74
+ # Create directory if it doesn't exist
75
+ os.makedirs(os.path.dirname(os.path.abspath(file_path)), exist_ok=True)
76
+
77
+ # Write to file
78
+ try:
79
+ with open(file_path, 'w') as f:
80
+ json.dump(serializable_dict, f, indent=2)
81
+ logger.info(f"Format sets saved to {file_path}")
82
+ except Exception as e:
83
+ logger.error(f"Error saving format sets to {file_path}: {e}")
84
+ raise
85
+
86
+ def load_format_sets_from_json(file_path: str) -> Dict[str, 'FormatSet']:
87
+ """
88
+ Load format sets from a JSON file.
89
+
90
+ Args:
91
+ file_path: The path to load the file from
92
+
93
+ Returns:
94
+ Dict[str, FormatSet]: The loaded format sets
95
+ """
96
+ try:
97
+ with open(file_path, 'r') as f:
98
+ data = json.load(f)
99
+
100
+ # Convert dictionaries to FormatSet objects
101
+ format_sets = {}
102
+ for key, value in data.items():
103
+ format_sets[key] = FormatSet(**value)
104
+
105
+ logger.info(f"Format sets loaded from {file_path}")
106
+ return format_sets
107
+ except Exception as e:
108
+ logger.error(f"Error loading format sets from {file_path}: {e}")
109
+ raise
110
+
111
+ class Column(BaseModel):
112
+ """
113
+ Represents a column in an output format.
114
+
115
+ Attributes:
116
+ name: The display name of the column
117
+ key: The key used to access the column's value in the data
118
+ format: Whether the column's value should be formatted
119
+ """
120
+ name: str
121
+ key: str
122
+ format: bool = False
123
+
124
+ class Format(BaseModel):
125
+ """
126
+ Represents a format configuration with types and columns.
127
+
128
+ Attributes:
129
+ types: The output types this format supports (e.g., "DICT", "TABLE", "ALL")
130
+ columns: The columns to include in the output
131
+ """
132
+ types: List[str]
133
+ columns: List[Union[Column, Dict[str, Any]]]
134
+
135
+ @validator('columns', pre=True)
136
+ def validate_columns(cls, v):
137
+ """Convert dictionary columns to Column objects."""
138
+ result = []
139
+ for item in v:
140
+ if isinstance(item, dict):
141
+ result.append(Column(**item))
142
+ else:
143
+ result.append(item)
144
+ return result
145
+
146
+ def dict(self, *args, **kwargs):
147
+ """Override dict method to convert Column objects back to dictionaries."""
148
+ result = super().dict(*args, **kwargs)
149
+ result['columns'] = [
150
+ column if isinstance(column, dict) else column.dict()
151
+ for column in self.columns
152
+ ]
153
+ return result
154
+
155
+ class ActionParameter(BaseModel):
156
+ """
157
+ Represents a parameter for an action.
158
+
159
+ Attributes:
160
+ function: The function to call
161
+ user_params: Parameters that can be provided by the user
162
+ spec_params: Parameters that are fixed for this action
163
+ """
164
+ function: str
165
+ user_params: List[str] = Field(default_factory=list)
166
+ spec_params: Dict[str, Any] = Field(default_factory=dict)
167
+
168
+ class FormatSet(BaseModel):
169
+ """
170
+ Represents a complete format set with heading, description, aliases, annotations, formats, and actions.
171
+
172
+ Attributes:
173
+ heading: A title for the format set
174
+ description: A description of what the format set is for
175
+ aliases: Alternative names that can be used to reference this format set
176
+ annotations: Additional metadata, like wiki links
177
+ formats: A list of format configurations
178
+ action: Optional actions associated with the format set
179
+ """
180
+ heading: str
181
+ description: str
182
+ aliases: List[str] = Field(default_factory=list)
183
+ annotations: Dict[str, List[str]] = Field(default_factory=dict)
184
+ formats: List[Union[Format, Dict[str, Any]]]
185
+ action: Optional[List[Union[ActionParameter, Dict[str, Any]]]] = None
186
+
187
+ @validator('formats', pre=True)
188
+ def validate_formats(cls, v):
189
+ """Convert dictionary formats to Format objects."""
190
+ result = []
191
+ for item in v:
192
+ if isinstance(item, dict):
193
+ result.append(Format(**item))
194
+ else:
195
+ result.append(item)
196
+ return result
197
+
198
+ @validator('action', pre=True)
199
+ def validate_action(cls, v):
200
+ """Convert dictionary actions to ActionParameter objects."""
201
+ if v is None:
202
+ return None
203
+ result = []
204
+ for item in v:
205
+ if isinstance(item, dict):
206
+ result.append(ActionParameter(**item))
207
+ else:
208
+ result.append(item)
209
+ return result
210
+
211
+ def dict(self, *args, **kwargs):
212
+ """Override dict method to convert nested objects back to dictionaries."""
213
+ result = super().dict(*args, **kwargs)
214
+ result['formats'] = [
215
+ format if isinstance(format, dict) else format.dict()
216
+ for format in self.formats
217
+ ]
218
+ if self.action:
219
+ result['action'] = [
220
+ action if isinstance(action, dict) else action.dict()
221
+ for action in self.action
222
+ ]
223
+ return result
224
+
225
+ def get(self, key, default=None):
226
+ """
227
+ Dictionary-like get method for backward compatibility.
228
+
229
+ Args:
230
+ key: The key to look up
231
+ default: The default value to return if the key is not found
232
+
233
+ Returns:
234
+ The value for the key if found, otherwise the default value
235
+ """
236
+ if hasattr(self, key):
237
+ return getattr(self, key)
238
+ return default
239
+
240
+ class FormatSetDict(Dict[str, FormatSet]):
241
+ """
242
+ A dictionary of format sets, with methods for backward compatibility.
243
+
244
+ This class allows the format sets to be accessed like a dictionary,
245
+ while providing the validation and structure of Pydantic models.
246
+
247
+ It also provides the ability to find format sets by either name or alias,
248
+ making it easier to work with format sets without knowing their exact name.
249
+ """
250
+ def __init__(self, *args, **kwargs):
251
+ super().__init__(*args, **kwargs)
252
+
253
+ def find_by_name_or_alias(self, key, default=None):
254
+ """
255
+ Find a format set by either name or alias.
256
+
257
+ This method first checks if the key exists directly in the dictionary.
258
+ If not found, it searches through all format sets to find one with a matching alias.
259
+
260
+ Args:
261
+ key: The name or alias to look up
262
+ default: The default value to return if the key is not found
263
+
264
+ Returns:
265
+ FormatSet: The format set if found, otherwise the default value
266
+ """
267
+ # First try to find by name (key)
268
+ format_set = super().get(key, None)
269
+
270
+ # If not found by name, try to find by alias
271
+ if format_set is None:
272
+ for value in self.values():
273
+ if key in value.aliases:
274
+ format_set = value
275
+ break
276
+
277
+ # Return the format set if found, otherwise the default value
278
+ return format_set if format_set is not None else default
279
+
280
+ def get(self, key, default=None):
281
+ """
282
+ Get a format set by name or alias.
283
+
284
+ This method first checks if the key exists directly in the dictionary.
285
+ If not found, it searches through all format sets to find one with a matching alias.
286
+
287
+ Args:
288
+ key: The name or alias to look up
289
+ default: The default value to return if the key is not found
290
+
291
+ Returns:
292
+ FormatSet: The format set if found, otherwise the default value
293
+ """
294
+ return self.find_by_name_or_alias(key, default)
295
+
296
+ def values(self):
297
+ """Get all format sets."""
298
+ return super().values()
299
+
300
+ def keys(self):
301
+ """Get all format set names."""
302
+ return super().keys()
303
+
304
+ def items(self):
305
+ """Get all format set items."""
306
+ return super().items()
307
+
308
+ def __getitem__(self, key):
309
+ """
310
+ Get a format set by name or alias.
311
+
312
+ This method first checks if the key exists directly in the dictionary.
313
+ If not found, it searches through all format sets to find one with a matching alias.
314
+ If still not found, it raises a KeyError.
315
+
316
+ Args:
317
+ key: The name or alias to look up
318
+
319
+ Returns:
320
+ FormatSet: The format set if found
321
+
322
+ Raises:
323
+ KeyError: If the format set is not found by name or alias
324
+ """
325
+ format_set = self.find_by_name_or_alias(key, None)
326
+ if format_set is None:
327
+ raise KeyError(key)
328
+ return format_set
329
+
330
+ def __setitem__(self, key, value):
331
+ """Set a format set by name."""
332
+ if isinstance(value, dict):
333
+ value = FormatSet(**value)
334
+ super().__setitem__(key, value)
335
+
336
+ def __contains__(self, key):
337
+ """
338
+ Check if a format set exists by name or alias.
339
+
340
+ Args:
341
+ key: The name or alias to check
342
+
343
+ Returns:
344
+ bool: True if the format set exists, False otherwise
345
+ """
346
+ return self.find_by_name_or_alias(key, None) is not None
347
+
348
+ def to_dict(self):
349
+ """Convert all format sets to dictionaries."""
350
+ return {key: value.dict() for key, value in self.items()}
351
+
352
+ def save_to_json(self, file_path: str) -> None:
353
+ """
354
+ Save format sets to a JSON file.
355
+
356
+ Args:
357
+ file_path: The path to save the file to
358
+ """
359
+ save_format_sets_to_json(self, file_path)
360
+
361
+ @classmethod
362
+ def load_from_json(cls, file_path: str) -> 'FormatSetDict':
363
+ """
364
+ Load format sets from a JSON file.
365
+
366
+ Args:
367
+ file_path: The path to load the file from
368
+
369
+ Returns:
370
+ FormatSetDict: A new FormatSetDict instance with the loaded format sets
371
+ """
372
+ format_sets = load_format_sets_from_json(file_path)
373
+ return cls(format_sets)