Coverage for tasks/mds_updrs.py: 86%

118 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-08 23:14 +0000

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/mds_updrs.py 

5 

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

7 

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

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

10 

11 This file is part of CamCOPS. 

12 

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. 

17 

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. 

22 

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/>. 

25 

26=============================================================================== 

27 

28""" 

29 

30from typing import List 

31 

32from sqlalchemy.sql.sqltypes import Boolean, Float, Integer 

33 

34from camcops_server.cc_modules.cc_cache import cache_region_static, fkg 

35from camcops_server.cc_modules.cc_constants import ( 

36 CssClass, 

37 DATA_COLLECTION_ONLY_DIV, 

38) 

39from camcops_server.cc_modules.cc_html import tr_qa 

40from camcops_server.cc_modules.cc_request import CamcopsRequest 

41from camcops_server.cc_modules.cc_snomed import SnomedExpression, SnomedLookup 

42from camcops_server.cc_modules.cc_sqla_coltypes import ( 

43 BIT_CHECKER, 

44 CamcopsColumn, 

45 gen_camcops_columns, 

46 get_camcops_column_attr_names, 

47 ZERO_TO_TWO_CHECKER, 

48 ZERO_TO_FOUR_CHECKER, 

49 ZERO_TO_FIVE_CHECKER, 

50) 

51from camcops_server.cc_modules.cc_task import ( 

52 Task, 

53 TaskHasPatientMixin, 

54 TaskHasClinicianMixin, 

55) 

56 

57 

58# ============================================================================= 

59# MDS-UPDRS (crippled) 

60# ============================================================================= 

61 

62 

63class MdsUpdrs(TaskHasClinicianMixin, TaskHasPatientMixin, Task): 

64 """ 

65 Server implementation of the MDS-UPDRS task. 

66 

67 Has clinician as of v2.0.0. 

