Skip to content

Commit 590478f

Browse files
committed
<Enhancement>[Sequence Diagrams]: <Actors can Link>
[ * I now recognize the update XML * Bump mypy dependency * Bump our version * tell mypy I know what I am doing ] [#86]
1 parent 9477456 commit 590478f

File tree

6 files changed

+203
-16
lines changed

6 files changed

+203
-16
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencies = [
3030

3131
[project.optional-dependencies]
3232
test = [
33-
'mypy==1.14.0',
33+
'mypy==1.15.0',
3434
'mypy-extensions==1.0.0',
3535
'types-Deprecated==1.2.9.20240311',
3636
'html-testRunner~=1.2.1',

src/untanglepyut/Types.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,6 @@ class Document:
135135

136136
Documents = NewType('Documents', dict[DocumentTitle, Document])
137137

138-
# @dataclass
139-
# class SDDocument(Document):
140-
# oglSDInstances: OglSDInstances = field(default_factory=createOglSDInstances)
141-
# oglSDMessages: OGLSDMessages = field(default_factory=createOGLSDMessages)
142-
143138
LinkableOglObject = Union[OglClass, OglNote, OglActor, OglUseCase]
144139

145140
LinkableOglObjects = NewType('LinkableOglObjects', Dict[int, LinkableOglObject])

src/untanglepyut/UnTangleSequenceDiagram.py

Lines changed: 157 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11

22
from typing import List
3+
from typing import Tuple
4+
from typing import cast
35

46
from logging import Logger
57
from logging import getLogger
68

9+
from ogl.OglActor import OglActor
10+
from pyutmodelv2.PyutLink import PyutLink
711
from untangle import Element
812

913
from pyutmodelv2.PyutSDInstance import PyutSDInstance
@@ -12,18 +16,34 @@
1216
from ogl.sd.OglSDInstance import OglSDInstance
1317
from ogl.sd.OglSDMessage import OglSDMessage
1418

19+
from ogl.OglAssociation import OglAssociation
20+
1521
from untanglepyut import XmlConstants
22+
from untanglepyut.Types import Elements
23+
1624
from untanglepyut.Types import GraphicInformation
25+
from untanglepyut.Types import GraphicLinkAttributes
26+
from untanglepyut.Types import LinkableOglObject
27+
from untanglepyut.Types import LinkableOglObjects
1728
from untanglepyut.Types import OglSDInstances
1829
from untanglepyut.Types import OglSDMessages
19-
20-
from untanglepyut.BaseUnTangle import BaseUnTangle
30+
from untanglepyut.Types import UntangledOglActors
31+
from untanglepyut.Types import UntangledOglLinks
32+
from untanglepyut.Types import createLinkableOglObjects
2133
from untanglepyut.Types import createOglSDInstances
2234
from untanglepyut.Types import createOglSDMessages
35+
from untanglepyut.Types import createUntangledOglActors
36+
from untanglepyut.Types import createUntangledOglLinks
37+
from untanglepyut.UnTangleOglLinks import UnTangleOglLinks
38+
2339
from untanglepyut.UnTanglePyut import ConvolutedPyutSDMessageInformation
2440
from untanglepyut.UnTanglePyut import UnTanglePyut
41+
from untanglepyut.UnTangleUseCaseDiagram import UnTangleUseCaseDiagram
42+
2543
from untanglepyut.XmlVersion import XmlVersion
2644

45+
from untanglepyut.BaseUnTangle import BaseUnTangle
46+
2747

2848
class UnTangleSequenceDiagram(BaseUnTangle):
2949

@@ -33,17 +53,33 @@ def __init__(self, xmlVersion: XmlVersion):
3353

3454
self.logger: Logger = getLogger(__name__)
3555

36-
self._oglSDInstances: OglSDInstances = createOglSDInstances()
37-
self._oglSDMessages: OglSDMessages = createOglSDMessages()
56+
self._oglSDInstances: OglSDInstances = createOglSDInstances()
57+
self._oglSDMessages: OglSDMessages = createOglSDMessages()
58+
self._oglActors: UntangledOglActors = createUntangledOglActors()
59+
self._oglLinks: UntangledOglLinks = createUntangledOglLinks()
3860

3961
self._untanglePyut: UnTanglePyut = UnTanglePyut(xmlVersion=xmlVersion)
62+
#
63+
# Need some help
64+
#
65+
self._untangleUseCaseStuff: UnTangleUseCaseDiagram = UnTangleUseCaseDiagram(xmlVersion=xmlVersion)
66+
self._untangleLinks: UnTangleOglLinks = UnTangleOglLinks(xmlVersion=xmlVersion)
4067

4168
if xmlVersion == XmlVersion.V10:
42-
self._elementInstance: str = XmlConstants.V10_ELEMENT_INSTANCE
43-
self._elementMessage: str = XmlConstants.V10_ELEMENT_MESSAGE
69+
self._elementInstance: str = XmlConstants.V10_ELEMENT_INSTANCE
70+
self._elementMessage: str = XmlConstants.V10_ELEMENT_MESSAGE
71+
self._elementOglLink: str = XmlConstants.V10_ELEMENT_OGL_LINK
72+
self._elementLink: str = XmlConstants.V10_ELEMENT_LINK
73+
self._attrSourceId: str = XmlConstants.V10_ATTR_SOURCE_ID
74+
self._attrDestinationId: str = XmlConstants.V10_ATTR_DESTINATION_ID
75+
4476
else:
45-
self._elementInstance = XmlConstants.V11_ELEMENT_INSTANCE
46-
self._elementMessage = XmlConstants.V11_ELEMENT_MESSAGE
77+
self._elementInstance = XmlConstants.V11_ELEMENT_INSTANCE
78+
self._elementMessage = XmlConstants.V11_ELEMENT_MESSAGE
79+
self._elementOglLink = XmlConstants.V11_ELEMENT_OGL_LINK
80+
self._elementLink = XmlConstants.V11_ELEMENT_LINK
81+
self._attrSourceId = XmlConstants.V11_ATTR_SOURCE_ID
82+
self._attrDestinationId = XmlConstants.V11_ATTR_DESTINATION_ID
4783

4884
def unTangle(self, pyutDocument: Element):
4985
"""
@@ -54,6 +90,14 @@ def unTangle(self, pyutDocument: Element):
5490
self._oglSDInstances = self._untangleSDInstances(pyutDocument=pyutDocument)
5591
self._oglSDMessages = self._untangleSDMessages(pyutDocument=pyutDocument)
5692

93+
self._untangleUseCaseStuff.unTangle(pyutDocument=pyutDocument)
94+
95+
self._oglActors = self._untangleUseCaseStuff.oglActors
96+
97+
linkableOglObjects: LinkableOglObjects = self._buildLinkableObjects()
98+
# self._oglLinks = self._untangleLinks.unTangle(pyutDocument=pyutDocument, linkableOglObjects=linkableOglObjects)
99+
self._oglLinks = self._connectActorsToSDInstances(pyutDocument=pyutDocument, linkableOglObjects=linkableOglObjects)
100+
57101
@property
58102
def oglSDInstances(self) -> OglSDInstances:
59103
return self._oglSDInstances
@@ -62,10 +106,18 @@ def oglSDInstances(self) -> OglSDInstances:
62106
def oglSDMessages(self) -> OglSDMessages:
63107
return self._oglSDMessages
64108

109+
@property
110+
def oglActors(self) -> UntangledOglActors:
111+
return self._oglActors
112+
113+
@property
114+
def oglLinks(self) -> UntangledOglLinks:
115+
return self._oglLinks
116+
65117
def _untangleSDInstances(self, pyutDocument: Element) -> OglSDInstances:
66118

67119
oglSDInstances: OglSDInstances = createOglSDInstances()
68-
graphicSDInstances: List[Element] = pyutDocument.get_elements(self._elementInstance)
120+
graphicSDInstances: List[Element] = pyutDocument.get_elements(self._elementInstance)
69121

70122
for graphicSDInstance in graphicSDInstances:
71123
self.logger.debug(f'{graphicSDInstance=}')
@@ -103,3 +155,99 @@ def _untangleSDMessages(self, pyutDocument: Element) -> OglSDMessages:
103155
oglSDMessages[pyutSDMessage.id] = oglSDMessage
104156

105157
return oglSDMessages
158+
159+
def _connectActorsToSDInstances(self, pyutDocument: Element, linkableOglObjects: LinkableOglObjects) -> UntangledOglLinks:
160+
161+
oglLinks: UntangledOglLinks = createUntangledOglLinks()
162+
163+
# US Agency for International Development must go away
164+
165+
graphicLinks: Elements = cast(Elements, pyutDocument.get_elements(self._elementOglLink))
166+
for graphicLink in graphicLinks:
167+
oglAssociation: OglAssociation = self._createActorLink(graphicLink=graphicLink, linkableOglObjects=linkableOglObjects)
168+
oglLinks.append(oglAssociation)
169+
170+
return oglLinks
171+
172+
def _createActorLink(self, graphicLink: Element, linkableOglObjects: LinkableOglObjects) -> OglAssociation:
173+
"""
174+
175+
Args:
176+
graphicLink:
177+
linkableOglObjects:
178+
179+
Returns:
180+
"""
181+
182+
links: Elements = cast(Elements, graphicLink.get_elements(self._elementLink))
183+
assert len(links) == 1, 'Should only ever be one'
184+
185+
singleLink: Element = links[0]
186+
sourceId, dstId = self._linkIDs(singleLink=singleLink)
187+
try:
188+
srcShape: LinkableOglObject = linkableOglObjects[sourceId]
189+
dstShape: OglSDInstance = linkableOglObjects[dstId] # type: ignore
190+
except KeyError as ke:
191+
self.logger.error(f'{linkableOglObjects=}')
192+
self.logger.error(f'Developer Error -- {singleLink=}')
193+
self.logger.error(f'Developer Error -- {sourceId=} {dstId=} KeyError index: {ke}')
194+
return cast(OglAssociation, None)
195+
196+
assert isinstance(srcShape, OglActor), 'Developer Error'
197+
assert isinstance(dstShape, OglSDInstance), 'Developer Error'
198+
199+
pyutLink: PyutLink = self._untanglePyut.linkToPyutLink(singleLink, source=srcShape.pyutObject, destination=dstShape.pyutSDInstance)
200+
gla: GraphicLinkAttributes = GraphicLinkAttributes.fromGraphicLink(xmlVersion=self._xmlVersion, graphicLink=graphicLink)
201+
202+
self.logger.debug(f'graphicLink= {gla.srcX=} {gla.srcY=} {gla.dstX=} {gla.dstY=} {gla.spline=}')
203+
204+
oglAssociation: OglAssociation = OglAssociation(srcShape=srcShape,
205+
pyutLink=pyutLink,
206+
dstShape=dstShape,
207+
srcPos=(gla.srcX, gla.srcY),
208+
dstPos=(gla.dstX, gla.dstY)
209+
)
210+
# put the anchors at the right position
211+
srcAnchor = oglAssociation.sourceAnchor
212+
dstAnchor = oglAssociation.destinationAnchor
213+
srcAnchor.SetPosition(gla.srcX, gla.srcY)
214+
dstAnchor.SetPosition(gla.dstX, gla.dstY)
215+
216+
srcModel = srcAnchor.model
217+
srcModel.SetPosition(x=gla.srcX, y=gla.srcY)
218+
dstModel = dstAnchor.model
219+
dstModel.SetPosition(x=gla.dstX, y=gla.dstY)
220+
#
221+
# Do not create association labels
222+
#
223+
return oglAssociation
224+
225+
def _linkIDs(self, singleLink: Element) -> Tuple[int, int]:
226+
"""
227+
Extracts the source and destination IDs
228+
Args:
229+
singleLink:
230+
231+
Returns: A tuple of sourceId,destinationId
232+
"""
233+
234+
sourceId: int = int(singleLink[self._attrSourceId])
235+
dstId: int = int(singleLink[self._attrDestinationId])
236+
237+
return sourceId, dstId
238+
239+
def _buildLinkableObjects(self) -> LinkableOglObjects:
240+
"""
241+
242+
Returns: Linkable Objects Dictionary
243+
"""
244+
245+
linkableOglObjects: LinkableOglObjects = createLinkableOglObjects()
246+
247+
for oglActor in self.oglActors:
248+
linkableOglObjects[oglActor.pyutObject.id] = oglActor
249+
250+
for pyutID, oglSDInstance in self.oglSDInstances.items():
251+
linkableOglObjects[pyutID] = oglSDInstance # type: ignore
252+
253+
return linkableOglObjects

src/untanglepyut/UnTangler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ def untangleXml(self, xmlString: str, fqFileName: str):
105105
untangleSequenceDiagram.unTangle(pyutDocument=pyutDocument)
106106
document.oglSDInstances = untangleSequenceDiagram.oglSDInstances
107107
document.oglSDMessages = untangleSequenceDiagram.oglSDMessages
108+
document.oglActors = untangleSequenceDiagram.oglActors
109+
document.oglLinks = untangleSequenceDiagram.oglLinks
108110

109111
elif document.documentType == 'USECASE_DIAGRAM':
110112

src/untanglepyut/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__: str = '2.6.3'
1+
__version__: str = '2.7.0'

tests/untanglepyut/v11/TestUnTangleSequenceDiagram.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from untanglepyut.Types import OglSDInstances
1414

1515
from untanglepyut.Types import OglSDMessages
16+
from untanglepyut.Types import UntangledOglActors
17+
from untanglepyut.Types import UntangledOglLinks
1618

1719
from untanglepyut.XmlVersion import XmlVersion
1820

@@ -42,6 +44,31 @@
4244
</PyutDocument>
4345
"""
4446

47+
V11_SEQUENCE_DIAGRAM_WITH_ACTOR_LINKS: str = """
48+
49+
<PyutDocument type="SEQUENCE_DIAGRAM" title="Seq Diagram" scrollPositionX="0" scrollPositionY="0" pixelsPerUnitX="20" pixelsPerUnitY="20">
50+
<OglLink sourceAnchorX="155" sourceAnchorY="225" destinationAnchorX="360" destinationAnchorY="250" spline="False">
51+
<LabelCenter x="0" y="0" />
52+
<LabelSource x="-102" y="-25" />
53+
<LabelDestination x="88" y="19" />
54+
<PyutLink name="" type="ASSOCIATION" cardinalitySource="" cardinalityDestination="" bidirectional="False" sourceId="3" destinationId="1" />
55+
</OglLink>
56+
<OglActor width="80" height="100" x="75" y="175">
57+
<PyutActor id="3" name="ActorName" fileName="" />
58+
</OglActor>
59+
<OglSDInstance width="100" height="400" x="360" y="50">
60+
<PyutSDInstance id="1" instanceName="Instance1" lifeLineLength="200" />
61+
</OglSDInstance>
62+
<OglSDInstance width="100" height="400" x="781" y="50">
63+
<PyutSDInstance id="2" instanceName="Instance2" lifeLineLength="200" />
64+
</OglSDInstance>
65+
<OglSDMessage>
66+
<PyutSDMessage id="3" message="testMessage()" sourceTime="149" destinationTime="150" sourceId="1" destinationId="2" />
67+
</OglSDMessage>
68+
</PyutDocument>
69+
70+
"""
71+
4572

4673
class TestUnTangleSequenceDiagram(ProjectTestBase):
4774
"""
@@ -59,6 +86,21 @@ def setUp(self):
5986
def tearDown(self):
6087
super().tearDown()
6188

89+
def testSequenceDiagramWithActorLink(self):
90+
91+
root: Element = parse(V11_SEQUENCE_DIAGRAM_WITH_ACTOR_LINKS)
92+
sequenceDiagramDocument: Element = root.PyutDocument
93+
94+
unTangleSequenceDiagram: UnTangleSequenceDiagram = UnTangleSequenceDiagram(xmlVersion=XmlVersion.V11)
95+
96+
unTangleSequenceDiagram.unTangle(pyutDocument=sequenceDiagramDocument)
97+
98+
oglActors: UntangledOglActors = unTangleSequenceDiagram.oglActors
99+
oglLinks: UntangledOglLinks = unTangleSequenceDiagram.oglLinks
100+
101+
self.assertEqual(1, len(oglActors), 'Oops not enough Uml Actors')
102+
self.assertEqual(1, len(oglLinks), 'Oops not enough association links')
103+
62104
def testOglSdMessage(self):
63105
unTangleSequenceDiagram: UnTangleSequenceDiagram = self._untangleSequenceDiagramDocument()
64106

0 commit comments

Comments
 (0)