Coverage for cc_modules/tests/cc_taskreports_tests.py: 99%

184 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-03-25 16:28 +0000

1""" 

2camcops_server/cc_modules/tests/cc_tasksreports_tests.py 

3 

4=============================================================================== 

5 

6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry. 

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk). 

8 

9 This file is part of CamCOPS. 

10 

11 CamCOPS is free software: you can redistribute it and/or modify 

12 it under the terms of the GNU General Public License as published by 

13 the Free Software Foundation, either version 3 of the License, or 

14 (at your option) any later version. 

15 

16 CamCOPS is distributed in the hope that it will be useful, 

17 but WITHOUT ANY WARRANTY; without even the implied warranty of 

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19 GNU General Public License for more details. 

20 

21 You should have received a copy of the GNU General Public License 

22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

23 

24=============================================================================== 

25 

26**Test server reports on CamCOPS tasks.** 

27 

28""" 

29 

30from typing import Optional 

31 

32from camcops_server.cc_modules.cc_pyramid import ViewParam 

33from camcops_server.cc_modules.cc_taskindex import TaskIndexEntry 

34from camcops_server.cc_modules.cc_taskreports import TaskCountReport 

35from camcops_server.cc_modules.cc_testfactories import ( 

36 GroupFactory, 

37 PatientFactory, 

38 UserFactory, 

39 UserGroupMembershipFactory, 

40) 

41from camcops_server.cc_modules.cc_unittest import BasicDatabaseTestCase 

42from camcops_server.tasks.tests.factories import BmiFactory, Phq9Factory 

43 

44from pendulum import DateTime as Pendulum, datetime 

45 

46 

47class TaskCountReportTestBase(BasicDatabaseTestCase): 

48 # pytest will collect tests that are derived from unitest.TestCase 

49 # regardless of what python_classes says in pytest.ini so we need to set 

50 # this to stop tests being run in the baseclass and override in the derived 

51 # class. 

52 __test__ = False 

53 

54 def setUp(self) -> None: 

55 super().setUp() 

56 

57 self.date_01_oct_2022 = datetime(2022, 10, 1, 12) 

58 self.date_01_nov_2022 = datetime(2022, 11, 1, 12) 

59 self.date_30_nov_2022 = datetime(2022, 11, 30, 12) 

60 self.date_01_jan_2023 = datetime(2023, 1, 1, 12) 

61 

62 self.num_01_nov_2022_phq9_tasks = 4 

63 self.num_30_nov_2022_phq9_tasks = 5 

64 self.num_01_oct_2022_bmi_tasks = 2 

65 self.num_01_nov_2022_bmi_tasks = 3 

66 self.num_01_jan_2023_bmi_tasks = 1 

67 

68 # Freda and Jim are both members of Group A. Freda took over from Jim 

69 # in Nov 2022 

70 self.group_a = GroupFactory(name="Group A") 

71 self.group_b = GroupFactory(name="Group B") 

72 

73 self.freda = UserFactory(username="freda") 

74 self.jim = UserFactory(username="jim") 

75 

76 UserGroupMembershipFactory( 

77 group_id=self.group_a.id, 

78 user_id=self.freda.id, 

79 may_run_reports=True, 

80 ) 

81 UserGroupMembershipFactory( 

82 group_id=self.group_a.id, user_id=self.jim.id, may_run_reports=True 

83 ) 

84 

85 self.num_jim_tasks = self.num_01_oct_2022_bmi_tasks = 2 

86 self.num_freda_tasks = ( 

87 self.num_01_nov_2022_phq9_tasks 

88 + self.num_30_nov_2022_phq9_tasks 

89 + self.num_01_nov_2022_bmi_tasks 

90 + self.num_01_jan_2023_bmi_tasks 

91 ) 

92 

93 self.num_group_a_tasks = self.num_jim_tasks + self.num_freda_tasks 

94 

95 all_tasks = [] 

96 

97 for i in range(0, self.num_01_oct_2022_bmi_tasks): 

98 patient = PatientFactory( 

99 _when_added_exact=self.date_01_oct_2022, 

100 ) 

101 all_tasks.append( 

102 BmiFactory( 

103 _group=self.group_a, 

104 patient_id=patient.id, 

105 when_created=self.date_01_oct_2022, 

106 _when_added_exact=self.date_01_oct_2022, 

107 _adding_user=self.jim, 

108 ) 

109 ) 

110 

111 for i in range(0, self.num_01_nov_2022_bmi_tasks): 

112 patient = PatientFactory( 

113 _when_added_exact=self.date_01_nov_2022, 

114 ) 

