etcion 0.2.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.
- etcion/__init__.py +350 -0
- etcion/comparison.py +152 -0
- etcion/conformance.py +136 -0
- etcion/derivation/__init__.py +19 -0
- etcion/derivation/engine.py +115 -0
- etcion/enums.py +117 -0
- etcion/exceptions.py +27 -0
- etcion/impact.py +590 -0
- etcion/metamodel/__init__.py +23 -0
- etcion/metamodel/application.py +162 -0
- etcion/metamodel/business.py +216 -0
- etcion/metamodel/concepts.py +129 -0
- etcion/metamodel/elements.py +112 -0
- etcion/metamodel/implementation_migration.py +99 -0
- etcion/metamodel/mixins.py +31 -0
- etcion/metamodel/model.py +374 -0
- etcion/metamodel/motivation.py +131 -0
- etcion/metamodel/notation.py +36 -0
- etcion/metamodel/physical.py +73 -0
- etcion/metamodel/profiles.py +68 -0
- etcion/metamodel/relationships.py +266 -0
- etcion/metamodel/strategy.py +72 -0
- etcion/metamodel/technology.py +210 -0
- etcion/metamodel/viewpoint_catalogue.py +1133 -0
- etcion/metamodel/viewpoints.py +83 -0
- etcion/patterns.py +1043 -0
- etcion/py.typed +0 -0
- etcion/serialization/__init__.py +1 -0
- etcion/serialization/json.py +92 -0
- etcion/serialization/registry.py +292 -0
- etcion/serialization/schema/archimate3_Diagram.xsd +554 -0
- etcion/serialization/schema/archimate3_Model.xsd +1340 -0
- etcion/serialization/schema/archimate3_View.xsd +321 -0
- etcion/serialization/xml.py +249 -0
- etcion/validation/__init__.py +20 -0
- etcion/validation/permissions.py +477 -0
- etcion/validation/rules.py +41 -0
- etcion-0.2.0.dist-info/METADATA +251 -0
- etcion-0.2.0.dist-info/RECORD +40 -0
- etcion-0.2.0.dist-info/WHEEL +4 -0
etcion/__init__.py
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""etcion -- Python implementation of the ArchiMate 3.2 metamodel."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError
|
|
4
|
+
from importlib.metadata import version as _meta_version
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
__version__: str = _meta_version("etcion")
|
|
8
|
+
except PackageNotFoundError: # pragma: no cover
|
|
9
|
+
__version__ = "0.0.0.dev0"
|
|
10
|
+
|
|
11
|
+
# Phase 4: Model comparison and diff utilities (EPIC-024, FEAT-24.1)
|
|
12
|
+
from etcion.comparison import ConceptChange, FieldChange, ModelDiff, diff_models
|
|
13
|
+
from etcion.conformance import CONFORMANCE, ConformanceProfile
|
|
14
|
+
from etcion.derivation.engine import DerivationEngine
|
|
15
|
+
from etcion.enums import (
|
|
16
|
+
AccessMode,
|
|
17
|
+
Aspect,
|
|
18
|
+
AssociationDirection,
|
|
19
|
+
ContentCategory,
|
|
20
|
+
InfluenceSign,
|
|
21
|
+
JunctionType,
|
|
22
|
+
Layer,
|
|
23
|
+
PurposeCategory,
|
|
24
|
+
RelationshipCategory,
|
|
25
|
+
)
|
|
26
|
+
from etcion.exceptions import (
|
|
27
|
+
ConformanceError,
|
|
28
|
+
DerivationError,
|
|
29
|
+
PyArchiError,
|
|
30
|
+
ValidationError,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Phase 5: Impact analysis engine (ADR-043, Issue #9)
|
|
34
|
+
from etcion.impact import ImpactedConcept, ImpactResult, analyze_impact, chain_impacts
|
|
35
|
+
|
|
36
|
+
# Phase 2: Application layer (EPIC-008)
|
|
37
|
+
from etcion.metamodel.application import (
|
|
38
|
+
ApplicationCollaboration,
|
|
39
|
+
ApplicationComponent,
|
|
40
|
+
ApplicationEvent,
|
|
41
|
+
ApplicationFunction,
|
|
42
|
+
ApplicationInteraction,
|
|
43
|
+
ApplicationInterface,
|
|
44
|
+
ApplicationInternalActiveStructureElement,
|
|
45
|
+
ApplicationInternalBehaviorElement,
|
|
46
|
+
ApplicationProcess,
|
|
47
|
+
ApplicationService,
|
|
48
|
+
DataObject,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Phase 2: Business layer (EPIC-007)
|
|
52
|
+
from etcion.metamodel.business import (
|
|
53
|
+
BusinessActor,
|
|
54
|
+
BusinessCollaboration,
|
|
55
|
+
BusinessEvent,
|
|
56
|
+
BusinessFunction,
|
|
57
|
+
BusinessInteraction,
|
|
58
|
+
BusinessInterface,
|
|
59
|
+
BusinessInternalActiveStructureElement,
|
|
60
|
+
BusinessInternalBehaviorElement,
|
|
61
|
+
BusinessObject,
|
|
62
|
+
BusinessPassiveStructureElement,
|
|
63
|
+
BusinessProcess,
|
|
64
|
+
BusinessRole,
|
|
65
|
+
BusinessService,
|
|
66
|
+
Contract,
|
|
67
|
+
Product,
|
|
68
|
+
Representation,
|
|
69
|
+
)
|
|
70
|
+
from etcion.metamodel.concepts import (
|
|
71
|
+
Concept,
|
|
72
|
+
Element,
|
|
73
|
+
Relationship,
|
|
74
|
+
RelationshipConnector,
|
|
75
|
+
)
|
|
76
|
+
from etcion.metamodel.elements import (
|
|
77
|
+
ActiveStructureElement,
|
|
78
|
+
BehaviorElement,
|
|
79
|
+
CompositeElement,
|
|
80
|
+
Event,
|
|
81
|
+
ExternalActiveStructureElement,
|
|
82
|
+
ExternalBehaviorElement,
|
|
83
|
+
Function,
|
|
84
|
+
Grouping,
|
|
85
|
+
Interaction,
|
|
86
|
+
InternalActiveStructureElement,
|
|
87
|
+
InternalBehaviorElement,
|
|
88
|
+
Location,
|
|
89
|
+
MotivationElement,
|
|
90
|
+
PassiveStructureElement,
|
|
91
|
+
Process,
|
|
92
|
+
StructureElement,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Phase 2: Implementation & Migration layer (EPIC-012)
|
|
96
|
+
from etcion.metamodel.implementation_migration import (
|
|
97
|
+
Deliverable,
|
|
98
|
+
Gap,
|
|
99
|
+
ImplementationEvent,
|
|
100
|
+
Plateau,
|
|
101
|
+
WorkPackage,
|
|
102
|
+
)
|
|
103
|
+
from etcion.metamodel.model import Model
|
|
104
|
+
|
|
105
|
+
# Phase 2: Motivation layer (EPIC-011)
|
|
106
|
+
from etcion.metamodel.motivation import (
|
|
107
|
+
Assessment,
|
|
108
|
+
Constraint,
|
|
109
|
+
Driver,
|
|
110
|
+
Goal,
|
|
111
|
+
Meaning,
|
|
112
|
+
Outcome,
|
|
113
|
+
Principle,
|
|
114
|
+
Requirement,
|
|
115
|
+
Stakeholder,
|
|
116
|
+
Value,
|
|
117
|
+
)
|
|
118
|
+
from etcion.metamodel.notation import NotationMetadata
|
|
119
|
+
|
|
120
|
+
# Phase 2: Physical layer (EPIC-010)
|
|
121
|
+
from etcion.metamodel.physical import (
|
|
122
|
+
DistributionNetwork,
|
|
123
|
+
Equipment,
|
|
124
|
+
Facility,
|
|
125
|
+
Material,
|
|
126
|
+
PhysicalActiveStructureElement,
|
|
127
|
+
PhysicalPassiveStructureElement,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Phase 3: Language customization (EPIC-018)
|
|
131
|
+
from etcion.metamodel.profiles import Profile
|
|
132
|
+
from etcion.metamodel.relationships import (
|
|
133
|
+
Access,
|
|
134
|
+
Aggregation,
|
|
135
|
+
Assignment,
|
|
136
|
+
Association,
|
|
137
|
+
Composition,
|
|
138
|
+
DependencyRelationship,
|
|
139
|
+
DynamicRelationship,
|
|
140
|
+
Flow,
|
|
141
|
+
Influence,
|
|
142
|
+
Junction,
|
|
143
|
+
OtherRelationship,
|
|
144
|
+
Realization,
|
|
145
|
+
Serving,
|
|
146
|
+
Specialization,
|
|
147
|
+
StructuralRelationship,
|
|
148
|
+
Triggering,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Phase 2: Strategy layer (EPIC-006)
|
|
152
|
+
from etcion.metamodel.strategy import (
|
|
153
|
+
Capability,
|
|
154
|
+
CourseOfAction,
|
|
155
|
+
Resource,
|
|
156
|
+
StrategyBehaviorElement,
|
|
157
|
+
StrategyStructureElement,
|
|
158
|
+
ValueStream,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Phase 2: Technology layer (EPIC-009)
|
|
162
|
+
from etcion.metamodel.technology import (
|
|
163
|
+
Artifact,
|
|
164
|
+
CommunicationNetwork,
|
|
165
|
+
Device,
|
|
166
|
+
Node,
|
|
167
|
+
Path,
|
|
168
|
+
SystemSoftware,
|
|
169
|
+
TechnologyCollaboration,
|
|
170
|
+
TechnologyEvent,
|
|
171
|
+
TechnologyFunction,
|
|
172
|
+
TechnologyInteraction,
|
|
173
|
+
TechnologyInterface,
|
|
174
|
+
TechnologyInternalActiveStructureElement,
|
|
175
|
+
TechnologyInternalBehaviorElement,
|
|
176
|
+
TechnologyProcess,
|
|
177
|
+
TechnologyService,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Phase 4: Predefined Viewpoint Catalogue (EPIC-022, FEAT-22.1)
|
|
181
|
+
from etcion.metamodel.viewpoint_catalogue import VIEWPOINT_CATALOGUE, ViewpointCatalogue
|
|
182
|
+
|
|
183
|
+
# Phase 3: Viewpoints (EPIC-017)
|
|
184
|
+
from etcion.metamodel.viewpoints import Concern, View, Viewpoint
|
|
185
|
+
from etcion.validation.permissions import is_permitted, warm_cache
|
|
186
|
+
from etcion.validation.rules import ValidationRule
|
|
187
|
+
|
|
188
|
+
SPEC_VERSION: str = "3.2"
|
|
189
|
+
"""The ArchiMate specification version implemented by this library."""
|
|
190
|
+
|
|
191
|
+
__all__: list[str] = [
|
|
192
|
+
"__version__",
|
|
193
|
+
"SPEC_VERSION",
|
|
194
|
+
# exceptions (FEAT-00.2)
|
|
195
|
+
"PyArchiError",
|
|
196
|
+
"ValidationError",
|
|
197
|
+
"DerivationError",
|
|
198
|
+
"ConformanceError",
|
|
199
|
+
# conformance (FEAT-01.1)
|
|
200
|
+
"ConformanceProfile",
|
|
201
|
+
"CONFORMANCE",
|
|
202
|
+
# root type hierarchy (EPIC-002)
|
|
203
|
+
"Concept",
|
|
204
|
+
"Element",
|
|
205
|
+
"Relationship",
|
|
206
|
+
"RelationshipConnector",
|
|
207
|
+
"Model",
|
|
208
|
+
# language structure (EPIC-003)
|
|
209
|
+
"Aspect",
|
|
210
|
+
"Layer",
|
|
211
|
+
"NotationMetadata",
|
|
212
|
+
# EPIC-004: Generic Metamodel -- Abstract Element Hierarchy
|
|
213
|
+
"ActiveStructureElement",
|
|
214
|
+
"BehaviorElement",
|
|
215
|
+
"CompositeElement",
|
|
216
|
+
"Event",
|
|
217
|
+
"ExternalActiveStructureElement",
|
|
218
|
+
"ExternalBehaviorElement",
|
|
219
|
+
"Function",
|
|
220
|
+
"Grouping",
|
|
221
|
+
"InternalActiveStructureElement",
|
|
222
|
+
"InternalBehaviorElement",
|
|
223
|
+
"Interaction",
|
|
224
|
+
"Location",
|
|
225
|
+
"MotivationElement",
|
|
226
|
+
"PassiveStructureElement",
|
|
227
|
+
"Process",
|
|
228
|
+
"StructureElement",
|
|
229
|
+
# EPIC-005: Relationships and Relationship Connectors
|
|
230
|
+
"RelationshipCategory",
|
|
231
|
+
"AccessMode",
|
|
232
|
+
"AssociationDirection",
|
|
233
|
+
"InfluenceSign",
|
|
234
|
+
"JunctionType",
|
|
235
|
+
"StructuralRelationship",
|
|
236
|
+
"DependencyRelationship",
|
|
237
|
+
"DynamicRelationship",
|
|
238
|
+
"OtherRelationship",
|
|
239
|
+
"Composition",
|
|
240
|
+
"Aggregation",
|
|
241
|
+
"Assignment",
|
|
242
|
+
"Realization",
|
|
243
|
+
"Serving",
|
|
244
|
+
"Access",
|
|
245
|
+
"Influence",
|
|
246
|
+
"Association",
|
|
247
|
+
"Triggering",
|
|
248
|
+
"Flow",
|
|
249
|
+
"Specialization",
|
|
250
|
+
"Junction",
|
|
251
|
+
"DerivationEngine",
|
|
252
|
+
"is_permitted",
|
|
253
|
+
"warm_cache",
|
|
254
|
+
"ValidationRule",
|
|
255
|
+
# Strategy layer (EPIC-006)
|
|
256
|
+
"StrategyStructureElement",
|
|
257
|
+
"StrategyBehaviorElement",
|
|
258
|
+
"Resource",
|
|
259
|
+
"Capability",
|
|
260
|
+
"ValueStream",
|
|
261
|
+
"CourseOfAction",
|
|
262
|
+
# Business layer (EPIC-007)
|
|
263
|
+
"BusinessInternalActiveStructureElement",
|
|
264
|
+
"BusinessInternalBehaviorElement",
|
|
265
|
+
"BusinessPassiveStructureElement",
|
|
266
|
+
"BusinessActor",
|
|
267
|
+
"BusinessRole",
|
|
268
|
+
"BusinessCollaboration",
|
|
269
|
+
"BusinessInterface",
|
|
270
|
+
"BusinessProcess",
|
|
271
|
+
"BusinessFunction",
|
|
272
|
+
"BusinessInteraction",
|
|
273
|
+
"BusinessEvent",
|
|
274
|
+
"BusinessService",
|
|
275
|
+
"BusinessObject",
|
|
276
|
+
"Contract",
|
|
277
|
+
"Representation",
|
|
278
|
+
"Product",
|
|
279
|
+
# Application layer (EPIC-008)
|
|
280
|
+
"ApplicationInternalActiveStructureElement",
|
|
281
|
+
"ApplicationInternalBehaviorElement",
|
|
282
|
+
"ApplicationComponent",
|
|
283
|
+
"ApplicationCollaboration",
|
|
284
|
+
"ApplicationInterface",
|
|
285
|
+
"ApplicationFunction",
|
|
286
|
+
"ApplicationInteraction",
|
|
287
|
+
"ApplicationProcess",
|
|
288
|
+
"ApplicationEvent",
|
|
289
|
+
"ApplicationService",
|
|
290
|
+
"DataObject",
|
|
291
|
+
# Technology layer (EPIC-009)
|
|
292
|
+
"TechnologyInternalActiveStructureElement",
|
|
293
|
+
"TechnologyInternalBehaviorElement",
|
|
294
|
+
"Node",
|
|
295
|
+
"Device",
|
|
296
|
+
"SystemSoftware",
|
|
297
|
+
"TechnologyCollaboration",
|
|
298
|
+
"TechnologyInterface",
|
|
299
|
+
"Path",
|
|
300
|
+
"CommunicationNetwork",
|
|
301
|
+
"TechnologyFunction",
|
|
302
|
+
"TechnologyProcess",
|
|
303
|
+
"TechnologyInteraction",
|
|
304
|
+
"TechnologyEvent",
|
|
305
|
+
"TechnologyService",
|
|
306
|
+
"Artifact",
|
|
307
|
+
# Physical layer (EPIC-010)
|
|
308
|
+
"PhysicalActiveStructureElement",
|
|
309
|
+
"PhysicalPassiveStructureElement",
|
|
310
|
+
"Equipment",
|
|
311
|
+
"Facility",
|
|
312
|
+
"DistributionNetwork",
|
|
313
|
+
"Material",
|
|
314
|
+
# Motivation layer (EPIC-011)
|
|
315
|
+
"Stakeholder",
|
|
316
|
+
"Driver",
|
|
317
|
+
"Assessment",
|
|
318
|
+
"Goal",
|
|
319
|
+
"Outcome",
|
|
320
|
+
"Principle",
|
|
321
|
+
"Requirement",
|
|
322
|
+
"Constraint",
|
|
323
|
+
"Meaning",
|
|
324
|
+
"Value",
|
|
325
|
+
# Implementation & Migration layer (EPIC-012)
|
|
326
|
+
"WorkPackage",
|
|
327
|
+
"Deliverable",
|
|
328
|
+
"ImplementationEvent",
|
|
329
|
+
"Plateau",
|
|
330
|
+
"Gap",
|
|
331
|
+
# Viewpoints & Language Customization (Phase 3)
|
|
332
|
+
"Viewpoint",
|
|
333
|
+
"View",
|
|
334
|
+
"Concern",
|
|
335
|
+
"Profile",
|
|
336
|
+
"PurposeCategory",
|
|
337
|
+
"ContentCategory",
|
|
338
|
+
# Predefined Viewpoint Catalogue (EPIC-022, FEAT-22.1)
|
|
339
|
+
"ViewpointCatalogue",
|
|
340
|
+
"VIEWPOINT_CATALOGUE",
|
|
341
|
+
# Model comparison and diff utilities (EPIC-024, FEAT-24.1)
|
|
342
|
+
"FieldChange",
|
|
343
|
+
"ConceptChange",
|
|
344
|
+
"ModelDiff",
|
|
345
|
+
"diff_models",
|
|
346
|
+
# Impact analysis engine (ADR-043, Issue #9)
|
|
347
|
+
"ImpactedConcept",
|
|
348
|
+
"ImpactResult",
|
|
349
|
+
"analyze_impact",
|
|
350
|
+
]
|
etcion/comparison.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Model comparison and diff utilities (EPIC-024, FEAT-24.1 / FEAT-24.2)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
|
|
8
|
+
from etcion.metamodel.concepts import Concept, Relationship
|
|
9
|
+
from etcion.metamodel.model import Model
|
|
10
|
+
|
|
11
|
+
__all__: list[str] = [
|
|
12
|
+
"FieldChange",
|
|
13
|
+
"ConceptChange",
|
|
14
|
+
"ModelDiff",
|
|
15
|
+
"diff_models",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class FieldChange:
|
|
21
|
+
"""A single field-level change between two concept snapshots."""
|
|
22
|
+
|
|
23
|
+
field: str
|
|
24
|
+
old: Any
|
|
25
|
+
new: Any
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class ConceptChange:
|
|
30
|
+
"""A concept present in both models whose fields differ."""
|
|
31
|
+
|
|
32
|
+
concept_id: str
|
|
33
|
+
concept_type: str
|
|
34
|
+
changes: dict[str, FieldChange]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class ModelDiff:
|
|
39
|
+
"""Immutable result of comparing two models."""
|
|
40
|
+
|
|
41
|
+
added: tuple[Concept, ...]
|
|
42
|
+
removed: tuple[Concept, ...]
|
|
43
|
+
modified: tuple[ConceptChange, ...]
|
|
44
|
+
|
|
45
|
+
def to_dict(self) -> dict[str, Any]:
|
|
46
|
+
"""Return a JSON-serializable dict representation."""
|
|
47
|
+
|
|
48
|
+
def _concept_entry(c: Concept) -> dict[str, Any]:
|
|
49
|
+
return {
|
|
50
|
+
"id": c.id,
|
|
51
|
+
"type": type(c).__name__,
|
|
52
|
+
"name": getattr(c, "name", None),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def _change_entry(cc: ConceptChange) -> dict[str, Any]:
|
|
56
|
+
return {
|
|
57
|
+
"concept_id": cc.concept_id,
|
|
58
|
+
"concept_type": cc.concept_type,
|
|
59
|
+
"changes": {k: {"old": fc.old, "new": fc.new} for k, fc in cc.changes.items()},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
"added": [_concept_entry(c) for c in self.added],
|
|
64
|
+
"removed": [_concept_entry(c) for c in self.removed],
|
|
65
|
+
"modified": [_change_entry(cc) for cc in self.modified],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def summary(self) -> str:
|
|
69
|
+
"""Return a human-readable one-line summary."""
|
|
70
|
+
return (
|
|
71
|
+
f"ModelDiff: {len(self.added)} added, "
|
|
72
|
+
f"{len(self.removed)} removed, "
|
|
73
|
+
f"{len(self.modified)} modified"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def __str__(self) -> str:
|
|
77
|
+
return self.summary()
|
|
78
|
+
|
|
79
|
+
def __bool__(self) -> bool:
|
|
80
|
+
return bool(self.added or self.removed or self.modified)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _build_key(concept: Concept, match_by: Literal["id", "type_name"]) -> str | tuple[str, str]:
|
|
84
|
+
"""Return the lookup key for a concept."""
|
|
85
|
+
if match_by == "id":
|
|
86
|
+
return concept.id
|
|
87
|
+
# match_by == "type_name"
|
|
88
|
+
name = getattr(concept, "name", "") or ""
|
|
89
|
+
return (type(concept).__name__, name)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _normalize_dump(concept: Concept) -> dict[str, Any]:
|
|
93
|
+
"""Return a model_dump() dict with id removed and source/target normalized."""
|
|
94
|
+
d = concept.model_dump()
|
|
95
|
+
d.pop("id", None)
|
|
96
|
+
if isinstance(concept, Relationship):
|
|
97
|
+
d["source"] = concept.source.id
|
|
98
|
+
d["target"] = concept.target.id
|
|
99
|
+
return d
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _diff_fields(dump_a: dict[str, Any], dump_b: dict[str, Any]) -> dict[str, FieldChange]:
|
|
103
|
+
"""Compare two normalized dumps, returning changed fields."""
|
|
104
|
+
changes: dict[str, FieldChange] = {}
|
|
105
|
+
all_keys = dump_a.keys() | dump_b.keys()
|
|
106
|
+
for key in all_keys:
|
|
107
|
+
old = dump_a.get(key)
|
|
108
|
+
new = dump_b.get(key)
|
|
109
|
+
if old != new:
|
|
110
|
+
changes[key] = FieldChange(field=key, old=old, new=new)
|
|
111
|
+
return changes
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def diff_models(
|
|
115
|
+
model_a: Model,
|
|
116
|
+
model_b: Model,
|
|
117
|
+
*,
|
|
118
|
+
match_by: Literal["id", "type_name"] = "id",
|
|
119
|
+
) -> ModelDiff:
|
|
120
|
+
"""Compare two models and return a structured diff.
|
|
121
|
+
|
|
122
|
+
:param model_a: Baseline ("before") model.
|
|
123
|
+
:param model_b: Revised ("after") model.
|
|
124
|
+
:param match_by: ``"id"`` matches concepts by id; ``"type_name"`` matches
|
|
125
|
+
by ``(type_name, name)`` tuple.
|
|
126
|
+
:returns: A frozen :class:`ModelDiff`.
|
|
127
|
+
"""
|
|
128
|
+
lookup_a = {_build_key(c, match_by): c for c in model_a.concepts}
|
|
129
|
+
lookup_b = {_build_key(c, match_by): c for c in model_b.concepts}
|
|
130
|
+
|
|
131
|
+
keys_a = set(lookup_a)
|
|
132
|
+
keys_b = set(lookup_b)
|
|
133
|
+
|
|
134
|
+
added = tuple(lookup_b[k] for k in keys_b - keys_a)
|
|
135
|
+
removed = tuple(lookup_a[k] for k in keys_a - keys_b)
|
|
136
|
+
|
|
137
|
+
modified: list[ConceptChange] = []
|
|
138
|
+
for key in keys_a & keys_b:
|
|
139
|
+
ca, cb = lookup_a[key], lookup_b[key]
|
|
140
|
+
dump_a = _normalize_dump(ca)
|
|
141
|
+
dump_b = _normalize_dump(cb)
|
|
142
|
+
changes = _diff_fields(dump_a, dump_b)
|
|
143
|
+
if changes:
|
|
144
|
+
modified.append(
|
|
145
|
+
ConceptChange(
|
|
146
|
+
concept_id=ca.id,
|
|
147
|
+
concept_type=type(ca).__name__,
|
|
148
|
+
changes=changes,
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return ModelDiff(added=added, removed=removed, modified=tuple(modified))
|
etcion/conformance.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Conformance profile for the etcion ArchiMate 3.2 implementation.
|
|
2
|
+
|
|
3
|
+
This module declares which features of the ArchiMate 3.2 specification the
|
|
4
|
+
library commits to implementing. The :class:`ConformanceProfile` dataclass
|
|
5
|
+
carries boolean flags grouped by the specification's three compliance levels:
|
|
6
|
+
|
|
7
|
+
* **shall** -- mandatory features for a conformant implementation.
|
|
8
|
+
* **should** -- recommended features the library plans to support.
|
|
9
|
+
* **may** -- optional features explicitly out of scope.
|
|
10
|
+
|
|
11
|
+
The module-level :data:`CONFORMANCE` singleton is the canonical instance.
|
|
12
|
+
It is re-exported from ``etcion.__init__`` so consumers can inspect the
|
|
13
|
+
library's conformance declaration via ``etcion.CONFORMANCE``.
|
|
14
|
+
|
|
15
|
+
.. note::
|
|
16
|
+
This module does **not** import ``SPEC_VERSION`` from ``etcion.__init__``
|
|
17
|
+
to avoid a circular import. The ``spec_version`` field uses the literal
|
|
18
|
+
``"3.2"`` as its default. The conformance test suite asserts that this
|
|
19
|
+
value equals ``etcion.SPEC_VERSION``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
|
|
26
|
+
__all__: list[str] = [
|
|
27
|
+
"ConformanceProfile",
|
|
28
|
+
"CONFORMANCE",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ConformanceProfile:
|
|
34
|
+
"""Machine-readable declaration of ArchiMate 3.2 conformance scope.
|
|
35
|
+
|
|
36
|
+
Every field defaults to the library's declared intent. ``shall``-level
|
|
37
|
+
and ``should``-level fields default to ``True`` (the library commits to
|
|
38
|
+
implementing them). The sole ``may``-level field defaults to ``False``
|
|
39
|
+
(explicitly out of scope).
|
|
40
|
+
|
|
41
|
+
The dataclass is frozen to prevent accidental mutation of the library's
|
|
42
|
+
conformance contract.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
# --- spec version ---
|
|
46
|
+
|
|
47
|
+
# The ArchiMate specification version this profile targets.
|
|
48
|
+
spec_version: str = "3.2"
|
|
49
|
+
|
|
50
|
+
# --- shall-level (mandatory for conformance) ---
|
|
51
|
+
|
|
52
|
+
# Full classification framework: layers (7) and aspects (5).
|
|
53
|
+
# Reference: ArchiMate 3.2 Specification, Sections 3.4--3.5.
|
|
54
|
+
language_structure: bool = True
|
|
55
|
+
|
|
56
|
+
# Abstract element hierarchy: Concept, Element, Relationship,
|
|
57
|
+
# RelationshipConnector ABCs.
|
|
58
|
+
# Reference: ArchiMate 3.2 Specification, Section 3.1.
|
|
59
|
+
generic_metamodel: bool = True
|
|
60
|
+
|
|
61
|
+
# Strategy layer element types: Resource, Capability, ValueStream,
|
|
62
|
+
# CourseOfAction.
|
|
63
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 7.
|
|
64
|
+
strategy_elements: bool = True
|
|
65
|
+
|
|
66
|
+
# Motivation layer element types: Stakeholder, Driver, Assessment, Goal,
|
|
67
|
+
# Outcome, Principle, Requirement, Constraint, Meaning, Value.
|
|
68
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 6.
|
|
69
|
+
motivation_elements: bool = True
|
|
70
|
+
|
|
71
|
+
# Business layer element types: BusinessActor, BusinessRole,
|
|
72
|
+
# BusinessCollaboration, BusinessInterface, BusinessProcess,
|
|
73
|
+
# BusinessFunction, BusinessInteraction, BusinessEvent,
|
|
74
|
+
# BusinessService, BusinessObject, Contract, Representation, Product.
|
|
75
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 8.
|
|
76
|
+
business_elements: bool = True
|
|
77
|
+
|
|
78
|
+
# Application layer element types: ApplicationComponent,
|
|
79
|
+
# ApplicationCollaboration, ApplicationInterface, ApplicationFunction,
|
|
80
|
+
# ApplicationInteraction, ApplicationProcess, ApplicationEvent,
|
|
81
|
+
# ApplicationService, DataObject.
|
|
82
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 9.
|
|
83
|
+
application_elements: bool = True
|
|
84
|
+
|
|
85
|
+
# Technology layer element types: Node, Device, SystemSoftware,
|
|
86
|
+
# TechnologyCollaboration, TechnologyInterface, Path,
|
|
87
|
+
# CommunicationNetwork, TechnologyFunction, TechnologyProcess,
|
|
88
|
+
# TechnologyInteraction, TechnologyEvent, TechnologyService, Artifact.
|
|
89
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 10.
|
|
90
|
+
technology_elements: bool = True
|
|
91
|
+
|
|
92
|
+
# Physical layer element types: Equipment, Facility,
|
|
93
|
+
# DistributionNetwork, Material.
|
|
94
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 11.
|
|
95
|
+
physical_elements: bool = True
|
|
96
|
+
|
|
97
|
+
# Implementation & Migration layer element types: WorkPackage,
|
|
98
|
+
# Deliverable, ImplementationEvent, Plateau, Gap.
|
|
99
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 13.
|
|
100
|
+
implementation_migration_elements: bool = True
|
|
101
|
+
|
|
102
|
+
# Support for relationships that cross layer boundaries, including all
|
|
103
|
+
# 11 concrete relationship types and the Junction connector.
|
|
104
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 5.
|
|
105
|
+
cross_layer_relationships: bool = True
|
|
106
|
+
|
|
107
|
+
# Full Appendix B relationship permission matrix encoding and the
|
|
108
|
+
# ``is_permitted`` lookup function.
|
|
109
|
+
# Reference: ArchiMate 3.2 Specification, Appendix B.
|
|
110
|
+
relationship_permission_table: bool = True
|
|
111
|
+
|
|
112
|
+
# Notation metadata: corner shapes, default layer colors, and badge
|
|
113
|
+
# letters for each concrete element type.
|
|
114
|
+
# Reference: ArchiMate 3.2 Specification, Appendix A.
|
|
115
|
+
iconography_metadata: bool = True
|
|
116
|
+
|
|
117
|
+
# --- should-level (recommended, not mandatory) ---
|
|
118
|
+
|
|
119
|
+
# Defining and applying viewpoints to filter model views.
|
|
120
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 14.
|
|
121
|
+
viewpoint_mechanism: bool = True
|
|
122
|
+
|
|
123
|
+
# Profile and extension mechanisms for customizing the language.
|
|
124
|
+
# Reference: ArchiMate 3.2 Specification, Chapter 15.
|
|
125
|
+
language_customization: bool = True
|
|
126
|
+
|
|
127
|
+
# --- may-level (optional, out of scope) ---
|
|
128
|
+
|
|
129
|
+
# Appendix C example viewpoints (Basic, Organization, etc.).
|
|
130
|
+
# This feature does not affect conformance checks.
|
|
131
|
+
# Reference: ArchiMate 3.2 Specification, Appendix C.
|
|
132
|
+
example_viewpoints: bool = False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
CONFORMANCE: ConformanceProfile = ConformanceProfile()
|
|
136
|
+
"""The canonical conformance profile singleton for the etcion library."""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""etcion.derivation -- Relationship derivation engine.
|
|
2
|
+
|
|
3
|
+
This sub-package implements the ArchiMate 3.2 relationship derivation rules,
|
|
4
|
+
computing implied (derived) relationships from chains of direct relationships
|
|
5
|
+
in a model.
|
|
6
|
+
|
|
7
|
+
Modules
|
|
8
|
+
-------
|
|
9
|
+
engine
|
|
10
|
+
DerivationEngine class implementing the derive() method and chain
|
|
11
|
+
traversal logic. (TODO: EPIC-005, FEAT-05.10)
|
|
12
|
+
|
|
13
|
+
Import policy
|
|
14
|
+
-------------
|
|
15
|
+
This package MAY import from etcion.metamodel, etcion.enums, and
|
|
16
|
+
etcion.validation. It is the top of the internal dependency graph.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# No public symbols yet -- DerivationEngine is added in EPIC-005.
|