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
« 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
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
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.
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.
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/>.
24===============================================================================
26**Test server reports on CamCOPS tasks.**
28"""
30from typing import Optional
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
44from pendulum import DateTime as Pendulum, datetime
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
54 def setUp(self) -> None:
55 super().setUp()
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)
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
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")
73 self.freda = UserFactory(username="freda")
74 self.jim = UserFactory(username="jim")
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 )
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 )
93 self.num_group_a_tasks = self.num_jim_tasks + self.num_freda_tasks
95 all_tasks = []
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 )
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 )
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 )
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 )
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 )
167 # A task in another group, which won't be seen by group A
168 self.shabeen = UserFactory(username="shabeen")
170 UserGroupMembershipFactory(
171 group_id=self.group_b.id, user_id=self.shabeen.id
172 )
174 patient = PatientFactory(
175 _when_added_exact=self.date_01_jan_2023,
176 )
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
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 )
197 self.report = TaskCountReport()
199 @property
200 def via_index(self) -> Optional[bool]:
201 raise NotImplementedError("via_index must return True or False")
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
209 num_nov_2022_phq9_tasks = (
210 self.num_01_nov_2022_phq9_tasks + self.num_30_nov_2022_phq9_tasks
211 )
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 )
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 )
234 row = 0
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 )
244 row += 1
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 )
254 row += 1
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 )
264 row += 1
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 )
274 row += 1
276 self.assertEqual(len(result.rows), row)
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
287 year_column = 0
288 task_column = 1
289 num_tasks_added_column = 2
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 )
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 )
310 row = 0
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 )
318 row += 1
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 )
326 row += 1
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 )
334 row += 1
336 self.assertEqual(len(result.rows), row)
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 )
348 task_column = 0
349 num_tasks_added_column = 1
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 )
360 self.req._debugging_user = self.freda
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 )
371 row = 0
373 self.assertEqual(result.rows[row][task_column], "bmi")
374 self.assertEqual(
375 result.rows[row][num_tasks_added_column], num_bmi_tasks
376 )
378 row += 1
380 self.assertEqual(result.rows[row][task_column], "phq9")
381 self.assertEqual(
382 result.rows[row][num_tasks_added_column], num_phq9_tasks
383 )
385 row += 1
387 self.assertEqual(len(result.rows), row)
389 def test_task_counts_by_user(self) -> None:
390 user_column = 0
391 num_tasks_added_column = 1
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 )
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 )
413 row = 0
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 )
420 row += 1
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 )
427 row += 1
429 self.assertEqual(len(result.rows), row)
431 def test_total_task_count_for_superuser(self) -> None:
432 num_tasks_added_column = 0
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 )
444 result = self.report.get_rows_colnames(self.req)
445 self.assertEqual(
446 result.column_names,
447 [
448 "num_tasks_added",
449 ],
450 )
452 row = 0
454 total_tasks = self.num_group_a_tasks + self.num_group_b_tasks
456 self.assertEqual(result.rows[row][num_tasks_added_column], total_tasks)
458 row += 1
460 self.assertEqual(len(result.rows), row)
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
474 day_of_month_column = 0
475 num_tasks_added_column = 1
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 )
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 )
497 row = 0
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 )
504 row += 1
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 )
511 row += 1
513 self.assertEqual(len(result.rows), row)
516class TaskCountReportNoIndexTests(TaskCountReportTestBase):
517 __test__ = True
519 @property
520 def via_index(self) -> Optional[bool]:
521 return False
524class TaskCountReportWithIndexTests(TaskCountReportTestBase):
525 __test__ = True
527 @property
528 def via_index(self) -> Optional[bool]:
529 return True