Coverage for backend/django/flowsheetInternals/unitops/models/delete_factory.py: 93%

76 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-07-22 05:22 +0000

1from django.db import transaction 

2from PinchAnalysis.models.InputModels import StreamDataEntry 

3from flowsheetInternals.unitops.models.Port import Port 

4from flowsheetInternals.unitops.config.config_methods import get_object_schema 

5from .SimulationObject import SimulationObject 

6from Economics.costing.costable_items.deleted_objects import ( 

7 delete_economics_lines_for_simulation_objects, 

8) 

9from flowsheetInternals.unitops.services.edit_operations.recorder import ( 

10 tracked_queryset_update, 

11) 

12 

13 

14class DeleteFactory: 

15 def __init__(self, objs: list[SimulationObject]) -> None: 

16 self.simulation_objects = list( 

17 SimulationObject.objects.filter( 

18 id__in=[obj.id for obj in objs] 

19 ).prefetch_related( 

20 "ports", 

21 "ports__stream", 

22 "properties", 

23 "connectedPorts", 

24 "connectedPorts__unitOp", 

25 "grouping", 

26 "recycleData", 

27 "recycleConnection", 

28 ) 

29 ) 

30 

31 @classmethod 

32 def delete_object(cls, obj): 

33 factory = DeleteFactory([obj]) 

34 return factory.run_delete() 

35 

36 @classmethod 

37 def delete_multiple_objects(cls, objs: list[SimulationObject]) -> list[int]: 

38 factory = DeleteFactory(objs) 

39 return factory.run_delete() 

40 

41 @classmethod 

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

43 """Reactivate object tombstones for the legacy restore endpoint. 

44 

45 Connections are restored separately by that endpoint's client-side 

46 workflow; database-backed undo replays the complete recorded change set. 

47 """ 

48 

49 with transaction.atomic(): 

50 tracked_queryset_update( 

51 SimulationObject.objects.include_deleted().filter( 

52 id__in=object_ids, 

53 ), 

54 is_deleted=False, 

55 ) 

56 

57 def run_delete(self) -> list[int]: 

58 """Delete the resolved object batch and return every affected object ID.""" 

59 

60 list_of_streams: list[SimulationObject] = [] 

61 streams_to_update: list[SimulationObject] = [] 

62 

63 for obj in self.simulation_objects: 

64 # Handle streams connected to ports 

65 for port in obj.ports.all(): 

66 stream = port.stream 

67 if stream is not None and stream not in self.simulation_objects: 

68 # this stream is not being deleted, so we need to update its enabled properties 

69 list_of_streams.append(stream) 

70 

71 if obj.objectType == "group": 

72 # Include everything inside the group in the deletion 

73 group_sim_objects = obj.grouping.get_simulation_objects() 

74 self.simulation_objects.extend(group_sim_objects) 

75 

76 if obj.objectType == "recycle": 

77 # Handle recycle connections 

78 if obj.recycleData.tearObject is not None: 

79 streams_to_update.append(obj.recycleData.tearObject) 

80 obj.recycleData.clear() 

81 elif obj.is_stream(): 

82 # Decide if we should delete this stream: 

83 if obj.connectedPorts.count() == 0: 

84 # no problem, we can delete 

85 pass 

86 elif obj.connectedPorts.count() == 1: 

87 # no problem, just delete 

88 pass 

89 else: 

90 # Connected to two ports 

91 connectedPorts = obj.connectedPorts.all() 

92 if connectedPorts[0].unitOp in self.simulation_objects: 

93 # connected to a unit op that is being deleted 

94 if connectedPorts[1].unitOp in self.simulation_objects: 94 ↛ 100line 94 didn't jump to line 100 because the condition on line 94 was always true

95 # connected to two unit ops that are being deleted, just delete it 

96 pass 

97 else: 

98 # Keep stream as it's connected to another unit op. 

99 # Remove it from the list of streams to delete 

100 self.simulation_objects.remove(obj) 

101 elif connectedPorts[1].unitOp in self.simulation_objects: 

102 self.simulation_objects.remove(obj) 

103 else: 

104 # neither unit op is being deleted, so we need to split the stream into two. 

105 # Remove the current stream from deletion and create a new stream for the pump 1 outlet. 

106 # keep the existing stream 

107 self.simulation_objects.remove(obj) 

108 connectedPorts[1].stream.split_stream() 

109 

110 # reindex ports on delete 

111 for port in obj.connectedPorts.all(): 

112 unitop = port.unitOp 

113 # If the unit operation itself is being deleted (or missing), skip reindexing 

114 if unitop is None or unitop in self.simulation_objects: 

115 continue 

116 # This is for when a decision node has a port in a parent group and the other port in a child group 

117 if getattr(unitop, "objectType", None) == "decisionNode": 117 ↛ 118line 117 didn't jump to line 118 because the condition on line 117 was never true

118 continue 

119 unitop_config = get_object_schema(unitop) 

120 ports_config = unitop_config.ports[port.key] 

121 if ports_config and ports_config.many: 

122 number_of_ports = sum( 

123 p.key == port.key for p in unitop.ports.all() 

124 ) 

125 if number_of_ports > ports_config.minimum: 125 ↛ 126line 125 didn't jump to line 126 because the condition on line 125 was never true

126 port.reindex_port_on_delete() 

127 

128 with transaction.atomic(): 

129 deleted_object_ids = [obj.id for obj in self.simulation_objects] 

130 delete_economics_lines_for_simulation_objects(deleted_object_ids) 

131 

132 # Update property access before deletion 

133 for stream in streams_to_update: 

134 if stream in self.simulation_objects: 134 ↛ 135line 134 didn't jump to line 135 because the condition on line 134 was never true

135 continue # skip, already being deleted 

136 stream.reevaluate_properties_enabled() 

137 

138 # Disconnect ports before deletion 

139 tracked_queryset_update( 

140 Port.objects.filter(unitOp__id__in=deleted_object_ids), 

141 stream=None, 

142 ) 

143 

144 # Perform the deletion of Simulation objects, and related StreamDataEntry's 

145 tracked_queryset_update( 

146 SimulationObject.objects.filter(id__in=deleted_object_ids), 

147 is_deleted=True, 

148 ) 

149 StreamDataEntry.objects.filter(unitop_id__in=deleted_object_ids).delete() 

150 

151 # Update remaining streams' property access 

152 for stream in list_of_streams: 

153 stream.reevaluate_properties_enabled() 

154 

155 return deleted_object_ids