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