Coverage for backend/django/idaes_factory/adapters/property_value_adapter.py: 80%
58 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-02-12 01:47 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-02-12 01:47 +0000
1import json
2from core.auxiliary.models.PropertyInfo import PropertyInfo, check_is_except_last
3from core.auxiliary.models.PropertyValue import PropertyValue, sort_indexes
4from core.auxiliary.enums.uiEnums import DisplayType
5from common.config_types import *
6from core.auxiliary.models.DataCell import DataCell
7from ..idaes_factory_context import IdaesFactoryContext
9class _PropertyInfoNotSetException(Exception):
10 def __init__(self, message: str = ""):
11 if message == "":
12 message = "PropertyInfo value not set"
13 super().__init__(message)
16# Property info operator
17def check_fixed(ctx:IdaesFactoryContext, property_info: PropertyInfo, property_value: PropertyValue, is_tear: bool = False) -> bool:
18 """
19 Check that the property is fixed
20 Raise an error if not, or return False if variables are not required to be fixed
21 """
22 if is_tear and not property_value.is_control_set_point() and not property_value.has_value():
23 # recycle guess, allowed to be empty
24 return False
25 # Property must be fixed, and have a value
26 if not property_value.has_value():
27 if property_value.is_control_manipulated():
28 # this value is a guess variable, and allowed to be empty
29 return False
30 indexed_items = [x for x in property_value.indexedItems.all()]
31 property_values = [x for x in property_info.values.all()]
32 pv_ii = [[x for x in y.indexedItems.all()] for y in property_values]
33 # error, property should be fixed and have a value
34 if ctx.require_variables_fixed: 34 ↛ 35line 34 didn't jump to line 35 because the condition on line 34 was never true
35 raise _PropertyInfoNotSetException(f"Property `{property_info.displayName}` is not set")
36 else:
37 # RequireVariablesFixed is a context variable that can be set to False to allow for properties to be unset
38 # Allow this to pass for testing purposes only.
39 return False
40 return True
43def serialise_property_value(ctx:IdaesFactoryContext, property_info: PropertyInfo, property_value: PropertyValue, is_indexed: bool = True, is_tear: bool = False):
44 """
45 Serialise a property info object
46 """
48 # TODO: add time step handling: get the value at the given time step
49 match property_info.type:
50 case DisplayType.dropdown: 50 ↛ 51line 50 didn't jump to line 51 because the pattern on line 50 never matched
51 raise NotImplementedError("Dropdown properties are not yet supported in idaes_factory")
52 case DisplayType.checkbox: 52 ↛ 53line 52 didn't jump to line 53 because the pattern on line 52 never matched
53 return bool(json.loads(property_value.value)) # return True or False
54 case DisplayType.numeric_arg: 54 ↛ 55line 54 didn't jump to line 55 because the pattern on line 54 never matched
55 raise NotImplementedError("Dropdown properties are not yet supported in idaes_factory")
56 case DisplayType.segmented: 56 ↛ 57line 56 didn't jump to line 57 because the pattern on line 56 never matched
57 raise NotImplementedError("Segmented properties are not yet supported in idaes_factory")
58 case DisplayType.numeric: 58 ↛ 102line 58 didn't jump to line 102 because the pattern on line 58 always matched
59 if is_tear:
60 # tear properties are disabled, but we still need to serialise the recycle guesses
61 if not (
62 property_info.is_recycle_var()
63 or property_value.is_control_set_point()
64 ):
65 # skip, not enabled
66 return None
67 else:
68 if not property_value.is_enabled() and not property_value.is_control_manipulated():
69 # property is disabled (and not a guess), so don't serialise it
70 return None
71 # check that the property is fixed, and raise an error if not
72 if not check_fixed(ctx, property_info, property_value, is_tear):
73 # variables are not required to be fixed, so don't serialise this property
74 return None
76 value = property_value.value
77 # We are no longer supporting parsing expressions here, becuase that functionality
78 # is possible with specification and control blocks.
79 if getattr(property_value, "dataColumn", None) is not None:
80 if ctx.solve_index is not None: # if index is given for a value from the csv
81 value = DataCell.objects.get(
82 data_column = property_value.dataColumn,
83 data_row = ctx.solve_index,
84 ).value
85 elif ctx.is_dynamic() and is_indexed: # only use MSS for dynamics if the property is indexed, because otherwise it will be a single value. 85 ↛ 98line 85 didn't jump to line 98 because the condition on line 85 was always true
86 # We want to get all the data cells, and use as many data cells as there are timesteps
87 # (We are using the CSV data as input data)
88 # Note: assuming DataCell.objects.all() returns the values in the correct order
89 value = [i.value for i in DataCell.objects.filter(data_column=property_value.dataColumn).all()]
90 value = value[0:len(ctx.time_steps)]
91 return value
93 elif ctx.is_dynamic() and is_indexed:
94 return [float(value) for _ in ctx.time_steps]
96 # we used to use the dynamic results as the input but we aren't doing that any more
97 # so just return the one value, idaes_service will assume it's constant.
98 if is_indexed:
99 return [float(value)]
100 else:
101 return float(value)
102 case _:
103 raise ValueError(f"Property type {property_info.type} is not supported in idaes_factory")