Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/tests/client_api_tests.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

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

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

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

15 (at your option) any later version. 

16 

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

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

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

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

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

24 

25=============================================================================== 

26 

27""" 

28 

29import json 

30# from pprint import pformat 

31import string 

32 

33from cardinal_pythonlib.convert import ( 

34 base64_64format_encode, 

35 hex_xformat_encode, 

36) 

37from cardinal_pythonlib.sql.literals import sql_quote_string 

38from cardinal_pythonlib.text import escape_newlines, unescape_newlines 

39 

40from camcops_server.cc_modules.cc_client_api_core import ( 

41 fail_server_error, 

42 fail_unsupported_operation, 

43 fail_user_error, 

44 ServerErrorException, 

45 TabletParam, 

46 UserErrorException, 

47) 

48from camcops_server.cc_modules.cc_convert import ( 

49 decode_values, 

50) 

51from camcops_server.cc_modules.cc_ipuse import IpUse 

52from camcops_server.cc_modules.cc_proquint import ( 

53 uuid_from_proquint, 

54) 

55from camcops_server.cc_modules.cc_taskindex import update_indexes_and_push_exports # noqa 

56from camcops_server.cc_modules.cc_testhelpers import class_attribute_names 

57from camcops_server.cc_modules.cc_unittest import DemoDatabaseTestCase 

58from camcops_server.cc_modules.cc_user import User 

59from camcops_server.cc_modules.cc_version import ( 

60 MINIMUM_TABLET_VERSION, 

61) 

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 get_reply_dict_from_response, 

70 make_single_user_mode_username, 

71 Operations, 

72 SUCCESS_CODE, 

73 TEST_NHS_NUMBER, 

74) 

75 

76 

77class ClientApiTests(DemoDatabaseTestCase): 

78 """ 

79 Unit tests. 

80 """ 

81 def test_client_api_basics(self) -> None: 

82 self.announce("test_client_api_basics") 

83 

84 with self.assertRaises(UserErrorException): 

85 fail_user_error("testmsg") 

86 with self.assertRaises(ServerErrorException): 

87 fail_server_error("testmsg") 

88 with self.assertRaises(UserErrorException): 

89 fail_unsupported_operation("duffop") 

90 

91 # Encoding/decoding tests 

92 # data = bytearray("hello") 

93 data = b"hello" 

94 enc_b64data = base64_64format_encode(data) 

95 enc_hexdata = hex_xformat_encode(data) 

96 not_enc_1 = "X'012345'" 

97 not_enc_2 = "64'aGVsbG8='" 

98 teststring = """one, two, 3, 4.5, NULL, 'hello "hi 

99 with linebreak"', 'NULL', 'quote''s here', {b}, {h}, {s1}, {s2}""" 

100 sql_csv_testdict = { 

101 teststring.format( 

102 b=enc_b64data, 

103 h=enc_hexdata, 

104 s1=sql_quote_string(not_enc_1), 

105 s2=sql_quote_string(not_enc_2), 

106 ): [ 

107 "one", 

108 "two", 

109 3, 

110 4.5, 

111 None, 

112 'hello "hi\n with linebreak"', 

113 "NULL", 

114 "quote's here", 

115 data, 

116 data, 

117 not_enc_1, 

118 not_enc_2, 

119 ], 

120 "": [], 

121 } 

122 for k, v in sql_csv_testdict.items(): 

123 r = decode_values(k) 

124 self.assertEqual(r, v, "Mismatch! Result: {r!s}\n" 

125 "Should have been: {v!s}\n" 

126 "Key was: {k!s}".format(r=r, v=v, k=k)) 

127 

128 # Newline encoding/decodine 

129 ts2 = "slash \\ newline \n ctrl_r \r special \\n other special \\r " \ 

130 "quote ' doublequote \" " 

131 self.assertEqual(unescape_newlines(escape_newlines(ts2)), ts2, 

132 "Bug in escape_newlines() or unescape_newlines()") 

133 

134 # TODO: client_api.ClientApiTests: more tests here... ? 

135 

136 def test_client_api_antique_support_1(self) -> None: 

137 self.announce("test_client_api_antique_support_1") 

138 self.req.fake_request_post_from_dict({ 

139 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

140 TabletParam.DEVICE: self.other_device.name, 

141 TabletParam.OPERATION: Operations.WHICH_KEYS_TO_SEND, 

142 TabletParam.TABLE: DEVICE_STORED_VAR_TABLENAME_DEFUNCT, 

143 }) 

