Coverage for backend/django/core/auxiliary/models/PropertySet.py: 93%

65 statements  

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

1from typing import TYPE_CHECKING, Tuple 

2from django.db import models 

3 

4from flowsheetInternals.unitops.config import PropertySetType 

5from core.auxiliary.enums.uiEnums import CompoundMode 

6 

7from .PropertyInfo import PropertyInfo 

8from .PropertyValue import PropertyValue 

9from .ControlValue import ControlValue 

10from flowsheetInternals.unitops.config import * 

11from core.managers import AccessControlManager, AllFlowsheetStatesManager 

12from .FlowsheetHistoryModel import FlowsheetHistoryModel 

13 

14if TYPE_CHECKING: 

15 from flowsheetInternals.unitops.models.SimulationObject import SimulationObject 

16 

17 

18class PropertySet(FlowsheetHistoryModel, models.Model): 

19 """ 

20 Base class for property sets 

21 This class implements no special methods for updating properties 

22 """ 

23 

24 flowsheet_state = models.ForeignKey( 

25 "FlowsheetState", on_delete=models.CASCADE, related_name="propertySets" 

26 ) 

27 compoundMode = models.CharField(choices=CompoundMode.choices, default="") 

28 simulationObject: models.OneToOneField["SimulationObject"] = models.OneToOneField( 

29 "flowsheetInternals_unitops.SimulationObject", 

30 on_delete=models.CASCADE, 

31 related_name="properties", 

32 null=True, 

33 ) 

34 

35 created_at = models.DateTimeField(auto_now_add=True) 

36 

37 objects = AccessControlManager() 

38 all_states = AllFlowsheetStatesManager() 

39 

40 class Meta: 

41 ordering = ["created_at"] 

42 

43 @property 

44 def containedProperties(self) -> models.QuerySet[PropertyInfo]: 

45 return self.ContainedProperties.all() 

46 

47 @property 

48 def schema(self) -> PropertySetType: 

49 return get_object_schema(self.simulationObject).properties 

50 

51 @property 

52 def disable_all(self) -> bool: 

53 simulationObject = self.simulationObject 

54 if not simulationObject.is_stream(): 

55 return False 

56 if simulationObject.has_recycle_connection: 

57 return False # don't disable properties if connected to a recycle block 

58 ports = simulationObject.connectedPorts 

59 if ports.count() == 0: 59 ↛ 60line 59 didn't jump to line 60 because the condition on line 59 was never true

60 return False # floating stream 

61 elif ports.count() == 1: 

62 if ports.get().direction == "inlet": 

63 return False # inlet stream 

64 return True 

65 

66 @property 

67 def has_simulation_object(self) -> bool: 

68 # eg. pinch analysis property set has no simulation object 

69 return bool(self.simulationObject) 

70 

71 def get_property(self, key: str, index: int = 0) -> PropertyInfo: 

72 """ 

73 Utility method for getting a property within this set by key. 

74 """ 

75 try: 

76 prop: PropertyInfo = self.ContainedProperties.get(key=key, index=index) 

77 return prop 

78 except PropertyInfo.DoesNotExist: 

79 raise ValueError( 

80 f"Property with key `{key}` not found in PropertySet, options: {[prop.key for prop in self.containedProperties]}" 

81 ) 

82 

83 def update_property( 

84 self, 

85 key: str, 

86 value: float | None = None, 

87 unit: str | None = None, 

88 index: int = 0, 

89 ) -> None: 

90 """ 

91 Utility method for updating the value and/or unit of a property, referenced by the key. 

92 """ 

93 prop = self.get_property(key, index) 

94 if value is not None: 

95 prop.set_value(value) 

96 if unit is not None: 

97 prop.unit = unit 

98 prop.save() 

99 

100 def add_control( 

101 self, prop1: str | PropertyInfo, prop2: str | PropertyInfo 

102 ) -> ControlValue: 

103 """ 

104 Add a control relationship between two properties. 

105 """ 

106 if isinstance(prop1, str): 106 ↛ 108line 106 didn't jump to line 108 because the condition on line 106 was always true

107 prop1 = self.get_property(prop1) 

108 if isinstance(prop2, str): 

109 prop2 = self.get_property(prop2) 

110 return prop1.add_control(prop2) 

111 

112 def get_unspecified_properties(self) -> list[str]: 

113 # For some reason, Pinch analysis is using property sets. They don't have simulation objects attached, 

114 # so getting unspecified properties is not possible. 

115 # Thus we have to return an empty list. 

116 if not self.simulationObject: 

117 return [] 

118 return self.simulationObject.get_unspecified_properties()