Coverage for backend/django/core/auxiliary/serializers/PropertyValueSerializer.py: 89%
114 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-12-18 04:00 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-12-18 04:00 +0000
1from core.auxiliary.models.PropertyValue import PropertyValue
2from rest_framework import serializers
3from core.auxiliary.models.PropertyInfo import PropertyInfo, check_is_except_last
4from core.auxiliary.models.PropertySet import PropertySet
5from flowsheetInternals.unitops.config.config_base import configuration
6from typing import List
7from drf_spectacular.utils import extend_schema_field
10from ..viewsets.compound_conversions import (
11 check_fully_defined,
12 convert_to_molar_fractions,
13 convert_to_raw_values,
14)
16class PropertyValueSerializer(serializers.ModelSerializer):
17 controlManipulated = serializers.IntegerField(
18 source='controlManipulated.id', read_only=True)
19 controlManipulatedName = serializers.CharField(
20 source='controlManipulated.setPoint.property.displayName', read_only=True)
21 controlManipulatedId = serializers.IntegerField(
22 source='controlManipulated.setPoint.property.set.simulationObject.id', read_only=True)
23 controlSetPoint = serializers.IntegerField(
24 source='controlSetPoint.id', read_only=True)
25 controlSetPointName = serializers.CharField(
26 source='controlSetPoint.manipulated.property.displayName', read_only=True)
27 controlSetPointId = serializers.IntegerField(
28 source='controlSetPoint.manipulated.property.set.simulationObject.id', read_only=True)
29 controlManipulatedObject = serializers.CharField(
30 source='controlManipulated.setPoint.property.set.simulationObject.componentName', read_only=True)
31 controlSetPointObject = serializers.CharField(
32 source='controlSetPoint.manipulated.property.set.simulationObject.componentName', read_only=True)
33 indexedSets = serializers.SerializerMethodField()
34 indexedSetNames = serializers.SerializerMethodField()
36 @extend_schema_field(serializers.ListField(child=serializers.CharField()))
37 def get_indexedSets(self, instance: PropertyValue) -> list:
38 return instance.get_indexes()
40 @extend_schema_field(serializers.ListField(child=serializers.CharField()))
41 def get_indexedSetNames(self, instance: PropertyValue) -> list:
42 return instance.get_index_names()
44 class Meta:
45 model = PropertyValue
46 fields = "__all__"
47 read_only_fields = ['id', 'controlManipulated', 'controlSetPoint']
49 def update(self, instance: PropertyValue, validated_data: dict) -> None:
50 if not instance.property.set.has_simulation_object: 50 ↛ 52line 50 didn't jump to line 52 because the condition on line 50 was never true
51 # eg. pinch property set
52 return super().update(instance, validated_data)
53 self.handle_update(instance, validated_data)
54 return instance
56 def handle_save(self, instance, validated_data) -> None:
57 value = validated_data["value"]
58 displayValue = validated_data.get("displayValue", value)
59 instance.value = value
60 instance.displayValue = displayValue
61 instance.save()
63 def handle_update(self, instance, validated_data) -> None:
64 from idaes_factory.endpoints import build_state_request, BuildStateSolveError
66 if "formula" in validated_data:
67 instance.formula = validated_data["formula"]
68 instance.save()
69 return
71 property_info: PropertyInfo = instance.property
72 property_set: PropertySet = property_info.set
73 simulation_object = property_set.simulationObject
74 def handle_save(): return self.handle_save(instance, validated_data)
76 object_type = simulation_object.objectType
77 config = configuration.get(object_type)
79 if simulation_object.objectType != "stream":
80 handle_save()
81 if check_is_except_last(property_info): 81 ↛ 104line 81 didn't jump to line 104 because the condition on line 81 was always true
82 # The last property should be calculated so that all of the last index set sums to 1.
84 # get the first index set
85 # The last items in the first index set will be calculated.
86 first_index_type = property_info.get_schema().indexSets[0]
88 # Get the all the indices for this PropertyValue
89 indexed_items = list(instance.indexedItems.all())
90 indexed_item_ids = [index.id for index in indexed_items if index.type != first_index_type]
91 # Get all the other property values that have the same indices
92 filtered_properties = instance.property.values
93 for index in indexed_item_ids:
94 filtered_properties = filtered_properties.filter(
95 indexedItems__id=index)
96 other_property_values: List[PropertyValue] = list(filtered_properties.all())
98 # Split off the last one, that will be calculated
99 calculated_property_value = other_property_values[-1]
100 known_property_values = other_property_values[:-1]
101 total_ = sum([float(prop_val.value) for prop_val in known_property_values if prop_val.pk != instance.pk and prop_val.value != None]) + float(instance.value)
102 calculated_property_value.value = 1 - total_
103 calculated_property_value.save()
104 return
106 property_set.ContainedProperties.all().prefetch_related("values")
108 revert_values: dict[PropertyValue, float] = {}
109 mole_frac_comp = property_set.get_property("mole_frac_comp")
111 def get_revert_values():
112 for prop in mole_frac_comp.values.all():
113 revert_values[prop] = prop.value
115 update_is_empty = validated_data["value"] in [None, ""]
116 if property_info.key == "mole_frac_comp":
117 fully_defined = check_fully_defined(
118 property_set, check_none_empty=True)
120 if fully_defined:
121 if update_is_empty: 121 ↛ 123line 121 didn't jump to line 123 because the condition on line 121 was never true
122 # we are becoming undefined, set all the properties to "raw" values
123 convert_to_raw_values(property_set)
124 handle_save()
125 else:
126 handle_save()
127 mole_frac_comp.refresh_from_db()
128 if check_fully_defined(property_set, check_fraction_sum=True): 128 ↛ 132line 128 didn't jump to line 132 because the condition on line 128 was always true
129 # previously had a value, staying as fully defined
130 convert_to_molar_fractions(property_set)
131 else:
132 convert_to_raw_values(property_set)
133 handle_save()
134 else:
135 # not fully defined
136 handle_save()
137 mole_frac_comp.refresh_from_db()
138 if (
139 not update_is_empty
140 and check_fully_defined(property_set, [mole_frac_comp])
141 ):
142 # this could make us fully defined
143 if check_fully_defined(property_set, check_fraction_sum=True):
144 get_revert_values()
145 convert_to_molar_fractions(property_set)
147 else:
148 fully_defined = check_fully_defined(
149 property_set, check_none_empty=True)
150 if update_is_empty: 150 ↛ 151line 150 didn't jump to line 151 because the condition on line 150 was never true
151 if fully_defined:
152 # we are becoming undefined, set all the properties to "raw" values
153 convert_to_raw_values(property_set)
154 handle_save()
155 else:
156 handle_save()
157 else:
158 handle_save()
159 if (
160 not fully_defined
161 and check_fully_defined(property_set, check_fraction_sum=True)
162 ):
163 # we are now fully defined
164 get_revert_values()
165 convert_to_molar_fractions(property_set)
167 simulation_object.refresh_from_db()
168 if (
169 not property_set.disable_all # ie. outlet stream or recycle stream
170 and check_fully_defined(property_set, check_fraction_sum=True)
171 and all([not value.is_externally_controlled()
172 for prop in PropertyInfo.objects.filter(set__simulationObject=simulation_object)
173 for value in prop.values.all()])
174 ):
175 # if stream and all properties are specified, solve the stream
176 try:
177 build_state_request(simulation_object)
178 except BuildStateSolveError as e:
179 # revert values if any
180 value_objs = []
181 for prop, value in revert_values.items():
182 value_objs.append(prop)
183 prop.value = value
185 PropertyValue.objects.bulk_update(value_objs, ["value"])
186 raise e