144 response = client_api(self.req) 

145 d = get_reply_dict_from_response(response) 

146 self.assertEqual(d[TabletParam.SUCCESS], SUCCESS_CODE) 

147 

148 def test_client_api_antique_support_2(self) -> None: 

149 self.announce("test_client_api_antique_support_2") 

150 self.req.fake_request_post_from_dict({ 

151 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

152 TabletParam.DEVICE: self.other_device.name, 

153 TabletParam.OPERATION: Operations.WHICH_KEYS_TO_SEND, 

154 TabletParam.TABLE: "nonexistent_table", 

155 }) 

156 response = client_api(self.req) 

157 d = get_reply_dict_from_response(response) 

158 self.assertEqual(d[TabletParam.SUCCESS], FAILURE_CODE) 

159 

160 def test_client_api_antique_support_3(self) -> None: 

161 self.announce("test_client_api_antique_support_3") 

162 self.req.fake_request_post_from_dict({ 

163 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

164 TabletParam.DEVICE: self.other_device.name, 

165 TabletParam.OPERATION: Operations.UPLOAD_TABLE, 

166 TabletParam.TABLE: DEVICE_STORED_VAR_TABLENAME_DEFUNCT, 

167 }) 

168 response = client_api(self.req) 

169 d = get_reply_dict_from_response(response) 

170 self.assertEqual(d[TabletParam.SUCCESS], SUCCESS_CODE) 

171 

172 def test_client_api_validators(self) -> None: 

173 self.announce("test_client_api_validators") 

174 for x in class_attribute_names(Operations): 

175 try: 

176 validate_alphanum_underscore(x, self.req) 

177 except ValueError: 

178 self.fail(f"Operations.{x} fails validate_alphanum_underscore") 

179 

180 

181class PatientRegistrationTests(DemoDatabaseTestCase): 

182 def create_tasks(self) -> None: 

183 # Speed things up a bit 

184 pass 

185 

186 def test_returns_patient_info(self) -> None: 

187 import datetime 

188 patient = self.create_patient( 

189 forename="JO", surname="PATIENT", dob=datetime.date(1958, 4, 19), 

190 sex="F", address="Address", gp="GP", other="Other", 

191 as_server_patient=True 

192 ) 

193 

194 self.create_patient_idnum( 

195 patient_id=patient.id, 

196 which_idnum=self.nhs_iddef.which_idnum, 

197 idnum_value=TEST_NHS_NUMBER, 

198 as_server_patient=True 

199 ) 

200 

201 proquint = patient.uuid_as_proquint 

202 

203 # For type checker 

204 assert proquint is not None 

205 assert self.other_device.name is not None 

206 

207 self.req.fake_request_post_from_dict({ 

208 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

209 TabletParam.DEVICE: self.other_device.name, 

210 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

211 TabletParam.PATIENT_PROQUINT: proquint, 

212 }) 

213 response = client_api(self.req) 

214 reply_dict = get_reply_dict_from_response(response) 

215 

216 self.assertEqual(reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, 

217 msg=reply_dict) 

218 

219 patient_dict = json.loads(reply_dict[TabletParam.PATIENT_INFO])[0] 

220 

221 self.assertEqual(patient_dict[TabletParam.SURNAME], "PATIENT") 

222 self.assertEqual(patient_dict[TabletParam.FORENAME], "JO") 

223 self.assertEqual(patient_dict[TabletParam.SEX], "F") 

224 self.assertEqual(patient_dict[TabletParam.DOB], "1958-04-19") 

225 self.assertEqual(patient_dict[TabletParam.ADDRESS], "Address") 

226 self.assertEqual(patient_dict[TabletParam.GP], "GP") 

227 self.assertEqual(patient_dict[TabletParam.OTHER], "Other") 

228 self.assertEqual(patient_dict[f"idnum{self.nhs_iddef.which_idnum}"], 

229 TEST_NHS_NUMBER) 

230 

231 def test_creates_user(self) -> None: 

232 from camcops_server.cc_modules.cc_taskindex import ( 

233 PatientIdNumIndexEntry, 

234 ) 

235 patient = self.create_patient(_group_id=self.group.id, 

236 as_server_patient=True) 