115 all_tasks.append( 

116 BmiFactory( 

117 _group=self.group_a, 

118 patient_id=patient.id, 

119 when_created=self.date_01_nov_2022, 

120 _when_added_exact=self.date_01_nov_2022, 

121 _adding_user=self.freda, 

122 ) 

123 ) 

124 

125 for i in range(0, self.num_01_nov_2022_phq9_tasks): 

126 patient = PatientFactory( 

127 _when_added_exact=self.date_01_nov_2022, 

128 ) 

129 all_tasks.append( 

130 Phq9Factory( 

131 _group=self.group_a, 

132 patient_id=patient.id, 

133 when_created=self.date_01_nov_2022, 

134 _when_added_exact=self.date_01_nov_2022, 

135 _adding_user=self.freda, 

136 ) 

137 ) 

138 

139 for i in range(0, self.num_30_nov_2022_phq9_tasks): 

140 patient = PatientFactory( 

141 _when_added_exact=self.date_30_nov_2022, 

142 ) 

143 all_tasks.append( 

144 Phq9Factory( 

145 _group=self.group_a, 

146 patient_id=patient.id, 

147 when_created=self.date_30_nov_2022, 

148 _when_added_exact=self.date_30_nov_2022, 

149 _adding_user=self.freda, 

150 ) 

151 ) 

152 

153 for i in range(0, self.num_01_jan_2023_bmi_tasks): 

154 patient = PatientFactory( 

155 _when_added_exact=self.date_01_jan_2023, 

156 ) 

157 all_tasks.append( 

158 BmiFactory( 

159 _group=self.group_a, 

160 patient_id=patient.id, 

161 when_created=self.date_01_jan_2023, 

162 _when_added_exact=self.date_01_jan_2023, 

163 _adding_user=self.freda, 

164 ) 

165 ) 

166 

167 # A task in another group, which won't be seen by group A 

168 self.shabeen = UserFactory(username="shabeen") 

169 

170 UserGroupMembershipFactory( 

171 group_id=self.group_b.id, user_id=self.shabeen.id 

172 ) 

173 

174 patient = PatientFactory( 

175 _when_added_exact=self.date_01_jan_2023, 

176 ) 

177 

178 all_tasks.append( 

179 BmiFactory( 

180 _group=self.group_b, 

181 patient_id=patient.id, 

182 _when_added_exact=self.date_01_jan_2023, 

183 _adding_user=self.shabeen, 

184 ) 

185 ) 

186 self.num_group_b_tasks = 1 

187 

188 if self.via_index: 

189 # There might be a better way of doing this automatically in 

190 # TaskFactory though in the real world indexing is a manual 

191 # process. 

192 for task in all_tasks: 

193 TaskIndexEntry.index_task( 

194 task, self.dbsession, indexed_at_utc=Pendulum.utcnow() 

195 ) 

196 

197 self.report = TaskCountReport() 

198 

199 @property 

200 def via_index(self) -> Optional[bool]: 

201 raise NotImplementedError("via_index must return True or False") 

202 

203 def test_task_counts_by_year_and_month(self) -> None: 

204 year_column = 0 

205 month_column = 1 

206 task_column = 2 

207 num_tasks_added_column = 3 

208 

209 num_nov_2022_phq9_tasks = ( 

210 self.num_01_nov_2022_phq9_tasks + self.num_30_nov_2022_phq9_tasks 

211 ) 

212 

213 # Default is by year and month but better to be explicit 

214 self.req.add_get_params( 

215 { 

216 ViewParam.BY_YEAR: "true", 

217 ViewParam.BY_MONTH: "true", 

218 ViewParam.VIA_INDEX: "true" if self.via_index else "false", 

219 } 

220 ) 

221 

222 self.req._debugging_user = self.freda 

223 result = self.report.get_rows_colnames(self.req) 

224 self.assertEqual( 

225 result.column_names, 

226 [ 

227 "year", 

228 "month", 

229 "task", 

230 "num_tasks_added", 

231 ], 

232 ) 

233 

234 row = 0 

235 

236 self.assertEqual(result.rows[row][year_column], 2023) 

237 self.assertEqual(result.rows[row][month_column], 1) 

238 self.assertEqual(result.rows[row][task_column], "bmi") 

239 self.assertEqual( 

240 result.rows[row][num_tasks_added_column], 

241 self.num_01_jan_2023_bmi_tasks, 

242 ) 

243 

244 row += 1 

245 

246 self.assertEqual(result.rows[row][year_column], 2022) 

247 self.assertEqual(result.rows[row][month_column], 11) 

248 self.assertEqual(result.rows[row][task_column], "bmi") 

249 self.assertEqual( 

250 result.rows[row][num_tasks_added_column], 

251 self.num_01_nov_2022_bmi_tasks, 

252 ) 

