Coverage for tests/test_agriculture_demand_projections.py: 100%

103 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-16 23:05 +0000

1"""Tests for agriculture demand projection helpers.""" 

2 

3import csv 

4import sys 

5import types 

6from pathlib import Path 

7 

8import pandas as pd 

9from openpyxl import Workbook 

10 

11fake_stage_0_settings = types.ModuleType("prepare_times_nz.stage_0.stage_0_settings") 

12fake_stage_0_settings.BASE_YEAR = 2023 

13sys.modules.setdefault( 

14 "prepare_times_nz.stage_0.stage_0_settings", fake_stage_0_settings 

15) 

16 

17# pylint: disable=wrong-import-position 

18from prepare_times_nz.stage_3.demand_projections import agriculture 

19 

20# pylint: disable= duplicate-code 

21ASSUMPTION_COLUMNS = [ 

22 "SectorGroup", 

23 "Sector", 

24 "Scenario", 

25 "Method", 

26 "Workbook", 

27 "SheetName", 

28 "SourceCategory1", 

29 "SourceCategory2", 

30 "ConstantIndex", 

31 "Note", 

32] 

33 

34WORKBOOK_NAME = "mfe/Detailed-results-for-ERP2-projection-scenarios.xlsx" 

35SECTOR_GROUP = "Agriculture, Forestry and Fishing" 

36 

37TEST_ASSUMPTIONS = [ 

38 { 

39 "SectorGroup": SECTOR_GROUP, 

40 "Sector": "Dairy Cattle Farming", 

41 "Scenario": "Steady", 

42 "Method": "Workbook", 

43 "Workbook": WORKBOOK_NAME, 

44 "SheetName": "Baseline high", 

45 "SourceCategory1": "", 

46 "SourceCategory2": "Total dairy cattle", 

47 "ConstantIndex": "", 

48 "Note": "", 

49 }, 

50 { 

51 "SectorGroup": SECTOR_GROUP, 

52 "Sector": "Dairy Cattle Farming", 

53 "Scenario": "Shift", 

54 "Method": "Workbook", 

55 "Workbook": WORKBOOK_NAME, 

56 "SheetName": "Baseline low", 

57 "SourceCategory1": "", 

58 "SourceCategory2": "Total dairy cattle", 

59 "ConstantIndex": "", 

60 "Note": "", 

61 }, 

62 { 

63 "SectorGroup": SECTOR_GROUP, 

64 "Sector": "Livestock Farming", 

65 "Scenario": "Steady", 

66 "Method": "Workbook", 

67 "Workbook": WORKBOOK_NAME, 

68 "SheetName": "Baseline", 

69 "SourceCategory1": "", 

70 "SourceCategory2": "Sheep and beef 'stock units'", 

71 "ConstantIndex": "", 

72 "Note": "", 

73 }, 

74 { 

75 "SectorGroup": SECTOR_GROUP, 

76 "Sector": "Forestry and Logging", 

77 "Scenario": "Steady", 

78 "Method": "Workbook", 

79 "Workbook": WORKBOOK_NAME, 

80 "SheetName": "Baseline", 

81 "SourceCategory1": "Forestry (million m3)", 

82 "SourceCategory2": "Harvested timber (TRV)", 

83 "ConstantIndex": "", 

84 "Note": "", 

85 }, 

86 { 

87 "SectorGroup": SECTOR_GROUP, 

88 "Sector": "Indoor Cropping", 

89 "Scenario": "Steady", 

90 "Method": "Workbook", 

91 "Workbook": WORKBOOK_NAME, 

92 "SheetName": "Baseline", 

93 "SourceCategory1": "", 

94 "SourceCategory2": "Horticulture", 

95 "ConstantIndex": "", 

96 "Note": "", 

97 }, 

98 { 

99 "SectorGroup": SECTOR_GROUP, 

100 "Sector": "Fishing, Hunting and Trapping", 

101 "Scenario": "Steady", 

102 "Method": "Constant", 

103 "Workbook": "", 

104 "SheetName": "", 

105 "SourceCategory1": "", 

106 "SourceCategory2": "", 

107 "ConstantIndex": 1, 

108 "Note": "", 

109 }, 

110] 

111 

112 

113def make_test_erp_workbook(path: Path): 

114 """Create a minimal ERP-like workbook with scenario sheets and year columns.""" 

115 wb = Workbook() 

116 ws = wb.active 

117 ws.title = "Baseline" 

118 

119 for sheet_name, dairy_2050, livestock_2050 in [ 

120 ("Baseline", 70, 80), 

121 ("Baseline high", 85, 80), 

122 ("Baseline low", 60, 65), 

123 ]: 

