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
« 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
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)
20from .compound_conversions import compound_db_to_molar_flow, serialize_to_current_mode
23class UpdateCompoundsSerializer(serializers.Serializer):
24 propertySet = serializers.IntegerField(required=True) # pk of property set
25 compounds = serializers.ListField(required=True)
28class UpdateCompoundModeSerializer(serializers.Serializer):
29 compoundMode = serializers.ChoiceField(choices=CompoundMode.choices, required=True)
32class ResetPropertyInfoValuesSerializer(serializers.Serializer):
33 property_set = serializers.IntegerField(required=True)
36class SchemaPropertySetViewSet(ModelViewSet):
37 serializer_class = PropertySetSerializer
39 def get_queryset(self):
40 queryset = PropertySet.objects.all().prefetch_related(
41 "ContainedProperties__values__indexedItems"
42 )
43 return queryset
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"]
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
81 update_fraction_display_values(property_set)
83 return Response(mutation.add_to_data({"compoundMode": compound_mode}))
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
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 )
101 properties_schema = {}
102 value_objects = {}
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 )
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)
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 )
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)
170 tracked_bulk_update(
171 PropertyValue.objects,
172 value_objs,
173 ["value", "displayValue"],
174 )
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)
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)
196 def retrieve(self, request, *args, **kwargs):
197 property_set = self.get_object()
198 simulation_object = property_set.simulationObject
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)
206 serializer = self.get_serializer(property_set)
208 return Response(serializer.data)