Coverage for backend/django/Economics/results/models.py: 98%

101 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-06-23 21:51 +0000

1from django.db import models 

2 

3from core.managers import AccessControlManager 

4from Economics.shared.choices import ResultDependencyType, ResultLineKind, ResultRunStatus 

5from Economics.shared.model_base import FlowsheetScopedEconomicsModel 

6 

7 

8class EconomicsResultRun(FlowsheetScopedEconomicsModel): 

9 same_flowsheet_fields = ("study",) 

10 

11 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="economics_result_runs") 

12 study = models.ForeignKey("EconomicsStudy", on_delete=models.CASCADE, related_name="result_runs") 

13 status = models.CharField(max_length=32, choices=ResultRunStatus.choices, default=ResultRunStatus.DRAFT) 

14 result_currency = models.CharField(max_length=3, default="NZD") 

15 result_basis_date = models.DateField(null=True, blank=True) 

16 warning_payload = models.JSONField(default=dict, blank=True) 

17 created_at = models.DateTimeField(auto_now_add=True) 

18 completed_at = models.DateTimeField(null=True, blank=True) 

19 

20 objects = AccessControlManager() 

21 

22 class Meta: 

23 ordering = ["-created_at"] 

24 

25 def __str__(self): 

26 return f"{self.study.name} result run {self.pk or 'unsaved'}" 

27 

28class EconomicsResultDependency(FlowsheetScopedEconomicsModel): 

29 same_flowsheet_fields = ( 

30 "result_run", 

31 "source_assumptions", 

32 "source_baseline", 

33 "source_settings_profile", 

34 "source_costable_item", 

35 "source_cost_curve", 

36 "source_capital_line", 

37 "source_operating_line", 

38 "source_scenario", 

39 "source_property_info", 

40 ) 

41 

42 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="economics_result_dependencies") 

43 result_run = models.ForeignKey("EconomicsResultRun", on_delete=models.CASCADE, related_name="dependencies") 

44 dependency_type = models.CharField(max_length=64, choices=ResultDependencyType.choices) 

45 dependency_key = models.CharField(max_length=160) 

46 fingerprint_value = models.CharField(max_length=160) 

47 fingerprint_algorithm = models.CharField(max_length=32, default="sha256") 

48 fingerprint_basis = models.CharField(max_length=128, blank=True) 

49 source_label = models.CharField(max_length=160, blank=True) 

50 source_row_key = models.CharField(max_length=160, blank=True) 

51 source_version = models.CharField(max_length=128, blank=True) 

52 source_assumptions = models.ForeignKey( 

53 "EconomicsAssumptions", 

54 on_delete=models.SET_NULL, 

55 related_name="result_dependencies", 

56 null=True, 

57 blank=True, 

58 ) 

59 source_baseline = models.ForeignKey( 

60 "EconomicsBaseline", 

61 on_delete=models.SET_NULL, 

62 related_name="result_dependencies", 

63 null=True, 

64 blank=True, 

65 ) 

66 source_settings_profile = models.ForeignKey( 

67 "EconomicsSettingsProfile", 

68 on_delete=models.SET_NULL, 

69 related_name="result_dependencies", 

70 null=True, 

71 blank=True, 

72 ) 

73 source_costable_item = models.ForeignKey( 

74 "CostableItem", 

75 on_delete=models.SET_NULL, 

76 related_name="result_dependencies", 

77 null=True, 

78 blank=True, 

79 ) 

80 source_cost_curve = models.ForeignKey( 

81 "CostCurve", 

82 on_delete=models.SET_NULL, 

83 related_name="result_dependencies", 

84 null=True, 

85 blank=True, 

86 ) 

87 source_capital_line = models.ForeignKey( 

88 "CapitalCostLine", 

89 on_delete=models.SET_NULL, 

90 related_name="result_dependencies", 

91 null=True, 

92 blank=True, 

93 ) 

94 source_operating_line = models.ForeignKey( 

95 "OperatingCostLine", 

96 on_delete=models.SET_NULL, 

97 related_name="result_dependencies", 

98 null=True, 

99 blank=True, 

100 ) 

101 source_index_series = models.ForeignKey( 

102 "CostIndexSeries", 

103 on_delete=models.SET_NULL, 

104 related_name="result_dependencies", 

105 null=True, 

106 blank=True, 

107 ) 

108 source_index_value = models.ForeignKey( 

109 "CostIndexValue", 

110 on_delete=models.SET_NULL, 

111 related_name="result_dependencies", 

112 null=True, 

113 blank=True, 

114 ) 

115 source_scenario = models.ForeignKey( 

116 "core_auxiliary.Scenario", 

117 on_delete=models.SET_NULL, 

118 related_name="economics_result_dependencies", 

119 null=True, 

120 blank=True, 

121 ) 

122 source_property_info = models.ForeignKey( 

123 "core_auxiliary.PropertyInfo", 

124 on_delete=models.SET_NULL, 

125 related_name="economics_result_dependencies", 

126 null=True, 

127 blank=True, 

128 ) 

129 created_at = models.DateTimeField(auto_now_add=True) 