68 """ 

69 

70 __tablename__ = "mds_updrs" 

71 shortname = "MDS-UPDRS" 

72 

73 main_cmt = " (0 normal, 1 slight, 2 mild, 3 moderate, 4 severe)" 

74 main_pv = ZERO_TO_FOUR_CHECKER 

75 informant_cmt = " (0 patient, 1 caregiver, 2 both)" 

76 informant_pv = ZERO_TO_TWO_CHECKER 

77 yn_cmt = " (0 no, 1 yes)" 

78 on_off_cmt = " (0 off, 1 on)" 

79 hy_pv = ZERO_TO_FIVE_CHECKER 

80 

81 # Part I 

82 q1a = CamcopsColumn( 

83 "q1a", 

84 Integer, 

85 permitted_value_checker=informant_pv, 

86 comment="Part I: informant for Q1.1-1.6" + informant_cmt, 

87 ) 

88 q1_1 = CamcopsColumn( 

89 "q1_1", 

90 Integer, 

91 permitted_value_checker=main_pv, 

92 comment="Part I, Q1.1 " + main_cmt, 

93 ) 

94 q1_2 = CamcopsColumn( 

95 "q1_2", 

96 Integer, 

97 permitted_value_checker=main_pv, 

98 comment="Part I, Q1.2 " + main_cmt, 

99 ) 

100 q1_3 = CamcopsColumn( 

101 "q1_3", 

102 Integer, 

103 permitted_value_checker=main_pv, 

104 comment="Part I, Q1.3 " + main_cmt, 

105 ) 

106 q1_4 = CamcopsColumn( 

107 "q1_4", 

108 Integer, 

109 permitted_value_checker=main_pv, 

110 comment="Part I, Q1.4 " + main_cmt, 

111 ) 

112 q1_5 = CamcopsColumn( 

113 "q1_5", 

114 Integer, 

115 permitted_value_checker=main_pv, 

116 comment="Part I, Q1.5 " + main_cmt, 

117 ) 

118 q1_6 = CamcopsColumn( 

119 "q1_6", 

120 Integer, 

121 permitted_value_checker=main_pv, 

122 comment="Part I, Q1.6 " + main_cmt, 

123 ) 

124 q1_6a = CamcopsColumn( 

125 "q1_6a", 

126 Integer, 

127 permitted_value_checker=informant_pv, 

128 comment="Part I, Q1.6a: informant for Q1.7-1.13" + informant_cmt, 

129 ) 

130 q1_7 = CamcopsColumn( 

131 "q1_7", 

132 Integer, 

133 permitted_value_checker=main_pv, 

134 comment="Part I, Q1.7 " + main_cmt, 

135 ) 

136 q1_8 = CamcopsColumn( 

137 "q1_8", 

138 Integer, 

139 permitted_value_checker=main_pv, 

140 comment="Part I, Q1.8 " + main_cmt, 

141 ) 

142 q1_9 = CamcopsColumn( 

143 "q1_9", 

144 Integer, 

145 permitted_value_checker=main_pv, 

146 comment="Part I, Q1.9 " + main_cmt, 

147 ) 

148 q1_10 = CamcopsColumn( 

149 "q1_10", 

150 Integer, 

151 permitted_value_checker=main_pv, 

152 comment="Part I, Q1.10 " + main_cmt, 

153 ) 

154 q1_11 = CamcopsColumn( 

155 "q1_11", 

156 Integer, 

157 permitted_value_checker=main_pv, 

158 comment="Part I, Q1.11 " + main_cmt, 

159 ) 

160 q1_12 = CamcopsColumn( 

161 "q1_12", 

162 Integer, 

163 permitted_value_checker=main_pv, 

164 comment="Part I, Q1.12 " + main_cmt, 

165 ) 

166 q1_13 = CamcopsColumn( 

167 "q1_13", 

168 Integer, 

169 permitted_value_checker=main_pv, 

170 comment="Part I, Q1.13 " + main_cmt, 

171 ) 

172 

173 # Part II 

174 q2_1 = CamcopsColumn( 

175 "q2_1", 

176 Integer, 

177 permitted_value_checker=main_pv, 

178 comment="Part II, Q2.1 " + main_cmt, 

179 ) 

180 q2_2 = CamcopsColumn( 

181 "q2_2", 

182 Integer, 

183 permitted_value_checker=main_pv, 

184 comment="Part II, Q2.2 " + main_cmt, 

185 ) 

186 q2_3 = CamcopsColumn( 

187 "q2_3", 

188 Integer, 

189 permitted_value_checker=main_pv, 

190 comment="Part II, Q2.3 " + main_cmt, 

191 ) 

192 q2_4 = CamcopsColumn( 

193 "q2_4", 

194 Integer, 

195 permitted_value_checker=main_pv, 

196 comment="Part II, Q2.4 " + main_cmt, 

197 ) 

198 q2_5 = CamcopsColumn( 

199 "q2_5", 

200 Integer, 

201 permitted_value_checker=main_pv, 

202 comment="Part II, Q2.5 " + main_cmt, 

203 ) 

204 q2_6 = CamcopsColumn( 

205 "q2_6", 

206 Integer, 

207 permitted_value_checker=main_pv, 

208 comment="Part II, Q2.6 " + main_cmt, 

209 ) 

210 q2_7 = CamcopsColumn( 

211 "q2_7", 

212 Integer, 

213 permitted_value_checker=main_pv, 

214 comment="Part II, Q2.7 " + main_cmt, 

215 ) 

216 q2_8 = CamcopsColumn( 

217 "q2_8", 

218 Integer, 

219 permitted_value_checker=main_pv, 

220 comment="Part II, Q2.8 " + main_cmt, 

221 ) 

222 q2_9 = CamcopsColumn( 

223 "q2_9", 

224 Integer, 

225 permitted_value_checker=main_pv, 

226 comment="Part II, Q2.9 " + main_cmt, 

227 ) 

228 q2_10 = CamcopsColumn( 

229 "q2_10", 

230 Integer, 

231 permitted_value_checker=main_pv, 

232 comment="Part II, Q2.10 " + main_cmt, 

233 ) 

234 q2_11 = CamcopsColumn( 

235 "q2_11", 

236 Integer, 

237 permitted_value_checker=main_pv, 

238 comment="Part II, Q2.11 " + main_cmt, 

239 ) 

240 q2_12 = CamcopsColumn( 

241 "q2_12", 

242 Integer, 

243 permitted_value_checker=main_pv, 

244 comment="Part II, Q2.12 " + main_cmt, 

245 ) 

246 q2_13 = CamcopsColumn( 

247 "q2_13", 

248 Integer, 

249 permitted_value_checker=main_pv, 

250 comment="Part II, Q2.13 " + main_cmt, 

251 ) 

252 

253 # Part III 

254 q3a = CamcopsColumn( 

255 "q3a", 

256 Boolean, 

257 permitted_value_checker=BIT_CHECKER, 

258 comment="Part III, Q3a (medication) " + yn_cmt, 

259 ) 

260 q3b = CamcopsColumn( 

261 "q3b", 

262 Boolean, 

263 permitted_value_checker=BIT_CHECKER, 

264 comment="Part III, Q3b (clinical state) " + on_off_cmt, 

265 ) 

266 q3c = CamcopsColumn( 

267 "q3c", 

268 Boolean, 

269 permitted_value_checker=BIT_CHECKER, 

270 comment="Part III, Q3c (levodopa) " + yn_cmt, 

271 ) 

272 q3c1 = CamcopsColumn( 

273 "q3c1", Float, comment="Part III, Q3c.1 (minutes since last dose)" 

274 ) 

275 q3_1 = CamcopsColumn( 

276 "q3_1", 

277 Integer, 

278 permitted_value_checker=main_pv, 

279 comment="Part III, Q3.1 " + main_cmt, 

280 ) 

281 q3_2 = CamcopsColumn( 

282 "q3_2", 

283 Integer, 

284 permitted_value_checker=main_pv, 

285 comment="Part III, Q3.2 " + main_cmt, 

286 ) 

287 q3_3a = CamcopsColumn( 

288 "q3_3a", 

289 Integer, 

290 permitted_value_checker=main_pv, 

291 comment="Part III, Q3.3a " + main_cmt, 

292 ) 

293 q3_3b = CamcopsColumn( 

294 "q3_3b", 

295 Integer, 

296 permitted_value_checker=main_pv, 

297 comment="Part III, Q3.3b " + main_cmt, 

298 ) 

299 q3_3c = CamcopsColumn( 

300 "q3_3c", 

301 Integer, 

302 permitted_value_checker=main_pv, 

303 comment="Part III, Q3.3c " + main_cmt, 

304 ) 

305 q3_3d = CamcopsColumn( 

306 "q3_3d", 

307 Integer, 

308 permitted_value_checker=main_pv, 

309 comment="Part III, Q3.3d " + main_cmt, 

310 ) 

311 q3_3e = CamcopsColumn( 

312 "q3_3e", 

313 Integer, 

314 permitted_value_checker=main_pv, 

315 comment="Part III, Q3.3e " + main_cmt, 

316 ) 

317 q3_4a = CamcopsColumn( 

318 "q3_4a", 

319 Integer, 

320 permitted_value_checker=main_pv, 

321 comment="Part III, Q3.4a " + main_cmt, 

322 ) 

323 q3_4b = CamcopsColumn( 

324 "q3_4b", 

325 Integer, 

326 permitted_value_checker=main_pv, 

327 comment="Part III, Q3.4b " + main_cmt, 

328 ) 

329 q3_5a = CamcopsColumn( 

330 "q3_5a", 

331 Integer, 

332 permitted_value_checker=main_pv, 

333 comment="Part III, Q3.5a " + main_cmt, 

334 ) 

335 q3_5b = CamcopsColumn( 

336 "q3_5b", 

337 Integer, 

338 permitted_value_checker=main_pv, 

339 comment="Part III, Q3.5b " + main_cmt, 

340 ) 

341 q3_6a = CamcopsColumn( 

342 "q3_6a", 

343 Integer, 

344 permitted_value_checker=main_pv, 

345 comment="Part III, Q3.6a " + main_cmt, 

346 ) 

347 q3_6b = CamcopsColumn( 

348 "q3_6b", 

349 Integer, 

350 permitted_value_checker=main_pv, 

351 comment="Part III, Q3.6b " + main_cmt, 

352 ) 

353 q3_7a = CamcopsColumn( 

354 "q3_7a", 

355 Integer, 

356 permitted_value_checker=main_pv, 

357 comment="Part III, Q3.7a " + main_cmt, 

358 ) 

359 q3_7b = CamcopsColumn( 

360 "q3_7b", 

361 Integer, 

362 permitted_value_checker=main_pv, 

363 comment="Part III, Q3.7b " + main_cmt, 

364 ) 

365 q3_8a = CamcopsColumn( 

366 "q3_8a", 

367 Integer, 

368 permitted_value_checker=main_pv, 

369 comment="Part III, Q3.8a " + main_cmt, 

370 ) 

371 q3_8b = CamcopsColumn( 

372 "q3_8b", 

373 Integer, 

374 permitted_value_checker=main_pv, 

375 comment="Part III, Q3.8b " + main_cmt, 

376 ) 

377 q3_9 = CamcopsColumn( 

378 "q3_9", 

379 Integer, 

380 permitted_value_checker=main_pv, 

381 comment="Part III, Q3.9 " + main_cmt, 

382 ) 

383 q3_10 = CamcopsColumn( 

384 "q3_10", 

385 Integer, 

386 permitted_value_checker=main_pv, 

387 comment="Part III, Q3.10 " + main_cmt, 

388 ) 

389 q3_11 = CamcopsColumn( 

390 "q3_11", 

391 Integer, 

392 permitted_value_checker=main_pv, 

393 comment="Part III, Q3.11 " + main_cmt, 

394 ) 

395 q3_12 = CamcopsColumn( 

396 "q3_12", 

397 Integer, 

398 permitted_value_checker=main_pv, 

399 comment="Part III, Q3.12 " + main_cmt, 

400 ) 

401 q3_13 = CamcopsColumn( 

402 "q3_13", 

403 Integer, 

404 permitted_value_checker=main_pv, 

405 comment="Part III, Q3.13 " + main_cmt, 

406 ) 

407 q3_14 = CamcopsColumn( 

408 "q3_14", 

409 Integer, 

410 permitted_value_checker=main_pv, 

411 comment="Part III, Q3.14 " + main_cmt, 

412 ) 

413 q3_15a = CamcopsColumn( 

414 "q3_15a", 

415 Integer, 

416 permitted_value_checker=main_pv, 

417 comment="Part III, Q3.15a " + main_cmt, 

418 ) 

419 q3_15b = CamcopsColumn( 

420 "q3_15b", 

421 Integer, 

422 permitted_value_checker=main_pv, 

423 comment="Part III, Q3.15b " + main_cmt, 

424 ) 

425 q3_16a = CamcopsColumn( 

426 "q3_16a", 

427 Integer, 

428 permitted_value_checker=main_pv, 

429 comment="Part III, Q3.16a " + main_cmt, 

430 ) 

431 q3_16b = CamcopsColumn( 

432 "q3_16b", 

433 Integer, 

434 permitted_value_checker=main_pv, 

435 comment="Part III, Q3.16b " + main_cmt, 

436 ) 

437 q3_17a = CamcopsColumn( 

438 "q3_17a", 

439 Integer, 

440 permitted_value_checker=main_pv, 

441 comment="Part III, Q3.17a " + main_cmt, 

442 ) 

443 q3_17b = CamcopsColumn( 

444 "q3_17b", 

445 Integer, 

446 permitted_value_checker=main_pv, 

447 comment="Part III, Q3.17b " + main_cmt, 

448 ) 

449 q3_17c = CamcopsColumn( 

450 "q3_17c", 

451 Integer, 

452 permitted_value_checker=main_pv, 

453 comment="Part III, Q3.17c " + main_cmt, 

454 ) 

455 q3_17d = CamcopsColumn( 

456 "q3_17d", 

457 Integer, 

458 permitted_value_checker=main_pv, 

459 comment="Part III, Q3.17d " + main_cmt, 

460 ) 

461 q3_17e = CamcopsColumn( 

462 "q3_17e", 

463 Integer, 

464 permitted_value_checker=main_pv, 

465 comment="Part III, Q3.17e " + main_cmt, 

466 ) 

467 q3_18 = CamcopsColumn( 

468 "q3_18", 

469 Integer, 

470 permitted_value_checker=main_pv, 

471 comment="Part III, Q3.18 " + main_cmt, 

472 ) 

473 q3_dyskinesia_present = CamcopsColumn( 

474 "q3_dyskinesia_present", 

475 Boolean, 

476 permitted_value_checker=BIT_CHECKER, 

477 comment="Part III, q3_dyskinesia_present " + yn_cmt, 

478 ) 

479 q3_dyskinesia_interfered = CamcopsColumn( 

480 "q3_dyskinesia_interfered", 

481 Boolean, 

482 permitted_value_checker=BIT_CHECKER, 

483 comment="Part III, q3_dyskinesia_interfered " + yn_cmt, 

484 ) 

485 q3_hy_stage = CamcopsColumn( 

486 "q3_hy_stage", 

487 Integer, 

488 permitted_value_checker=hy_pv, 

489 comment="Part III, q3_hy_stage (0-5)", 

490 ) 

491 

492 # Part IV 

493 q4_1 = CamcopsColumn( 

494 "q4_1", 

495 Integer, 

496 permitted_value_checker=main_pv, 

497 comment="Part IV, Q4.1 " + main_cmt, 

498 ) 

499 q4_2 = CamcopsColumn( 

500 "q4_2", 

501 Integer, 

502 permitted_value_checker=main_pv, 

503 comment="Part IV, Q4.2 " + main_cmt, 

504 ) 

505 q4_3 = CamcopsColumn( 

506 "q4_3", 

507 Integer, 

508 permitted_value_checker=main_pv, 

509 comment="Part IV, Q4.3 " + main_cmt, 

510 ) 

511 q4_4 = CamcopsColumn( 

512 "q4_4", 

513 Integer, 

514 permitted_value_checker=main_pv, 

515 comment="Part IV, Q4.4 " + main_cmt, 

516 ) 

517 q4_5 = CamcopsColumn( 

518 "q4_5", 

519 Integer, 

520 permitted_value_checker=main_pv, 

521 comment="Part IV, Q4.5 " + main_cmt, 

522 ) 

523 q4_6 = CamcopsColumn( 

524 "q4_6", 

525 Integer, 

526 permitted_value_checker=main_pv, 

527 comment="Part IV, Q4.6 " + main_cmt, 

528 ) 

529 

530 @staticmethod 

531 def longname(req: "CamcopsRequest") -> str: 

532 _ = req.gettext 

533 return _( 

534 "Movement Disorder Society-Sponsored Revision of the Unified " 

535 "Parkinson’s Disease Rating Scale (data collection only)" 

536 ) 

537 

538 @classmethod 

539 @cache_region_static.cache_on_arguments(function_key_generator=fkg) 

540 def task_fields_except_3c1(cls) -> List[str]: 

541 task_fields = get_camcops_column_attr_names(cls) 

542 return [x for x in task_fields if x != "q3c1"] 

543 

544 def is_complete(self) -> bool: 

545 return ( 

546 self.field_contents_valid() 

547 and self.all_fields_not_none(self.task_fields_except_3c1()) 

548 and (self.q3c1 is not None or not self.q3c) 

549 ) 

550 

551 def get_task_html(self, req: CamcopsRequest) -> str: 

552 q_a = "" 

553 for attrname, column in gen_camcops_columns(self): 

554 if attrname.startswith("clinician_"): # not the most elegant! 

555 continue 

556 question = column.comment 

557 value = getattr(self, attrname) 

558 q_a += tr_qa(question, value) 

559 return f""" 

560 <div class="{CssClass.SUMMARY}"> 

561 <table class="{CssClass.SUMMARY}"> 

562 {self.get_is_complete_tr(req)} 

563 </table> 

564 </div> 

565 <table class="{CssClass.TASKDETAIL}"> 

566 <tr> 

567 <th width="70%">Question</th> 

568 <th width="30%">Answer</th> 

569 </tr> 

570 {q_a} 

571 </table> 

572 {DATA_COLLECTION_ONLY_DIV} 

573 """ 

574 

575 def get_snomed_codes(self, req: CamcopsRequest) -> List[SnomedExpression]: 

576 if not self.is_complete(): 

577 return [] 

578 return [SnomedExpression(req.snomed(SnomedLookup.UPDRS_SCALE))]