Coverage for backend/core/auxiliary/serializers/PropertyValueSerializer.py: 89%

105 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-11-06 23:27 +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 

7 

8 

9from ..viewsets.compound_conversions import ( 

10 check_fully_defined, 

11 convert_to_molar_fractions, 

12 convert_to_raw_values, 

13) 

14 

15class PropertyValueSerializer(serializers.ModelSerializer): 

16 controlManipulated = serializers.IntegerField( 

17 source='controlManipulated.id', read_only=True) 

18 controlManipulatedName = serializers.CharField( 

19 source='controlManipulated.setPoint.property.displayName', read_only=True) 

20 controlManipulatedId = serializers.IntegerField( 

21 source='controlManipulated.setPoint.property.set.simulationObject.id', read_only=True) 

22 controlSetPoint = serializers.IntegerField( 

23 source='controlSetPoint.id', read_only=True) 

24 controlSetPointName = serializers.CharField( 

25 source='controlSetPoint.manipulated.property.displayName', read_only=True) 

26 controlSetPointId = serializers.IntegerField( 

27 source='controlSetPoint.manipulated.property.set.simulationObject.id', read_only=True) 

28 controlManipulatedObject = serializers.CharField( 

29 source='controlManipulated.setPoint.property.set.simulationObject.componentName', read_only=True) 

30 controlSetPointObject = serializers.CharField( 

31 source='controlSetPoint.manipulated.property.set.simulationObject.componentName', read_only=True) 

32 

33 class Meta: 

34 model = PropertyValue 

35 fields = "__all__" 

36 read_only_fields = ['id', 'controlManipulated', 'controlSetPoint'] 

37 

38 def update(self, instance: PropertyValue, validated_data: dict) -> None: 

39 if not instance.property.set.has_simulation_object: 39 ↛ 41line 39 didn't jump to line 41 because the condition on line 39 was never true

40 # eg. pinch property set 

41 return super().update(instance, validated_data) 

42 self.handle_update(instance, validated_data) 

43 return instance 

44 

45 def handle_save(self, instance, validated_data) -> None: 

46 value = validated_data["value"] 

47 displayValue = validated_data.get("displayValue", value) 

48 instance.value = value 

49 instance.displayValue = displayValue 

50 instance.save() 

51 

52 def handle_update(self, instance, validated_data) -> None: 

53 from idaes_factory.endpoints import build_state_request, BuildStateSolveError 

54 

55 if "formula" in validated_data: 

56 instance.formula = validated_data["formula"] 

57 instance.save() 

58 return 

59 

60 property_info: PropertyInfo = instance.property 

61 property_set: PropertySet = property_info.set 

62 simulation_object = property_set.simulationObject 

63 def handle_save(): return self.handle_save(instance, validated_data) 

64 

65 object_type = simulation_object.objectType 

66 config = configuration.get(object_type) 

67 

68 if simulation_object.objectType != "stream": 

69 handle_save() 

70 if check_is_except_last(property_info): 70 ↛ 93line 70 didn't jump to line 93 because the condition on line 70 was always true

71 # The last property should be calculated so that all of the last index set sums to 1. 

72 

73 # get the first index set 

74 # The last items in the first index set will be calculated. 

75 first_index_type = property_info.get_schema().indexSets[0] 

76 

77 # Get the all the indices for this PropertyValue 

78 indexed_items = list(instance.indexedItems.all()) 

79 indexed_item_ids = [index.id for index in indexed_items if index.type != first_index_type] 

80 # Get all the other property values that have the same indices 

81 filtered_properties = instance.property.values 

82 for index in indexed_item_ids: 

83 filtered_properties = filtered_properties.filter( 

84 indexedItems__id=index) 

85 other_property_values: List[PropertyValue] = list(filtered_properties.all()) 

86 

87 # Split off the last one, that will be calculated 

88 calculated_property_value = other_property_values[-1] 

89 known_property_values = other_property_values[:-1] 

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

91 calculated_property_value.value = 1 - total_ 

92 calculated_property_value.save() 

93 return 

94 

95 property_set.ContainedProperties.all().prefetch_related("values") 

96 

97 revert_values: dict[PropertyValue, float] = {} 

98 mole_frac_comp = property_set.get_property("mole_frac_comp") 

99 

100 def get_revert_values(): 

101 for prop in mole_frac_comp.values.all(): 

102 revert_values[prop] = prop.value 

103 

104 update_is_empty = validated_data["value"] in [None, ""] 

105 if property_info.key == "mole_frac_comp": 

106 fully_defined = check_fully_defined( 

107 property_set, check_none_empty=True) 

108 

109 if fully_defined: 

110 if update_is_empty: 110 ↛ 112line 110 didn't jump to line 112 because the condition on line 110 was never true

111 # we are becoming undefined, set all the properties to "raw" values 

112 convert_to_raw_values(property_set) 

113 handle_save() 

114 else: 

115 handle_save() 

116 mole_frac_comp.refresh_from_db() 

117 if check_fully_defined(property_set, check_fraction_sum=True): 117 ↛ 121line 117 didn't jump to line 121 because the condition on line 117 was always true

118 # previously had a value, staying as fully defined 

119 convert_to_molar_fractions(property_set) 

120 else: 

121 convert_to_raw_values(property_set) 

122 handle_save() 

123 else: 

124 # not fully defined 

125 handle_save() 

126 mole_frac_comp.refresh_from_db() 

127 if ( 

128 not update_is_empty 

129 and check_fully_defined(property_set, [mole_frac_comp]) 

130 ): 

131 # this could make us fully defined 

132 if check_fully_defined(property_set, check_fraction_sum=True): 

133 get_revert_values() 

134 convert_to_molar_fractions(property_set) 

135 

136 else: 

137 fully_defined = check_fully_defined( 

138 property_set, check_none_empty=True) 

139 if update_is_empty: 139 ↛ 140line 139 didn't jump to line 140 because the condition on line 139 was never true

140 if fully_defined: 

141 # we are becoming undefined, set all the properties to "raw" values 

142 convert_to_raw_values(property_set) 

143 handle_save() 

144 else: 

145 handle_save() 

146 else: 

147 handle_save() 

148 if ( 

149 not fully_defined 

150 and check_fully_defined(property_set, check_fraction_sum=True) 

151 ): 

152 # we are now fully defined 

153 get_revert_values() 

154 convert_to_molar_fractions(property_set) 

155 

156 simulation_object.refresh_from_db() 

157 if ( 

158 not property_set.disable_all # ie. outlet stream or recycle stream 

159 and check_fully_defined(property_set, check_fraction_sum=True) 

160 and all([not value.is_externally_controlled() 

161 for prop in PropertyInfo.objects.filter(set__simulationObject=simulation_object) 

162 for value in prop.values.all()]) 

163 ): 

164 # if stream and all properties are specified, solve the stream 

165 try: 

166 build_state_request(simulation_object) 

167 except BuildStateSolveError as e: 

168 # revert values if any 

169 value_objs = [] 

170 for prop, value in revert_values.items(): 

171 value_objs.append(prop) 

172 prop.value = value 

173 

174 PropertyValue.objects.bulk_update(value_objs, ["value"]) 

175 raise e