Coverage for backend/flowsheetInternals/formula_templates/add_template.py: 91%
37 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, Dict
2from flowsheetInternals.unitops.models import SimulationObject
3from .template_schema import TemplateSchema
4from .formula_templates import templates
5from core.auxiliary.models.PropertyInfo import PropertyInfo
6from core.auxiliary.models.PropertyValue import PropertyValue
8ID_MAPPING = dict[
9 str, Tuple[PropertyInfo, PropertyValue]
10] # Mapping from property key to the info/value tuple persisted in the DB
13def add_template(object: SimulationObject, key: str):
14 """Attach a property template and initialise its custom formulas.
16 The template describes one or more synthetic properties (for example cost
17 expressions) that should be created on ``object``. Required properties are
18 validated up-front; once the new ``PropertyInfo`` and ``PropertyValue``
19 records are persisted, each configured formula is rewritten to point at the
20 database identifiers of the participating properties.
22 :param object: Simulation object that receives the template-driven properties.
23 :param key: Template key, as defined in ``formula_templates``.
24 :raises ValueError: If the template does not exist or required properties
25 are missing on the simulation object.
26 """
27 template: TemplateSchema = templates.get(key)
29 if not template: 29 ↛ 30line 29 didn't jump to line 30 because the condition on line 29 was never true
30 raise ValueError(f"Template with key '{key}' does not exist.")
32 property_keys = [prop.key for prop in object.properties.containedProperties.all()]
33 for required_property in template.required_properties:
34 if required_property not in property_keys: 34 ↛ 35line 34 didn't jump to line 35 because the condition on line 34 was never true
35 raise ValueError(
36 f"Template '{key}' requires property '{required_property}' which is not present in the object."
37 )
39 property_infos = []
40 property_values = []
42 for field in template.fields:
43 property_info = PropertyInfo(
44 flowsheet=object.flowsheet,
45 displayName=field.name,
46 key=field.key,
47 set=object.properties,
48 )
49 property_value = PropertyValue(
50 property=property_info,
51 flowsheet=object.flowsheet,
52 value=None, # Default value can be set later
53 )
54 property_infos.append(property_info)
55 property_values.append(property_value)
57 PropertyInfo.objects.bulk_create(property_infos)
58 PropertyValue.objects.bulk_create(property_values)
60 # Now all properties are created, we can set the formula for each property value.
61 # This must be done later, because we need the IDs of the new objects in the database.
62 # We now create a mapping so that we can set the formula information correctly.
63 id_mapping: ID_MAPPING = {
64 field.key: (property_info, property_value)
65 for field, property_info, property_value in zip(
66 template.fields, property_infos, property_values
67 )
68 }
69 # Also add all the properties that already exist on the object
70 for property_info in object.properties.containedProperties.all():
71 if property_info.key not in id_mapping:
72 property_value = (
73 property_info.values.first()
74 ) # TODO: Support indexed properties
75 if property_value: 75 ↛ 70line 75 didn't jump to line 70 because the condition on line 75 was always true
76 id_mapping[property_info.key] = (property_info, property_value)
78 for field, property_value, property_info in zip(
79 template.fields, property_values, property_infos
80 ):
81 property_value.formula = replace_with_ids(field.formula, id_mapping)
83 PropertyValue.objects.bulk_update(property_values, ["formula"])
86def replace_with_ids(formula: str, id_mapping: ID_MAPPING) -> str:
87 """Swap template placeholders with the persisted property identifiers.
89 ``formula`` uses the template convention ``[key]``. This helper updates the
90 formula with the ``@[property display name](prop<ID>)`` syntax expected by the front-end using
91 the mapping collected during template application.
93 :param formula: Raw template formula string.
94 :param id_mapping: Mapping from property keys to the ``PropertyInfo`` and
95 ``PropertyValue`` instances created or found on the simulation object.
96 :return: Updated formula string with property references rewritten.
97 """
98 for key, (property_info, property_value) in id_mapping.items():
99 formula = formula.replace(
100 f"[{key}]", f"@[{property_info.displayName}](prop{property_value.id})"
101 )
102 return formula