253 

254 row += 1 

255 

256 self.assertEqual(result.rows[row][year_column], 2022) 

257 self.assertEqual(result.rows[row][month_column], 11) 

258 self.assertEqual(result.rows[row][task_column], "phq9") 

259 self.assertEqual( 

260 result.rows[row][num_tasks_added_column], 

261 num_nov_2022_phq9_tasks, 

262 ) 

263 

264 row += 1 

265 

266 self.assertEqual(result.rows[row][year_column], 2022) 

267 self.assertEqual(result.rows[row][month_column], 10) 

268 self.assertEqual(result.rows[row][task_column], "bmi") 

269 self.assertEqual( 

270 result.rows[row][num_tasks_added_column], 

271 self.num_01_oct_2022_bmi_tasks, 

272 ) 

273 

274 row += 1 

275 

276 self.assertEqual(len(result.rows), row) 

277 

278 def test_task_counts_by_year(self) -> None: 

279 num_2022_bmi_tasks = ( 

280 self.num_01_oct_2022_bmi_tasks + self.num_01_nov_2022_bmi_tasks 

281 ) 

282 num_2022_phq9_tasks = ( 

283 self.num_01_nov_2022_phq9_tasks + self.num_30_nov_2022_phq9_tasks 

284 ) 

285 num_2023_bmi_tasks = self.num_01_jan_2023_bmi_tasks 

286 

287 year_column = 0 

288 task_column = 1 

289 num_tasks_added_column = 2 

290 

291 self.req.add_get_params( 

292 { 

293 ViewParam.BY_YEAR: "true", 

294 ViewParam.BY_MONTH: "false", 

295 ViewParam.VIA_INDEX: "true" if self.via_index else "false", 

296 } 

297 ) 

298 

299 self.req._debugging_user = self.freda 

300 result = self.report.get_rows_colnames(self.req) 

301 self.assertEqual( 

302 result.column_names, 

303 [ 

304 "year", 

305 "task", 

306 "num_tasks_added", 

307 ], 

308 ) 

309 

310 row = 0 

311 

312 self.assertEqual(result.rows[row][year_column], 2023) 

313 self.assertEqual(result.rows[row][task_column], "bmi") 

314 self.assertEqual( 

315 result.rows[row][num_tasks_added_column], num_2023_bmi_tasks 

316 ) 

317 

318 row += 1 

319 

320 self.assertEqual(result.rows[row][year_column], 2022) 

321 self.assertEqual(result.rows[row][task_column], "bmi") 

322 self.assertEqual( 

323 result.rows[row][num_tasks_added_column], num_2022_bmi_tasks 

324 ) 

325 

326 row += 1 

327 

328 self.assertEqual(result.rows[row][year_column], 2022) 

329 self.assertEqual(result.rows[row][task_column], "phq9") 

330 self.assertEqual( 

331 result.rows[row][num_tasks_added_column], num_2022_phq9_tasks 

332 ) 

333 

334 row += 1 

335 

336 self.assertEqual(len(result.rows), row) 

337 

338 def test_task_counts_by_task(self) -> None: 

339 num_bmi_tasks = ( 

340 self.num_01_oct_2022_bmi_tasks 

341 + self.num_01_nov_2022_bmi_tasks 

342 + self.num_01_jan_2023_bmi_tasks 

343 ) 

344 num_phq9_tasks = ( 

345 self.num_01_nov_2022_phq9_tasks + self.num_30_nov_2022_phq9_tasks 

346 ) 

347 

348 task_column = 0 

349 num_tasks_added_column = 1 

350 

351 self.req.add_get_params( 

352 { 

353 ViewParam.BY_YEAR: "false", 

354 ViewParam.BY_MONTH: "false", 

355 ViewParam.BY_TASK: "true", 

356 ViewParam.VIA_INDEX: "true" if self.via_index else "false", 

357 } 

358 ) 

359 

360 self.req._debugging_user = self.freda 

361 

362 result = self.report.get_rows_colnames(self.req) 

363 self.assertEqual( 

364 result.column_names, 

365 [ 

366 "task", 

367 "num_tasks_added", 

368 ], 

369 ) 

370 

371 row = 0 

372 

373 self.assertEqual(result.rows[row][task_column], "bmi") 

374 self.assertEqual( 

375 result.rows[row][num_tasks_added_column], num_bmi_tasks 

376 ) 

377 

378 row += 1 

379 

380 self.assertEqual(result.rows[row][task_column], "phq9") 

381 self.assertEqual( 

382 result.rows[row][num_tasks_added_column], num_phq9_tasks 

383 ) 

