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

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 

6 

7 

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 )) 

22 

23 @classmethod 

24 def delete_object(cls, obj): 

25 factory = DeleteFactory([obj]) 

26 factory.run_delete() 

27 

28 @classmethod 

29 def delete_multiple_objects(cls, objs: list[SimulationObject]) -> None: 

30 factory = DeleteFactory(objs) 

31 factory.run_delete() 

32 

33 def run_delete(self) -> None: 

34 

35 list_of_streams: list[SimulationObject] = [] 

36 streams_to_update: list[SimulationObject] = [] 

37 

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) 

45 

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) 

50 

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() 

84 

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() 

97 

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() 

104 

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) 

109 

110 # Perform the deletion 

111 SimulationObject.objects.filter(id__in=[obj.id for obj in self.simulation_objects]).update(is_deleted=True) 

112 

113 # Update remaining streams' property access 

114 for stream in list_of_streams: 

115 stream.reevaluate_properties_enabled() 

116 

117 

118 @classmethod 

119 def _restore_object_ids(cls, object_ids: list[int]) -> None: 

120 """ 

121 Restore simulation objects by setting is_deleted=False. 

122  

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) 

133 

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.