130 

131 objects = AccessControlManager() 

132 

133 class Meta: 

134 ordering = ["dependency_type", "dependency_key"] 

135 constraints = [ 

136 models.UniqueConstraint( 

137 fields=["result_run", "dependency_type", "dependency_key"], 

138 name="unique_result_dependency_key_per_run", 

139 ), 

140 ] 

141 

142 def __str__(self): 

143 return f"{self.dependency_type}:{self.dependency_key}" 

144 

145class EconomicsResultLine(FlowsheetScopedEconomicsModel): 

146 same_flowsheet_fields = ( 

147 "result_run", 

148 "source_costable_item", 

149 "source_cost_curve", 

150 "source_capital_line", 

151 "source_operating_line", 

152 "source_property_info", 

153 ) 

154 

155 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="economics_result_lines") 

156 result_run = models.ForeignKey("EconomicsResultRun", on_delete=models.CASCADE, related_name="lines") 

157 kind = models.CharField(max_length=32, choices=ResultLineKind.choices) 

158 group = models.CharField(max_length=64, blank=True) 

159 label = models.CharField(max_length=160) 

160 row_key = models.CharField(max_length=128) 

161 amount = models.DecimalField(max_digits=18, decimal_places=4, null=True, blank=True) 

162 unit = models.CharField(max_length=32, blank=True) 

163 source_costable_item = models.ForeignKey( 

164 "CostableItem", 

165 on_delete=models.SET_NULL, 

166 related_name="result_lines", 

167 null=True, 

168 blank=True, 

169 ) 

170 source_cost_curve = models.ForeignKey( 

171 "CostCurve", 

172 on_delete=models.SET_NULL, 

173 related_name="result_lines", 

174 null=True, 

175 blank=True, 

176 ) 

177 source_capital_line = models.ForeignKey( 

178 "CapitalCostLine", 

179 on_delete=models.SET_NULL, 

180 related_name="result_lines", 

181 null=True, 

182 blank=True, 

183 ) 

184 source_operating_line = models.ForeignKey( 

185 "OperatingCostLine", 

186 on_delete=models.SET_NULL, 

187 related_name="result_lines", 

188 null=True, 

189 blank=True, 

190 ) 

191 source_index_value = models.ForeignKey( 

192 "CostIndexValue", 

193 on_delete=models.SET_NULL, 

194 related_name="result_lines", 

195 null=True, 

196 blank=True, 

197 ) 

198 source_property_info = models.ForeignKey( 

199 "core_auxiliary.PropertyInfo", 

200 on_delete=models.SET_NULL, 

201 related_name="economics_result_lines", 

202 null=True, 

203 blank=True, 

204 ) 

205 source_row_key = models.CharField(max_length=160, blank=True) 

206 source_label = models.CharField(max_length=160, blank=True) 

207 source_note = models.TextField(blank=True) 

208 resource_source_kind = models.CharField(max_length=64, blank=True) 

209 resource_source_object_name = models.CharField(max_length=160, blank=True) 

210 resource_source_object_type = models.CharField(max_length=64, blank=True) 

211 resource_property_name = models.CharField(max_length=160, blank=True) 

212 resource_breakdown_category = models.CharField(max_length=64, blank=True) 

213 annual_basis_quantity = models.DecimalField(max_digits=24, decimal_places=8, null=True, blank=True) 

214 annual_basis_unit = models.CharField(max_length=32, blank=True) 

215 warning_payload = models.JSONField(default=dict, blank=True) 

216 sort_order = models.PositiveIntegerField(default=0) 

217 created_at = models.DateTimeField(auto_now_add=True) 

218 

219 objects = AccessControlManager() 

220 

221 class Meta: 

222 ordering = ["sort_order", "created_at"] 

223 constraints = [ 

224 models.UniqueConstraint( 

225 fields=["result_run", "row_key"], 

226 name="unique_result_line_key_per_run", 

227 ), 

228 ] 

229 

230 def __str__(self): 

231 return self.label 

232 

233class EconomicsChartDataset(FlowsheetScopedEconomicsModel): 

234 same_flowsheet_fields = ("result_run",) 

235 

236 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="economics_chart_datasets") 

237 result_run = models.ForeignKey("EconomicsResultRun", on_delete=models.CASCADE, related_name="chart_datasets") 

238 chart_key = models.CharField(max_length=128) 

239 title = models.CharField(max_length=160) 

240 chart_type = models.CharField(max_length=64) 

241 source_row_keys = models.JSONField(default=list, blank=True) 

242 chart_data = models.JSONField(default=dict, blank=True) 

243 rendering_metadata = models.JSONField(default=dict, blank=True) 

244 created_at = models.DateTimeField(auto_now_add=True) 

245 

246 objects = AccessControlManager() 

247 

248 class Meta: 

249 ordering = ["created_at"] 

250 constraints = [ 

251 models.UniqueConstraint( 

252 fields=["result_run", "chart_key"], 

253 name="unique_chart_dataset_key_per_run", 

254 ), 

255 ] 

256 

257 def __str__(self): 

258 return self.title