Coverage for cc_modules/tests/client_api_tests.py: 12%
317 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/tests/client_api_tests.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
13 CamCOPS is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 CamCOPS is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
26===============================================================================
28"""
30import json
32import string
33from typing import Dict
35from cardinal_pythonlib.classes import class_attribute_names
36from cardinal_pythonlib.convert import (
37 base64_64format_encode,
38 hex_xformat_encode,
39)
40from cardinal_pythonlib.nhs import generate_random_nhs_number
41from cardinal_pythonlib.sql.literals import sql_quote_string
42from cardinal_pythonlib.text import escape_newlines, unescape_newlines
43from pyramid.response import Response
45from camcops_server.cc_modules.cc_client_api_core import (
46 fail_server_error,
47 fail_unsupported_operation,
48 fail_user_error,
49 ServerErrorException,
50 TabletParam,
51 UserErrorException,
52)
53from camcops_server.cc_modules.cc_convert import decode_values
54from camcops_server.cc_modules.cc_ipuse import IpUse
55from camcops_server.cc_modules.cc_proquint import uuid_from_proquint
56from camcops_server.cc_modules.cc_unittest import (
57 BasicDatabaseTestCase,
58 DemoDatabaseTestCase,
59)
60from camcops_server.cc_modules.cc_user import User
61from camcops_server.cc_modules.cc_version import MINIMUM_TABLET_VERSION
62from camcops_server.cc_modules.cc_validators import (
63 validate_alphanum_underscore,
64)
65from camcops_server.cc_modules.client_api import (
66 client_api,
67 DEVICE_STORED_VAR_TABLENAME_DEFUNCT,
68 FAILURE_CODE,
69 make_single_user_mode_username,
70 Operations,
71 SUCCESS_CODE,
72)
75TEST_NHS_NUMBER = generate_random_nhs_number()
78def get_reply_dict_from_response(response: Response) -> Dict[str, str]:
79 """
80 For unit testing: convert the text in a :class:`Response` back to a
81 dictionary, so we can check it was correct.
82 """
83 txt = str(response)
84 d = {} # type: Dict[str, str]
85 # Format is: "200 OK\r\n<other headers>\r\n\r\n<content>"
86 # There's a blank line between the heads and the body.
87 http_gap = "\r\n\r\n"
88 camcops_linesplit = "\n"
89 camcops_k_v_sep = ":"
90 try:
91 start_of_content = txt.index(http_gap) + len(http_gap)
92 txt = txt[start_of_content:]
93 for line in txt.split(camcops_linesplit):
94 if not line:
95 continue
96 colon_pos = line.index(camcops_k_v_sep)
97 key = line[:colon_pos]
98 value = line[colon_pos + len(camcops_k_v_sep) :] # noqa: E203
99 key = key.strip()
100 value = value.strip()
101 d[key] = value
102 return d
103 except ValueError:
104 return {}
107class ClientApiTests(DemoDatabaseTestCase):
108 """
109 Unit tests.
110 """
112 def test_client_api_basics(self) -> None:
113 self.announce("test_client_api_basics")
115 with self.assertRaises(UserErrorException):
116 fail_user_error("testmsg")
117 with self.assertRaises(ServerErrorException):
118 fail_server_error("testmsg")
119 with self.assertRaises(UserErrorException):
120 fail_unsupported_operation("duffop")
122 # Encoding/decoding tests
123 # data = bytearray("hello")
124 data = b"hello"
125 enc_b64data = base64_64format_encode(data)
126 enc_hexdata = hex_xformat_encode(data)
127 not_enc_1 = "X'012345'"
128 not_enc_2 = "64'aGVsbG8='"
129 teststring = """one, two, 3, 4.5, NULL, 'hello "hi
130 with linebreak"', 'NULL', 'quote''s here', {b}, {h}, {s1}, {s2}"""
131 sql_csv_testdict = {
132 teststring.format(
133 b=enc_b64data,
134 h=enc_hexdata,
135 s1=sql_quote_string(not_enc_1),
136 s2=sql_quote_string(not_enc_2),
137 ): [
138 "one",
139 "two",
140 3,
141 4.5,
142 None,
143 'hello "hi\n with linebreak"',
144 "NULL",
145 "quote's here",
146 data,
147 data,
148 not_enc_1,
149 not_enc_2,
150 ],
151 "": [],
152 }
153 for k, v in sql_csv_testdict.items():
154 r = decode_values(k)
155 self.assertEqual(
156 r,
157 v,
158 "Mismatch! Result: {r!s}\n"
159 "Should have been: {v!s}\n"
160 "Key was: {k!s}".format(r=r, v=v, k=k),
161 )
163 # Newline encoding/decodine
164 ts2 = (
165 "slash \\ newline \n ctrl_r \r special \\n other special \\r "
166 "quote ' doublequote \" "
167 )
168 self.assertEqual(
169 unescape_newlines(escape_newlines(ts2)),
170 ts2,
171 "Bug in escape_newlines() or unescape_newlines()",
172 )
174 # TODO: client_api.ClientApiTests: more tests here... ?
176 def test_client_api_antique_support_1(self) -> None:
177 self.announce("test_client_api_antique_support_1")
178 self.req.fake_request_post_from_dict(
179 {
180 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
181 TabletParam.DEVICE: self.other_device.name,
182 TabletParam.OPERATION: Operations.WHICH_KEYS_TO_SEND,
183 TabletParam.TABLE: DEVICE_STORED_VAR_TABLENAME_DEFUNCT,
184 }
185 )
186 response = client_api(self.req)
187 d = get_reply_dict_from_response(response)
188 self.assertEqual(d[TabletParam.SUCCESS], SUCCESS_CODE)
190 def test_client_api_antique_support_2(self) -> None:
191 self.announce("test_client_api_antique_support_2")
192 self.req.fake_request_post_from_dict(
193 {
194 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
195 TabletParam.DEVICE: self.other_device.name,
196 TabletParam.OPERATION: Operations.WHICH_KEYS_TO_SEND,
197 TabletParam.TABLE: "nonexistent_table",
198 }
199 )
200 response = client_api(self.req)
201 d = get_reply_dict_from_response(response)
202 self.assertEqual(d[TabletParam.SUCCESS], FAILURE_CODE)
204 def test_client_api_antique_support_3(self) -> None:
205 self.announce("test_client_api_antique_support_3")
206 self.req.fake_request_post_from_dict(
207 {
208 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
209 TabletParam.DEVICE: self.other_device.name,
210 TabletParam.OPERATION: Operations.UPLOAD_TABLE,
211 TabletParam.TABLE: DEVICE_STORED_VAR_TABLENAME_DEFUNCT,
212 }
213 )
214 response = client_api(self.req)
215 d = get_reply_dict_from_response(response)
216 self.assertEqual(d[TabletParam.SUCCESS], SUCCESS_CODE)
218 def test_client_api_validators(self) -> None:
219 self.announce("test_client_api_validators")
220 for x in class_attribute_names(Operations):
221 try:
222 validate_alphanum_underscore(x, self.req)
223 except ValueError:
224 self.fail(f"Operations.{x} fails validate_alphanum_underscore")
227class PatientRegistrationTests(BasicDatabaseTestCase):
228 def test_returns_patient_info(self) -> None:
229 import datetime
231 patient = self.create_patient(
232 forename="JO",
233 surname="PATIENT",
234 dob=datetime.date(1958, 4, 19),
235 sex="F",
236 address="Address",
237 gp="GP",
238 other="Other",
239 as_server_patient=True,
240 )
242 self.create_patient_idnum(
243 patient_id=patient.id,
244 which_idnum=self.nhs_iddef.which_idnum,
245 idnum_value=TEST_NHS_NUMBER,
246 as_server_patient=True,
247 )
249 proquint = patient.uuid_as_proquint
251 # For type checker
252 assert proquint is not None
253 assert self.other_device.name is not None
255 self.req.fake_request_post_from_dict(
256 {
257 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
258 TabletParam.DEVICE: self.other_device.name,
259 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
260 TabletParam.PATIENT_PROQUINT: proquint,
261 }
262 )
263 response = client_api(self.req)
264 reply_dict = get_reply_dict_from_response(response)
266 self.assertEqual(
267 reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, msg=reply_dict
268 )
270 patient_dict = json.loads(reply_dict[TabletParam.PATIENT_INFO])[0]
272 self.assertEqual(patient_dict[TabletParam.SURNAME], "PATIENT")
273 self.assertEqual(patient_dict[TabletParam.FORENAME], "JO")
274 self.assertEqual(patient_dict[TabletParam.SEX], "F")
275 self.assertEqual(patient_dict[TabletParam.DOB], "1958-04-19")
276 self.assertEqual(patient_dict[TabletParam.ADDRESS], "Address")
277 self.assertEqual(patient_dict[TabletParam.GP], "GP")
278 self.assertEqual(patient_dict[TabletParam.OTHER], "Other")
279 self.assertEqual(
280 patient_dict[f"idnum{self.nhs_iddef.which_idnum}"], TEST_NHS_NUMBER
281 )
283 def test_creates_user(self) -> None:
284 from camcops_server.cc_modules.cc_taskindex import (
285 PatientIdNumIndexEntry,
286 )
288 patient = self.create_patient(
289 _group_id=self.group.id, as_server_patient=True
290 )
291 idnum = self.create_patient_idnum(
292 patient_id=patient.id,
293 which_idnum=self.nhs_iddef.which_idnum,
294 idnum_value=TEST_NHS_NUMBER,
295 as_server_patient=True,
296 )
297 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession)
299 proquint = patient.uuid_as_proquint
301 # For type checker
302 assert proquint is not None
303 assert self.other_device.name is not None
305 self.req.fake_request_post_from_dict(
306 {
307 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
308 TabletParam.DEVICE: self.other_device.name,
309 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
310 TabletParam.PATIENT_PROQUINT: proquint,
311 }
312 )
313 response = client_api(self.req)
314 reply_dict = get_reply_dict_from_response(response)
316 self.assertEqual(
317 reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, msg=reply_dict
318 )
320 username = reply_dict[TabletParam.USER]
321 self.assertEqual(
322 username,
323 make_single_user_mode_username(
324 self.other_device.name, patient._pk
325 ),
326 )
327 password = reply_dict[TabletParam.PASSWORD]
328 self.assertEqual(len(password), 32)
330 valid_chars = string.ascii_letters + string.digits + string.punctuation
331 self.assertTrue(all(c in valid_chars for c in password))
333 user = (
334 self.req.dbsession.query(User)
335 .filter(User.username == username)
336 .one_or_none()
337 )
338 self.assertIsNotNone(user)
339 self.assertEqual(user.upload_group, patient.group)
340 self.assertTrue(user.auto_generated)
341 self.assertTrue(user.may_register_devices)
342 self.assertTrue(user.may_upload)
344 def test_does_not_create_user_when_name_exists(self) -> None:
345 from camcops_server.cc_modules.cc_taskindex import (
346 PatientIdNumIndexEntry,
347 )
349 patient = self.create_patient(
350 _group_id=self.group.id, as_server_patient=True
351 )
352 idnum = self.create_patient_idnum(
353 patient_id=patient.id,
354 which_idnum=self.nhs_iddef.which_idnum,
355 idnum_value=TEST_NHS_NUMBER,
356 as_server_patient=True,
357 )
358 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession)
360 proquint = patient.uuid_as_proquint
362 user = User(
363 username=make_single_user_mode_username(
364 self.other_device.name, patient._pk
365 )
366 )
367 user.set_password(self.req, "old password")
368 self.dbsession.add(user)
369 self.dbsession.commit()
371 self.req.fake_request_post_from_dict(
372 {
373 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
374 TabletParam.DEVICE: self.other_device.name,
375 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
376 TabletParam.PATIENT_PROQUINT: proquint,
377 }
378 )
379 response = client_api(self.req)
380 reply_dict = get_reply_dict_from_response(response)
382 self.assertEqual(
383 reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, msg=reply_dict
384 )
386 username = reply_dict[TabletParam.USER]
387 self.assertEqual(
388 username,
389 make_single_user_mode_username(
390 self.other_device.name, patient._pk
391 ),
392 )
393 password = reply_dict[TabletParam.PASSWORD]
394 self.assertEqual(len(password), 32)
396 valid_chars = string.ascii_letters + string.digits + string.punctuation
397 self.assertTrue(all(c in valid_chars for c in password))
399 user = (
400 self.req.dbsession.query(User)
401 .filter(User.username == username)
402 .one_or_none()
403 )
404 self.assertIsNotNone(user)
405 self.assertEqual(user.upload_group, patient.group)
406 self.assertTrue(user.auto_generated)
407 self.assertTrue(user.may_register_devices)
408 self.assertTrue(user.may_upload)
410 def test_raises_for_invalid_proquint(self) -> None:
411 # For type checker
412 assert self.other_device.name is not None
414 self.req.fake_request_post_from_dict(
415 {
416 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
417 TabletParam.DEVICE: self.other_device.name,
418 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
419 TabletParam.PATIENT_PROQUINT: "invalid",
420 }
421 )
422 response = client_api(self.req)
423 reply_dict = get_reply_dict_from_response(response)
425 self.assertEqual(
426 reply_dict[TabletParam.SUCCESS], FAILURE_CODE, msg=reply_dict
427 )
428 self.assertIn(
429 "no patient with access key 'invalid'",
430 reply_dict[TabletParam.ERROR],
431 )
433 def test_raises_for_missing_valid_proquint(self) -> None:
434 valid_proquint = "sazom-diliv-navol-hubot-mufur-mamuv-kojus-loluv-v"
436 # Error message is same as for invalid proquint so make sure our
437 # test proquint really is valid (should not raise)
438 uuid_from_proquint(valid_proquint)
440 assert self.other_device.name is not None
442 self.req.fake_request_post_from_dict(
443 {
444 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
445 TabletParam.DEVICE: self.other_device.name,
446 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
447 TabletParam.PATIENT_PROQUINT: valid_proquint,
448 }
449 )
450 response = client_api(self.req)
451 reply_dict = get_reply_dict_from_response(response)
453 self.assertEqual(
454 reply_dict[TabletParam.SUCCESS], FAILURE_CODE, msg=reply_dict
455 )
456 self.assertIn(
457 f"no patient with access key '{valid_proquint}'",
458 reply_dict[TabletParam.ERROR],
459 )
461 def test_raises_when_no_patient_idnums(self) -> None:
462 # In theory this shouldn't be possible in normal operation as the
463 # patient cannot be created without any idnums
464 patient = self.create_patient(as_server_patient=True)
466 proquint = patient.uuid_as_proquint
467 self.req.fake_request_post_from_dict(
468 {
469 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
470 TabletParam.DEVICE: self.other_device.name,
471 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
472 TabletParam.PATIENT_PROQUINT: proquint,
473 }
474 )
476 response = client_api(self.req)
477 reply_dict = get_reply_dict_from_response(response)
478 self.assertEqual(
479 reply_dict[TabletParam.SUCCESS], FAILURE_CODE, msg=reply_dict
480 )
481 self.assertIn(
482 "Patient has no ID numbers", reply_dict[TabletParam.ERROR]
483 )
485 def test_raises_when_patient_not_created_on_server(self) -> None:
486 patient = self.create_patient(
487 _device_id=self.other_device.id, as_server_patient=True
488 )
490 proquint = patient.uuid_as_proquint
491 self.req.fake_request_post_from_dict(
492 {
493 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
494 TabletParam.DEVICE: self.other_device.name,
495 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
496 TabletParam.PATIENT_PROQUINT: proquint,
497 }
498 )
500 response = client_api(self.req)
501 reply_dict = get_reply_dict_from_response(response)
502 self.assertEqual(
503 reply_dict[TabletParam.SUCCESS], FAILURE_CODE, msg=reply_dict
504 )
505 self.assertIn(
506 f"no patient with access key '{proquint}'",
507 reply_dict[TabletParam.ERROR],
508 )
510 def test_returns_ip_use_flags(self) -> None:
511 import datetime
512 from camcops_server.cc_modules.cc_taskindex import (
513 PatientIdNumIndexEntry,
514 )
516 patient = self.create_patient(
517 forename="JO",
518 surname="PATIENT",
519 dob=datetime.date(1958, 4, 19),
520 sex="F",
521 address="Address",
522 gp="GP",
523 other="Other",
524 as_server_patient=True,
525 )
526 idnum = self.create_patient_idnum(
527 patient_id=patient.id,
528 which_idnum=self.nhs_iddef.which_idnum,
529 idnum_value=TEST_NHS_NUMBER,
530 as_server_patient=True,
531 )
532 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession)
534 patient.group.ip_use = IpUse()
536 patient.group.ip_use.commercial = True
537 patient.group.ip_use.clinical = True
538 patient.group.ip_use.educational = False
539 patient.group.ip_use.research = False
541 self.dbsession.add(patient.group)
542 self.dbsession.commit()
544 proquint = patient.uuid_as_proquint
546 # For type checker
547 assert proquint is not None
548 assert self.other_device.name is not None
550 self.req.fake_request_post_from_dict(
551 {
552 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
553 TabletParam.DEVICE: self.other_device.name,
554 TabletParam.OPERATION: Operations.REGISTER_PATIENT,
555 TabletParam.PATIENT_PROQUINT: proquint,
556 }
557 )
558 response = client_api(self.req)
559 reply_dict = get_reply_dict_from_response(response)
561 self.assertEqual(
562 reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, msg=reply_dict
563 )
565 ip_use_info = json.loads(reply_dict[TabletParam.IP_USE_INFO])
567 self.assertEqual(ip_use_info[TabletParam.IP_USE_COMMERCIAL], 1)
568 self.assertEqual(ip_use_info[TabletParam.IP_USE_CLINICAL], 1)
569 self.assertEqual(ip_use_info[TabletParam.IP_USE_EDUCATIONAL], 0)
570 self.assertEqual(ip_use_info[TabletParam.IP_USE_RESEARCH], 0)
573class GetTaskSchedulesTests(BasicDatabaseTestCase):
574 def test_returns_task_schedules(self) -> None:
575 from pendulum import DateTime as Pendulum, Duration, local, parse
577 from camcops_server.cc_modules.cc_taskindex import (
578 PatientIdNumIndexEntry,
579 TaskIndexEntry,
580 )
581 from camcops_server.cc_modules.cc_taskschedule import (
582 PatientTaskSchedule,
583 TaskSchedule,
584 TaskScheduleItem,
585 )
586 from camcops_server.tasks.bmi import Bmi
588 schedule1 = TaskSchedule()
589 schedule1.group_id = self.group.id
590 schedule1.name = "Test 1"
591 self.dbsession.add(schedule1)
593 schedule2 = TaskSchedule()
594 schedule2.group_id = self.group.id
595 self.dbsession.add(schedule2)
596 self.dbsession.commit()
598 item1 = TaskScheduleItem()
599 item1.schedule_id = schedule1.id
600 item1.task_table_name = "phq9"
601 item1.due_from = Duration(days=0)
602 item1.due_by = Duration(days=7)
603 self.dbsession.add(item1)
605 item2 = TaskScheduleItem()
606 item2.schedule_id = schedule1.id
607 item2.task_table_name = "bmi"
608 item2.due_from = Duration(days=0)
609 item2.due_by = Duration(days=8)
610 self.dbsession.add(item2)
612 item3 = TaskScheduleItem()
613 item3.schedule_id = schedule1.id
614 item3.task_table_name = "phq9"
615 item3.due_from = Duration(days=30)
616 item3.due_by = Duration(days=37)
617 self.dbsession.add(item3)
619 item4 = TaskScheduleItem()
620 item4.schedule_id = schedule1.id
621 item4.task_table_name = "gmcpq"
622 item4.due_from = Duration(days=30)
623 item4.due_by = Duration(days=38)
624 self.dbsession.add(item4)
625 self.dbsession.commit()
627 patient = self.create_patient()
628 idnum = self.create_patient_idnum(
629 patient_id=patient.id,
630 which_idnum=self.nhs_iddef.which_idnum,
631 idnum_value=TEST_NHS_NUMBER,
632 )
633 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession)
635 server_patient = self.create_patient(as_server_patient=True)
636 _ = self.create_patient_idnum(
637 patient_id=server_patient.id,
638 which_idnum=self.nhs_iddef.which_idnum,
639 idnum_value=TEST_NHS_NUMBER,
640 as_server_patient=True,
641 )
643 schedule_1 = PatientTaskSchedule()
644 schedule_1.patient_pk = server_patient.pk
645 schedule_1.schedule_id = schedule1.id
646 schedule_1.settings = {
647 "bmi": {"bmi_key": "bmi_value"},
648 "phq9": {"phq9_key": "phq9_value"},
649 }
650 schedule_1.start_datetime = local(2020, 7, 31)
651 self.dbsession.add(schedule_1)
653 schedule_2 = PatientTaskSchedule()
654 schedule_2.patient_pk = server_patient.pk
655 schedule_2.schedule_id = schedule2.id
656 self.dbsession.add(schedule_2)
658 bmi = Bmi()
659 self.apply_standard_task_fields(bmi)
660 bmi.id = 1
661 bmi.height_m = 1.83
662 bmi.mass_kg = 67.57
663 bmi.patient_id = patient.id
664 bmi.when_created = local(2020, 8, 1)
665 self.dbsession.add(bmi)
666 self.dbsession.commit()
667 self.assertTrue(bmi.is_complete())
669 TaskIndexEntry.index_task(
670 bmi, self.dbsession, indexed_at_utc=Pendulum.utcnow()
671 )
672 self.dbsession.commit()
674 proquint = server_patient.uuid_as_proquint
676 # For type checker
677 assert proquint is not None
678 assert self.other_device.name is not None
680 self.req.fake_request_post_from_dict(
681 {
682 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION,
683 TabletParam.DEVICE: self.other_device.name,
684 TabletParam.OPERATION: Operations.GET_TASK_SCHEDULES,
685 TabletParam.PATIENT_PROQUINT: proquint,
686 }
687 )
688 response = client_api(self.req)
689 reply_dict = get_reply_dict_from_response(response)
691 self.assertEqual(
692 reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, msg=reply_dict
693 )
695 task_schedules = json.loads(reply_dict[TabletParam.TASK_SCHEDULES])
697 self.assertEqual(len(task_schedules), 2)
699 s = task_schedules[0]
700 self.assertEqual(s[TabletParam.TASK_SCHEDULE_NAME], "Test 1")
702 schedule_items = s[TabletParam.TASK_SCHEDULE_ITEMS]
703 self.assertEqual(len(schedule_items), 4)
705 phq9_1_sched = schedule_items[0]
706 self.assertEqual(phq9_1_sched[TabletParam.TABLE], "phq9")
707 self.assertEqual(
708 phq9_1_sched[TabletParam.SETTINGS], {"phq9_key": "phq9_value"}
709 )
710 self.assertEqual(
711 parse(phq9_1_sched[TabletParam.DUE_FROM]), local(2020, 7, 31)
712 )
713 self.assertEqual(
714 parse(phq9_1_sched[TabletParam.DUE_BY]), local(2020, 8, 7)
715 )
716 self.assertFalse(phq9_1_sched[TabletParam.COMPLETE])
717 self.assertFalse(phq9_1_sched[TabletParam.ANONYMOUS])
719 bmi_sched = schedule_items[1]
720 self.assertEqual(bmi_sched[TabletParam.TABLE], "bmi")
721 self.assertEqual(
722 bmi_sched[TabletParam.SETTINGS], {"bmi_key": "bmi_value"}
723 )
724 self.assertEqual(
725 parse(bmi_sched[TabletParam.DUE_FROM]), local(2020, 7, 31)
726 )
727 self.assertEqual(
728 parse(bmi_sched[TabletParam.DUE_BY]), local(2020, 8, 8)
729 )
730 self.assertTrue(bmi_sched[TabletParam.COMPLETE])
731 self.assertFalse(bmi_sched[TabletParam.ANONYMOUS])
733 phq9_2_sched = schedule_items[2]
734 self.assertEqual(phq9_2_sched[TabletParam.TABLE], "phq9")
735 self.assertEqual(
736 phq9_2_sched[TabletParam.SETTINGS], {"phq9_key": "phq9_value"}
737 )
738 self.assertEqual(
739 parse(phq9_2_sched[TabletParam.DUE_FROM]), local(2020, 8, 30)
740 )
741 self.assertEqual(
742 parse(phq9_2_sched[TabletParam.DUE_BY]), local(2020, 9, 6)
743 )
744 self.assertFalse(phq9_2_sched[TabletParam.COMPLETE])
745 self.assertFalse(phq9_2_sched[TabletParam.ANONYMOUS])
747 # GMCPQ
748 gmcpq_sched = schedule_items[3]
749 self.assertTrue(gmcpq_sched[TabletParam.ANONYMOUS])