237 idnum = self.create_patient_idnum( 

238 patient_id=patient.id, 

239 which_idnum=self.nhs_iddef.which_idnum, 

240 idnum_value=TEST_NHS_NUMBER, 

241 as_server_patient=True 

242 ) 

243 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession) 

244 

245 proquint = patient.uuid_as_proquint 

246 

247 # For type checker 

248 assert proquint is not None 

249 assert self.other_device.name is not None 

250 

251 self.req.fake_request_post_from_dict({ 

252 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

253 TabletParam.DEVICE: self.other_device.name, 

254 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

255 TabletParam.PATIENT_PROQUINT: proquint, 

256 }) 

257 response = client_api(self.req) 

258 reply_dict = get_reply_dict_from_response(response) 

259 

260 self.assertEqual(reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, 

261 msg=reply_dict) 

262 

263 username = reply_dict[TabletParam.USER] 

264 self.assertEqual( 

265 username, 

266 make_single_user_mode_username(self.other_device.name, patient._pk) 

267 ) 

268 password = reply_dict[TabletParam.PASSWORD] 

269 self.assertEqual(len(password), 32) 

270 

271 valid_chars = string.ascii_letters + string.digits + string.punctuation 

272 self.assertTrue(all(c in valid_chars for c in password)) 

273 

274 user = self.req.dbsession.query(User).filter( 

275 User.username == username).one_or_none() 

276 self.assertIsNotNone(user) 

277 self.assertEqual(user.upload_group, patient.group) 

278 self.assertTrue(user.auto_generated) 

279 self.assertTrue(user.may_register_devices) 

280 self.assertTrue(user.may_upload) 

281 

282 def test_does_not_create_user_when_name_exists(self) -> None: 

283 from camcops_server.cc_modules.cc_taskindex import ( 

284 PatientIdNumIndexEntry, 

285 ) 

286 patient = self.create_patient(_group_id=self.group.id, 

287 as_server_patient=True) 

288 idnum = self.create_patient_idnum( 

289 patient_id=patient.id, 

290 which_idnum=self.nhs_iddef.which_idnum, 

291 idnum_value=TEST_NHS_NUMBER, 

292 as_server_patient=True 

293 ) 

294 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession) 

295 

296 proquint = patient.uuid_as_proquint 

297 

298 user = User( 

299 username=make_single_user_mode_username( 

300 self.other_device.name, patient._pk 

301 ) 

302 ) 

303 user.set_password(self.req, "old password") 

304 self.dbsession.add(user) 

305 self.dbsession.commit() 

306 

307 self.req.fake_request_post_from_dict({ 

308 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

309 TabletParam.DEVICE: self.other_device.name, 

310 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

311 TabletParam.PATIENT_PROQUINT: proquint, 

312 }) 

313 response = client_api(self.req) 

314 reply_dict = get_reply_dict_from_response(response) 

315 

316 self.assertEqual(reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, 

317 msg=reply_dict) 

318 

319 username = reply_dict[TabletParam.USER] 

320 self.assertEqual( 

321 username, 

322 make_single_user_mode_username(self.other_device.name, patient._pk) 

323 ) 

324 password = reply_dict[TabletParam.PASSWORD] 

325 self.assertEqual(len(password), 32) 

326 

327 valid_chars = string.ascii_letters + string.digits + string.punctuation 

328 self.assertTrue(all(c in valid_chars for c in password)) 

329 

330 user = self.req.dbsession.query(User).filter( 

331 User.username == username).one_or_none() 

332 self.assertIsNotNone(user) 

333 self.assertEqual(user.upload_group, patient.group) 

334 self.assertTrue(user.auto_generated) 

335 self.assertTrue(user.may_register_devices) 

336 self.assertTrue(user.may_upload) 

337 

338 def test_raises_for_invalid_proquint(self) -> None: 

339 # For type checker 

340 assert self.other_device.name is not None 

341 

342 self.req.fake_request_post_from_dict({ 

343 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

344 TabletParam.DEVICE: self.other_device.name, 

345 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

346 TabletParam.PATIENT_PROQUINT: "invalid", 

347 }) 

348 response = client_api(self.req) 

349 reply_dict = get_reply_dict_from_response(response) 

350 

351 self.assertEqual(reply_dict[TabletParam.SUCCESS], FAILURE_CODE, 

352 msg=reply_dict) 