384 

385 row += 1 

386 

387 self.assertEqual(len(result.rows), row) 

388 

389 def test_task_counts_by_user(self) -> None: 

390 user_column = 0 

391 num_tasks_added_column = 1 

392 

393 self.req.add_get_params( 

394 { 

395 ViewParam.BY_YEAR: "false", 

396 ViewParam.BY_MONTH: "false", 

397 ViewParam.BY_TASK: "false", 

398 ViewParam.BY_USER: "true", 

399 ViewParam.VIA_INDEX: "true" if self.via_index else "false", 

400 } 

401 ) 

402 

403 self.req._debugging_user = self.freda 

404 result = self.report.get_rows_colnames(self.req) 

405 self.assertEqual( 

406 result.column_names, 

407 [ 

408 "adding_user_name", 

409 "num_tasks_added", 

410 ], 

411 ) 

412 

413 row = 0 

414 

415 self.assertEqual(result.rows[row][user_column], "freda") 

416 self.assertEqual( 

417 result.rows[row][num_tasks_added_column], self.num_freda_tasks 

418 ) 

419 

420 row += 1 

421 

422 self.assertEqual(result.rows[row][user_column], "jim") 

423 self.assertEqual( 

424 result.rows[row][num_tasks_added_column], self.num_jim_tasks 

425 ) 

426 

427 row += 1 

428 

429 self.assertEqual(len(result.rows), row) 

430 

431 def test_total_task_count_for_superuser(self) -> None: 

432 num_tasks_added_column = 0 

433 

434 self.req.add_get_params( 

435 { 

436 ViewParam.BY_YEAR: "false", 

437 ViewParam.BY_MONTH: "false", 

438 ViewParam.BY_TASK: "false", 

439 ViewParam.BY_USER: "false", 

440 ViewParam.VIA_INDEX: "true" if self.via_index else "false", 

441 } 

442 ) 

443 

444 result = self.report.get_rows_colnames(self.req) 

445 self.assertEqual( 

446 result.column_names, 

447 [ 

448 "num_tasks_added", 

449 ], 

450 ) 

451 

452 row = 0 

453 

454 total_tasks = self.num_group_a_tasks + self.num_group_b_tasks 

455 

456 self.assertEqual(result.rows[row][num_tasks_added_column], total_tasks) 

457 

458 row += 1 

459 

460 self.assertEqual(len(result.rows), row) 

461 

462 def test_task_counts_by_day_of_month(self) -> None: 

463 # Not a very realistic scenario. Would normally 

464 # be combined with month and year but these are 

465 # covered in other tests. 

466 num_day_01_tasks = ( 

467 self.num_01_oct_2022_bmi_tasks 

468 + self.num_01_nov_2022_bmi_tasks 

469 + self.num_01_nov_2022_phq9_tasks 

470 + self.num_01_jan_2023_bmi_tasks 

471 ) 

472 num_day_30_tasks = self.num_30_nov_2022_phq9_tasks 

473 

474 day_of_month_column = 0 

475 num_tasks_added_column = 1 

476 

477 self.req.add_get_params( 

478 { 

479 ViewParam.BY_YEAR: "false", 

480 ViewParam.BY_MONTH: "false", 

481 ViewParam.BY_DAY_OF_MONTH: "true", 

482 ViewParam.BY_TASK: "false", 

483 ViewParam.VIA_INDEX: "true" if self.via_index else "false", 

484 } 

485 ) 

486 

487 self.req._debugging_user = self.freda 

488 result = self.report.get_rows_colnames(self.req) 

489 self.assertEqual( 

490 result.column_names, 

491 [ 

492 "day_of_month", 

493 "num_tasks_added", 

494 ], 

495 ) 

496 

497 row = 0 

498 

499 self.assertEqual(result.rows[row][day_of_month_column], 30) 

500 self.assertEqual( 

501 result.rows[row][num_tasks_added_column], num_day_30_tasks 

502 ) 

503 

504 row += 1 

505 

506 self.assertEqual(result.rows[row][day_of_month_column], 1) 

507 self.assertEqual( 

508 result.rows[row][num_tasks_added_column], num_day_01_tasks 

509 ) 

510 

511 row += 1 

512 

513 self.assertEqual(len(result.rows), row) 

514 

515 

516class TaskCountReportNoIndexTests(TaskCountReportTestBase): 

517 __test__ = True 

518 

519 @property 

520 def via_index(self) -> Optional[bool]: 

521 return False 

522 

523 

524class TaskCountReportWithIndexTests(TaskCountReportTestBase): 

525 __test__ = True 

526 

527 @property 

528 def via_index(self) -> Optional[bool]: 

529 return True