Coverage for backend/django/core/auxiliary/viewsets/PropertySetViewSet.py: 87%

106 statements  

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

1import traceback 

2from core.viewset import ModelViewSet 

3from rest_framework.decorators import action 

4from rest_framework.response import Response 

5from rest_framework import serializers 

6from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes 

7 

8from ..serializers.PropertyInfoSerializer import PropertySetSerializer 

9from ..models.PropertySet import PropertySet 

10from ..models.PropertyValue import PropertyValue 

11from core.auxiliary.enums.uiEnums import CompoundMode 

12from flowsheetInternals.unitops.models.FlowsheetEditOperation import ( 

13 FlowsheetEditOperation, 

14) 

15from flowsheetInternals.unitops.services.edit_operations.mutation import flowsheet_edit 

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

17 tracked_bulk_update, 

18) 

19 

20from .compound_conversions import compound_db_to_molar_flow, serialize_to_current_mode 

21 

22 

23class UpdateCompoundsSerializer(serializers.Serializer): 

24 propertySet = serializers.IntegerField(required=True) # pk of property set 

25 compounds = serializers.ListField(required=True) 

26 

27 

28class UpdateCompoundModeSerializer(serializers.Serializer): 

29 compoundMode = serializers.ChoiceField(choices=CompoundMode.choices, required=True) 

30 

31 

32class ResetPropertyInfoValuesSerializer(serializers.Serializer): 

33 property_set = serializers.IntegerField(required=True) 

34 

35 

36class SchemaPropertySetViewSet(ModelViewSet): 

37 serializer_class = PropertySetSerializer 

38 

39 def get_queryset(self): 

40 queryset = PropertySet.objects.all().prefetch_related( 

41 "ContainedProperties__values__indexedItems" 

42 ) 

43 return queryset 

44 

45 @extend_schema( 

46 parameters=[ 

47 OpenApiParameter(name="id", required=True, type=OpenApiTypes.INT), 

48 ], 

49 request=UpdateCompoundModeSerializer, 

50 ) 

51 @action(detail=False, methods=["PUT"]) 

52 def update_compound_mode(self, request): 

53 """Change composition basis as one reversible property-set edit.""" 

54 request_serializer = UpdateCompoundModeSerializer(data=request.data) 

55 request_serializer.is_valid(raise_exception=True) 

56 property_set = PropertySet.objects.get(id=self.request.query_params.get("id")) 

57 compound_mode = request_serializer.validated_data["compoundMode"] 

58 

59 with flowsheet_edit( 

60 flowsheet=property_set.flowsheet_state.flowsheet, 

61 user=request.user, 

62 kind=FlowsheetEditOperation.Kind.FieldPatch, 

63 label_key="Change compound basis", 

64 ) as mutation: 

65 property_set.compoundMode = compound_mode 

66 property_set.save(update_fields=["compoundMode"]) 

67 if compound_mode == "MolarFraction": 67 ↛ 78line 67 didn't jump to line 78 because the condition on line 67 was always true

68 property_values = list( 

69 property_set.get_property("mole_frac_comp").values.all() 

70 ) 

71 for prop in property_values: 

72 prop.displayValue = prop.value 

73 tracked_bulk_update( 

74 PropertyValue.objects, 

75 property_values, 

76 ["displayValue"], 

77 ) 

78 elif compound_mode == "MassFraction": 

79 from .compound_conversions import update_fraction_display_values 

80 

81 update_fraction_display_values(property_set) 

82 

83 return Response(mutation.add_to_data({"compoundMode": compound_mode})) 

84 

85 @extend_schema(request=None) 

86 @action(detail=True, methods=["POST"]) 

87 def normalize_compound_values(self, request, pk=None): 

88 try: 

89 property_set = PropertySet.objects.get(id=pk) 

90 mole_frac_comp = property_set.get_property("mole_frac_comp") 

91 compoundMode = property_set.compoundMode 

92 

93 value_objs = mole_frac_comp.values.all() 

94 sum_frac_comp = sum( 

95 [ 

96 float(prop.value) if prop.value not in [None, ""] else 0 

97 for prop in value_objs 

98 ] 

99 ) 

100 

101 properties_schema = {} 

102 value_objects = {} 

103 

