Coverage for tasks/cisr.py : 42%

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/cisr.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 enum import Enum
30import logging
31from typing import List, Optional
33from cardinal_pythonlib.classes import classproperty
34from cardinal_pythonlib.logs import BraceStyleAdapter
35import cardinal_pythonlib.rnc_web as ws
36from semantic_version import Version
37from sqlalchemy.sql.sqltypes import Boolean, Integer, UnicodeText
39from camcops_server.cc_modules.cc_constants import CssClass
40from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
41from camcops_server.cc_modules.cc_html import (
42 answer,
43 bold,
44 get_yes_no,
45 get_yes_no_none,
46 italic,
47 subheading_spanning_two_columns,
48 td,
49 tr,
50)
51from camcops_server.cc_modules.cc_request import CamcopsRequest
52from camcops_server.cc_modules.cc_sqla_coltypes import (
53 CamcopsColumn,
54 ZERO_TO_FOUR_CHECKER,
55 ONE_TO_TWO_CHECKER,
56 ONE_TO_THREE_CHECKER,
57 ONE_TO_FOUR_CHECKER,
58 ONE_TO_FIVE_CHECKER,
59 ONE_TO_SIX_CHECKER,
60 ONE_TO_SEVEN_CHECKER,
61 ONE_TO_EIGHT_CHECKER,
62 ONE_TO_NINE_CHECKER,
63)
64from camcops_server.cc_modules.cc_summaryelement import SummaryElement
65from camcops_server.cc_modules.cc_task import (
66 Task,
67 TaskHasPatientMixin,
68)
70log = BraceStyleAdapter(logging.getLogger(__name__))
73# =============================================================================
74# Constants
75# =============================================================================
77DEBUG_SHOW_QUESTIONS_CONSIDERED = True
79NOT_APPLICABLE_TEXT = "—"
81# -----------------------------------------------------------------------------
82# Comments for fields
83# -----------------------------------------------------------------------------
85CMT_DEMOGRAPHICS = "(Demographics) "
86CMT_1_NO_2_YES = " (1 no, 2 yes)"
87CMT_1_YES_2_NO = " (1 yes, 2 no)"
88CMT_DURATION = (" (1: <2 weeks; 2: 2 weeks–6 months; 3: 6 months–1 year; "
89 "4: 1–2 years; 5: >2 years)")
90CMT_NEVER_SOMETIMES_ALWAYS = " (1 never, 2 sometimes, 3 always)"
91CMT_DAYS_PER_WEEK = " (1: none, 2: one to three, 3: four or more)"
92CMT_NIGHTS_PER_WEEK = CMT_DAYS_PER_WEEK
93CMT_UNPLEASANT = (" (1 not at all, 2 a little unpleasant, 3 unpleasant, "
94 "4 very unpleasant)")
95CMT_NO_SOMETIMES_OFTEN = " (1 no, 2 sometimes, 3 often)"
96CMT_BOTHERSOME_INTERESTING = (" (1 no, 2 yes, 3 haven't done anything "
97 "interesting)")
98CMT_DURING_ENJOYABLE = " (1 no, 2 yes, 3 haven't done anything enjoyable)"
99CMT_FATIGUE_CAUSE = (
100 " (1 problems with sleep; 2 medication; 3 physical illness; 4 working too "
101 "hard inc. childcare; 5 stress/worry/other psychological; 6 physical "
102 "exercise; 7 other; 8 don't know)"
103)
104CMT_STRESSORS = (
105 " (1 family members; 2 relationships with friends/colleagues; 3 housing; "
106 "4 money/bills; 5 own physical health; 6 own mental health; 7 work/lack "
107 "of work; 8 legal; 9 political/news)"
108)
109CMT_SLEEP_CHANGE = " (1: <15min, 2: 15–60min, 3: 1–3h, 4: >=3h)"
110CMT_ANHEDONIA = (" (1 yes; 2 no, less enjoyment than usual; "
111 "3 no, don't enjoy anything)")
112CMT_PANIC_SYMPTOM = "Panic symptom in past week: "
114# ... and results:
115DESC_DEPCRIT1 = "Depressive criterion 1 (mood, anhedonia, energy; max. 3)"
116DESC_DEPCRIT2 = ("Depressive criterion 2 (appetite/weight, concentration, "
117 "sleep, motor, guilt, self-worth, suicidality; max. 7)")
118DESC_DEPCRIT3 = (
119 "Depressive criterion 3: somatic syndrome (anhedonia, lack of emotional "
120 "reactivity, early-morning waking, depression worse in the morning, "
121 "psychomotor retardation/agitation, loss of appetite, weight loss, loss "
122 "of libido; max. 8)"
123)
124DESC_DEPCRIT3_MET = "Somatic syndrome criterion met (≥4)?"
125DESC_NEURASTHENIA_SCORE = "Neurasthenia score (max. 3)"
127DISORDER_OCD = "Obsessive–compulsive disorder"
128DISORDER_DEPR_MILD = "Depressive episode: at least mild"
129DISORDER_DEPR_MOD = "Depressive episode: at least moderate"
130DISORDER_DEPR_SEV = "Depressive episode: severe"
131DISORDER_CFS = "Chronic fatigue syndrome"
132DISORDER_GAD = "Generalized anxiety disorder"
133DISORDER_AGORAPHOBIA = "Agoraphobia"
134DISORDER_SOCIAL_PHOBIA = "Social phobia"
135DISORDER_SPECIFIC_PHOBIA = "Specific phobia"
136DISORDER_PANIC = "Panic disorder"
138# -----------------------------------------------------------------------------
139# Number of response values (numbered from 1 to N)
140# -----------------------------------------------------------------------------
142N_DURATIONS = 5
143N_OPTIONS_DAYS_PER_WEEK = 3
144N_OPTIONS_NIGHTS_PER_WEEK = 3
145N_OPTIONS_HOW_UNPLEASANT = 4
146N_OPTIONS_FATIGUE_CAUSES = 8
147N_OPTIONS_STRESSORS = 9
148N_OPTIONS_NO_SOMETIMES_OFTEN = 3
149NUM_PANIC_SYMPTOMS = 13 # from a to m
151# -----------------------------------------------------------------------------
152# Actual response values
153# -----------------------------------------------------------------------------
155# For e.g. SOMATIC_DUR, etc.: "How long have you..."
156V_DURATION_LT_2W = 1
157V_DURATION_2W_6M = 2
158V_DURATION_6M_1Y = 3
159V_DURATION_1Y_2Y = 4
160V_DURATION_GE_2Y = 5
162# For quite a few: "on how many days in the past week...?"
163V_DAYS_IN_PAST_WEEK_0 = 1
164V_DAYS_IN_PAST_WEEK_1_TO_3 = 2
165V_DAYS_IN_PAST_WEEK_4_OR_MORE = 3
167V_NIGHTS_IN_PAST_WEEK_0 = 1
168V_NIGHTS_IN_PAST_WEEK_1_TO_3 = 2
169V_NIGHTS_IN_PAST_WEEK_4_OR_MORE = 3
171V_HOW_UNPLEASANT_NOT_AT_ALL = 1
172V_HOW_UNPLEASANT_A_LITTLE = 2
173V_HOW_UNPLEASANT_UNPLEASANT = 3
174V_HOW_UNPLEASANT_VERY = 4
176V_FATIGUE_CAUSE_SLEEP = 1
177V_FATIGUE_CAUSE_MEDICATION = 2
178V_FATIGUE_CAUSE_PHYSICAL_ILLNESS = 3
179V_FATIGUE_CAUSE_OVERWORK = 4
180V_FATIGUE_CAUSE_PSYCHOLOGICAL = 5
181V_FATIGUE_CAUSE_EXERCISE = 6
182V_FATIGUE_CAUSE_OTHER = 7
183V_FATIGUE_CAUSE_DONT_KNOW = 8
185V_STRESSOR_FAMILY = 1
186V_STRESSOR_FRIENDS_COLLEAGUES = 2
187V_STRESSOR_HOUSING = 3
188V_STRESSOR_MONEY = 4
189V_STRESSOR_PHYSICAL_HEALTH = 5
190V_STRESSOR_MENTAL_HEALTH = 6
191V_STRESSOR_WORK = 7
192V_STRESSOR_LEGAL = 8
193V_STRESSOR_POLITICAL_NEWS = 9
195V_NSO_NO = 1
196V_NSO_SOMETIMES = 2
197V_NSO_OFTEN = 3
199V_SLEEP_CHANGE_LT_15_MIN = 1
200V_SLEEP_CHANGE_15_MIN_TO_1_H = 2
201V_SLEEP_CHANGE_1_TO_3_H = 3
202V_SLEEP_CHANGE_GT_3_H = 4
204V_ANHEDONIA_ENJOYING_NORMALLY = 1
205V_ANHEDONIA_ENJOYING_LESS = 2
206V_ANHEDONIA_NOT_ENJOYING = 3
208# Specific other question values:
210V_EMPSTAT_FT = 1 # unused
211V_EMPSTAT_PT = 2 # unused
212V_EMPSTAT_STUDENT = 3 # unused
213V_EMPSTAT_RETIRED = 4 # unused
214V_EMPSTAT_HOUSEPERSON = 5 # unused
215V_EMPSTAT_UNEMPJOBSEEKER = 6 # unused
216V_EMPSTAT_UNEMPILLHEALTH = 7 # unused
218V_EMPTYPE_SELFEMPWITHEMPLOYEES = 1 # unused
219V_EMPTYPE_SELFEMPNOEMPLOYEES = 2 # unused
220V_EMPTYPE_EMPLOYEE = 3 # unused
221V_EMPTYPE_SUPERVISOR = 4 # unused
222V_EMPTYPE_MANAGER = 5 # unused
223V_EMPTYPE_NOT_APPLICABLE = 6 # unused
224# ... the last one: added by RNC, in case pt never employed. (Mentioned to
225# Glyn Lewis 2017-12-04. Not, in any case, part of the important bits of the
226# CIS-R.)
228V_HOME_OWNER = 1 # unused
229V_HOME_TENANT = 2 # unused
230V_HOME_RELATIVEFRIEND = 3 # unused
231V_HOME_HOSTELCAREHOME = 4 # unused
232V_HOME_HOMELESS = 5 # unused
233V_HOME_OTHER = 6 # unused
235V_WEIGHT2_WTLOSS_NOTTRYING = 1
236V_WEIGHT2_WTLOSS_TRYING = 2
238V_WEIGHT3_WTLOSS_GE_HALF_STONE = 1
239V_WEIGHT3_WTLOSS_LT_HALF_STONE = 2
241V_WEIGHT4_WTGAIN_YES_PREGNANT = 3
243V_WEIGHT5_WTGAIN_GE_HALF_STONE = 1
244V_WEIGHT5_WTGAIN_LT_HALF_STONE = 2
246V_GPYEAR_NONE = 0
247V_GPYEAR_1_2 = 1
248V_GPYEAR_3_5 = 2
249V_GPYEAR_6_10 = 3
250V_GPYEAR_GT_10 = 4
252V_ILLNESS_DIABETES = 1
253V_ILLNESS_ASTHMA = 2
254V_ILLNESS_ARTHRITIS = 3
255V_ILLNESS_HEART_DISEASE = 4
256V_ILLNESS_HYPERTENSION = 5
257V_ILLNESS_LUNG_DISEASE = 6
258V_ILLNESS_MORE_THAN_ONE = 7
259V_ILLNESS_NONE = 8
261V_SOMATIC_PAIN1_NEVER = 1
262V_SOMATIC_PAIN1_SOMETIMES = 2
263V_SOMATIC_PAIN1_ALWAYS = 3
265V_SOMATIC_PAIN3_LT_3H = 1
266V_SOMATIC_PAIN3_GT_3H = 2
268V_SOMATIC_PAIN4_NOT_AT_ALL = 1
269V_SOMATIC_PAIN4_LITTLE_UNPLEASANT = 2
270V_SOMATIC_PAIN4_UNPLEASANT = 3
271V_SOMATIC_PAIN4_VERY_UNPLEASANT = 4
273V_SOMATIC_PAIN5_NO = 1
274V_SOMATIC_PAIN5_YES = 2
275V_SOMATIC_PAIN5_NOT_DONE_ANYTHING_INTERESTING = 3
277V_SOMATIC_MAND2_NO = 1
278V_SOMATIC_MAND2_YES = 2
280V_SOMATIC_DIS1_NEVER = 1
281V_SOMATIC_DIS1_SOMETIMES = 2
282V_SOMATIC_DIS1_ALWAYS = 3
284V_SOMATIC_DIS2_NONE = 1
285V_SOMATIC_DIS2_1_TO_3_DAYS = 2
286V_SOMATIC_DIS2_4_OR_MORE_DAYS = 3
288V_SOMATIC_DIS3_LT_3H = 1
289V_SOMATIC_DIS3_GT_3H = 2
291V_SOMATIC_DIS4_NOT_AT_ALL = 1
292V_SOMATIC_DIS4_LITTLE_UNPLEASANT = 2
293V_SOMATIC_DIS4_UNPLEASANT = 3
294V_SOMATIC_DIS4_VERY_UNPLEASANT = 4
296V_SOMATIC_DIS5_NO = 1
297V_SOMATIC_DIS5_YES = 2
298V_SOMATIC_DIS5_NOT_DONE_ANYTHING_INTERESTING = 3
300V_SLEEP_MAND2_NO = 1
301V_SLEEP_MAND2_YES_BUT_NOT_A_PROBLEM = 2
302V_SLEEP_MAND2_YES = 3
304V_IRRIT_MAND2_NO = 1
305V_IRRIT_MAND2_SOMETIMES = 2
306V_IRRIT_MAND2_YES = 3
308V_IRRIT3_SHOUTING_NO = 1
309V_IRRIT3_SHOUTING_WANTED_TO = 2
310V_IRRIT3_SHOUTING_DID = 3
312V_IRRIT4_ARGUMENTS_NO = 1
313V_IRRIT4_ARGUMENTS_YES_JUSTIFIED = 2
314V_IRRIT4_ARGUMENTS_YES_UNJUSTIFIED = 3
316V_DEPR5_COULD_CHEER_UP_YES = 1
317V_DEPR5_COULD_CHEER_UP_SOMETIMES = 2
318V_DEPR5_COULD_CHEER_UP_NO = 3
320V_DEPTH1_DMV_WORSE_MORNING = 1
321V_DEPTH1_DMV_WORSE_EVENING = 2
322V_DEPTH1_DMV_VARIES = 3
323V_DEPTH1_DMV_NONE = 4
325V_DEPTH2_LIBIDO_NA = 1
326V_DEPTH2_LIBIDO_NO_CHANGE = 2
327V_DEPTH2_LIBIDO_INCREASED = 3
328V_DEPTH2_LIBIDO_DECREASED = 4
330V_DEPTH5_GUILT_NEVER = 1
331V_DEPTH5_GUILT_WHEN_AT_FAULT = 2
332V_DEPTH5_GUILT_SOMETIMES = 3
333V_DEPTH5_GUILT_OFTEN = 4
335V_DEPTH8_LNWL_NO = 1
336V_DEPTH8_LNWL_SOMETIMES = 2
337V_DEPTH8_LNWL_ALWAYS = 3
339V_DEPTH9_SUICIDAL_THOUGHTS_NO = 1
340V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD = 2
341V_DEPTH9_SUICIDAL_THOUGHTS_YES = 3
343V_DOCTOR_YES = 1
344V_DOCTOR_NO_BUT_OTHERS = 2
345V_DOCTOR_NO = 3
347V_ANX_PHOBIA2_ALWAYS_SPECIFIC = 1
348V_ANX_PHOBIA2_SOMETIMES_GENERAL = 2
350V_PHOBIAS_TYPE1_ALONE_PUBLIC_TRANSPORT = 1
351V_PHOBIAS_TYPE1_FAR_FROM_HOME = 2
352V_PHOBIAS_TYPE1_PUBLIC_SPEAKING_EATING = 3
353V_PHOBIAS_TYPE1_BLOOD = 4
354V_PHOBIAS_TYPE1_CROWDED_SHOPS = 5
355V_PHOBIAS_TYPE1_ANIMALS = 6
356V_PHOBIAS_TYPE1_BEING_WATCHED = 7
357V_PHOBIAS_TYPE1_ENCLOSED_SPACES_HEIGHTS = 8
358V_PHOBIAS_TYPE1_OTHER = 9
360V_PANIC1_N_PANICS_PAST_WEEK_0 = 1
361V_PANIC1_N_PANICS_PAST_WEEK_1 = 2
362V_PANIC1_N_PANICS_PAST_WEEK_GT_1 = 3
364V_PANIC3_WORST_LT_10_MIN = 1
365V_PANIC3_WORST_GE_10_MIN = 2
367V_COMP4_MAX_N_REPEATS_1 = 1
368V_COMP4_MAX_N_REPEATS_2 = 2
369V_COMP4_MAX_N_REPEATS_GE_3 = 3
371V_OBSESS_MAND1_SAME_THOUGHTS_REPEATED = 1
372V_OBSESS_MAND1_GENERAL_WORRIES = 2
374V_OBSESS4_LT_15_MIN = 1
375V_OBSESS4_GE_15_MIN = 2
377V_OVERALL_IMPAIRMENT_NONE = 1
378V_OVERALL_IMPAIRMENT_DIFFICULT = 2
379V_OVERALL_IMPAIRMENT_STOP_1_ACTIVITY = 3
380V_OVERALL_IMPAIRMENT_STOP_GT_1_ACTIVITY = 4
382# -----------------------------------------------------------------------------
383# Internal coding, NOT answer values:
384# -----------------------------------------------------------------------------
386# Magic numbers from the original:
387WTCHANGE_NONE_OR_APPETITE_INCREASE = 0
388WTCHANGE_APPETITE_LOSS = 1
389WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN = 2
390WTCHANGE_WTLOSS_GE_HALF_STONE = 3
391WTCHANGE_WTGAIN_GE_HALF_STONE = 4
392# ... I'm not entirely sure why this labelling system is used!
394DESC_WEIGHT_CHANGE = (
395 "Weight change "
396 f"({WTCHANGE_NONE_OR_APPETITE_INCREASE}: none or appetite increase; "
397 f"{WTCHANGE_APPETITE_LOSS}: appetite loss without weight loss; "
398 f"{WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN}: "
399 "non-deliberate weight loss or gain <0.5 st; "
400 f"{WTCHANGE_WTLOSS_GE_HALF_STONE}: weight loss ≥0.5 st; "
401 f"{WTCHANGE_WTGAIN_GE_HALF_STONE}: weight gain ≥0.5 st)"
402)
404PHOBIATYPES_OTHER = 0
405PHOBIATYPES_AGORAPHOBIA = 1
406PHOBIATYPES_SOCIAL = 2
407PHOBIATYPES_BLOOD_INJURY = 3
408PHOBIATYPES_ANIMALS_ENCLOSED_HEIGHTS = 4
409# ... some of these are not really used, but I've followed the original CIS-R
410# for clarity
412# One smaller than the answer codes:
413OVERALL_IMPAIRMENT_NONE = 0
414OVERALL_IMPAIRMENT_DIFFICULT = 1
415OVERALL_IMPAIRMENT_STOP_1_ACTIVITY = 2
416OVERALL_IMPAIRMENT_STOP_GT_1_ACTIVITY = 3
418# Again, we're following this coding structure primarily for compatibility:
419DIAG_0_NO_DIAGNOSIS = 0
420DIAG_1_MIXED_ANX_DEPR_DIS_MILD = 1
421DIAG_2_GENERALIZED_ANX_DIS_MILD = 2
422DIAG_3_OBSESSIVE_COMPULSIVE_DIS = 3
423DIAG_4_MIXED_ANX_DEPR_DIS = 4
424DIAG_5_SPECIFIC_PHOBIA = 5
425DIAG_6_SOCIAL_PHOBIA = 6
426DIAG_7_AGORAPHOBIA = 7
427DIAG_8_GENERALIZED_ANX_DIS = 8
428DIAG_9_PANIC_DIS = 9
429DIAG_10_MILD_DEPR_EPISODE = 10
430DIAG_11_MOD_DEPR_EPISODE = 11
431DIAG_12_SEVERE_DEPR_EPISODE = 12
433SUICIDE_INTENT_NONE = 0
434SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS = 1
435SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING = 2
436SUICIDE_INTENT_SUICIDAL_THOUGHTS = 3
437SUICIDE_INTENT_SUICIDAL_PLANS = 4
439SLEEPCHANGE_NONE = 0 # added
440SLEEPCHANGE_EMW = 1
441SLEEPCHANGE_INSOMNIA_NOT_EMW = 2
442SLEEPCHANGE_INCREASE = 3
444DESC_SLEEP_CHANGE = (
445 f"Sleep change ({SLEEPCHANGE_NONE}: none; "
446 f"{SLEEPCHANGE_EMW}: early-morning waking; "
447 f"{SLEEPCHANGE_INSOMNIA_NOT_EMW}: insomnia without early-morning waking; "
448 f"{SLEEPCHANGE_INCREASE}: sleep increase)"
449)
451DIURNAL_MOOD_VAR_NONE = 0
452DIURNAL_MOOD_VAR_WORSE_MORNING = 1
453DIURNAL_MOOD_VAR_WORSE_EVENING = 2
455PSYCHOMOTOR_NONE = 0
456PSYCHOMOTOR_RETARDATION = 1
457PSYCHOMOTOR_AGITATION = 2
459# Answer values or pseudo-values:
461V_MISSING = 0 # Integer value of a missing answer
462V_UNKNOWN = -1 # Dummy value, never used for answers
464SCORE_PREFIX = "... "
466# -----------------------------------------------------------------------------
467# Scoring constants:
468# -----------------------------------------------------------------------------
470MAX_TOTAL = 57
472MAX_SOMATIC = 4
473MAX_HYPO = 4
474MAX_IRRIT = 4
475MAX_CONC = 4
476MAX_FATIGUE = 4
477MAX_SLEEP = 4
478MAX_DEPR = 4
479MAX_DEPTHTS = 5
480MAX_PHOBIAS = 4
481MAX_WORRY = 4
482MAX_ANX = 4
483MAX_PANIC = 4
484MAX_COMP = 4
485MAX_OBSESS = 4
486MAX_DEPCRIT1 = 3
487MAX_DEPCRIT2 = 7
488MAX_DEPCRIT3 = 8
490SOMATIC_SYNDROME_CRITERION = 4 # number of symptoms
492# -----------------------------------------------------------------------------
493# Question numbers
494# -----------------------------------------------------------------------------
496# Not quite sure to do an autonumbering enum that also can have synonyms, like
497# C++. The AutoNumberEnum (q.v.) is close, but won't do the synonyms. So:
499_nasty_hack_next_enum = 1 # start with 1
502def next_enum() -> int:
503 global _nasty_hack_next_enum
504 v = _nasty_hack_next_enum
505 _nasty_hack_next_enum += 1
506 return v
509class CisrQuestion(Enum):
510 # The values below look like integers, but they aren't; they are of type
511 # CisrQuestion, and have attributes like ".value".
512 START_MARKER = next_enum()
514 INTRO_1 = START_MARKER
515 INTRO_2 = next_enum()
516 INTRO_DEMOGRAPHICS = next_enum()
518 ETHNIC = next_enum()
519 MARRIED = next_enum()
520 EMPSTAT = next_enum()
521 EMPTYPE = next_enum()
522 HOME = next_enum()
523 HEALTH_WELLBEING = next_enum()
525 APPETITE1_LOSS_PAST_MONTH = next_enum()
527 WEIGHT1_LOSS_PAST_MONTH = next_enum()
528 WEIGHT2_TRYING_TO_LOSE = next_enum()
529 WEIGHT3_LOST_LOTS = next_enum()
530 APPETITE2_INCREASE_PAST_MONTH = next_enum()
531 WEIGHT4_INCREASE_PAST_MONTH = next_enum()
532 # WEIGHT4A = WEIGHT4 with pregnancy question; blended
533 WEIGHT5_GAINED_LOTS = next_enum()
534 GP_YEAR = next_enum()
535 DISABLE = next_enum()
536 ILLNESS = next_enum()
538 SOMATIC_MAND1_PAIN_PAST_MONTH = next_enum()
539 SOMATIC_PAIN1_PSYCHOL_EXAC = next_enum()
540 SOMATIC_PAIN2_DAYS_PAST_WEEK = next_enum()
541 SOMATIC_PAIN3_GT_3H_ANY_DAY = next_enum()
542 SOMATIC_PAIN4_UNPLEASANT = next_enum()
543 SOMATIC_PAIN5_INTERRUPTED_INTERESTING = next_enum()
544 SOMATIC_MAND2_DISCOMFORT = next_enum()
545 SOMATIC_DIS1_PSYCHOL_EXAC = next_enum()
546 SOMATIC_DIS2_DAYS_PAST_WEEK = next_enum()
547 SOMATIC_DIS3_GT_3H_ANY_DAY = next_enum()
548 SOMATIC_DIS4_UNPLEASANT = next_enum()
549 SOMATIC_DIS5_INTERRUPTED_INTERESTING = next_enum()
550 SOMATIC_DUR = next_enum()
552 FATIGUE_MAND1_TIRED_PAST_MONTH = next_enum()
553 FATIGUE_CAUSE1_TIRED = next_enum()
554 FATIGUE_TIRED1_DAYS_PAST_WEEK = next_enum()
555 FATIGUE_TIRED2_GT_3H_ANY_DAY = next_enum()
556 FATIGUE_TIRED3_HAD_TO_PUSH = next_enum()
557 FATIGUE_TIRED4_DURING_ENJOYABLE = next_enum()
558 FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH = next_enum()
559 FATIGUE_CAUSE2_LACK_ENERGY = next_enum()
560 FATIGUE_ENERGY1_DAYS_PAST_WEEK = next_enum()
561 FATIGUE_ENERGY2_GT_3H_ANY_DAY = next_enum()
562 FATIGUE_ENERGY3_HAD_TO_PUSH = next_enum()
563 FATIGUE_ENERGY4_DURING_ENJOYABLE = next_enum()
564 FATIGUE_DUR = next_enum()
566 CONC_MAND1_POOR_CONC_PAST_MONTH = next_enum()
567 CONC_MAND2_FORGETFUL_PAST_MONTH = next_enum()
568 CONC1_CONC_DAYS_PAST_WEEK = next_enum()
569 CONC2_CONC_FOR_TV_READING_CONVERSATION = next_enum()
570 CONC3_CONC_PREVENTED_ACTIVITIES = next_enum()
571 CONC_DUR = next_enum()
572 CONC4_FORGOTTEN_IMPORTANT = next_enum()
573 FORGET_DUR = next_enum()
575 SLEEP_MAND1_LOSS_PAST_MONTH = next_enum()
576 SLEEP_LOSE1_NIGHTS_PAST_WEEK = next_enum()
577 SLEEP_LOSE2_DIS_WORST_DURATION = next_enum() # DIS = delayed initiation of sleep # noqa
578 SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK = next_enum()
579 SLEEP_EMW_PAST_WEEK = next_enum() # EMW = early-morning waking
580 SLEEP_CAUSE = next_enum()
581 SLEEP_MAND2_GAIN_PAST_MONTH = next_enum()
582 SLEEP_GAIN1_NIGHTS_PAST_WEEK = next_enum()
583 SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT = next_enum()
584 SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK = next_enum()
585 SLEEP_DUR = next_enum()
587 IRRIT_MAND1_PEOPLE_PAST_MONTH = next_enum()
588 IRRIT_MAND2_THINGS_PAST_MONTH = next_enum()
589 IRRIT1_DAYS_PER_WEEK = next_enum()
590 IRRIT2_GT_1H_ANY_DAY = next_enum()
591 IRRIT3_WANTED_TO_SHOUT = next_enum()
592 IRRIT4_ARGUMENTS = next_enum()
593 IRRIT_DUR = next_enum()
595 HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH = next_enum()
596 HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS = next_enum()
597 HYPO1_DAYS_PAST_WEEK = next_enum()
598 HYPO2_WORRY_TOO_MUCH = next_enum()
599 HYPO3_HOW_UNPLEASANT = next_enum()
600 HYPO4_CAN_DISTRACT = next_enum()
601 HYPO_DUR = next_enum()
603 DEPR_MAND1_LOW_MOOD_PAST_MONTH = next_enum()
604 DEPR1_LOW_MOOD_PAST_WEEK = next_enum()
605 DEPR_MAND2_ENJOYMENT_PAST_MONTH = next_enum()
606 DEPR2_ENJOYMENT_PAST_WEEK = next_enum()
607 DEPR3_DAYS_PAST_WEEK = next_enum()
608 DEPR4_GT_3H_ANY_DAY = next_enum()
609 DEPR_CONTENT = next_enum()
610 DEPR5_COULD_CHEER_UP = next_enum()
611 DEPR_DUR = next_enum()
612 DEPTH1_DIURNAL_VARIATION = next_enum() # "depth" = depressive thoughts?
613 DEPTH2_LIBIDO = next_enum()
614 DEPTH3_RESTLESS = next_enum()
615 DEPTH4_SLOWED = next_enum()
616 DEPTH5_GUILT = next_enum()
617 DEPTH6_WORSE_THAN_OTHERS = next_enum()
618 DEPTH7_HOPELESS = next_enum()
619 DEPTH8_LNWL = next_enum() # life not worth living
620 DEPTH9_SUICIDE_THOUGHTS = next_enum()
621 DEPTH10_SUICIDE_METHOD = next_enum()
622 DOCTOR = next_enum()
623 DOCTOR2_PLEASE_TALK_TO = next_enum()
624 DEPR_OUTRO = next_enum()
626 WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH = next_enum()
627 WORRY_MAND2_ANY_WORRIES_PAST_MONTH = next_enum()
628 WORRY_CONT1 = next_enum()
629 WORRY1_INFO_ONLY = next_enum()
630 WORRY2_DAYS_PAST_WEEK = next_enum()
631 WORRY3_TOO_MUCH = next_enum()
632 WORRY4_HOW_UNPLEASANT = next_enum()
633 WORRY5_GT_3H_ANY_DAY = next_enum()
634 WORRY_DUR = next_enum()
636 ANX_MAND1_ANXIETY_PAST_MONTH = next_enum()
637 ANX_MAND2_TENSION_PAST_MONTH = next_enum()
638 ANX_PHOBIA1_SPECIFIC_PAST_MONTH = next_enum()
639 ANX_PHOBIA2_SPECIFIC_OR_GENERAL = next_enum()
640 ANX1_INFO_ONLY = next_enum()
641 ANX2_GENERAL_DAYS_PAST_WEEK = next_enum()
642 ANX3_GENERAL_HOW_UNPLEASANT = next_enum()
643 ANX4_GENERAL_PHYSICAL_SYMPTOMS = next_enum()
644 ANX5_GENERAL_GT_3H_ANY_DAY = next_enum()
645 ANX_DUR_GENERAL = next_enum()
647 PHOBIAS_MAND_AVOIDANCE_PAST_MONTH = next_enum()
648 PHOBIAS_TYPE1 = next_enum()
649 PHOBIAS1_DAYS_PAST_WEEK = next_enum()
650 PHOBIAS2_PHYSICAL_SYMPTOMS = next_enum()
651 PHOBIAS3_AVOIDANCE = next_enum()
652 PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK = next_enum()
653 PHOBIAS_DUR = next_enum()
655 PANIC_MAND_PAST_MONTH = next_enum()
656 PANIC1_NUM_PAST_WEEK = next_enum()
657 PANIC2_HOW_UNPLEASANT = next_enum()
658 PANIC3_PANIC_GE_10_MIN = next_enum()
659 PANIC4_RAPID_ONSET = next_enum()
660 PANSYM = next_enum() # questions about each of several symptoms
661 PANIC5_ALWAYS_SPECIFIC_TRIGGER = next_enum()
662 PANIC_DUR = next_enum()
664 ANX_OUTRO = next_enum()
666 COMP_MAND1_COMPULSIONS_PAST_MONTH = next_enum()
667 COMP1_DAYS_PAST_WEEK = next_enum()
668 COMP2_TRIED_TO_STOP = next_enum()
669 COMP3_UPSETTING = next_enum()
670 COMP4_MAX_N_REPETITIONS = next_enum()
671 COMP_DUR = next_enum()
673 OBSESS_MAND1_OBSESSIONS_PAST_MONTH = next_enum()
674 OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL = next_enum()
675 OBSESS1_DAYS_PAST_WEEK = next_enum()
676 OBSESS2_TRIED_TO_STOP = next_enum()
677 OBSESS3_UPSETTING = next_enum()
678 OBSESS4_MAX_DURATION = next_enum()
679 OBSESS_DUR = next_enum()
681 OVERALL1_INFO_ONLY = next_enum()
682 OVERALL2_IMPACT_PAST_WEEK = next_enum()
683 THANKS_FINISHED = next_enum()
684 END_MARKER = next_enum() # not a real page
687CQ = CisrQuestion # shorthand
689# Demographics section
690FN_ETHNIC = "ethnic"
691FN_MARRIED = "married"
692FN_EMPSTAT = "empstat"
693FN_EMPTYPE = "emptype"
694FN_HOME = "home"
696FN_APPETITE1 = "appetite1"
697FN_WEIGHT1 = "weight1"
698FN_WEIGHT2 = "weight2"
699FN_WEIGHT3 = "weight3"
700FN_APPETITE2 = "appetite2"
701FN_WEIGHT4 = "weight4" # male/female responses unified (no "weight4a"
702FN_WEIGHT5 = "weight5"
704FN_GP_YEAR = "gp_year"
705FN_DISABLE = "disable"
706FN_ILLNESS = "illness"
708FN_SOMATIC_MAND1 = "somatic_mand1"
709FN_SOMATIC_PAIN1 = "somatic_pain1"
710FN_SOMATIC_PAIN2 = "somatic_pain2"
711FN_SOMATIC_PAIN3 = "somatic_pain3"
712FN_SOMATIC_PAIN4 = "somatic_pain4"
713FN_SOMATIC_PAIN5 = "somatic_pain5"
714FN_SOMATIC_MAND2 = "somatic_mand2"
715FN_SOMATIC_DIS1 = "somatic_dis1"
716FN_SOMATIC_DIS2 = "somatic_dis2"
717FN_SOMATIC_DIS3 = "somatic_dis3"
718FN_SOMATIC_DIS4 = "somatic_dis4"
719FN_SOMATIC_DIS5 = "somatic_dis5"
720FN_SOMATIC_DUR = "somatic_dur"
722FN_FATIGUE_MAND1 = "fatigue_mand1"
723FN_FATIGUE_CAUSE1 = "fatigue_cause1"
724FN_FATIGUE_TIRED1 = "fatigue_tired1"
725FN_FATIGUE_TIRED2 = "fatigue_tired2"
726FN_FATIGUE_TIRED3 = "fatigue_tired3"
727FN_FATIGUE_TIRED4 = "fatigue_tired4"
728FN_FATIGUE_MAND2 = "fatigue_mand2"
729FN_FATIGUE_CAUSE2 = "fatigue_cause2"
730FN_FATIGUE_ENERGY1 = "fatigue_energy1"
731FN_FATIGUE_ENERGY2 = "fatigue_energy2"
732FN_FATIGUE_ENERGY3 = "fatigue_energy3"
733FN_FATIGUE_ENERGY4 = "fatigue_energy4"
734FN_FATIGUE_DUR = "fatigue_dur"
736FN_CONC_MAND1 = "conc_mand1"
737FN_CONC_MAND2 = "conc_mand2"
738FN_CONC1 = "conc1"
739FN_CONC2 = "conc2"
740FN_CONC3 = "conc3"
741FN_CONC_DUR = "conc_dur"
742FN_CONC4 = "conc4"
743FN_FORGET_DUR = "forget_dur"
745FN_SLEEP_MAND1 = "sleep_mand1"
746FN_SLEEP_LOSE1 = "sleep_lose1"
747FN_SLEEP_LOSE2 = "sleep_lose2"
748FN_SLEEP_LOSE3 = "sleep_lose3"
749FN_SLEEP_EMW = "sleep_emw"
750FN_SLEEP_CAUSE = "sleep_cause"
751FN_SLEEP_MAND2 = "sleep_mand2"
752FN_SLEEP_GAIN1 = "sleep_gain1"
753FN_SLEEP_GAIN2 = "sleep_gain2"
754FN_SLEEP_GAIN3 = "sleep_gain3"
755FN_SLEEP_DUR = "sleep_dur"
757FN_IRRIT_MAND1 = "irrit_mand1"
758FN_IRRIT_MAND2 = "irrit_mand2"
759FN_IRRIT1 = "irrit1"
760FN_IRRIT2 = "irrit2"
761FN_IRRIT3 = "irrit3"
762FN_IRRIT4 = "irrit4"
763FN_IRRIT_DUR = "irrit_dur"
765FN_HYPO_MAND1 = "hypo_mand1"
766FN_HYPO_MAND2 = "hypo_mand2"
767FN_HYPO1 = "hypo1"
768FN_HYPO2 = "hypo2"
769FN_HYPO3 = "hypo3"
770FN_HYPO4 = "hypo4"
771FN_HYPO_DUR = "hypo_dur"
773FN_DEPR_MAND1 = "depr_mand1"
774FN_DEPR1 = "depr1"
775FN_DEPR_MAND2 = "depr_mand2"
776FN_DEPR2 = "depr2"
777FN_DEPR3 = "depr3"
778FN_DEPR4 = "depr4"
779FN_DEPR_CONTENT = "depr_content"
780FN_DEPR5 = "depr5"
781FN_DEPR_DUR = "depr_dur"
782FN_DEPTH1 = "depth1"
783FN_DEPTH2 = "depth2"
784FN_DEPTH3 = "depth3"
785FN_DEPTH4 = "depth4"
786FN_DEPTH5 = "depth5"
787FN_DEPTH6 = "depth6"
788FN_DEPTH7 = "depth7"
789FN_DEPTH8 = "depth8"
790FN_DEPTH9 = "depth9"
791FN_DEPTH10 = "depth10"
792FN_DOCTOR = "doctor"
794FN_WORRY_MAND1 = "worry_mand1"
795FN_WORRY_MAND2 = "worry_mand2"
796FN_WORRY_CONT1 = "worry_cont1"
797FN_WORRY2 = "worry2"
798FN_WORRY3 = "worry3"
799FN_WORRY4 = "worry4"
800FN_WORRY5 = "worry5"
801FN_WORRY_DUR = "worry_dur"
803FN_ANX_MAND1 = "anx_mand1"
804FN_ANX_MAND2 = "anx_mand2"
805FN_ANX_PHOBIA1 = "anx_phobia1"
806FN_ANX_PHOBIA2 = "anx_phobia2"
807FN_ANX2 = "anx2"
808FN_ANX3 = "anx3"
809FN_ANX4 = "anx4"
810FN_ANX5 = "anx5"
811FN_ANX_DUR = "anx_dur"
813FN_PHOBIAS_MAND = "phobias_mand"
814FN_PHOBIAS_TYPE1 = "phobias_type1"
815FN_PHOBIAS1 = "phobias1"
816FN_PHOBIAS2 = "phobias2"
817FN_PHOBIAS3 = "phobias3"
818FN_PHOBIAS4 = "phobias4"
819FN_PHOBIAS_DUR = "phobias_dur"
821FN_PANIC_MAND = "panic_mand"
822FN_PANIC1 = "panic1"
823FN_PANIC2 = "panic2"
824FN_PANIC3 = "panic3"
825FN_PANIC4 = "panic4"
826FN_PANSYM_A = "pansym_a"
827FN_PANSYM_B = "pansym_b"
828FN_PANSYM_C = "pansym_c"
829FN_PANSYM_D = "pansym_d"
830FN_PANSYM_E = "pansym_e"
831FN_PANSYM_F = "pansym_f"
832FN_PANSYM_G = "pansym_g"
833FN_PANSYM_H = "pansym_h"
834FN_PANSYM_I = "pansym_i"
835FN_PANSYM_J = "pansym_j"
836FN_PANSYM_K = "pansym_k"
837FN_PANSYM_L = "pansym_l"
838FN_PANSYM_M = "pansym_m"
839FN_PANIC5 = "panic5"
840FN_PANIC_DUR = "panic_dur"
842FN_COMP_MAND1 = "comp_mand1"
843FN_COMP1 = "comp1"
844FN_COMP2 = "comp2"
845FN_COMP3 = "comp3"
846FN_COMP4 = "comp4"
847FN_COMP_DUR = "comp_dur"
849FN_OBSESS_MAND1 = "obsess_mand1"
850FN_OBSESS_MAND2 = "obsess_mand2"
851FN_OBSESS1 = "obsess1"
852FN_OBSESS2 = "obsess2"
853FN_OBSESS3 = "obsess3"
854FN_OBSESS4 = "obsess4"
855FN_OBSESS_DUR = "obsess_dur"
857FN_OVERALL2 = "overall2"
859PANIC_SYMPTOM_FIELDNAMES = [
860 FN_PANSYM_A,
861 FN_PANSYM_B,
862 FN_PANSYM_C,
863 FN_PANSYM_D,
864 FN_PANSYM_E,
865 FN_PANSYM_F,
866 FN_PANSYM_G,
867 FN_PANSYM_H,
868 FN_PANSYM_I,
869 FN_PANSYM_J,
870 FN_PANSYM_K,
871 FN_PANSYM_L,
872 FN_PANSYM_M,
873]
875FIELDNAME_FOR_QUESTION = {
876 # CQ.INTRO_1: # information only
877 # CQ.INTRO_2: # information only
878 # CQ.INTRO_DEMOGRAPHICS: # information only
880 CQ.ETHNIC: FN_ETHNIC,
881 CQ.MARRIED: FN_MARRIED,
882 CQ.EMPSTAT: FN_EMPSTAT,
883 CQ.EMPTYPE: FN_EMPTYPE,
884 CQ.HOME: FN_HOME,
886 # CQ.HEALTH_WELLBEING: # information only
888 CQ.APPETITE1_LOSS_PAST_MONTH: FN_APPETITE1,
889 CQ.WEIGHT1_LOSS_PAST_MONTH: FN_WEIGHT1,
890 CQ.WEIGHT2_TRYING_TO_LOSE: FN_WEIGHT2,
891 CQ.WEIGHT3_LOST_LOTS: FN_WEIGHT3,
892 CQ.APPETITE2_INCREASE_PAST_MONTH: FN_APPETITE2,
893 CQ.WEIGHT4_INCREASE_PAST_MONTH: FN_WEIGHT4,
894 # CQ.WEIGHT4A: not used (= WEIGHT4 + pregnancy option)
895 CQ.WEIGHT5_GAINED_LOTS: FN_WEIGHT5,
896 CQ.GP_YEAR: FN_GP_YEAR,
897 CQ.DISABLE: FN_DISABLE,
898 CQ.ILLNESS: FN_ILLNESS,
900 CQ.SOMATIC_MAND1_PAIN_PAST_MONTH: FN_SOMATIC_MAND1,
901 CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: FN_SOMATIC_PAIN1,
902 CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK: FN_SOMATIC_PAIN2,
903 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY: FN_SOMATIC_PAIN3,
904 CQ.SOMATIC_PAIN4_UNPLEASANT: FN_SOMATIC_PAIN4,
905 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: FN_SOMATIC_PAIN5,
906 CQ.SOMATIC_MAND2_DISCOMFORT: FN_SOMATIC_MAND2,
907 CQ.SOMATIC_DIS1_PSYCHOL_EXAC: FN_SOMATIC_DIS1,
908 CQ.SOMATIC_DIS2_DAYS_PAST_WEEK: FN_SOMATIC_DIS2,
909 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY: FN_SOMATIC_DIS3,
910 CQ.SOMATIC_DIS4_UNPLEASANT: FN_SOMATIC_DIS4,
911 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: FN_SOMATIC_DIS5,
912 CQ.SOMATIC_DUR: FN_SOMATIC_DUR,
914 CQ.FATIGUE_MAND1_TIRED_PAST_MONTH: FN_FATIGUE_MAND1,
915 CQ.FATIGUE_CAUSE1_TIRED: FN_FATIGUE_CAUSE1,
916 CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK: FN_FATIGUE_TIRED1,
917 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY: FN_FATIGUE_TIRED2,
918 CQ.FATIGUE_TIRED3_HAD_TO_PUSH: FN_FATIGUE_TIRED3,
919 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: FN_FATIGUE_TIRED4,
920 CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH: FN_FATIGUE_MAND2,
921 CQ.FATIGUE_CAUSE2_LACK_ENERGY: FN_FATIGUE_CAUSE2,
922 CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK: FN_FATIGUE_ENERGY1,
923 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY: FN_FATIGUE_ENERGY2,
924 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH: FN_FATIGUE_ENERGY3,
925 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: FN_FATIGUE_ENERGY4,
926 CQ.FATIGUE_DUR: FN_FATIGUE_DUR,
928 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH: FN_CONC_MAND1,
929 CQ.CONC_MAND2_FORGETFUL_PAST_MONTH: FN_CONC_MAND2,
930 CQ.CONC1_CONC_DAYS_PAST_WEEK: FN_CONC1,
931 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION: FN_CONC2,
932 CQ.CONC3_CONC_PREVENTED_ACTIVITIES: FN_CONC3,
933 CQ.CONC_DUR: FN_CONC_DUR,
934 CQ.CONC4_FORGOTTEN_IMPORTANT: FN_CONC4,
935 CQ.FORGET_DUR: FN_FORGET_DUR,
937 CQ.SLEEP_MAND1_LOSS_PAST_MONTH: FN_SLEEP_MAND1,
938 CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK: FN_SLEEP_LOSE1,
939 CQ.SLEEP_LOSE2_DIS_WORST_DURATION: FN_SLEEP_LOSE2,
940 CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK: FN_SLEEP_LOSE3,
941 CQ.SLEEP_EMW_PAST_WEEK: FN_SLEEP_EMW,
942 CQ.SLEEP_CAUSE: FN_SLEEP_CAUSE,
943 CQ.SLEEP_MAND2_GAIN_PAST_MONTH: FN_SLEEP_MAND2,
944 CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK: FN_SLEEP_GAIN1,
945 CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: FN_SLEEP_GAIN2,
946 CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK: FN_SLEEP_GAIN3,
947 CQ.SLEEP_DUR: FN_SLEEP_DUR,
949 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH: FN_IRRIT_MAND1,
950 CQ.IRRIT_MAND2_THINGS_PAST_MONTH: FN_IRRIT_MAND2,
951 CQ.IRRIT1_DAYS_PER_WEEK: FN_IRRIT1,
952 CQ.IRRIT2_GT_1H_ANY_DAY: FN_IRRIT2,
953 CQ.IRRIT3_WANTED_TO_SHOUT: FN_IRRIT3,
954 CQ.IRRIT4_ARGUMENTS: FN_IRRIT4,
955 CQ.IRRIT_DUR: FN_IRRIT_DUR,
957 CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH: FN_HYPO_MAND1,
958 CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS: FN_HYPO_MAND2,
959 CQ.HYPO1_DAYS_PAST_WEEK: FN_HYPO1,
960 CQ.HYPO2_WORRY_TOO_MUCH: FN_HYPO2,
961 CQ.HYPO3_HOW_UNPLEASANT: FN_HYPO3,
962 CQ.HYPO4_CAN_DISTRACT: FN_HYPO4,
963 CQ.HYPO_DUR: FN_HYPO_DUR,
965 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH: FN_DEPR_MAND1,
966 CQ.DEPR1_LOW_MOOD_PAST_WEEK: FN_DEPR1,
967 CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: FN_DEPR_MAND2,
968 CQ.DEPR2_ENJOYMENT_PAST_WEEK: FN_DEPR2,
969 CQ.DEPR3_DAYS_PAST_WEEK: FN_DEPR3,
970 CQ.DEPR4_GT_3H_ANY_DAY: FN_DEPR4,
971 CQ.DEPR_CONTENT: FN_DEPR_CONTENT,
972 CQ.DEPR5_COULD_CHEER_UP: FN_DEPR5,
973 CQ.DEPR_DUR: FN_DEPR_DUR,
974 CQ.DEPTH1_DIURNAL_VARIATION: FN_DEPTH1,
975 CQ.DEPTH2_LIBIDO: FN_DEPTH2,
976 CQ.DEPTH3_RESTLESS: FN_DEPTH3,
977 CQ.DEPTH4_SLOWED: FN_DEPTH4,
978 CQ.DEPTH5_GUILT: FN_DEPTH5,
979 CQ.DEPTH6_WORSE_THAN_OTHERS: FN_DEPTH6,
980 CQ.DEPTH7_HOPELESS: FN_DEPTH7,
981 CQ.DEPTH8_LNWL: FN_DEPTH8,
982 CQ.DEPTH9_SUICIDE_THOUGHTS: FN_DEPTH9,
983 CQ.DEPTH10_SUICIDE_METHOD: FN_DEPTH10,
984 CQ.DOCTOR: FN_DOCTOR,
985 # CQ.DOCTOR2_PLEASE_TALK_TO: # info only
986 # CQ.DEPR_OUTRO: # info only
988 CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH: FN_WORRY_MAND1,
989 CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH: FN_WORRY_MAND2,
990 CQ.WORRY_CONT1: FN_WORRY_CONT1,
991 # CQ.WORRY1_INFO_ONLY: # info only
992 CQ.WORRY2_DAYS_PAST_WEEK: FN_WORRY2,
993 CQ.WORRY3_TOO_MUCH: FN_WORRY3,
994 CQ.WORRY4_HOW_UNPLEASANT: FN_WORRY4,
995 CQ.WORRY5_GT_3H_ANY_DAY: FN_WORRY5,
996 CQ.WORRY_DUR: FN_WORRY_DUR,
998 CQ.ANX_MAND1_ANXIETY_PAST_MONTH: FN_ANX_MAND1,
999 CQ.ANX_MAND2_TENSION_PAST_MONTH: FN_ANX_MAND2,
1000 CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH: FN_ANX_PHOBIA1,
1001 CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: FN_ANX_PHOBIA2,
1002 # CQ.ANX1_INFO_ONLY: # info only
1003 CQ.ANX2_GENERAL_DAYS_PAST_WEEK: FN_ANX2,
1004 CQ.ANX3_GENERAL_HOW_UNPLEASANT: FN_ANX3,
1005 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS: FN_ANX4,
1006 CQ.ANX5_GENERAL_GT_3H_ANY_DAY: FN_ANX5,
1007 CQ.ANX_DUR_GENERAL: FN_ANX_DUR,
1009 CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH: FN_PHOBIAS_MAND,
1010 CQ.PHOBIAS_TYPE1: FN_PHOBIAS_TYPE1,
1011 CQ.PHOBIAS1_DAYS_PAST_WEEK: FN_PHOBIAS1,
1012 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS: FN_PHOBIAS2,
1013 CQ.PHOBIAS3_AVOIDANCE: FN_PHOBIAS3,
1014 CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: FN_PHOBIAS4,
1015 CQ.PHOBIAS_DUR: FN_PHOBIAS_DUR,
1017 CQ.PANIC_MAND_PAST_MONTH: FN_PANIC_MAND,
1018 CQ.PANIC1_NUM_PAST_WEEK: FN_PANIC1,
1019 CQ.PANIC2_HOW_UNPLEASANT: FN_PANIC2,
1020 CQ.PANIC3_PANIC_GE_10_MIN: FN_PANIC3,
1021 CQ.PANIC4_RAPID_ONSET: FN_PANIC4,
1022 # CQ.PANSYM: # multiple stems
1023 CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER: FN_PANIC5,
1024 CQ.PANIC_DUR: FN_PANIC_DUR,
1026 # CQ.ANX_OUTRO: # info only
1028 CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH: FN_COMP_MAND1,
1029 CQ.COMP1_DAYS_PAST_WEEK: FN_COMP1,
1030 CQ.COMP2_TRIED_TO_STOP: FN_COMP2,
1031 CQ.COMP3_UPSETTING: FN_COMP3,
1032 CQ.COMP4_MAX_N_REPETITIONS: FN_COMP4,
1033 CQ.COMP_DUR: FN_COMP_DUR,
1035 CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH: FN_OBSESS_MAND1,
1036 CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: FN_OBSESS_MAND2,
1037 CQ.OBSESS1_DAYS_PAST_WEEK: FN_OBSESS1,
1038 CQ.OBSESS2_TRIED_TO_STOP: FN_OBSESS2,
1039 CQ.OBSESS3_UPSETTING: FN_OBSESS3,
1040 CQ.OBSESS4_MAX_DURATION: FN_OBSESS4,
1041 CQ.OBSESS_DUR: FN_OBSESS_DUR,
1043 # CQ.OVERALL1: # info only
1044 CQ.OVERALL2_IMPACT_PAST_WEEK: FN_OVERALL2,
1045}
1047# Questions for which 1 = no, 2 = yes (+/- other options)
1048QUESTIONS_1_NO_2_YES = [
1049 CQ.APPETITE1_LOSS_PAST_MONTH,
1050 CQ.WEIGHT1_LOSS_PAST_MONTH,
1051 CQ.WEIGHT2_TRYING_TO_LOSE,
1052 CQ.APPETITE2_INCREASE_PAST_MONTH,
1053 CQ.WEIGHT4_INCREASE_PAST_MONTH, # may also offer "yes but pregnant" # noqa
1054 CQ.SOMATIC_MAND1_PAIN_PAST_MONTH,
1055 CQ.SOMATIC_MAND2_DISCOMFORT,
1056 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY,
1057 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING, # also has other options # noqa
1058 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY,
1059 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING, # also has other options # noqa
1060 CQ.FATIGUE_MAND1_TIRED_PAST_MONTH,
1061 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY,
1062 CQ.FATIGUE_TIRED3_HAD_TO_PUSH,
1063 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE, # also has other options
1064 CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH,
1065 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY,
1066 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH,
1067 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE, # also has other options
1068 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH,
1069 CQ.CONC_MAND2_FORGETFUL_PAST_MONTH,
1070 CQ.CONC3_CONC_PREVENTED_ACTIVITIES,
1071 CQ.CONC4_FORGOTTEN_IMPORTANT,
1072 CQ.SLEEP_MAND1_LOSS_PAST_MONTH,
1073 CQ.SLEEP_EMW_PAST_WEEK,
1074 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH,
1075 CQ.IRRIT2_GT_1H_ANY_DAY,
1076 CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH,
1077 CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS,
1078 CQ.HYPO2_WORRY_TOO_MUCH,
1079 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH,
1080 CQ.DEPR1_LOW_MOOD_PAST_WEEK,
1081 CQ.DEPR4_GT_3H_ANY_DAY,
1082 CQ.DEPTH3_RESTLESS,
1083 CQ.DEPTH4_SLOWED,
1084 CQ.DEPTH6_WORSE_THAN_OTHERS,
1085 CQ.DEPTH7_HOPELESS,
1086 CQ.DEPTH10_SUICIDE_METHOD,
1087 CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH,
1088 CQ.WORRY3_TOO_MUCH,
1089 CQ.WORRY5_GT_3H_ANY_DAY,
1090 CQ.ANX_MAND1_ANXIETY_PAST_MONTH,
1091 CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH,
1092 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS,
1093 CQ.ANX5_GENERAL_GT_3H_ANY_DAY,
1094 CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH,
1095 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS,
1096 CQ.PHOBIAS3_AVOIDANCE,
1097 CQ.PANIC4_RAPID_ONSET,
1098 CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER,
1099 CQ.COMP2_TRIED_TO_STOP,
1100 CQ.COMP3_UPSETTING,
1101 CQ.OBSESS2_TRIED_TO_STOP,
1102 CQ.OBSESS3_UPSETTING,
1103]
1104# Questions for which 1 = yes, 2 = no (+/- other options)
1105QUESTIONS_1_YES_2_NO = [
1106 CQ.DISABLE,
1107 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION,
1108 CQ.HYPO4_CAN_DISTRACT,
1109]
1110# Yes-no (or no-yes) questions but with specific text
1111QUESTIONS_YN_SPECIFIC_TEXT = [
1112 CQ.WEIGHT2_TRYING_TO_LOSE,
1113 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY,
1114 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY,
1115 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY,
1116 CQ.FATIGUE_TIRED3_HAD_TO_PUSH,
1117 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY,
1118 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH,
1119 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH,
1120 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION,
1121 CQ.CONC4_FORGOTTEN_IMPORTANT,
1122 CQ.SLEEP_EMW_PAST_WEEK,
1123 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH,
1124 CQ.IRRIT2_GT_1H_ANY_DAY,
1125 CQ.HYPO2_WORRY_TOO_MUCH,
1126 CQ.HYPO4_CAN_DISTRACT,
1127 CQ.DEPR1_LOW_MOOD_PAST_WEEK,
1128 CQ.DEPR4_GT_3H_ANY_DAY,
1129 CQ.DEPTH6_WORSE_THAN_OTHERS,
1130 CQ.DEPTH7_HOPELESS,
1131 CQ.WORRY3_TOO_MUCH,
1132 CQ.WORRY5_GT_3H_ANY_DAY,
1133 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS,
1134 CQ.ANX5_GENERAL_GT_3H_ANY_DAY,
1135 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS,
1136 CQ.PHOBIAS3_AVOIDANCE,
1137 CQ.COMP2_TRIED_TO_STOP,
1138 CQ.COMP3_UPSETTING,
1139 CQ.OBSESS2_TRIED_TO_STOP,
1140 CQ.OBSESS3_UPSETTING,
1141]
1142# Demographics questions (optional for diagnosis)
1143QUESTIONS_DEMOGRAPHICS = [
1144 CQ.ETHNIC,
1145 CQ.MARRIED,
1146 CQ.EMPSTAT,
1147 CQ.EMPTYPE,
1148 CQ.HOME,
1149]
1150# "Questions" that are just a prompt screen
1151QUESTIONS_PROMPT_ONLY = {
1152 # Maps questions to their prompt's xstring name
1153 CQ.INTRO_1: "intro_1",
1154 CQ.INTRO_2: "intro_2",
1155 CQ.INTRO_DEMOGRAPHICS: "intro_demographics_statement",
1157 CQ.HEALTH_WELLBEING: "health_wellbeing_statement",
1158 CQ.DOCTOR2_PLEASE_TALK_TO: "doctor2",
1159 CQ.DEPR_OUTRO: "depr_outro",
1160 CQ.WORRY1_INFO_ONLY: "worry1",
1161 CQ.ANX1_INFO_ONLY: "anx1",
1162 CQ.ANX_OUTRO: "anx_outro",
1163 CQ.OVERALL1_INFO_ONLY: "overall1",
1164 CQ.THANKS_FINISHED: "end",
1165}
1166# "How many days per week" questions
1167# "Overall duration" questions
1168QUESTIONS_OVERALL_DURATION = [
1169 CQ.SOMATIC_DUR,
1170 CQ.FATIGUE_DUR,
1171 CQ.CONC_DUR,
1172 CQ.FORGET_DUR,
1173 CQ.SLEEP_DUR,
1174 CQ.IRRIT_DUR,
1175 CQ.HYPO_DUR,
1176 CQ.DEPR_DUR,
1177 CQ.WORRY_DUR,
1178 CQ.ANX_DUR_GENERAL,
1179 CQ.PHOBIAS_DUR,
1180 CQ.PANIC_DUR,
1181 CQ.COMP_DUR,
1182 CQ.OBSESS_DUR,
1183]
1184# Multi-way questions, other than yes/no ones.
1185QUESTIONS_MULTIWAY = {
1186 # Maps questions to first and last number of answers.
1187 CQ.WEIGHT3_LOST_LOTS: (1, 2),
1188 CQ.WEIGHT4_INCREASE_PAST_MONTH: (1, 2), # may be modified to 3 if female
1189 CQ.WEIGHT5_GAINED_LOTS: (1, 2),
1190 CQ.GP_YEAR: (0, 4), # unusual; starts at 0
1191 CQ.ILLNESS: (1, 8),
1192 CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: (1, 3),
1193 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: (1, 3),
1194 CQ.SOMATIC_DIS1_PSYCHOL_EXAC: (1, 3),
1195 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: (1, 3),
1196 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: (1, 3),
1197 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: (1, 3),
1198 CQ.SLEEP_LOSE2_DIS_WORST_DURATION: (1, 4),
1199 CQ.SLEEP_CAUSE: (1, 6),
1200 CQ.SLEEP_MAND2_GAIN_PAST_MONTH: (1, 3),
1201 CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: (1, 4),
1202 CQ.IRRIT_MAND2_THINGS_PAST_MONTH: (1, 3),
1203 CQ.IRRIT3_WANTED_TO_SHOUT: (1, 3),
1204 CQ.IRRIT4_ARGUMENTS: (1, 3),
1205 CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: (1, 3),
1206 CQ.DEPR2_ENJOYMENT_PAST_WEEK: (1, 3),
1207 CQ.DEPR5_COULD_CHEER_UP: (1, 3),
1208 CQ.DEPTH1_DIURNAL_VARIATION: (1, 4),
1209 CQ.DEPTH2_LIBIDO: (1, 4),
1210 CQ.DEPTH5_GUILT: (1, 4),
1211 CQ.DEPTH8_LNWL: (1, 3),
1212 CQ.DEPTH9_SUICIDE_THOUGHTS: (1, 3),
1213 CQ.DOCTOR: (1, 3),
1214 CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: (1, 2),
1215 CQ.PHOBIAS_TYPE1: (1, 9),
1216 CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: (1, 3),
1217 CQ.PANIC_MAND_PAST_MONTH: (1, 3),
1218 CQ.PANIC1_NUM_PAST_WEEK: (1, 3),
1219 CQ.PANIC2_HOW_UNPLEASANT: (1, 3),
1220 CQ.PANIC3_PANIC_GE_10_MIN: (1, 2),
1221 CQ.COMP4_MAX_N_REPETITIONS: (1, 3),
1222 CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: (1, 2),
1223 CQ.OBSESS4_MAX_DURATION: (1, 2),
1224 CQ.OVERALL2_IMPACT_PAST_WEEK: (1, 4),
1225}
1226QUESTIONS_MULTIWAY_WITH_EXTRA_STEM = {
1227 # Maps questions to first and last number of answers.
1228 CQ.ETHNIC: (1, 7), # 7 includes our additional "prefer not to say"
1229 CQ.MARRIED: (1, 6), # 6 includes our additional "prefer not to say"
1230 CQ.EMPSTAT: (1, 8), # 8 includes our additional "prefer not to say"
1231 CQ.EMPTYPE: (1, 7), # 7 includes our additional "not applicable" + "prefer not to say" # noqa
1232 CQ.HOME: (1, 7), # 7 includes our additional "prefer not to say"
1233}
1234QUESTIONS_DAYS_PER_WEEK = [
1235 CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK,
1236 CQ.SOMATIC_DIS2_DAYS_PAST_WEEK,
1237 CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK,
1238 CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK,
1239 CQ.CONC1_CONC_DAYS_PAST_WEEK,
1240 CQ.IRRIT1_DAYS_PER_WEEK,
1241 CQ.HYPO1_DAYS_PAST_WEEK,
1242 CQ.DEPR3_DAYS_PAST_WEEK,
1243 CQ.WORRY2_DAYS_PAST_WEEK,
1244 CQ.ANX2_GENERAL_DAYS_PAST_WEEK,
1245 CQ.PHOBIAS1_DAYS_PAST_WEEK,
1246 # not this: CQ.PHOBIAS4_AVOIDANCE_FREQUENCY -- different phrasing
1247 # not this: CQ.PANIC1_FREQUENCY
1248 CQ.COMP1_DAYS_PAST_WEEK,
1249 CQ.OBSESS1_DAYS_PAST_WEEK,
1250]
1251QUESTIONS_NIGHTS_PER_WEEK = [
1252 CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK,
1253 CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK,
1254 CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK, # (*) see below
1255 # (*) Probably an error in the original:
1256 # "On how many nights in the PAST SEVEN NIGHTS did you have problems
1257 # with your sleep? (1) None. (2) Between one and three days. (3) Four
1258 # days or more." Note day/night confusion. Altered to "nights".
1259 CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK,
1260]
1261QUESTIONS_HOW_UNPLEASANT_STANDARD = [
1262 CQ.SOMATIC_PAIN4_UNPLEASANT,
1263 CQ.SOMATIC_DIS4_UNPLEASANT,
1264 CQ.HYPO3_HOW_UNPLEASANT,
1265 CQ.WORRY4_HOW_UNPLEASANT,
1266 CQ.ANX3_GENERAL_HOW_UNPLEASANT,
1267]
1268QUESTIONS_FATIGUE_CAUSES = [
1269 CQ.FATIGUE_CAUSE1_TIRED,
1270 CQ.FATIGUE_CAUSE2_LACK_ENERGY,
1271]
1272QUESTIONS_STRESSORS = [
1273 CQ.DEPR_CONTENT,
1274 CQ.WORRY_CONT1,
1275]
1276QUESTIONS_NO_SOMETIMES_OFTEN = [
1277 CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH,
1278 CQ.ANX_MAND2_TENSION_PAST_MONTH,
1279 CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH,
1280 CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH,
1281 # and no-sometimes-often values also used by:
1282 # CQ.PANIC_MAND_PAST_MONTH
1283 # ... but with variations on the text.
1284]
1287# =============================================================================
1288# Ancillary functions
1289# =============================================================================
1291def fieldname_for_q(q: CisrQuestion) -> str:
1292 return FIELDNAME_FOR_QUESTION.get(q, "")
1295def enum_to_int(qe: CisrQuestion) -> int:
1296 return qe.value
1299def int_to_enum(qi: int) -> CisrQuestion:
1300 # https://stackoverflow.com/questions/23951641/how-to-convert-int-to-enum-in-python # noqa
1301 return CisrQuestion(qi)
1304# =============================================================================
1305# CisrResult
1306# =============================================================================
1308class CisrResult(object):
1309 def __init__(self, record_decisions: bool = False) -> None:
1310 self.incomplete = False
1311 self.record_decisions = record_decisions
1312 self.decisions = [] # type: List[str]
1314 # Symptom scoring
1315 self.depression = 0 # DEPR in original
1316 self.depr_crit_1_mood_anhedonia_energy = 0 # DEPCRIT1
1317 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui = 0 # DEPCRIT2
1318 self.depr_crit_3_somatic_synd = 0 # DEPCRIT3
1319 # ... looks to me like the ICD-10 criteria for somatic syndrome
1320 # (e.g. F32.01, F32.11, F33.01, F33.11), with the "do you cheer up
1321 # when..." question (DEPR5) being the one for "lack of emotional
1322 # reactions to events or activities that normally produce an
1323 # emotional response".
1324 self.weight_change = WTCHANGE_NONE_OR_APPETITE_INCREASE # WTCHANGE IN original # noqa
1325 self.somatic_symptoms = 0 # SOMATIC in original
1326 self.fatigue = 0 # FATIGUE in original
1327 self.neurasthenia = 0 # NEURAS in original
1328 self.concentration_poor = 0 # CONC in original
1329 self.sleep_problems = 0 # SLEEP in original
1330 self.sleep_change = SLEEPCHANGE_NONE # SLEEPCH in original
1331 self.depressive_thoughts = 0 # DEPTHTS in original
1332 self.irritability = 0 # IRRIT in original
1333 self.diurnal_mood_variation = DIURNAL_MOOD_VAR_NONE # DVM in original
1334 self.libido_decreased = False # LIBID in original
1335 self.psychomotor_changes = PSYCHOMOTOR_NONE # PSYCHMOT in original
1336 self.suicidality = SUICIDE_INTENT_NONE # type: int # SUICID in original # noqa
1337 self.depression_at_least_2_weeks = False # DEPR_DUR >= 2 in original
1339 self.hypochondria = 0 # HYPO in original
1340 self.worry = 0 # WORRY in original
1341 self.anxiety = 0 # ANX in original
1342 self.anxiety_physical_symptoms = False # AN4 == 2 in original
1343 self.anxiety_at_least_2_weeks = False # ANX_DUR >= 2 in original
1344 self.phobias_flag = False # PHOBIAS_FLAG in original
1345 self.phobias_score = 0 # PHOBIAS in original
1346 self.phobias_type = 0 # PHOBIAS_TYPE in original
1347 self.phobic_avoidance = False # PHOBIAS3 == 2 in original
1348 self.panic = 0 # PANIC in original
1349 self.panic_rapid_onset = False # PANIC4 == 2 in original
1350 self.panic_symptoms_total = 0 # PANSYTOT in original
1352 self.compulsions = 0 # COMP in original
1353 self.compulsions_tried_to_stop = False # COMP2 == 2 in original
1354 self.compulsions_at_least_2_weeks = False # COMP_DUR >= 2 in original
1355 self.obsessions = 0 # OBSESS in original
1356 self.obsessions_tried_to_stop = False # OBSESS2 == 2 in original
1357 self.obsessions_at_least_2_weeks = False # OBSESS_DUR >= 2 in original
1359 self.functional_impairment = 0 # IMPAIR in original
1361 # Disorder flags
1362 self.obsessive_compulsive_disorder = False # OBCOMP in original
1363 self.depression_mild = False # DEPRMILD in original
1364 self.depression_moderate = False # DEPRMOD in original
1365 self.depression_severe = False # DEPRSEV in original
1366 self.chronic_fatigue_syndrome = False # CFS in original
1367 self.generalized_anxiety_disorder = False # GAD in original
1368 self.phobia_agoraphobia = False # PHOBAG in original
1369 self.phobia_social = False # PHOBSOC in original
1370 self.phobia_specific = False # PHOBSPEC in original
1371 self.panic_disorder = False # PANICD in original
1373 # Final diagnoses
1374 self.diagnosis_1 = DIAG_0_NO_DIAGNOSIS # DIAG1 in original
1375 self.diagnosis_2 = DIAG_0_NO_DIAGNOSIS # DIAG2 in original
1377 # -------------------------------------------------------------------------
1378 # Overall scoring
1379 # -------------------------------------------------------------------------
1381 def get_score(self) -> int: # SCORE in original
1382 return (
1383 self.somatic_symptoms +
1384 self.fatigue +
1385 self.concentration_poor +
1386 self.sleep_problems +
1387 self.irritability +
1388 self.hypochondria +
1389 self.depression +
1390 self.depressive_thoughts +
1391 self.worry +
1392 self.anxiety +
1393 self.phobias_score +
1394 self.panic +
1395 self.compulsions +
1396 self.obsessions
1397 )
1399 def needs_impairment_question(self) -> bool:
1400 # code in OVERALL1 in original
1401 threshold = 2 # for all symptoms
1402 return (
1403 self.somatic_symptoms >= threshold or
1404 self.hypochondria >= threshold or
1405 self.fatigue >= threshold or
1406 self.sleep_problems >= threshold or
1407 self.irritability >= threshold or
1408 self.concentration_poor >= threshold or
1409 self.depression >= threshold or
1410 self.depressive_thoughts >= threshold or
1411 self.phobias_score >= threshold or
1412 self.worry >= threshold or
1413 self.anxiety >= threshold or
1414 self.panic >= threshold or
1415 self.compulsions >= threshold or
1416 self.obsessions >= threshold
1417 )
1419 def has_somatic_syndrome(self) -> bool:
1420 return self.depr_crit_3_somatic_synd >= SOMATIC_SYNDROME_CRITERION
1422 def get_final_page(self) -> CisrQuestion:
1423 # see chooseFinalPage() in the C++ version
1424 return (
1425 CQ.OVERALL1_INFO_ONLY if self.needs_impairment_question()
1426 else CQ.THANKS_FINISHED
1427 )
1429 def decide(self, decision: str) -> None:
1430 if self.record_decisions:
1431 self.decisions.append(decision)
1433 def _showint(self, name: str, value: int) -> None:
1434 self.decide(f"{SCORE_PREFIX}{name}: {value}")
1436 def _showbool(self, name: str, value: bool) -> None:
1437 self.decide(f"{SCORE_PREFIX}{name}: {'true' if value else 'false'}")
1439 def diagnosis_name(self, diagnosis_code: int) -> str:
1440 if self.incomplete:
1441 # Do NOT offer diagnostic information based on partial data.
1442 # Might be dangerous (e.g. say "mild depressive episode" when it's
1443 # severe + incomplete information).
1444 return "INFORMATION INCOMPLETE"
1446 if diagnosis_code == DIAG_0_NO_DIAGNOSIS:
1447 return "No diagnosis identified"
1448 elif diagnosis_code == DIAG_1_MIXED_ANX_DEPR_DIS_MILD:
1449 return "Mixed anxiety and depressive disorder (mild)"
1450 elif diagnosis_code == DIAG_2_GENERALIZED_ANX_DIS_MILD:
1451 return "Generalized anxiety disorder (mild)"
1452 elif diagnosis_code == DIAG_3_OBSESSIVE_COMPULSIVE_DIS:
1453 return "Obsessive–compulsive disorder"
1454 elif diagnosis_code == DIAG_4_MIXED_ANX_DEPR_DIS:
1455 return "Mixed anxiety and depressive disorder"
1456 elif diagnosis_code == DIAG_5_SPECIFIC_PHOBIA:
1457 return "Specific (isolated) phobia"
1458 elif diagnosis_code == DIAG_6_SOCIAL_PHOBIA:
1459 return "Social phobia"
1460 elif diagnosis_code == DIAG_7_AGORAPHOBIA:
1461 return "Agoraphobia"
1462 elif diagnosis_code == DIAG_8_GENERALIZED_ANX_DIS:
1463 return "Generalized anxiety disorder"
1464 elif diagnosis_code == DIAG_9_PANIC_DIS:
1465 return "Panic disorder"
1466 elif diagnosis_code == DIAG_10_MILD_DEPR_EPISODE:
1467 return "Mild depressive episode"
1468 elif diagnosis_code == DIAG_11_MOD_DEPR_EPISODE:
1469 return "Moderate depressive episode"
1470 elif diagnosis_code == DIAG_12_SEVERE_DEPR_EPISODE:
1471 return "Severe depressive episode"
1472 else:
1473 return "[INTERNAL ERROR: BAD DIAGNOSIS CODE]"
1475 def diagnosis_icd10_code(self, diagnosis_code: int) -> str:
1476 if self.incomplete:
1477 return ""
1479 if diagnosis_code == DIAG_0_NO_DIAGNOSIS:
1480 return ""
1481 elif diagnosis_code == DIAG_1_MIXED_ANX_DEPR_DIS_MILD:
1482 return "F41.2" # no sub-code for "mild"
1483 elif diagnosis_code == DIAG_2_GENERALIZED_ANX_DIS_MILD:
1484 return "F41.1" # no sub-code for "mild"
1485 elif diagnosis_code == DIAG_3_OBSESSIVE_COMPULSIVE_DIS:
1486 return "Obsessive–compulsive disorder"
1487 elif diagnosis_code == DIAG_4_MIXED_ANX_DEPR_DIS:
1488 return "F41.2"
1489 elif diagnosis_code == DIAG_5_SPECIFIC_PHOBIA:
1490 return "F40.2"
1491 elif diagnosis_code == DIAG_6_SOCIAL_PHOBIA:
1492 return "F40.1"
1493 elif diagnosis_code == DIAG_7_AGORAPHOBIA:
1494 return "F40.0" # not clear whether F40.00/F40.01 are distinguished
1495 elif diagnosis_code == DIAG_8_GENERALIZED_ANX_DIS:
1496 return "F41.1"
1497 elif diagnosis_code == DIAG_9_PANIC_DIS:
1498 return "F41.0"
1499 elif diagnosis_code == DIAG_10_MILD_DEPR_EPISODE:
1500 if self.has_somatic_syndrome():
1501 return "F32.01"
1502 else:
1503 return "F32.00"
1504 elif diagnosis_code == DIAG_11_MOD_DEPR_EPISODE:
1505 if self.has_somatic_syndrome():
1506 return "F32.11"
1507 else:
1508 return "F32.10"
1509 elif diagnosis_code == DIAG_12_SEVERE_DEPR_EPISODE:
1510 return "F32.2 or F32.3"
1511 else:
1512 return "[INTERNAL ERROR: BAD DIAGNOSIS CODE]"
1514 def has_diagnosis(self, diagnosis_code: int) -> bool:
1515 return not self.incomplete and diagnosis_code != DIAG_0_NO_DIAGNOSIS
1517 def has_diagnosis_1(self) -> bool:
1518 return self.has_diagnosis(self.diagnosis_1)
1520 def has_diagnosis_2(self) -> bool:
1521 return self.has_diagnosis(self.diagnosis_1)
1523 def diagnosis_1_name(self) -> str:
1524 return self.diagnosis_name(self.diagnosis_1)
1526 def diagnosis_1_icd10_code(self) -> str:
1527 return self.diagnosis_icd10_code(self.diagnosis_1)
1529 def diagnosis_2_name(self) -> str:
1530 return self.diagnosis_name(self.diagnosis_2)
1532 def diagnosis_2_icd10_code(self) -> str:
1533 return self.diagnosis_icd10_code(self.diagnosis_2)
1535 def finalize(self) -> None:
1536 at_least_1_activity_impaired = (self.functional_impairment >=
1537 OVERALL_IMPAIRMENT_STOP_1_ACTIVITY)
1538 score = self.get_score()
1540 # GAD
1541 if (self.anxiety >= 2 and
1542 self.anxiety_physical_symptoms and
1543 self.anxiety_at_least_2_weeks):
1544 self.decide(
1545 "Anxiety score >= 2 AND physical symptoms of anxiety AND "
1546 "anxiety for at least 2 weeks. "
1547 "Setting generalized_anxiety_disorder.")
1548 self.generalized_anxiety_disorder = True
1550 # Panic
1551 if self.panic >= 3 and self.panic_rapid_onset:
1552 self.decide("Panic score >= 3 AND panic_rapid_onset. "
1553 "Setting panic_disorder.")
1554 self.panic_disorder = True
1556 # Phobias
1557 if (self.phobias_type == PHOBIATYPES_AGORAPHOBIA and
1558 self.phobic_avoidance and
1559 self.phobias_score >= 2):
1560 self.decide("Phobia type is agoraphobia AND phobic avoidance AND"
1561 "phobia score >= 2. Setting phobia_agoraphobia.")
1562 self.phobia_agoraphobia = True
1563 if (self.phobias_type == PHOBIATYPES_SOCIAL and
1564 self.phobic_avoidance and
1565 self.phobias_score >= 2):
1566 self.decide("Phobia type is social AND phobic avoidance AND"
1567 "phobia score >= 2. Setting phobia_social.")
1568 self.phobia_social = True
1569 if (self.phobias_type == PHOBIATYPES_SOCIAL and
1570 self.phobic_avoidance and
1571 self.phobias_score >= 2):
1572 self.decide(
1573 "Phobia type is (animals/enclosed/heights OR other) AND "
1574 "phobic avoidance AND phobia score >= 2. "
1575 "Setting phobia_specific.")
1576 self.phobia_specific = True
1578 # OCD
1579 if (self.obsessions + self.compulsions >= 6 and
1580 self.obsessions_tried_to_stop and
1581 self.obsessions_at_least_2_weeks and
1582 at_least_1_activity_impaired):
1583 self.decide("obsessions + compulsions >= 6 AND "
1584 "tried to stop obsessions AND "
1585 "obsessions for at least 2 weeks AND "
1586 "at least 1 activity impaired. "
1587 "Setting obsessive_compulsive_disorder.")
1588 self.obsessive_compulsive_disorder = True
1589 if (self.obsessions + self.compulsions >= 6 and
1590 self.compulsions_tried_to_stop and
1591 self.compulsions_at_least_2_weeks and
1592 at_least_1_activity_impaired):
1593 self.decide("obsessions + compulsions >= 6 AND "
1594 "tried to stop compulsions AND "
1595 "compulsions for at least 2 weeks AND "
1596 "at least 1 activity impaired. "
1597 "Setting obsessive_compulsive_disorder.")
1598 self.obsessive_compulsive_disorder = True
1599 if (self.obsessions == 4 and
1600 self.obsessions_tried_to_stop and
1601 self.obsessions_at_least_2_weeks and
1602 at_least_1_activity_impaired):
1603 # NOTE: 4 is the maximum for obsessions
1604 self.decide("obsessions == 4 AND "
1605 "tried to stop obsessions AND "
1606 "obsessions for at least 2 weeks AND "
1607 "at least 1 activity impaired. "
1608 "Setting obsessive_compulsive_disorder.")
1609 self.obsessive_compulsive_disorder = True
1610 if (self.compulsions == 4 and
1611 self.compulsions_tried_to_stop and
1612 self.compulsions_at_least_2_weeks and
1613 at_least_1_activity_impaired):
1614 # NOTE: 4 is the maximum for compulsions
1615 self.decide("compulsions == 4 AND "
1616 "tried to stop compulsions AND "
1617 "compulsions for at least 2 weeks AND "
1618 "at least 1 activity impaired. "
1619 "Setting obsessive_compulsive_disorder.")
1620 self.obsessive_compulsive_disorder = True
1622 # Depression
1623 if (self.depression_at_least_2_weeks and
1624 self.depr_crit_1_mood_anhedonia_energy > 1 and
1625 self.depr_crit_1_mood_anhedonia_energy +
1626 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 3):
1627 self.decide("Depressive symptoms >=2 weeks AND "
1628 "depr_crit_1_mood_anhedonia_energy > 1 AND "
1629 "depr_crit_1_mood_anhedonia_energy + "
1630 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 3. "
1631 "Setting depression_mild.")
1632 self.depression_mild = True
1633 if (self.depression_at_least_2_weeks and
1634 self.depr_crit_1_mood_anhedonia_energy > 1 and
1635 (self.depr_crit_1_mood_anhedonia_energy +
1636 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui) > 5):
1637 self.decide("Depressive symptoms >=2 weeks AND "
1638 "depr_crit_1_mood_anhedonia_energy > 1 AND "
1639 "depr_crit_1_mood_anhedonia_energy + "
1640 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 5. "
1641 "Setting depression_moderate.")
1642 self.depression_moderate = True
1643 if (self.depression_at_least_2_weeks and
1644 self.depr_crit_1_mood_anhedonia_energy == 3 and
1645 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 4):
1646 self.decide("Depressive symptoms >=2 weeks AND "
1647 "depr_crit_1_mood_anhedonia_energy == 3 AND "
1648 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 4. "
1649 "Setting depression_severe.")
1650 self.depression_severe = True
1652 # CFS
1653 if self.neurasthenia >= 2:
1654 # The original had a pointless check for "DIAG1 == 0" too, but that
1655 # was always true.
1656 self.decide("neurasthenia >= 2. Setting chronic_fatigue_syndrome.")
1657 self.chronic_fatigue_syndrome = True
1659 # Final diagnostic hierarchy
1661 # ... primary diagnosis
1662 if score >= 12:
1663 self.decide("Total score >= 12. Setting diagnosis_1 to "
1664 "DIAG_1_MIXED_ANX_DEPR_DIS_MILD.")
1665 self.diagnosis_1 = DIAG_1_MIXED_ANX_DEPR_DIS_MILD
1666 if self.generalized_anxiety_disorder:
1667 self.decide("generalized_anxiety_disorder is true. Setting "
1668 "diagnosis_1 to DIAG_2_GENERALIZED_ANX_DIS_MILD.")
1669 self.diagnosis_1 = DIAG_2_GENERALIZED_ANX_DIS_MILD
1670 if self.obsessive_compulsive_disorder:
1671 self.decide("obsessive_compulsive_disorder is true. Setting "
1672 "diagnosis_1 to DIAG_3_OBSESSIVE_COMPULSIVE_DIS.")
1673 self.diagnosis_1 = DIAG_3_OBSESSIVE_COMPULSIVE_DIS
1674 if score >= 20:
1675 self.decide("Total score >= 20. Setting diagnosis_1 to "
1676 "DIAG_4_MIXED_ANX_DEPR_DIS.")
1677 self.diagnosis_1 = DIAG_4_MIXED_ANX_DEPR_DIS
1678 if self.phobia_specific:
1679 self.decide("phobia_specific is true. Setting diagnosis_1 to "
1680 "DIAG_5_SPECIFIC_PHOBIA.")
1681 self.diagnosis_1 = DIAG_5_SPECIFIC_PHOBIA
1682 if self.phobia_social:
1683 self.decide("phobia_social is true. Setting diagnosis_1 to "
1684 "DIAG_6_SOCIAL_PHOBIA.")
1685 self.diagnosis_1 = DIAG_6_SOCIAL_PHOBIA
1686 if self.phobia_agoraphobia:
1687 self.decide("phobia_agoraphobia is true. Setting diagnosis_1 to "
1688 "DIAG_7_AGORAPHOBIA.")
1689 self.diagnosis_1 = DIAG_7_AGORAPHOBIA
1690 if self.generalized_anxiety_disorder and score >= 20:
1691 self.decide("generalized_anxiety_disorder is true AND "
1692 "score >= 20. Setting diagnosis_1 to "
1693 "DIAG_8_GENERALIZED_ANX_DIS.")
1694 self.diagnosis_1 = DIAG_8_GENERALIZED_ANX_DIS
1695 if self.panic_disorder:
1696 self.decide("panic_disorder is true. Setting diagnosis_1 to "
1697 "DIAG_9_PANIC_DIS.")
1698 self.diagnosis_1 = DIAG_9_PANIC_DIS
1699 if self.depression_mild:
1700 self.decide("depression_mild is true. Setting diagnosis_1 to "
1701 "DIAG_10_MILD_DEPR_EPISODE.")
1702 self.diagnosis_1 = DIAG_10_MILD_DEPR_EPISODE
1703 if self.depression_moderate:
1704 self.decide("depression_moderate is true. Setting diagnosis_1 to "
1705 "DIAG_11_MOD_DEPR_EPISODE.")
1706 self.diagnosis_1 = DIAG_11_MOD_DEPR_EPISODE
1707 if self.depression_severe:
1708 self.decide("depression_severe is true. Setting diagnosis_1 to "
1709 "DIAG_12_SEVERE_DEPR_EPISODE.")
1710 self.diagnosis_1 = DIAG_12_SEVERE_DEPR_EPISODE
1712 # ... secondary diagnosis
1713 if score >= 12 and self.diagnosis_1 >= 2:
1714 self.decide(
1715 "score >= 12 AND diagnosis_1 >= 2. "
1716 "Setting diagnosis_2 to DIAG_1_MIXED_ANX_DEPR_DIS_MILD.")
1717 self.diagnosis_2 = DIAG_1_MIXED_ANX_DEPR_DIS_MILD
1718 if self.generalized_anxiety_disorder and self.diagnosis_1 >= 3:
1719 self.decide(
1720 "generalized_anxiety_disorder is true AND "
1721 "diagnosis_1 >= 3. "
1722 "Setting diagnosis_2 to DIAG_2_GENERALIZED_ANX_DIS_MILD.")
1723 self.diagnosis_2 = DIAG_2_GENERALIZED_ANX_DIS_MILD
1724 if self.obsessive_compulsive_disorder and self.diagnosis_1 >= 4:
1725 self.decide(
1726 "obsessive_compulsive_disorder is true AND "
1727 "diagnosis_1 >= 4. "
1728 "Setting diagnosis_2 to DIAG_3_OBSESSIVE_COMPULSIVE_DIS.")
1729 self.diagnosis_2 = DIAG_3_OBSESSIVE_COMPULSIVE_DIS
1730 if score >= 20 and self.diagnosis_1 >= 5:
1731 self.decide("score >= 20 AND diagnosis_1 >= 5. "
1732 "Setting diagnosis_2 to DIAG_4_MIXED_ANX_DEPR_DIS.")
1733 self.diagnosis_2 = DIAG_4_MIXED_ANX_DEPR_DIS
1734 if self.phobia_specific and self.diagnosis_1 >= 6:
1735 self.decide("phobia_specific is true AND diagnosis_1 >= 6. "
1736 "Setting diagnosis_2 to DIAG_5_SPECIFIC_PHOBIA.")
1737 self.diagnosis_2 = DIAG_5_SPECIFIC_PHOBIA
1738 if self.phobia_social and self.diagnosis_1 >= 7:
1739 self.decide("phobia_social is true AND diagnosis_1 >= 7. "
1740 "Setting diagnosis_2 to DIAG_6_SOCIAL_PHOBIA.")
1741 self.diagnosis_2 = DIAG_6_SOCIAL_PHOBIA
1742 if self.phobia_agoraphobia and self.diagnosis_1 >= 8:
1743 self.decide("phobia_agoraphobia is true AND diagnosis_1 >= 8. "
1744 "Setting diagnosis_2 to DIAG_7_AGORAPHOBIA.")
1745 self.diagnosis_2 = DIAG_7_AGORAPHOBIA
1746 if (self.generalized_anxiety_disorder and score >= 20 and
1747 self.diagnosis_1 >= 9):
1748 self.decide("generalized_anxiety_disorder is true AND "
1749 "score >= 20 AND "
1750 "diagnosis_1 >= 9. "
1751 "Setting diagnosis_2 to DIAG_8_GENERALIZED_ANX_DIS.")
1752 self.diagnosis_2 = DIAG_8_GENERALIZED_ANX_DIS
1753 if self.panic_disorder and self.diagnosis_1 >= 9:
1754 self.decide("panic_disorder is true AND diagnosis_1 >= 9. "
1755 "Setting diagnosis_2 to DIAG_9_PANIC_DIS.")
1756 self.diagnosis_2 = DIAG_9_PANIC_DIS
1758 # In summary:
1759 self.decide("FINISHED.")
1760 self.decide("--- Final scores:")
1761 self._showint("depression", self.depression)
1762 self._showint("depr_crit_1_mood_anhedonia_energy",
1763 self.depr_crit_1_mood_anhedonia_energy)
1764 self._showint("depr_crit_2_app_cnc_slp_mtr_glt_wth_sui",
1765 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui)
1766 self._showint("depr_crit_3_somatic_synd",
1767 self.depr_crit_3_somatic_synd)
1768 self._showint("weight_change", self.weight_change)
1769 self._showint("somatic_symptoms", self.somatic_symptoms)
1770 self._showint("fatigue", self.fatigue)
1771 self._showint("neurasthenia", self.neurasthenia)
1772 self._showint("concentration_poor", self.concentration_poor)
1773 self._showint("sleep_problems", self.sleep_problems)
1774 self._showint("sleep_change", self.sleep_change)
1775 self._showint("depressive_thoughts", self.depressive_thoughts)
1776 self._showint("irritability", self.irritability)
1777 self._showint("diurnal_mood_variation", self.diurnal_mood_variation)
1778 self._showbool("libido_decreased", self.libido_decreased)
1779 self._showint("psychomotor_changes", self.psychomotor_changes)
1780 self._showint("suicidality", self.suicidality)
1781 self._showbool("depression_at_least_2_weeks",
1782 self.depression_at_least_2_weeks)
1784 self._showint("hypochondria", self.hypochondria)
1785 self._showint("worry", self.worry)
1786 self._showint("anxiety", self.anxiety)
1787 self._showbool("anxiety_physical_symptoms",
1788 self.anxiety_physical_symptoms)
1789 self._showbool("anxiety_at_least_2_weeks",
1790 self.anxiety_at_least_2_weeks)
1791 self._showbool("phobias_flag", self.phobias_flag)
1792 self._showint("phobias_score", self.phobias_score)
1793 self._showint("phobias_type", self.phobias_type)
1794 self._showbool("phobic_avoidance", self.phobic_avoidance)
1795 self._showint("panic", self.panic)
1796 self._showbool("panic_rapid_onset", self.panic_rapid_onset)
1797 self._showint("panic_symptoms_total", self.panic_symptoms_total)
1799 self._showint("compulsions", self.compulsions)
1800 self._showbool("compulsions_tried_to_stop",
1801 self.compulsions_tried_to_stop)
1802 self._showbool("compulsions_at_least_2_weeks",
1803 self.compulsions_at_least_2_weeks)
1804 self._showint("obsessions", self.obsessions)
1805 self._showbool("obsessions_tried_to_stop",
1806 self.obsessions_tried_to_stop)
1807 self._showbool("obsessions_at_least_2_weeks",
1808 self.obsessions_at_least_2_weeks)
1810 self._showint("functional_impairment", self.functional_impairment)
1812 # Disorder flags
1813 self._showbool("obsessive_compulsive_disorder",
1814 self.obsessive_compulsive_disorder)
1815 self._showbool("depression_mild", self.depression_mild)
1816 self._showbool("depression_moderate", self.depression_moderate)
1817 self._showbool("depression_severe", self.depression_severe)
1818 self._showbool("chronic_fatigue_syndrome",
1819 self.chronic_fatigue_syndrome)
1820 self._showbool("generalized_anxiety_disorder",
1821 self.generalized_anxiety_disorder)
1822 self._showbool("phobia_agoraphobia", self.phobia_agoraphobia)
1823 self._showbool("phobia_social", self.phobia_social)
1824 self._showbool("phobia_specific", self.phobia_specific)
1825 self._showbool("panic_disorder", self.panic_disorder)
1827 self.decide("--- Final diagnoses:")
1828 self.decide("Probable primary diagnosis: " +
1829 self.diagnosis_name(self.diagnosis_1))
1830 self.decide("Probable secondary diagnosis: " +
1831 self.diagnosis_name(self.diagnosis_2))
1834# =============================================================================
1835# CISR
1836# =============================================================================
1838class Cisr(TaskHasPatientMixin, Task):
1839 """
1840 Server implementation of the CIS-R task.
1841 """
1842 __tablename__ = "cisr"
1843 shortname = "CIS-R"
1844 provides_trackers = False
1846 # Demographics
1848 ethnic = CamcopsColumn(
1849 FN_ETHNIC, Integer,
1850 comment=(
1851 CMT_DEMOGRAPHICS +
1852 "Ethnicity (1 white, 2 mixed, 3 Asian/British Asian, "
1853 "4 Black/Black British, 5 Chinese, 6 other, 7 prefer not to say)"
1854 ),
1855 permitted_value_checker=ONE_TO_SEVEN_CHECKER,
1856 )
1857 married = CamcopsColumn(
1858 FN_MARRIED, Integer,
1859 comment=(
1860 CMT_DEMOGRAPHICS +
1861 "Marital status (1 married/living as married, 2 single, "
1862 "3 separated, 4 divorced, 5 widowed, 6 prefer not to say)"
1863 ),
1864 permitted_value_checker=ONE_TO_SIX_CHECKER,
1865 )
1866 empstat = CamcopsColumn(
1867 FN_EMPSTAT, Integer,
1868 comment=(
1869 CMT_DEMOGRAPHICS +
1870 "Current employment status (1 working full time, "
1871 "2 working part time, 3 student, 4 retired, 5 houseperson, "
1872 "6 unemployed job seeker, 7 unemployed due to ill health,"
1873 "8 prefer not to say)"
1874 ),
1875 permitted_value_checker=ONE_TO_EIGHT_CHECKER,
1876 )
1877 emptype = CamcopsColumn(
1878 FN_EMPTYPE, Integer,
1879 comment=(
1880 CMT_DEMOGRAPHICS +
1881 "Current/last paid employment "
1882 "(1 self-employed with paid employees, "
1883 "2 self-employed with no paid employees, 3 employee, "
1884 "4 foreman/supervisor, 5 manager, 6 not applicable,"
1885 "7 prefer not to say)"
1886 ),
1887 permitted_value_checker=ONE_TO_SEVEN_CHECKER,
1888 )
1889 home = CamcopsColumn(
1890 FN_HOME, Integer,
1891 comment=(
1892 CMT_DEMOGRAPHICS +
1893 "Housing situation (1 home owner, 2 tenant, 3 living with "
1894 "relative/friend, 4 hostel/care home, 5 homeless, 6 other,"
1895 "7 prefer not to say)"
1896 ),
1897 permitted_value_checker=ONE_TO_SEVEN_CHECKER,
1898 )
1900 # Appetite/weight
1902 appetite1 = CamcopsColumn(
1903 FN_APPETITE1, Integer,
1904 comment="Marked appetite loss in past month" + CMT_1_NO_2_YES,
1905 permitted_value_checker=ONE_TO_TWO_CHECKER,
1906 )
1907 weight1 = CamcopsColumn(
1908 FN_WEIGHT1, Integer,
1909 comment="Weight loss in past month" + CMT_1_NO_2_YES,
1910 permitted_value_checker=ONE_TO_TWO_CHECKER,
1911 )
1912 weight2 = CamcopsColumn(
1913 FN_WEIGHT2, Integer,
1914 comment="Weight loss: trying to lose weight?" + CMT_1_NO_2_YES,
1915 permitted_value_checker=ONE_TO_TWO_CHECKER,
1916 )
1917 weight3 = CamcopsColumn(
1918 FN_WEIGHT3, Integer,
1919 comment="Weight loss amount (1: ≥0.5 stones; 2: <0.5 stones)",
1920 permitted_value_checker=ONE_TO_TWO_CHECKER,
1921 )
1922 appetite2 = CamcopsColumn(
1923 FN_APPETITE2, Integer,
1924 comment="Marked increase in appetite in past month" + CMT_1_NO_2_YES,
1925 permitted_value_checker=ONE_TO_TWO_CHECKER,
1926 )
1927 weight4 = CamcopsColumn(
1928 # male/female responses unified (no "weight4a")
1929 FN_WEIGHT4, Integer,
1930 comment="Weight gain in past month (1 yes, 2 no, 3 yes but pregnant)",
1931 permitted_value_checker=ONE_TO_THREE_CHECKER,
1932 )
1933 weight5 = CamcopsColumn(
1934 FN_WEIGHT5, Integer,
1935 comment="Weight gain amount (1: ≥0.5 stones; 2: <0.5 stones)",
1936 permitted_value_checker=ONE_TO_TWO_CHECKER,
1937 )
1939 # Somatic problems
1941 gp_year = CamcopsColumn(
1942 FN_GP_YEAR, Integer,
1943 comment="Consultations with GP in past year (0: none, 1: 1–2, 2: 3–4, "
1944 "3: 6–10; 4: >10",
1945 permitted_value_checker=ZERO_TO_FOUR_CHECKER,
1946 )
1947 disable = CamcopsColumn(
1948 FN_DISABLE, Integer,
1949 comment="Longstanding illness/disability/infirmity" + CMT_1_YES_2_NO,
1950 permitted_value_checker=ONE_TO_TWO_CHECKER,
1951 )
1952 illness = CamcopsColumn(
1953 FN_ILLNESS, Integer,
1954 comment="Conditions (1 diabetes, 2 asthma, 3 arthritis, 4 heart "
1955 "disease, 5 high blood pressure, 6 lung disease, 7 more than "
1956 "one of the above, 8 none of the above)",
1957 permitted_value_checker=ONE_TO_EIGHT_CHECKER,
1958 )
1960 somatic_mand1 = CamcopsColumn(
1961 FN_SOMATIC_MAND1, Integer,
1962 comment="Any aches/pains in past month?" + CMT_1_NO_2_YES,
1963 permitted_value_checker=ONE_TO_TWO_CHECKER,
1964 )
1965 somatic_pain1 = CamcopsColumn(
1966 FN_SOMATIC_PAIN1, Integer,
1967 comment="Pain/ache brought on or made worse because low/anxious/"
1968 "stressed" + CMT_NEVER_SOMETIMES_ALWAYS,
1969 permitted_value_checker=ONE_TO_THREE_CHECKER,
1970 )
1971 somatic_pain2 = CamcopsColumn(
1972 FN_SOMATIC_PAIN2, Integer,
1973 comment="Pain: days in past week" + CMT_DAYS_PER_WEEK,
1974 permitted_value_checker=ONE_TO_THREE_CHECKER,
1975 )
1976 somatic_pain3 = CamcopsColumn(
1977 FN_SOMATIC_PAIN3, Integer,
1978 comment="Pain: lasted >3h on any day in past week" + CMT_1_NO_2_YES,
1979 permitted_value_checker=ONE_TO_TWO_CHECKER,
1980 )
1981 somatic_pain4 = CamcopsColumn(
1982 FN_SOMATIC_PAIN4, Integer,
1983 comment="Pain: unpleasant in past week?" + CMT_UNPLEASANT,
1984 permitted_value_checker=ONE_TO_FOUR_CHECKER,
1985 )
1986 somatic_pain5 = CamcopsColumn(
1987 FN_SOMATIC_PAIN5, Integer,
1988 comment="Pain: bothersome whilst doing something interesting in past "
1989 "week?" + CMT_BOTHERSOME_INTERESTING,
1990 permitted_value_checker=ONE_TO_THREE_CHECKER,
1991 )
1992 somatic_mand2 = CamcopsColumn(
1993 FN_SOMATIC_MAND2, Integer,
1994 comment="Bodily discomfort in past month?" + CMT_1_NO_2_YES,
1995 permitted_value_checker=ONE_TO_TWO_CHECKER,
1996 )
1997 somatic_dis1 = CamcopsColumn(
1998 FN_SOMATIC_DIS1, Integer,
1999 comment="Discomfort brought on or made worse because low/anxious/"
2000 "stressed" + CMT_NEVER_SOMETIMES_ALWAYS,
2001 permitted_value_checker=ONE_TO_THREE_CHECKER,
2002 )
2003 somatic_dis2 = CamcopsColumn(
2004 FN_SOMATIC_DIS2, Integer,
2005 comment="Discomfort: days in past week" + CMT_DAYS_PER_WEEK,
2006 permitted_value_checker=ONE_TO_THREE_CHECKER,
2007 )
2008 somatic_dis3 = CamcopsColumn(
2009 FN_SOMATIC_DIS3, Integer,
2010 comment="Discomfort: lasted >3h on any day in past week"
2011 + CMT_1_NO_2_YES,
2012 permitted_value_checker=ONE_TO_TWO_CHECKER,
2013 )
2014 somatic_dis4 = CamcopsColumn(
2015 FN_SOMATIC_DIS4, Integer,
2016 comment="Discomfort: unpleasant in past week?" + CMT_UNPLEASANT,
2017 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2018 )
2019 somatic_dis5 = CamcopsColumn(
2020 FN_SOMATIC_DIS5, Integer,
2021 comment="Discomfort: bothersome whilst doing something interesting in "
2022 "past week?" + CMT_BOTHERSOME_INTERESTING,
2023 permitted_value_checker=ONE_TO_THREE_CHECKER,
2024 )
2025 somatic_dur = CamcopsColumn(
2026 FN_SOMATIC_DUR, Integer,
2027 comment="Duration of ache/pain/discomfort" + CMT_DURATION,
2028 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2029 )
2031 # Fatigue/lacking energy
2033 fatigue_mand1 = CamcopsColumn(
2034 FN_FATIGUE_MAND1, Integer,
2035 comment="Tired in past month" + CMT_1_NO_2_YES,
2036 permitted_value_checker=ONE_TO_TWO_CHECKER,
2037 )
2038 fatigue_cause1 = CamcopsColumn(
2039 FN_FATIGUE_CAUSE1, Integer,
2040 comment="Main reason for feeling tired" + CMT_FATIGUE_CAUSE,
2041 permitted_value_checker=ONE_TO_EIGHT_CHECKER,
2042 )
2043 fatigue_tired1 = CamcopsColumn(
2044 FN_FATIGUE_TIRED1, Integer,
2045 comment="Tired: days in past week" + CMT_DAYS_PER_WEEK,
2046 permitted_value_checker=ONE_TO_THREE_CHECKER,
2047 )
2048 fatigue_tired2 = CamcopsColumn(
2049 FN_FATIGUE_TIRED2, Integer,
2050 comment="Tired: >3h on any one day in past week" + CMT_1_NO_2_YES,
2051 permitted_value_checker=ONE_TO_TWO_CHECKER,
2052 )
2053 fatigue_tired3 = CamcopsColumn(
2054 FN_FATIGUE_TIRED3, Integer,
2055 comment="So tired you've had to push yourself to get things done in "
2056 "past week" + CMT_1_NO_2_YES,
2057 permitted_value_checker=ONE_TO_TWO_CHECKER,
2058 )
2059 fatigue_tired4 = CamcopsColumn(
2060 FN_FATIGUE_TIRED4, Integer,
2061 comment="Tired during an enjoyable activity" + CMT_DURING_ENJOYABLE,
2062 permitted_value_checker=ONE_TO_THREE_CHECKER,
2063 )
2064 fatigue_mand2 = CamcopsColumn(
2065 FN_FATIGUE_MAND2, Integer,
2066 comment="Lacking in energy in past month" + CMT_1_NO_2_YES,
2067 permitted_value_checker=ONE_TO_TWO_CHECKER,
2068 )
2069 fatigue_cause2 = CamcopsColumn(
2070 FN_FATIGUE_CAUSE2, Integer,
2071 comment="Main reason for lacking energy" + CMT_FATIGUE_CAUSE,
2072 permitted_value_checker=ONE_TO_EIGHT_CHECKER,
2073 )
2074 fatigue_energy1 = CamcopsColumn(
2075 FN_FATIGUE_ENERGY1, Integer,
2076 comment="Lacking energy: days in past week" + CMT_DAYS_PER_WEEK,
2077 permitted_value_checker=ONE_TO_THREE_CHECKER,
2078 )
2079 fatigue_energy2 = CamcopsColumn(
2080 FN_FATIGUE_ENERGY2, Integer,
2081 comment="Lacking energy: for >3h on any one day in past week" +
2082 CMT_1_NO_2_YES,
2083 permitted_value_checker=ONE_TO_TWO_CHECKER,
2084 )
2085 fatigue_energy3 = CamcopsColumn(
2086 FN_FATIGUE_ENERGY3, Integer,
2087 comment="So lacking in energy you've had to push yourself to get "
2088 "things done in past week" + CMT_1_NO_2_YES,
2089 permitted_value_checker=ONE_TO_TWO_CHECKER,
2090 )
2091 fatigue_energy4 = CamcopsColumn(
2092 FN_FATIGUE_ENERGY4, Integer,
2093 comment="Lacking energy during an enjoyable activity" +
2094 CMT_DURING_ENJOYABLE,
2095 permitted_value_checker=ONE_TO_THREE_CHECKER,
2096 )
2097 fatigue_dur = CamcopsColumn(
2098 FN_FATIGUE_DUR, Integer,
2099 comment="Feeling tired/lacking energy for how long?" + CMT_DURATION,
2100 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2101 )
2103 # Concentration/memory
2105 conc_mand1 = CamcopsColumn(
2106 FN_CONC_MAND1, Integer,
2107 comment="Problems in concentrating during past monnth?" +
2108 CMT_1_NO_2_YES,
2109 permitted_value_checker=ONE_TO_TWO_CHECKER,
2110 )
2111 conc_mand2 = CamcopsColumn(
2112 FN_CONC_MAND2, Integer,
2113 comment="Problems with forgetting things during past month?" +
2114 CMT_1_NO_2_YES,
2115 permitted_value_checker=ONE_TO_TWO_CHECKER,
2116 )
2117 conc1 = CamcopsColumn(
2118 FN_CONC1, Integer,
2119 comment="Concentration/memory problems: days in past week" +
2120 CMT_DAYS_PER_WEEK,
2121 permitted_value_checker=ONE_TO_THREE_CHECKER,
2122 )
2123 conc2 = CamcopsColumn(
2124 FN_CONC2, Integer,
2125 comment="In past week, could concentrate on all of: TV, newspaper, "
2126 "conversation" + CMT_1_YES_2_NO,
2127 permitted_value_checker=ONE_TO_TWO_CHECKER,
2128 )
2129 conc3 = CamcopsColumn(
2130 FN_CONC3, Integer,
2131 comment="Problems with concentration have stopped you from getting on "
2132 "with things in past week" + CMT_1_NO_2_YES,
2133 permitted_value_checker=ONE_TO_TWO_CHECKER,
2134 )
2135 conc_dur = CamcopsColumn(
2136 FN_CONC_DUR, Integer,
2137 comment="Problems with concentration: for how long?" + CMT_DURATION,
2138 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2139 )
2140 conc4 = CamcopsColumn(
2141 FN_CONC4, Integer,
2142 comment="Forgotten anything important in past week" + CMT_1_NO_2_YES,
2143 permitted_value_checker=ONE_TO_TWO_CHECKER,
2144 )
2145 forget_dur = CamcopsColumn(
2146 FN_FORGET_DUR, Integer,
2147 comment="Problems with memory: for how long?" + CMT_DURATION,
2148 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2149 )
2151 # Sleep
2153 sleep_mand1 = CamcopsColumn(
2154 FN_SLEEP_MAND1, Integer,
2155 comment="Problems with sleep loss in past month" + CMT_1_NO_2_YES,
2156 permitted_value_checker=ONE_TO_TWO_CHECKER,
2157 )
2158 sleep_lose1 = CamcopsColumn(
2159 FN_SLEEP_LOSE1, Integer,
2160 comment="Sleep loss: nights in past week with problems" +
2161 CMT_NIGHTS_PER_WEEK,
2162 permitted_value_checker=ONE_TO_THREE_CHECKER,
2163 )
2164 sleep_lose2 = CamcopsColumn(
2165 FN_SLEEP_LOSE2, Integer,
2166 comment="On night with least sleep in past week, how long trying to "
2167 "get to sleep?" + CMT_SLEEP_CHANGE,
2168 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2169 )
2170 sleep_lose3 = CamcopsColumn(
2171 FN_SLEEP_LOSE3, Integer,
2172 comment="On how many nights in past week did you spend >=3h trying to "
2173 "get to sleep?" + CMT_NIGHTS_PER_WEEK,
2174 permitted_value_checker=ONE_TO_THREE_CHECKER,
2175 )
2176 sleep_emw = CamcopsColumn(
2177 FN_SLEEP_EMW, Integer,
2178 comment="Woken >2h earlier (and couldn't return to sleep) in past "
2179 "week?" + CMT_1_NO_2_YES,
2180 permitted_value_checker=ONE_TO_TWO_CHECKER,
2181 )
2182 sleep_cause = CamcopsColumn(
2183 FN_SLEEP_CAUSE, Integer,
2184 comment="What are your sleep difficulties caused by? (1 noise, "
2185 "2 shift work, 3 pain/illness, 4 worries, 5 unknown, 6 other",
2186 permitted_value_checker=ONE_TO_SIX_CHECKER,
2187 )
2188 sleep_mand2 = CamcopsColumn(
2189 FN_SLEEP_MAND2, Integer,
2190 comment="Problems with excess sleep in past month (1 no, 2 slept more "
2191 "than usual but not a problem, 3 yes)",
2192 permitted_value_checker=ONE_TO_THREE_CHECKER,
2193 )
2194 sleep_gain1 = CamcopsColumn(
2195 FN_SLEEP_GAIN1, Integer,
2196 comment="Sleep gain: how many nights in past week" +
2197 CMT_NIGHTS_PER_WEEK,
2198 permitted_value_checker=ONE_TO_THREE_CHECKER,
2199 )
2200 sleep_gain2 = CamcopsColumn(
2201 FN_SLEEP_GAIN2, Integer,
2202 comment="On night with most sleep in past week, how much more than "
2203 "usual?" + CMT_SLEEP_CHANGE,
2204 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2205 )
2206 sleep_gain3 = CamcopsColumn(
2207 FN_SLEEP_GAIN3, Integer,
2208 comment="On how many nights in past week did you sleep >3h longer "
2209 "than usual?" + CMT_NIGHTS_PER_WEEK,
2210 permitted_value_checker=ONE_TO_THREE_CHECKER,
2211 )
2212 sleep_dur = CamcopsColumn(
2213 FN_SLEEP_DUR, Integer,
2214 comment="How long have you had these problems with sleep?" +
2215 CMT_DURATION,
2216 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2217 )
2219 # Irritability
2221 irrit_mand1 = CamcopsColumn(
2222 FN_IRRIT_MAND1, Integer,
2223 comment="Irritable with those around you in past month?" +
2224 CMT_1_NO_2_YES,
2225 permitted_value_checker=ONE_TO_TWO_CHECKER,
2226 )
2227 irrit_mand2 = CamcopsColumn(
2228 FN_IRRIT_MAND2, Integer,
2229 comment="Short-tempered/angry over trivial things in past month? "
2230 "(1 no, 2 sometimes, 3 yes)",
2231 permitted_value_checker=ONE_TO_THREE_CHECKER,
2232 )
2233 irrit1 = CamcopsColumn(
2234 FN_IRRIT1, Integer,
2235 comment="Irritable/short-tempered/angry: days in past week" +
2236 CMT_DAYS_PER_WEEK,
2237 permitted_value_checker=ONE_TO_THREE_CHECKER,
2238 )
2239 irrit2 = CamcopsColumn(
2240 FN_IRRIT2, Integer,
2241 comment="Irritable/short-tempered/angry: for >1h on any day in past "
2242 "week?" + CMT_1_NO_2_YES,
2243 permitted_value_checker=ONE_TO_TWO_CHECKER,
2244 )
2245 irrit3 = CamcopsColumn(
2246 FN_IRRIT3, Integer,
2247 comment="Irritable/short-tempered/angry: wanted to shout at someone? "
2248 "(1 no; yes but didn't shout; 3 yes and did shout)",
2249 permitted_value_checker=ONE_TO_THREE_CHECKER,
2250 )
2251 irrit4 = CamcopsColumn(
2252 FN_IRRIT4, Integer,
2253 comment="In past week, have you had arguments/rows/lost temper? "
2254 "(1 no; 2 yes but justified; 3 yes)",
2255 permitted_value_checker=ONE_TO_THREE_CHECKER,
2256 )
2257 irrit_dur = CamcopsColumn(
2258 FN_IRRIT_DUR, Integer,
2259 comment="Irritable/short-tempered/angry: for how long?" + CMT_DURATION,
2260 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2261 )
2263 # Hypochondriasis
2265 hypo_mand1 = CamcopsColumn(
2266 FN_HYPO_MAND1, Integer,
2267 comment="Worried about physical health in past month?" +
2268 CMT_1_NO_2_YES,
2269 permitted_value_checker=ONE_TO_TWO_CHECKER,
2270 )
2271 hypo_mand2 = CamcopsColumn(
2272 FN_HYPO_MAND2, Integer,
2273 comment="Do you worry you have a serious illness?" + CMT_1_NO_2_YES,
2274 permitted_value_checker=ONE_TO_TWO_CHECKER,
2275 )
2276 hypo1 = CamcopsColumn(
2277 FN_HYPO1, Integer,
2278 comment="Worrying about health/having a serious illness: how many "
2279 "days in past week?" + CMT_DAYS_PER_WEEK,
2280 permitted_value_checker=ONE_TO_THREE_CHECKER,
2281 )
2282 hypo2 = CamcopsColumn(
2283 FN_HYPO2, Integer,
2284 comment="Worrying too much about physical health?" + CMT_1_NO_2_YES,
2285 permitted_value_checker=ONE_TO_TWO_CHECKER,
2286 )
2287 hypo3 = CamcopsColumn(
2288 FN_HYPO3, Integer,
2289 comment="Worrying about health: how unpleasant?" + CMT_UNPLEASANT,
2290 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2291 )
2292 hypo4 = CamcopsColumn(
2293 FN_HYPO4, Integer,
2294 comment="Able to take mind off health worries in past week?" +
2295 CMT_1_YES_2_NO,
2296 permitted_value_checker=ONE_TO_TWO_CHECKER,
2297 )
2298 hypo_dur = CamcopsColumn(
2299 FN_HYPO_DUR, Integer,
2300 comment="Worrying about physical health: for how long?" + CMT_DURATION,
2301 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2302 )
2304 # Depression
2306 depr_mand1 = CamcopsColumn(
2307 FN_DEPR_MAND1, Integer,
2308 comment="Sad/miserable/depressed in past month?" + CMT_1_NO_2_YES,
2309 permitted_value_checker=ONE_TO_TWO_CHECKER,
2310 )
2311 depr1 = CamcopsColumn(
2312 FN_DEPR1, Integer,
2313 comment="Sad/miserable/depressed in past week?" + CMT_1_NO_2_YES,
2314 permitted_value_checker=ONE_TO_TWO_CHECKER,
2315 )
2316 depr_mand2 = CamcopsColumn(
2317 FN_DEPR_MAND2, Integer,
2318 comment="In the past month, able to enjoy/take an interest in things "
2319 "as much as usual?" + CMT_ANHEDONIA,
2320 permitted_value_checker=ONE_TO_THREE_CHECKER,
2321 )
2322 depr2 = CamcopsColumn(
2323 FN_DEPR2, Integer,
2324 comment="In the past week, able to enjoy/take an interest in things "
2325 "as much as usual?" + CMT_ANHEDONIA,
2326 permitted_value_checker=ONE_TO_THREE_CHECKER,
2327 )
2328 depr3 = CamcopsColumn(
2329 FN_DEPR3, Integer,
2330 comment="[Depressed mood] or [anhedonia] on how many days in past "
2331 "week" + CMT_DAYS_PER_WEEK,
2332 permitted_value_checker=ONE_TO_THREE_CHECKER,
2333 )
2334 depr4 = CamcopsColumn(
2335 FN_DEPR4, Integer,
2336 comment="[Depressed mood] or [anhedonia] for >3h on any day in past "
2337 "week?" + CMT_1_NO_2_YES,
2338 permitted_value_checker=ONE_TO_TWO_CHECKER,
2339 )
2340 depr_content = CamcopsColumn(
2341 FN_DEPR_CONTENT, Integer,
2342 comment="Main reason for [depressed mood] or [anhedonia]?" +
2343 CMT_STRESSORS,
2344 permitted_value_checker=ONE_TO_NINE_CHECKER,
2345 )
2346 depr5 = CamcopsColumn(
2347 FN_DEPR5, Integer,
2348 comment="In past week, during [depressed mood] or [anhedonia], did "
2349 "nice things/company make you happier? "
2350 "(1 always, 2 sometimes, 3 no)",
2351 permitted_value_checker=ONE_TO_THREE_CHECKER,
2352 )
2353 depr_dur = CamcopsColumn(
2354 FN_DEPR_DUR, Integer,
2355 comment="Depressed mood/anhedonia: for how long?" + CMT_DURATION,
2356 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2357 )
2358 depth1 = CamcopsColumn(
2359 FN_DEPTH1, Integer,
2360 comment="Diurnal mood variation in past week (1 worse in the morning, "
2361 "2 worse in the evening, 3 varies, 4 no difference)",
2362 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2363 )
2364 depth2 = CamcopsColumn(
2365 FN_DEPTH2, Integer,
2366 comment="Libido in past month (1 not applicable, 2 no change, "
2367 "3 increased, 4 decreased)",
2368 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2369 )
2370 depth3 = CamcopsColumn(
2371 FN_DEPTH3, Integer,
2372 comment="Restlessness in past week" + CMT_1_NO_2_YES,
2373 permitted_value_checker=ONE_TO_TWO_CHECKER,
2374 )
2375 depth4 = CamcopsColumn(
2376 FN_DEPTH4, Integer,
2377 comment="Psychomotor retardation in past week" + CMT_1_NO_2_YES,
2378 permitted_value_checker=ONE_TO_TWO_CHECKER,
2379 )
2380 depth5 = CamcopsColumn(
2381 FN_DEPTH5, Integer,
2382 comment="Guilt/blamed self in past week (1 never, 2 only when it was "
2383 "my fault, 3 sometimes, 4 often)",
2384 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2385 )
2386 depth6 = CamcopsColumn(
2387 FN_DEPTH6, Integer,
2388 comment="Feeling not as good as other people in past week" +
2389 CMT_1_NO_2_YES,
2390 permitted_value_checker=ONE_TO_TWO_CHECKER,
2391 )
2392 depth7 = CamcopsColumn(
2393 FN_DEPTH7, Integer,
2394 comment="Hopeless in past week" + CMT_1_NO_2_YES,
2395 permitted_value_checker=ONE_TO_TWO_CHECKER,
2396 )
2397 depth8 = CamcopsColumn(
2398 FN_DEPTH8, Integer,
2399 comment="Life not worth living in past week (1 no, 2 sometimes, "
2400 "3 always)",
2401 permitted_value_checker=ONE_TO_THREE_CHECKER,
2402 )
2403 depth9 = CamcopsColumn(
2404 FN_DEPTH9, Integer,
2405 comment="Thoughts of suicide in past week (1 no; 2 yes, but would "
2406 "never commit suicide; 3 yes)",
2407 permitted_value_checker=ONE_TO_THREE_CHECKER,
2408 )
2409 depth10 = CamcopsColumn(
2410 FN_DEPTH10, Integer,
2411 comment="Thoughts of way to kill self in past week" + CMT_1_NO_2_YES,
2412 permitted_value_checker=ONE_TO_TWO_CHECKER,
2413 )
2414 doctor = CamcopsColumn(
2415 FN_DOCTOR, Integer,
2416 comment="Have you spoken to your doctor about these thoughts of "
2417 "killing yourself (1 yes; 2 no, but have talked to other "
2418 "people; 3 no)",
2419 permitted_value_checker=ONE_TO_THREE_CHECKER,
2420 )
2422 # Worry/generalized anxiety
2424 worry_mand1 = CamcopsColumn(
2425 FN_WORRY_MAND1, Integer,
2426 comment="Excessive worry in past month?" + CMT_NO_SOMETIMES_OFTEN,
2427 permitted_value_checker=ONE_TO_THREE_CHECKER,
2428 )
2429 worry_mand2 = CamcopsColumn(
2430 FN_WORRY_MAND2, Integer,
2431 comment="Any worries at all in past month?" + CMT_1_NO_2_YES,
2432 permitted_value_checker=ONE_TO_TWO_CHECKER,
2433 )
2434 worry_cont1 = CamcopsColumn(
2435 FN_WORRY_CONT1, Integer,
2436 comment="Main source of worry in past week?" + CMT_STRESSORS,
2437 permitted_value_checker=ONE_TO_NINE_CHECKER,
2438 )
2439 worry2 = CamcopsColumn(
2440 FN_WORRY2, Integer,
2441 comment="Worries (about things other than physical health) on how "
2442 "many days in past week" + CMT_DAYS_PER_WEEK,
2443 permitted_value_checker=ONE_TO_THREE_CHECKER,
2444 )
2445 worry3 = CamcopsColumn(
2446 FN_WORRY3, Integer,
2447 comment="Worrying too much?" + CMT_1_NO_2_YES,
2448 permitted_value_checker=ONE_TO_TWO_CHECKER,
2449 )
2450 worry4 = CamcopsColumn(
2451 FN_WORRY4, Integer,
2452 comment="How unpleasant is worry (about things other than physical "
2453 "health)" + CMT_UNPLEASANT,
2454 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2455 )
2456 worry5 = CamcopsColumn(
2457 FN_WORRY5, Integer,
2458 comment="Worry (about things other than physical health) for >3h on "
2459 "any day in past week?" + CMT_1_NO_2_YES,
2460 permitted_value_checker=ONE_TO_TWO_CHECKER,
2461 )
2462 worry_dur = CamcopsColumn(
2463 FN_WORRY_DUR, Integer,
2464 comment="Worry (about things other than physical health): for how "
2465 "long?" + CMT_DURATION,
2466 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2467 )
2469 anx_mand1 = CamcopsColumn(
2470 FN_ANX_MAND1, Integer,
2471 comment="Anxious/nervous in past month?" + CMT_1_NO_2_YES,
2472 permitted_value_checker=ONE_TO_TWO_CHECKER,
2473 )
2474 anx_mand2 = CamcopsColumn(
2475 FN_ANX_MAND2, Integer,
2476 comment="Muscle tension/couldn't relax in past month?" +
2477 CMT_NO_SOMETIMES_OFTEN,
2478 permitted_value_checker=ONE_TO_THREE_CHECKER,
2479 )
2480 anx_phobia1 = CamcopsColumn(
2481 FN_ANX_PHOBIA1, Integer,
2482 comment="Phobic anxiety in past month?" + CMT_1_NO_2_YES,
2483 permitted_value_checker=ONE_TO_TWO_CHECKER,
2484 )
2485 anx_phobia2 = CamcopsColumn(
2486 FN_ANX_PHOBIA2, Integer,
2487 comment="Phobic anxiety: always specific? (1 always specific, "
2488 "2 sometimes general)",
2489 permitted_value_checker=ONE_TO_TWO_CHECKER,
2490 )
2491 anx2 = CamcopsColumn(
2492 FN_ANX2, Integer,
2493 comment="Anxiety/nervousness/tension: how many days in past week" +
2494 CMT_DAYS_PER_WEEK,
2495 permitted_value_checker=ONE_TO_THREE_CHECKER,
2496 )
2497 anx3 = CamcopsColumn(
2498 FN_ANX3, Integer,
2499 comment="Anxiety/nervousness/tension: how unpleasant in past week" +
2500 CMT_UNPLEASANT,
2501 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2502 )
2503 anx4 = CamcopsColumn(
2504 FN_ANX4, Integer,
2505 comment="Anxiety/nervousness/tension: physical symptoms in past "
2506 "week?" + CMT_1_NO_2_YES,
2507 permitted_value_checker=ONE_TO_TWO_CHECKER,
2508 )
2509 anx5 = CamcopsColumn(
2510 FN_ANX5, Integer,
2511 comment="Anxiety/nervousness/tension: for >3h on any day in past "
2512 "week?" + CMT_1_NO_2_YES,
2513 permitted_value_checker=ONE_TO_TWO_CHECKER,
2514 )
2515 anx_dur = CamcopsColumn(
2516 FN_ANX_DUR, Integer,
2517 comment="Anxiety/nervousness/tension: for how long?" + CMT_DURATION,
2518 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2519 )
2521 # Specific phobias
2523 phobias_mand = CamcopsColumn(
2524 FN_PHOBIAS_MAND, Integer,
2525 comment="Phobic avoidance in past month?" + CMT_1_NO_2_YES,
2526 permitted_value_checker=ONE_TO_TWO_CHECKER,
2527 )
2528 phobias_type1 = CamcopsColumn(
2529 FN_PHOBIAS_TYPE1, Integer,
2530 comment="Which phobia? (1 travelling alone by bus/train; 2 being far "
2531 "from home; 3 public eating/speaking; 4 sight of blood; "
2532 "5 crowded shops; 6 insects/spiders/animals; 7 being watched; "
2533 "8 enclosed spaces or heights; 9 something else)",
2534 permitted_value_checker=ONE_TO_NINE_CHECKER,
2535 )
2536 phobias1 = CamcopsColumn(
2537 FN_PHOBIAS1, Integer,
2538 comment="Phobic anxiety: days in past week" + CMT_DAYS_PER_WEEK,
2539 permitted_value_checker=ONE_TO_THREE_CHECKER,
2540 )
2541 phobias2 = CamcopsColumn(
2542 FN_PHOBIAS2, Integer,
2543 comment="Phobic anxiety: physical symptoms in past week?" +
2544 CMT_1_NO_2_YES,
2545 permitted_value_checker=ONE_TO_TWO_CHECKER,
2546 )
2547 phobias3 = CamcopsColumn(
2548 FN_PHOBIAS3, Integer,
2549 comment="Phobic avoidance in past week?" + CMT_1_NO_2_YES,
2550 permitted_value_checker=ONE_TO_TWO_CHECKER,
2551 )
2552 phobias4 = CamcopsColumn(
2553 FN_PHOBIAS4, Integer,
2554 comment="Phobic avoidance: how many times in past week? (1: none, "
2555 "2: 1–3, 3: >=4)",
2556 permitted_value_checker=ONE_TO_THREE_CHECKER,
2557 )
2558 phobias_dur = CamcopsColumn(
2559 FN_PHOBIAS_DUR, Integer,
2560 comment="Phobic anxiety: for how long?" + CMT_DURATION,
2561 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2562 )
2564 # Panic
2566 panic_mand = CamcopsColumn(
2567 FN_PANIC_MAND, Integer,
2568 comment="Panic in past month (1: no, my anxiety never got that bad; "
2569 "2: yes, sometimes; 3: yes, often)",
2570 permitted_value_checker=ONE_TO_THREE_CHECKER,
2571 )
2572 panic1 = CamcopsColumn(
2573 FN_PANIC1, Integer,
2574 comment="Panic: how often in past week (1 not in past seven days, "
2575 "2 once, 3 more than once)",
2576 permitted_value_checker=ONE_TO_THREE_CHECKER,
2577 )
2578 panic2 = CamcopsColumn(
2579 FN_PANIC2, Integer,
2580 comment="Panic: how unpleasant in past week (1 a little "
2581 "uncomfortable; 2 unpleasant; 3 unbearable, or very "
2582 "unpleasant)",
2583 permitted_value_checker=ONE_TO_THREE_CHECKER,
2584 )
2585 panic3 = CamcopsColumn(
2586 FN_PANIC3, Integer,
2587 comment="Panic: in the past week, did the worst panic last >10min "
2588 "(1: <10min; 2 >=10min)",
2589 permitted_value_checker=ONE_TO_TWO_CHECKER,
2590 )
2591 panic4 = CamcopsColumn(
2592 FN_PANIC4, Integer,
2593 comment="Do panics start suddenly?" + CMT_1_NO_2_YES,
2594 permitted_value_checker=ONE_TO_TWO_CHECKER,
2595 )
2596 pansym_a = CamcopsColumn(
2597 FN_PANSYM_A, Integer,
2598 comment=CMT_PANIC_SYMPTOM + "heart racing" + CMT_1_NO_2_YES,
2599 permitted_value_checker=ONE_TO_TWO_CHECKER,
2600 )
2601 pansym_b = CamcopsColumn(
2602 FN_PANSYM_B, Integer,
2603 comment=CMT_PANIC_SYMPTOM + "hands sweaty/clammy" + CMT_1_NO_2_YES,
2604 permitted_value_checker=ONE_TO_TWO_CHECKER,
2605 )
2606 pansym_c = CamcopsColumn(
2607 FN_PANSYM_C, Integer,
2608 comment=CMT_PANIC_SYMPTOM + "trembling/shaking" + CMT_1_NO_2_YES,
2609 permitted_value_checker=ONE_TO_TWO_CHECKER,
2610 )
2611 pansym_d = CamcopsColumn(
2612 FN_PANSYM_D, Integer,
2613 comment=CMT_PANIC_SYMPTOM + "short of breath" + CMT_1_NO_2_YES,
2614 permitted_value_checker=ONE_TO_TWO_CHECKER,
2615 )
2616 pansym_e = CamcopsColumn(
2617 FN_PANSYM_E, Integer,
2618 comment=CMT_PANIC_SYMPTOM + "choking sensation" + CMT_1_NO_2_YES,
2619 permitted_value_checker=ONE_TO_TWO_CHECKER,
2620 )
2621 pansym_f = CamcopsColumn(
2622 FN_PANSYM_F, Integer,
2623 comment=(CMT_PANIC_SYMPTOM + "chest pain/pressure/discomfort" +
2624 CMT_1_NO_2_YES),
2625 permitted_value_checker=ONE_TO_TWO_CHECKER,
2626 )
2627 pansym_g = CamcopsColumn(
2628 FN_PANSYM_G, Integer,
2629 comment=CMT_PANIC_SYMPTOM + "nausea" + CMT_1_NO_2_YES,
2630 permitted_value_checker=ONE_TO_TWO_CHECKER,
2631 )
2632 pansym_h = CamcopsColumn(
2633 FN_PANSYM_H, Integer,
2634 comment=(CMT_PANIC_SYMPTOM + "dizzy/unsteady/lightheaded/faint" +
2635 CMT_1_NO_2_YES),
2636 permitted_value_checker=ONE_TO_TWO_CHECKER,
2637 )
2638 pansym_i = CamcopsColumn(
2639 FN_PANSYM_I, Integer,
2640 comment=(CMT_PANIC_SYMPTOM + "derealization/depersonalization" +
2641 CMT_1_NO_2_YES),
2642 permitted_value_checker=ONE_TO_TWO_CHECKER,
2643 )
2644 pansym_j = CamcopsColumn(
2645 FN_PANSYM_J, Integer,
2646 comment=(CMT_PANIC_SYMPTOM + "losing control/going crazy" +
2647 CMT_1_NO_2_YES),
2648 permitted_value_checker=ONE_TO_TWO_CHECKER,
2649 )
2650 pansym_k = CamcopsColumn(
2651 FN_PANSYM_K, Integer,
2652 comment=CMT_PANIC_SYMPTOM + "fear were dying" + CMT_1_NO_2_YES,
2653 permitted_value_checker=ONE_TO_TWO_CHECKER,
2654 )
2655 pansym_l = CamcopsColumn(
2656 FN_PANSYM_L, Integer,
2657 comment=CMT_PANIC_SYMPTOM + "tingling/numbness" + CMT_1_NO_2_YES,
2658 permitted_value_checker=ONE_TO_TWO_CHECKER,
2659 )
2660 pansym_m = CamcopsColumn(
2661 FN_PANSYM_M, Integer,
2662 comment=CMT_PANIC_SYMPTOM + "hot flushes/chills" + CMT_1_NO_2_YES,
2663 permitted_value_checker=ONE_TO_TWO_CHECKER,
2664 )
2665 panic5 = CamcopsColumn(
2666 FN_PANIC5, Integer,
2667 comment="Is panic always brought on by specific things?" +
2668 CMT_1_NO_2_YES,
2669 permitted_value_checker=ONE_TO_TWO_CHECKER,
2670 )
2671 panic_dur = CamcopsColumn(
2672 FN_PANIC_DUR, Integer,
2673 comment="Panic: for how long?" + CMT_DURATION,
2674 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2675 )
2677 # Compulsions
2679 comp_mand1 = CamcopsColumn(
2680 FN_COMP_MAND1, Integer,
2681 comment="Compulsions in past month" + CMT_NO_SOMETIMES_OFTEN,
2682 permitted_value_checker=ONE_TO_THREE_CHECKER,
2683 )
2684 comp1 = CamcopsColumn(
2685 FN_COMP1, Integer,
2686 comment="Compulsions: how many days in past week" + CMT_DAYS_PER_WEEK,
2687 permitted_value_checker=ONE_TO_THREE_CHECKER,
2688 )
2689 comp2 = CamcopsColumn(
2690 FN_COMP2, Integer,
2691 comment="Compulsions: tried to stop in past week" + CMT_1_NO_2_YES,
2692 permitted_value_checker=ONE_TO_TWO_CHECKER,
2693 )
2694 comp3 = CamcopsColumn(
2695 FN_COMP3, Integer,
2696 comment="Compulsions: upsetting/annoying in past week" +
2697 CMT_1_NO_2_YES,
2698 permitted_value_checker=ONE_TO_TWO_CHECKER,
2699 )
2700 comp4 = CamcopsColumn(
2701 FN_COMP4, Integer,
2702 comment="Compulsions: greatest number of repeats in past week "
2703 "(1: once, i.e. two times altogether; 2: two repeats; "
2704 "3: three or more repeats)",
2705 permitted_value_checker=ONE_TO_THREE_CHECKER,
2706 )
2707 comp_dur = CamcopsColumn(
2708 FN_COMP_DUR, Integer,
2709 comment="Compulsions: for how long?" + CMT_DURATION,
2710 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2711 )
2713 # Obsessions
2715 obsess_mand1 = CamcopsColumn(
2716 FN_OBSESS_MAND1, Integer,
2717 comment="Obsessions in past month" + CMT_NO_SOMETIMES_OFTEN,
2718 permitted_value_checker=ONE_TO_THREE_CHECKER,
2719 )
2720 obsess_mand2 = CamcopsColumn(
2721 FN_OBSESS_MAND2, Integer,
2722 comment="Obsessions: same thoughts repeating or general worries (1 "
2723 "same thoughts over and over, 2 worrying about something in "
2724 "general)",
2725 permitted_value_checker=ONE_TO_TWO_CHECKER,
2726 )
2727 obsess1 = CamcopsColumn(
2728 FN_OBSESS1, Integer,
2729 comment="Obsessions: how many days in past week" + CMT_DAYS_PER_WEEK,
2730 permitted_value_checker=ONE_TO_THREE_CHECKER,
2731 )
2732 obsess2 = CamcopsColumn(
2733 FN_OBSESS2, Integer,
2734 comment="Obsessions: tried to stop in past week" + CMT_1_NO_2_YES,
2735 permitted_value_checker=ONE_TO_TWO_CHECKER,
2736 )
2737 obsess3 = CamcopsColumn(
2738 FN_OBSESS3, Integer,
2739 comment="Obsessions: upsetting/annoying in past week" + CMT_1_NO_2_YES,
2740 permitted_value_checker=ONE_TO_TWO_CHECKER,
2741 )
2742 obsess4 = CamcopsColumn(
2743 FN_OBSESS4, Integer,
2744 comment="Obsessions: longest time spent thinking these thoughts, in "
2745 "past week (1: <15min; 2: >=15min)",
2746 permitted_value_checker=ONE_TO_TWO_CHECKER,
2747 )
2748 obsess_dur = CamcopsColumn(
2749 FN_OBSESS_DUR, Integer,
2750 comment="Obsessions: for how long?" + CMT_DURATION,
2751 permitted_value_checker=ONE_TO_FIVE_CHECKER,
2752 )
2754 # Overall impact
2756 overall2 = CamcopsColumn(
2757 FN_OVERALL2, Integer,
2758 comment="Overall impact on normal activities in past week (1 not at "
2759 "all; 2 they have made things more difficult but I get "
2760 "everything done; 3 they have stopped one activity; 4 they "
2761 "have stopped >1 activity)",
2762 permitted_value_checker=ONE_TO_FOUR_CHECKER,
2763 )
2765 # -------------------------------------------------------------------------
2766 # Functions
2767 # -------------------------------------------------------------------------
2769 @staticmethod
2770 def longname(req: "CamcopsRequest") -> str:
2771 _ = req.gettext
2772 return _("Clinical Interview Schedule, Revised")
2774 # noinspection PyMethodParameters
2775 @classproperty
2776 def minimum_client_version(cls) -> Version:
2777 return Version("2.2.0")
2779 def value_for_question(self, q: CisrQuestion) -> Optional[int]:
2780 fieldname = fieldname_for_q(q)
2781 assert fieldname, f"Blank fieldname for question {q}"
2782 return getattr(self, fieldname)
2784 def int_value_for_question(self, q: CisrQuestion) -> int:
2785 value = self.value_for_question(q)
2786 return int(value) if value is not None else 0
2788 def answer_is_no(self, q: CisrQuestion, value: int = V_UNKNOWN) -> bool:
2789 if value == V_UNKNOWN: # "Please look it up for me"
2790 value = self.int_value_for_question(q)
2791 if q in QUESTIONS_1_NO_2_YES:
2792 return value == 1
2793 elif q in QUESTIONS_1_YES_2_NO:
2794 return value == 2
2795 else:
2796 raise ValueError("answer_is_no() called for inappropriate "
2797 f"question {q}")
2799 def answer_is_yes(self, q: CisrQuestion, value: int = V_UNKNOWN) -> bool:
2800 if value == V_UNKNOWN: # "Please look it up for me"
2801 value = self.int_value_for_question(q)
2802 if q in QUESTIONS_1_NO_2_YES:
2803 return value == 2
2804 elif q in QUESTIONS_1_YES_2_NO:
2805 return value == 1
2806 else:
2807 raise ValueError("answer_is_yes() called for inappropriate "
2808 f"question {q}")
2810 def answered(self, q: CisrQuestion, value: int = V_UNKNOWN) -> bool:
2811 if value == V_UNKNOWN: # "Please look it up for me"
2812 value = self.int_value_for_question(q)
2813 return value != V_MISSING
2815 def get_textual_answer(self, req: CamcopsRequest,
2816 q: CisrQuestion) -> Optional[str]:
2817 value = self.value_for_question(q)
2818 if value is None or value == V_MISSING:
2819 return None
2820 if q in QUESTIONS_1_NO_2_YES:
2821 return get_yes_no(req, value == 2)
2822 elif q in QUESTIONS_1_YES_2_NO:
2823 return get_yes_no(req, value == 1)
2824 elif q in QUESTIONS_PROMPT_ONLY:
2825 return NOT_APPLICABLE_TEXT
2826 fieldname = fieldname_for_q(q)
2827 if (q in QUESTIONS_YN_SPECIFIC_TEXT or
2828 q in QUESTIONS_MULTIWAY or
2829 q in QUESTIONS_MULTIWAY_WITH_EXTRA_STEM):
2830 return self.wxstring(req, fieldname + f"_a{value}")
2831 elif q in QUESTIONS_OVERALL_DURATION:
2832 return self.wxstring(req, f"duration_a{value}")
2833 elif q in QUESTIONS_DAYS_PER_WEEK:
2834 return self.wxstring(req, f"dpw_a{value}")
2835 elif q in QUESTIONS_NIGHTS_PER_WEEK:
2836 return self.wxstring(req, f"npw_a{value}")
2837 elif q in QUESTIONS_HOW_UNPLEASANT_STANDARD:
2838 return self.wxstring(req, f"how_unpleasant_a{value}")
2839 elif q in QUESTIONS_FATIGUE_CAUSES:
2840 return self.wxstring(req, f"fatigue_causes_a{value}")
2841 elif q in QUESTIONS_STRESSORS:
2842 return self.wxstring(req, f"stressors_a{value}")
2843 elif q in QUESTIONS_NO_SOMETIMES_OFTEN:
2844 return self.wxstring(req, f"nso_a{value}")
2845 return f"? [value: {value}]"
2847 def next_q(self, q: CisrQuestion, r: CisrResult) -> CisrQuestion:
2848 # See equivalent in the C++ code.
2849 # ANY CHANGES HERE MUST BE REFLECTED IN THE C++ CODE AND VICE VERSA.
2851 v = V_MISSING # integer value
2852 if DEBUG_SHOW_QUESTIONS_CONSIDERED:
2853 r.decide(f"Considering question {q.value}: {q.name}")
2854 fieldname = fieldname_for_q(q)
2855 if fieldname: # eliminates prompt-only questions
2856 var_q = getattr(self, fieldname) # integer-or-NULL value
2857 if var_q is None:
2858 if q not in QUESTIONS_DEMOGRAPHICS:
2859 # From a diagnostic point of view, OK to have missing
2860 # demographic information. Otherwise:
2861 r.decide("INCOMPLETE INFORMATION. STOPPING.")
2862 r.incomplete = True
2863 else:
2864 v = int(var_q)
2866 next_q = -1
2868 def jump_to(qe: CisrQuestion) -> None:
2869 nonlocal next_q
2870 next_q = enum_to_int(qe)
2872 # If there is no special handling for a question, then after the
2873 # switch() statement we will move to the next question in sequence.
2874 # So only special "skip" situations are handled here.
2876 # FOLLOW THE EXACT SEQUENCE of the CIS-R. Don't agglomerate case
2877 # statements just because it's shorter (except empty ones when they are
2878 # in sequence). Clarity is key.
2880 # ---------------------------------------------------------------------
2881 # Demographics/preamble
2882 # ---------------------------------------------------------------------
2884 if q in QUESTIONS_DEMOGRAPHICS or q in QUESTIONS_PROMPT_ONLY:
2885 # Nothing special
2886 pass
2887 # Note that this makes some of the other prompt-only checks
2888 # below redundant! Still, it's quicker. The C++ version uses
2889 # switch() instead.
2891 # --------------------------------------------------------------------
2892 # Appetite/weight
2893 # --------------------------------------------------------------------
2895 elif q == CQ.APPETITE1_LOSS_PAST_MONTH:
2896 if self.answer_is_no(q, v):
2897 r.decide("No loss of appetite in past month.")
2898 jump_to(CQ.APPETITE2_INCREASE_PAST_MONTH)
2899 elif self.answer_is_yes(q, v):
2900 r.decide("Loss of appetite in past month. "
2901 "Incrementing depr_crit_3_somatic_synd.")
2902 r.depr_crit_3_somatic_synd += 1
2903 r.weight_change = WTCHANGE_APPETITE_LOSS
2905 elif q == CQ.WEIGHT1_LOSS_PAST_MONTH:
2906 if self.answer_is_no(q, v):
2907 r.decide("No weight loss.")
2908 jump_to(CQ.GP_YEAR)
2910 elif q == CQ.WEIGHT2_TRYING_TO_LOSE:
2911 if v == V_WEIGHT2_WTLOSS_TRYING:
2912 # Trying to lose weight. Move on.
2913 r.decide("Weight loss but it was deliberate.")
2914 elif v == V_WEIGHT2_WTLOSS_NOTTRYING:
2915 r.decide("Non-deliberate weight loss.")
2916 r.weight_change = WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN
2918 elif q == CQ.WEIGHT3_LOST_LOTS:
2919 if v == V_WEIGHT3_WTLOSS_GE_HALF_STONE:
2920 r.decide("Weight loss ≥0.5st in past month. "
2921 "Incrementing depr_crit_3_somatic_synd.")
2922 r.weight_change = WTCHANGE_WTLOSS_GE_HALF_STONE
2923 r.depr_crit_3_somatic_synd += 1
2924 r.decide("Loss of weight, so skipping appetite/weight gain "
2925 "questions.")
2926 jump_to(CQ.GP_YEAR)
2928 elif q == CQ.APPETITE2_INCREASE_PAST_MONTH:
2929 if self.answer_is_no(q, v):
2930 r.decide("No increase in appetite in past month.")
2931 jump_to(CQ.GP_YEAR)
2933 elif q == CQ.WEIGHT4_INCREASE_PAST_MONTH:
2934 if self.answer_is_yes(q, v):
2935 r.decide("Weight gain.")
2936 r.weight_change = WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN
2937 elif self.answered(q, v):
2938 r.decide("No weight gain, or weight gain but pregnant.")
2939 jump_to(CQ.GP_YEAR)
2941 elif q == CQ.WEIGHT5_GAINED_LOTS:
2942 if (v == V_WEIGHT5_WTGAIN_GE_HALF_STONE and
2943 r.weight_change == WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN): # noqa
2944 # ... redundant check on weight_change, I think!
2945 r.decide("Weight gain ≥0.5 st in past month.")
2946 r.weight_change = WTCHANGE_WTGAIN_GE_HALF_STONE
2948 # --------------------------------------------------------------------
2949 # Somatic symptoms
2950 # --------------------------------------------------------------------
2952 elif q == CQ.GP_YEAR:
2953 # Score the preceding block:
2954 if (r.weight_change == WTCHANGE_WTLOSS_GE_HALF_STONE and
2955 self.answer_is_yes(CQ.APPETITE1_LOSS_PAST_MONTH)):
2956 r.decide(
2957 "Appetite loss and weight loss ≥0.5st in past month. "
2958 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
2959 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
2960 if (r.weight_change == WTCHANGE_WTGAIN_GE_HALF_STONE and
2961 self.answer_is_yes(CQ.APPETITE2_INCREASE_PAST_MONTH)):
2962 r.decide(
2963 "Appetite gain and weight gain ≥0.5st in past month. "
2964 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
2965 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
2967 elif q == CQ.DISABLE:
2968 if self.answer_is_no(q):
2969 r.decide("No longstanding illness/disability/infirmity.")
2970 jump_to(CQ.SOMATIC_MAND1_PAIN_PAST_MONTH)
2972 elif q == CQ.ILLNESS:
2973 pass
2975 elif q == CQ.SOMATIC_MAND1_PAIN_PAST_MONTH:
2976 if self.answer_is_no(q):
2977 r.decide("No aches/pains in past month.")
2978 jump_to(CQ.SOMATIC_MAND2_DISCOMFORT)
2980 elif q == CQ.SOMATIC_PAIN1_PSYCHOL_EXAC:
2981 if v == V_SOMATIC_PAIN1_NEVER:
2982 r.decide("Pains never exacerbated by low mood/anxiety/stress.")
2983 jump_to(CQ.SOMATIC_MAND2_DISCOMFORT)
2985 elif q == CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK:
2986 if v == V_DAYS_IN_PAST_WEEK_0:
2987 r.decide("No pain in last 7 days.")
2988 jump_to(CQ.SOMATIC_MAND2_DISCOMFORT)
2989 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
2990 r.decide("Pain on >=4 of last 7 days. "
2991 "Incrementing somatic_symptoms.")
2992 r.somatic_symptoms += 1
2994 elif q == CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY:
2995 if self.answer_is_yes(q, v):
2996 r.decide("Pain for >3h on any day in past week. "
2997 "Incrementing somatic_symptoms.")
2998 r.somatic_symptoms += 1
3000 elif q == CQ.SOMATIC_PAIN4_UNPLEASANT:
3001 if v >= V_HOW_UNPLEASANT_UNPLEASANT:
3002 r.decide("Pain 'unpleasant' or worse in past week. "
3003 "Incrementing somatic_symptoms.")
3004 r.somatic_symptoms += 1
3006 elif q == CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING:
3007 if self.answer_is_yes(q, v):
3008 r.decide("Pain interrupted an interesting activity in past "
3009 "week. "
3010 "Incrementing somatic_symptoms.")
3011 r.somatic_symptoms += 1
3012 r.decide("There was pain, so skip 'discomfort' section.")
3013 jump_to(CQ.SOMATIC_DUR) # skip SOMATIC_MAND2
3015 elif q == CQ.SOMATIC_MAND2_DISCOMFORT:
3016 if self.answer_is_no(q, v):
3017 r.decide("No discomfort.")
3018 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH)
3020 elif q == CQ.SOMATIC_DIS1_PSYCHOL_EXAC:
3021 if v == V_SOMATIC_DIS1_NEVER:
3022 r.decide("Discomfort never exacerbated by being "
3023 "low/anxious/stressed.")
3024 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH)
3026 elif q == CQ.SOMATIC_DIS2_DAYS_PAST_WEEK:
3027 if v == V_DAYS_IN_PAST_WEEK_0:
3028 r.decide("No discomfort in last 7 days.")
3029 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH)
3030 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3031 r.decide("Discomfort on >=4 days in past week. "
3032 "Incrementing somatic_symptoms.")
3033 r.somatic_symptoms += 1
3035 elif q == CQ.SOMATIC_DIS3_GT_3H_ANY_DAY:
3036 if self.answer_is_yes(q, v):
3037 r.decide("Discomfort for >3h on any day in past week. "
3038 "Incrementing somatic_symptoms.")
3039 r.somatic_symptoms += 1
3041 elif q == CQ.SOMATIC_DIS4_UNPLEASANT:
3042 if v >= V_HOW_UNPLEASANT_UNPLEASANT:
3043 r.decide("Discomfort 'unpleasant' or worse in past week. "
3044 "Incrementing somatic_symptoms.")
3045 r.somatic_symptoms += 1
3047 elif q == CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING:
3048 if self.answer_is_yes(q, v):
3049 r.decide("Discomfort interrupted an interesting activity in "
3050 "past "
3051 "week. Incrementing somatic_symptoms.")
3052 r.somatic_symptoms += 1
3054 # --------------------------------------------------------------------
3055 # Fatigue/energy
3056 # --------------------------------------------------------------------
3058 elif q == CQ.FATIGUE_MAND1_TIRED_PAST_MONTH:
3059 if self.answer_is_no(q, v):
3060 r.decide("Not tired.")
3061 jump_to(CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH)
3063 elif q == CQ.FATIGUE_CAUSE1_TIRED:
3064 if v == V_FATIGUE_CAUSE_EXERCISE:
3065 r.decide("Tired due to exercise. Move on.")
3066 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)
3068 elif q == CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK:
3069 if v == V_DAYS_IN_PAST_WEEK_0:
3070 r.decide("Not tired in past week.")
3071 jump_to(CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH)
3072 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3073 r.decide("Tired on >=4 days in past week. "
3074 "Incrementing fatigue.")
3075 r.fatigue += 1
3077 elif q == CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY:
3078 if self.answer_is_yes(q, v):
3079 r.decide("Tired for >3h on any day in past week. "
3080 "Incrementing fatigue.")
3081 r.fatigue += 1
3083 elif q == CQ.FATIGUE_TIRED3_HAD_TO_PUSH:
3084 if self.answer_is_yes(q, v):
3085 r.decide("Tired enough to have to push self during past week. "
3086 "Incrementing fatigue.")
3087 r.fatigue += 1
3089 elif q == CQ.FATIGUE_TIRED4_DURING_ENJOYABLE:
3090 if self.answer_is_yes(q, v):
3091 r.decide("Tired during an enjoyable activity during past "
3092 "week. "
3093 "Incrementing fatigue.")
3094 r.fatigue += 1
3095 r.decide("There was tiredness, so skip 'lack of energy' section.")
3096 jump_to(CQ.FATIGUE_DUR) # skip FATIGUE_MAND2
3098 elif q == CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH:
3099 if self.answer_is_no(q, v):
3100 r.decide("Not lacking in energy.")
3101 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)
3103 elif q == CQ.FATIGUE_CAUSE2_LACK_ENERGY:
3104 if v == V_FATIGUE_CAUSE_EXERCISE:
3105 r.decide("Lacking in energy due to exercise. Move on.")
3106 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)
3108 elif q == CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK:
3109 if v == V_DAYS_IN_PAST_WEEK_0:
3110 r.decide("Not lacking in energy during last week.")
3111 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)
3112 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3113 r.decide("Lacking in energy on >=4 days in past week. "
3114 "Incrementing fatigue.")
3115 r.fatigue += 1
3117 elif q == CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY:
3118 if self.answer_is_yes(q, v):
3119 r.decide("Lacking in energy for >3h on any day in past week. "
3120 "Incrementing fatigue.")
3121 r.fatigue += 1
3123 elif q == CQ.FATIGUE_ENERGY3_HAD_TO_PUSH:
3124 if self.answer_is_yes(q, v):
3125 r.decide("Lacking in energy enough to have to push self during "
3126 "past week. Incrementing fatigue.")
3127 r.fatigue += 1
3129 elif q == CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE:
3130 if self.answer_is_yes(q, v):
3131 r.decide("Lacking in energy during an enjoyable activity "
3132 "during "
3133 "past week. Incrementing fatigue.")
3134 r.fatigue += 1
3136 elif q == CQ.FATIGUE_DUR:
3137 # Score preceding:
3138 if r.somatic_symptoms >= 2 and r.fatigue >= 2:
3139 r.decide("somatic >= 2 and fatigue >= 2. "
3140 "Incrementing neurasthenia.")
3141 r.neurasthenia += 1
3143 # --------------------------------------------------------------------
3144 # Concentration/memory
3145 # --------------------------------------------------------------------
3147 elif q == CQ.CONC_MAND1_POOR_CONC_PAST_MONTH:
3148 # Score preceding:
3149 if r.fatigue >= 2:
3150 r.decide("fatigue >= 2. "
3151 "Incrementing depr_crit_1_mood_anhedonia_energy.")
3152 r.depr_crit_1_mood_anhedonia_energy += 1
3154 elif q == CQ.CONC_MAND2_FORGETFUL_PAST_MONTH:
3155 if (self.answer_is_no(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)
3156 and self.answer_is_no(q, v)):
3157 r.decide("No problems with concentration or forgetfulness.")
3158 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH)
3160 elif q == CQ.CONC1_CONC_DAYS_PAST_WEEK:
3161 if v == V_DAYS_IN_PAST_WEEK_0:
3162 r.decide("No concentration/memory problems in past week.")
3163 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH)
3164 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3165 r.decide("Problems with concentration/memory problems on >=4 "
3166 "days in past week. Incrementing concentration_poor.")
3167 r.concentration_poor += 1
3168 if (self.answer_is_no(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) and
3169 self.answer_is_yes(CQ.CONC_MAND2_FORGETFUL_PAST_MONTH)):
3170 r.decide("Forgetfulness, not concentration, problems; skip "
3171 "over more detailed concentration questions.")
3172 jump_to(CQ.CONC4_FORGOTTEN_IMPORTANT) # skip CONC2, CONC3, CONC_DUR # noqa
3174 elif q == CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION:
3175 if self.answer_is_no(q, v):
3176 r.decide("Couldn't concentrate on at least one of {TV, "
3177 "newspaper, "
3178 "conversation}. Incrementing concentration_poor.")
3179 r.concentration_poor += 1
3181 elif q == CQ.CONC3_CONC_PREVENTED_ACTIVITIES:
3182 if self.answer_is_yes(q, v):
3183 r.decide("Problems with concentration stopped usual/desired "
3184 "activity. Incrementing concentration_poor.")
3185 r.concentration_poor += 1
3187 elif q == CQ.CONC_DUR:
3188 if self.answer_is_no(CQ.CONC_MAND2_FORGETFUL_PAST_MONTH):
3189 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH)
3191 elif q == CQ.CONC4_FORGOTTEN_IMPORTANT:
3192 if self.answer_is_yes(q, v):
3193 r.decide("Forgotten something important in past week. "
3194 "Incrementing concentration_poor.")
3195 r.concentration_poor += 1
3197 elif q == CQ.FORGET_DUR:
3198 pass
3200 # --------------------------------------------------------------------
3201 # Sleep
3202 # --------------------------------------------------------------------
3204 elif q == CQ.SLEEP_MAND1_LOSS_PAST_MONTH:
3205 # Score previous block:
3206 if r.concentration_poor >= 2:
3207 r.decide(
3208 "concentration >= 2. "
3209 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
3210 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
3211 # This question:
3212 if self.answer_is_no(q, v):
3213 r.decide("No problems with sleep loss in past month. "
3214 "Moving on.")
3215 jump_to(CQ.SLEEP_MAND2_GAIN_PAST_MONTH)
3217 elif q == CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK:
3218 if v == V_NIGHTS_IN_PAST_WEEK_0:
3219 r.decide("No problems with sleep in past week. Moving on.")
3220 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)
3221 elif v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:
3222 r.decide("Problems with sleep on >=4 nights in past week. "
3223 "Incrementing sleep_problems.")
3224 r.sleep_problems += 1
3226 elif q == CQ.SLEEP_LOSE2_DIS_WORST_DURATION:
3227 if v == V_SLEEP_CHANGE_LT_15_MIN:
3228 r.decide("Less than 15min maximum delayed initiation of sleep "
3229 "in past week. Moving on.")
3230 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)
3231 elif v == V_SLEEP_CHANGE_15_MIN_TO_1_H:
3232 r.decide("15min-1h maximum delayed initiation of sleep in past "
3233 "week. Incrementing sleep_problems.")
3234 r.sleep_problems += 1
3235 elif v == V_SLEEP_CHANGE_1_TO_3_H or v == V_SLEEP_CHANGE_GT_3_H:
3236 r.decide(">=1h maximum delayed initiation of sleep in past "
3237 "week. Adding 2 to sleep_problems.")
3238 r.sleep_problems += 2
3240 elif q == CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK:
3241 if v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:
3242 r.decide(">=4 nights in past week with >=3h delayed "
3243 "initiation of "
3244 "sleep. Incrementing sleep_problems.")
3245 r.sleep_problems += 1
3247 elif q == CQ.SLEEP_EMW_PAST_WEEK:
3248 if self.answer_is_yes(q, v):
3249 r.decide("EMW of >2h in past week. "
3250 "Setting sleep_change to SLEEPCHANGE_EMW. "
3251 "Incrementing depr_crit_3_somatic_synd.")
3252 # Was: SLEEPCH += answer - 1 (which only does anything for a
3253 # "yes" (2) answer).
3254 # ... but at this point, SLEEPCH is always 0.
3255 r.sleep_change = SLEEPCHANGE_EMW # LIKELY REDUNDANT.
3256 r.depr_crit_3_somatic_synd += 1
3257 if r.sleep_problems >= 1:
3258 r.decide("EMW of >2h in past week and sleep_problems >= 1; "
3259 "setting sleep_change to SLEEPCHANGE_EMW.")
3260 r.sleep_change = SLEEPCHANGE_EMW
3261 elif self.answer_is_no(q, v):
3262 r.decide("No EMW of >2h in past week.")
3263 if r.sleep_problems >= 1:
3264 r.decide("No EMW of >2h in past week, and sleep_problems "
3265 ">= 1. Setting sleep_change to "
3266 "SLEEPCHANGE_INSOMNIA_NOT_EMW.")
3267 r.sleep_change = SLEEPCHANGE_INSOMNIA_NOT_EMW
3269 elif q == CQ.SLEEP_CAUSE:
3270 r.decide("Problems with sleep loss; skipping over sleep gain.")
3271 jump_to(CQ.SLEEP_DUR)
3273 elif q == CQ.SLEEP_MAND2_GAIN_PAST_MONTH:
3274 if (v == V_SLEEP_MAND2_NO or
3275 v == V_SLEEP_MAND2_YES_BUT_NOT_A_PROBLEM):
3276 r.decide("No problematic sleep gain. Moving on.")
3277 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)
3279 elif q == CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK:
3280 if v == V_NIGHTS_IN_PAST_WEEK_0:
3281 r.decide("No nights with sleep problems [gain] in past week.")
3282 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)
3283 elif v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:
3284 r.decide("Problems with sleep [gain] on >=4 nights in past "
3285 "week. Incrementing sleep_problems.")
3286 r.sleep_problems += 1
3288 elif q == CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT:
3289 if v == V_SLEEP_CHANGE_LT_15_MIN:
3290 r.decide("Sleep gain <15min. Moving on.")
3291 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)
3292 elif v == V_SLEEP_CHANGE_15_MIN_TO_1_H:
3293 r.decide("Sleep gain 15min-1h. Incrementing sleep_problems.")
3294 r.sleep_problems += 1
3295 elif v >= V_SLEEP_CHANGE_1_TO_3_H:
3296 r.decide("Sleep gain >=1h. "
3297 "Adding 2 to sleep_problems. "
3298 "Setting sleep_change to SLEEPCHANGE_INCREASE.")
3299 r.sleep_problems += 2
3300 r.sleep_change = SLEEPCHANGE_INCREASE
3301 # Note that in the original, if the answer was 3
3302 # (V_SLEEP_CHANGE_1_TO_3_H) or greater, first 2 was added to
3303 # sleep, and then if sleep was >=1, sleepch [sleep_change] was set # noqa
3304 # to 3. However, sleep is never decremented/set below 0, so that # noqa
3305 # was a redundant test (always true).
3307 elif q == CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK:
3308 if v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:
3309 r.decide("Sleep gain of >3h on >=4 nights in past week. "
3310 "Incrementing sleep_problems.")
3311 r.sleep_problems += 1
3313 elif q == CQ.SLEEP_DUR:
3314 pass
3316 # --------------------------------------------------------------------
3317 # Irritability
3318 # --------------------------------------------------------------------
3320 elif q == CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH:
3321 # Score previous block:
3322 if r.sleep_problems >= 2:
3323 r.decide(
3324 "sleep_problems >= 2. "
3325 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
3326 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
3327 # This bit erroneously lived under IRRIT_DUR in the original; see
3328 # discussion there:
3329 if r.sleep_problems >= 2 and r.fatigue >= 2:
3330 r.decide("sleep_problems >=2 and fatigue >=2. "
3331 "Incrementing neurasthenia.")
3332 r.neurasthenia += 1
3333 # This question:
3334 if self.answer_is_yes(q, v):
3335 r.decide("Irritability (people) in past month; exploring "
3336 "further.")
3337 jump_to(CQ.IRRIT1_DAYS_PER_WEEK)
3339 elif q == CQ.IRRIT_MAND2_THINGS_PAST_MONTH:
3340 if v == V_IRRIT_MAND2_NO:
3341 r.decide("No irritability. Moving on.")
3342 jump_to(CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH)
3343 elif self.answered(q, v):
3344 r.decide("Irritability (things) in past month; exploring "
3345 "further.")
3347 elif q == CQ.IRRIT1_DAYS_PER_WEEK:
3348 if v == V_DAYS_IN_PAST_WEEK_0:
3349 r.decide("No irritability in past week. Moving on.")
3350 jump_to(CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH)
3351 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3352 r.decide("Irritable on >=4 days in past week. "
3353 "Incrementing irritability.")
3354 r.irritability += 1
3356 elif q == CQ.IRRIT2_GT_1H_ANY_DAY:
3357 if self.answer_is_yes(q, v):
3358 r.decide("Irritable for >1h on any day in past week. "
3359 "Incrementing irritability.")
3360 r.irritability += 1
3362 elif q == CQ.IRRIT3_WANTED_TO_SHOUT:
3363 if v >= V_IRRIT3_SHOUTING_WANTED_TO:
3364 r.decide("Wanted to or did shout. Incrementing irritability.")
3365 r.irritability += 1
3367 elif q == CQ.IRRIT4_ARGUMENTS:
3368 if v == V_IRRIT4_ARGUMENTS_YES_UNJUSTIFIED:
3369 r.decide("Arguments without justification. "
3370 "Incrementing irritability.")
3371 r.irritability += 1
3373 elif q == CQ.IRRIT_DUR:
3374 # Score recent things:
3375 if r.irritability >= 2 and r.fatigue >= 2:
3376 r.decide("irritability >=2 and fatigue >=2. "
3377 "Incrementing neurasthenia.")
3378 r.neurasthenia += 1
3379 # In the original, we had the rule "sleep_problems >=2 and
3380 # fatigue >=2 -> incrementing neurasthenia" here, but that would mean # noqa
3381 # we would fail to score sleep if the patient didn't report
3382 # irritability (because if you say no at IRRIT_MAND2, you jump beyond # noqa
3383 # this point to HYPO_MAND1). Checked with Glyn Lewis 2017-12-04, who # noqa
3384 # agreed on 2017-12-05. Therefore, moved to IRRIT_MAND1 as above.
3385 # Note that the only implication would have been potential small
3386 # mis-scoring of the CFS criterion (not any of the diagnoses that
3387 # the CIS-R reports as its primary/secondary diagnoses).
3389 # --------------------------------------------------------------------
3390 # Hypochondriasis
3391 # --------------------------------------------------------------------
3393 elif q == CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH:
3394 if self.answer_is_yes(q, v):
3395 r.decide("No worries about physical health in past month. "
3396 "Moving on.")
3397 jump_to(CQ.HYPO1_DAYS_PAST_WEEK)
3399 elif q == CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS:
3400 if self.answer_is_no(q, v):
3401 r.decide("No worries about having a serious illness. "
3402 "Moving on.")
3403 jump_to(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH)
3405 elif q == CQ.HYPO1_DAYS_PAST_WEEK:
3406 if v == V_DAYS_IN_PAST_WEEK_0:
3407 r.decide("No days in past week worrying about health. "
3408 "Moving on.")
3409 jump_to(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH)
3410 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3411 r.decide("Worries about health on >=4 days in past week. "
3412 "Incrementing hypochondria.")
3413 r.hypochondria += 1
3415 elif q == CQ.HYPO2_WORRY_TOO_MUCH:
3416 if self.answer_is_yes(q, v):
3417 r.decide("Worrying too much about health. "
3418 "Incrementing hypochondria.")
3419 r.hypochondria += 1
3421 elif q == CQ.HYPO3_HOW_UNPLEASANT:
3422 if v >= V_HOW_UNPLEASANT_UNPLEASANT:
3423 r.decide("Worrying re health 'unpleasant' or worse in past "
3424 "week. Incrementing hypochondria.")
3425 r.hypochondria += 1
3427 elif q == CQ.HYPO4_CAN_DISTRACT:
3428 if self.answer_is_no(q, v):
3429 r.decide("Cannot take mind off health worries by doing "
3430 "something else. Incrementing hypochondria.")
3431 r.hypochondria += 1
3433 elif q == CQ.HYPO_DUR:
3434 pass
3436 # --------------------------------------------------------------------
3437 # Depression
3438 # --------------------------------------------------------------------
3440 elif q == CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH:
3441 if self.answer_is_no(q, v):
3442 r.decide("Mood not low in past month. Moving to anhedonia.")
3443 jump_to(CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH)
3445 elif q == CQ.DEPR1_LOW_MOOD_PAST_WEEK:
3446 pass
3448 elif q == CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH:
3449 if (v == V_ANHEDONIA_ENJOYING_NORMALLY and
3450 self.answer_is_no(CQ.DEPR1_LOW_MOOD_PAST_WEEK)):
3451 r.decide("Neither low mood nor anhedonia in past month. "
3452 "Moving on.")
3453 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH)
3455 elif q == CQ.DEPR2_ENJOYMENT_PAST_WEEK:
3456 if (v == V_ANHEDONIA_ENJOYING_NORMALLY and
3457 self.answer_is_no(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH)):
3458 r.decide("No anhedonia in past week and no low mood in past "
3459 "month. Moving on.")
3460 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH)
3461 elif v >= V_ANHEDONIA_ENJOYING_LESS:
3462 r.decide("Partial or complete anhedonia in past week. "
3463 "Incrementing depression. "
3464 "Incrementing depr_crit_1_mood_anhedonia_energy. "
3465 "Incrementing depr_crit_3_somatic_synd.")
3466 r.depression += 1
3467 r.depr_crit_1_mood_anhedonia_energy += 1
3468 r.depr_crit_3_somatic_synd += 1
3470 elif q == CQ.DEPR3_DAYS_PAST_WEEK:
3471 if v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3472 r.decide("Low mood or anhedonia on >=4 days in past week. "
3473 "Incrementing depression.")
3474 r.depression += 1
3476 elif q == CQ.DEPR4_GT_3H_ANY_DAY:
3477 if self.answer_is_yes(q, v):
3478 r.decide("Low mood or anhedonia for >3h/day on at least one "
3479 "day in past week. Incrementing depression.")
3480 r.depression += 1
3481 if (self.int_value_for_question(CQ.DEPR3_DAYS_PAST_WEEK) and
3482 self.answer_is_yes(CQ.DEPR1_LOW_MOOD_PAST_WEEK)):
3483 r.decide("(A) Low mood in past week, and "
3484 "(B) low mood or anhedonia for >3h/day on at "
3485 "least one day in past week, and "
3486 "(C) low mood or anhedonia on >=4 days in past "
3487 "week. "
3488 "Incrementing depr_crit_1_mood_anhedonia_energy.")
3489 r.depr_crit_1_mood_anhedonia_energy += 1
3491 elif q == CQ.DEPR_CONTENT:
3492 pass
3494 elif q == CQ.DEPR5_COULD_CHEER_UP:
3495 if v >= V_DEPR5_COULD_CHEER_UP_SOMETIMES:
3496 r.decide("'Sometimes' or 'never' cheered up by nice things. "
3497 "Incrementing depression. "
3498 "Incrementing depr_crit_3_somatic_synd.")
3499 r.depression += 1
3500 r.depr_crit_3_somatic_synd += 1
3502 elif q == CQ.DEPR_DUR:
3503 if v >= V_DURATION_2W_6M:
3504 r.decide("Depressive symptoms for >=2 weeks. "
3505 "Setting depression_at_least_2_weeks.")
3506 r.depression_at_least_2_weeks = True
3507 # This code was at the start of DEPTH1, but involves skipping over
3508 # DEPTH1; since we never get to DEPTH1 without coming here, we can
3509 # move it here:
3510 if r.depression == 0:
3511 r.decide("Score for 'depression' is 0; skipping over "
3512 "depressive thought content questions.")
3513 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH)
3515 elif q == CQ.DEPTH1_DIURNAL_VARIATION:
3516 if (v == V_DEPTH1_DMV_WORSE_MORNING or
3517 v == V_DEPTH1_DMV_WORSE_EVENING):
3518 r.decide("Diurnal mood variation present.")
3519 r.diurnal_mood_variation = (
3520 DIURNAL_MOOD_VAR_WORSE_MORNING
3521 if v == V_DEPTH1_DMV_WORSE_MORNING
3522 else DIURNAL_MOOD_VAR_WORSE_EVENING
3523 )
3524 if v == V_DEPTH1_DMV_WORSE_MORNING:
3525 r.decide("Diurnal mood variation, worse in the mornings. "
3526 "Incrementing depr_crit_3_somatic_synd.")
3527 r.depr_crit_3_somatic_synd += 1
3529 elif q == CQ.DEPTH2_LIBIDO:
3530 if v == V_DEPTH2_LIBIDO_DECREASED:
3531 r.decide("Libido decreased over past month. "
3532 "Setting libido_decreased. "
3533 "Incrementing depr_crit_3_somatic_synd.")
3534 r.libido_decreased = True
3535 r.depr_crit_3_somatic_synd += 1
3537 elif q == CQ.DEPTH3_RESTLESS:
3538 if self.answer_is_yes(q):
3539 r.decide("Psychomotor agitation.")
3540 r.psychomotor_changes = PSYCHOMOTOR_AGITATION
3542 elif q == CQ.DEPTH4_SLOWED:
3543 if self.answer_is_yes(q):
3544 r.decide("Psychomotor retardation.")
3545 r.psychomotor_changes = PSYCHOMOTOR_RETARDATION
3546 if r.psychomotor_changes > PSYCHOMOTOR_NONE:
3547 r.decide(
3548 "Psychomotor agitation or retardation. "
3549 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui. "
3550 "Incrementing depr_crit_3_somatic_synd.")
3551 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
3552 r.depr_crit_3_somatic_synd += 1
3554 elif q == CQ.DEPTH5_GUILT:
3555 if v >= V_DEPTH5_GUILT_SOMETIMES:
3556 r.decide(
3557 "Feel guilty when not at fault sometimes or often. "
3558 "Incrementing depressive_thoughts. "
3559 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
3560 r.depressive_thoughts += 1
3561 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
3563 elif q == CQ.DEPTH6_WORSE_THAN_OTHERS:
3564 if self.answer_is_yes(q, v):
3565 r.decide(
3566 "Feeling not as good as other people. "
3567 "Incrementing depressive_thoughts. "
3568 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
3569 r.depressive_thoughts += 1
3570 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
3572 elif q == CQ.DEPTH7_HOPELESS:
3573 if self.answer_is_yes(q, v):
3574 r.decide("Hopelessness. "
3575 "Incrementing depressive_thoughts. "
3576 "Setting suicidality to "
3577 "SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS.")
3578 r.depressive_thoughts += 1
3579 r.suicidality = SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS
3581 elif q == CQ.DEPTH8_LNWL:
3582 if v == V_DEPTH8_LNWL_NO:
3583 r.decide("No thoughts of life not being worth living. "
3584 "Skipping to end of depression section.")
3585 jump_to(CQ.DEPR_OUTRO)
3586 elif v >= V_DEPTH8_LNWL_SOMETIMES:
3587 r.decide("Sometimes or always feeling life isn't worth living. "
3588 "Incrementing depressive_thoughts. "
3589 "Setting suicidality to "
3590 "SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING.")
3591 r.depressive_thoughts += 1
3592 r.suicidality = SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING
3594 elif q == CQ.DEPTH9_SUICIDE_THOUGHTS:
3595 if v == V_DEPTH9_SUICIDAL_THOUGHTS_NO:
3596 r.decide("No thoughts of suicide. Skipping to end of "
3597 "depression section.")
3598 jump_to(CQ.DEPR_OUTRO)
3599 if v >= V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD:
3600 r.decide("Suicidal thoughts present. "
3601 "Setting suicidality to "
3602 "SUICIDE_INTENT_SUICIDAL_THOUGHTS.")
3603 r.suicidality = SUICIDE_INTENT_SUICIDAL_THOUGHTS
3604 if v == V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD:
3605 r.decide("Suicidal thoughts present but denies would ever act. "
3606 "Skipping to talk-to-doctor section.")
3607 jump_to(CQ.DOCTOR)
3608 if v == V_DEPTH9_SUICIDAL_THOUGHTS_YES:
3609 r.decide(
3610 "Thoughts of suicide in past week. "
3611 "Incrementing depressive_thoughts. "
3612 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.")
3613 r.depressive_thoughts += 1
3614 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1
3616 elif q == CQ.DEPTH10_SUICIDE_METHOD:
3617 if self.answer_is_yes(q, v):
3618 r.decide("Suicidal thoughts without denying might ever act. "
3619 "Setting suicidality to "
3620 "SUICIDE_INTENT_SUICIDAL_PLANS.")
3621 r.suicidality = SUICIDE_INTENT_SUICIDAL_PLANS
3623 elif q == CQ.DOCTOR:
3624 if v == V_DOCTOR_YES:
3625 r.decide("Has spoken to doctor about suicidality. Skipping "
3626 "exhortation to do so.")
3627 jump_to(CQ.DEPR_OUTRO)
3629 elif q == CQ.DOCTOR2_PLEASE_TALK_TO:
3630 pass
3632 elif q == CQ.DEPR_OUTRO:
3633 pass
3635 # --------------------------------------------------------------------
3636 # Worry/anxiety
3637 # --------------------------------------------------------------------
3639 elif q == CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH:
3640 if v >= V_NSO_SOMETIMES:
3641 r.decide("Worrying excessively 'sometimes' or 'often'. "
3642 "Exploring further.")
3643 jump_to(CQ.WORRY_CONT1)
3645 elif q == CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH:
3646 if self.answer_is_no(q, v):
3647 r.decide("No worries at all in the past month. Moving on.")
3648 jump_to(CQ.ANX_MAND1_ANXIETY_PAST_MONTH)
3650 elif q == CQ.WORRY_CONT1:
3651 pass
3653 elif q == CQ.WORRY1_INFO_ONLY:
3654 pass
3656 elif q == CQ.WORRY2_DAYS_PAST_WEEK:
3657 if v == V_DAYS_IN_PAST_WEEK_0:
3658 r.decide("Worry [other than re physical health] on 0 days in "
3659 "past week. Moving on.")
3660 jump_to(CQ.ANX_MAND1_ANXIETY_PAST_MONTH)
3661 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3662 r.decide("Worry [other than re physical health] on >=4 days in "
3663 "past week. Incrementing worry.")
3664 r.worry += 1
3666 elif q == CQ.WORRY3_TOO_MUCH:
3667 if self.answer_is_yes(q, v):
3668 r.decide("Worrying too much. Incrementing worry.")
3669 r.worry += 1
3671 elif q == CQ.WORRY4_HOW_UNPLEASANT:
3672 if v >= V_HOW_UNPLEASANT_UNPLEASANT:
3673 r.decide("Worry [other than re physical health] 'unpleasant' "
3674 "or worse in past week. Incrementing worry.")
3675 r.worry += 1
3677 elif q == CQ.WORRY5_GT_3H_ANY_DAY:
3678 if self.answer_is_yes(q, v):
3679 r.decide("Worry [other than re physical health] for >3h on any "
3680 "day in past week. Incrementing worry.")
3681 r.worry += 1
3683 elif q == CQ.WORRY_DUR:
3684 pass
3686 elif q == CQ.ANX_MAND1_ANXIETY_PAST_MONTH:
3687 if self.answer_is_yes(q, v):
3688 r.decide("Anxious/nervous in past month. "
3689 "Skipping tension question.")
3690 jump_to(CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH)
3692 elif q == CQ.ANX_MAND2_TENSION_PAST_MONTH:
3693 if v == V_NSO_NO:
3694 r.decide("No tension in past month (and no anxiety, from "
3695 "previous question). Moving on.")
3696 jump_to(CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH)
3698 elif q == CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH:
3699 if self.answer_is_no(q, v):
3700 r.decide("No phobias. Moving on to general anxiety.")
3701 jump_to(CQ.ANX2_GENERAL_DAYS_PAST_WEEK)
3702 elif self.answer_is_yes(q, v):
3703 # This was in ANX_PHOBIA2; PHOBIAS_FLAG was set by arriving
3704 # there (but that only happens when we get a 'yes' answer here).
3705 r.decide("Phobias. Exploring further. Setting phobias flag.")
3706 r.phobias_flag = True
3708 elif q == CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL:
3709 if v == V_ANX_PHOBIA2_ALWAYS_SPECIFIC:
3710 r.decide("Anxiety always specific. "
3711 "Skipping generalized anxiety.")
3712 jump_to(CQ.PHOBIAS_TYPE1)
3714 elif q == CQ.ANX1_INFO_ONLY:
3715 pass
3717 elif q == CQ.ANX2_GENERAL_DAYS_PAST_WEEK:
3718 if v == V_DAYS_IN_PAST_WEEK_0:
3719 if r.phobias_flag:
3720 r.decide("No generalized anxiety in past week. "
3721 "Skipping further generalized anxiety questions.")
3722 jump_to(CQ.PHOBIAS1_DAYS_PAST_WEEK)
3723 else:
3724 r.decide("No generalized anxiety in past week. Moving on.")
3725 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH)
3726 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3727 r.decide("Generalized anxiety on >=4 days in past week. "
3728 "Incrementing anxiety.")
3729 r.anxiety += 1
3731 elif q == CQ.ANX3_GENERAL_HOW_UNPLEASANT:
3732 if v >= V_HOW_UNPLEASANT_UNPLEASANT:
3733 r.decide("Anxiety 'unpleasant' or worse in past week. "
3734 "Incrementing anxiety.")
3735 r.anxiety += 1
3737 elif q == CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS:
3738 if self.answer_is_yes(q, v):
3739 r.decide("Physical symptoms of anxiety. "
3740 "Setting anxiety_physical_symptoms. "
3741 "Incrementing anxiety.")
3742 r.anxiety_physical_symptoms = True
3743 r.anxiety += 1
3745 elif q == CQ.ANX5_GENERAL_GT_3H_ANY_DAY:
3746 if self.answer_is_yes(q, v):
3747 r.decide("Anxiety for >3h on any day in past week. "
3748 "Incrementing anxiety.")
3749 r.anxiety += 1
3751 elif q == CQ.ANX_DUR_GENERAL:
3752 if v >= V_DURATION_2W_6M:
3753 r.decide("Anxiety for >=2 weeks. "
3754 "Setting anxiety_at_least_2_weeks.")
3755 r.anxiety_at_least_2_weeks = True
3756 if r.phobias_flag:
3757 r.decide("Phobias flag set. Exploring further.")
3758 jump_to(CQ.PHOBIAS_TYPE1)
3759 else:
3760 if r.anxiety <= 1:
3761 r.decide("Anxiety score <=1. Moving on to compulsions.")
3762 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH)
3763 else:
3764 r.decide("Anxiety score >=2. Exploring panic.")
3765 jump_to(CQ.PANIC_MAND_PAST_MONTH)
3767 elif q == CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH:
3768 if self.answer_is_no(q, v):
3769 if r.anxiety <= 1:
3770 r.decide("Anxiety score <=1. Moving on to compulsions.")
3771 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH)
3772 else:
3773 r.decide("Anxiety score >=2. Exploring panic.")
3774 jump_to(CQ.PANIC_MAND_PAST_MONTH)
3776 elif q == CQ.PHOBIAS_TYPE1:
3777 if v in [V_PHOBIAS_TYPE1_ALONE_PUBLIC_TRANSPORT,
3778 V_PHOBIAS_TYPE1_FAR_FROM_HOME,
3779 V_PHOBIAS_TYPE1_CROWDED_SHOPS]:
3780 r.decide("Phobia type category: agoraphobia.")
3781 r.phobias_type = PHOBIATYPES_AGORAPHOBIA
3783 elif v in [V_PHOBIAS_TYPE1_PUBLIC_SPEAKING_EATING,
3784 V_PHOBIAS_TYPE1_BEING_WATCHED]:
3785 r.decide("Phobia type category: social.")
3786 r.phobias_type = PHOBIATYPES_SOCIAL
3788 elif v == V_PHOBIAS_TYPE1_BLOOD:
3789 r.decide("Phobia type category: blood/injury.")
3790 r.phobias_type = PHOBIATYPES_BLOOD_INJURY
3792 elif v in [V_PHOBIAS_TYPE1_ANIMALS,
3793 V_PHOBIAS_TYPE1_ENCLOSED_SPACES_HEIGHTS]:
3794 r.decide("Phobia type category: animals/enclosed spaces/"
3795 "heights.")
3796 r.phobias_type = PHOBIATYPES_ANIMALS_ENCLOSED_HEIGHTS
3798 elif v == V_PHOBIAS_TYPE1_OTHER:
3799 r.decide("Phobia type category: other.")
3800 r.phobias_type = PHOBIATYPES_OTHER
3802 else:
3803 pass
3805 elif q == CQ.PHOBIAS1_DAYS_PAST_WEEK:
3806 if v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3807 r.decide("Phobic anxiety on >=4 days in past week. "
3808 "Incrementing phobias_score.")
3809 r.phobias_score += 1
3811 elif q == CQ.PHOBIAS2_PHYSICAL_SYMPTOMS:
3812 if self.answer_is_yes(q, v):
3813 r.decide("Physical symptoms during phobic anxiety in past "
3814 "week. Incrementing phobias_score.")
3815 r.phobias_score += 1
3817 elif q == CQ.PHOBIAS3_AVOIDANCE:
3818 if self.answer_is_no(q, v): # no avoidance in past week
3819 if r.anxiety <= 1 and r.phobias_score == 0:
3820 r.decide("No avoidance in past week; "
3821 "anxiety <= 1 and phobias_score == 0. "
3822 "Finishing anxiety section.")
3823 jump_to(CQ.ANX_OUTRO)
3824 else:
3825 r.decide("No avoidance in past week; "
3826 "anxiety >= 2 or phobias_score >= 1. "
3827 "Moving to panic section.")
3828 jump_to(CQ.PANIC_MAND_PAST_MONTH)
3829 elif self.answer_is_yes(q, v):
3830 r.decide("Setting phobic_avoidance.")
3831 r.phobic_avoidance = True
3833 elif q == CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK:
3834 if v == V_DAYS_IN_PAST_WEEK_1_TO_3:
3835 r.decide("Phobic avoidance on 1-3 days in past week. "
3836 "Incrementing phobias_score.")
3837 r.phobias_score += 1
3838 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3839 r.decide("Phobic avoidance on >=4 days in past week. "
3840 "Adding 2 to phobias_score.")
3841 r.phobias_score += 2
3842 if (r.anxiety <= 1 and
3843 self.int_value_for_question(CQ.PHOBIAS1_DAYS_PAST_WEEK) ==
3844 V_DAYS_IN_PAST_WEEK_0):
3845 r.decide("anxiety <= 1 and no phobic anxiety in past week. "
3846 "Finishing anxiety section.")
3847 jump_to(CQ.ANX_OUTRO)
3849 elif q == CQ.PHOBIAS_DUR:
3850 pass
3852 elif q == CQ.PANIC_MAND_PAST_MONTH:
3853 if v == V_NSO_NO:
3854 r.decide("No panic in the past month. Finishing anxiety "
3855 "section.")
3856 jump_to(CQ.ANX_OUTRO)
3858 elif q == CQ.PANIC1_NUM_PAST_WEEK:
3859 if v == V_PANIC1_N_PANICS_PAST_WEEK_0:
3860 r.decide("No panic in past week. Finishing anxiety section.")
3861 jump_to(CQ.ANX_OUTRO)
3862 elif v == V_PANIC1_N_PANICS_PAST_WEEK_1:
3863 r.decide("One panic in past week. Incrementing panic.")
3864 r.panic += 1
3865 elif v == V_PANIC1_N_PANICS_PAST_WEEK_GT_1:
3866 r.decide("More than one panic in past week. Adding 2 to panic.")
3867 r.panic += 2
3869 elif q == CQ.PANIC2_HOW_UNPLEASANT:
3870 if v >= V_HOW_UNPLEASANT_UNPLEASANT:
3871 r.decide("Panic 'unpleasant' or worse in past week. "
3872 "Incrementing panic.")
3873 r.panic += 1
3875 elif q == CQ.PANIC3_PANIC_GE_10_MIN:
3876 if v == V_PANIC3_WORST_GE_10_MIN:
3877 r.decide("Worst panic in past week lasted >=10 min. "
3878 "Incrementing panic.")
3879 r.panic += 1
3881 elif q == CQ.PANIC4_RAPID_ONSET:
3882 if self.answer_is_yes(q, v):
3883 r.decide("Rapid onset of panic symptoms. "
3884 "Setting panic_rapid_onset.")
3885 r.panic_rapid_onset = True
3887 elif q == CQ.PANSYM:
3888 # Multi-way answer. All are scored 1=no, 2=yes.
3889 n_panic_symptoms = 0
3890 for panic_fn in PANIC_SYMPTOM_FIELDNAMES:
3891 panic_symptom = getattr(self, panic_fn) or 0 # force to int
3892 yes_present = panic_symptom == 2
3893 if yes_present:
3894 n_panic_symptoms += 1
3895 r.decide(
3896 f"{n_panic_symptoms} out of "
3897 f"{NUM_PANIC_SYMPTOMS} specific panic symptoms endorsed.")
3898 # The next bit was coded in PANIC5, but lives more naturally here:
3899 if self.answer_is_no(CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH):
3900 jump_to(CQ.PANIC_DUR)
3902 elif q == CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER:
3903 pass
3905 elif q == CQ.PANIC_DUR:
3906 pass
3908 elif q == CQ.ANX_OUTRO:
3909 pass
3911 # --------------------------------------------------------------------
3912 # Compulsions and obsessions
3913 # --------------------------------------------------------------------
3915 elif q == CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH:
3916 if v == V_NSO_NO:
3917 r.decide("No compulsions in past month. Moving to obsessions.")
3918 jump_to(CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH)
3920 elif q == CQ.COMP1_DAYS_PAST_WEEK:
3921 if v == V_DAYS_IN_PAST_WEEK_0:
3922 r.decide("No compulsions in past week. Moving to obesssions.")
3923 jump_to(CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH)
3924 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3925 r.decide("Obsessions on >=4 days in past week. "
3926 "Incrementing compulsions.")
3927 r.compulsions += 1
3929 elif q == CQ.COMP2_TRIED_TO_STOP:
3930 if self.answer_is_yes(q, v):
3931 r.decide("Attempts to stop compulsions in past week. "
3932 "Setting compulsions_tried_to_stop. "
3933 "Incrementing compulsions.")
3934 r.compulsions_tried_to_stop = True
3935 r.compulsions += 1
3937 elif q == CQ.COMP3_UPSETTING:
3938 if self.answer_is_yes(q, v):
3939 r.decide("Compulsions upsetting/annoying. "
3940 "Incrementing compulsions.")
3941 r.compulsions += 1
3943 elif q == CQ.COMP4_MAX_N_REPETITIONS:
3944 if v == V_COMP4_MAX_N_REPEATS_GE_3:
3945 r.decide("At worst, >=3 repeats. Incrementing compulsions.")
3946 r.compulsions += 1
3948 elif q == CQ.COMP_DUR:
3949 if v >= V_DURATION_2W_6M:
3950 r.decide("Compulsions for >=2 weeks. "
3951 "Setting compulsions_at_least_2_weeks.")
3952 r.compulsions_at_least_2_weeks = True
3954 elif q == CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH:
3955 if v == V_NSO_NO:
3956 r.decide("No obsessions in past month. Moving on.")
3957 jump_to(r.get_final_page())
3959 elif q == CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL:
3960 if v == V_OBSESS_MAND1_GENERAL_WORRIES:
3961 r.decide("Worrying about something in general, not the same "
3962 "thoughts over and over again. Moving on.")
3963 jump_to(r.get_final_page())
3965 elif q == CQ.OBSESS1_DAYS_PAST_WEEK:
3966 if v == V_DAYS_IN_PAST_WEEK_0:
3967 r.decide("No obsessions in past week. Moving on.")
3968 jump_to(r.get_final_page())
3969 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:
3970 r.decide("Obsessions on >=4 days in past week. "
3971 "Incrementing obsessions.")
3972 r.obsessions += 1
3974 elif q == CQ.OBSESS2_TRIED_TO_STOP:
3975 if self.answer_is_yes(q, v):
3976 r.decide("Tried to stop obsessional thoughts in past week. "
3977 "Setting obsessions_tried_to_stop. "
3978 "Incrementing obsessions.")
3979 r.obsessions_tried_to_stop = True
3980 r.obsessions += 1
3982 elif q == CQ.OBSESS3_UPSETTING:
3983 if self.answer_is_yes(q, v):
3984 r.decide("Obsessions upsetting/annoying in past week. "
3985 "Incrementing obsessions.")
3986 r.obsessions += 1
3988 elif q == CQ.OBSESS4_MAX_DURATION:
3989 if v == V_OBSESS4_GE_15_MIN:
3990 r.decide("Obsessions lasting >=15 min in past week. "
3991 "Incrementing obsessions.")
3992 r.obsessions += 1
3994 elif q == CQ.OBSESS_DUR:
3995 if v >= V_DURATION_2W_6M:
3996 r.decide("Obsessions for >=2 weeks. "
3997 "Setting obsessions_at_least_2_weeks.")
3998 r.obsessions_at_least_2_weeks = True
4000 # --------------------------------------------------------------------
4001 # End
4002 # --------------------------------------------------------------------
4004 elif q == CQ.OVERALL1_INFO_ONLY:
4005 pass
4007 elif q == CQ.OVERALL2_IMPACT_PAST_WEEK:
4008 if self.answered(q, v):
4009 r.functional_impairment = v - 1
4010 r.decide(
4011 f"Setting functional_impairment to "
4012 f"{r.functional_impairment}")
4014 elif q == CQ.THANKS_FINISHED:
4015 pass
4017 elif q == CQ.END_MARKER: # this is not a page
4018 # we've reached the end; no point thinking further
4019 return CQ.END_MARKER
4021 else:
4022 pass
4024 if next_q == -1:
4025 # Nothing has expressed an overriding preference, so increment...
4026 next_q = enum_to_int(q) + 1
4028 return int_to_enum(next_q)
4030 def get_result(self, record_decisions: bool = False) -> CisrResult:
4031 # internal_q = CQ.START_MARKER
4032 internal_q = CQ.APPETITE1_LOSS_PAST_MONTH # skip the preamble etc.
4033 result = CisrResult(record_decisions)
4034 while (not result.incomplete) and internal_q != CQ.END_MARKER:
4035 internal_q = self.next_q(internal_q, result)
4036 # loop until we reach the end or have incomplete data
4037 result.finalize()
4038 return result
4040 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
4041 result = self.get_result()
4042 if result.incomplete:
4043 return CTV_INCOMPLETE
4044 return [
4045 CtvInfo(content=(
4046 f"Probable primary diagnosis: "
4047 f"{bold(result.diagnosis_1_name())} "
4048 f"({result.diagnosis_1_icd10_code()})")),
4049 CtvInfo(content=(
4050 f"Probable secondary diagnosis: "
4051 f"{bold(result.diagnosis_2_name())} "
4052 f"({result.diagnosis_2_icd10_code()})")),
4053 CtvInfo(content=(
4054 f"CIS-R suicide intent: "
4055 f"{self.get_suicide_intent(req, result, with_warning=False)}")),
4056 ]
4058 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
4059 result = self.get_result()
4060 return self.standard_task_summary_fields() + [
4062 # Diagnoses
4064 SummaryElement(
4065 name="diagnosis_1_code",
4066 coltype=Integer(),
4067 value=result.diagnosis_1,
4068 comment="Probable primary diagnosis (CIS-R code)"),
4069 SummaryElement(
4070 name="diagnosis_1_text",
4071 coltype=UnicodeText(),
4072 value=result.diagnosis_1_name(),
4073 comment="Probable primary diagnosis (text)"),
4074 SummaryElement(
4075 name="diagnosis_1_icd10",
4076 coltype=UnicodeText(),
4077 value=result.diagnosis_1_icd10_code(),
4078 comment="Probable primary diagnosis (ICD-10 code/codes)"),
4079 SummaryElement(
4080 name="diagnosis_2_code",
4081 coltype=Integer(),
4082 value=result.diagnosis_2,
4083 comment="Probable secondary diagnosis (CIS-R code)"),
4084 SummaryElement(
4085 name="diagnosis_2_text",
4086 coltype=UnicodeText(),
4087 value=result.diagnosis_2_icd10_code(),
4088 comment="Probable secondary diagnosis (text)"),
4089 SummaryElement(
4090 name="diagnosis_2_icd10",
4091 coltype=UnicodeText(),
4092 value=result.diagnosis_2_icd10_code(),
4093 comment="Probable secondary diagnosis (ICD-10 code/codes)"),
4095 # Suicidality/doctell: directly encoded in data
4097 # Total score
4099 SummaryElement(
4100 name="score_total",
4101 coltype=Integer(),
4102 value=result.get_score(),
4103 comment=f"CIS-R total score (max. {MAX_TOTAL})"),
4104 # Functional impairment: directly encoded in data
4106 # Subscores
4108 SummaryElement(
4109 name="score_somatic_symptoms",
4110 coltype=Integer(),
4111 value=result.somatic_symptoms,
4112 comment="Score: somatic symptoms (max. 4)"),
4113 SummaryElement(
4114 name="score_hypochondria",
4115 coltype=Integer(),
4116 value=result.hypochondria,
4117 comment="Score: worry over physical health (max. 4)"),
4118 SummaryElement(
4119 name="score_irritability",
4120 coltype=Integer(),
4121 value=result.irritability,
4122 comment="Score: irritability (max. 4)"),
4123 SummaryElement(
4124 name="score_concentration_poor",
4125 coltype=Integer(),
4126 value=result.concentration_poor,
4127 comment="Score: poor concentration (max. 4)"),
4128 SummaryElement(
4129 name="score_fatigue",
4130 coltype=Integer(),
4131 value=result.fatigue,
4132 comment="Score: fatigue (max. 4)"),
4133 SummaryElement(
4134 name="score_sleep_problems",
4135 coltype=Integer(),
4136 value=result.sleep_problems,
4137 comment="Score: sleep problems (max. 4)"),
4138 SummaryElement(
4139 name="score_depression",
4140 coltype=Integer(),
4141 value=result.depression,
4142 comment="Score: depression (max. 4)"),
4143 SummaryElement(
4144 name="score_depressive_thoughts",
4145 coltype=Integer(),
4146 value=result.depressive_thoughts,
4147 comment="Score: depressive ideas (max. 5)"),
4148 SummaryElement(
4149 name="score_phobias",
4150 coltype=Integer(),
4151 value=result.phobias_score,
4152 comment="Score: phobias (max. 4)"),
4153 SummaryElement(
4154 name="score_worry",
4155 coltype=Integer(),
4156 value=result.worry,
4157 comment="Score: worry (max. 4)"),
4158 SummaryElement(
4159 name="score_anxiety",
4160 coltype=Integer(),
4161 value=result.anxiety,
4162 comment="Score: anxiety (max. 4)"),
4163 SummaryElement(
4164 name="score_panic",
4165 coltype=Integer(),
4166 value=result.panic,
4167 comment="Score: panic (max. 4)"),
4168 SummaryElement(
4169 name="score_compulsions",
4170 coltype=Integer(),
4171 value=result.compulsions,
4172 comment="Score: compulsions (max. 4)"),
4173 SummaryElement(
4174 name="score_obsessions",
4175 coltype=Integer(),
4176 value=result.obsessions,
4177 comment="Score: obsessions (max. 4)"),
4179 # Other
4181 SummaryElement(
4182 name="sleep_change",
4183 coltype=Integer(),
4184 value=result.sleep_change,
4185 comment=DESC_SLEEP_CHANGE),
4186 SummaryElement(
4187 name="weight_change",
4188 coltype=Integer(),
4189 value=result.weight_change,
4190 comment=DESC_WEIGHT_CHANGE),
4191 SummaryElement(
4192 name="depcrit1_score",
4193 coltype=Integer(),
4194 value=result.depr_crit_1_mood_anhedonia_energy,
4195 comment=DESC_DEPCRIT1),
4196 SummaryElement(
4197 name="depcrit2_score",
4198 coltype=Integer(),
4199 value=result.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui,
4200 comment=DESC_DEPCRIT2),
4201 SummaryElement(
4202 name="depcrit3_score",
4203 coltype=Integer(),
4204 value=result.depr_crit_3_somatic_synd,
4205 comment=DESC_DEPCRIT3),
4206 SummaryElement(
4207 name="depcrit3_met_somatic_syndrome",
4208 coltype=Boolean(),
4209 value=result.has_somatic_syndrome(),
4210 comment=DESC_DEPCRIT3_MET),
4211 SummaryElement(
4212 name="neurasthenia_score",
4213 coltype=Integer(),
4214 value=result.neurasthenia,
4215 comment=DESC_NEURASTHENIA_SCORE),
4217 # Disorder flags
4219 SummaryElement(
4220 name="disorder_ocd",
4221 coltype=Boolean(),
4222 value=result.obsessive_compulsive_disorder,
4223 comment=DISORDER_OCD),
4224 SummaryElement(
4225 name="disorder_depression_mild",
4226 coltype=Boolean(),
4227 value=result.depression_mild,
4228 comment=DISORDER_DEPR_MILD),
4229 SummaryElement(
4230 name="disorder_depression_moderate",
4231 coltype=Boolean(),
4232 value=result.depression_moderate,
4233 comment=DISORDER_DEPR_MOD),
4234 SummaryElement(
4235 name="disorder_depression_severe",
4236 coltype=Boolean(),
4237 value=result.depression_severe,
4238 comment=DISORDER_DEPR_SEV),
4239 SummaryElement(
4240 name="disorder_cfs",
4241 coltype=Boolean(),
4242 value=result.chronic_fatigue_syndrome,
4243 comment=DISORDER_CFS),
4244 SummaryElement(
4245 name="disorder_gad",
4246 coltype=Boolean(),
4247 value=result.generalized_anxiety_disorder,
4248 comment=DISORDER_GAD),
4249 SummaryElement(
4250 name="disorder_agoraphobia",
4251 coltype=Boolean(),
4252 value=result.phobia_agoraphobia,
4253 comment=DISORDER_AGORAPHOBIA),
4254 SummaryElement(
4255 name="disorder_social_phobia",
4256 coltype=Boolean(),
4257 value=result.phobia_social,
4258 comment=DISORDER_SOCIAL_PHOBIA),
4259 SummaryElement(
4260 name="disorder_specific_phobia",
4261 coltype=Boolean(),
4262 value=result.phobia_specific,
4263 comment=DISORDER_SPECIFIC_PHOBIA),
4264 SummaryElement(
4265 name="disorder_panic_disorder",
4266 coltype=Boolean(),
4267 value=result.panic_disorder,
4268 comment=DISORDER_PANIC),
4269 ]
4271 def is_complete(self) -> bool:
4272 result = self.get_result()
4273 return not result.incomplete
4275 def diagnosis_name(self, req: CamcopsRequest, diagnosis_code: int) -> str:
4276 xstring_name = f"diag_{diagnosis_code}_desc"
4277 return self.wxstring(req, xstring_name)
4279 def diagnosis_reason(self, req: CamcopsRequest,
4280 diagnosis_code: int) -> str:
4281 xstring_name = f"diag_{diagnosis_code}_explan"
4282 return self.wxstring(req, xstring_name)
4284 def get_suicide_intent(self, req: CamcopsRequest,
4285 result: CisrResult,
4286 with_warning: bool = True) -> str:
4287 if result.incomplete:
4288 html = "TASK INCOMPLETE. SO FAR: "
4289 else:
4290 html = ""
4291 html += self.wxstring(req, f"suicid_{result.suicidality}")
4292 if (with_warning and
4293 result.suicidality >= SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING):
4294 html += f" <i>{self.wxstring(req, 'suicid_instruction')}</i>"
4295 if result.suicidality != SUICIDE_INTENT_NONE:
4296 html = bold(html)
4297 return html
4299 def get_doctell(self, req: CamcopsRequest) -> str:
4300 if self.doctor is None:
4301 return ""
4302 return self.xstring(req, f"doctell_{self.doctor}")
4303 # ... xstring() as may use HTML
4305 def get_sleep_change(self, req: CamcopsRequest, result: CisrResult) -> str:
4306 if result.sleep_change == SLEEPCHANGE_NONE:
4307 return ""
4308 return self.wxstring(req, f"sleepch_{result.sleep_change}")
4310 def get_weight_change(self, req: CamcopsRequest, result: CisrResult) -> str:
4311 if result.weight_change in [WTCHANGE_NONE_OR_APPETITE_INCREASE,
4312 WTCHANGE_APPETITE_LOSS]:
4313 return ""
4314 return self.wxstring(req, f"wtchange_{result.weight_change}")
4316 def get_impairment(self, req: CamcopsRequest, result: CisrResult) -> str:
4317 return self.wxstring(
4318 req, f"impair_{result.functional_impairment}")
4320 def get_task_html(self, req: CamcopsRequest) -> str:
4321 # Iterate only once, for efficiency, so don't use get_result().
4323 def qa_row(q_: CisrQuestion, qtext: str,
4324 a_: Optional[str]) -> str:
4325 return tr(f"{q_.value}. {qtext}", answer(a_))
4327 def max_text(maxval: int) -> str:
4328 return f" (max. {maxval})"
4330 demographics_html_list = [] # type: List[str]
4331 question_html_list = [] # type: List[str]
4332 q = CQ.ETHNIC # type: CisrQuestion
4333 result = CisrResult(record_decisions=True)
4334 while (not result.incomplete) and q != CQ.END_MARKER:
4335 # Iterate until we get to the end or the result declares itself
4336 # incomplete.
4337 # noinspection PyTypeChecker
4338 target_list = (
4339 demographics_html_list if q.value < CQ.HEALTH_WELLBEING.value
4340 else question_html_list)
4341 if q in QUESTIONS_PROMPT_ONLY:
4342 question = self.wxstring(req, QUESTIONS_PROMPT_ONLY[q])
4343 target_list.append(
4344 qa_row(q, question, NOT_APPLICABLE_TEXT))
4345 elif q == CQ.PANSYM: # special!
4346 target_list.append(qa_row(
4347 q,
4348 self.wxstring(req, "pansym_q_prefix"),
4349 NOT_APPLICABLE_TEXT))
4350 for fieldname in PANIC_SYMPTOM_FIELDNAMES:
4351 question = self.wxstring(req, fieldname + "_q")
4352 value = getattr(self, fieldname)
4353 a = get_yes_no_none(
4354 req, value == 2 if value is not None else None)
4355 target_list.append(qa_row(q, question, a))
4356 else:
4357 fieldname = fieldname_for_q(q)
4358 assert fieldname, f"No fieldname for question {q}"
4359 question = self.wxstring(req, fieldname + "_q")
4360 a = self.get_textual_answer(req, q)
4361 target_list.append(qa_row(q, question, a))
4363 q = self.next_q(q, result)
4364 # loop until we reach the end or have incomplete data
4365 result.finalize()
4367 is_complete = not result.incomplete
4368 is_complete_html_td = """{}<b>{}</b></td>""".format(
4369 "<td>" if is_complete
4370 else f"""<td class="{CssClass.INCOMPLETE}">""",
4371 get_yes_no(req, is_complete)
4372 )
4374 summary_rows = [
4376 subheading_spanning_two_columns("Diagnoses"),
4378 tr(
4379 "Probable primary diagnosis",
4380 (
4381 bold(self.diagnosis_name(req, result.diagnosis_1)) +
4382 (f" ({result.diagnosis_1_icd10_code()})"
4383 if result.has_diagnosis_1() else "")
4384 )
4385 ),
4386 tr(italic("... summary of reasons/description"),
4387 italic(self.diagnosis_reason(req, result.diagnosis_1))),
4388 tr(
4389 "Probable secondary diagnosis",
4390 (
4391 bold(self.diagnosis_name(req, result.diagnosis_2)) +
4392 (f" ({result.diagnosis_2_icd10_code()})"
4393 if result.has_diagnosis_2() else "")
4394 )
4395 ),
4396 tr(italic("... summary of reasons/description"),
4397 italic(self.diagnosis_reason(req, result.diagnosis_2))),
4399 subheading_spanning_two_columns("Suicidality"),
4401 tr(td(self.wxstring(req, "suicid_heading")),
4402 td(self.get_suicide_intent(req, result)),
4403 literal=True),
4404 tr("... spoken to doctor?",
4405 self.get_doctell(req)),
4407 subheading_spanning_two_columns("Total score/overall impairment"),
4409 tr(
4410 f"CIS-R total score (max. {MAX_TOTAL}) <sup>[1]</sup>",
4411 result.get_score()
4412 ),
4413 tr(self.wxstring(req, "impair_label"),
4414 self.get_impairment(req, result)),
4416 subheading_spanning_two_columns("Subscores contributing to total "
4417 "<sup>[2]</sup>"),
4419 tr(self.wxstring(req, "somatic_label") + max_text(MAX_SOMATIC),
4420 result.somatic_symptoms),
4421 tr(self.wxstring(req, "hypo_label") + max_text(MAX_HYPO),
4422 result.hypochondria),
4423 tr(self.wxstring(req, "irrit_label") + max_text(MAX_IRRIT),
4424 result.irritability),
4425 tr(self.wxstring(req, "conc_label") + max_text(MAX_CONC),
4426 result.concentration_poor),
4427 tr(self.wxstring(req, "fatigue_label") + max_text(MAX_FATIGUE),
4428 result.fatigue),
4429 tr(self.wxstring(req, "sleep_label") + max_text(MAX_SLEEP),
4430 result.sleep_problems),
4431 tr(self.wxstring(req, "depr_label") + max_text(MAX_DEPR),
4432 result.depression),
4433 tr(self.wxstring(req, "depthts_label") + max_text(MAX_DEPTHTS),
4434 result.depressive_thoughts),
4435 tr(self.wxstring(req, "phobias_label") + max_text(MAX_PHOBIAS),
4436 result.phobias_score),
4437 tr(self.wxstring(req, "worry_label") + max_text(MAX_WORRY),
4438 result.worry),
4439 tr(self.wxstring(req, "anx_label") + max_text(MAX_ANX),
4440 result.anxiety),
4441 tr(self.wxstring(req, "panic_label") + max_text(MAX_PANIC),
4442 result.panic),
4443 tr(self.wxstring(req, "comp_label") + max_text(MAX_COMP),
4444 result.compulsions),
4445 tr(self.wxstring(req, "obsess_label") + max_text(MAX_OBSESS),
4446 result.obsessions),
4448 subheading_spanning_two_columns("Other"),
4450 tr("Sleep change", self.get_sleep_change(req, result)),
4451 tr("Weight change", self.get_weight_change(req, result)),
4452 tr(DESC_DEPCRIT1, result.depr_crit_1_mood_anhedonia_energy),
4453 tr(DESC_DEPCRIT2, result.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui),
4454 tr(DESC_DEPCRIT3, result.depr_crit_3_somatic_synd),
4455 tr(DESC_DEPCRIT3_MET, result.has_somatic_syndrome()), # RNC
4456 tr(DESC_NEURASTHENIA_SCORE, result.neurasthenia),
4458 subheading_spanning_two_columns("Disorder flags"),
4460 tr(DISORDER_OCD, result.obsessive_compulsive_disorder),
4461 tr(DISORDER_DEPR_MILD, result.depression_mild),
4462 tr(DISORDER_DEPR_MOD, result.depression_moderate),
4463 tr(DISORDER_DEPR_SEV, result.depression_severe),
4464 tr(DISORDER_CFS, result.chronic_fatigue_syndrome),
4465 tr(DISORDER_GAD, result.generalized_anxiety_disorder),
4466 tr(DISORDER_AGORAPHOBIA, result.phobia_agoraphobia),
4467 tr(DISORDER_SOCIAL_PHOBIA, result.phobia_social),
4468 tr(DISORDER_SPECIFIC_PHOBIA, result.phobia_specific),
4469 tr(DISORDER_PANIC, result.panic_disorder),
4470 ]
4472 return f"""
4473 <div class="{CssClass.HEADING}">{self.wxstring(req, "results_1")}</div>
4474 <div>{self.wxstring(req, "results_2")}</div>
4475 <div class="{CssClass.SUMMARY}">
4476 <table class="{CssClass.SUMMARY}">
4477 <tr>
4478 <td width="50%">Completed?</td>
4479 {is_complete_html_td}
4480 </tr>
4481 {"".join(summary_rows)}
4482 </table>
4483 </div>
4485 <div class="{CssClass.FOOTNOTES}">
4486 [1] {self.wxstring(req, "score_note")}
4487 [2] {self.wxstring(req, "symptom_score_note")}
4488 </div>
4490 <div class="{CssClass.HEADING}">
4491 Preamble/demographics (not contributing to diagnosis)
4492 </div>
4493 <table class="{CssClass.TASKDETAIL}">
4494 <tr>
4495 <th width="75%">Page</th>
4496 <th width="25%">Answer</td>
4497 </tr>
4498 {"".join(demographics_html_list)}
4499 </table>
4501 <div class="{CssClass.HEADING}">
4502 Data considered by algorithm (may be a subset of all data if
4503 subject revised answers)
4504 </div>
4505 <table class="{CssClass.TASKDETAIL}">
4506 <tr>
4507 <th width="75%">Page</th>
4508 <th width="25%">Answer</td>
4509 </tr>
4510 {"".join(question_html_list)}
4511 </table>
4513 <div class="{CssClass.HEADING}">Decisions</div>
4514 <pre>{"<br>".join(ws.webify("‣ " + x) for x in result.decisions)}</pre>
4516 <div class="{CssClass.COPYRIGHT}">
4517 • Original papers:
4519 ▶ Lewis G, Pelosi AJ, Aray R, Dunn G (1992).
4520 Measuring psychiatric disorder in the community: a standardized
4521 assessment for use by lay interviewers.
4522 Psychological Medicine 22: 465-486. PubMed ID
4523 <a href="https://www.ncbi.nlm.nih.gov/pubmed/1615114">1615114</a>.
4525 ▶ Lewis G (1994).
4526 Assessing psychiatric disorder with a human interviewer or a
4527 computer.
4528 J Epidemiol Community Health 48: 207-210. PubMed ID
4529 <a href="https://www.ncbi.nlm.nih.gov/pubmed/8189180">8189180</a>.
4531 • Source/copyright: Glyn Lewis.
4533 ▶ The task itself is not in the reference publications, so
4534 copyright presumed to rest with the authors (not the journals).
4536 ▶ “There are no copyright issues with the CISR so please adapt
4537 it for use.” — Prof. Glyn Lewis, personal communication to
4538 Rudolf Cardinal, 27 Oct 2017.
4539 </div>
4540 """ # noqa