Coverage for backend/django/core/auxiliary/models/PropertyValue.py: 94%

67 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-12-18 04:00 +0000

1from django.db import models 

2 

3from core.auxiliary.models.IndexedItem import IndexedItem 

4from core.managers import AccessControlManager 

5from core.auxiliary.models.ControlValue import ControlValue 

6from typing import TYPE_CHECKING 

7if TYPE_CHECKING: 

8 from core.auxiliary.models.PropertyInfo import PropertyInfo 

9 from core.auxiliary.models.ControlValue import ControlValue 

10 

11class PropertyValue(models.Model): 

12 flowsheet = models.ForeignKey("Flowsheet", on_delete=models.CASCADE, related_name="propertyValues") 

13 value = models.JSONField(null=True, blank=True) 

14 displayValue = models.JSONField(null=True, blank=True) 

15 enabled = models.BooleanField(default=True) 

16 formula = models.CharField(max_length=2048, null=True, blank=True, default=None) 

17 property: "PropertyInfo" = models.ForeignKey("PropertyInfo", on_delete=models.CASCADE, related_name="values", null=True) 

18 indexedItems = models.ManyToManyField(IndexedItem, related_name="propertyValues") 

19 created_at = models.DateTimeField(auto_now_add=True) 

20 

21 objects = AccessControlManager() 

22 controlManipulated: ControlValue | None 

23 controlSetPoint: ControlValue | None 

24 # Bulk update doesn't work with access control manager so we 

25 # sometimes need to just use the normal manager. 

26 _unsafe_objects = models.Manager() 

27 

28 class Meta: 

29 ordering = ['created_at'] 

30 

31 def enable(self, state = True) -> None: 

32 self.enabled = state 

33 self.save() 

34 

35 def get_index(self, index_set: str) -> list[IndexedItem]: 

36 """ 

37 Get the indexed item of the specified type for this property. 

38 """ 

39 return self.indexedItems.get(type=index_set) 

40 

41 def has_value(self) -> bool: 

42 return self.value not in [None, ""] 

43 

44 def get_indexed_items(self) -> list[IndexedItem]: 

45 """ 

46 Gets the list of indexed items for this property value, in order 

47 Note that this is kinda expensive if you're doing it for everything, 

48 as it looks up the unit op type every time. 

49 But it's helpful for testing. 

50 """ 

51 if self.property.is_custom_property(): 

52 return [] # no special indexes on custom properties as they don't exist in the schema 

53 indexes = list(self.indexedItems.all()) 

54 property_key = self.property.key 

55 properties = self.property.set.simulationObject.schema.properties.get(property_key, None) 

56 index_set_order = self.property.set.simulationObject.schema.properties[property_key].indexSets 

57 return sort_indexes(index_set_order, indexes) 

58 

59 def get_indexes(self) -> list[str]: 

60 """ 

61 Gets the list of indexes for this property value, in order. e.g "outlet_1 

62, Note that this is kinda expensive if you're doing it for everything, 

63 as it looks up the unit op type every time. 

64 But it's helpful for testing. 

65 """ 

66 return [index.key for index in self.get_indexed_items()] 

67 

68 def get_index_names(self) -> list[str]: 

69 return [index.displayName for index in self.get_indexed_items()] 

70 

71 def is_control_set_point(self) -> bool: 

72 return hasattr(self, "controlSetPoint") 

73 

74 def is_control_manipulated(self) -> bool: 

75 return hasattr(self, "controlManipulated") 

76 

77 def is_externally_controlled(self) -> bool: 

78 """ 

79 Returns true if the property is controlled or controlling an external property 

80 """ 

81 if not self.is_control_manipulated() and not self.is_control_set_point(): 

82 return False 

83 if self.is_control_manipulated(): 

84 # set point is from a different property set 

85 return self.controlManipulated.setPoint.property.set != self.property.set 

86 if self.is_control_set_point(): 86 ↛ 89line 86 didn't jump to line 89 because the condition on line 86 was always true

87 # manipulated property is from a different property set 

88 return self.controlSetPoint.manipulated.property.set != self.property.set 

89 return False 

90 

91 def is_enabled(self) -> bool: 

92 """ 

93 Returns true if the property is to be enabled in simulation: either a statevar or a controlled property 

94 """ 

95 return (self.enabled and not self.is_control_manipulated() ) or self.is_control_set_point() 

96 

97 

98 def add_control(self, prop: "PropertyValue") -> ControlValue: 

99 # This property is the set point controlling the manipulated property 

100 return ControlValue.create(setPoint=self, manipulated=prop, flowsheet=prop.flowsheet) 

101 

102PropertyValueIntermediate = PropertyValue.indexedItems.through 

103 

104 

105def sort_indexes(index_set_order : list[str], indexes : list[IndexedItem]) -> list[IndexedItem]: 

106 """ 

107 Sorts the list of indexed items based on the order defined in the config file. 

108 index_set_order: list[str], a list of index set types, the order in which the indexes should be sorted. e.g ["splitter_fraction","compound","phase"] 

109 indexes: list[IndexedItem], a list of indexed items to be sorted. e.g a list of indexed items attached to a PropertyValue 

110 """ 

111 item = [index.type for index in indexes] 

112 reordered_indexes = [None] * len(indexes) 

113 

114 for i in range(len(item)): 

115 if item[i] != index_set_order[i]: 

116 # Find the correct position for the current item in object_properties 

117 correct_position = index_set_order.index(item[i]) 

118 # Place the current index in the correct position 

119 reordered_indexes[correct_position] = indexes[i] 

120 else: 

121 # If the item is already in the correct position, keep it as is 

122 reordered_indexes[i] = indexes[i] 

123 

124 # Update the original indexes list with the reordered indexes 

125 for i in range(len(indexes)): 

126 indexes[i] = reordered_indexes[i] 

127 return indexes