Coverage for backend/flowsheetInternals/unitops/models/delete_factory.py: 88%
68 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
1from django.db import transaction
2from flowsheetInternals.unitops.models.simulation_object_factory import SimulationObjectFactory
3from flowsheetInternals.unitops.models.Port import Port
4from flowsheetInternals.unitops.config.config_methods import get_object_schema
5from .SimulationObject import SimulationObject
8class DeleteFactory:
9 def __init__(self, objs: list[SimulationObject]) -> None:
10 self.simulation_objects = list(SimulationObject.objects.filter(
11 id__in=[obj.id for obj in objs]
12 ).prefetch_related(
13 "ports",
14 "ports__stream",
15 "properties",
16 "connectedPorts",
17 "connectedPorts__unitOp",
18 "grouping",
19 "recycleData",
20 "recycleConnection",
21 ))
23 @classmethod
24 def delete_object(cls, obj):
25 factory = DeleteFactory([obj])
26 factory.run_delete()
28 @classmethod
29 def delete_multiple_objects(cls, objs: list[SimulationObject]) -> None:
30 factory = DeleteFactory(objs)
31 factory.run_delete()
33 def run_delete(self) -> None:
35 list_of_streams: list[SimulationObject] = []
36 streams_to_update: list[SimulationObject] = []
38 for obj in self.simulation_objects:
39 # Handle streams connected to ports
40 for port in obj.ports.all():
41 stream = port.stream
42 if stream is not None and stream not in self.simulation_objects:
43 # this stream is not being deleted, so we need to update its enabled properties
44 list_of_streams.append(stream)
46 if obj.objectType == "group":
47 # Include everything inside the group in the deletion
48 group_sim_objects = obj.grouping.get_simulation_objects()
49 self.simulation_objects.extend(group_sim_objects)
51 if obj.objectType == "recycle":
52 # Handle recycle connections
53 if obj.recycleData.tearObject is not None: 53 ↛ 55line 53 didn't jump to line 55 because the condition on line 53 was always true
54 streams_to_update.append(obj.recycleData.tearObject)
55 obj.recycleData.clear()
56 elif obj.is_stream():
57 # Decide if we should delete this stream:
58 if obj.connectedPorts.count() == 0:
59 # no problem, we can delete
60 pass
61 elif obj.connectedPorts.count() == 1:
62 # no problem, just delete
63 pass
64 else:
65 # Connected to two ports
66 connectedPorts = obj.connectedPorts.all()
67 if connectedPorts[0].unitOp in self.simulation_objects: 67 ↛ 69line 67 didn't jump to line 69 because the condition on line 67 was never true
68 # connected to a unit op that is being deleted
69 if connectedPorts[1].unitOp in self.simulation_objects:
70 # connected to two unit ops that are being deleted, just delete it
71 pass
72 else:
73 # Keep stream as it's connected to another unit op.
74 # Remove it from the list of streams to delete
75 self.simulation_objects.remove(obj)
76 elif connectedPorts[1].unitOp in self.simulation_objects:
77 self.simulation_objects.remove(obj)
78 else:
79 # neither unit op is being deleted, so we need to split the stream into two.
80 # Remove the current stream from deletion and create a new stream for the pump 1 outlet.
81 # keep the existing stream
82 self.simulation_objects.remove(obj)
83 connectedPorts[1].stream.split_stream()
85 # reindex ports on delete
86 for port in obj.connectedPorts.all():
87 unitop = port.unitOp
88 # If the unit operation itself is being deleted (or missing), skip reindexing
89 if unitop is None or unitop in self.simulation_objects:
90 continue
91 unitop_config = get_object_schema(unitop)
92 ports_config = unitop_config.ports[port.key]
93 if ports_config and ports_config.many:
94 number_of_ports = sum(p.key == port.key for p in unitop.ports.all())
95 if number_of_ports > ports_config.minimum: 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true
96 port.reindex_port_on_delete()
98 with transaction.atomic():
99 # Update property access before deletion
100 for stream in streams_to_update:
101 if stream in self.simulation_objects: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true
102 continue # skip, already being deleted
103 stream.reevaluate_properties_enabled()
105 # Disconnect ports before deletion
106 Port.objects.filter(
107 unitOp__id__in=[obj.id for obj in self.simulation_objects]
108 ).update(stream=None)
110 # Perform the deletion
111 SimulationObject.objects.filter(id__in=[obj.id for obj in self.simulation_objects]).update(is_deleted=True)
113 # Update remaining streams' property access
114 for stream in list_of_streams:
115 stream.reevaluate_properties_enabled()
118 @classmethod
119 def _restore_object_ids(cls, object_ids: list[int]) -> None:
120 """
121 Restore simulation objects by setting is_deleted=False.
123 Note: This method only restores the objects themselves.
124 Port-stream connections must be restored separately via the
125 frontend connection restoration system which has the captured
126 connection state from before deletion.
127 """
128 with transaction.atomic():
129 # Restore the simulation objects
130 SimulationObject.objects.include_deleted().filter(
131 id__in=object_ids
132 ).update(is_deleted=False)
134 # Note: Ports are not soft-deleted, they remain in the database
135 # with stream=None. The frontend is responsible for restoring
136 # the port-stream connections using the captured connection data.