Coverage for tasks/khandaker_mojo_medical.py : 69%

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/khandaker_mojo_medical.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 Any, Dict, Tuple, Type
32from sqlalchemy.ext.declarative import DeclarativeMeta
33from sqlalchemy.sql.sqltypes import Date, Float, Integer, UnicodeText
35from camcops_server.cc_modules.cc_constants import CssClass
36from camcops_server.cc_modules.cc_html import tr_qa
37from camcops_server.cc_modules.cc_request import CamcopsRequest
38from camcops_server.cc_modules.cc_sqla_coltypes import (
39 BoolColumn,
40 CamcopsColumn,
41 ZERO_TO_TWO_CHECKER,
42)
43from camcops_server.cc_modules.cc_task import (
44 Task,
45 TaskHasPatientMixin,
46)
49class KhandakerMojoMedicalMetaclass(DeclarativeMeta):
50 # noinspection PyInitNewSignature
51 def __init__(cls: Type['KhandakerMojoMedical'],
52 name: str,
53 bases: Tuple[Type, ...],
54 classdict: Dict[str, Any]) -> None:
55 setattr(
56 cls, cls.FN_DIAGNOSIS,
57 CamcopsColumn(
58 cls.FN_DIAGNOSIS, Integer,
59 permitted_value_checker=ZERO_TO_TWO_CHECKER,
60 comment=("Diagnosis (0 Rheumatoid Arthritis, "
61 "1 Ankylosing Spondylitis, 2 Sjögren’s Syndrome)")
62 )
63 )
64 setattr(
65 cls, cls.FN_DIAGNOSIS_DATE,
66 CamcopsColumn(
67 cls.FN_DIAGNOSIS_DATE, Date,
68 comment=("Date of first diagnosis (may be approx from "
69 "'duration of illness (years))'")
70 )
71 )
72 setattr(
73 cls, cls.FN_DIAGNOSIS_DATE_APPROXIMATE,
74 BoolColumn(
75 cls.FN_DIAGNOSIS_DATE_APPROXIMATE,
76 comment="True if diagnosis date was derived from duration"
77 )
78 )
79 setattr(
80 cls, cls.FN_HAS_FIBROMYALGIA,
81 BoolColumn(
82 cls.FN_HAS_FIBROMYALGIA,
83 comment="Do you have a diagnosis of fibromyalgia?"
84 )
85 )
86 setattr(
87 cls, cls.FN_IS_PREGNANT,
88 BoolColumn(
89 cls.FN_IS_PREGNANT,
90 comment=("Are you, or is there any possibility that you might "
91 "be pregnant?")
92 )
93 )
94 setattr(
95 cls, cls.FN_HAS_INFECTION_PAST_MONTH,
96 BoolColumn(
97 cls.FN_HAS_INFECTION_PAST_MONTH,
98 comment=("Do you currently have an infection, or had "
99 "treatment for an infection (e.g antibiotics) "
100 "in the past month?")
101 )
102 )
103 setattr(
104 cls, cls.FN_HAD_INFECTION_TWO_MONTHS_PRECEDING,
105 BoolColumn(
106 cls.FN_HAD_INFECTION_TWO_MONTHS_PRECEDING,
107 comment=("Have you had an infection, or had treatment for "
108 "an infection (e.g antibiotics) in the 2 months "
109 "preceding last month?"),
110 constraint_name="ck_kh2mm_had_infection"
111 )
112 )
113 setattr(
114 cls, cls.FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE,
115 BoolColumn(
116 cls.FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE,
117 comment=("Do you have a current diagnosis of alcohol or "
118 "substance dependence?"),
119 constraint_name="ck_kh2mm_has_alcohol"
120 )
121 )
122 setattr(
123 cls, cls.FN_SMOKING_STATUS,
124 CamcopsColumn(
125 cls.FN_SMOKING_STATUS, Integer,
126 permitted_value_checker=ZERO_TO_TWO_CHECKER,
127 comment=("What is your smoking status? (0 Never smoked, "
128 "1 Ex-smoker, 2 Current smoker)")
129 )
130 )
131 setattr(
132 cls, cls.FN_ALCOHOL_UNITS_PER_WEEK,
133 CamcopsColumn(
134 cls.FN_ALCOHOL_UNITS_PER_WEEK, Float,
135 comment=("How much alcohol do you drink per week? (medium "
136 "glass of wine = 2 units, pint of beer at 4.5% = "
137 "2.5 units, 25ml of spirits at 40% = 1 unit)")
138 )
139 )
140 setattr(
141 cls, cls.FN_DEPRESSION,
142 BoolColumn(
143 cls.FN_DEPRESSION,
144 comment=("Have you had any of the following conditions "
145 "diagnosed by a doctor?")
146 )
147 )
148 setattr(
149 cls, cls.FN_BIPOLAR_DISORDER,
150 BoolColumn(
151 cls.FN_BIPOLAR_DISORDER,
152 comment=("Have you had any of the following conditions "
153 "diagnosed by a doctor?")
154 )
155 )
156 setattr(
157 cls, cls.FN_SCHIZOPHRENIA,
158 BoolColumn(
159 cls.FN_SCHIZOPHRENIA,
160 comment=("Have you had any of the following conditions "
161 "diagnosed by a doctor?")
162 )
163 )
164 setattr(
165 cls, cls.FN_AUTISM,
166 BoolColumn(
167 cls.FN_AUTISM,
168 comment=("Have you had any of the following conditions "
169 "diagnosed by a doctor?")
170 )
171 )
172 setattr(
173 cls, cls.FN_PTSD,
174 BoolColumn(
175 cls.FN_PTSD,
176 comment=("Have you had any of the following conditions "
177 "diagnosed by a doctor?")
178 )
179 )
180 setattr(
181 cls, cls.FN_ANXIETY,
182 BoolColumn(
183 cls.FN_ANXIETY,
184 comment=("Have you had any of the following conditions "
185 "diagnosed by a doctor?")
186 )
187 )
188 setattr(
189 cls, cls.FN_PERSONALITY_DISORDER,
190 BoolColumn(
191 cls.FN_PERSONALITY_DISORDER,
192 comment=("Have you had any of the following conditions "
193 "diagnosed by a doctor?")
194 )
195 )
196 setattr(
197 cls, cls.FN_INTELLECTUAL_DISABILITY,
198 BoolColumn(
199 cls.FN_INTELLECTUAL_DISABILITY,
200 comment=("Have you had any of the following conditions "
201 "diagnosed by a doctor?")
202 )
203 )
204 setattr(
205 cls, cls.FN_OTHER_MENTAL_ILLNESS,
206 BoolColumn(
207 cls.FN_OTHER_MENTAL_ILLNESS,
208 comment=("Have you had any of the following conditions "
209 "diagnosed by a doctor?")
210 )
211 )
212 setattr(
213 cls, cls.FN_OTHER_MENTAL_ILLNESS_DETAILS,
214 CamcopsColumn(
215 cls.FN_OTHER_MENTAL_ILLNESS_DETAILS, UnicodeText,
216 comment="If other, please list here"
217 )
218 )
219 setattr(
220 cls, cls.FN_HOSPITALISED_IN_LAST_YEAR,
221 BoolColumn(
222 cls.FN_HOSPITALISED_IN_LAST_YEAR,
223 comment=("Have you had a physical or mental illness "
224 "requiring hospitalisation in the previous 12 "
225 "months?")
226 )
227 )
228 setattr(
229 cls, cls.FN_HOSPITALISATION_DETAILS,
230 CamcopsColumn(
231 cls.FN_HOSPITALISATION_DETAILS, UnicodeText,
232 comment=("If yes, please list here (name of illness, number "
233 "of hospitilisations and duration):")
234 )
235 )
236 setattr(
237 cls, cls.FN_FAMILY_DEPRESSION,
238 BoolColumn(
239 cls.FN_FAMILY_DEPRESSION,
240 comment=("Has anyone in your immediate family "
241 "(parents, siblings or children) had any of the "
242 "following conditions diagnosed by a doctor?")
243 )
244 )
245 setattr(
246 cls, cls.FN_FAMILY_BIPOLAR_DISORDER,
247 BoolColumn(
248 cls.FN_FAMILY_BIPOLAR_DISORDER,
249 comment=("Has anyone in your immediate family "
250 "(parents, siblings or children) had any of the "
251 "following conditions diagnosed by a doctor?")
252 )
253 )
254 setattr(
255 cls, cls.FN_FAMILY_SCHIZOPHRENIA,
256 BoolColumn(
257 cls.FN_FAMILY_SCHIZOPHRENIA,
258 comment=("Has anyone in your immediate family "
259 "(parents, siblings or children) had any of the "
260 "following conditions diagnosed by a doctor?")
261 )
262 )
263 setattr(
264 cls, cls.FN_FAMILY_AUTISM,
265 BoolColumn(
266 cls.FN_FAMILY_AUTISM,
267 comment=("Has anyone in your immediate family "
268 "(parents, siblings or children) had any of the "
269 "following conditions diagnosed by a doctor?")
270 )
271 )
272 setattr(
273 cls, cls.FN_FAMILY_PTSD,
274 BoolColumn(
275 cls.FN_FAMILY_PTSD,
276 comment=("Has anyone in your immediate family "
277 "(parents, siblings or children) had any of the "
278 "following conditions diagnosed by a doctor?")
279 )
280 )
281 setattr(
282 cls, cls.FN_FAMILY_ANXIETY,
283 BoolColumn(
284 cls.FN_FAMILY_ANXIETY,
285 comment=("Has anyone in your immediate family "
286 "(parents, siblings or children) had any of the "
287 "following conditions diagnosed by a doctor?")
288 )
289 )
290 setattr(
291 cls, cls.FN_FAMILY_PERSONALITY_DISORDER,
292 BoolColumn(
293 cls.FN_FAMILY_PERSONALITY_DISORDER,
294 comment=("Has anyone in your immediate family "
295 "(parents, siblings or children) had any of the "
296 "following conditions diagnosed by a doctor?")
297 )
298 )
299 setattr(
300 cls, cls.FN_FAMILY_INTELLECTUAL_DISABILITY,
301 BoolColumn(
302 cls.FN_FAMILY_INTELLECTUAL_DISABILITY,
303 comment=("Has anyone in your immediate family "
304 "(parents, siblings or children) had any of the "
305 "following conditions diagnosed by a doctor?"),
306 constraint_name="ck_kh2mm_fam_int_dis"
307 )
308 )
309 setattr(
310 cls, cls.FN_FAMILY_OTHER_MENTAL_ILLNESS,
311 BoolColumn(
312 cls.FN_FAMILY_OTHER_MENTAL_ILLNESS,
313 comment=("Has anyone in your immediate family "
314 "(parents, siblings or children) had any of the "
315 "following conditions diagnosed by a doctor?")
316 )
317 )
318 setattr(
319 cls, cls.FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS,
320 CamcopsColumn(
321 cls.FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS, UnicodeText,
322 comment="If other, please list here"
323 )
324 )
326 super().__init__(name, bases, classdict)
329class KhandakerMojoMedical(
330 TaskHasPatientMixin, Task,
331 metaclass=KhandakerMojoMedicalMetaclass):
332 """
333 Server implementation of the KhandakerMojoMedical task
334 """
335 __tablename__ = "khandaker_mojo_medical"
336 shortname = "Khandaker_MOJO_Medical"
337 provides_trackers = False
339 # Section 1: General Information
340 FN_DIAGNOSIS = "diagnosis"
341 FN_DIAGNOSIS_DATE = "diagnosis_date"
342 FN_DIAGNOSIS_DATE_APPROXIMATE = "diagnosis_date_approximate"
343 FN_HAS_FIBROMYALGIA = "has_fibromyalgia"
344 FN_IS_PREGNANT = "is_pregnant"
345 FN_HAS_INFECTION_PAST_MONTH = "has_infection_past_month"
346 FN_HAD_INFECTION_TWO_MONTHS_PRECEDING = "had_infection_two_months_preceding" # noqa
347 FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE = "has_alcohol_substance_dependence"
348 FN_SMOKING_STATUS = "smoking_status"
349 FN_ALCOHOL_UNITS_PER_WEEK = "alcohol_units_per_week"
351 # Section 2: Medical History
352 FN_DEPRESSION = "depression"
353 FN_BIPOLAR_DISORDER = "bipolar_disorder"
354 FN_SCHIZOPHRENIA = "schizophrenia"
355 FN_AUTISM = "autism"
356 FN_PTSD = "ptsd"
357 FN_ANXIETY = "anxiety"
358 FN_PERSONALITY_DISORDER = "personality_disorder"
359 FN_INTELLECTUAL_DISABILITY = "intellectual_disability"
360 FN_OTHER_MENTAL_ILLNESS = "other_mental_illness"
361 FN_OTHER_MENTAL_ILLNESS_DETAILS = "other_mental_illness_details"
362 FN_HOSPITALISED_IN_LAST_YEAR = "hospitalised_in_last_year"
363 FN_HOSPITALISATION_DETAILS = "hospitalisation_details"
365 # Section 3: Family history
366 FN_FAMILY_DEPRESSION = "family_depression"
367 FN_FAMILY_BIPOLAR_DISORDER = "family_bipolar_disorder"
368 FN_FAMILY_SCHIZOPHRENIA = "family_schizophrenia"
369 FN_FAMILY_AUTISM = "family_autism"
370 FN_FAMILY_PTSD = "family_ptsd"
371 FN_FAMILY_ANXIETY = "family_anxiety"
372 FN_FAMILY_PERSONALITY_DISORDER = "family_personality_disorder"
373 FN_FAMILY_INTELLECTUAL_DISABILITY = "family_intellectual_disability"
374 FN_FAMILY_OTHER_MENTAL_ILLNESS = "family_other_mental_illness"
375 FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS = "family_other_mental_illness_details" # noqa
377 MANDATORY_FIELD_NAMES_1 = [
378 FN_DIAGNOSIS,
379 FN_DIAGNOSIS_DATE,
380 FN_HAS_FIBROMYALGIA,
381 FN_IS_PREGNANT,
382 FN_HAS_INFECTION_PAST_MONTH,
383 FN_HAD_INFECTION_TWO_MONTHS_PRECEDING,
384 FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE,
385 FN_SMOKING_STATUS,
386 FN_ALCOHOL_UNITS_PER_WEEK,
387 ]
389 MANDATORY_FIELD_NAMES_2 = [
390 FN_DEPRESSION,
391 FN_BIPOLAR_DISORDER,
392 FN_SCHIZOPHRENIA,
393 FN_AUTISM,
394 FN_PTSD,
395 FN_ANXIETY,
396 FN_PERSONALITY_DISORDER,
397 FN_INTELLECTUAL_DISABILITY,
398 FN_OTHER_MENTAL_ILLNESS,
399 FN_HOSPITALISED_IN_LAST_YEAR,
400 ]
402 MANDATORY_FIELD_NAMES_3 = [
403 FN_FAMILY_DEPRESSION,
404 FN_FAMILY_BIPOLAR_DISORDER,
405 FN_FAMILY_SCHIZOPHRENIA,
406 FN_FAMILY_AUTISM,
407 FN_FAMILY_PTSD,
408 FN_FAMILY_ANXIETY,
409 FN_FAMILY_PERSONALITY_DISORDER,
410 FN_FAMILY_INTELLECTUAL_DISABILITY,
411 FN_FAMILY_OTHER_MENTAL_ILLNESS,
412 ]
414 MANDATORY_FIELD_NAMES = (MANDATORY_FIELD_NAMES_1 +
415 MANDATORY_FIELD_NAMES_2 +
416 MANDATORY_FIELD_NAMES_3)
418 # If the answer is yes to any of these, we need to have the details
419 DETAILS_FIELDS = {
420 FN_OTHER_MENTAL_ILLNESS: FN_OTHER_MENTAL_ILLNESS_DETAILS,
421 FN_HOSPITALISED_IN_LAST_YEAR: FN_HOSPITALISATION_DETAILS,
422 FN_FAMILY_OTHER_MENTAL_ILLNESS: FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS,
423 }
425 MULTI_CHOICE_FIELD_NAMES = [
426 FN_DIAGNOSIS,
427 FN_SMOKING_STATUS,
428 ]
430 @staticmethod
431 def longname(req: "CamcopsRequest") -> str:
432 _ = req.gettext
433 return _("Khandaker GM — MOJO — Medical questionnaire")
435 def is_complete(self) -> bool:
436 if self.any_fields_none(self.MANDATORY_FIELD_NAMES):
437 return False
439 if not self.field_contents_valid():
440 return False
442 for field_name, details_field_name in self.DETAILS_FIELDS.items():
443 if getattr(self, field_name):
444 if getattr(self, details_field_name) is None:
445 return False
447 return True
449 def get_task_html(self, req: CamcopsRequest) -> str:
450 heading_1 = self.xstring(req, "general_information_title")
452 rows_1 = ""
453 for field_name in self.MANDATORY_FIELD_NAMES_1:
454 rows_1 += self.get_rows(req, field_name)
456 heading_2 = self.xstring(req, "medical_history_title")
458 rows_2 = ""
459 for field_name in self.MANDATORY_FIELD_NAMES_2:
460 rows_2 += self.get_rows(req, field_name)
462 heading_3 = self.xstring(req, "family_history_title")
464 rows_3 = ""
465 for field_name in self.MANDATORY_FIELD_NAMES_3:
466 rows_3 += self.get_rows(req, field_name)
468 html = f"""
469 <div class="{CssClass.SUMMARY}">
470 <table class="{CssClass.SUMMARY}">
471 {self.get_is_complete_tr(req)}
472 </table>
473 </div>
474 <h3>{heading_1}</h3>
475 <table class="{CssClass.TASKDETAIL}">
476 <tr>
477 <th width="60%">Question</th>
478 <th width="40%">Answer</th>
479 </tr>
480 {rows_1}
481 </table>
482 <h3>{heading_2}</h3>
483 <table class="{CssClass.TASKDETAIL}">
484 <tr>
485 <th width="60%">Question</th>
486 <th width="40%">Answer</th>
487 </tr>
488 {rows_2}
489 </table>
490 <h3>{heading_3}</h3>
491 <table class="{CssClass.TASKDETAIL}">
492 <tr>
493 <th width="60%">Question</th>
494 <th width="40%">Answer</th>
495 </tr>
496 {rows_3}
497 </table>
498 """
500 return html
502 def get_rows(self, req: CamcopsRequest, field_name: str) -> str:
503 rows = ""
505 question_text = self.xstring(req, f"q_{field_name}")
506 answer = getattr(self, field_name)
508 answer_text = answer
510 if answer is not None and (
511 field_name in self.MULTI_CHOICE_FIELD_NAMES):
512 answer_text = self.xstring(req, f"{field_name}_{answer}")
514 rows += tr_qa(question_text, answer_text)
516 if answer and field_name in self.DETAILS_FIELDS:
517 details_field_name = self.DETAILS_FIELDS[field_name]
518 details_question_text = self.xstring(
519 req, f"q_{details_field_name}")
520 details_answer = getattr(self, details_field_name)
522 rows += tr_qa(details_question_text, details_answer)
524 if field_name == self.FN_DIAGNOSIS_DATE:
525 rows += tr_qa(
526 self.xstring(
527 req, f"q_{self.FN_DIAGNOSIS_DATE_APPROXIMATE}"
528 ),
529 getattr(self, self.FN_DIAGNOSIS_DATE_APPROXIMATE)
530 )
532 return rows