stepxmlclient 1.0.0__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 (70) hide show
  1. stepxmlclient/__init__.py +22 -0
  2. stepxmlclient/client.py +373 -0
  3. stepxmlclient/creator.py +317 -0
  4. stepxmlclient/model/__init__.py +418 -0
  5. stepxmlclient/model/asset.py +250 -0
  6. stepxmlclient/model/attribute.py +288 -0
  7. stepxmlclient/model/business.py +233 -0
  8. stepxmlclient/model/classification.py +409 -0
  9. stepxmlclient/model/complex_elements.py +260 -0
  10. stepxmlclient/model/deletion.py +87 -0
  11. stepxmlclient/model/entity.py +368 -0
  12. stepxmlclient/model/metadata.py +199 -0
  13. stepxmlclient/model/organization.py +167 -0
  14. stepxmlclient/model/product.py +519 -0
  15. stepxmlclient/model/relations.py +105 -0
  16. stepxmlclient/model/replacement.py +417 -0
  17. stepxmlclient/parser.py +2805 -0
  18. stepxmlclient/rest_client/__init__.py +16 -0
  19. stepxmlclient/rest_client/base.py +388 -0
  20. stepxmlclient/rest_client/client.py +127 -0
  21. stepxmlclient/rest_client/exceptions.py +97 -0
  22. stepxmlclient/rest_client/models/__init__.py +175 -0
  23. stepxmlclient/rest_client/models/asset.py +253 -0
  24. stepxmlclient/rest_client/models/attribute.py +112 -0
  25. stepxmlclient/rest_client/models/background_process.py +23 -0
  26. stepxmlclient/rest_client/models/batch.py +41 -0
  27. stepxmlclient/rest_client/models/classification.py +57 -0
  28. stepxmlclient/rest_client/models/collection.py +26 -0
  29. stepxmlclient/rest_client/models/common.py +192 -0
  30. stepxmlclient/rest_client/models/context.py +16 -0
  31. stepxmlclient/rest_client/models/data_type_group.py +15 -0
  32. stepxmlclient/rest_client/models/entity.py +63 -0
  33. stepxmlclient/rest_client/models/event_processor.py +15 -0
  34. stepxmlclient/rest_client/models/import_export.py +43 -0
  35. stepxmlclient/rest_client/models/integration_endpoint.py +16 -0
  36. stepxmlclient/rest_client/models/product.py +200 -0
  37. stepxmlclient/rest_client/models/relations.py +280 -0
  38. stepxmlclient/rest_client/models/system_information.py +11 -0
  39. stepxmlclient/rest_client/models/user.py +28 -0
  40. stepxmlclient/rest_client/models/workflow.py +30 -0
  41. stepxmlclient/rest_client/operations/__init__.py +54 -0
  42. stepxmlclient/rest_client/operations/assets.py +652 -0
  43. stepxmlclient/rest_client/operations/attributes.py +252 -0
  44. stepxmlclient/rest_client/operations/background_processes.py +84 -0
  45. stepxmlclient/rest_client/operations/batch.py +28 -0
  46. stepxmlclient/rest_client/operations/classifications.py +248 -0
  47. stepxmlclient/rest_client/operations/collection_groups.py +46 -0
  48. stepxmlclient/rest_client/operations/collections.py +67 -0
  49. stepxmlclient/rest_client/operations/contexts.py +88 -0
  50. stepxmlclient/rest_client/operations/data_type_groups.py +68 -0
  51. stepxmlclient/rest_client/operations/entities.py +295 -0
  52. stepxmlclient/rest_client/operations/event_processors.py +109 -0
  53. stepxmlclient/rest_client/operations/exports.py +153 -0
  54. stepxmlclient/rest_client/operations/gateway.py +53 -0
  55. stepxmlclient/rest_client/operations/inbound.py +472 -0
  56. stepxmlclient/rest_client/operations/outbound.py +64 -0
  57. stepxmlclient/rest_client/operations/products.py +489 -0
  58. stepxmlclient/rest_client/operations/relations.py +349 -0
  59. stepxmlclient/rest_client/operations/reports.py +27 -0
  60. stepxmlclient/rest_client/operations/system_information.py +28 -0
  61. stepxmlclient/rest_client/operations/user_groups.py +79 -0
  62. stepxmlclient/rest_client/operations/users.py +119 -0
  63. stepxmlclient/rest_client/operations/workflow_tasks.py +61 -0
  64. stepxmlclient/rest_client/operations/workflows.py +50 -0
  65. stepxmlclient/upload.py +293 -0
  66. stepxmlclient-1.0.0.dist-info/METADATA +666 -0
  67. stepxmlclient-1.0.0.dist-info/RECORD +70 -0
  68. stepxmlclient-1.0.0.dist-info/WHEEL +5 -0
  69. stepxmlclient-1.0.0.dist-info/licenses/LICENSE +21 -0
  70. stepxmlclient-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,87 @@
