Coverage for backend/core/auxiliary/models/PropertySet.py: 88%
63 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
1from typing import Tuple
2from django.db import models
4from flowsheetInternals.unitops.config import PropertySetType
5from core.auxiliary.enums.uiEnums import CompoundMode
7from .PropertyInfo import PropertyInfo
8from .PropertyValue import PropertyValue
9from .ControlValue import ControlValue
10from flowsheetInternals.unitops.config import *
11from core.managers import AccessControlManager
12class PropertySet(models.Model):
13 """
14 Base class for property sets
15 This class implements no special methods for updating properties
16 """
17 flowsheet = models.ForeignKey("Flowsheet", on_delete=models.CASCADE, related_name="propertySets")
18 compoundMode = models.CharField(choices=CompoundMode.choices, default="")
19 simulationObject = models.OneToOneField("flowsheetInternals_unitops.SimulationObject", on_delete=models.CASCADE, related_name="properties", null=True)
22 created_at = models.DateTimeField(auto_now_add=True)
24 objects = AccessControlManager()
26 class Meta:
27 ordering = ['created_at']
29 @property
30 def containedProperties(self) -> models.QuerySet[PropertyInfo]:
31 return self.ContainedProperties.all()
33 @property
34 def schema(self) -> PropertySetType:
35 return get_object_schema(self.simulationObject).properties
37 @property
38 def disable_all(self) -> bool:
39 simulationObject = self.simulationObject
40 if not simulationObject.is_stream():
41 return False
42 if simulationObject.has_recycle_connection: 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true
43 return False # don't disable properties if connected to a recycle block
44 ports = simulationObject.connectedPorts
45 if ports.count() == 0: 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true
46 return False # floating stream
47 elif ports.count() == 1:
48 if ports.get().direction == "inlet":
49 return False # inlet stream
50 return True
52 @property
53 def has_simulation_object(self) -> bool:
54 # eg. pinch analysis property set has no simulation object
55 return bool(self.simulationObject)
58 def get_property(self, key: str, index: int = 0) -> PropertyInfo:
59 """
60 Utility method for getting a property within this set by key.
61 """
62 try:
63 prop: PropertyInfo = self.ContainedProperties.get(key=key, index=index)
64 return prop
65 except PropertyInfo.DoesNotExist:
66 raise ValueError(f"Property with key `{key}` not found in PropertySet, options: {[prop.key for prop in self.containedProperties]}")
69 def update_property(self, key: str, value: float | None = None, unit: str | None = None, index: int = 0) -> None:
70 """
71 Utility method for updating the value and/or unit of a property, referenced by the key.
72 """
73 prop = self.get_property(key, index)
74 if value is not None:
75 prop.set_value(value)
76 if unit is not None:
77 prop.unit = unit
78 prop.save()
81 def add_control(self, prop1: str | PropertyInfo, prop2: str | PropertyInfo) -> ControlValue:
82 """
83 Add a control relationship between two properties.
84 """
85 if isinstance(prop1, str): 85 ↛ 87line 85 didn't jump to line 87 because the condition on line 85 was always true
86 prop1 = self.get_property(prop1)
87 if isinstance(prop2, str):
88 prop2 = self.get_property(prop2)
89 return prop1.add_control(prop2)
91 def get_unspecified_properties(self) -> list[str]:
92 # For some reason, Pinch analysis is using property sets. They don't have simulation objects attached,
93 # so getting unspecified properties is not possible.
94 # Thus we have to return an empty list.
95 if not self.simulationObject: 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true
96 return []
97 return self.simulationObject.get_unspecified_properties()