353 self.assertIn("no patient with access key 'invalid'", 

354 reply_dict[TabletParam.ERROR]) 

355 

356 def test_raises_for_missing_valid_proquint(self) -> None: 

357 valid_proquint = "sazom-diliv-navol-hubot-mufur-mamuv-kojus-loluv-v" 

358 

359 # Error message is same as for invalid proquint so make sure our 

360 # test proquint really is valid (should not raise) 

361 uuid_from_proquint(valid_proquint) 

362 

363 assert self.other_device.name is not None 

364 

365 self.req.fake_request_post_from_dict({ 

366 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

367 TabletParam.DEVICE: self.other_device.name, 

368 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

369 TabletParam.PATIENT_PROQUINT: valid_proquint, 

370 }) 

371 response = client_api(self.req) 

372 reply_dict = get_reply_dict_from_response(response) 

373 

374 self.assertEqual(reply_dict[TabletParam.SUCCESS], FAILURE_CODE, 

375 msg=reply_dict) 

376 self.assertIn(f"no patient with access key '{valid_proquint}'", 

377 reply_dict[TabletParam.ERROR]) 

378 

379 def test_raises_when_no_patient_idnums(self) -> None: 

380 # In theory this shouldn't be possible in normal operation as the 

381 # patient cannot be created without any idnums 

382 patient = self.create_patient(as_server_patient=True) 

383 

384 proquint = patient.uuid_as_proquint 

385 self.req.fake_request_post_from_dict({ 

386 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

387 TabletParam.DEVICE: self.other_device.name, 

388 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

389 TabletParam.PATIENT_PROQUINT: proquint, 

390 }) 

391 

392 response = client_api(self.req) 

393 reply_dict = get_reply_dict_from_response(response) 

394 self.assertEqual(reply_dict[TabletParam.SUCCESS], FAILURE_CODE, 

395 msg=reply_dict) 

396 self.assertIn("Patient has no ID numbers", 

397 reply_dict[TabletParam.ERROR]) 

398 

399 def test_raises_when_patient_not_created_on_server(self) -> None: 

400 patient = self.create_patient(_device_id=self.other_device.id, 

401 as_server_patient=True) 

402 

403 proquint = patient.uuid_as_proquint 

404 self.req.fake_request_post_from_dict({ 

405 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

406 TabletParam.DEVICE: self.other_device.name, 

407 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

408 TabletParam.PATIENT_PROQUINT: proquint, 

409 }) 

410 

411 response = client_api(self.req) 

412 reply_dict = get_reply_dict_from_response(response) 

413 self.assertEqual(reply_dict[TabletParam.SUCCESS], FAILURE_CODE, 

414 msg=reply_dict) 

415 self.assertIn(f"no patient with access key '{proquint}'", 

416 reply_dict[TabletParam.ERROR]) 

417 

418 def test_returns_ip_use_flags(self) -> None: 

419 import datetime 

420 from camcops_server.cc_modules.cc_taskindex import ( 

421 PatientIdNumIndexEntry, 

422 ) 

423 

424 patient = self.create_patient( 

425 forename="JO", surname="PATIENT", dob=datetime.date(1958, 4, 19), 

426 sex="F", address="Address", gp="GP", other="Other", 

427 as_server_patient=True 

428 ) 

429 idnum = self.create_patient_idnum( 

430 patient_id=patient.id, 

431 which_idnum=self.nhs_iddef.which_idnum, 

432 idnum_value=TEST_NHS_NUMBER, 

433 as_server_patient=True 

434 ) 

435 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession) 

436 

437 patient.group.ip_use = IpUse() 

438 

439 patient.group.ip_use.commercial = True 

440 patient.group.ip_use.clinical = True 

441 patient.group.ip_use.educational = False 

442 patient.group.ip_use.research = False 

443 

444 self.dbsession.add(patient.group) 

445 self.dbsession.commit() 

446 

447 proquint = patient.uuid_as_proquint 

448 

449 # For type checker 

450 assert proquint is not None 

451 assert self.other_device.name is not None 

452 

453 self.req.fake_request_post_from_dict({ 

454 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

455 TabletParam.DEVICE: self.other_device.name, 

456 TabletParam.OPERATION: Operations.REGISTER_PATIENT, 

457 TabletParam.PATIENT_PROQUINT: proquint, 

458 }) 

459 response = client_api(self.req) 

