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