124 ws = ( 

125 wb[sheet_name] 

126 if sheet_name in wb.sheetnames 

127 else wb.create_sheet(sheet_name) 

128 ) 

129 ws.append([None, None]) 

130 ws.append(["Scenario", None]) 

131 ws.append([sheet_name, None]) 

132 ws.append([None, None]) 

133 ws.append([None, None]) 

134 ws.append([None, None, 2023, 2025, 2030, 2050]) 

135 ws.append([None, "Total dairy cattle", 100, 90, 80, dairy_2050]) 

136 ws.append([None, "Sheep and beef 'stock units'", 100, 95, 90, livestock_2050]) 

137 ws.append( 

138 ["Forestry (million m3)", "Harvested timber (TRV)", 100, 110, 130, 160] 

139 ) 

140 ws.append([None, "Horticulture", 100, 105, 110, 120]) 

141 ws.append(["Other agriculture", "Total", 100, 102, 104, 106]) 

142 

143 wb.save(path) 

144 

145 

146def write_assumptions_csv(path: Path, rows): 

147 """Write the agriculture assumptions test file from structured rows.""" 

148 with path.open("w", newline="", encoding="utf-8") as file_obj: 

149 writer = csv.DictWriter(file_obj, fieldnames=ASSUMPTION_COLUMNS) 

150 writer.writeheader() 

151 writer.writerows(rows) 

152 

153 

154def write_baseyear_demand_csv(path: Path): 

155 """Write a minimal base-year demand file for agriculture projection tests.""" 

156 rows = [ 

157 { 

158 "Sector": "Dairy Cattle Farming", 

159 "CommodityOut": "MILK", 

160 "Island": "NI", 

161 "Technology": "DAIRY_TECH", 

162 "EndUse": "Process", 

163 "Variable": "InputEnergy", 

164 "Unit": "PJ", 

165 "Value": 10.0, 

166 }, 

167 { 

168 "Sector": "Dairy Cattle Farming", 

169 "CommodityOut": "MILK", 

170 "Island": "NI", 

171 "Technology": "DAIRY_TECH", 

172 "EndUse": "Process", 

173 "Variable": "OutputEnergy", 

174 "Unit": "PJ", 

175 "Value": 5.0, 

176 }, 

177 { 

178 "Sector": "Fishing, Hunting and Trapping", 

179 "CommodityOut": "FISH", 

180 "Island": "SI", 

181 "Technology": "FISH_TECH", 

182 "EndUse": "Process", 

183 "Variable": "InputEnergy", 

184 "Unit": "PJ", 

185 "Value": 3.0, 

186 }, 

187 ] 

188 path.parent.mkdir(parents=True, exist_ok=True) 

189 pd.DataFrame(rows).to_csv(path, index=False) 

190 

191 

192def setup_temp_agriculture_paths(tmp_path: Path, monkeypatch): 

193 """Point the agriculture module at temporary stage data locations.""" 

194 stage_2_data = tmp_path / "stage_2_baseyear_data" 

195 stage_3_data = tmp_path / "stage_3_scenario_data" 

196 output = stage_3_data / "demand_projections" 

197 

198 monkeypatch.setattr(agriculture, "STAGE_2_DATA", stage_2_data) 

199 monkeypatch.setattr(agriculture, "STAGE_3_DATA", stage_3_data) 

200 monkeypatch.setattr(agriculture, "OUTPUT", output) 

201 monkeypatch.setattr(agriculture, "OUTPUT_CHECKS", output / "checks") 

202 

203 return stage_2_data, stage_3_data 

204 

205 

206def get_index(df, sector, scenario, year): 

207 """Return one compiled index value for the requested sector/scenario/year.""" 

208 return df[ 

209 (df["Sector"] == sector) & (df["Scenario"] == scenario) & (df["Year"] == year) 

210 ]["Index"].iloc[0] 

211 

212 

213def test_get_agriculture_growth_indices_reads_workbook_mappings(tmp_path): 

214 """Workbook-mapped and constant agriculture projections should compile correctly.""" 

215 external_data_dir = tmp_path / "external_data" 

216 workbook_dir = external_data_dir / "mfe" 

217 workbook_dir.mkdir(parents=True) 

218 workbook_path = workbook_dir / "Detailed-results-for-ERP2-projection-scenarios.xlsx" 

219 make_test_erp_workbook(workbook_path) 

220 

221 assumptions_path = tmp_path / "agriculture_demand_projections.csv" 

222 write_assumptions_csv(assumptions_path, TEST_ASSUMPTIONS) 

223 

224 df = agriculture.get_agriculture_growth_indices( 

225 assumptions_path=assumptions_path, 

226 external_data_dir=external_data_dir, 

227 ) 