460 reply_dict = get_reply_dict_from_response(response) 

461 

462 self.assertEqual(reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, 

463 msg=reply_dict) 

464 

465 ip_use_info = json.loads(reply_dict[TabletParam.IP_USE_INFO]) 

466 

467 self.assertEqual(ip_use_info[TabletParam.IP_USE_COMMERCIAL], 1) 

468 self.assertEqual(ip_use_info[TabletParam.IP_USE_CLINICAL], 1) 

469 self.assertEqual(ip_use_info[TabletParam.IP_USE_EDUCATIONAL], 0) 

470 self.assertEqual(ip_use_info[TabletParam.IP_USE_RESEARCH], 0) 

471 

472 

473class GetTaskSchedulesTests(DemoDatabaseTestCase): 

474 def create_tasks(self) -> None: 

475 # Speed things up a bit 

476 pass 

477 

478 def test_returns_task_schedules(self) -> None: 

479 from pendulum import DateTime as Pendulum, Duration, local, parse 

480 

481 from camcops_server.cc_modules.cc_taskindex import ( 

482 PatientIdNumIndexEntry, 

483 TaskIndexEntry, 

484 ) 

485 from camcops_server.cc_modules.cc_taskschedule import ( 

486 PatientTaskSchedule, 

487 TaskSchedule, 

488 TaskScheduleItem 

489 ) 

490 from camcops_server.tasks.bmi import Bmi 

491 

492 schedule1 = TaskSchedule() 

493 schedule1.group_id = self.group.id 

494 schedule1.name = "Test 1" 

495 self.dbsession.add(schedule1) 

496 

497 schedule2 = TaskSchedule() 

498 schedule2.group_id = self.group.id 

499 self.dbsession.add(schedule2) 

500 self.dbsession.commit() 

501 

502 item1 = TaskScheduleItem() 

503 item1.schedule_id = schedule1.id 

504 item1.task_table_name = "phq9" 

505 item1.due_from = Duration(days=0) 

506 item1.due_by = Duration(days=7) 

507 self.dbsession.add(item1) 

508 

509 item2 = TaskScheduleItem() 

510 item2.schedule_id = schedule1.id 

511 item2.task_table_name = "bmi" 

512 item2.due_from = Duration(days=0) 

513 item2.due_by = Duration(days=8) 

514 self.dbsession.add(item2) 

515 

516 item3 = TaskScheduleItem() 

517 item3.schedule_id = schedule1.id 

518 item3.task_table_name = "phq9" 

519 item3.due_from = Duration(days=30) 

520 item3.due_by = Duration(days=37) 

521 self.dbsession.add(item3) 

522 

523 item4 = TaskScheduleItem() 

524 item4.schedule_id = schedule1.id 

525 item4.task_table_name = "gmcpq" 

526 item4.due_from = Duration(days=30) 

527 item4.due_by = Duration(days=38) 

528 self.dbsession.add(item4) 

529 self.dbsession.commit() 

530 

531 patient = self.create_patient() 

532 idnum = self.create_patient_idnum( 

533 patient_id=patient.id, 

534 which_idnum=self.nhs_iddef.which_idnum, 

535 idnum_value=TEST_NHS_NUMBER 

536 ) 

537 PatientIdNumIndexEntry.index_idnum(idnum, self.dbsession) 

538 

539 server_patient = self.create_patient(as_server_patient=True) 

540 _ = self.create_patient_idnum( 

541 patient_id=server_patient.id, 

542 which_idnum=self.nhs_iddef.which_idnum, 

543 idnum_value=TEST_NHS_NUMBER, 

544 as_server_patient=True 

545 ) 

546 

547 schedule_1 = PatientTaskSchedule() 

548 schedule_1.patient_pk = server_patient.pk 

549 schedule_1.schedule_id = schedule1.id 

550 schedule_1.settings = { 

551 "bmi": { 

552 "bmi_key": "bmi_value", 

553 }, 

554 "phq9": { 

555 "phq9_key": "phq9_value", 

556 } 

557 } 

558 schedule_1.start_datetime = local(2020, 7, 31) 

559 self.dbsession.add(schedule_1) 

560 

561 schedule_2 = PatientTaskSchedule() 

562 schedule_2.patient_pk = server_patient.pk 

563 schedule_2.schedule_id = schedule2.id 

564 self.dbsession.add(schedule_2) 

