Skip to content
This repository was archived by the owner on Jan 21, 2023. It is now read-only.

Commit 2a0c71e

Browse files
yt-msMidnighter
authored andcommitted
refactor: make view.find_element_view and find_relationship_view more generally useful
1 parent 2c79d9d commit 2a0c71e

File tree

2 files changed

+121
-29
lines changed

2 files changed

+121
-29
lines changed

src/structurizr/view/view.py

+45-29
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def relationship_views(self) -> Iterable[RelationshipView]:
130130
"""Return the relationship views contained by this view."""
131131
return set(self._relationship_views)
132132

133-
def _add_element(self, element: Element, add_relationships: bool) -> None:
133+
def _add_element(self, element: Element, add_relationships: bool) -> ElementView:
134134
"""
135135
Add the given element to this view.
136136
@@ -145,10 +145,13 @@ def _add_element(self, element: Element, add_relationships: bool) -> None:
145145
f"The element {element} does not exist in the model associated with "
146146
f"this view."
147147
)
148-
if element not in [view.element for view in self.element_views]:
149-
self.element_views.add(ElementView(element=element))
148+
view = self.find_element_view(element=element)
149+
if view is None:
150+
view = ElementView(element=element)
151+
self.element_views.add(view)
150152
if add_relationships:
151153
self._add_relationships(element)
154+
return view
152155

153156
def _remove_element(self, element: Element) -> None:
154157
"""
@@ -192,15 +195,8 @@ def _add_relationship(
192195
if self.is_element_in_view(relationship.source) and self.is_element_in_view(
193196
relationship.destination
194197
):
195-
view = next(
196-
(
197-
rv
198-
for rv in self._relationship_views
199-
if rv.relationship is relationship
200-
and rv.description == description
201-
and rv.response == response
202-
),
203-
None,
198+
view = self.find_relationship_view(
199+
relationship=relationship, description=description, response=response
204200
)
205201
if not view:
206202
view = RelationshipView(
@@ -211,7 +207,6 @@ def _add_relationship(
211207
)
212208
self._relationship_views.add(view)
213209
return view
214-
return None
215210

216211
def _add_relationships(self, element: Element) -> None:
217212
"""
@@ -241,15 +236,17 @@ def copy_layout_information_from(self, source: "View") -> None:
241236
self.paper_size = source.paper_size
242237

243238
for source_element_view in source.element_views:
244-
destination_element_view = self.find_element_view(source_element_view)
239+
destination_element_view = self.find_element_view(
240+
element=source_element_view.element
241+
)
245242
if destination_element_view:
246243
destination_element_view.copy_layout_information_from(
247244
source_element_view
248245
)
249246

250247
for source_relationship_view in source.relationship_views:
251248
destintion_relationship_view = self.find_relationship_view(
252-
source_relationship_view
249+
relationship=source_relationship_view.relationship
253250
)
254251
if destintion_relationship_view:
255252
destintion_relationship_view.copy_layout_information_from(
@@ -258,28 +255,47 @@ def copy_layout_information_from(self, source: "View") -> None:
258255

259256
def is_element_in_view(self, element: Element) -> bool:
260257
"""Return True if the given element is in this view."""
261-
return any([e.element.id == element.id for e in self.element_views])
258+
return self.find_element_view(element=element) is not None
262259

263260
def find_element_view(
264-
self, source_element_view: ElementView
261+
self,
262+
*,
263+
element: Optional[Element] = None,
265264
) -> Optional[ElementView]:
266-
"""Find a child element view corresponding to the given source view."""
267-
for element_view in self.element_views:
268-
if element_view.element.id == source_element_view.element.id:
269-
return element_view
270-
return None
265+
"""Find a child element view matching a given element."""
266+
return next(
267+
(view for view in self.element_views if view.element.id == element.id), None
268+
)
271269

272270
def find_relationship_view(
273-
self, source_relationship_view: RelationshipView
271+
self,
272+
*,
273+
relationship: Optional[Relationship] = None,
274+
description: Optional[str] = None,
275+
response: Optional[bool] = None,
274276
) -> Optional[RelationshipView]:
275-
"""Find a child element view corresponding to the given relationship view."""
276-
for relationship_view in self._relationship_views:
277+
"""
278+
Find a child relationship view matching the supplied non-None arguments.
279+
280+
Args:
281+
relationship: find a child view with matching relationship ID
282+
description: find a child view with matching view description. Note that
283+
the view description is not always the same as that of the
284+
relationship
285+
response: find a child view with matching response indicator.
286+
"""
287+
for view in self._relationship_views:
288+
rel = view.relationship
277289
if (
278-
relationship_view.relationship.id
279-
== source_relationship_view.relationship.id
290+
(relationship is None or rel.id == relationship.id)
291+
and (
292+
description is None
293+
or view.description == description
294+
or (view.description is None and rel.description == description)
295+
)
296+
and (response is None or view.response == response)
280297
):
281-
return relationship_view
282-
return None
298+
return view
283299

284300
def check_parent_and_children_not_in_view(self, element: Element) -> None:
285301
"""Ensure that an element can't be added if parent or children are in view."""

tests/unit/view/test_view.py

+76
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"""Ensure the expected behaviour of View."""
1717

1818
from structurizr.model import Model
19+
from structurizr.view.paper_size import PaperSize
1920
from structurizr.view.view import View, ViewIO
2021

2122

@@ -25,6 +26,56 @@ class DerivedView(View):
2526
pass
2627

2728

29+
def test_find_element_view():
30+
"""Test behaviour of find_element_view."""
31+
model = Model()
32+
sys1 = model.add_software_system(name="System 1")
33+
sys2 = model.add_software_system(name="System 2")
34+
35+
view = DerivedView(software_system=sys1, description="")
36+
view._add_element(sys1, False)
37+
38+
assert view.find_element_view(element=sys1).element is sys1
39+
assert view.find_element_view(element=sys2) is None
40+
41+
42+
def test_find_relationship_view():
43+
"""Test behaviour of find_element_view."""
44+
model = Model()
45+
sys1 = model.add_software_system(name="System 1")
46+
sys2 = model.add_software_system(name="System 2")
47+
rel1 = sys1.uses(sys2, "Uses")
48+
rel2 = sys2.uses(sys1, "Also uses")
49+
rel3 = sys2.uses(sys1, "Returns")
50+
51+
view = DerivedView(software_system=sys1, description="")
52+
view._add_element(sys1, False)
53+
view._add_element(sys2, False)
54+
view._add_relationship(rel1).description = "Override"
55+
view._add_relationship(rel3).response = True
56+
57+
assert view.find_relationship_view(relationship=rel1).relationship is rel1
58+
assert view.find_relationship_view(relationship=rel2) is None
59+
assert view.find_relationship_view(description="Override").relationship is rel1
60+
assert view.find_relationship_view(description="Uses") is None
61+
assert view.find_relationship_view(description="Returns").relationship is rel3
62+
assert view.find_relationship_view(response=True).relationship is rel3
63+
assert view.find_relationship_view(response=False).relationship is rel1
64+
65+
66+
def test_is_element_in_view():
67+
"""Test check for an element being in the view."""
68+
model = Model()
69+
sys1 = model.add_software_system(name="System 1")
70+
sys2 = model.add_software_system(name="System 2")
71+
72+
view = DerivedView(software_system=sys1, description="")
73+
view._add_element(sys1, False)
74+
75+
assert view.is_element_in_view(sys1)
76+
assert not view.is_element_in_view(sys2)
77+
78+
2879
def test_add_relationship_doesnt_duplicate():
2980
"""Test that adding a relationships twice doesn't duplicate it."""
3081
model = Model()
@@ -97,3 +148,28 @@ def test_missing_json_description_allowed():
97148
"""
98149
io = ViewIO.parse_raw(json)
99150
assert io is not None
151+
152+
153+
def test_copy_layout():
154+
"""Ensure that layout is copied over, including sub-views."""
155+
model = Model()
156+
sys1 = model.add_software_system(name="System 1")
157+
sys2 = model.add_software_system(name="System 2")
158+
rel1 = sys1.uses(sys2)
159+
160+
view1 = DerivedView(software_system=sys1, description="")
161+
view1._add_element(sys1, False).paper_size = PaperSize.A1_Portrait
162+
view1._add_element(sys2, False).paper_size = PaperSize.A2_Portrait
163+
view1._add_relationship(rel1).paper_size = PaperSize.A3_Portrait
164+
view1.paper_size = PaperSize.A4_Portrait
165+
166+
view2 = DerivedView(software_system=sys1, description="")
167+
view2._add_element(sys1, False).paper_size = PaperSize.A1_Portrait
168+
view2._add_element(sys2, False).paper_size = PaperSize.A2_Portrait
169+
view2._add_relationship(rel1).paper_size = PaperSize.A3_Portrait
170+
view2.copy_layout_information_from(view1)
171+
172+
assert view2.paper_size == PaperSize.A4_Portrait
173+
assert view2.find_element_view(element=sys1).paper_size == PaperSize.A1_Portrait
174+
rv = view2.find_relationship_view(description="Uses")
175+
assert rv.paper_size == PaperSize.A3_Portrait

0 commit comments

Comments
 (0)