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

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 

7 

8ID_MAPPING = dict[ 

9 str, Tuple[PropertyInfo, PropertyValue] 

10] # Mapping from property key to the info/value tuple persisted in the DB 

11 

12 

13def add_template(object: SimulationObject, key: str): 

14 """Attach a property template and initialise its custom formulas. 

15 

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. 

21 

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) 

28 

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.") 

31 

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 ) 

38 

39 property_infos = [] 

40 property_values = [] 

41 

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) 

56 

57 PropertyInfo.objects.bulk_create(property_infos) 

58 PropertyValue.objects.bulk_create(property_values) 

59 

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) 

77 

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) 

82 

83 PropertyValue.objects.bulk_update(property_values, ["formula"]) 

84 

85 

86def replace_with_ids(formula: str, id_mapping: ID_MAPPING) -> str: 

87 """Swap template placeholders with the persisted property identifiers. 

88 

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. 

92 

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