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
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/tasks/mds_updrs.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"""
30from typing import List
32from sqlalchemy.sql.sqltypes import Boolean, Float, Integer
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)
58# =============================================================================
59# MDS-UPDRS (crippled)
60# =============================================================================
63class MdsUpdrs(TaskHasClinicianMixin, TaskHasPatientMixin, Task):
64 """
65 Server implementation of the MDS-UPDRS task.
67 Has clinician as of v2.0.0.
68 """
70 __tablename__ = "mds_updrs"
71 shortname = "MDS-UPDRS"
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
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 )
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 )
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 )
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 )
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 )
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"]
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 )
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 """
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))]