565 

566 bmi = Bmi() 

567 self.apply_standard_task_fields(bmi) 

568 bmi.id = 1 

569 bmi.height_m = 1.83 

570 bmi.mass_kg = 67.57 

571 bmi.patient_id = patient.id 

572 bmi.when_created = local(2020, 8, 1) 

573 self.dbsession.add(bmi) 

574 self.dbsession.commit() 

575 self.assertTrue(bmi.is_complete()) 

576 

577 TaskIndexEntry.index_task( 

578 bmi, 

579 self.dbsession, 

580 indexed_at_utc=Pendulum.utcnow() 

581 ) 

582 self.dbsession.commit() 

583 

584 proquint = server_patient.uuid_as_proquint 

585 

586 # For type checker 

587 assert proquint is not None 

588 assert self.other_device.name is not None 

589 

590 self.req.fake_request_post_from_dict({ 

591 TabletParam.CAMCOPS_VERSION: MINIMUM_TABLET_VERSION, 

592 TabletParam.DEVICE: self.other_device.name, 

593 TabletParam.OPERATION: Operations.GET_TASK_SCHEDULES, 

594 TabletParam.PATIENT_PROQUINT: proquint, 

595 }) 

596 response = client_api(self.req) 

597 reply_dict = get_reply_dict_from_response(response) 

598 

599 self.assertEqual(reply_dict[TabletParam.SUCCESS], SUCCESS_CODE, 

600 msg=reply_dict) 

601 

602 task_schedules = json.loads(reply_dict[TabletParam.TASK_SCHEDULES]) 

603 

604 self.assertEqual(len(task_schedules), 2) 

605 

606 s = task_schedules[0] 

607 self.assertEqual(s[TabletParam.TASK_SCHEDULE_NAME], "Test 1") 

608 

609 schedule_items = s[TabletParam.TASK_SCHEDULE_ITEMS] 

610 self.assertEqual(len(schedule_items), 4) 

611 

612 phq9_1_sched = schedule_items[0] 

613 self.assertEqual(phq9_1_sched[TabletParam.TABLE], "phq9") 

614 self.assertEqual(phq9_1_sched[TabletParam.SETTINGS], { 

615 "phq9_key": "phq9_value" 

616 }) 

617 self.assertEqual(parse(phq9_1_sched[TabletParam.DUE_FROM]), 

618 local(2020, 7, 31)) 

619 self.assertEqual(parse(phq9_1_sched[TabletParam.DUE_BY]), 

620 local(2020, 8, 7)) 

621 self.assertFalse(phq9_1_sched[TabletParam.COMPLETE]) 

622 self.assertFalse(phq9_1_sched[TabletParam.ANONYMOUS]) 

623 

624 bmi_sched = schedule_items[1] 

625 self.assertEqual(bmi_sched[TabletParam.TABLE], "bmi") 

626 self.assertEqual(bmi_sched[TabletParam.SETTINGS], { 

627 "bmi_key": "bmi_value", 

628 }) 

629 self.assertEqual(parse(bmi_sched[TabletParam.DUE_FROM]), 

630 local(2020, 7, 31)) 

631 self.assertEqual(parse(bmi_sched[TabletParam.DUE_BY]), 

632 local(2020, 8, 8)) 

633 self.assertTrue(bmi_sched[TabletParam.COMPLETE]) 

634 self.assertFalse(bmi_sched[TabletParam.ANONYMOUS]) 

635 

636 phq9_2_sched = schedule_items[2] 

637 self.assertEqual(phq9_2_sched[TabletParam.TABLE], "phq9") 

638 self.assertEqual(phq9_2_sched[TabletParam.SETTINGS], { 

639 "phq9_key": "phq9_value" 

640 }) 

641 self.assertEqual(parse(phq9_2_sched[TabletParam.DUE_FROM]), 

642 local(2020, 8, 30)) 

643 self.assertEqual(parse(phq9_2_sched[TabletParam.DUE_BY]), 

644 local(2020, 9, 6)) 

645 self.assertFalse(phq9_2_sched[TabletParam.COMPLETE]) 

646 self.assertFalse(phq9_2_sched[TabletParam.ANONYMOUS]) 

647 

648 # GMCPQ 

649 gmcpq_sched = schedule_items[3] 

650 self.assertTrue(gmcpq_sched[TabletParam.ANONYMOUS])