Coverage for tasks/mds_updrs.py : 86%

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
3"""
4camcops_server/tasks/mds_updrs.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
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.
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.
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/>.
25===============================================================================
27"""
29from typing import List
31from sqlalchemy.sql.sqltypes import Boolean, Float, Integer
33from camcops_server.cc_modules.cc_cache import cache_region_static, fkg
34from camcops_server.cc_modules.cc_constants import (
35 CssClass,
36 DATA_COLLECTION_ONLY_DIV,
37)
38from camcops_server.cc_modules.cc_html import tr_qa
39from camcops_server.cc_modules.cc_request import CamcopsRequest
40from camcops_server.cc_modules.cc_snomed import SnomedExpression, SnomedLookup
41from camcops_server.cc_modules.cc_sqla_coltypes import (
42 BIT_CHECKER,
43 CamcopsColumn,
44 gen_camcops_columns,
45 get_camcops_column_attr_names,
46 ZERO_TO_TWO_CHECKER,
47 ZERO_TO_FOUR_CHECKER,
48 ZERO_TO_FIVE_CHECKER,
49)
50from camcops_server.cc_modules.cc_task import (
51 Task,
52 TaskHasPatientMixin,
53 TaskHasClinicianMixin,
54)
57# =============================================================================
58# MDS-UPDRS (crippled)
59# =============================================================================
61class MdsUpdrs(TaskHasClinicianMixin, TaskHasPatientMixin, Task):
62 """
63 Server implementation of the MDS-UPDRS task.
64 """
65 __tablename__ = "mds_updrs"
66 shortname = "MDS-UPDRS"
67 # Has clinician as of v2.0.0
69 main_cmt = " (0 normal, 1 slight, 2 mild, 3 moderate, 4 severe)"
70 main_pv = ZERO_TO_FOUR_CHECKER
71 informant_cmt = " (0 patient, 1 caregiver, 2 both)"
72 informant_pv = ZERO_TO_TWO_CHECKER
73 yn_cmt = " (0 no, 1 yes)"
74 on_off_cmt = " (0 off, 1 on)"
75 hy_pv = ZERO_TO_FIVE_CHECKER
77 # Part I
78 q1a = CamcopsColumn(
79 "q1a", Integer, permitted_value_checker=informant_pv,
80 comment="Part I: informant for Q1.1-1.6" + informant_cmt
81 )
82 q1_1 = CamcopsColumn(
83 "q1_1", Integer, permitted_value_checker=main_pv,
84 comment="Part I, Q1.1 " + main_cmt
85 )
86 q1_2 = CamcopsColumn(
87 "q1_2", Integer, permitted_value_checker=main_pv,
88 comment="Part I, Q1.2 " + main_cmt
89 )
90 q1_3 = CamcopsColumn(
91 "q1_3", Integer, permitted_value_checker=main_pv,
92 comment="Part I, Q1.3 " + main_cmt
93 )
94 q1_4 = CamcopsColumn(
95 "q1_4", Integer, permitted_value_checker=main_pv,
96 comment="Part I, Q1.4 " + main_cmt
97 )
98 q1_5 = CamcopsColumn(
99 "q1_5", Integer, permitted_value_checker=main_pv,
100 comment="Part I, Q1.5 " + main_cmt
101 )
102 q1_6 = CamcopsColumn(
103 "q1_6", Integer, permitted_value_checker=main_pv,
104 comment="Part I, Q1.6 " + main_cmt
105 )
106 q1_6a = CamcopsColumn(
107 "q1_6a", Integer, permitted_value_checker=informant_pv,
108 comment="Part I, Q1.6a: informant for Q1.7-1.13" + informant_cmt
109 )
110 q1_7 = CamcopsColumn(
111 "q1_7", Integer, permitted_value_checker=main_pv,
112 comment="Part I, Q1.7 " + main_cmt
113 )
114 q1_8 = CamcopsColumn(
115 "q1_8", Integer, permitted_value_checker=main_pv,
116 comment="Part I, Q1.8 " + main_cmt
117 )
118 q1_9 = CamcopsColumn(
119 "q1_9", Integer, permitted_value_checker=main_pv,
120 comment="Part I, Q1.9 " + main_cmt
121 )
122 q1_10 = CamcopsColumn(
123 "q1_10", Integer, permitted_value_checker=main_pv,
124 comment="Part I, Q1.10 " + main_cmt
125 )
126 q1_11 = CamcopsColumn(
127 "q1_11", Integer, permitted_value_checker=main_pv,
128 comment="Part I, Q1.11 " + main_cmt
129 )
130 q1_12 = CamcopsColumn(
131 "q1_12", Integer, permitted_value_checker=main_pv,
132 comment="Part I, Q1.12 " + main_cmt
133 )
134 q1_13 = CamcopsColumn(
135 "q1_13", Integer, permitted_value_checker=main_pv,
136 comment="Part I, Q1.13 " + main_cmt
137 )
139 # Part II
140 q2_1 = CamcopsColumn(
141 "q2_1", Integer, permitted_value_checker=main_pv,
142 comment="Part II, Q2.1 " + main_cmt
143 )
144 q2_2 = CamcopsColumn(
145 "q2_2", Integer, permitted_value_checker=main_pv,
146 comment="Part II, Q2.2 " + main_cmt
147 )
148 q2_3 = CamcopsColumn(
149 "q2_3", Integer, permitted_value_checker=main_pv,
150 comment="Part II, Q2.3 " + main_cmt
151 )
152 q2_4 = CamcopsColumn(
153 "q2_4", Integer, permitted_value_checker=main_pv,
154 comment="Part II, Q2.4 " + main_cmt
155 )
156 q2_5 = CamcopsColumn(
157 "q2_5", Integer, permitted_value_checker=main_pv,
158 comment="Part II, Q2.5 " + main_cmt
159 )
160 q2_6 = CamcopsColumn(
161 "q2_6", Integer, permitted_value_checker=main_pv,
162 comment="Part II, Q2.6 " + main_cmt
163 )
164 q2_7 = CamcopsColumn(
165 "q2_7", Integer, permitted_value_checker=main_pv,
166 comment="Part II, Q2.7 " + main_cmt
167 )
168 q2_8 = CamcopsColumn(
169 "q2_8", Integer, permitted_value_checker=main_pv,
170 comment="Part II, Q2.8 " + main_cmt
171 )
172 q2_9 = CamcopsColumn(
173 "q2_9", Integer, permitted_value_checker=main_pv,
174 comment="Part II, Q2.9 " + main_cmt
175 )
176 q2_10 = CamcopsColumn(
177 "q2_10", Integer, permitted_value_checker=main_pv,
178 comment="Part II, Q2.10 " + main_cmt
179 )
180 q2_11 = CamcopsColumn(
181 "q2_11", Integer, permitted_value_checker=main_pv,
182 comment="Part II, Q2.11 " + main_cmt
183 )
184 q2_12 = CamcopsColumn(
185 "q2_12", Integer, permitted_value_checker=main_pv,
186 comment="Part II, Q2.12 " + main_cmt
187 )
188 q2_13 = CamcopsColumn(
189 "q2_13", Integer, permitted_value_checker=main_pv,
190 comment="Part II, Q2.13 " + main_cmt
191 )
193 # Part III
194 q3a = CamcopsColumn(
195 "q3a", Boolean, permitted_value_checker=BIT_CHECKER,
196 comment="Part III, Q3a (medication) " + yn_cmt
197 )
198 q3b = CamcopsColumn(
199 "q3b", Boolean, permitted_value_checker=BIT_CHECKER,
200 comment="Part III, Q3b (clinical state) " + on_off_cmt
201 )
202 q3c = CamcopsColumn(
203 "q3c", Boolean, permitted_value_checker=BIT_CHECKER,
204 comment="Part III, Q3c (levodopa) " + yn_cmt
205 )
206 q3c1 = CamcopsColumn(
207 "q3c1", Float,
208 comment="Part III, Q3c.1 (minutes since last dose)"
209 )
210 q3_1 = CamcopsColumn(
211 "q3_1", Integer, permitted_value_checker=main_pv,
212 comment="Part III, Q3.1 " + main_cmt
213 )
214 q3_2 = CamcopsColumn(
215 "q3_2", Integer, permitted_value_checker=main_pv,
216 comment="Part III, Q3.2 " + main_cmt
217 )
218 q3_3a = CamcopsColumn(
219 "q3_3a", Integer, permitted_value_checker=main_pv,
220 comment="Part III, Q3.3a " + main_cmt
221 )
222 q3_3b = CamcopsColumn(
223 "q3_3b", Integer, permitted_value_checker=main_pv,
224 comment="Part III, Q3.3b " + main_cmt
225 )
226 q3_3c = CamcopsColumn(
227 "q3_3c", Integer, permitted_value_checker=main_pv,
228 comment="Part III, Q3.3c " + main_cmt
229 )
230 q3_3d = CamcopsColumn(
231 "q3_3d", Integer, permitted_value_checker=main_pv,
232 comment="Part III, Q3.3d " + main_cmt
233 )
234 q3_3e = CamcopsColumn(
235 "q3_3e", Integer, permitted_value_checker=main_pv,
236 comment="Part III, Q3.3e " + main_cmt
237 )
238 q3_4a = CamcopsColumn(
239 "q3_4a", Integer, permitted_value_checker=main_pv,
240 comment="Part III, Q3.4a " + main_cmt
241 )
242 q3_4b = CamcopsColumn(
243 "q3_4b", Integer, permitted_value_checker=main_pv,
244 comment="Part III, Q3.4b " + main_cmt
245 )
246 q3_5a = CamcopsColumn(
247 "q3_5a", Integer, permitted_value_checker=main_pv,
248 comment="Part III, Q3.5a " + main_cmt
249 )
250 q3_5b = CamcopsColumn(
251 "q3_5b", Integer, permitted_value_checker=main_pv,
252 comment="Part III, Q3.5b " + main_cmt
253 )
254 q3_6a = CamcopsColumn(
255 "q3_6a", Integer, permitted_value_checker=main_pv,
256 comment="Part III, Q3.6a " + main_cmt
257 )
258 q3_6b = CamcopsColumn(
259 "q3_6b", Integer, permitted_value_checker=main_pv,
260 comment="Part III, Q3.6b " + main_cmt
261 )
262 q3_7a = CamcopsColumn(
263 "q3_7a", Integer, permitted_value_checker=main_pv,
264 comment="Part III, Q3.7a " + main_cmt
265 )
266 q3_7b = CamcopsColumn(
267 "q3_7b", Integer, permitted_value_checker=main_pv,
268 comment="Part III, Q3.7b " + main_cmt
269 )
270 q3_8a = CamcopsColumn(
271 "q3_8a", Integer, permitted_value_checker=main_pv,
272 comment="Part III, Q3.8a " + main_cmt
273 )
274 q3_8b = CamcopsColumn(
275 "q3_8b", Integer, permitted_value_checker=main_pv,
276 comment="Part III, Q3.8b " + main_cmt
277 )
278 q3_9 = CamcopsColumn(
279 "q3_9", Integer, permitted_value_checker=main_pv,
280 comment="Part III, Q3.9 " + main_cmt
281 )
282 q3_10 = CamcopsColumn(
283 "q3_10", Integer, permitted_value_checker=main_pv,
284 comment="Part III, Q3.10 " + main_cmt
285 )
286 q3_11 = CamcopsColumn(
287 "q3_11", Integer, permitted_value_checker=main_pv,
288 comment="Part III, Q3.11 " + main_cmt
289 )
290 q3_12 = CamcopsColumn(
291 "q3_12", Integer, permitted_value_checker=main_pv,
292 comment="Part III, Q3.12 " + main_cmt
293 )
294 q3_13 = CamcopsColumn(
295 "q3_13", Integer, permitted_value_checker=main_pv,
296 comment="Part III, Q3.13 " + main_cmt
297 )
298 q3_14 = CamcopsColumn(
299 "q3_14", Integer, permitted_value_checker=main_pv,
300 comment="Part III, Q3.14 " + main_cmt
301 )
302 q3_15a = CamcopsColumn(
303 "q3_15a", Integer, permitted_value_checker=main_pv,
304 comment="Part III, Q3.15a " + main_cmt
305 )
306 q3_15b = CamcopsColumn(
307 "q3_15b", Integer, permitted_value_checker=main_pv,
308 comment="Part III, Q3.15b " + main_cmt
309 )
310 q3_16a = CamcopsColumn(
311 "q3_16a", Integer, permitted_value_checker=main_pv,
312 comment="Part III, Q3.16a " + main_cmt
313 )
314 q3_16b = CamcopsColumn(
315 "q3_16b", Integer, permitted_value_checker=main_pv,
316 comment="Part III, Q3.16b " + main_cmt
317 )
318 q3_17a = CamcopsColumn(
319 "q3_17a", Integer, permitted_value_checker=main_pv,
320 comment="Part III, Q3.17a " + main_cmt
321 )
322 q3_17b = CamcopsColumn(
323 "q3_17b", Integer, permitted_value_checker=main_pv,
324 comment="Part III, Q3.17b " + main_cmt
325 )
326 q3_17c = CamcopsColumn(
327 "q3_17c", Integer, permitted_value_checker=main_pv,
328 comment="Part III, Q3.17c " + main_cmt
329 )
330 q3_17d = CamcopsColumn(
331 "q3_17d", Integer, permitted_value_checker=main_pv,
332 comment="Part III, Q3.17d " + main_cmt
333 )
334 q3_17e = CamcopsColumn(
335 "q3_17e", Integer, permitted_value_checker=main_pv,
336 comment="Part III, Q3.17e " + main_cmt
337 )
338 q3_18 = CamcopsColumn(
339 "q3_18", Integer, permitted_value_checker=main_pv,
340 comment="Part III, Q3.18 " + main_cmt
341 )
342 q3_dyskinesia_present = CamcopsColumn(
343 "q3_dyskinesia_present", Boolean, permitted_value_checker=BIT_CHECKER,
344 comment="Part III, q3_dyskinesia_present " + yn_cmt
345 )
346 q3_dyskinesia_interfered = CamcopsColumn(
347 "q3_dyskinesia_interfered", Boolean,
348 permitted_value_checker=BIT_CHECKER,
349 comment="Part III, q3_dyskinesia_interfered " + yn_cmt
350 )
351 q3_hy_stage = CamcopsColumn(
352 "q3_hy_stage", Integer, permitted_value_checker=hy_pv,
353 comment="Part III, q3_hy_stage (0-5)"
354 )
356 # Part IV
357 q4_1 = CamcopsColumn(
358 "q4_1", Integer, permitted_value_checker=main_pv,
359 comment="Part IV, Q4.1 " + main_cmt
360 )
361 q4_2 = CamcopsColumn(
362 "q4_2", Integer, permitted_value_checker=main_pv,
363 comment="Part IV, Q4.2 " + main_cmt
364 )
365 q4_3 = CamcopsColumn(
366 "q4_3", Integer, permitted_value_checker=main_pv,
367 comment="Part IV, Q4.3 " + main_cmt
368 )
369 q4_4 = CamcopsColumn(
370 "q4_4", Integer, permitted_value_checker=main_pv,
371 comment="Part IV, Q4.4 " + main_cmt
372 )
373 q4_5 = CamcopsColumn(
374 "q4_5", Integer, permitted_value_checker=main_pv,
375 comment="Part IV, Q4.5 " + main_cmt
376 )
377 q4_6 = CamcopsColumn(
378 "q4_6", Integer, permitted_value_checker=main_pv,
379 comment="Part IV, Q4.6 " + main_cmt
380 )
382 @staticmethod
383 def longname(req: "CamcopsRequest") -> str:
384 _ = req.gettext
385 return _(
386 "Movement Disorder Society-Sponsored Revision of the Unified "
387 "Parkinson’s Disease Rating Scale (data collection only)"
388 )
390 @classmethod
391 @cache_region_static.cache_on_arguments(function_key_generator=fkg)
392 def task_fields_except_3c1(cls) -> List[str]:
393 task_fields = get_camcops_column_attr_names(cls)
394 return [x for x in task_fields if x != "q3c1"]
396 def is_complete(self) -> bool:
397 return (
398 self.field_contents_valid() and
399 self.all_fields_not_none(self.task_fields_except_3c1()) and
400 (self.q3c1 is not None or not self.q3c)
401 )
403 def get_task_html(self, req: CamcopsRequest) -> str:
404 q_a = ""
405 for attrname, column in gen_camcops_columns(self):
406 if attrname.startswith("clinician_"): # not the most elegant!
407 continue
408 question = column.comment
409 value = getattr(self, attrname)
410 q_a += tr_qa(question, value)
411 return f"""
412 <div class="{CssClass.SUMMARY}">
413 <table class="{CssClass.SUMMARY}">
414 {self.get_is_complete_tr(req)}
415 </table>
416 </div>
417 <table class="{CssClass.TASKDETAIL}">
418 <tr>
419 <th width="70%">Question</th>
420 <th width="30%">Answer</th>
421 </tr>
422 {q_a}
423 </table>
424 {DATA_COLLECTION_ONLY_DIV}
425 """
427 def get_snomed_codes(self, req: CamcopsRequest) -> List[SnomedExpression]:
428 if not self.is_complete():
429 return []
430 return [SnomedExpression(req.snomed(SnomedLookup.UPDRS_SCALE))]