104 ## this only works for mole fractions, since mass fractions need to have converted equivalent molar frac values. 

105 ## so we need to pass in compoundMode, 

106 def normalise_fractions(): 

107 for prop in value_objs: 

108 if prop.displayValue in [None, "", 0]: 

109 raise Exception( 

110 "Please specify all values for compounds, or remove them from the stream." 

111 ) 

112 else: 

113 prop.displayValue = str( 

114 float(prop.displayValue) / sum_frac_comp 

115 ) 

116 

117 if compoundMode == "MolarFraction": 

118 if prop.displayValue in [None, "", 0]: 118 ↛ 122line 118 didn't jump to line 122 because the condition on line 118 was never true

119 # this will work one day. But currently, allowing this gives an infeasible error on solve. 

120 # prop.value = 10e-11 

121 # prop.displayValue = 0 

122 raise Exception( 

123 "Please specify all values for compounds, or remove them from the stream." 

124 ) 

125 else: 

126 prop.value = prop.displayValue 

127 else: 

128 # we are in mass fraction basis. 

129 key = prop.get_index("compound").key 

130 value_objects[key] = prop 

131 if prop.displayValue not in [None, "", 0]: 131 ↛ 136line 131 didn't jump to line 136 because the condition on line 131 was always true

132 prop.value = compound_db_to_molar_flow( 

133 prop.get_index("compound").key, float(prop.displayValue) 

134 ) 

135 else: 

136 raise Exception( 

137 "Please specify all values for compounds, or remove them from the stream." 

138 ) 

139 properties_schema[key] = float(prop.value) 

140 

141 def mass_frac_to_molar_frac(properties_schema): 

142 sum_mass_frac = sum(prop for prop in properties_schema.values()) 

143 for prop in value_objs: 

144 key = prop.get_index("compound").key 

145 value_objects[key] = prop 

146 if properties_schema[key] not in [None, ""]: 146 ↛ 150line 146 didn't jump to line 150 because the condition on line 146 was always true

147 prop.value = float(properties_schema[key]) / float( 

148 sum_mass_frac 

149 ) 

150 if properties_schema[key] in [None, "", 0]: 150 ↛ 153line 150 didn't jump to line 153 because the condition on line 150 was never true

151 # prop.value = 10e-11 

152 # prop.displayValue = 0 

153 raise Exception( 

154 "Please specify all values for compounds, or remove them from the stream." 

155 ) 

156 

157 with flowsheet_edit( 

158 flowsheet=property_set.flowsheet_state.flowsheet, 

159 user=request.user, 

160 kind=FlowsheetEditOperation.Kind.FieldPatch, 

161 label_key="Normalize compounds", 

162 ) as mutation: 

163 match compoundMode: 

164 case "MolarFraction": 

165 normalise_fractions() 

166 case "MassFraction": 166 ↛ 170line 166 didn't jump to line 170 because the pattern on line 166 always matched

167 normalise_fractions() 

168 mass_frac_to_molar_frac(properties_schema) 

169 

170 tracked_bulk_update( 

171 PropertyValue.objects, 

172 value_objs, 

173 ["value", "displayValue"], 

174 ) 

175 

176 return Response( 

177 mutation.add_to_data( 

178 { 

179 "message": "Compound values normalized successfully", 

180 } 

181 ) 

182 ) 

183 except Exception as e: 

184 return self.error_response(e) 

185 

186 def error_response(self, e): 

187 tb_info = traceback.format_exc() 

188 error_message = str(e) 

189 response_data = { 

190 "status": "error", 

191 "message": error_message, 

192 "traceback": tb_info, 

193 } 

194 return Response(response_data, status=400) 

195 

196 def retrieve(self, request, *args, **kwargs): 

197 property_set = self.get_object() 

198 simulation_object = property_set.simulationObject 

199 

200 if simulation_object.is_stream(): 200 ↛ 206line 200 didn't jump to line 206 because the condition on line 200 was always true

201 serializer = self.get_serializer(property_set) 

202 properties_schema = serializer.data["ContainedProperties"] 

203 serialize_to_current_mode(property_set, properties_schema) 

204 return Response(serializer.data) 

205 

206 serializer = self.get_serializer(property_set) 

207 

208 return Response(serializer.data)