228 

229 assert get_index(df, "Dairy Cattle Farming", "Steady", 2025) == 0.9 

230 assert get_index(df, "Dairy Cattle Farming", "Steady", 2024) == 0.95 

231 assert get_index(df, "Dairy Cattle Farming", "Steady", 2050) == 0.85 

232 assert get_index(df, "Dairy Cattle Farming", "Shift", 2050) == 0.6 

233 assert get_index(df, "Livestock Farming", "Steady", 2050) == 0.8 

234 assert get_index(df, "Indoor Cropping", "Steady", 2050) == 1.2 

235 assert get_index(df, "Fishing, Hunting and Trapping", "Steady", 2042) == 1.0 

236 

237 

238def test_get_energy_demand_projections_uses_temp_stage_data(tmp_path, monkeypatch): 

239 """Energy projections should be computed from temp test data, not repo intermediates.""" 

240 stage_2_data, _ = setup_temp_agriculture_paths(tmp_path, monkeypatch) 

241 

242 external_data_dir = tmp_path / "external_data" 

243 workbook_dir = external_data_dir / "mfe" 

244 workbook_dir.mkdir(parents=True) 

245 workbook_path = workbook_dir / "Detailed-results-for-ERP2-projection-scenarios.xlsx" 

246 make_test_erp_workbook(workbook_path) 

247 

248 assumptions_path = tmp_path / "agriculture_demand_projections.csv" 

249 write_assumptions_csv(assumptions_path, TEST_ASSUMPTIONS) 

250 baseyear_path = ( 

251 stage_2_data / "ag_forest_fish" / "baseyear_ag_forest_fish_demand.csv" 

252 ) 

253 

254 write_baseyear_demand_csv(baseyear_path) 

255 original_get_growth_indices = agriculture.get_agriculture_growth_indices 

256 monkeypatch.setattr( 

257 agriculture, 

258 "get_agriculture_growth_indices", 

259 lambda: original_get_growth_indices( 

260 assumptions_path=assumptions_path, 

261 external_data_dir=external_data_dir, 

262 ), 

263 ) 

264 

265 df = agriculture.get_energy_demand_projections("InputEnergy") 

266 

267 dairy_2025 = df[ 

268 (df["Sector"] == "Dairy Cattle Farming") 

269 & (df["Scenario"] == "Steady") 

270 & (df["Year"] == 2025) 

271 & (df["Variable"] == "InputEnergy") 

272 ]["Value"].iloc[0] 

273 fishing_2042 = df[ 

274 (df["Sector"] == "Fishing, Hunting and Trapping") 

275 & (df["Scenario"] == "Steady") 

276 & (df["Year"] == 2042) 

277 & (df["Variable"] == "InputEnergy") 

278 ]["Value"].iloc[0] 

279 

280 assert dairy_2025 == 9.0 

281 assert fishing_2042 == 3.0 

282 

283 

284def test_main_writes_outputs_to_temp_stage_data(tmp_path, monkeypatch): 

285 """Main should save outputs under temp stage data rather than repo intermediates.""" 

286 stage_2_data, stage_3_data = setup_temp_agriculture_paths(tmp_path, monkeypatch) 

287 

288 external_data_dir = tmp_path / "external_data" 

289 workbook_dir = external_data_dir / "mfe" 

290 workbook_dir.mkdir(parents=True) 

291 workbook_path = workbook_dir / "Detailed-results-for-ERP2-projection-scenarios.xlsx" 

292 make_test_erp_workbook(workbook_path) 

293 

294 assumptions_path = tmp_path / "agriculture_demand_projections.csv" 

295 write_assumptions_csv(assumptions_path, TEST_ASSUMPTIONS) 

296 baseyear_path = ( 

297 stage_2_data / "ag_forest_fish" / "baseyear_ag_forest_fish_demand.csv" 

298 ) 

299 

300 write_baseyear_demand_csv(baseyear_path) 

301 original_get_growth_indices = agriculture.get_agriculture_growth_indices 

302 monkeypatch.setattr( 

303 agriculture, 

304 "get_agriculture_growth_indices", 

305 lambda: original_get_growth_indices( 

306 assumptions_path=assumptions_path, 

307 external_data_dir=external_data_dir, 

308 ), 

309 ) 

310 

311 agriculture.main() 

312 

313 assert ( 

314 stage_3_data / "demand_projections" / "agriculture_demand_index.csv" 

315 ).exists() 

316 assert ( 

317 stage_3_data / "demand_projections" / "checks" / "agriculture_input.csv" 

318 ).exists() 

319 assert ( 

320 stage_3_data / "demand_projections" / "checks" / "agriculture_output.csv" 

321 ).exists()