1
+ """Deletion models for STEP XML Client.
2
+
3
+ Defines entity and cross-reference deletion classes for removing items from STEP MDM.
4
+ Supports both full entity deletion and link severance operations.
5
+ """
6
+
7
+ from typing import List, Optional
8
+ from . import BaseObject
9
+
10
+ class DeleteAsset(BaseObject):
11
+ """Top-level deletion specification for assets.
12
+
13
+ Moves an asset to the recycle bin. The asset and all its associated data is marked for deletion.
14
+
15
+ Attributes:
16
+ ID: Asset ID to delete
17
+ KeyValue: Optional key value reference
18
+ """
19
+ _GROUP_NAME: str = 'deletedassets'
20
+ _TAG_NAME: str = 'DeleteAsset'
21
+ _ATTRIBUTE_NAMES: List[str] = ["ID"]
22
+ _TEXT_NAMES: List[str] = []
23
+ _CHILDREN_NAMES: List[str] = ["KeyValue"]
24
+
25
+
26
+ class DeleteProduct(BaseObject):
27
+ """Top-level deletion specification for products.
28
+
29
+ Moves a product to the recycle bin. The product and all its associated data is marked for deletion.
30
+
31
+ Attributes:
32
+ ID: Product ID to delete
33
+ KeyValue: Optional key value reference
34
+ """
35
+ _GROUP_NAME: str = 'deletedproducts'
36
+ _TAG_NAME: str = 'DeleteProduct'
37
+ _ATTRIBUTE_NAMES: List[str] = ["ID"]
38
+ _TEXT_NAMES: List[str] = []
39
+ _CHILDREN_NAMES: List[str] = ["KeyValue"]
40
+
41
+
42
+ class DeleteAssetCrossReference(BaseObject):
43
+ """Deletion specification for asset cross-references.
44
+
45
+ Severs the link between an asset and a product while keeping both items intact.
46
+ The product remains but the asset reference is removed.
47
+
48
+ Attributes:
49
+ AssetID: Asset ID of the link to sever
50
+ Type: Link type
51
+ QualifierID: Optional qualifier
52
+ Changed: Whether changed
53
+ KeyValue: Optional key value reference
54
+ MetaData: Optional metadata
55
+ """
56
+ _GROUP_NAME: Optional[str] = None # Used as child element only
57
+ _TAG_NAME: str = 'DeleteAssetCrossReference'
58
+ _ATTRIBUTE_NAMES: List[str] = ["AssetID", "Type", "QualifierID", "Changed"]
59
+ _TEXT_NAMES: List[str] = []
60
+ _CHILDREN_NAMES: List[str] = ["KeyValue", "MetaData"]
61
+
62
+
63
+ class DeleteProductCrossReference(BaseObject):
64
+ """Delete/sever product link from another product while keeping both intact."""
65
+ _GROUP_NAME: Optional[str] = None # Used as child element only
66
+ _TAG_NAME: str = 'DeleteProductCrossReference'
67
+ _ATTRIBUTE_NAMES: List[str] = ["ProductID", "Type", "QualifierID", "MountLocation", "Changed"]
68
+ _TEXT_NAMES: List[str] = []
69
+ _CHILDREN_NAMES: List[str] = ["KeyValue", "MetaData"]
70
+
71
+
72
+ class DeleteClassificationCrossReference(BaseObject):
73
+ """Delete/sever classification link while keeping classification intact."""
74
+ _GROUP_NAME: Optional[str] = None # Used as child element only
75
+ _TAG_NAME: str = 'DeleteClassificationCrossReference'
76
+ _ATTRIBUTE_NAMES: List[str] = ["ClassificationID", "Type", "QualifierID", "Changed"]
77
+ _TEXT_NAMES: List[str] = []
78
+ _CHILDREN_NAMES: List[str] = ["KeyValue", "MetaData"]
79
+
80
+
81
+ class DeleteEntityCrossReference(BaseObject):
82
+ """Delete/sever entity link while keeping entity intact."""
83
+ _GROUP_NAME: Optional[str] = None # Used as child element only
84
+ _TAG_NAME: str = 'DeleteEntityCrossReference'
85
+ _ATTRIBUTE_NAMES: List[str] = ["EntityID", "Type", "QualifierID", "Changed"]
86
+ _TEXT_NAMES: List[str] = []
87
+ _CHILDREN_NAMES: List[str] = ["KeyValue", "MetaData"]
@@ -0,0 +1,368 @@
1
+ """Entity model for STEP XML Client.
2
+
3
+ Defines the Entity class for representing generic MDM objects in STEP.
4
+ Entities provide a flexible structure for modeling various types of business objects beyond
5
+ products, classifications, and assets.
6
+ """
7
+
8
+ from typing import List, Optional, Any, Dict, TYPE_CHECKING
9
+ from . import BaseObject
10
+
11
+ if TYPE_CHECKING:
12
+ from .relations import ProductCrossReference, ClassificationCrossReference, EntityCrossReference, AssetCrossReference
13
+ from .deletion import DeleteProductCrossReference, DeleteClassificationCrossReference, DeleteEntityCrossReference, DeleteAssetCrossReference
14
+ from .complex_elements import CurrentTasks, Task, Terms, Term
15
+
16
+
17
+ class Entity(BaseObject):
18
+ """Entity model in STEP MDM.
19
+
20
+ Common ``data`` keys:
21
+ - ``ID``, ``ParentID``, ``UserTypeID``, ``QualifierID``
22
+ - ``Name``
23
+ - ``KeyValue`` / ``ParentKeyValue``
24
+ - ``Values`` and ``DataContainers``
25
+
26
+ Supports cross-references and also ``CurrentTasks`` / ``Terms`` children.
27
+
28
+ Usage examples:
29
+ Dict-first style::
30
+
31
+ entity = Entity({"ID": "E100", "UserTypeID": "UT_ENTITY"})
32
+ entity.add_product_reference(product_id="P100", type_="Owner")
33
+ entity.add_term({"TermsListID": "NET", "value": "Net 30"})
34
+
35
+ Pre-built object style::
36
+
37
+ from stepxmlclient.model import Task, Terms, Term
38
+ entity = Entity({"ID": "E101", "UserTypeID": "UT_ENTITY"})
39
+ entity.add_current_task(task_object=Task({
40
+ "STEPWorkflowID": "WF2",
41
+ "StateID": "S2",
42
+ "AssigneeID": "U2",
43
+ "AssignedToGroup": False,
44
+ "EntryTime": "2026-02-02T00:00:00Z",
45
+ }))
46
+ entity.add_term(terms_object=Terms({"Term": [Term({"TermsListID": "T1", "value": "Immediate"})]}))
47
+ """
48
+ _GROUP_NAME: str = 'entities'
49
+ _TAG_NAME: str = 'Entity'
50
+ _ATTRIBUTE_NAMES: List[str] = ["ID", "ParentID", "UserTypeID", "QualifierID", "Changed", "Republished", "Selected", "Referenced"]
51
+ _TEXT_NAMES: List[str] = ["Name"]
52
+ _CHILDREN_NAMES: List[str] = [
53
+ "KeyValue",
54
+ "ParentKeyValue",
55
+ "Entity", # Nested entities
56
+ "DeleteSuppressedClassificationCrossReference",
57
+ "DeleteClassificationCrossReference",
58
+ "SuppressedClassificationCrossReference",
59
+ "ClassificationCrossReference",
60
+ "DeleteSuppressedProductCrossReference",
61
+ "DeleteProductCrossReference",
62
+ "SuppressedProductCrossReference",
63
+ "ProductCrossReference",
64
+ "DeleteSuppressedAssetCrossReference",
65
+ "DeleteAssetCrossReference",
66
+ "SuppressedAssetCrossReference",
67
+ "AssetCrossReference",
68
+ "DeleteSuppressedEntityCrossReference",
69
+ "DeleteEntityCrossReference",
70
+ "SuppressedEntityCrossReference",
71
+ "EntityCrossReference",
72
+ "SuppressedContextCrossReference",
73
+ "ContextCrossReference",
74
+ "SuppressedWorkspaceCrossReference",
75
+ "WorkspaceCrossReference",
76
+ "Values",
77
+ "DataContainers",
78
+ "CurrentTasks",
79
+ "Terms"
80
+ ]
81
+
82
+ def __init__(self, data: Dict[str, Any]) -> None:
83
+ super().__init__(data)
84
+ # Initialize relation lists if not provided
85
+ for rel_name in ["ProductCrossReference", "DeleteProductCrossReference",
86
+ "SuppressedProductCrossReference", "DeleteSuppressedProductCrossReference",
87
+ "ClassificationCrossReference", "DeleteClassificationCrossReference",
88
+ "SuppressedClassificationCrossReference", "DeleteSuppressedClassificationCrossReference",
89
+ "EntityCrossReference", "DeleteEntityCrossReference",
90
+ "SuppressedEntityCrossReference", "DeleteSuppressedEntityCrossReference",
91
+ "AssetCrossReference", "DeleteAssetCrossReference",
92
+ "SuppressedAssetCrossReference", "DeleteSuppressedAssetCrossReference",
93
+ "ContextCrossReference", "SuppressedContextCrossReference",
94
+ "WorkspaceCrossReference", "SuppressedWorkspaceCrossReference",
95
+ "Values", "DataContainers", "CurrentTasks", "Terms"]:
96
+ if rel_name not in self._data:
97
+ self._data[rel_name] = []
98
+
99
+ # Initialize nested entities list
100
+ if "Entity" not in self._data:
101
+ self._data["Entity"] = []
102
+
103
+ # ===== Product Cross-Reference Methods =====
104
+
105
+ def add_product_reference(self, product_id: Optional[str] = None,
106
+ type_: Optional[str] = None,
107
+ qualifier_id: Optional[str] = None,
108
+ mount_location: Optional[str] = None,
109
+ ref_object: Optional['ProductCrossReference'] = None) -> None:
110
+ """Add ``ProductCrossReference``.
111
+
112
+ Fields: ``ProductID``, ``Type``, ``QualifierID``, ``MountLocation``.
113
+ """
114
+ from .relations import ProductCrossReference
115
+
116
+ if ref_object is not None:
117
+ ref = ref_object
118
+ else:
119
+ ref = ProductCrossReference({
120
+ "ProductID": product_id,
121
+ "Type": type_,
122
+ "QualifierID": qualifier_id,
123
+ "MountLocation": mount_location
124
+ })
125
+
126
+ if not isinstance(self._data["ProductCrossReference"], list):
127
+ self._data["ProductCrossReference"] = []
128
+ self._data["ProductCrossReference"].append(ref)
129
+
130
+ def delete_product_reference(self, product_id: Optional[str] = None,
131
+ type_: Optional[str] = None,
132
+ qualifier_id: Optional[str] = None,
133
+ mount_location: Optional[str] = None,
134
+ delete_ref_object: Optional['DeleteProductCrossReference'] = None) -> None:
135
+ """Add ``DeleteProductCrossReference``.
136
+
137
+ Fields: ``ProductID``, ``Type``, ``QualifierID``, ``MountLocation``.
138
+ """
139
+ from .deletion import DeleteProductCrossReference
140
+
141
+ if delete_ref_object is not None:
142
+ delete_ref = delete_ref_object
143
+ else:
144
+ delete_ref = DeleteProductCrossReference({
145
+ "ProductID": product_id,
146
+ "Type": type_,
147
+ "QualifierID": qualifier_id,
148
+ "MountLocation": mount_location
149
+ })
150
+
151
+ if not isinstance(self._data["DeleteProductCrossReference"], list):
152
+ self._data["DeleteProductCrossReference"] = []
153
+ self._data["DeleteProductCrossReference"].append(delete_ref)
154
+
155
+ # ===== Classification Cross-Reference Methods =====
156
+
157
+ def add_classification_reference(self, classification_id: Optional[str] = None,
158
+ type_: Optional[str] = None,
159
+ qualifier_id: Optional[str] = None,
160
+ mount_location: Optional[str] = None,
161
+ ref_object: Optional['ClassificationCrossReference'] = None) -> None:
162
+ """Add ``ClassificationCrossReference``.
163
+
164
+ Fields: ``ClassificationID``, ``Type``, ``QualifierID``,
165
+ ``MountLocation``.
166
+ """
167
+ from .relations import ClassificationCrossReference
168
+
169
+ if ref_object is not None:
170
+ ref = ref_object
171
+ else:
172
+ ref = ClassificationCrossReference({
173
+ "ClassificationID": classification_id,
174
+ "Type": type_,
175
+ "QualifierID": qualifier_id,
176
+ "MountLocation": mount_location
177
+ })
178
+
179
+ if not isinstance(self._data["ClassificationCrossReference"], list):
180
+ self._data["ClassificationCrossReference"] = []
181
+ self._data["ClassificationCrossReference"].append(ref)
182
+
183
+ def delete_classification_reference(self, classification_id: Optional[str] = None,
184
+ type_: Optional[str] = None,
185
+ qualifier_id: Optional[str] = None,
186
+ mount_location: Optional[str] = None,
187
+ delete_ref_object: Optional['DeleteClassificationCrossReference'] = None) -> None:
188
+ """Add ``DeleteClassificationCrossReference``.
189
+
190
+ Fields: ``ClassificationID``, ``Type``, ``QualifierID``,
191
+ ``MountLocation``.
192
+ """
193
+ from .deletion import DeleteClassificationCrossReference
194
+
195
+ if delete_ref_object is not None:
196
+ delete_ref = delete_ref_object
197
+ else:
198
+ delete_ref = DeleteClassificationCrossReference({
199
+ "ClassificationID": classification_id,
200
+ "Type": type_,
201
+ "QualifierID": qualifier_id,
202
+ "MountLocation": mount_location
203
+ })
204
+
205
+ if not isinstance(self._data["DeleteClassificationCrossReference"], list):
206
+ self._data["DeleteClassificationCrossReference"] = []
207
+ self._data["DeleteClassificationCrossReference"].append(delete_ref)
208
+
209
+ # ===== Entity Cross-Reference Methods =====
210
+
211
+ def add_entity_reference(self, entity_id: Optional[str] = None,
212
+ type_: Optional[str] = None,
213
+ qualifier_id: Optional[str] = None,
214
+ mount_location: Optional[str] = None,
215
+ ref_object: Optional['EntityCrossReference'] = None) -> None:
216
+ """Add ``EntityCrossReference``.
217
+
218
+ Fields: ``EntityID``, ``Type``, ``QualifierID``, ``MountLocation``.
219
+ """
220
+ from .relations import EntityCrossReference
221
+
222
+ if ref_object is not None:
223
+ ref = ref_object
224
+ else:
225
+ ref = EntityCrossReference({
226
+ "EntityID": entity_id,
227
+ "Type": type_,
228
+ "QualifierID": qualifier_id,
229
+ "MountLocation": mount_location
230
+ })
231
+
232
+ if not isinstance(self._data["EntityCrossReference"], list):
233
+ self._data["EntityCrossReference"] = []
234
+ self._data["EntityCrossReference"].append(ref)
235
+
236
+ def delete_entity_reference(self, entity_id: Optional[str] = None,
237
+ type_: Optional[str] = None,
238
+ qualifier_id: Optional[str] = None,
239
+ mount_location: Optional[str] = None,
240
+ delete_ref_object: Optional['DeleteEntityCrossReference'] = None) -> None:
241
+ """Add ``DeleteEntityCrossReference``.
242
+
243
+ Fields: ``EntityID``, ``Type``, ``QualifierID``, ``MountLocation``.
244
+ """
245
+ from .deletion import DeleteEntityCrossReference
246
+
247
+ if delete_ref_object is not None:
248
+ delete_ref = delete_ref_object
249
+ else:
250
+ delete_ref = DeleteEntityCrossReference({
251
+ "EntityID": entity_id,
252
+ "Type": type_,
253
+ "QualifierID": qualifier_id,
254
+ "MountLocation": mount_location
255
+ })
256
+
257
+ if not isinstance(self._data["DeleteEntityCrossReference"], list):
258
+ self._data["DeleteEntityCrossReference"] = []
259
+ self._data["DeleteEntityCrossReference"].append(delete_ref)
260
+
261
+ # ===== Asset Cross-Reference Methods =====
262
+
263
+ def add_asset_reference(self, asset_id: Optional[str] = None,
264
+ type_: Optional[str] = None,
265
+ qualifier_id: Optional[str] = None,
266
+ ref_object: Optional['AssetCrossReference'] = None) -> None:
267
+ """Add ``AssetCrossReference``.
268
+
269
+ Fields: ``AssetID``, ``Type``, ``QualifierID``.
270
+ """
271
+ from .relations import AssetCrossReference
272
+
273
+ if ref_object is not None:
274
+ ref = ref_object
275
+ else:
276
+ ref = AssetCrossReference({
277
+ "AssetID": asset_id,
278
+ "Type": type_,
279
+ "QualifierID": qualifier_id
280
+ })
281
+
282
+ if not isinstance(self._data["AssetCrossReference"], list):
283
+ self._data["AssetCrossReference"] = []
284
+ self._data["AssetCrossReference"].append(ref)
285
+
286
+ def delete_asset_reference(self, asset_id: Optional[str] = None,
287
+ type_: Optional[str] = None,
288
+ qualifier_id: Optional[str] = None,
289
+ delete_ref_object: Optional['DeleteAssetCrossReference'] = None) -> None:
290
+ """Add ``DeleteAssetCrossReference``.
291
+
292
+ Fields: ``AssetID``, ``Type``, ``QualifierID``.
293
+ """
294
+ from .deletion import DeleteAssetCrossReference
295
+
296
+ if delete_ref_object is not None:
297
+ delete_ref = delete_ref_object
298
+ else:
299
+ delete_ref = DeleteAssetCrossReference({
300
+ "AssetID": asset_id,
301
+ "Type": type_,
302
+ "QualifierID": qualifier_id
303
+ })
304
+
305
+ if not isinstance(self._data["DeleteAssetCrossReference"], list):
306
+ self._data["DeleteAssetCrossReference"] = []
307
+ self._data["DeleteAssetCrossReference"].append(delete_ref)
308
+
309
+ # ===== Nested Entity Methods =====
310
+
311
+ def add_nested_entity(self, entity: Optional['Entity'] = None,
312
+ **kwargs) -> None:
313
+ """Add nested ``Entity`` object.
314
+
315
+ Pass either ``entity`` or kwargs for ``Entity(...)``.
316
+ """
317
+ if entity is not None:
318
+ nested = entity
319
+ else:
320
+ nested = Entity(kwargs)
321
+
322
+ if not isinstance(self._data["Entity"], list):
323
+ self._data["Entity"] = []
324
+ self._data["Entity"].append(nested)
325
+
326
+ def add_current_task(self,
327
+ task_data: Optional[Dict[str, Any]] = None,
328
+ task_object: Optional['Task'] = None,
329
+ current_tasks_object: Optional['CurrentTasks'] = None) -> None:
330
+ """Add ``CurrentTasks`` with task entries.
331
+
332
+ Task keys: ``STEPWorkflowID``, ``StateID``, ``AssigneeID``,
333
+ ``AssignedToGroup``, ``EntryTime``, optional ``Deadline``, ``Priority``,
334
+ ``StatusID``.
335
+ """
336
+ from .complex_elements import CurrentTasks, Task
337
+
338
+ if current_tasks_object is not None:
339
+ entry: Any = current_tasks_object
340
+ else:
341
+ task = task_object or Task(task_data or {})
342
+ entry = CurrentTasks({"Task": [task]})
343
+
344
+ if not isinstance(self._data.get("CurrentTasks"), list):
345
+ self._data["CurrentTasks"] = []
346
+ self._data["CurrentTasks"].append(entry)
347
+
348
+ def add_term(self,
349
+ term_data: Optional[Dict[str, Any]] = None,
350
+ term_object: Optional['Term'] = None,
351
+ terms_object: Optional['Terms'] = None) -> None:
352
+ """Add ``Terms`` with one or more term entries.
353
+
354
+ Term keys: ``TermsListID``, ``MinimumQuantity``, ``MaximumQuantity``,
355
+ ``StartDate``, ``EndDate``, ``LeadTime``, ``UnitID``, ``LotSize`` and
356
+ optional text value via ``value``.
357
+ """
358
+ from .complex_elements import Terms, Term
359
+
360
+ if terms_object is not None:
361
+ entry: Any = terms_object
362
+ else:
363
+ term = term_object or Term(term_data or {})
364
+ entry = Terms({"Term": [term]})
365
+
366
+ if not isinstance(self._data.get("Terms"), list):
367
+ self._data["Terms"] = []
368
+ self._data["Terms"].append(entry)
@@ -0,0 +1,199 @@
1
+ """Metadata and configuration models for STEP XML Client.
2
+
3
+ Defines metadata and configuration classes for STEP MDM systems including user types,
4
+ contexts, qualifiers, dimensions, tags, units, and other configuration elements.
5
+ """
6
+
7
+ from typing import List, Optional, Any, Dict, TYPE_CHECKING
8
+ from . import BaseObject
9
+
10
+ if TYPE_CHECKING:
11
+ pass
12
+
13
+
14
+ class UserType(BaseObject):
15
+ """User type - object type definition in STEP MDM.
16
+
17
+ User types define customizable object classifications with attributes, validation rules,
18
+ icons, and category information. User types control the structure and behavior of
19
+ products, assets, classifications, and entities.
20
+
21
+ Supports:
22
+ - Multiple names (multi-language)
23
+ - Custom icons and templates
24
+ - Attribute and dimension associations
25
+ - Validation rules
26
+ - Category classification
27
+ - Revisability and link policies
28
+ - Boolean state tracking (Selected, Referenced, Changed, Deleted)
29
+
30
+ Attributes:
31
+ ID: Unique user type identifier
32
+ AllowOverlappingTerms: Whether overlapping terms are allowed
33
+ AllowQuarkTemplate: Support for Quark templates
34
+ AllowInDesignTemplate: Support for InDesign templates
35
+ Revisability: Revision tracking policy
36
+ IsCategory: Whether this type represents a category
37
+ DefaultProfileConfiguration: Default profile configuration
38
+ ClassificationOwnsProductLinks: Classification ownership semantics
39
+ NamePattern: Pattern for naming instances
40
+ IDPattern: Pattern for ID generation
41
+ ManuallySorted: Whether instances are manually sorted
42
+ ReferenceTargetLockPolicy: Link policy
43
+ Name: User type name (localized)
44
+ Icon: Icon representation
45
+ VisibleTermFields: Visible fields for search
46
+ MetaData: Metadata
47
+ DimensionLink: Dimension associations
48
+ UserTypeLink: Links to other user types
49
+ AttributeLink: Attribute associations
50
+ Validation: Validation rules
51
+ MIMEType: Associated MIME types
52
+ """
53
+ _GROUP_NAME: str = 'usertypes'
54
+ _TAG_NAME: str = 'UserType'
55
+ _ATTRIBUTE_NAMES: List[str] = [
56
+ "ID", "AllowOverlappingTerms", "AllowQuarkTemplate",
57
+ "AllowInDesignTemplate", "Revisability", "IsCategory",
58
+ "DefaultProfileConfiguration", "ClassificationOwnsProductLinks",
59
+ "NamePattern", "IDPattern", "ManuallySorted",
60
+ "ReferenceTargetLockPolicy", "Selected", "Referenced",
61
+ "Changed", "Deleted"
62
+ ]
63
+ _TEXT_NAMES: List[str] = ["Name"]
64
+ _CHILDREN_NAMES: List[str] = [
65
+ "Icon",
66
+ "VisibleTermFields",
67
+ "MetaData",
68
+ "DimensionLink",
69
+ "UserTypeLink",
70
+ "AttributeLink",
71
+ "Validation",
72
+ "MIMEType"
73
+ ]
74
+
75
+ def __init__(self, data: Dict[str, Any]) -> None:
76
+ super().__init__(data)
77
+ # Initialize link lists if not provided
78
+ for link_name in ["DimensionLink", "UserTypeLink", "AttributeLink", "MIMEType"]:
79
+ if link_name not in self._data:
80
+ self._data[link_name] = []
81
+
82
+
83
+ class LinkType(BaseObject):
84
+ """LinkType - defines the type of cross-reference between objects.
85
+
86
+ Link types specify valid relationship types (e.g., CA=Classification-to-Attribute,
87
+ PA=Product-to-Attribute) for organizing cross-references.
88
+
89
+ Simple metadata element with minimal structure.
90
+ """
91
+ _GROUP_NAME: str = 'linktypes'
92
+ _TAG_NAME: str = 'LinkType'
93
+ _ATTRIBUTE_NAMES: List[str] = ["LinkTypeID"]
94
+ _TEXT_NAMES: List[str] = []
95
+ _CHILDREN_NAMES: List[str] = []
96
+
97
+ def __init__(self, data: Dict[str, Any]) -> None:
98
+ super().__init__(data)
99
+
100
+
101
+ class Tag(BaseObject):
102
+ """Tag - flexible categorization element independent of hierarchies.
103
+
104
+ Tags enable custom classification and filtering of objects
105
+ outside the standard product/classification hierarchies.
106
+
107
+ Supports:
108
+ - Multi-language names
109
+ - Metadata tracking
110
+ """
111
+ _GROUP_NAME: str = 'tags'
112
+ _TAG_NAME: str = 'Tag'
113
+ _ATTRIBUTE_NAMES: List[str] = ["ID"]
114
+ _TEXT_NAMES: List[str] = ["Name"]
115
+ _CHILDREN_NAMES: List[str] = ["MetaData"]
116
+
117
+ def __init__(self, data: Dict[str, Any]) -> None:
118
+ super().__init__(data)
119
+
120
+
121
+ class TagGroup(BaseObject):
122
+ """TagGroup - hierarchical grouping of tags.
123
+
124
+ Organizes tags into logical collections with parent/child relationships.
125
+
126
+ Supports:
127
+ - Nested tag groups (hierarchies)
128
+ - Multi-language names
129
+ - Tag collection
130
+ - Metadata tracking
131
+ """
132
+ _GROUP_NAME: str = 'taggroups'
133
+ _TAG_NAME: str = 'TagGroup'
134
+ _ATTRIBUTE_NAMES: List[str] = ["ID"]
135
+ _TEXT_NAMES: List[str] = ["Name"]
136
+ _CHILDREN_NAMES: List[str] = ["MetaData", "TagGroup", "Tag"]
137
+
138
+ def __init__(self, data: Dict[str, Any]) -> None:
139
+ super().__init__(data)
140
+ # Initialize nested tag groups and tags lists
141
+ if "TagGroup" not in self._data:
142
+ self._data["TagGroup"] = []
143
+ if "Tag" not in self._data:
144
+ self._data["Tag"] = []
145
+
146
+ def add_nested_tag_group(self, tag_group: Optional['TagGroup'] = None,
147
+ **kwargs) -> None:
148
+ """Add nested tag group.
149
+
150
+ Can be called two ways:
151
+ 1. With object: add_nested_tag_group(tag_group=group_obj)
152
+ 2. With parameters: add_nested_tag_group(ID="TG002", Name="Nested Group")
153
+ """
154
+ if tag_group is not None:
155
+ group = tag_group
156
+ else:
157
+ group = TagGroup(kwargs)
158
+
159
+ if not isinstance(self._data["TagGroup"], list):
160
+ self._data["TagGroup"] = []
161
+ self._data["TagGroup"].append(group)
162
+
163
+ def add_tag(self, tag: Optional['Tag'] = None,
164
+ **kwargs) -> None:
165
+ """Add a tag to this group.
166
+
167
+ Can be called two ways:
168
+ 1. With object: add_tag(tag=tag_obj)
169
+ 2. With parameters: add_tag(ID="T001", Name="Tag Name")
170
+ """
171
+ if tag is not None:
172
+ t = tag
173
+ else:
174
+ t = Tag(kwargs)
175
+
176
+ if not isinstance(self._data["Tag"], list):
177
+ self._data["Tag"] = []
178
+ self._data["Tag"].append(t)
179
+
180
+
181
+ class Unit(BaseObject):
182
+ """Unit - defines a measurement unit for quantitative attributes.
183
+
184
+ Units represent measurement standards (meters, kilograms, liters, etc.)
185
+ used for quantitative attribute values.
186
+
187
+ Supports:
188
+ - Unit symbol (m, kg, l, etc.)
189
+ - Conversion formula to base unit
190
+ - Metadata tracking
191
+ """
192
+ _GROUP_NAME: str = 'units'
193
+ _TAG_NAME: str = 'Unit'
194
+ _ATTRIBUTE_NAMES: List[str] = ["ID", "Name", "Symbol", "Formula"]
195
+ _TEXT_NAMES: List[str] = []
196
+ _CHILDREN_NAMES: List[str] = ["MetaData"]
197
+
198
+ def __init__(self, data: Dict[str, Any]) -> None:
199
+ super().__init__(data)