1717
1818
1919import logging
20- from typing import Iterable , List , Optional , Union , ValuesView
20+ from typing import Iterable , List , Optional , Set , ValuesView
2121
2222from pydantic import Field
2323
24- from structurizr .model .deployment_node import DeploymentNode , DeploymentNodeIO
24+ from structurizr .model .deployment_node import DeploymentNode
2525
2626from ..abstract_base import AbstractBase
2727from ..base_model import BaseModel
28- from .component import Component
2928from .container import Container
3029from .container_instance import ContainerInstance
3130from .element import Element
@@ -106,20 +105,29 @@ def __init__(
106105 """
107106 super ().__init__ (** kwargs )
108107 self .enterprise = enterprise
109- self .people = set (people )
110- self .software_systems = set (software_systems )
111108 self .deployment_nodes = set (deployment_nodes )
112109 # TODO: simply iterate attributes
113110 self ._elements_by_id = {}
114111 self ._relationships_by_id = {}
115112 self ._id_generator = SequentialIntegerIDGenerator ()
116113
117114 def __contains__ (self , element : Element ):
115+ """Return True if the element is in the model."""
118116 return element in self .get_elements ()
119117
118+ @property
119+ def software_systems (self ) -> Set [SoftwareSystem ]:
120+ """Return the software systems in the model."""
121+ return set ([e for e in self .get_elements () if isinstance (e , SoftwareSystem )])
122+
123+ @property
124+ def people (self ) -> Set [Person ]:
125+ """Return the people in the model."""
126+ return set ([e for e in self .get_elements () if isinstance (e , Person )])
127+
120128 @classmethod
121129 def hydrate (cls , model_io : ModelIO ) -> "Model" :
122- """"""
130+ """Return a new model, hydrated from its IO. """
123131 model = cls (
124132 enterprise = Enterprise .hydrate (model_io .enterprise )
125133 if model_io .enterprise is not None
@@ -128,12 +136,10 @@ def hydrate(cls, model_io: ModelIO) -> "Model":
128136 )
129137
130138 for person_io in model_io .people :
131- person = Person .hydrate (person_io )
132- model .add_person (person = person )
139+ model += Person .hydrate (person_io )
133140
134141 for software_system_io in model_io .software_systems :
135- software_system = SoftwareSystem .hydrate (software_system_io , model = model )
136- model .add_software_system (software_system = software_system )
142+ model += SoftwareSystem .hydrate (software_system_io , model = model )
137143
138144 # for deployment_node_io in model_io.deployment_nodes:
139145 # deployment_node = DeploymentNode.hydrate(deployment_node_io)
@@ -154,12 +160,10 @@ def add_person(self, person=None, **kwargs) -> Person:
154160 Add a new person to the model.
155161
156162 Args:
157- person (Person, optional): Either provide a `Person` instance or
158- **kwargs: Provide keyword arguments for instantiating a `Person`
159- (recommended).
163+ **kwargs: Provide keyword arguments for instantiating a `Person`.
160164
161165 Returns:
162- Person: Either the same or a new instance, depending on arguments .
166+ Person: New instance.
163167
164168 Raises:
165169 ValueError: When a person with the same name already exists.
@@ -168,28 +172,20 @@ def add_person(self, person=None, **kwargs) -> Person:
168172 Person
169173
170174 """
171- if person is None :
172- person = Person (** kwargs )
173- if any (person .name == p .name for p in self .people ):
174- ValueError (
175- f"A person with the name '{ person .name } ' already exists in the model."
176- )
177- self ._add_element (person )
178- self .people .add (person )
175+ person = Person (** kwargs )
176+ self += person
179177 return person
180178
181- def add_software_system (self , software_system = None , ** kwargs ) -> SoftwareSystem :
179+ def add_software_system (self , ** kwargs ) -> SoftwareSystem :
182180 """
183181 Add a new software system to the model.
184182
185183 Args:
186- software_system (SoftwareSystem, optional): Either provide a
187- `SoftwareSystem` instance or
188184 **kwargs: Provide keyword arguments for instantiating a `SoftwareSystem`
189185 (recommended).
190186
191187 Returns:
192- SoftwareSystem: Either the same or a new instance, depending on arguments .
188+ SoftwareSystem: New instance.
193189
194190 Raises:
195191 ValueError: When a software system with the same name already exists.
@@ -198,55 +194,32 @@ def add_software_system(self, software_system=None, **kwargs) -> SoftwareSystem:
198194 SoftwareSystem
199195
200196 """
201- if software_system is None :
202- software_system = SoftwareSystem (** kwargs )
203- if any (software_system .name == s .name for s in self .software_systems ):
204- ValueError (
205- f"A software system with the name { software_system .name } already "
206- f"exists in the model."
207- )
208- self ._add_element (software_system )
209- self .software_systems .add (software_system )
197+ software_system = SoftwareSystem (** kwargs )
198+ self += software_system
210199 return software_system
211200
212- def __iadd__ (self , element : Union [ Person , SoftwareSystem ] ) -> "Model" :
213- """Add a new Person or SoftwareSystem to the model."""
201+ def __iadd__ (self , element : Element ) -> "Model" :
202+ """Add a newly constructed element to the model."""
214203 if isinstance (element , Person ):
215- self .add_person (person = element )
204+ if any (element .name == p .name for p in self .people ):
205+ raise ValueError (
206+ f"A person with the name '{ element .name } ' already exists in the "
207+ f"model."
208+ )
216209 elif isinstance (element , SoftwareSystem ):
217- self .add_software_system (software_system = element )
218- else :
210+ if any (element .name == s .name for s in self .software_systems ):
211+ raise ValueError (
212+ f"A software system with the name '{ element .name } ' already "
213+ f"exists in the model."
214+ )
215+ elif element .parent is None :
219216 raise ValueError (
220- f"Cannot add element with the name { element .name } to Model with += as it is not a Person or a SoftwareSystem."
217+ f"Element with name { element .name } has no parent. Please ensure "
218+ f"you have added it to the parent element."
221219 )
220+ self ._add_element (element )
222221 return self
223222
224- def add_container (self , container : Container ) -> Container :
225- """
226- Register a newly constructed container with the model.
227-
228- Args:
229- container (Container): `Container` instance to register.
230-
231- Returns:
232- Container: The provided container.
233-
234- Raises:
235- ValueError: When the container isn't a child of a `SoftwareSystem`
236-
237- See Also:
238- Container
239-
240- """
241- if container .parent is None :
242- raise ValueError (
243- f"Container with name { container .name } has no parent software system."
244- f"Adding to software system will register with the system's model."
245- )
246-
247- self ._add_element (container )
248- return container
249-
250223 def add_container_instance (
251224 self ,
252225 deployment_node : DeploymentNode ,
@@ -275,20 +248,6 @@ def add_container_instance(
275248 # TODO: implement
276249 # instance_number =
277250
278- def add_component (
279- self ,
280- component : Component ,
281- ) -> Component :
282- """Register a newly constructed Component with the model."""
283- if component .parent is None :
284- raise ValueError (
285- f"Component with name { component .name } has no parent container. "
286- f"Adding to container will register with the container's model."
287- )
288-
289- self ._add_element (component )
290- return component
291-
292251 def add_deployment_node (
293252 self , deployment_node : Optional [DeploymentNode ] = None , ** kwargs
294253 ) -> DeploymentNode :
@@ -383,16 +342,17 @@ def get_relationships(self) -> ValuesView[Relationship]:
383342 return self ._relationships_by_id .values ()
384343
385344 def get_elements (self ) -> ValuesView [Element ]:
345+ """Return an iterator over all elements contained in this model."""
386346 return self ._elements_by_id .values ()
387347
388348 def get_software_system_with_id (self , id : str ) -> Optional [SoftwareSystem ]:
349+ """Return the software system with a given ID."""
389350 result = self .get_element (id )
390351 if not isinstance (result , SoftwareSystem ):
391352 return None
392353 return result
393354
394355 def _add_element (self , element : Element ) -> None :
395- """"""
396356 if not element .id :
397357 element .id = self ._id_generator .generate_id ()
398358 elif (
@@ -405,7 +365,6 @@ def _add_element(self, element: Element) -> None:
405365 self ._id_generator .found (element .id )
406366
407367 def _add_relationship (self , relationship : Relationship ) -> bool :
408- """"""
409368 if relationship in self .get_relationships ():
410369 return True
411370 if not relationship .id :
0 commit comments