Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/cisr.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27""" 

28 

29from enum import Enum 

30import logging 

31from typing import List, Optional 

32 

33from cardinal_pythonlib.classes import classproperty 

34from cardinal_pythonlib.logs import BraceStyleAdapter 

35import cardinal_pythonlib.rnc_web as ws 

36from semantic_version import Version 

37from sqlalchemy.sql.sqltypes import Boolean, Integer, UnicodeText 

38 

39from camcops_server.cc_modules.cc_constants import CssClass 

40from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo 

41from camcops_server.cc_modules.cc_html import ( 

42 answer, 

43 bold, 

44 get_yes_no, 

45 get_yes_no_none, 

46 italic, 

47 subheading_spanning_two_columns, 

48 td, 

49 tr, 

50) 

51from camcops_server.cc_modules.cc_request import CamcopsRequest 

52from camcops_server.cc_modules.cc_sqla_coltypes import ( 

53 CamcopsColumn, 

54 ZERO_TO_FOUR_CHECKER, 

55 ONE_TO_TWO_CHECKER, 

56 ONE_TO_THREE_CHECKER, 

57 ONE_TO_FOUR_CHECKER, 

58 ONE_TO_FIVE_CHECKER, 

59 ONE_TO_SIX_CHECKER, 

60 ONE_TO_SEVEN_CHECKER, 

61 ONE_TO_EIGHT_CHECKER, 

62 ONE_TO_NINE_CHECKER, 

63) 

64from camcops_server.cc_modules.cc_summaryelement import SummaryElement 

65from camcops_server.cc_modules.cc_task import ( 

66 Task, 

67 TaskHasPatientMixin, 

68) 

69 

70log = BraceStyleAdapter(logging.getLogger(__name__)) 

71 

72 

73# ============================================================================= 

74# Constants 

75# ============================================================================= 

76 

77DEBUG_SHOW_QUESTIONS_CONSIDERED = True 

78 

79NOT_APPLICABLE_TEXT = "—" 

80 

81# ----------------------------------------------------------------------------- 

82# Comments for fields 

83# ----------------------------------------------------------------------------- 

84 

85CMT_DEMOGRAPHICS = "(Demographics) " 

86CMT_1_NO_2_YES = " (1 no, 2 yes)" 

87CMT_1_YES_2_NO = " (1 yes, 2 no)" 

88CMT_DURATION = (" (1: <2 weeks; 2: 2 weeks–6 months; 3: 6 months–1 year; " 

89 "4: 1–2 years; 5: >2 years)") 

90CMT_NEVER_SOMETIMES_ALWAYS = " (1 never, 2 sometimes, 3 always)" 

91CMT_DAYS_PER_WEEK = " (1: none, 2: one to three, 3: four or more)" 

92CMT_NIGHTS_PER_WEEK = CMT_DAYS_PER_WEEK 

93CMT_UNPLEASANT = (" (1 not at all, 2 a little unpleasant, 3 unpleasant, " 

94 "4 very unpleasant)") 

95CMT_NO_SOMETIMES_OFTEN = " (1 no, 2 sometimes, 3 often)" 

96CMT_BOTHERSOME_INTERESTING = (" (1 no, 2 yes, 3 haven't done anything " 

97 "interesting)") 

98CMT_DURING_ENJOYABLE = " (1 no, 2 yes, 3 haven't done anything enjoyable)" 

99CMT_FATIGUE_CAUSE = ( 

100 " (1 problems with sleep; 2 medication; 3 physical illness; 4 working too " 

101 "hard inc. childcare; 5 stress/worry/other psychological; 6 physical " 

102 "exercise; 7 other; 8 don't know)" 

103) 

104CMT_STRESSORS = ( 

105 " (1 family members; 2 relationships with friends/colleagues; 3 housing; " 

106 "4 money/bills; 5 own physical health; 6 own mental health; 7 work/lack " 

107 "of work; 8 legal; 9 political/news)" 

108) 

109CMT_SLEEP_CHANGE = " (1: <15min, 2: 15–60min, 3: 1–3h, 4: >=3h)" 

110CMT_ANHEDONIA = (" (1 yes; 2 no, less enjoyment than usual; " 

111 "3 no, don't enjoy anything)") 

112CMT_PANIC_SYMPTOM = "Panic symptom in past week: " 

113 

114# ... and results: 

115DESC_DEPCRIT1 = "Depressive criterion 1 (mood, anhedonia, energy; max. 3)" 

116DESC_DEPCRIT2 = ("Depressive criterion 2 (appetite/weight, concentration, " 

117 "sleep, motor, guilt, self-worth, suicidality; max. 7)") 

118DESC_DEPCRIT3 = ( 

119 "Depressive criterion 3: somatic syndrome (anhedonia, lack of emotional " 

120 "reactivity, early-morning waking, depression worse in the morning, " 

121 "psychomotor retardation/agitation, loss of appetite, weight loss, loss " 

122 "of libido; max. 8)" 

123) 

124DESC_DEPCRIT3_MET = "Somatic syndrome criterion met (≥4)?" 

125DESC_NEURASTHENIA_SCORE = "Neurasthenia score (max. 3)" 

126 

127DISORDER_OCD = "Obsessive–compulsive disorder" 

128DISORDER_DEPR_MILD = "Depressive episode: at least mild" 

129DISORDER_DEPR_MOD = "Depressive episode: at least moderate" 

130DISORDER_DEPR_SEV = "Depressive episode: severe" 

131DISORDER_CFS = "Chronic fatigue syndrome" 

132DISORDER_GAD = "Generalized anxiety disorder" 

133DISORDER_AGORAPHOBIA = "Agoraphobia" 

134DISORDER_SOCIAL_PHOBIA = "Social phobia" 

135DISORDER_SPECIFIC_PHOBIA = "Specific phobia" 

136DISORDER_PANIC = "Panic disorder" 

137 

138# ----------------------------------------------------------------------------- 

139# Number of response values (numbered from 1 to N) 

140# ----------------------------------------------------------------------------- 

141 

142N_DURATIONS = 5 

143N_OPTIONS_DAYS_PER_WEEK = 3 

144N_OPTIONS_NIGHTS_PER_WEEK = 3 

145N_OPTIONS_HOW_UNPLEASANT = 4 

146N_OPTIONS_FATIGUE_CAUSES = 8 

147N_OPTIONS_STRESSORS = 9 

148N_OPTIONS_NO_SOMETIMES_OFTEN = 3 

149NUM_PANIC_SYMPTOMS = 13 # from a to m 

150 

151# ----------------------------------------------------------------------------- 

152# Actual response values 

153# ----------------------------------------------------------------------------- 

154 

155# For e.g. SOMATIC_DUR, etc.: "How long have you..." 

156V_DURATION_LT_2W = 1 

157V_DURATION_2W_6M = 2 

158V_DURATION_6M_1Y = 3 

159V_DURATION_1Y_2Y = 4 

160V_DURATION_GE_2Y = 5 

161 

162# For quite a few: "on how many days in the past week...?" 

163V_DAYS_IN_PAST_WEEK_0 = 1 

164V_DAYS_IN_PAST_WEEK_1_TO_3 = 2 

165V_DAYS_IN_PAST_WEEK_4_OR_MORE = 3 

166 

167V_NIGHTS_IN_PAST_WEEK_0 = 1 

168V_NIGHTS_IN_PAST_WEEK_1_TO_3 = 2 

169V_NIGHTS_IN_PAST_WEEK_4_OR_MORE = 3 

170 

171V_HOW_UNPLEASANT_NOT_AT_ALL = 1 

172V_HOW_UNPLEASANT_A_LITTLE = 2 

173V_HOW_UNPLEASANT_UNPLEASANT = 3 

174V_HOW_UNPLEASANT_VERY = 4 

175 

176V_FATIGUE_CAUSE_SLEEP = 1 

177V_FATIGUE_CAUSE_MEDICATION = 2 

178V_FATIGUE_CAUSE_PHYSICAL_ILLNESS = 3 

179V_FATIGUE_CAUSE_OVERWORK = 4 

180V_FATIGUE_CAUSE_PSYCHOLOGICAL = 5 

181V_FATIGUE_CAUSE_EXERCISE = 6 

182V_FATIGUE_CAUSE_OTHER = 7 

183V_FATIGUE_CAUSE_DONT_KNOW = 8 

184 

185V_STRESSOR_FAMILY = 1 

186V_STRESSOR_FRIENDS_COLLEAGUES = 2 

187V_STRESSOR_HOUSING = 3 

188V_STRESSOR_MONEY = 4 

189V_STRESSOR_PHYSICAL_HEALTH = 5 

190V_STRESSOR_MENTAL_HEALTH = 6 

191V_STRESSOR_WORK = 7 

192V_STRESSOR_LEGAL = 8 

193V_STRESSOR_POLITICAL_NEWS = 9 

194 

195V_NSO_NO = 1 

196V_NSO_SOMETIMES = 2 

197V_NSO_OFTEN = 3 

198 

199V_SLEEP_CHANGE_LT_15_MIN = 1 

200V_SLEEP_CHANGE_15_MIN_TO_1_H = 2 

201V_SLEEP_CHANGE_1_TO_3_H = 3 

202V_SLEEP_CHANGE_GT_3_H = 4 

203 

204V_ANHEDONIA_ENJOYING_NORMALLY = 1 

205V_ANHEDONIA_ENJOYING_LESS = 2 

206V_ANHEDONIA_NOT_ENJOYING = 3 

207 

208# Specific other question values: 

209 

210V_EMPSTAT_FT = 1 # unused 

211V_EMPSTAT_PT = 2 # unused 

212V_EMPSTAT_STUDENT = 3 # unused 

213V_EMPSTAT_RETIRED = 4 # unused 

214V_EMPSTAT_HOUSEPERSON = 5 # unused 

215V_EMPSTAT_UNEMPJOBSEEKER = 6 # unused 

216V_EMPSTAT_UNEMPILLHEALTH = 7 # unused 

217 

218V_EMPTYPE_SELFEMPWITHEMPLOYEES = 1 # unused 

219V_EMPTYPE_SELFEMPNOEMPLOYEES = 2 # unused 

220V_EMPTYPE_EMPLOYEE = 3 # unused 

221V_EMPTYPE_SUPERVISOR = 4 # unused 

222V_EMPTYPE_MANAGER = 5 # unused 

223V_EMPTYPE_NOT_APPLICABLE = 6 # unused 

224# ... the last one: added by RNC, in case pt never employed. (Mentioned to 

225# Glyn Lewis 2017-12-04. Not, in any case, part of the important bits of the 

226# CIS-R.) 

227 

228V_HOME_OWNER = 1 # unused 

229V_HOME_TENANT = 2 # unused 

230V_HOME_RELATIVEFRIEND = 3 # unused 

231V_HOME_HOSTELCAREHOME = 4 # unused 

232V_HOME_HOMELESS = 5 # unused 

233V_HOME_OTHER = 6 # unused 

234 

235V_WEIGHT2_WTLOSS_NOTTRYING = 1 

236V_WEIGHT2_WTLOSS_TRYING = 2 

237 

238V_WEIGHT3_WTLOSS_GE_HALF_STONE = 1 

239V_WEIGHT3_WTLOSS_LT_HALF_STONE = 2 

240 

241V_WEIGHT4_WTGAIN_YES_PREGNANT = 3 

242 

243V_WEIGHT5_WTGAIN_GE_HALF_STONE = 1 

244V_WEIGHT5_WTGAIN_LT_HALF_STONE = 2 

245 

246V_GPYEAR_NONE = 0 

247V_GPYEAR_1_2 = 1 

248V_GPYEAR_3_5 = 2 

249V_GPYEAR_6_10 = 3 

250V_GPYEAR_GT_10 = 4 

251 

252V_ILLNESS_DIABETES = 1 

253V_ILLNESS_ASTHMA = 2 

254V_ILLNESS_ARTHRITIS = 3 

255V_ILLNESS_HEART_DISEASE = 4 

256V_ILLNESS_HYPERTENSION = 5 

257V_ILLNESS_LUNG_DISEASE = 6 

258V_ILLNESS_MORE_THAN_ONE = 7 

259V_ILLNESS_NONE = 8 

260 

261V_SOMATIC_PAIN1_NEVER = 1 

262V_SOMATIC_PAIN1_SOMETIMES = 2 

263V_SOMATIC_PAIN1_ALWAYS = 3 

264 

265V_SOMATIC_PAIN3_LT_3H = 1 

266V_SOMATIC_PAIN3_GT_3H = 2 

267 

268V_SOMATIC_PAIN4_NOT_AT_ALL = 1 

269V_SOMATIC_PAIN4_LITTLE_UNPLEASANT = 2 

270V_SOMATIC_PAIN4_UNPLEASANT = 3 

271V_SOMATIC_PAIN4_VERY_UNPLEASANT = 4 

272 

273V_SOMATIC_PAIN5_NO = 1 

274V_SOMATIC_PAIN5_YES = 2 

275V_SOMATIC_PAIN5_NOT_DONE_ANYTHING_INTERESTING = 3 

276 

277V_SOMATIC_MAND2_NO = 1 

278V_SOMATIC_MAND2_YES = 2 

279 

280V_SOMATIC_DIS1_NEVER = 1 

281V_SOMATIC_DIS1_SOMETIMES = 2 

282V_SOMATIC_DIS1_ALWAYS = 3 

283 

284V_SOMATIC_DIS2_NONE = 1 

285V_SOMATIC_DIS2_1_TO_3_DAYS = 2 

286V_SOMATIC_DIS2_4_OR_MORE_DAYS = 3 

287 

288V_SOMATIC_DIS3_LT_3H = 1 

289V_SOMATIC_DIS3_GT_3H = 2 

290 

291V_SOMATIC_DIS4_NOT_AT_ALL = 1 

292V_SOMATIC_DIS4_LITTLE_UNPLEASANT = 2 

293V_SOMATIC_DIS4_UNPLEASANT = 3 

294V_SOMATIC_DIS4_VERY_UNPLEASANT = 4 

295 

296V_SOMATIC_DIS5_NO = 1 

297V_SOMATIC_DIS5_YES = 2 

298V_SOMATIC_DIS5_NOT_DONE_ANYTHING_INTERESTING = 3 

299 

300V_SLEEP_MAND2_NO = 1 

301V_SLEEP_MAND2_YES_BUT_NOT_A_PROBLEM = 2 

302V_SLEEP_MAND2_YES = 3 

303 

304V_IRRIT_MAND2_NO = 1 

305V_IRRIT_MAND2_SOMETIMES = 2 

306V_IRRIT_MAND2_YES = 3 

307 

308V_IRRIT3_SHOUTING_NO = 1 

309V_IRRIT3_SHOUTING_WANTED_TO = 2 

310V_IRRIT3_SHOUTING_DID = 3 

311 

312V_IRRIT4_ARGUMENTS_NO = 1 

313V_IRRIT4_ARGUMENTS_YES_JUSTIFIED = 2 

314V_IRRIT4_ARGUMENTS_YES_UNJUSTIFIED = 3 

315 

316V_DEPR5_COULD_CHEER_UP_YES = 1 

317V_DEPR5_COULD_CHEER_UP_SOMETIMES = 2 

318V_DEPR5_COULD_CHEER_UP_NO = 3 

319 

320V_DEPTH1_DMV_WORSE_MORNING = 1 

321V_DEPTH1_DMV_WORSE_EVENING = 2 

322V_DEPTH1_DMV_VARIES = 3 

323V_DEPTH1_DMV_NONE = 4 

324 

325V_DEPTH2_LIBIDO_NA = 1 

326V_DEPTH2_LIBIDO_NO_CHANGE = 2 

327V_DEPTH2_LIBIDO_INCREASED = 3 

328V_DEPTH2_LIBIDO_DECREASED = 4 

329 

330V_DEPTH5_GUILT_NEVER = 1 

331V_DEPTH5_GUILT_WHEN_AT_FAULT = 2 

332V_DEPTH5_GUILT_SOMETIMES = 3 

333V_DEPTH5_GUILT_OFTEN = 4 

334 

335V_DEPTH8_LNWL_NO = 1 

336V_DEPTH8_LNWL_SOMETIMES = 2 

337V_DEPTH8_LNWL_ALWAYS = 3 

338 

339V_DEPTH9_SUICIDAL_THOUGHTS_NO = 1 

340V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD = 2 

341V_DEPTH9_SUICIDAL_THOUGHTS_YES = 3 

342 

343V_DOCTOR_YES = 1 

344V_DOCTOR_NO_BUT_OTHERS = 2 

345V_DOCTOR_NO = 3 

346 

347V_ANX_PHOBIA2_ALWAYS_SPECIFIC = 1 

348V_ANX_PHOBIA2_SOMETIMES_GENERAL = 2 

349 

350V_PHOBIAS_TYPE1_ALONE_PUBLIC_TRANSPORT = 1 

351V_PHOBIAS_TYPE1_FAR_FROM_HOME = 2 

352V_PHOBIAS_TYPE1_PUBLIC_SPEAKING_EATING = 3 

353V_PHOBIAS_TYPE1_BLOOD = 4 

354V_PHOBIAS_TYPE1_CROWDED_SHOPS = 5 

355V_PHOBIAS_TYPE1_ANIMALS = 6 

356V_PHOBIAS_TYPE1_BEING_WATCHED = 7 

357V_PHOBIAS_TYPE1_ENCLOSED_SPACES_HEIGHTS = 8 

358V_PHOBIAS_TYPE1_OTHER = 9 

359 

360V_PANIC1_N_PANICS_PAST_WEEK_0 = 1 

361V_PANIC1_N_PANICS_PAST_WEEK_1 = 2 

362V_PANIC1_N_PANICS_PAST_WEEK_GT_1 = 3 

363 

364V_PANIC3_WORST_LT_10_MIN = 1 

365V_PANIC3_WORST_GE_10_MIN = 2 

366 

367V_COMP4_MAX_N_REPEATS_1 = 1 

368V_COMP4_MAX_N_REPEATS_2 = 2 

369V_COMP4_MAX_N_REPEATS_GE_3 = 3 

370 

371V_OBSESS_MAND1_SAME_THOUGHTS_REPEATED = 1 

372V_OBSESS_MAND1_GENERAL_WORRIES = 2 

373 

374V_OBSESS4_LT_15_MIN = 1 

375V_OBSESS4_GE_15_MIN = 2 

376 

377V_OVERALL_IMPAIRMENT_NONE = 1 

378V_OVERALL_IMPAIRMENT_DIFFICULT = 2 

379V_OVERALL_IMPAIRMENT_STOP_1_ACTIVITY = 3 

380V_OVERALL_IMPAIRMENT_STOP_GT_1_ACTIVITY = 4 

381 

382# ----------------------------------------------------------------------------- 

383# Internal coding, NOT answer values: 

384# ----------------------------------------------------------------------------- 

385 

386# Magic numbers from the original: 

387WTCHANGE_NONE_OR_APPETITE_INCREASE = 0 

388WTCHANGE_APPETITE_LOSS = 1 

389WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN = 2 

390WTCHANGE_WTLOSS_GE_HALF_STONE = 3 

391WTCHANGE_WTGAIN_GE_HALF_STONE = 4 

392# ... I'm not entirely sure why this labelling system is used! 

393 

394DESC_WEIGHT_CHANGE = ( 

395 "Weight change " 

396 f"({WTCHANGE_NONE_OR_APPETITE_INCREASE}: none or appetite increase; " 

397 f"{WTCHANGE_APPETITE_LOSS}: appetite loss without weight loss; " 

398 f"{WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN}: " 

399 "non-deliberate weight loss or gain <0.5 st; " 

400 f"{WTCHANGE_WTLOSS_GE_HALF_STONE}: weight loss ≥0.5 st; " 

401 f"{WTCHANGE_WTGAIN_GE_HALF_STONE}: weight gain ≥0.5 st)" 

402) 

403 

404PHOBIATYPES_OTHER = 0 

405PHOBIATYPES_AGORAPHOBIA = 1 

406PHOBIATYPES_SOCIAL = 2 

407PHOBIATYPES_BLOOD_INJURY = 3 

408PHOBIATYPES_ANIMALS_ENCLOSED_HEIGHTS = 4 

409# ... some of these are not really used, but I've followed the original CIS-R 

410# for clarity 

411 

412# One smaller than the answer codes: 

413OVERALL_IMPAIRMENT_NONE = 0 

414OVERALL_IMPAIRMENT_DIFFICULT = 1 

415OVERALL_IMPAIRMENT_STOP_1_ACTIVITY = 2 

416OVERALL_IMPAIRMENT_STOP_GT_1_ACTIVITY = 3 

417 

418# Again, we're following this coding structure primarily for compatibility: 

419DIAG_0_NO_DIAGNOSIS = 0 

420DIAG_1_MIXED_ANX_DEPR_DIS_MILD = 1 

421DIAG_2_GENERALIZED_ANX_DIS_MILD = 2 

422DIAG_3_OBSESSIVE_COMPULSIVE_DIS = 3 

423DIAG_4_MIXED_ANX_DEPR_DIS = 4 

424DIAG_5_SPECIFIC_PHOBIA = 5 

425DIAG_6_SOCIAL_PHOBIA = 6 

426DIAG_7_AGORAPHOBIA = 7 

427DIAG_8_GENERALIZED_ANX_DIS = 8 

428DIAG_9_PANIC_DIS = 9 

429DIAG_10_MILD_DEPR_EPISODE = 10 

430DIAG_11_MOD_DEPR_EPISODE = 11 

431DIAG_12_SEVERE_DEPR_EPISODE = 12 

432 

433SUICIDE_INTENT_NONE = 0 

434SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS = 1 

435SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING = 2 

436SUICIDE_INTENT_SUICIDAL_THOUGHTS = 3 

437SUICIDE_INTENT_SUICIDAL_PLANS = 4 

438 

439SLEEPCHANGE_NONE = 0 # added 

440SLEEPCHANGE_EMW = 1 

441SLEEPCHANGE_INSOMNIA_NOT_EMW = 2 

442SLEEPCHANGE_INCREASE = 3 

443 

444DESC_SLEEP_CHANGE = ( 

445 f"Sleep change ({SLEEPCHANGE_NONE}: none; " 

446 f"{SLEEPCHANGE_EMW}: early-morning waking; " 

447 f"{SLEEPCHANGE_INSOMNIA_NOT_EMW}: insomnia without early-morning waking; " 

448 f"{SLEEPCHANGE_INCREASE}: sleep increase)" 

449) 

450 

451DIURNAL_MOOD_VAR_NONE = 0 

452DIURNAL_MOOD_VAR_WORSE_MORNING = 1 

453DIURNAL_MOOD_VAR_WORSE_EVENING = 2 

454 

455PSYCHOMOTOR_NONE = 0 

456PSYCHOMOTOR_RETARDATION = 1 

457PSYCHOMOTOR_AGITATION = 2 

458 

459# Answer values or pseudo-values: 

460 

461V_MISSING = 0 # Integer value of a missing answer 

462V_UNKNOWN = -1 # Dummy value, never used for answers 

463 

464SCORE_PREFIX = "... " 

465 

466# ----------------------------------------------------------------------------- 

467# Scoring constants: 

468# ----------------------------------------------------------------------------- 

469 

470MAX_TOTAL = 57 

471 

472MAX_SOMATIC = 4 

473MAX_HYPO = 4 

474MAX_IRRIT = 4 

475MAX_CONC = 4 

476MAX_FATIGUE = 4 

477MAX_SLEEP = 4 

478MAX_DEPR = 4 

479MAX_DEPTHTS = 5 

480MAX_PHOBIAS = 4 

481MAX_WORRY = 4 

482MAX_ANX = 4 

483MAX_PANIC = 4 

484MAX_COMP = 4 

485MAX_OBSESS = 4 

486MAX_DEPCRIT1 = 3 

487MAX_DEPCRIT2 = 7 

488MAX_DEPCRIT3 = 8 

489 

490SOMATIC_SYNDROME_CRITERION = 4 # number of symptoms 

491 

492# ----------------------------------------------------------------------------- 

493# Question numbers 

494# ----------------------------------------------------------------------------- 

495 

496# Not quite sure to do an autonumbering enum that also can have synonyms, like 

497# C++. The AutoNumberEnum (q.v.) is close, but won't do the synonyms. So: 

498 

499_nasty_hack_next_enum = 1 # start with 1 

500 

501 

502def next_enum() -> int: 

503 global _nasty_hack_next_enum 

504 v = _nasty_hack_next_enum 

505 _nasty_hack_next_enum += 1 

506 return v 

507 

508 

509class CisrQuestion(Enum): 

510 # The values below look like integers, but they aren't; they are of type 

511 # CisrQuestion, and have attributes like ".value". 

512 START_MARKER = next_enum() 

513 

514 INTRO_1 = START_MARKER 

515 INTRO_2 = next_enum() 

516 INTRO_DEMOGRAPHICS = next_enum() 

517 

518 ETHNIC = next_enum() 

519 MARRIED = next_enum() 

520 EMPSTAT = next_enum() 

521 EMPTYPE = next_enum() 

522 HOME = next_enum() 

523 HEALTH_WELLBEING = next_enum() 

524 

525 APPETITE1_LOSS_PAST_MONTH = next_enum() 

526 

527 WEIGHT1_LOSS_PAST_MONTH = next_enum() 

528 WEIGHT2_TRYING_TO_LOSE = next_enum() 

529 WEIGHT3_LOST_LOTS = next_enum() 

530 APPETITE2_INCREASE_PAST_MONTH = next_enum() 

531 WEIGHT4_INCREASE_PAST_MONTH = next_enum() 

532 # WEIGHT4A = WEIGHT4 with pregnancy question; blended 

533 WEIGHT5_GAINED_LOTS = next_enum() 

534 GP_YEAR = next_enum() 

535 DISABLE = next_enum() 

536 ILLNESS = next_enum() 

537 

538 SOMATIC_MAND1_PAIN_PAST_MONTH = next_enum() 

539 SOMATIC_PAIN1_PSYCHOL_EXAC = next_enum() 

540 SOMATIC_PAIN2_DAYS_PAST_WEEK = next_enum() 

541 SOMATIC_PAIN3_GT_3H_ANY_DAY = next_enum() 

542 SOMATIC_PAIN4_UNPLEASANT = next_enum() 

543 SOMATIC_PAIN5_INTERRUPTED_INTERESTING = next_enum() 

544 SOMATIC_MAND2_DISCOMFORT = next_enum() 

545 SOMATIC_DIS1_PSYCHOL_EXAC = next_enum() 

546 SOMATIC_DIS2_DAYS_PAST_WEEK = next_enum() 

547 SOMATIC_DIS3_GT_3H_ANY_DAY = next_enum() 

548 SOMATIC_DIS4_UNPLEASANT = next_enum() 

549 SOMATIC_DIS5_INTERRUPTED_INTERESTING = next_enum() 

550 SOMATIC_DUR = next_enum() 

551 

552 FATIGUE_MAND1_TIRED_PAST_MONTH = next_enum() 

553 FATIGUE_CAUSE1_TIRED = next_enum() 

554 FATIGUE_TIRED1_DAYS_PAST_WEEK = next_enum() 

555 FATIGUE_TIRED2_GT_3H_ANY_DAY = next_enum() 

556 FATIGUE_TIRED3_HAD_TO_PUSH = next_enum() 

557 FATIGUE_TIRED4_DURING_ENJOYABLE = next_enum() 

558 FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH = next_enum() 

559 FATIGUE_CAUSE2_LACK_ENERGY = next_enum() 

560 FATIGUE_ENERGY1_DAYS_PAST_WEEK = next_enum() 

561 FATIGUE_ENERGY2_GT_3H_ANY_DAY = next_enum() 

562 FATIGUE_ENERGY3_HAD_TO_PUSH = next_enum() 

563 FATIGUE_ENERGY4_DURING_ENJOYABLE = next_enum() 

564 FATIGUE_DUR = next_enum() 

565 

566 CONC_MAND1_POOR_CONC_PAST_MONTH = next_enum() 

567 CONC_MAND2_FORGETFUL_PAST_MONTH = next_enum() 

568 CONC1_CONC_DAYS_PAST_WEEK = next_enum() 

569 CONC2_CONC_FOR_TV_READING_CONVERSATION = next_enum() 

570 CONC3_CONC_PREVENTED_ACTIVITIES = next_enum() 

571 CONC_DUR = next_enum() 

572 CONC4_FORGOTTEN_IMPORTANT = next_enum() 

573 FORGET_DUR = next_enum() 

574 

575 SLEEP_MAND1_LOSS_PAST_MONTH = next_enum() 

576 SLEEP_LOSE1_NIGHTS_PAST_WEEK = next_enum() 

577 SLEEP_LOSE2_DIS_WORST_DURATION = next_enum() # DIS = delayed initiation of sleep # noqa 

578 SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK = next_enum() 

579 SLEEP_EMW_PAST_WEEK = next_enum() # EMW = early-morning waking 

580 SLEEP_CAUSE = next_enum() 

581 SLEEP_MAND2_GAIN_PAST_MONTH = next_enum() 

582 SLEEP_GAIN1_NIGHTS_PAST_WEEK = next_enum() 

583 SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT = next_enum() 

584 SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK = next_enum() 

585 SLEEP_DUR = next_enum() 

586 

587 IRRIT_MAND1_PEOPLE_PAST_MONTH = next_enum() 

588 IRRIT_MAND2_THINGS_PAST_MONTH = next_enum() 

589 IRRIT1_DAYS_PER_WEEK = next_enum() 

590 IRRIT2_GT_1H_ANY_DAY = next_enum() 

591 IRRIT3_WANTED_TO_SHOUT = next_enum() 

592 IRRIT4_ARGUMENTS = next_enum() 

593 IRRIT_DUR = next_enum() 

594 

595 HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH = next_enum() 

596 HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS = next_enum() 

597 HYPO1_DAYS_PAST_WEEK = next_enum() 

598 HYPO2_WORRY_TOO_MUCH = next_enum() 

599 HYPO3_HOW_UNPLEASANT = next_enum() 

600 HYPO4_CAN_DISTRACT = next_enum() 

601 HYPO_DUR = next_enum() 

602 

603 DEPR_MAND1_LOW_MOOD_PAST_MONTH = next_enum() 

604 DEPR1_LOW_MOOD_PAST_WEEK = next_enum() 

605 DEPR_MAND2_ENJOYMENT_PAST_MONTH = next_enum() 

606 DEPR2_ENJOYMENT_PAST_WEEK = next_enum() 

607 DEPR3_DAYS_PAST_WEEK = next_enum() 

608 DEPR4_GT_3H_ANY_DAY = next_enum() 

609 DEPR_CONTENT = next_enum() 

610 DEPR5_COULD_CHEER_UP = next_enum() 

611 DEPR_DUR = next_enum() 

612 DEPTH1_DIURNAL_VARIATION = next_enum() # "depth" = depressive thoughts? 

613 DEPTH2_LIBIDO = next_enum() 

614 DEPTH3_RESTLESS = next_enum() 

615 DEPTH4_SLOWED = next_enum() 

616 DEPTH5_GUILT = next_enum() 

617 DEPTH6_WORSE_THAN_OTHERS = next_enum() 

618 DEPTH7_HOPELESS = next_enum() 

619 DEPTH8_LNWL = next_enum() # life not worth living 

620 DEPTH9_SUICIDE_THOUGHTS = next_enum() 

621 DEPTH10_SUICIDE_METHOD = next_enum() 

622 DOCTOR = next_enum() 

623 DOCTOR2_PLEASE_TALK_TO = next_enum() 

624 DEPR_OUTRO = next_enum() 

625 

626 WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH = next_enum() 

627 WORRY_MAND2_ANY_WORRIES_PAST_MONTH = next_enum() 

628 WORRY_CONT1 = next_enum() 

629 WORRY1_INFO_ONLY = next_enum() 

630 WORRY2_DAYS_PAST_WEEK = next_enum() 

631 WORRY3_TOO_MUCH = next_enum() 

632 WORRY4_HOW_UNPLEASANT = next_enum() 

633 WORRY5_GT_3H_ANY_DAY = next_enum() 

634 WORRY_DUR = next_enum() 

635 

636 ANX_MAND1_ANXIETY_PAST_MONTH = next_enum() 

637 ANX_MAND2_TENSION_PAST_MONTH = next_enum() 

638 ANX_PHOBIA1_SPECIFIC_PAST_MONTH = next_enum() 

639 ANX_PHOBIA2_SPECIFIC_OR_GENERAL = next_enum() 

640 ANX1_INFO_ONLY = next_enum() 

641 ANX2_GENERAL_DAYS_PAST_WEEK = next_enum() 

642 ANX3_GENERAL_HOW_UNPLEASANT = next_enum() 

643 ANX4_GENERAL_PHYSICAL_SYMPTOMS = next_enum() 

644 ANX5_GENERAL_GT_3H_ANY_DAY = next_enum() 

645 ANX_DUR_GENERAL = next_enum() 

646 

647 PHOBIAS_MAND_AVOIDANCE_PAST_MONTH = next_enum() 

648 PHOBIAS_TYPE1 = next_enum() 

649 PHOBIAS1_DAYS_PAST_WEEK = next_enum() 

650 PHOBIAS2_PHYSICAL_SYMPTOMS = next_enum() 

651 PHOBIAS3_AVOIDANCE = next_enum() 

652 PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK = next_enum() 

653 PHOBIAS_DUR = next_enum() 

654 

655 PANIC_MAND_PAST_MONTH = next_enum() 

656 PANIC1_NUM_PAST_WEEK = next_enum() 

657 PANIC2_HOW_UNPLEASANT = next_enum() 

658 PANIC3_PANIC_GE_10_MIN = next_enum() 

659 PANIC4_RAPID_ONSET = next_enum() 

660 PANSYM = next_enum() # questions about each of several symptoms 

661 PANIC5_ALWAYS_SPECIFIC_TRIGGER = next_enum() 

662 PANIC_DUR = next_enum() 

663 

664 ANX_OUTRO = next_enum() 

665 

666 COMP_MAND1_COMPULSIONS_PAST_MONTH = next_enum() 

667 COMP1_DAYS_PAST_WEEK = next_enum() 

668 COMP2_TRIED_TO_STOP = next_enum() 

669 COMP3_UPSETTING = next_enum() 

670 COMP4_MAX_N_REPETITIONS = next_enum() 

671 COMP_DUR = next_enum() 

672 

673 OBSESS_MAND1_OBSESSIONS_PAST_MONTH = next_enum() 

674 OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL = next_enum() 

675 OBSESS1_DAYS_PAST_WEEK = next_enum() 

676 OBSESS2_TRIED_TO_STOP = next_enum() 

677 OBSESS3_UPSETTING = next_enum() 

678 OBSESS4_MAX_DURATION = next_enum() 

679 OBSESS_DUR = next_enum() 

680 

681 OVERALL1_INFO_ONLY = next_enum() 

682 OVERALL2_IMPACT_PAST_WEEK = next_enum() 

683 THANKS_FINISHED = next_enum() 

684 END_MARKER = next_enum() # not a real page 

685 

686 

687CQ = CisrQuestion # shorthand 

688 

689# Demographics section 

690FN_ETHNIC = "ethnic" 

691FN_MARRIED = "married" 

692FN_EMPSTAT = "empstat" 

693FN_EMPTYPE = "emptype" 

694FN_HOME = "home" 

695 

696FN_APPETITE1 = "appetite1" 

697FN_WEIGHT1 = "weight1" 

698FN_WEIGHT2 = "weight2" 

699FN_WEIGHT3 = "weight3" 

700FN_APPETITE2 = "appetite2" 

701FN_WEIGHT4 = "weight4" # male/female responses unified (no "weight4a" 

702FN_WEIGHT5 = "weight5" 

703 

704FN_GP_YEAR = "gp_year" 

705FN_DISABLE = "disable" 

706FN_ILLNESS = "illness" 

707 

708FN_SOMATIC_MAND1 = "somatic_mand1" 

709FN_SOMATIC_PAIN1 = "somatic_pain1" 

710FN_SOMATIC_PAIN2 = "somatic_pain2" 

711FN_SOMATIC_PAIN3 = "somatic_pain3" 

712FN_SOMATIC_PAIN4 = "somatic_pain4" 

713FN_SOMATIC_PAIN5 = "somatic_pain5" 

714FN_SOMATIC_MAND2 = "somatic_mand2" 

715FN_SOMATIC_DIS1 = "somatic_dis1" 

716FN_SOMATIC_DIS2 = "somatic_dis2" 

717FN_SOMATIC_DIS3 = "somatic_dis3" 

718FN_SOMATIC_DIS4 = "somatic_dis4" 

719FN_SOMATIC_DIS5 = "somatic_dis5" 

720FN_SOMATIC_DUR = "somatic_dur" 

721 

722FN_FATIGUE_MAND1 = "fatigue_mand1" 

723FN_FATIGUE_CAUSE1 = "fatigue_cause1" 

724FN_FATIGUE_TIRED1 = "fatigue_tired1" 

725FN_FATIGUE_TIRED2 = "fatigue_tired2" 

726FN_FATIGUE_TIRED3 = "fatigue_tired3" 

727FN_FATIGUE_TIRED4 = "fatigue_tired4" 

728FN_FATIGUE_MAND2 = "fatigue_mand2" 

729FN_FATIGUE_CAUSE2 = "fatigue_cause2" 

730FN_FATIGUE_ENERGY1 = "fatigue_energy1" 

731FN_FATIGUE_ENERGY2 = "fatigue_energy2" 

732FN_FATIGUE_ENERGY3 = "fatigue_energy3" 

733FN_FATIGUE_ENERGY4 = "fatigue_energy4" 

734FN_FATIGUE_DUR = "fatigue_dur" 

735 

736FN_CONC_MAND1 = "conc_mand1" 

737FN_CONC_MAND2 = "conc_mand2" 

738FN_CONC1 = "conc1" 

739FN_CONC2 = "conc2" 

740FN_CONC3 = "conc3" 

741FN_CONC_DUR = "conc_dur" 

742FN_CONC4 = "conc4" 

743FN_FORGET_DUR = "forget_dur" 

744 

745FN_SLEEP_MAND1 = "sleep_mand1" 

746FN_SLEEP_LOSE1 = "sleep_lose1" 

747FN_SLEEP_LOSE2 = "sleep_lose2" 

748FN_SLEEP_LOSE3 = "sleep_lose3" 

749FN_SLEEP_EMW = "sleep_emw" 

750FN_SLEEP_CAUSE = "sleep_cause" 

751FN_SLEEP_MAND2 = "sleep_mand2" 

752FN_SLEEP_GAIN1 = "sleep_gain1" 

753FN_SLEEP_GAIN2 = "sleep_gain2" 

754FN_SLEEP_GAIN3 = "sleep_gain3" 

755FN_SLEEP_DUR = "sleep_dur" 

756 

757FN_IRRIT_MAND1 = "irrit_mand1" 

758FN_IRRIT_MAND2 = "irrit_mand2" 

759FN_IRRIT1 = "irrit1" 

760FN_IRRIT2 = "irrit2" 

761FN_IRRIT3 = "irrit3" 

762FN_IRRIT4 = "irrit4" 

763FN_IRRIT_DUR = "irrit_dur" 

764 

765FN_HYPO_MAND1 = "hypo_mand1" 

766FN_HYPO_MAND2 = "hypo_mand2" 

767FN_HYPO1 = "hypo1" 

768FN_HYPO2 = "hypo2" 

769FN_HYPO3 = "hypo3" 

770FN_HYPO4 = "hypo4" 

771FN_HYPO_DUR = "hypo_dur" 

772 

773FN_DEPR_MAND1 = "depr_mand1" 

774FN_DEPR1 = "depr1" 

775FN_DEPR_MAND2 = "depr_mand2" 

776FN_DEPR2 = "depr2" 

777FN_DEPR3 = "depr3" 

778FN_DEPR4 = "depr4" 

779FN_DEPR_CONTENT = "depr_content" 

780FN_DEPR5 = "depr5" 

781FN_DEPR_DUR = "depr_dur" 

782FN_DEPTH1 = "depth1" 

783FN_DEPTH2 = "depth2" 

784FN_DEPTH3 = "depth3" 

785FN_DEPTH4 = "depth4" 

786FN_DEPTH5 = "depth5" 

787FN_DEPTH6 = "depth6" 

788FN_DEPTH7 = "depth7" 

789FN_DEPTH8 = "depth8" 

790FN_DEPTH9 = "depth9" 

791FN_DEPTH10 = "depth10" 

792FN_DOCTOR = "doctor" 

793 

794FN_WORRY_MAND1 = "worry_mand1" 

795FN_WORRY_MAND2 = "worry_mand2" 

796FN_WORRY_CONT1 = "worry_cont1" 

797FN_WORRY2 = "worry2" 

798FN_WORRY3 = "worry3" 

799FN_WORRY4 = "worry4" 

800FN_WORRY5 = "worry5" 

801FN_WORRY_DUR = "worry_dur" 

802 

803FN_ANX_MAND1 = "anx_mand1" 

804FN_ANX_MAND2 = "anx_mand2" 

805FN_ANX_PHOBIA1 = "anx_phobia1" 

806FN_ANX_PHOBIA2 = "anx_phobia2" 

807FN_ANX2 = "anx2" 

808FN_ANX3 = "anx3" 

809FN_ANX4 = "anx4" 

810FN_ANX5 = "anx5" 

811FN_ANX_DUR = "anx_dur" 

812 

813FN_PHOBIAS_MAND = "phobias_mand" 

814FN_PHOBIAS_TYPE1 = "phobias_type1" 

815FN_PHOBIAS1 = "phobias1" 

816FN_PHOBIAS2 = "phobias2" 

817FN_PHOBIAS3 = "phobias3" 

818FN_PHOBIAS4 = "phobias4" 

819FN_PHOBIAS_DUR = "phobias_dur" 

820 

821FN_PANIC_MAND = "panic_mand" 

822FN_PANIC1 = "panic1" 

823FN_PANIC2 = "panic2" 

824FN_PANIC3 = "panic3" 

825FN_PANIC4 = "panic4" 

826FN_PANSYM_A = "pansym_a" 

827FN_PANSYM_B = "pansym_b" 

828FN_PANSYM_C = "pansym_c" 

829FN_PANSYM_D = "pansym_d" 

830FN_PANSYM_E = "pansym_e" 

831FN_PANSYM_F = "pansym_f" 

832FN_PANSYM_G = "pansym_g" 

833FN_PANSYM_H = "pansym_h" 

834FN_PANSYM_I = "pansym_i" 

835FN_PANSYM_J = "pansym_j" 

836FN_PANSYM_K = "pansym_k" 

837FN_PANSYM_L = "pansym_l" 

838FN_PANSYM_M = "pansym_m" 

839FN_PANIC5 = "panic5" 

840FN_PANIC_DUR = "panic_dur" 

841 

842FN_COMP_MAND1 = "comp_mand1" 

843FN_COMP1 = "comp1" 

844FN_COMP2 = "comp2" 

845FN_COMP3 = "comp3" 

846FN_COMP4 = "comp4" 

847FN_COMP_DUR = "comp_dur" 

848 

849FN_OBSESS_MAND1 = "obsess_mand1" 

850FN_OBSESS_MAND2 = "obsess_mand2" 

851FN_OBSESS1 = "obsess1" 

852FN_OBSESS2 = "obsess2" 

853FN_OBSESS3 = "obsess3" 

854FN_OBSESS4 = "obsess4" 

855FN_OBSESS_DUR = "obsess_dur" 

856 

857FN_OVERALL2 = "overall2" 

858 

859PANIC_SYMPTOM_FIELDNAMES = [ 

860 FN_PANSYM_A, 

861 FN_PANSYM_B, 

862 FN_PANSYM_C, 

863 FN_PANSYM_D, 

864 FN_PANSYM_E, 

865 FN_PANSYM_F, 

866 FN_PANSYM_G, 

867 FN_PANSYM_H, 

868 FN_PANSYM_I, 

869 FN_PANSYM_J, 

870 FN_PANSYM_K, 

871 FN_PANSYM_L, 

872 FN_PANSYM_M, 

873] 

874 

875FIELDNAME_FOR_QUESTION = { 

876 # CQ.INTRO_1: # information only 

877 # CQ.INTRO_2: # information only 

878 # CQ.INTRO_DEMOGRAPHICS: # information only 

879 

880 CQ.ETHNIC: FN_ETHNIC, 

881 CQ.MARRIED: FN_MARRIED, 

882 CQ.EMPSTAT: FN_EMPSTAT, 

883 CQ.EMPTYPE: FN_EMPTYPE, 

884 CQ.HOME: FN_HOME, 

885 

886 # CQ.HEALTH_WELLBEING: # information only 

887 

888 CQ.APPETITE1_LOSS_PAST_MONTH: FN_APPETITE1, 

889 CQ.WEIGHT1_LOSS_PAST_MONTH: FN_WEIGHT1, 

890 CQ.WEIGHT2_TRYING_TO_LOSE: FN_WEIGHT2, 

891 CQ.WEIGHT3_LOST_LOTS: FN_WEIGHT3, 

892 CQ.APPETITE2_INCREASE_PAST_MONTH: FN_APPETITE2, 

893 CQ.WEIGHT4_INCREASE_PAST_MONTH: FN_WEIGHT4, 

894 # CQ.WEIGHT4A: not used (= WEIGHT4 + pregnancy option) 

895 CQ.WEIGHT5_GAINED_LOTS: FN_WEIGHT5, 

896 CQ.GP_YEAR: FN_GP_YEAR, 

897 CQ.DISABLE: FN_DISABLE, 

898 CQ.ILLNESS: FN_ILLNESS, 

899 

900 CQ.SOMATIC_MAND1_PAIN_PAST_MONTH: FN_SOMATIC_MAND1, 

901 CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: FN_SOMATIC_PAIN1, 

902 CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK: FN_SOMATIC_PAIN2, 

903 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY: FN_SOMATIC_PAIN3, 

904 CQ.SOMATIC_PAIN4_UNPLEASANT: FN_SOMATIC_PAIN4, 

905 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: FN_SOMATIC_PAIN5, 

906 CQ.SOMATIC_MAND2_DISCOMFORT: FN_SOMATIC_MAND2, 

907 CQ.SOMATIC_DIS1_PSYCHOL_EXAC: FN_SOMATIC_DIS1, 

908 CQ.SOMATIC_DIS2_DAYS_PAST_WEEK: FN_SOMATIC_DIS2, 

909 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY: FN_SOMATIC_DIS3, 

910 CQ.SOMATIC_DIS4_UNPLEASANT: FN_SOMATIC_DIS4, 

911 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: FN_SOMATIC_DIS5, 

912 CQ.SOMATIC_DUR: FN_SOMATIC_DUR, 

913 

914 CQ.FATIGUE_MAND1_TIRED_PAST_MONTH: FN_FATIGUE_MAND1, 

915 CQ.FATIGUE_CAUSE1_TIRED: FN_FATIGUE_CAUSE1, 

916 CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK: FN_FATIGUE_TIRED1, 

917 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY: FN_FATIGUE_TIRED2, 

918 CQ.FATIGUE_TIRED3_HAD_TO_PUSH: FN_FATIGUE_TIRED3, 

919 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: FN_FATIGUE_TIRED4, 

920 CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH: FN_FATIGUE_MAND2, 

921 CQ.FATIGUE_CAUSE2_LACK_ENERGY: FN_FATIGUE_CAUSE2, 

922 CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK: FN_FATIGUE_ENERGY1, 

923 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY: FN_FATIGUE_ENERGY2, 

924 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH: FN_FATIGUE_ENERGY3, 

925 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: FN_FATIGUE_ENERGY4, 

926 CQ.FATIGUE_DUR: FN_FATIGUE_DUR, 

927 

928 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH: FN_CONC_MAND1, 

929 CQ.CONC_MAND2_FORGETFUL_PAST_MONTH: FN_CONC_MAND2, 

930 CQ.CONC1_CONC_DAYS_PAST_WEEK: FN_CONC1, 

931 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION: FN_CONC2, 

932 CQ.CONC3_CONC_PREVENTED_ACTIVITIES: FN_CONC3, 

933 CQ.CONC_DUR: FN_CONC_DUR, 

934 CQ.CONC4_FORGOTTEN_IMPORTANT: FN_CONC4, 

935 CQ.FORGET_DUR: FN_FORGET_DUR, 

936 

937 CQ.SLEEP_MAND1_LOSS_PAST_MONTH: FN_SLEEP_MAND1, 

938 CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK: FN_SLEEP_LOSE1, 

939 CQ.SLEEP_LOSE2_DIS_WORST_DURATION: FN_SLEEP_LOSE2, 

940 CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK: FN_SLEEP_LOSE3, 

941 CQ.SLEEP_EMW_PAST_WEEK: FN_SLEEP_EMW, 

942 CQ.SLEEP_CAUSE: FN_SLEEP_CAUSE, 

943 CQ.SLEEP_MAND2_GAIN_PAST_MONTH: FN_SLEEP_MAND2, 

944 CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK: FN_SLEEP_GAIN1, 

945 CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: FN_SLEEP_GAIN2, 

946 CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK: FN_SLEEP_GAIN3, 

947 CQ.SLEEP_DUR: FN_SLEEP_DUR, 

948 

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 

957 CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH: FN_HYPO_MAND1, 

958 CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS: FN_HYPO_MAND2, 

959 CQ.HYPO1_DAYS_PAST_WEEK: FN_HYPO1, 

960 CQ.HYPO2_WORRY_TOO_MUCH: FN_HYPO2, 

961 CQ.HYPO3_HOW_UNPLEASANT: FN_HYPO3, 

962 CQ.HYPO4_CAN_DISTRACT: FN_HYPO4, 

963 CQ.HYPO_DUR: FN_HYPO_DUR, 

964 

965 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH: FN_DEPR_MAND1, 

966 CQ.DEPR1_LOW_MOOD_PAST_WEEK: FN_DEPR1, 

967 CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: FN_DEPR_MAND2, 

968 CQ.DEPR2_ENJOYMENT_PAST_WEEK: FN_DEPR2, 

969 CQ.DEPR3_DAYS_PAST_WEEK: FN_DEPR3, 

970 CQ.DEPR4_GT_3H_ANY_DAY: FN_DEPR4, 

971 CQ.DEPR_CONTENT: FN_DEPR_CONTENT, 

972 CQ.DEPR5_COULD_CHEER_UP: FN_DEPR5, 

973 CQ.DEPR_DUR: FN_DEPR_DUR, 

974 CQ.DEPTH1_DIURNAL_VARIATION: FN_DEPTH1, 

975 CQ.DEPTH2_LIBIDO: FN_DEPTH2, 

976 CQ.DEPTH3_RESTLESS: FN_DEPTH3, 

977 CQ.DEPTH4_SLOWED: FN_DEPTH4, 

978 CQ.DEPTH5_GUILT: FN_DEPTH5, 

979 CQ.DEPTH6_WORSE_THAN_OTHERS: FN_DEPTH6, 

980 CQ.DEPTH7_HOPELESS: FN_DEPTH7, 

981 CQ.DEPTH8_LNWL: FN_DEPTH8, 

982 CQ.DEPTH9_SUICIDE_THOUGHTS: FN_DEPTH9, 

983 CQ.DEPTH10_SUICIDE_METHOD: FN_DEPTH10, 

984 CQ.DOCTOR: FN_DOCTOR, 

985 # CQ.DOCTOR2_PLEASE_TALK_TO: # info only 

986 # CQ.DEPR_OUTRO: # info only 

987 

988 CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH: FN_WORRY_MAND1, 

989 CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH: FN_WORRY_MAND2, 

990 CQ.WORRY_CONT1: FN_WORRY_CONT1, 

991 # CQ.WORRY1_INFO_ONLY: # info only 

992 CQ.WORRY2_DAYS_PAST_WEEK: FN_WORRY2, 

993 CQ.WORRY3_TOO_MUCH: FN_WORRY3, 

994 CQ.WORRY4_HOW_UNPLEASANT: FN_WORRY4, 

995 CQ.WORRY5_GT_3H_ANY_DAY: FN_WORRY5, 

996 CQ.WORRY_DUR: FN_WORRY_DUR, 

997 

998 CQ.ANX_MAND1_ANXIETY_PAST_MONTH: FN_ANX_MAND1, 

999 CQ.ANX_MAND2_TENSION_PAST_MONTH: FN_ANX_MAND2, 

1000 CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH: FN_ANX_PHOBIA1, 

1001 CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: FN_ANX_PHOBIA2, 

1002 # CQ.ANX1_INFO_ONLY: # info only 

1003 CQ.ANX2_GENERAL_DAYS_PAST_WEEK: FN_ANX2, 

1004 CQ.ANX3_GENERAL_HOW_UNPLEASANT: FN_ANX3, 

1005 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS: FN_ANX4, 

1006 CQ.ANX5_GENERAL_GT_3H_ANY_DAY: FN_ANX5, 

1007 CQ.ANX_DUR_GENERAL: FN_ANX_DUR, 

1008 

1009 CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH: FN_PHOBIAS_MAND, 

1010 CQ.PHOBIAS_TYPE1: FN_PHOBIAS_TYPE1, 

1011 CQ.PHOBIAS1_DAYS_PAST_WEEK: FN_PHOBIAS1, 

1012 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS: FN_PHOBIAS2, 

1013 CQ.PHOBIAS3_AVOIDANCE: FN_PHOBIAS3, 

1014 CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: FN_PHOBIAS4, 

1015 CQ.PHOBIAS_DUR: FN_PHOBIAS_DUR, 

1016 

1017 CQ.PANIC_MAND_PAST_MONTH: FN_PANIC_MAND, 

1018 CQ.PANIC1_NUM_PAST_WEEK: FN_PANIC1, 

1019 CQ.PANIC2_HOW_UNPLEASANT: FN_PANIC2, 

1020 CQ.PANIC3_PANIC_GE_10_MIN: FN_PANIC3, 

1021 CQ.PANIC4_RAPID_ONSET: FN_PANIC4, 

1022 # CQ.PANSYM: # multiple stems 

1023 CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER: FN_PANIC5, 

1024 CQ.PANIC_DUR: FN_PANIC_DUR, 

1025 

1026 # CQ.ANX_OUTRO: # info only 

1027 

1028 CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH: FN_COMP_MAND1, 

1029 CQ.COMP1_DAYS_PAST_WEEK: FN_COMP1, 

1030 CQ.COMP2_TRIED_TO_STOP: FN_COMP2, 

1031 CQ.COMP3_UPSETTING: FN_COMP3, 

1032 CQ.COMP4_MAX_N_REPETITIONS: FN_COMP4, 

1033 CQ.COMP_DUR: FN_COMP_DUR, 

1034 

1035 CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH: FN_OBSESS_MAND1, 

1036 CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: FN_OBSESS_MAND2, 

1037 CQ.OBSESS1_DAYS_PAST_WEEK: FN_OBSESS1, 

1038 CQ.OBSESS2_TRIED_TO_STOP: FN_OBSESS2, 

1039 CQ.OBSESS3_UPSETTING: FN_OBSESS3, 

1040 CQ.OBSESS4_MAX_DURATION: FN_OBSESS4, 

1041 CQ.OBSESS_DUR: FN_OBSESS_DUR, 

1042 

1043 # CQ.OVERALL1: # info only 

1044 CQ.OVERALL2_IMPACT_PAST_WEEK: FN_OVERALL2, 

1045} 

1046 

1047# Questions for which 1 = no, 2 = yes (+/- other options) 

1048QUESTIONS_1_NO_2_YES = [ 

1049 CQ.APPETITE1_LOSS_PAST_MONTH, 

1050 CQ.WEIGHT1_LOSS_PAST_MONTH, 

1051 CQ.WEIGHT2_TRYING_TO_LOSE, 

1052 CQ.APPETITE2_INCREASE_PAST_MONTH, 

1053 CQ.WEIGHT4_INCREASE_PAST_MONTH, # may also offer "yes but pregnant" # noqa 

1054 CQ.SOMATIC_MAND1_PAIN_PAST_MONTH, 

1055 CQ.SOMATIC_MAND2_DISCOMFORT, 

1056 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY, 

1057 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING, # also has other options # noqa 

1058 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY, 

1059 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING, # also has other options # noqa 

1060 CQ.FATIGUE_MAND1_TIRED_PAST_MONTH, 

1061 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY, 

1062 CQ.FATIGUE_TIRED3_HAD_TO_PUSH, 

1063 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE, # also has other options 

1064 CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH, 

1065 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY, 

1066 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH, 

1067 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE, # also has other options 

1068 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH, 

1069 CQ.CONC_MAND2_FORGETFUL_PAST_MONTH, 

1070 CQ.CONC3_CONC_PREVENTED_ACTIVITIES, 

1071 CQ.CONC4_FORGOTTEN_IMPORTANT, 

1072 CQ.SLEEP_MAND1_LOSS_PAST_MONTH, 

1073 CQ.SLEEP_EMW_PAST_WEEK, 

1074 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH, 

1075 CQ.IRRIT2_GT_1H_ANY_DAY, 

1076 CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH, 

1077 CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS, 

1078 CQ.HYPO2_WORRY_TOO_MUCH, 

1079 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH, 

1080 CQ.DEPR1_LOW_MOOD_PAST_WEEK, 

1081 CQ.DEPR4_GT_3H_ANY_DAY, 

1082 CQ.DEPTH3_RESTLESS, 

1083 CQ.DEPTH4_SLOWED, 

1084 CQ.DEPTH6_WORSE_THAN_OTHERS, 

1085 CQ.DEPTH7_HOPELESS, 

1086 CQ.DEPTH10_SUICIDE_METHOD, 

1087 CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH, 

1088 CQ.WORRY3_TOO_MUCH, 

1089 CQ.WORRY5_GT_3H_ANY_DAY, 

1090 CQ.ANX_MAND1_ANXIETY_PAST_MONTH, 

1091 CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH, 

1092 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS, 

1093 CQ.ANX5_GENERAL_GT_3H_ANY_DAY, 

1094 CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH, 

1095 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS, 

1096 CQ.PHOBIAS3_AVOIDANCE, 

1097 CQ.PANIC4_RAPID_ONSET, 

1098 CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER, 

1099 CQ.COMP2_TRIED_TO_STOP, 

1100 CQ.COMP3_UPSETTING, 

1101 CQ.OBSESS2_TRIED_TO_STOP, 

1102 CQ.OBSESS3_UPSETTING, 

1103] 

1104# Questions for which 1 = yes, 2 = no (+/- other options) 

1105QUESTIONS_1_YES_2_NO = [ 

1106 CQ.DISABLE, 

1107 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION, 

1108 CQ.HYPO4_CAN_DISTRACT, 

1109] 

1110# Yes-no (or no-yes) questions but with specific text 

1111QUESTIONS_YN_SPECIFIC_TEXT = [ 

1112 CQ.WEIGHT2_TRYING_TO_LOSE, 

1113 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY, 

1114 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY, 

1115 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY, 

1116 CQ.FATIGUE_TIRED3_HAD_TO_PUSH, 

1117 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY, 

1118 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH, 

1119 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH, 

1120 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION, 

1121 CQ.CONC4_FORGOTTEN_IMPORTANT, 

1122 CQ.SLEEP_EMW_PAST_WEEK, 

1123 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH, 

1124 CQ.IRRIT2_GT_1H_ANY_DAY, 

1125 CQ.HYPO2_WORRY_TOO_MUCH, 

1126 CQ.HYPO4_CAN_DISTRACT, 

1127 CQ.DEPR1_LOW_MOOD_PAST_WEEK, 

1128 CQ.DEPR4_GT_3H_ANY_DAY, 

1129 CQ.DEPTH6_WORSE_THAN_OTHERS, 

1130 CQ.DEPTH7_HOPELESS, 

1131 CQ.WORRY3_TOO_MUCH, 

1132 CQ.WORRY5_GT_3H_ANY_DAY, 

1133 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS, 

1134 CQ.ANX5_GENERAL_GT_3H_ANY_DAY, 

1135 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS, 

1136 CQ.PHOBIAS3_AVOIDANCE, 

1137 CQ.COMP2_TRIED_TO_STOP, 

1138 CQ.COMP3_UPSETTING, 

1139 CQ.OBSESS2_TRIED_TO_STOP, 

1140 CQ.OBSESS3_UPSETTING, 

1141] 

1142# Demographics questions (optional for diagnosis) 

1143QUESTIONS_DEMOGRAPHICS = [ 

1144 CQ.ETHNIC, 

1145 CQ.MARRIED, 

1146 CQ.EMPSTAT, 

1147 CQ.EMPTYPE, 

1148 CQ.HOME, 

1149] 

1150# "Questions" that are just a prompt screen 

1151QUESTIONS_PROMPT_ONLY = { 

1152 # Maps questions to their prompt's xstring name 

1153 CQ.INTRO_1: "intro_1", 

1154 CQ.INTRO_2: "intro_2", 

1155 CQ.INTRO_DEMOGRAPHICS: "intro_demographics_statement", 

1156 

1157 CQ.HEALTH_WELLBEING: "health_wellbeing_statement", 

1158 CQ.DOCTOR2_PLEASE_TALK_TO: "doctor2", 

1159 CQ.DEPR_OUTRO: "depr_outro", 

1160 CQ.WORRY1_INFO_ONLY: "worry1", 

1161 CQ.ANX1_INFO_ONLY: "anx1", 

1162 CQ.ANX_OUTRO: "anx_outro", 

1163 CQ.OVERALL1_INFO_ONLY: "overall1", 

1164 CQ.THANKS_FINISHED: "end", 

1165} 

1166# "How many days per week" questions 

1167# "Overall duration" questions 

1168QUESTIONS_OVERALL_DURATION = [ 

1169 CQ.SOMATIC_DUR, 

1170 CQ.FATIGUE_DUR, 

1171 CQ.CONC_DUR, 

1172 CQ.FORGET_DUR, 

1173 CQ.SLEEP_DUR, 

1174 CQ.IRRIT_DUR, 

1175 CQ.HYPO_DUR, 

1176 CQ.DEPR_DUR, 

1177 CQ.WORRY_DUR, 

1178 CQ.ANX_DUR_GENERAL, 

1179 CQ.PHOBIAS_DUR, 

1180 CQ.PANIC_DUR, 

1181 CQ.COMP_DUR, 

1182 CQ.OBSESS_DUR, 

1183] 

1184# Multi-way questions, other than yes/no ones. 

1185QUESTIONS_MULTIWAY = { 

1186 # Maps questions to first and last number of answers. 

1187 CQ.WEIGHT3_LOST_LOTS: (1, 2), 

1188 CQ.WEIGHT4_INCREASE_PAST_MONTH: (1, 2), # may be modified to 3 if female 

1189 CQ.WEIGHT5_GAINED_LOTS: (1, 2), 

1190 CQ.GP_YEAR: (0, 4), # unusual; starts at 0 

1191 CQ.ILLNESS: (1, 8), 

1192 CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: (1, 3), 

1193 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: (1, 3), 

1194 CQ.SOMATIC_DIS1_PSYCHOL_EXAC: (1, 3), 

1195 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: (1, 3), 

1196 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: (1, 3), 

1197 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: (1, 3), 

1198 CQ.SLEEP_LOSE2_DIS_WORST_DURATION: (1, 4), 

1199 CQ.SLEEP_CAUSE: (1, 6), 

1200 CQ.SLEEP_MAND2_GAIN_PAST_MONTH: (1, 3), 

1201 CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: (1, 4), 

1202 CQ.IRRIT_MAND2_THINGS_PAST_MONTH: (1, 3), 

1203 CQ.IRRIT3_WANTED_TO_SHOUT: (1, 3), 

1204 CQ.IRRIT4_ARGUMENTS: (1, 3), 

1205 CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: (1, 3), 

1206 CQ.DEPR2_ENJOYMENT_PAST_WEEK: (1, 3), 

1207 CQ.DEPR5_COULD_CHEER_UP: (1, 3), 

1208 CQ.DEPTH1_DIURNAL_VARIATION: (1, 4), 

1209 CQ.DEPTH2_LIBIDO: (1, 4), 

1210 CQ.DEPTH5_GUILT: (1, 4), 

1211 CQ.DEPTH8_LNWL: (1, 3), 

1212 CQ.DEPTH9_SUICIDE_THOUGHTS: (1, 3), 

1213 CQ.DOCTOR: (1, 3), 

1214 CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: (1, 2), 

1215 CQ.PHOBIAS_TYPE1: (1, 9), 

1216 CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: (1, 3), 

1217 CQ.PANIC_MAND_PAST_MONTH: (1, 3), 

1218 CQ.PANIC1_NUM_PAST_WEEK: (1, 3), 

1219 CQ.PANIC2_HOW_UNPLEASANT: (1, 3), 

1220 CQ.PANIC3_PANIC_GE_10_MIN: (1, 2), 

1221 CQ.COMP4_MAX_N_REPETITIONS: (1, 3), 

1222 CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: (1, 2), 

1223 CQ.OBSESS4_MAX_DURATION: (1, 2), 

1224 CQ.OVERALL2_IMPACT_PAST_WEEK: (1, 4), 

1225} 

1226QUESTIONS_MULTIWAY_WITH_EXTRA_STEM = { 

1227 # Maps questions to first and last number of answers. 

1228 CQ.ETHNIC: (1, 7), # 7 includes our additional "prefer not to say" 

1229 CQ.MARRIED: (1, 6), # 6 includes our additional "prefer not to say" 

1230 CQ.EMPSTAT: (1, 8), # 8 includes our additional "prefer not to say" 

1231 CQ.EMPTYPE: (1, 7), # 7 includes our additional "not applicable" + "prefer not to say" # noqa 

1232 CQ.HOME: (1, 7), # 7 includes our additional "prefer not to say" 

1233} 

1234QUESTIONS_DAYS_PER_WEEK = [ 

1235 CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK, 

1236 CQ.SOMATIC_DIS2_DAYS_PAST_WEEK, 

1237 CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK, 

1238 CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK, 

1239 CQ.CONC1_CONC_DAYS_PAST_WEEK, 

1240 CQ.IRRIT1_DAYS_PER_WEEK, 

1241 CQ.HYPO1_DAYS_PAST_WEEK, 

1242 CQ.DEPR3_DAYS_PAST_WEEK, 

1243 CQ.WORRY2_DAYS_PAST_WEEK, 

1244 CQ.ANX2_GENERAL_DAYS_PAST_WEEK, 

1245 CQ.PHOBIAS1_DAYS_PAST_WEEK, 

1246 # not this: CQ.PHOBIAS4_AVOIDANCE_FREQUENCY -- different phrasing 

1247 # not this: CQ.PANIC1_FREQUENCY 

1248 CQ.COMP1_DAYS_PAST_WEEK, 

1249 CQ.OBSESS1_DAYS_PAST_WEEK, 

1250] 

1251QUESTIONS_NIGHTS_PER_WEEK = [ 

1252 CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK, 

1253 CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK, 

1254 CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK, # (*) see below 

1255 # (*) Probably an error in the original: 

1256 # "On how many nights in the PAST SEVEN NIGHTS did you have problems 

1257 # with your sleep? (1) None. (2) Between one and three days. (3) Four 

1258 # days or more." Note day/night confusion. Altered to "nights". 

1259 CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK, 

1260] 

1261QUESTIONS_HOW_UNPLEASANT_STANDARD = [ 

1262 CQ.SOMATIC_PAIN4_UNPLEASANT, 

1263 CQ.SOMATIC_DIS4_UNPLEASANT, 

1264 CQ.HYPO3_HOW_UNPLEASANT, 

1265 CQ.WORRY4_HOW_UNPLEASANT, 

1266 CQ.ANX3_GENERAL_HOW_UNPLEASANT, 

1267] 

1268QUESTIONS_FATIGUE_CAUSES = [ 

1269 CQ.FATIGUE_CAUSE1_TIRED, 

1270 CQ.FATIGUE_CAUSE2_LACK_ENERGY, 

1271] 

1272QUESTIONS_STRESSORS = [ 

1273 CQ.DEPR_CONTENT, 

1274 CQ.WORRY_CONT1, 

1275] 

1276QUESTIONS_NO_SOMETIMES_OFTEN = [ 

1277 CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH, 

1278 CQ.ANX_MAND2_TENSION_PAST_MONTH, 

1279 CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH, 

1280 CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH, 

1281 # and no-sometimes-often values also used by: 

1282 # CQ.PANIC_MAND_PAST_MONTH 

1283 # ... but with variations on the text. 

1284] 

1285 

1286 

1287# ============================================================================= 

1288# Ancillary functions 

1289# ============================================================================= 

1290 

1291def fieldname_for_q(q: CisrQuestion) -> str: 

1292 return FIELDNAME_FOR_QUESTION.get(q, "") 

1293 

1294 

1295def enum_to_int(qe: CisrQuestion) -> int: 

1296 return qe.value 

1297 

1298 

1299def int_to_enum(qi: int) -> CisrQuestion: 

1300 # https://stackoverflow.com/questions/23951641/how-to-convert-int-to-enum-in-python # noqa 

1301 return CisrQuestion(qi) 

1302 

1303 

1304# ============================================================================= 

1305# CisrResult 

1306# ============================================================================= 

1307 

1308class CisrResult(object): 

1309 def __init__(self, record_decisions: bool = False) -> None: 

1310 self.incomplete = False 

1311 self.record_decisions = record_decisions 

1312 self.decisions = [] # type: List[str] 

1313 

1314 # Symptom scoring 

1315 self.depression = 0 # DEPR in original 

1316 self.depr_crit_1_mood_anhedonia_energy = 0 # DEPCRIT1 

1317 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui = 0 # DEPCRIT2 

1318 self.depr_crit_3_somatic_synd = 0 # DEPCRIT3 

1319 # ... looks to me like the ICD-10 criteria for somatic syndrome 

1320 # (e.g. F32.01, F32.11, F33.01, F33.11), with the "do you cheer up 

1321 # when..." question (DEPR5) being the one for "lack of emotional 

1322 # reactions to events or activities that normally produce an 

1323 # emotional response". 

1324 self.weight_change = WTCHANGE_NONE_OR_APPETITE_INCREASE # WTCHANGE IN original # noqa 

1325 self.somatic_symptoms = 0 # SOMATIC in original 

1326 self.fatigue = 0 # FATIGUE in original 

1327 self.neurasthenia = 0 # NEURAS in original 

1328 self.concentration_poor = 0 # CONC in original 

1329 self.sleep_problems = 0 # SLEEP in original 

1330 self.sleep_change = SLEEPCHANGE_NONE # SLEEPCH in original 

1331 self.depressive_thoughts = 0 # DEPTHTS in original 

1332 self.irritability = 0 # IRRIT in original 

1333 self.diurnal_mood_variation = DIURNAL_MOOD_VAR_NONE # DVM in original 

1334 self.libido_decreased = False # LIBID in original 

1335 self.psychomotor_changes = PSYCHOMOTOR_NONE # PSYCHMOT in original 

1336 self.suicidality = SUICIDE_INTENT_NONE # type: int # SUICID in original # noqa 

1337 self.depression_at_least_2_weeks = False # DEPR_DUR >= 2 in original 

1338 

1339 self.hypochondria = 0 # HYPO in original 

1340 self.worry = 0 # WORRY in original 

1341 self.anxiety = 0 # ANX in original 

1342 self.anxiety_physical_symptoms = False # AN4 == 2 in original 

1343 self.anxiety_at_least_2_weeks = False # ANX_DUR >= 2 in original 

1344 self.phobias_flag = False # PHOBIAS_FLAG in original 

1345 self.phobias_score = 0 # PHOBIAS in original 

1346 self.phobias_type = 0 # PHOBIAS_TYPE in original 

1347 self.phobic_avoidance = False # PHOBIAS3 == 2 in original 

1348 self.panic = 0 # PANIC in original 

1349 self.panic_rapid_onset = False # PANIC4 == 2 in original 

1350 self.panic_symptoms_total = 0 # PANSYTOT in original 

1351 

1352 self.compulsions = 0 # COMP in original 

1353 self.compulsions_tried_to_stop = False # COMP2 == 2 in original 

1354 self.compulsions_at_least_2_weeks = False # COMP_DUR >= 2 in original 

1355 self.obsessions = 0 # OBSESS in original 

1356 self.obsessions_tried_to_stop = False # OBSESS2 == 2 in original 

1357 self.obsessions_at_least_2_weeks = False # OBSESS_DUR >= 2 in original 

1358 

1359 self.functional_impairment = 0 # IMPAIR in original 

1360 

1361 # Disorder flags 

1362 self.obsessive_compulsive_disorder = False # OBCOMP in original 

1363 self.depression_mild = False # DEPRMILD in original 

1364 self.depression_moderate = False # DEPRMOD in original 

1365 self.depression_severe = False # DEPRSEV in original 

1366 self.chronic_fatigue_syndrome = False # CFS in original 

1367 self.generalized_anxiety_disorder = False # GAD in original 

1368 self.phobia_agoraphobia = False # PHOBAG in original 

1369 self.phobia_social = False # PHOBSOC in original 

1370 self.phobia_specific = False # PHOBSPEC in original 

1371 self.panic_disorder = False # PANICD in original 

1372 

1373 # Final diagnoses 

1374 self.diagnosis_1 = DIAG_0_NO_DIAGNOSIS # DIAG1 in original 

1375 self.diagnosis_2 = DIAG_0_NO_DIAGNOSIS # DIAG2 in original 

1376 

1377 # ------------------------------------------------------------------------- 

1378 # Overall scoring 

1379 # ------------------------------------------------------------------------- 

1380 

1381 def get_score(self) -> int: # SCORE in original 

1382 return ( 

1383 self.somatic_symptoms + 

1384 self.fatigue + 

1385 self.concentration_poor + 

1386 self.sleep_problems + 

1387 self.irritability + 

1388 self.hypochondria + 

1389 self.depression + 

1390 self.depressive_thoughts + 

1391 self.worry + 

1392 self.anxiety + 

1393 self.phobias_score + 

1394 self.panic + 

1395 self.compulsions + 

1396 self.obsessions 

1397 ) 

1398 

1399 def needs_impairment_question(self) -> bool: 

1400 # code in OVERALL1 in original 

1401 threshold = 2 # for all symptoms 

1402 return ( 

1403 self.somatic_symptoms >= threshold or 

1404 self.hypochondria >= threshold or 

1405 self.fatigue >= threshold or 

1406 self.sleep_problems >= threshold or 

1407 self.irritability >= threshold or 

1408 self.concentration_poor >= threshold or 

1409 self.depression >= threshold or 

1410 self.depressive_thoughts >= threshold or 

1411 self.phobias_score >= threshold or 

1412 self.worry >= threshold or 

1413 self.anxiety >= threshold or 

1414 self.panic >= threshold or 

1415 self.compulsions >= threshold or 

1416 self.obsessions >= threshold 

1417 ) 

1418 

1419 def has_somatic_syndrome(self) -> bool: 

1420 return self.depr_crit_3_somatic_synd >= SOMATIC_SYNDROME_CRITERION 

1421 

1422 def get_final_page(self) -> CisrQuestion: 

1423 # see chooseFinalPage() in the C++ version 

1424 return ( 

1425 CQ.OVERALL1_INFO_ONLY if self.needs_impairment_question() 

1426 else CQ.THANKS_FINISHED 

1427 ) 

1428 

1429 def decide(self, decision: str) -> None: 

1430 if self.record_decisions: 

1431 self.decisions.append(decision) 

1432 

1433 def _showint(self, name: str, value: int) -> None: 

1434 self.decide(f"{SCORE_PREFIX}{name}: {value}") 

1435 

1436 def _showbool(self, name: str, value: bool) -> None: 

1437 self.decide(f"{SCORE_PREFIX}{name}: {'true' if value else 'false'}") 

1438 

1439 def diagnosis_name(self, diagnosis_code: int) -> str: 

1440 if self.incomplete: 

1441 # Do NOT offer diagnostic information based on partial data. 

1442 # Might be dangerous (e.g. say "mild depressive episode" when it's 

1443 # severe + incomplete information). 

1444 return "INFORMATION INCOMPLETE" 

1445 

1446 if diagnosis_code == DIAG_0_NO_DIAGNOSIS: 

1447 return "No diagnosis identified" 

1448 elif diagnosis_code == DIAG_1_MIXED_ANX_DEPR_DIS_MILD: 

1449 return "Mixed anxiety and depressive disorder (mild)" 

1450 elif diagnosis_code == DIAG_2_GENERALIZED_ANX_DIS_MILD: 

1451 return "Generalized anxiety disorder (mild)" 

1452 elif diagnosis_code == DIAG_3_OBSESSIVE_COMPULSIVE_DIS: 

1453 return "Obsessive–compulsive disorder" 

1454 elif diagnosis_code == DIAG_4_MIXED_ANX_DEPR_DIS: 

1455 return "Mixed anxiety and depressive disorder" 

1456 elif diagnosis_code == DIAG_5_SPECIFIC_PHOBIA: 

1457 return "Specific (isolated) phobia" 

1458 elif diagnosis_code == DIAG_6_SOCIAL_PHOBIA: 

1459 return "Social phobia" 

1460 elif diagnosis_code == DIAG_7_AGORAPHOBIA: 

1461 return "Agoraphobia" 

1462 elif diagnosis_code == DIAG_8_GENERALIZED_ANX_DIS: 

1463 return "Generalized anxiety disorder" 

1464 elif diagnosis_code == DIAG_9_PANIC_DIS: 

1465 return "Panic disorder" 

1466 elif diagnosis_code == DIAG_10_MILD_DEPR_EPISODE: 

1467 return "Mild depressive episode" 

1468 elif diagnosis_code == DIAG_11_MOD_DEPR_EPISODE: 

1469 return "Moderate depressive episode" 

1470 elif diagnosis_code == DIAG_12_SEVERE_DEPR_EPISODE: 

1471 return "Severe depressive episode" 

1472 else: 

1473 return "[INTERNAL ERROR: BAD DIAGNOSIS CODE]" 

1474 

1475 def diagnosis_icd10_code(self, diagnosis_code: int) -> str: 

1476 if self.incomplete: 

1477 return "" 

1478 

1479 if diagnosis_code == DIAG_0_NO_DIAGNOSIS: 

1480 return "" 

1481 elif diagnosis_code == DIAG_1_MIXED_ANX_DEPR_DIS_MILD: 

1482 return "F41.2" # no sub-code for "mild" 

1483 elif diagnosis_code == DIAG_2_GENERALIZED_ANX_DIS_MILD: 

1484 return "F41.1" # no sub-code for "mild" 

1485 elif diagnosis_code == DIAG_3_OBSESSIVE_COMPULSIVE_DIS: 

1486 return "Obsessive–compulsive disorder" 

1487 elif diagnosis_code == DIAG_4_MIXED_ANX_DEPR_DIS: 

1488 return "F41.2" 

1489 elif diagnosis_code == DIAG_5_SPECIFIC_PHOBIA: 

1490 return "F40.2" 

1491 elif diagnosis_code == DIAG_6_SOCIAL_PHOBIA: 

1492 return "F40.1" 

1493 elif diagnosis_code == DIAG_7_AGORAPHOBIA: 

1494 return "F40.0" # not clear whether F40.00/F40.01 are distinguished 

1495 elif diagnosis_code == DIAG_8_GENERALIZED_ANX_DIS: 

1496 return "F41.1" 

1497 elif diagnosis_code == DIAG_9_PANIC_DIS: 

1498 return "F41.0" 

1499 elif diagnosis_code == DIAG_10_MILD_DEPR_EPISODE: 

1500 if self.has_somatic_syndrome(): 

1501 return "F32.01" 

1502 else: 

1503 return "F32.00" 

1504 elif diagnosis_code == DIAG_11_MOD_DEPR_EPISODE: 

1505 if self.has_somatic_syndrome(): 

1506 return "F32.11" 

1507 else: 

1508 return "F32.10" 

1509 elif diagnosis_code == DIAG_12_SEVERE_DEPR_EPISODE: 

1510 return "F32.2 or F32.3" 

1511 else: 

1512 return "[INTERNAL ERROR: BAD DIAGNOSIS CODE]" 

1513 

1514 def has_diagnosis(self, diagnosis_code: int) -> bool: 

1515 return not self.incomplete and diagnosis_code != DIAG_0_NO_DIAGNOSIS 

1516 

1517 def has_diagnosis_1(self) -> bool: 

1518 return self.has_diagnosis(self.diagnosis_1) 

1519 

1520 def has_diagnosis_2(self) -> bool: 

1521 return self.has_diagnosis(self.diagnosis_1) 

1522 

1523 def diagnosis_1_name(self) -> str: 

1524 return self.diagnosis_name(self.diagnosis_1) 

1525 

1526 def diagnosis_1_icd10_code(self) -> str: 

1527 return self.diagnosis_icd10_code(self.diagnosis_1) 

1528 

1529 def diagnosis_2_name(self) -> str: 

1530 return self.diagnosis_name(self.diagnosis_2) 

1531 

1532 def diagnosis_2_icd10_code(self) -> str: 

1533 return self.diagnosis_icd10_code(self.diagnosis_2) 

1534 

1535 def finalize(self) -> None: 

1536 at_least_1_activity_impaired = (self.functional_impairment >= 

1537 OVERALL_IMPAIRMENT_STOP_1_ACTIVITY) 

1538 score = self.get_score() 

1539 

1540 # GAD 

1541 if (self.anxiety >= 2 and 

1542 self.anxiety_physical_symptoms and 

1543 self.anxiety_at_least_2_weeks): 

1544 self.decide( 

1545 "Anxiety score >= 2 AND physical symptoms of anxiety AND " 

1546 "anxiety for at least 2 weeks. " 

1547 "Setting generalized_anxiety_disorder.") 

1548 self.generalized_anxiety_disorder = True 

1549 

1550 # Panic 

1551 if self.panic >= 3 and self.panic_rapid_onset: 

1552 self.decide("Panic score >= 3 AND panic_rapid_onset. " 

1553 "Setting panic_disorder.") 

1554 self.panic_disorder = True 

1555 

1556 # Phobias 

1557 if (self.phobias_type == PHOBIATYPES_AGORAPHOBIA and 

1558 self.phobic_avoidance and 

1559 self.phobias_score >= 2): 

1560 self.decide("Phobia type is agoraphobia AND phobic avoidance AND" 

1561 "phobia score >= 2. Setting phobia_agoraphobia.") 

1562 self.phobia_agoraphobia = True 

1563 if (self.phobias_type == PHOBIATYPES_SOCIAL and 

1564 self.phobic_avoidance and 

1565 self.phobias_score >= 2): 

1566 self.decide("Phobia type is social AND phobic avoidance AND" 

1567 "phobia score >= 2. Setting phobia_social.") 

1568 self.phobia_social = True 

1569 if (self.phobias_type == PHOBIATYPES_SOCIAL and 

1570 self.phobic_avoidance and 

1571 self.phobias_score >= 2): 

1572 self.decide( 

1573 "Phobia type is (animals/enclosed/heights OR other) AND " 

1574 "phobic avoidance AND phobia score >= 2. " 

1575 "Setting phobia_specific.") 

1576 self.phobia_specific = True 

1577 

1578 # OCD 

1579 if (self.obsessions + self.compulsions >= 6 and 

1580 self.obsessions_tried_to_stop and 

1581 self.obsessions_at_least_2_weeks and 

1582 at_least_1_activity_impaired): 

1583 self.decide("obsessions + compulsions >= 6 AND " 

1584 "tried to stop obsessions AND " 

1585 "obsessions for at least 2 weeks AND " 

1586 "at least 1 activity impaired. " 

1587 "Setting obsessive_compulsive_disorder.") 

1588 self.obsessive_compulsive_disorder = True 

1589 if (self.obsessions + self.compulsions >= 6 and 

1590 self.compulsions_tried_to_stop and 

1591 self.compulsions_at_least_2_weeks and 

1592 at_least_1_activity_impaired): 

1593 self.decide("obsessions + compulsions >= 6 AND " 

1594 "tried to stop compulsions AND " 

1595 "compulsions for at least 2 weeks AND " 

1596 "at least 1 activity impaired. " 

1597 "Setting obsessive_compulsive_disorder.") 

1598 self.obsessive_compulsive_disorder = True 

1599 if (self.obsessions == 4 and 

1600 self.obsessions_tried_to_stop and 

1601 self.obsessions_at_least_2_weeks and 

1602 at_least_1_activity_impaired): 

1603 # NOTE: 4 is the maximum for obsessions 

1604 self.decide("obsessions == 4 AND " 

1605 "tried to stop obsessions AND " 

1606 "obsessions for at least 2 weeks AND " 

1607 "at least 1 activity impaired. " 

1608 "Setting obsessive_compulsive_disorder.") 

1609 self.obsessive_compulsive_disorder = True 

1610 if (self.compulsions == 4 and 

1611 self.compulsions_tried_to_stop and 

1612 self.compulsions_at_least_2_weeks and 

1613 at_least_1_activity_impaired): 

1614 # NOTE: 4 is the maximum for compulsions 

1615 self.decide("compulsions == 4 AND " 

1616 "tried to stop compulsions AND " 

1617 "compulsions for at least 2 weeks AND " 

1618 "at least 1 activity impaired. " 

1619 "Setting obsessive_compulsive_disorder.") 

1620 self.obsessive_compulsive_disorder = True 

1621 

1622 # Depression 

1623 if (self.depression_at_least_2_weeks and 

1624 self.depr_crit_1_mood_anhedonia_energy > 1 and 

1625 self.depr_crit_1_mood_anhedonia_energy + 

1626 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 3): 

1627 self.decide("Depressive symptoms >=2 weeks AND " 

1628 "depr_crit_1_mood_anhedonia_energy > 1 AND " 

1629 "depr_crit_1_mood_anhedonia_energy + " 

1630 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 3. " 

1631 "Setting depression_mild.") 

1632 self.depression_mild = True 

1633 if (self.depression_at_least_2_weeks and 

1634 self.depr_crit_1_mood_anhedonia_energy > 1 and 

1635 (self.depr_crit_1_mood_anhedonia_energy + 

1636 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui) > 5): 

1637 self.decide("Depressive symptoms >=2 weeks AND " 

1638 "depr_crit_1_mood_anhedonia_energy > 1 AND " 

1639 "depr_crit_1_mood_anhedonia_energy + " 

1640 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 5. " 

1641 "Setting depression_moderate.") 

1642 self.depression_moderate = True 

1643 if (self.depression_at_least_2_weeks and 

1644 self.depr_crit_1_mood_anhedonia_energy == 3 and 

1645 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 4): 

1646 self.decide("Depressive symptoms >=2 weeks AND " 

1647 "depr_crit_1_mood_anhedonia_energy == 3 AND " 

1648 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 4. " 

1649 "Setting depression_severe.") 

1650 self.depression_severe = True 

1651 

1652 # CFS 

1653 if self.neurasthenia >= 2: 

1654 # The original had a pointless check for "DIAG1 == 0" too, but that 

1655 # was always true. 

1656 self.decide("neurasthenia >= 2. Setting chronic_fatigue_syndrome.") 

1657 self.chronic_fatigue_syndrome = True 

1658 

1659 # Final diagnostic hierarchy 

1660 

1661 # ... primary diagnosis 

1662 if score >= 12: 

1663 self.decide("Total score >= 12. Setting diagnosis_1 to " 

1664 "DIAG_1_MIXED_ANX_DEPR_DIS_MILD.") 

1665 self.diagnosis_1 = DIAG_1_MIXED_ANX_DEPR_DIS_MILD 

1666 if self.generalized_anxiety_disorder: 

1667 self.decide("generalized_anxiety_disorder is true. Setting " 

1668 "diagnosis_1 to DIAG_2_GENERALIZED_ANX_DIS_MILD.") 

1669 self.diagnosis_1 = DIAG_2_GENERALIZED_ANX_DIS_MILD 

1670 if self.obsessive_compulsive_disorder: 

1671 self.decide("obsessive_compulsive_disorder is true. Setting " 

1672 "diagnosis_1 to DIAG_3_OBSESSIVE_COMPULSIVE_DIS.") 

1673 self.diagnosis_1 = DIAG_3_OBSESSIVE_COMPULSIVE_DIS 

1674 if score >= 20: 

1675 self.decide("Total score >= 20. Setting diagnosis_1 to " 

1676 "DIAG_4_MIXED_ANX_DEPR_DIS.") 

1677 self.diagnosis_1 = DIAG_4_MIXED_ANX_DEPR_DIS 

1678 if self.phobia_specific: 

1679 self.decide("phobia_specific is true. Setting diagnosis_1 to " 

1680 "DIAG_5_SPECIFIC_PHOBIA.") 

1681 self.diagnosis_1 = DIAG_5_SPECIFIC_PHOBIA 

1682 if self.phobia_social: 

1683 self.decide("phobia_social is true. Setting diagnosis_1 to " 

1684 "DIAG_6_SOCIAL_PHOBIA.") 

1685 self.diagnosis_1 = DIAG_6_SOCIAL_PHOBIA 

1686 if self.phobia_agoraphobia: 

1687 self.decide("phobia_agoraphobia is true. Setting diagnosis_1 to " 

1688 "DIAG_7_AGORAPHOBIA.") 

1689 self.diagnosis_1 = DIAG_7_AGORAPHOBIA 

1690 if self.generalized_anxiety_disorder and score >= 20: 

1691 self.decide("generalized_anxiety_disorder is true AND " 

1692 "score >= 20. Setting diagnosis_1 to " 

1693 "DIAG_8_GENERALIZED_ANX_DIS.") 

1694 self.diagnosis_1 = DIAG_8_GENERALIZED_ANX_DIS 

1695 if self.panic_disorder: 

1696 self.decide("panic_disorder is true. Setting diagnosis_1 to " 

1697 "DIAG_9_PANIC_DIS.") 

1698 self.diagnosis_1 = DIAG_9_PANIC_DIS 

1699 if self.depression_mild: 

1700 self.decide("depression_mild is true. Setting diagnosis_1 to " 

1701 "DIAG_10_MILD_DEPR_EPISODE.") 

1702 self.diagnosis_1 = DIAG_10_MILD_DEPR_EPISODE 

1703 if self.depression_moderate: 

1704 self.decide("depression_moderate is true. Setting diagnosis_1 to " 

1705 "DIAG_11_MOD_DEPR_EPISODE.") 

1706 self.diagnosis_1 = DIAG_11_MOD_DEPR_EPISODE 

1707 if self.depression_severe: 

1708 self.decide("depression_severe is true. Setting diagnosis_1 to " 

1709 "DIAG_12_SEVERE_DEPR_EPISODE.") 

1710 self.diagnosis_1 = DIAG_12_SEVERE_DEPR_EPISODE 

1711 

1712 # ... secondary diagnosis 

1713 if score >= 12 and self.diagnosis_1 >= 2: 

1714 self.decide( 

1715 "score >= 12 AND diagnosis_1 >= 2. " 

1716 "Setting diagnosis_2 to DIAG_1_MIXED_ANX_DEPR_DIS_MILD.") 

1717 self.diagnosis_2 = DIAG_1_MIXED_ANX_DEPR_DIS_MILD 

1718 if self.generalized_anxiety_disorder and self.diagnosis_1 >= 3: 

1719 self.decide( 

1720 "generalized_anxiety_disorder is true AND " 

1721 "diagnosis_1 >= 3. " 

1722 "Setting diagnosis_2 to DIAG_2_GENERALIZED_ANX_DIS_MILD.") 

1723 self.diagnosis_2 = DIAG_2_GENERALIZED_ANX_DIS_MILD 

1724 if self.obsessive_compulsive_disorder and self.diagnosis_1 >= 4: 

1725 self.decide( 

1726 "obsessive_compulsive_disorder is true AND " 

1727 "diagnosis_1 >= 4. " 

1728 "Setting diagnosis_2 to DIAG_3_OBSESSIVE_COMPULSIVE_DIS.") 

1729 self.diagnosis_2 = DIAG_3_OBSESSIVE_COMPULSIVE_DIS 

1730 if score >= 20 and self.diagnosis_1 >= 5: 

1731 self.decide("score >= 20 AND diagnosis_1 >= 5. " 

1732 "Setting diagnosis_2 to DIAG_4_MIXED_ANX_DEPR_DIS.") 

1733 self.diagnosis_2 = DIAG_4_MIXED_ANX_DEPR_DIS 

1734 if self.phobia_specific and self.diagnosis_1 >= 6: 

1735 self.decide("phobia_specific is true AND diagnosis_1 >= 6. " 

1736 "Setting diagnosis_2 to DIAG_5_SPECIFIC_PHOBIA.") 

1737 self.diagnosis_2 = DIAG_5_SPECIFIC_PHOBIA 

1738 if self.phobia_social and self.diagnosis_1 >= 7: 

1739 self.decide("phobia_social is true AND diagnosis_1 >= 7. " 

1740 "Setting diagnosis_2 to DIAG_6_SOCIAL_PHOBIA.") 

1741 self.diagnosis_2 = DIAG_6_SOCIAL_PHOBIA 

1742 if self.phobia_agoraphobia and self.diagnosis_1 >= 8: 

1743 self.decide("phobia_agoraphobia is true AND diagnosis_1 >= 8. " 

1744 "Setting diagnosis_2 to DIAG_7_AGORAPHOBIA.") 

1745 self.diagnosis_2 = DIAG_7_AGORAPHOBIA 

1746 if (self.generalized_anxiety_disorder and score >= 20 and 

1747 self.diagnosis_1 >= 9): 

1748 self.decide("generalized_anxiety_disorder is true AND " 

1749 "score >= 20 AND " 

1750 "diagnosis_1 >= 9. " 

1751 "Setting diagnosis_2 to DIAG_8_GENERALIZED_ANX_DIS.") 

1752 self.diagnosis_2 = DIAG_8_GENERALIZED_ANX_DIS 

1753 if self.panic_disorder and self.diagnosis_1 >= 9: 

1754 self.decide("panic_disorder is true AND diagnosis_1 >= 9. " 

1755 "Setting diagnosis_2 to DIAG_9_PANIC_DIS.") 

1756 self.diagnosis_2 = DIAG_9_PANIC_DIS 

1757 

1758 # In summary: 

1759 self.decide("FINISHED.") 

1760 self.decide("--- Final scores:") 

1761 self._showint("depression", self.depression) 

1762 self._showint("depr_crit_1_mood_anhedonia_energy", 

1763 self.depr_crit_1_mood_anhedonia_energy) 

1764 self._showint("depr_crit_2_app_cnc_slp_mtr_glt_wth_sui", 

1765 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui) 

1766 self._showint("depr_crit_3_somatic_synd", 

1767 self.depr_crit_3_somatic_synd) 

1768 self._showint("weight_change", self.weight_change) 

1769 self._showint("somatic_symptoms", self.somatic_symptoms) 

1770 self._showint("fatigue", self.fatigue) 

1771 self._showint("neurasthenia", self.neurasthenia) 

1772 self._showint("concentration_poor", self.concentration_poor) 

1773 self._showint("sleep_problems", self.sleep_problems) 

1774 self._showint("sleep_change", self.sleep_change) 

1775 self._showint("depressive_thoughts", self.depressive_thoughts) 

1776 self._showint("irritability", self.irritability) 

1777 self._showint("diurnal_mood_variation", self.diurnal_mood_variation) 

1778 self._showbool("libido_decreased", self.libido_decreased) 

1779 self._showint("psychomotor_changes", self.psychomotor_changes) 

1780 self._showint("suicidality", self.suicidality) 

1781 self._showbool("depression_at_least_2_weeks", 

1782 self.depression_at_least_2_weeks) 

1783 

1784 self._showint("hypochondria", self.hypochondria) 

1785 self._showint("worry", self.worry) 

1786 self._showint("anxiety", self.anxiety) 

1787 self._showbool("anxiety_physical_symptoms", 

1788 self.anxiety_physical_symptoms) 

1789 self._showbool("anxiety_at_least_2_weeks", 

1790 self.anxiety_at_least_2_weeks) 

1791 self._showbool("phobias_flag", self.phobias_flag) 

1792 self._showint("phobias_score", self.phobias_score) 

1793 self._showint("phobias_type", self.phobias_type) 

1794 self._showbool("phobic_avoidance", self.phobic_avoidance) 

1795 self._showint("panic", self.panic) 

1796 self._showbool("panic_rapid_onset", self.panic_rapid_onset) 

1797 self._showint("panic_symptoms_total", self.panic_symptoms_total) 

1798 

1799 self._showint("compulsions", self.compulsions) 

1800 self._showbool("compulsions_tried_to_stop", 

1801 self.compulsions_tried_to_stop) 

1802 self._showbool("compulsions_at_least_2_weeks", 

1803 self.compulsions_at_least_2_weeks) 

1804 self._showint("obsessions", self.obsessions) 

1805 self._showbool("obsessions_tried_to_stop", 

1806 self.obsessions_tried_to_stop) 

1807 self._showbool("obsessions_at_least_2_weeks", 

1808 self.obsessions_at_least_2_weeks) 

1809 

1810 self._showint("functional_impairment", self.functional_impairment) 

1811 

1812 # Disorder flags 

1813 self._showbool("obsessive_compulsive_disorder", 

1814 self.obsessive_compulsive_disorder) 

1815 self._showbool("depression_mild", self.depression_mild) 

1816 self._showbool("depression_moderate", self.depression_moderate) 

1817 self._showbool("depression_severe", self.depression_severe) 

1818 self._showbool("chronic_fatigue_syndrome", 

1819 self.chronic_fatigue_syndrome) 

1820 self._showbool("generalized_anxiety_disorder", 

1821 self.generalized_anxiety_disorder) 

1822 self._showbool("phobia_agoraphobia", self.phobia_agoraphobia) 

1823 self._showbool("phobia_social", self.phobia_social) 

1824 self._showbool("phobia_specific", self.phobia_specific) 

1825 self._showbool("panic_disorder", self.panic_disorder) 

1826 

1827 self.decide("--- Final diagnoses:") 

1828 self.decide("Probable primary diagnosis: " + 

1829 self.diagnosis_name(self.diagnosis_1)) 

1830 self.decide("Probable secondary diagnosis: " + 

1831 self.diagnosis_name(self.diagnosis_2)) 

1832 

1833 

1834# ============================================================================= 

1835# CISR 

1836# ============================================================================= 

1837 

1838class Cisr(TaskHasPatientMixin, Task): 

1839 """ 

1840 Server implementation of the CIS-R task. 

1841 """ 

1842 __tablename__ = "cisr" 

1843 shortname = "CIS-R" 

1844 provides_trackers = False 

1845 

1846 # Demographics 

1847 

1848 ethnic = CamcopsColumn( 

1849 FN_ETHNIC, Integer, 

1850 comment=( 

1851 CMT_DEMOGRAPHICS + 

1852 "Ethnicity (1 white, 2 mixed, 3 Asian/British Asian, " 

1853 "4 Black/Black British, 5 Chinese, 6 other, 7 prefer not to say)" 

1854 ), 

1855 permitted_value_checker=ONE_TO_SEVEN_CHECKER, 

1856 ) 

1857 married = CamcopsColumn( 

1858 FN_MARRIED, Integer, 

1859 comment=( 

1860 CMT_DEMOGRAPHICS + 

1861 "Marital status (1 married/living as married, 2 single, " 

1862 "3 separated, 4 divorced, 5 widowed, 6 prefer not to say)" 

1863 ), 

1864 permitted_value_checker=ONE_TO_SIX_CHECKER, 

1865 ) 

1866 empstat = CamcopsColumn( 

1867 FN_EMPSTAT, Integer, 

1868 comment=( 

1869 CMT_DEMOGRAPHICS + 

1870 "Current employment status (1 working full time, " 

1871 "2 working part time, 3 student, 4 retired, 5 houseperson, " 

1872 "6 unemployed job seeker, 7 unemployed due to ill health," 

1873 "8 prefer not to say)" 

1874 ), 

1875 permitted_value_checker=ONE_TO_EIGHT_CHECKER, 

1876 ) 

1877 emptype = CamcopsColumn( 

1878 FN_EMPTYPE, Integer, 

1879 comment=( 

1880 CMT_DEMOGRAPHICS + 

1881 "Current/last paid employment " 

1882 "(1 self-employed with paid employees, " 

1883 "2 self-employed with no paid employees, 3 employee, " 

1884 "4 foreman/supervisor, 5 manager, 6 not applicable," 

1885 "7 prefer not to say)" 

1886 ), 

1887 permitted_value_checker=ONE_TO_SEVEN_CHECKER, 

1888 ) 

1889 home = CamcopsColumn( 

1890 FN_HOME, Integer, 

1891 comment=( 

1892 CMT_DEMOGRAPHICS + 

1893 "Housing situation (1 home owner, 2 tenant, 3 living with " 

1894 "relative/friend, 4 hostel/care home, 5 homeless, 6 other," 

1895 "7 prefer not to say)" 

1896 ), 

1897 permitted_value_checker=ONE_TO_SEVEN_CHECKER, 

1898 ) 

1899 

1900 # Appetite/weight 

1901 

1902 appetite1 = CamcopsColumn( 

1903 FN_APPETITE1, Integer, 

1904 comment="Marked appetite loss in past month" + CMT_1_NO_2_YES, 

1905 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1906 ) 

1907 weight1 = CamcopsColumn( 

1908 FN_WEIGHT1, Integer, 

1909 comment="Weight loss in past month" + CMT_1_NO_2_YES, 

1910 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1911 ) 

1912 weight2 = CamcopsColumn( 

1913 FN_WEIGHT2, Integer, 

1914 comment="Weight loss: trying to lose weight?" + CMT_1_NO_2_YES, 

1915 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1916 ) 

1917 weight3 = CamcopsColumn( 

1918 FN_WEIGHT3, Integer, 

1919 comment="Weight loss amount (1: ≥0.5 stones; 2: <0.5 stones)", 

1920 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1921 ) 

1922 appetite2 = CamcopsColumn( 

1923 FN_APPETITE2, Integer, 

1924 comment="Marked increase in appetite in past month" + CMT_1_NO_2_YES, 

1925 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1926 ) 

1927 weight4 = CamcopsColumn( 

1928 # male/female responses unified (no "weight4a") 

1929 FN_WEIGHT4, Integer, 

1930 comment="Weight gain in past month (1 yes, 2 no, 3 yes but pregnant)", 

1931 permitted_value_checker=ONE_TO_THREE_CHECKER, 

1932 ) 

1933 weight5 = CamcopsColumn( 

1934 FN_WEIGHT5, Integer, 

1935 comment="Weight gain amount (1: ≥0.5 stones; 2: <0.5 stones)", 

1936 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1937 ) 

1938 

1939 # Somatic problems 

1940 

1941 gp_year = CamcopsColumn( 

1942 FN_GP_YEAR, Integer, 

1943 comment="Consultations with GP in past year (0: none, 1: 1–2, 2: 3–4, " 

1944 "3: 6–10; 4: >10", 

1945 permitted_value_checker=ZERO_TO_FOUR_CHECKER, 

1946 ) 

1947 disable = CamcopsColumn( 

1948 FN_DISABLE, Integer, 

1949 comment="Longstanding illness/disability/infirmity" + CMT_1_YES_2_NO, 

1950 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1951 ) 

1952 illness = CamcopsColumn( 

1953 FN_ILLNESS, Integer, 

1954 comment="Conditions (1 diabetes, 2 asthma, 3 arthritis, 4 heart " 

1955 "disease, 5 high blood pressure, 6 lung disease, 7 more than " 

1956 "one of the above, 8 none of the above)", 

1957 permitted_value_checker=ONE_TO_EIGHT_CHECKER, 

1958 ) 

1959 

1960 somatic_mand1 = CamcopsColumn( 

1961 FN_SOMATIC_MAND1, Integer, 

1962 comment="Any aches/pains in past month?" + CMT_1_NO_2_YES, 

1963 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1964 ) 

1965 somatic_pain1 = CamcopsColumn( 

1966 FN_SOMATIC_PAIN1, Integer, 

1967 comment="Pain/ache brought on or made worse because low/anxious/" 

1968 "stressed" + CMT_NEVER_SOMETIMES_ALWAYS, 

1969 permitted_value_checker=ONE_TO_THREE_CHECKER, 

1970 ) 

1971 somatic_pain2 = CamcopsColumn( 

1972 FN_SOMATIC_PAIN2, Integer, 

1973 comment="Pain: days in past week" + CMT_DAYS_PER_WEEK, 

1974 permitted_value_checker=ONE_TO_THREE_CHECKER, 

1975 ) 

1976 somatic_pain3 = CamcopsColumn( 

1977 FN_SOMATIC_PAIN3, Integer, 

1978 comment="Pain: lasted >3h on any day in past week" + CMT_1_NO_2_YES, 

1979 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1980 ) 

1981 somatic_pain4 = CamcopsColumn( 

1982 FN_SOMATIC_PAIN4, Integer, 

1983 comment="Pain: unpleasant in past week?" + CMT_UNPLEASANT, 

1984 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

1985 ) 

1986 somatic_pain5 = CamcopsColumn( 

1987 FN_SOMATIC_PAIN5, Integer, 

1988 comment="Pain: bothersome whilst doing something interesting in past " 

1989 "week?" + CMT_BOTHERSOME_INTERESTING, 

1990 permitted_value_checker=ONE_TO_THREE_CHECKER, 

1991 ) 

1992 somatic_mand2 = CamcopsColumn( 

1993 FN_SOMATIC_MAND2, Integer, 

1994 comment="Bodily discomfort in past month?" + CMT_1_NO_2_YES, 

1995 permitted_value_checker=ONE_TO_TWO_CHECKER, 

1996 ) 

1997 somatic_dis1 = CamcopsColumn( 

1998 FN_SOMATIC_DIS1, Integer, 

1999 comment="Discomfort brought on or made worse because low/anxious/" 

2000 "stressed" + CMT_NEVER_SOMETIMES_ALWAYS, 

2001 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2002 ) 

2003 somatic_dis2 = CamcopsColumn( 

2004 FN_SOMATIC_DIS2, Integer, 

2005 comment="Discomfort: days in past week" + CMT_DAYS_PER_WEEK, 

2006 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2007 ) 

2008 somatic_dis3 = CamcopsColumn( 

2009 FN_SOMATIC_DIS3, Integer, 

2010 comment="Discomfort: lasted >3h on any day in past week" 

2011 + CMT_1_NO_2_YES, 

2012 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2013 ) 

2014 somatic_dis4 = CamcopsColumn( 

2015 FN_SOMATIC_DIS4, Integer, 

2016 comment="Discomfort: unpleasant in past week?" + CMT_UNPLEASANT, 

2017 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2018 ) 

2019 somatic_dis5 = CamcopsColumn( 

2020 FN_SOMATIC_DIS5, Integer, 

2021 comment="Discomfort: bothersome whilst doing something interesting in " 

2022 "past week?" + CMT_BOTHERSOME_INTERESTING, 

2023 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2024 ) 

2025 somatic_dur = CamcopsColumn( 

2026 FN_SOMATIC_DUR, Integer, 

2027 comment="Duration of ache/pain/discomfort" + CMT_DURATION, 

2028 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2029 ) 

2030 

2031 # Fatigue/lacking energy 

2032 

2033 fatigue_mand1 = CamcopsColumn( 

2034 FN_FATIGUE_MAND1, Integer, 

2035 comment="Tired in past month" + CMT_1_NO_2_YES, 

2036 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2037 ) 

2038 fatigue_cause1 = CamcopsColumn( 

2039 FN_FATIGUE_CAUSE1, Integer, 

2040 comment="Main reason for feeling tired" + CMT_FATIGUE_CAUSE, 

2041 permitted_value_checker=ONE_TO_EIGHT_CHECKER, 

2042 ) 

2043 fatigue_tired1 = CamcopsColumn( 

2044 FN_FATIGUE_TIRED1, Integer, 

2045 comment="Tired: days in past week" + CMT_DAYS_PER_WEEK, 

2046 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2047 ) 

2048 fatigue_tired2 = CamcopsColumn( 

2049 FN_FATIGUE_TIRED2, Integer, 

2050 comment="Tired: >3h on any one day in past week" + CMT_1_NO_2_YES, 

2051 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2052 ) 

2053 fatigue_tired3 = CamcopsColumn( 

2054 FN_FATIGUE_TIRED3, Integer, 

2055 comment="So tired you've had to push yourself to get things done in " 

2056 "past week" + CMT_1_NO_2_YES, 

2057 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2058 ) 

2059 fatigue_tired4 = CamcopsColumn( 

2060 FN_FATIGUE_TIRED4, Integer, 

2061 comment="Tired during an enjoyable activity" + CMT_DURING_ENJOYABLE, 

2062 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2063 ) 

2064 fatigue_mand2 = CamcopsColumn( 

2065 FN_FATIGUE_MAND2, Integer, 

2066 comment="Lacking in energy in past month" + CMT_1_NO_2_YES, 

2067 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2068 ) 

2069 fatigue_cause2 = CamcopsColumn( 

2070 FN_FATIGUE_CAUSE2, Integer, 

2071 comment="Main reason for lacking energy" + CMT_FATIGUE_CAUSE, 

2072 permitted_value_checker=ONE_TO_EIGHT_CHECKER, 

2073 ) 

2074 fatigue_energy1 = CamcopsColumn( 

2075 FN_FATIGUE_ENERGY1, Integer, 

2076 comment="Lacking energy: days in past week" + CMT_DAYS_PER_WEEK, 

2077 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2078 ) 

2079 fatigue_energy2 = CamcopsColumn( 

2080 FN_FATIGUE_ENERGY2, Integer, 

2081 comment="Lacking energy: for >3h on any one day in past week" + 

2082 CMT_1_NO_2_YES, 

2083 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2084 ) 

2085 fatigue_energy3 = CamcopsColumn( 

2086 FN_FATIGUE_ENERGY3, Integer, 

2087 comment="So lacking in energy you've had to push yourself to get " 

2088 "things done in past week" + CMT_1_NO_2_YES, 

2089 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2090 ) 

2091 fatigue_energy4 = CamcopsColumn( 

2092 FN_FATIGUE_ENERGY4, Integer, 

2093 comment="Lacking energy during an enjoyable activity" + 

2094 CMT_DURING_ENJOYABLE, 

2095 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2096 ) 

2097 fatigue_dur = CamcopsColumn( 

2098 FN_FATIGUE_DUR, Integer, 

2099 comment="Feeling tired/lacking energy for how long?" + CMT_DURATION, 

2100 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2101 ) 

2102 

2103 # Concentration/memory 

2104 

2105 conc_mand1 = CamcopsColumn( 

2106 FN_CONC_MAND1, Integer, 

2107 comment="Problems in concentrating during past monnth?" + 

2108 CMT_1_NO_2_YES, 

2109 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2110 ) 

2111 conc_mand2 = CamcopsColumn( 

2112 FN_CONC_MAND2, Integer, 

2113 comment="Problems with forgetting things during past month?" + 

2114 CMT_1_NO_2_YES, 

2115 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2116 ) 

2117 conc1 = CamcopsColumn( 

2118 FN_CONC1, Integer, 

2119 comment="Concentration/memory problems: days in past week" + 

2120 CMT_DAYS_PER_WEEK, 

2121 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2122 ) 

2123 conc2 = CamcopsColumn( 

2124 FN_CONC2, Integer, 

2125 comment="In past week, could concentrate on all of: TV, newspaper, " 

2126 "conversation" + CMT_1_YES_2_NO, 

2127 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2128 ) 

2129 conc3 = CamcopsColumn( 

2130 FN_CONC3, Integer, 

2131 comment="Problems with concentration have stopped you from getting on " 

2132 "with things in past week" + CMT_1_NO_2_YES, 

2133 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2134 ) 

2135 conc_dur = CamcopsColumn( 

2136 FN_CONC_DUR, Integer, 

2137 comment="Problems with concentration: for how long?" + CMT_DURATION, 

2138 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2139 ) 

2140 conc4 = CamcopsColumn( 

2141 FN_CONC4, Integer, 

2142 comment="Forgotten anything important in past week" + CMT_1_NO_2_YES, 

2143 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2144 ) 

2145 forget_dur = CamcopsColumn( 

2146 FN_FORGET_DUR, Integer, 

2147 comment="Problems with memory: for how long?" + CMT_DURATION, 

2148 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2149 ) 

2150 

2151 # Sleep 

2152 

2153 sleep_mand1 = CamcopsColumn( 

2154 FN_SLEEP_MAND1, Integer, 

2155 comment="Problems with sleep loss in past month" + CMT_1_NO_2_YES, 

2156 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2157 ) 

2158 sleep_lose1 = CamcopsColumn( 

2159 FN_SLEEP_LOSE1, Integer, 

2160 comment="Sleep loss: nights in past week with problems" + 

2161 CMT_NIGHTS_PER_WEEK, 

2162 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2163 ) 

2164 sleep_lose2 = CamcopsColumn( 

2165 FN_SLEEP_LOSE2, Integer, 

2166 comment="On night with least sleep in past week, how long trying to " 

2167 "get to sleep?" + CMT_SLEEP_CHANGE, 

2168 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2169 ) 

2170 sleep_lose3 = CamcopsColumn( 

2171 FN_SLEEP_LOSE3, Integer, 

2172 comment="On how many nights in past week did you spend >=3h trying to " 

2173 "get to sleep?" + CMT_NIGHTS_PER_WEEK, 

2174 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2175 ) 

2176 sleep_emw = CamcopsColumn( 

2177 FN_SLEEP_EMW, Integer, 

2178 comment="Woken >2h earlier (and couldn't return to sleep) in past " 

2179 "week?" + CMT_1_NO_2_YES, 

2180 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2181 ) 

2182 sleep_cause = CamcopsColumn( 

2183 FN_SLEEP_CAUSE, Integer, 

2184 comment="What are your sleep difficulties caused by? (1 noise, " 

2185 "2 shift work, 3 pain/illness, 4 worries, 5 unknown, 6 other", 

2186 permitted_value_checker=ONE_TO_SIX_CHECKER, 

2187 ) 

2188 sleep_mand2 = CamcopsColumn( 

2189 FN_SLEEP_MAND2, Integer, 

2190 comment="Problems with excess sleep in past month (1 no, 2 slept more " 

2191 "than usual but not a problem, 3 yes)", 

2192 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2193 ) 

2194 sleep_gain1 = CamcopsColumn( 

2195 FN_SLEEP_GAIN1, Integer, 

2196 comment="Sleep gain: how many nights in past week" + 

2197 CMT_NIGHTS_PER_WEEK, 

2198 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2199 ) 

2200 sleep_gain2 = CamcopsColumn( 

2201 FN_SLEEP_GAIN2, Integer, 

2202 comment="On night with most sleep in past week, how much more than " 

2203 "usual?" + CMT_SLEEP_CHANGE, 

2204 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2205 ) 

2206 sleep_gain3 = CamcopsColumn( 

2207 FN_SLEEP_GAIN3, Integer, 

2208 comment="On how many nights in past week did you sleep >3h longer " 

2209 "than usual?" + CMT_NIGHTS_PER_WEEK, 

2210 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2211 ) 

2212 sleep_dur = CamcopsColumn( 

2213 FN_SLEEP_DUR, Integer, 

2214 comment="How long have you had these problems with sleep?" + 

2215 CMT_DURATION, 

2216 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2217 ) 

2218 

2219 # Irritability 

2220 

2221 irrit_mand1 = CamcopsColumn( 

2222 FN_IRRIT_MAND1, Integer, 

2223 comment="Irritable with those around you in past month?" + 

2224 CMT_1_NO_2_YES, 

2225 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2226 ) 

2227 irrit_mand2 = CamcopsColumn( 

2228 FN_IRRIT_MAND2, Integer, 

2229 comment="Short-tempered/angry over trivial things in past month? " 

2230 "(1 no, 2 sometimes, 3 yes)", 

2231 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2232 ) 

2233 irrit1 = CamcopsColumn( 

2234 FN_IRRIT1, Integer, 

2235 comment="Irritable/short-tempered/angry: days in past week" + 

2236 CMT_DAYS_PER_WEEK, 

2237 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2238 ) 

2239 irrit2 = CamcopsColumn( 

2240 FN_IRRIT2, Integer, 

2241 comment="Irritable/short-tempered/angry: for >1h on any day in past " 

2242 "week?" + CMT_1_NO_2_YES, 

2243 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2244 ) 

2245 irrit3 = CamcopsColumn( 

2246 FN_IRRIT3, Integer, 

2247 comment="Irritable/short-tempered/angry: wanted to shout at someone? " 

2248 "(1 no; yes but didn't shout; 3 yes and did shout)", 

2249 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2250 ) 

2251 irrit4 = CamcopsColumn( 

2252 FN_IRRIT4, Integer, 

2253 comment="In past week, have you had arguments/rows/lost temper? " 

2254 "(1 no; 2 yes but justified; 3 yes)", 

2255 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2256 ) 

2257 irrit_dur = CamcopsColumn( 

2258 FN_IRRIT_DUR, Integer, 

2259 comment="Irritable/short-tempered/angry: for how long?" + CMT_DURATION, 

2260 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2261 ) 

2262 

2263 # Hypochondriasis 

2264 

2265 hypo_mand1 = CamcopsColumn( 

2266 FN_HYPO_MAND1, Integer, 

2267 comment="Worried about physical health in past month?" + 

2268 CMT_1_NO_2_YES, 

2269 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2270 ) 

2271 hypo_mand2 = CamcopsColumn( 

2272 FN_HYPO_MAND2, Integer, 

2273 comment="Do you worry you have a serious illness?" + CMT_1_NO_2_YES, 

2274 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2275 ) 

2276 hypo1 = CamcopsColumn( 

2277 FN_HYPO1, Integer, 

2278 comment="Worrying about health/having a serious illness: how many " 

2279 "days in past week?" + CMT_DAYS_PER_WEEK, 

2280 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2281 ) 

2282 hypo2 = CamcopsColumn( 

2283 FN_HYPO2, Integer, 

2284 comment="Worrying too much about physical health?" + CMT_1_NO_2_YES, 

2285 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2286 ) 

2287 hypo3 = CamcopsColumn( 

2288 FN_HYPO3, Integer, 

2289 comment="Worrying about health: how unpleasant?" + CMT_UNPLEASANT, 

2290 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2291 ) 

2292 hypo4 = CamcopsColumn( 

2293 FN_HYPO4, Integer, 

2294 comment="Able to take mind off health worries in past week?" + 

2295 CMT_1_YES_2_NO, 

2296 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2297 ) 

2298 hypo_dur = CamcopsColumn( 

2299 FN_HYPO_DUR, Integer, 

2300 comment="Worrying about physical health: for how long?" + CMT_DURATION, 

2301 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2302 ) 

2303 

2304 # Depression 

2305 

2306 depr_mand1 = CamcopsColumn( 

2307 FN_DEPR_MAND1, Integer, 

2308 comment="Sad/miserable/depressed in past month?" + CMT_1_NO_2_YES, 

2309 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2310 ) 

2311 depr1 = CamcopsColumn( 

2312 FN_DEPR1, Integer, 

2313 comment="Sad/miserable/depressed in past week?" + CMT_1_NO_2_YES, 

2314 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2315 ) 

2316 depr_mand2 = CamcopsColumn( 

2317 FN_DEPR_MAND2, Integer, 

2318 comment="In the past month, able to enjoy/take an interest in things " 

2319 "as much as usual?" + CMT_ANHEDONIA, 

2320 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2321 ) 

2322 depr2 = CamcopsColumn( 

2323 FN_DEPR2, Integer, 

2324 comment="In the past week, able to enjoy/take an interest in things " 

2325 "as much as usual?" + CMT_ANHEDONIA, 

2326 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2327 ) 

2328 depr3 = CamcopsColumn( 

2329 FN_DEPR3, Integer, 

2330 comment="[Depressed mood] or [anhedonia] on how many days in past " 

2331 "week" + CMT_DAYS_PER_WEEK, 

2332 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2333 ) 

2334 depr4 = CamcopsColumn( 

2335 FN_DEPR4, Integer, 

2336 comment="[Depressed mood] or [anhedonia] for >3h on any day in past " 

2337 "week?" + CMT_1_NO_2_YES, 

2338 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2339 ) 

2340 depr_content = CamcopsColumn( 

2341 FN_DEPR_CONTENT, Integer, 

2342 comment="Main reason for [depressed mood] or [anhedonia]?" + 

2343 CMT_STRESSORS, 

2344 permitted_value_checker=ONE_TO_NINE_CHECKER, 

2345 ) 

2346 depr5 = CamcopsColumn( 

2347 FN_DEPR5, Integer, 

2348 comment="In past week, during [depressed mood] or [anhedonia], did " 

2349 "nice things/company make you happier? " 

2350 "(1 always, 2 sometimes, 3 no)", 

2351 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2352 ) 

2353 depr_dur = CamcopsColumn( 

2354 FN_DEPR_DUR, Integer, 

2355 comment="Depressed mood/anhedonia: for how long?" + CMT_DURATION, 

2356 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2357 ) 

2358 depth1 = CamcopsColumn( 

2359 FN_DEPTH1, Integer, 

2360 comment="Diurnal mood variation in past week (1 worse in the morning, " 

2361 "2 worse in the evening, 3 varies, 4 no difference)", 

2362 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2363 ) 

2364 depth2 = CamcopsColumn( 

2365 FN_DEPTH2, Integer, 

2366 comment="Libido in past month (1 not applicable, 2 no change, " 

2367 "3 increased, 4 decreased)", 

2368 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2369 ) 

2370 depth3 = CamcopsColumn( 

2371 FN_DEPTH3, Integer, 

2372 comment="Restlessness in past week" + CMT_1_NO_2_YES, 

2373 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2374 ) 

2375 depth4 = CamcopsColumn( 

2376 FN_DEPTH4, Integer, 

2377 comment="Psychomotor retardation in past week" + CMT_1_NO_2_YES, 

2378 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2379 ) 

2380 depth5 = CamcopsColumn( 

2381 FN_DEPTH5, Integer, 

2382 comment="Guilt/blamed self in past week (1 never, 2 only when it was " 

2383 "my fault, 3 sometimes, 4 often)", 

2384 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2385 ) 

2386 depth6 = CamcopsColumn( 

2387 FN_DEPTH6, Integer, 

2388 comment="Feeling not as good as other people in past week" + 

2389 CMT_1_NO_2_YES, 

2390 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2391 ) 

2392 depth7 = CamcopsColumn( 

2393 FN_DEPTH7, Integer, 

2394 comment="Hopeless in past week" + CMT_1_NO_2_YES, 

2395 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2396 ) 

2397 depth8 = CamcopsColumn( 

2398 FN_DEPTH8, Integer, 

2399 comment="Life not worth living in past week (1 no, 2 sometimes, " 

2400 "3 always)", 

2401 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2402 ) 

2403 depth9 = CamcopsColumn( 

2404 FN_DEPTH9, Integer, 

2405 comment="Thoughts of suicide in past week (1 no; 2 yes, but would " 

2406 "never commit suicide; 3 yes)", 

2407 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2408 ) 

2409 depth10 = CamcopsColumn( 

2410 FN_DEPTH10, Integer, 

2411 comment="Thoughts of way to kill self in past week" + CMT_1_NO_2_YES, 

2412 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2413 ) 

2414 doctor = CamcopsColumn( 

2415 FN_DOCTOR, Integer, 

2416 comment="Have you spoken to your doctor about these thoughts of " 

2417 "killing yourself (1 yes; 2 no, but have talked to other " 

2418 "people; 3 no)", 

2419 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2420 ) 

2421 

2422 # Worry/generalized anxiety 

2423 

2424 worry_mand1 = CamcopsColumn( 

2425 FN_WORRY_MAND1, Integer, 

2426 comment="Excessive worry in past month?" + CMT_NO_SOMETIMES_OFTEN, 

2427 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2428 ) 

2429 worry_mand2 = CamcopsColumn( 

2430 FN_WORRY_MAND2, Integer, 

2431 comment="Any worries at all in past month?" + CMT_1_NO_2_YES, 

2432 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2433 ) 

2434 worry_cont1 = CamcopsColumn( 

2435 FN_WORRY_CONT1, Integer, 

2436 comment="Main source of worry in past week?" + CMT_STRESSORS, 

2437 permitted_value_checker=ONE_TO_NINE_CHECKER, 

2438 ) 

2439 worry2 = CamcopsColumn( 

2440 FN_WORRY2, Integer, 

2441 comment="Worries (about things other than physical health) on how " 

2442 "many days in past week" + CMT_DAYS_PER_WEEK, 

2443 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2444 ) 

2445 worry3 = CamcopsColumn( 

2446 FN_WORRY3, Integer, 

2447 comment="Worrying too much?" + CMT_1_NO_2_YES, 

2448 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2449 ) 

2450 worry4 = CamcopsColumn( 

2451 FN_WORRY4, Integer, 

2452 comment="How unpleasant is worry (about things other than physical " 

2453 "health)" + CMT_UNPLEASANT, 

2454 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2455 ) 

2456 worry5 = CamcopsColumn( 

2457 FN_WORRY5, Integer, 

2458 comment="Worry (about things other than physical health) for >3h on " 

2459 "any day in past week?" + CMT_1_NO_2_YES, 

2460 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2461 ) 

2462 worry_dur = CamcopsColumn( 

2463 FN_WORRY_DUR, Integer, 

2464 comment="Worry (about things other than physical health): for how " 

2465 "long?" + CMT_DURATION, 

2466 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2467 ) 

2468 

2469 anx_mand1 = CamcopsColumn( 

2470 FN_ANX_MAND1, Integer, 

2471 comment="Anxious/nervous in past month?" + CMT_1_NO_2_YES, 

2472 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2473 ) 

2474 anx_mand2 = CamcopsColumn( 

2475 FN_ANX_MAND2, Integer, 

2476 comment="Muscle tension/couldn't relax in past month?" + 

2477 CMT_NO_SOMETIMES_OFTEN, 

2478 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2479 ) 

2480 anx_phobia1 = CamcopsColumn( 

2481 FN_ANX_PHOBIA1, Integer, 

2482 comment="Phobic anxiety in past month?" + CMT_1_NO_2_YES, 

2483 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2484 ) 

2485 anx_phobia2 = CamcopsColumn( 

2486 FN_ANX_PHOBIA2, Integer, 

2487 comment="Phobic anxiety: always specific? (1 always specific, " 

2488 "2 sometimes general)", 

2489 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2490 ) 

2491 anx2 = CamcopsColumn( 

2492 FN_ANX2, Integer, 

2493 comment="Anxiety/nervousness/tension: how many days in past week" + 

2494 CMT_DAYS_PER_WEEK, 

2495 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2496 ) 

2497 anx3 = CamcopsColumn( 

2498 FN_ANX3, Integer, 

2499 comment="Anxiety/nervousness/tension: how unpleasant in past week" + 

2500 CMT_UNPLEASANT, 

2501 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2502 ) 

2503 anx4 = CamcopsColumn( 

2504 FN_ANX4, Integer, 

2505 comment="Anxiety/nervousness/tension: physical symptoms in past " 

2506 "week?" + CMT_1_NO_2_YES, 

2507 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2508 ) 

2509 anx5 = CamcopsColumn( 

2510 FN_ANX5, Integer, 

2511 comment="Anxiety/nervousness/tension: for >3h on any day in past " 

2512 "week?" + CMT_1_NO_2_YES, 

2513 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2514 ) 

2515 anx_dur = CamcopsColumn( 

2516 FN_ANX_DUR, Integer, 

2517 comment="Anxiety/nervousness/tension: for how long?" + CMT_DURATION, 

2518 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2519 ) 

2520 

2521 # Specific phobias 

2522 

2523 phobias_mand = CamcopsColumn( 

2524 FN_PHOBIAS_MAND, Integer, 

2525 comment="Phobic avoidance in past month?" + CMT_1_NO_2_YES, 

2526 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2527 ) 

2528 phobias_type1 = CamcopsColumn( 

2529 FN_PHOBIAS_TYPE1, Integer, 

2530 comment="Which phobia? (1 travelling alone by bus/train; 2 being far " 

2531 "from home; 3 public eating/speaking; 4 sight of blood; " 

2532 "5 crowded shops; 6 insects/spiders/animals; 7 being watched; " 

2533 "8 enclosed spaces or heights; 9 something else)", 

2534 permitted_value_checker=ONE_TO_NINE_CHECKER, 

2535 ) 

2536 phobias1 = CamcopsColumn( 

2537 FN_PHOBIAS1, Integer, 

2538 comment="Phobic anxiety: days in past week" + CMT_DAYS_PER_WEEK, 

2539 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2540 ) 

2541 phobias2 = CamcopsColumn( 

2542 FN_PHOBIAS2, Integer, 

2543 comment="Phobic anxiety: physical symptoms in past week?" + 

2544 CMT_1_NO_2_YES, 

2545 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2546 ) 

2547 phobias3 = CamcopsColumn( 

2548 FN_PHOBIAS3, Integer, 

2549 comment="Phobic avoidance in past week?" + CMT_1_NO_2_YES, 

2550 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2551 ) 

2552 phobias4 = CamcopsColumn( 

2553 FN_PHOBIAS4, Integer, 

2554 comment="Phobic avoidance: how many times in past week? (1: none, " 

2555 "2: 1–3, 3: >=4)", 

2556 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2557 ) 

2558 phobias_dur = CamcopsColumn( 

2559 FN_PHOBIAS_DUR, Integer, 

2560 comment="Phobic anxiety: for how long?" + CMT_DURATION, 

2561 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2562 ) 

2563 

2564 # Panic 

2565 

2566 panic_mand = CamcopsColumn( 

2567 FN_PANIC_MAND, Integer, 

2568 comment="Panic in past month (1: no, my anxiety never got that bad; " 

2569 "2: yes, sometimes; 3: yes, often)", 

2570 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2571 ) 

2572 panic1 = CamcopsColumn( 

2573 FN_PANIC1, Integer, 

2574 comment="Panic: how often in past week (1 not in past seven days, " 

2575 "2 once, 3 more than once)", 

2576 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2577 ) 

2578 panic2 = CamcopsColumn( 

2579 FN_PANIC2, Integer, 

2580 comment="Panic: how unpleasant in past week (1 a little " 

2581 "uncomfortable; 2 unpleasant; 3 unbearable, or very " 

2582 "unpleasant)", 

2583 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2584 ) 

2585 panic3 = CamcopsColumn( 

2586 FN_PANIC3, Integer, 

2587 comment="Panic: in the past week, did the worst panic last >10min " 

2588 "(1: <10min; 2 >=10min)", 

2589 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2590 ) 

2591 panic4 = CamcopsColumn( 

2592 FN_PANIC4, Integer, 

2593 comment="Do panics start suddenly?" + CMT_1_NO_2_YES, 

2594 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2595 ) 

2596 pansym_a = CamcopsColumn( 

2597 FN_PANSYM_A, Integer, 

2598 comment=CMT_PANIC_SYMPTOM + "heart racing" + CMT_1_NO_2_YES, 

2599 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2600 ) 

2601 pansym_b = CamcopsColumn( 

2602 FN_PANSYM_B, Integer, 

2603 comment=CMT_PANIC_SYMPTOM + "hands sweaty/clammy" + CMT_1_NO_2_YES, 

2604 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2605 ) 

2606 pansym_c = CamcopsColumn( 

2607 FN_PANSYM_C, Integer, 

2608 comment=CMT_PANIC_SYMPTOM + "trembling/shaking" + CMT_1_NO_2_YES, 

2609 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2610 ) 

2611 pansym_d = CamcopsColumn( 

2612 FN_PANSYM_D, Integer, 

2613 comment=CMT_PANIC_SYMPTOM + "short of breath" + CMT_1_NO_2_YES, 

2614 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2615 ) 

2616 pansym_e = CamcopsColumn( 

2617 FN_PANSYM_E, Integer, 

2618 comment=CMT_PANIC_SYMPTOM + "choking sensation" + CMT_1_NO_2_YES, 

2619 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2620 ) 

2621 pansym_f = CamcopsColumn( 

2622 FN_PANSYM_F, Integer, 

2623 comment=(CMT_PANIC_SYMPTOM + "chest pain/pressure/discomfort" + 

2624 CMT_1_NO_2_YES), 

2625 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2626 ) 

2627 pansym_g = CamcopsColumn( 

2628 FN_PANSYM_G, Integer, 

2629 comment=CMT_PANIC_SYMPTOM + "nausea" + CMT_1_NO_2_YES, 

2630 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2631 ) 

2632 pansym_h = CamcopsColumn( 

2633 FN_PANSYM_H, Integer, 

2634 comment=(CMT_PANIC_SYMPTOM + "dizzy/unsteady/lightheaded/faint" + 

2635 CMT_1_NO_2_YES), 

2636 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2637 ) 

2638 pansym_i = CamcopsColumn( 

2639 FN_PANSYM_I, Integer, 

2640 comment=(CMT_PANIC_SYMPTOM + "derealization/depersonalization" + 

2641 CMT_1_NO_2_YES), 

2642 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2643 ) 

2644 pansym_j = CamcopsColumn( 

2645 FN_PANSYM_J, Integer, 

2646 comment=(CMT_PANIC_SYMPTOM + "losing control/going crazy" + 

2647 CMT_1_NO_2_YES), 

2648 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2649 ) 

2650 pansym_k = CamcopsColumn( 

2651 FN_PANSYM_K, Integer, 

2652 comment=CMT_PANIC_SYMPTOM + "fear were dying" + CMT_1_NO_2_YES, 

2653 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2654 ) 

2655 pansym_l = CamcopsColumn( 

2656 FN_PANSYM_L, Integer, 

2657 comment=CMT_PANIC_SYMPTOM + "tingling/numbness" + CMT_1_NO_2_YES, 

2658 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2659 ) 

2660 pansym_m = CamcopsColumn( 

2661 FN_PANSYM_M, Integer, 

2662 comment=CMT_PANIC_SYMPTOM + "hot flushes/chills" + CMT_1_NO_2_YES, 

2663 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2664 ) 

2665 panic5 = CamcopsColumn( 

2666 FN_PANIC5, Integer, 

2667 comment="Is panic always brought on by specific things?" + 

2668 CMT_1_NO_2_YES, 

2669 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2670 ) 

2671 panic_dur = CamcopsColumn( 

2672 FN_PANIC_DUR, Integer, 

2673 comment="Panic: for how long?" + CMT_DURATION, 

2674 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2675 ) 

2676 

2677 # Compulsions 

2678 

2679 comp_mand1 = CamcopsColumn( 

2680 FN_COMP_MAND1, Integer, 

2681 comment="Compulsions in past month" + CMT_NO_SOMETIMES_OFTEN, 

2682 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2683 ) 

2684 comp1 = CamcopsColumn( 

2685 FN_COMP1, Integer, 

2686 comment="Compulsions: how many days in past week" + CMT_DAYS_PER_WEEK, 

2687 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2688 ) 

2689 comp2 = CamcopsColumn( 

2690 FN_COMP2, Integer, 

2691 comment="Compulsions: tried to stop in past week" + CMT_1_NO_2_YES, 

2692 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2693 ) 

2694 comp3 = CamcopsColumn( 

2695 FN_COMP3, Integer, 

2696 comment="Compulsions: upsetting/annoying in past week" + 

2697 CMT_1_NO_2_YES, 

2698 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2699 ) 

2700 comp4 = CamcopsColumn( 

2701 FN_COMP4, Integer, 

2702 comment="Compulsions: greatest number of repeats in past week " 

2703 "(1: once, i.e. two times altogether; 2: two repeats; " 

2704 "3: three or more repeats)", 

2705 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2706 ) 

2707 comp_dur = CamcopsColumn( 

2708 FN_COMP_DUR, Integer, 

2709 comment="Compulsions: for how long?" + CMT_DURATION, 

2710 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2711 ) 

2712 

2713 # Obsessions 

2714 

2715 obsess_mand1 = CamcopsColumn( 

2716 FN_OBSESS_MAND1, Integer, 

2717 comment="Obsessions in past month" + CMT_NO_SOMETIMES_OFTEN, 

2718 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2719 ) 

2720 obsess_mand2 = CamcopsColumn( 

2721 FN_OBSESS_MAND2, Integer, 

2722 comment="Obsessions: same thoughts repeating or general worries (1 " 

2723 "same thoughts over and over, 2 worrying about something in " 

2724 "general)", 

2725 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2726 ) 

2727 obsess1 = CamcopsColumn( 

2728 FN_OBSESS1, Integer, 

2729 comment="Obsessions: how many days in past week" + CMT_DAYS_PER_WEEK, 

2730 permitted_value_checker=ONE_TO_THREE_CHECKER, 

2731 ) 

2732 obsess2 = CamcopsColumn( 

2733 FN_OBSESS2, Integer, 

2734 comment="Obsessions: tried to stop in past week" + CMT_1_NO_2_YES, 

2735 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2736 ) 

2737 obsess3 = CamcopsColumn( 

2738 FN_OBSESS3, Integer, 

2739 comment="Obsessions: upsetting/annoying in past week" + CMT_1_NO_2_YES, 

2740 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2741 ) 

2742 obsess4 = CamcopsColumn( 

2743 FN_OBSESS4, Integer, 

2744 comment="Obsessions: longest time spent thinking these thoughts, in " 

2745 "past week (1: <15min; 2: >=15min)", 

2746 permitted_value_checker=ONE_TO_TWO_CHECKER, 

2747 ) 

2748 obsess_dur = CamcopsColumn( 

2749 FN_OBSESS_DUR, Integer, 

2750 comment="Obsessions: for how long?" + CMT_DURATION, 

2751 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

2752 ) 

2753 

2754 # Overall impact 

2755 

2756 overall2 = CamcopsColumn( 

2757 FN_OVERALL2, Integer, 

2758 comment="Overall impact on normal activities in past week (1 not at " 

2759 "all; 2 they have made things more difficult but I get " 

2760 "everything done; 3 they have stopped one activity; 4 they " 

2761 "have stopped >1 activity)", 

2762 permitted_value_checker=ONE_TO_FOUR_CHECKER, 

2763 ) 

2764 

2765 # ------------------------------------------------------------------------- 

2766 # Functions 

2767 # ------------------------------------------------------------------------- 

2768 

2769 @staticmethod 

2770 def longname(req: "CamcopsRequest") -> str: 

2771 _ = req.gettext 

2772 return _("Clinical Interview Schedule, Revised") 

2773 

2774 # noinspection PyMethodParameters 

2775 @classproperty 

2776 def minimum_client_version(cls) -> Version: 

2777 return Version("2.2.0") 

2778 

2779 def value_for_question(self, q: CisrQuestion) -> Optional[int]: 

2780 fieldname = fieldname_for_q(q) 

2781 assert fieldname, f"Blank fieldname for question {q}" 

2782 return getattr(self, fieldname) 

2783 

2784 def int_value_for_question(self, q: CisrQuestion) -> int: 

2785 value = self.value_for_question(q) 

2786 return int(value) if value is not None else 0 

2787 

2788 def answer_is_no(self, q: CisrQuestion, value: int = V_UNKNOWN) -> bool: 

2789 if value == V_UNKNOWN: # "Please look it up for me" 

2790 value = self.int_value_for_question(q) 

2791 if q in QUESTIONS_1_NO_2_YES: 

2792 return value == 1 

2793 elif q in QUESTIONS_1_YES_2_NO: 

2794 return value == 2 

2795 else: 

2796 raise ValueError("answer_is_no() called for inappropriate " 

2797 f"question {q}") 

2798 

2799 def answer_is_yes(self, q: CisrQuestion, value: int = V_UNKNOWN) -> bool: 

2800 if value == V_UNKNOWN: # "Please look it up for me" 

2801 value = self.int_value_for_question(q) 

2802 if q in QUESTIONS_1_NO_2_YES: 

2803 return value == 2 

2804 elif q in QUESTIONS_1_YES_2_NO: 

2805 return value == 1 

2806 else: 

2807 raise ValueError("answer_is_yes() called for inappropriate " 

2808 f"question {q}") 

2809 

2810 def answered(self, q: CisrQuestion, value: int = V_UNKNOWN) -> bool: 

2811 if value == V_UNKNOWN: # "Please look it up for me" 

2812 value = self.int_value_for_question(q) 

2813 return value != V_MISSING 

2814 

2815 def get_textual_answer(self, req: CamcopsRequest, 

2816 q: CisrQuestion) -> Optional[str]: 

2817 value = self.value_for_question(q) 

2818 if value is None or value == V_MISSING: 

2819 return None 

2820 if q in QUESTIONS_1_NO_2_YES: 

2821 return get_yes_no(req, value == 2) 

2822 elif q in QUESTIONS_1_YES_2_NO: 

2823 return get_yes_no(req, value == 1) 

2824 elif q in QUESTIONS_PROMPT_ONLY: 

2825 return NOT_APPLICABLE_TEXT 

2826 fieldname = fieldname_for_q(q) 

2827 if (q in QUESTIONS_YN_SPECIFIC_TEXT or 

2828 q in QUESTIONS_MULTIWAY or 

2829 q in QUESTIONS_MULTIWAY_WITH_EXTRA_STEM): 

2830 return self.wxstring(req, fieldname + f"_a{value}") 

2831 elif q in QUESTIONS_OVERALL_DURATION: 

2832 return self.wxstring(req, f"duration_a{value}") 

2833 elif q in QUESTIONS_DAYS_PER_WEEK: 

2834 return self.wxstring(req, f"dpw_a{value}") 

2835 elif q in QUESTIONS_NIGHTS_PER_WEEK: 

2836 return self.wxstring(req, f"npw_a{value}") 

2837 elif q in QUESTIONS_HOW_UNPLEASANT_STANDARD: 

2838 return self.wxstring(req, f"how_unpleasant_a{value}") 

2839 elif q in QUESTIONS_FATIGUE_CAUSES: 

2840 return self.wxstring(req, f"fatigue_causes_a{value}") 

2841 elif q in QUESTIONS_STRESSORS: 

2842 return self.wxstring(req, f"stressors_a{value}") 

2843 elif q in QUESTIONS_NO_SOMETIMES_OFTEN: 

2844 return self.wxstring(req, f"nso_a{value}") 

2845 return f"? [value: {value}]" 

2846 

2847 def next_q(self, q: CisrQuestion, r: CisrResult) -> CisrQuestion: 

2848 # See equivalent in the C++ code. 

2849 # ANY CHANGES HERE MUST BE REFLECTED IN THE C++ CODE AND VICE VERSA. 

2850 

2851 v = V_MISSING # integer value 

2852 if DEBUG_SHOW_QUESTIONS_CONSIDERED: 

2853 r.decide(f"Considering question {q.value}: {q.name}") 

2854 fieldname = fieldname_for_q(q) 

2855 if fieldname: # eliminates prompt-only questions 

2856 var_q = getattr(self, fieldname) # integer-or-NULL value 

2857 if var_q is None: 

2858 if q not in QUESTIONS_DEMOGRAPHICS: 

2859 # From a diagnostic point of view, OK to have missing 

2860 # demographic information. Otherwise: 

2861 r.decide("INCOMPLETE INFORMATION. STOPPING.") 

2862 r.incomplete = True 

2863 else: 

2864 v = int(var_q) 

2865 

2866 next_q = -1 

2867 

2868 def jump_to(qe: CisrQuestion) -> None: 

2869 nonlocal next_q 

2870 next_q = enum_to_int(qe) 

2871 

2872 # If there is no special handling for a question, then after the 

2873 # switch() statement we will move to the next question in sequence. 

2874 # So only special "skip" situations are handled here. 

2875 

2876 # FOLLOW THE EXACT SEQUENCE of the CIS-R. Don't agglomerate case 

2877 # statements just because it's shorter (except empty ones when they are 

2878 # in sequence). Clarity is key. 

2879 

2880 # --------------------------------------------------------------------- 

2881 # Demographics/preamble 

2882 # --------------------------------------------------------------------- 

2883 

2884 if q in QUESTIONS_DEMOGRAPHICS or q in QUESTIONS_PROMPT_ONLY: 

2885 # Nothing special 

2886 pass 

2887 # Note that this makes some of the other prompt-only checks 

2888 # below redundant! Still, it's quicker. The C++ version uses 

2889 # switch() instead. 

2890 

2891 # -------------------------------------------------------------------- 

2892 # Appetite/weight 

2893 # -------------------------------------------------------------------- 

2894 

2895 elif q == CQ.APPETITE1_LOSS_PAST_MONTH: 

2896 if self.answer_is_no(q, v): 

2897 r.decide("No loss of appetite in past month.") 

2898 jump_to(CQ.APPETITE2_INCREASE_PAST_MONTH) 

2899 elif self.answer_is_yes(q, v): 

2900 r.decide("Loss of appetite in past month. " 

2901 "Incrementing depr_crit_3_somatic_synd.") 

2902 r.depr_crit_3_somatic_synd += 1 

2903 r.weight_change = WTCHANGE_APPETITE_LOSS 

2904 

2905 elif q == CQ.WEIGHT1_LOSS_PAST_MONTH: 

2906 if self.answer_is_no(q, v): 

2907 r.decide("No weight loss.") 

2908 jump_to(CQ.GP_YEAR) 

2909 

2910 elif q == CQ.WEIGHT2_TRYING_TO_LOSE: 

2911 if v == V_WEIGHT2_WTLOSS_TRYING: 

2912 # Trying to lose weight. Move on. 

2913 r.decide("Weight loss but it was deliberate.") 

2914 elif v == V_WEIGHT2_WTLOSS_NOTTRYING: 

2915 r.decide("Non-deliberate weight loss.") 

2916 r.weight_change = WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN 

2917 

2918 elif q == CQ.WEIGHT3_LOST_LOTS: 

2919 if v == V_WEIGHT3_WTLOSS_GE_HALF_STONE: 

2920 r.decide("Weight loss ≥0.5st in past month. " 

2921 "Incrementing depr_crit_3_somatic_synd.") 

2922 r.weight_change = WTCHANGE_WTLOSS_GE_HALF_STONE 

2923 r.depr_crit_3_somatic_synd += 1 

2924 r.decide("Loss of weight, so skipping appetite/weight gain " 

2925 "questions.") 

2926 jump_to(CQ.GP_YEAR) 

2927 

2928 elif q == CQ.APPETITE2_INCREASE_PAST_MONTH: 

2929 if self.answer_is_no(q, v): 

2930 r.decide("No increase in appetite in past month.") 

2931 jump_to(CQ.GP_YEAR) 

2932 

2933 elif q == CQ.WEIGHT4_INCREASE_PAST_MONTH: 

2934 if self.answer_is_yes(q, v): 

2935 r.decide("Weight gain.") 

2936 r.weight_change = WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN 

2937 elif self.answered(q, v): 

2938 r.decide("No weight gain, or weight gain but pregnant.") 

2939 jump_to(CQ.GP_YEAR) 

2940 

2941 elif q == CQ.WEIGHT5_GAINED_LOTS: 

2942 if (v == V_WEIGHT5_WTGAIN_GE_HALF_STONE and 

2943 r.weight_change == WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN): # noqa 

2944 # ... redundant check on weight_change, I think! 

2945 r.decide("Weight gain ≥0.5 st in past month.") 

2946 r.weight_change = WTCHANGE_WTGAIN_GE_HALF_STONE 

2947 

2948 # -------------------------------------------------------------------- 

2949 # Somatic symptoms 

2950 # -------------------------------------------------------------------- 

2951 

2952 elif q == CQ.GP_YEAR: 

2953 # Score the preceding block: 

2954 if (r.weight_change == WTCHANGE_WTLOSS_GE_HALF_STONE and 

2955 self.answer_is_yes(CQ.APPETITE1_LOSS_PAST_MONTH)): 

2956 r.decide( 

2957 "Appetite loss and weight loss ≥0.5st in past month. " 

2958 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

2959 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

2960 if (r.weight_change == WTCHANGE_WTGAIN_GE_HALF_STONE and 

2961 self.answer_is_yes(CQ.APPETITE2_INCREASE_PAST_MONTH)): 

2962 r.decide( 

2963 "Appetite gain and weight gain ≥0.5st in past month. " 

2964 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

2965 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

2966 

2967 elif q == CQ.DISABLE: 

2968 if self.answer_is_no(q): 

2969 r.decide("No longstanding illness/disability/infirmity.") 

2970 jump_to(CQ.SOMATIC_MAND1_PAIN_PAST_MONTH) 

2971 

2972 elif q == CQ.ILLNESS: 

2973 pass 

2974 

2975 elif q == CQ.SOMATIC_MAND1_PAIN_PAST_MONTH: 

2976 if self.answer_is_no(q): 

2977 r.decide("No aches/pains in past month.") 

2978 jump_to(CQ.SOMATIC_MAND2_DISCOMFORT) 

2979 

2980 elif q == CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: 

2981 if v == V_SOMATIC_PAIN1_NEVER: 

2982 r.decide("Pains never exacerbated by low mood/anxiety/stress.") 

2983 jump_to(CQ.SOMATIC_MAND2_DISCOMFORT) 

2984 

2985 elif q == CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK: 

2986 if v == V_DAYS_IN_PAST_WEEK_0: 

2987 r.decide("No pain in last 7 days.") 

2988 jump_to(CQ.SOMATIC_MAND2_DISCOMFORT) 

2989 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

2990 r.decide("Pain on >=4 of last 7 days. " 

2991 "Incrementing somatic_symptoms.") 

2992 r.somatic_symptoms += 1 

2993 

2994 elif q == CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY: 

2995 if self.answer_is_yes(q, v): 

2996 r.decide("Pain for >3h on any day in past week. " 

2997 "Incrementing somatic_symptoms.") 

2998 r.somatic_symptoms += 1 

2999 

3000 elif q == CQ.SOMATIC_PAIN4_UNPLEASANT: 

3001 if v >= V_HOW_UNPLEASANT_UNPLEASANT: 

3002 r.decide("Pain 'unpleasant' or worse in past week. " 

3003 "Incrementing somatic_symptoms.") 

3004 r.somatic_symptoms += 1 

3005 

3006 elif q == CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: 

3007 if self.answer_is_yes(q, v): 

3008 r.decide("Pain interrupted an interesting activity in past " 

3009 "week. " 

3010 "Incrementing somatic_symptoms.") 

3011 r.somatic_symptoms += 1 

3012 r.decide("There was pain, so skip 'discomfort' section.") 

3013 jump_to(CQ.SOMATIC_DUR) # skip SOMATIC_MAND2 

3014 

3015 elif q == CQ.SOMATIC_MAND2_DISCOMFORT: 

3016 if self.answer_is_no(q, v): 

3017 r.decide("No discomfort.") 

3018 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH) 

3019 

3020 elif q == CQ.SOMATIC_DIS1_PSYCHOL_EXAC: 

3021 if v == V_SOMATIC_DIS1_NEVER: 

3022 r.decide("Discomfort never exacerbated by being " 

3023 "low/anxious/stressed.") 

3024 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH) 

3025 

3026 elif q == CQ.SOMATIC_DIS2_DAYS_PAST_WEEK: 

3027 if v == V_DAYS_IN_PAST_WEEK_0: 

3028 r.decide("No discomfort in last 7 days.") 

3029 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH) 

3030 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3031 r.decide("Discomfort on >=4 days in past week. " 

3032 "Incrementing somatic_symptoms.") 

3033 r.somatic_symptoms += 1 

3034 

3035 elif q == CQ.SOMATIC_DIS3_GT_3H_ANY_DAY: 

3036 if self.answer_is_yes(q, v): 

3037 r.decide("Discomfort for >3h on any day in past week. " 

3038 "Incrementing somatic_symptoms.") 

3039 r.somatic_symptoms += 1 

3040 

3041 elif q == CQ.SOMATIC_DIS4_UNPLEASANT: 

3042 if v >= V_HOW_UNPLEASANT_UNPLEASANT: 

3043 r.decide("Discomfort 'unpleasant' or worse in past week. " 

3044 "Incrementing somatic_symptoms.") 

3045 r.somatic_symptoms += 1 

3046 

3047 elif q == CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: 

3048 if self.answer_is_yes(q, v): 

3049 r.decide("Discomfort interrupted an interesting activity in " 

3050 "past " 

3051 "week. Incrementing somatic_symptoms.") 

3052 r.somatic_symptoms += 1 

3053 

3054 # -------------------------------------------------------------------- 

3055 # Fatigue/energy 

3056 # -------------------------------------------------------------------- 

3057 

3058 elif q == CQ.FATIGUE_MAND1_TIRED_PAST_MONTH: 

3059 if self.answer_is_no(q, v): 

3060 r.decide("Not tired.") 

3061 jump_to(CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH) 

3062 

3063 elif q == CQ.FATIGUE_CAUSE1_TIRED: 

3064 if v == V_FATIGUE_CAUSE_EXERCISE: 

3065 r.decide("Tired due to exercise. Move on.") 

3066 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) 

3067 

3068 elif q == CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK: 

3069 if v == V_DAYS_IN_PAST_WEEK_0: 

3070 r.decide("Not tired in past week.") 

3071 jump_to(CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH) 

3072 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3073 r.decide("Tired on >=4 days in past week. " 

3074 "Incrementing fatigue.") 

3075 r.fatigue += 1 

3076 

3077 elif q == CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY: 

3078 if self.answer_is_yes(q, v): 

3079 r.decide("Tired for >3h on any day in past week. " 

3080 "Incrementing fatigue.") 

3081 r.fatigue += 1 

3082 

3083 elif q == CQ.FATIGUE_TIRED3_HAD_TO_PUSH: 

3084 if self.answer_is_yes(q, v): 

3085 r.decide("Tired enough to have to push self during past week. " 

3086 "Incrementing fatigue.") 

3087 r.fatigue += 1 

3088 

3089 elif q == CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: 

3090 if self.answer_is_yes(q, v): 

3091 r.decide("Tired during an enjoyable activity during past " 

3092 "week. " 

3093 "Incrementing fatigue.") 

3094 r.fatigue += 1 

3095 r.decide("There was tiredness, so skip 'lack of energy' section.") 

3096 jump_to(CQ.FATIGUE_DUR) # skip FATIGUE_MAND2 

3097 

3098 elif q == CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH: 

3099 if self.answer_is_no(q, v): 

3100 r.decide("Not lacking in energy.") 

3101 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) 

3102 

3103 elif q == CQ.FATIGUE_CAUSE2_LACK_ENERGY: 

3104 if v == V_FATIGUE_CAUSE_EXERCISE: 

3105 r.decide("Lacking in energy due to exercise. Move on.") 

3106 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) 

3107 

3108 elif q == CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK: 

3109 if v == V_DAYS_IN_PAST_WEEK_0: 

3110 r.decide("Not lacking in energy during last week.") 

3111 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) 

3112 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3113 r.decide("Lacking in energy on >=4 days in past week. " 

3114 "Incrementing fatigue.") 

3115 r.fatigue += 1 

3116 

3117 elif q == CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY: 

3118 if self.answer_is_yes(q, v): 

3119 r.decide("Lacking in energy for >3h on any day in past week. " 

3120 "Incrementing fatigue.") 

3121 r.fatigue += 1 

3122 

3123 elif q == CQ.FATIGUE_ENERGY3_HAD_TO_PUSH: 

3124 if self.answer_is_yes(q, v): 

3125 r.decide("Lacking in energy enough to have to push self during " 

3126 "past week. Incrementing fatigue.") 

3127 r.fatigue += 1 

3128 

3129 elif q == CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: 

3130 if self.answer_is_yes(q, v): 

3131 r.decide("Lacking in energy during an enjoyable activity " 

3132 "during " 

3133 "past week. Incrementing fatigue.") 

3134 r.fatigue += 1 

3135 

3136 elif q == CQ.FATIGUE_DUR: 

3137 # Score preceding: 

3138 if r.somatic_symptoms >= 2 and r.fatigue >= 2: 

3139 r.decide("somatic >= 2 and fatigue >= 2. " 

3140 "Incrementing neurasthenia.") 

3141 r.neurasthenia += 1 

3142 

3143 # -------------------------------------------------------------------- 

3144 # Concentration/memory 

3145 # -------------------------------------------------------------------- 

3146 

3147 elif q == CQ.CONC_MAND1_POOR_CONC_PAST_MONTH: 

3148 # Score preceding: 

3149 if r.fatigue >= 2: 

3150 r.decide("fatigue >= 2. " 

3151 "Incrementing depr_crit_1_mood_anhedonia_energy.") 

3152 r.depr_crit_1_mood_anhedonia_energy += 1 

3153 

3154 elif q == CQ.CONC_MAND2_FORGETFUL_PAST_MONTH: 

3155 if (self.answer_is_no(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) 

3156 and self.answer_is_no(q, v)): 

3157 r.decide("No problems with concentration or forgetfulness.") 

3158 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH) 

3159 

3160 elif q == CQ.CONC1_CONC_DAYS_PAST_WEEK: 

3161 if v == V_DAYS_IN_PAST_WEEK_0: 

3162 r.decide("No concentration/memory problems in past week.") 

3163 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH) 

3164 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3165 r.decide("Problems with concentration/memory problems on >=4 " 

3166 "days in past week. Incrementing concentration_poor.") 

3167 r.concentration_poor += 1 

3168 if (self.answer_is_no(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH) and 

3169 self.answer_is_yes(CQ.CONC_MAND2_FORGETFUL_PAST_MONTH)): 

3170 r.decide("Forgetfulness, not concentration, problems; skip " 

3171 "over more detailed concentration questions.") 

3172 jump_to(CQ.CONC4_FORGOTTEN_IMPORTANT) # skip CONC2, CONC3, CONC_DUR # noqa 

3173 

3174 elif q == CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION: 

3175 if self.answer_is_no(q, v): 

3176 r.decide("Couldn't concentrate on at least one of {TV, " 

3177 "newspaper, " 

3178 "conversation}. Incrementing concentration_poor.") 

3179 r.concentration_poor += 1 

3180 

3181 elif q == CQ.CONC3_CONC_PREVENTED_ACTIVITIES: 

3182 if self.answer_is_yes(q, v): 

3183 r.decide("Problems with concentration stopped usual/desired " 

3184 "activity. Incrementing concentration_poor.") 

3185 r.concentration_poor += 1 

3186 

3187 elif q == CQ.CONC_DUR: 

3188 if self.answer_is_no(CQ.CONC_MAND2_FORGETFUL_PAST_MONTH): 

3189 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH) 

3190 

3191 elif q == CQ.CONC4_FORGOTTEN_IMPORTANT: 

3192 if self.answer_is_yes(q, v): 

3193 r.decide("Forgotten something important in past week. " 

3194 "Incrementing concentration_poor.") 

3195 r.concentration_poor += 1 

3196 

3197 elif q == CQ.FORGET_DUR: 

3198 pass 

3199 

3200 # -------------------------------------------------------------------- 

3201 # Sleep 

3202 # -------------------------------------------------------------------- 

3203 

3204 elif q == CQ.SLEEP_MAND1_LOSS_PAST_MONTH: 

3205 # Score previous block: 

3206 if r.concentration_poor >= 2: 

3207 r.decide( 

3208 "concentration >= 2. " 

3209 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

3210 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

3211 # This question: 

3212 if self.answer_is_no(q, v): 

3213 r.decide("No problems with sleep loss in past month. " 

3214 "Moving on.") 

3215 jump_to(CQ.SLEEP_MAND2_GAIN_PAST_MONTH) 

3216 

3217 elif q == CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK: 

3218 if v == V_NIGHTS_IN_PAST_WEEK_0: 

3219 r.decide("No problems with sleep in past week. Moving on.") 

3220 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH) 

3221 elif v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE: 

3222 r.decide("Problems with sleep on >=4 nights in past week. " 

3223 "Incrementing sleep_problems.") 

3224 r.sleep_problems += 1 

3225 

3226 elif q == CQ.SLEEP_LOSE2_DIS_WORST_DURATION: 

3227 if v == V_SLEEP_CHANGE_LT_15_MIN: 

3228 r.decide("Less than 15min maximum delayed initiation of sleep " 

3229 "in past week. Moving on.") 

3230 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH) 

3231 elif v == V_SLEEP_CHANGE_15_MIN_TO_1_H: 

3232 r.decide("15min-1h maximum delayed initiation of sleep in past " 

3233 "week. Incrementing sleep_problems.") 

3234 r.sleep_problems += 1 

3235 elif v == V_SLEEP_CHANGE_1_TO_3_H or v == V_SLEEP_CHANGE_GT_3_H: 

3236 r.decide(">=1h maximum delayed initiation of sleep in past " 

3237 "week. Adding 2 to sleep_problems.") 

3238 r.sleep_problems += 2 

3239 

3240 elif q == CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK: 

3241 if v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE: 

3242 r.decide(">=4 nights in past week with >=3h delayed " 

3243 "initiation of " 

3244 "sleep. Incrementing sleep_problems.") 

3245 r.sleep_problems += 1 

3246 

3247 elif q == CQ.SLEEP_EMW_PAST_WEEK: 

3248 if self.answer_is_yes(q, v): 

3249 r.decide("EMW of >2h in past week. " 

3250 "Setting sleep_change to SLEEPCHANGE_EMW. " 

3251 "Incrementing depr_crit_3_somatic_synd.") 

3252 # Was: SLEEPCH += answer - 1 (which only does anything for a 

3253 # "yes" (2) answer). 

3254 # ... but at this point, SLEEPCH is always 0. 

3255 r.sleep_change = SLEEPCHANGE_EMW # LIKELY REDUNDANT. 

3256 r.depr_crit_3_somatic_synd += 1 

3257 if r.sleep_problems >= 1: 

3258 r.decide("EMW of >2h in past week and sleep_problems >= 1; " 

3259 "setting sleep_change to SLEEPCHANGE_EMW.") 

3260 r.sleep_change = SLEEPCHANGE_EMW 

3261 elif self.answer_is_no(q, v): 

3262 r.decide("No EMW of >2h in past week.") 

3263 if r.sleep_problems >= 1: 

3264 r.decide("No EMW of >2h in past week, and sleep_problems " 

3265 ">= 1. Setting sleep_change to " 

3266 "SLEEPCHANGE_INSOMNIA_NOT_EMW.") 

3267 r.sleep_change = SLEEPCHANGE_INSOMNIA_NOT_EMW 

3268 

3269 elif q == CQ.SLEEP_CAUSE: 

3270 r.decide("Problems with sleep loss; skipping over sleep gain.") 

3271 jump_to(CQ.SLEEP_DUR) 

3272 

3273 elif q == CQ.SLEEP_MAND2_GAIN_PAST_MONTH: 

3274 if (v == V_SLEEP_MAND2_NO or 

3275 v == V_SLEEP_MAND2_YES_BUT_NOT_A_PROBLEM): 

3276 r.decide("No problematic sleep gain. Moving on.") 

3277 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH) 

3278 

3279 elif q == CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK: 

3280 if v == V_NIGHTS_IN_PAST_WEEK_0: 

3281 r.decide("No nights with sleep problems [gain] in past week.") 

3282 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH) 

3283 elif v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE: 

3284 r.decide("Problems with sleep [gain] on >=4 nights in past " 

3285 "week. Incrementing sleep_problems.") 

3286 r.sleep_problems += 1 

3287 

3288 elif q == CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: 

3289 if v == V_SLEEP_CHANGE_LT_15_MIN: 

3290 r.decide("Sleep gain <15min. Moving on.") 

3291 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH) 

3292 elif v == V_SLEEP_CHANGE_15_MIN_TO_1_H: 

3293 r.decide("Sleep gain 15min-1h. Incrementing sleep_problems.") 

3294 r.sleep_problems += 1 

3295 elif v >= V_SLEEP_CHANGE_1_TO_3_H: 

3296 r.decide("Sleep gain >=1h. " 

3297 "Adding 2 to sleep_problems. " 

3298 "Setting sleep_change to SLEEPCHANGE_INCREASE.") 

3299 r.sleep_problems += 2 

3300 r.sleep_change = SLEEPCHANGE_INCREASE 

3301 # Note that in the original, if the answer was 3 

3302 # (V_SLEEP_CHANGE_1_TO_3_H) or greater, first 2 was added to 

3303 # sleep, and then if sleep was >=1, sleepch [sleep_change] was set # noqa 

3304 # to 3. However, sleep is never decremented/set below 0, so that # noqa 

3305 # was a redundant test (always true). 

3306 

3307 elif q == CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK: 

3308 if v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE: 

3309 r.decide("Sleep gain of >3h on >=4 nights in past week. " 

3310 "Incrementing sleep_problems.") 

3311 r.sleep_problems += 1 

3312 

3313 elif q == CQ.SLEEP_DUR: 

3314 pass 

3315 

3316 # -------------------------------------------------------------------- 

3317 # Irritability 

3318 # -------------------------------------------------------------------- 

3319 

3320 elif q == CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH: 

3321 # Score previous block: 

3322 if r.sleep_problems >= 2: 

3323 r.decide( 

3324 "sleep_problems >= 2. " 

3325 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

3326 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

3327 # This bit erroneously lived under IRRIT_DUR in the original; see 

3328 # discussion there: 

3329 if r.sleep_problems >= 2 and r.fatigue >= 2: 

3330 r.decide("sleep_problems >=2 and fatigue >=2. " 

3331 "Incrementing neurasthenia.") 

3332 r.neurasthenia += 1 

3333 # This question: 

3334 if self.answer_is_yes(q, v): 

3335 r.decide("Irritability (people) in past month; exploring " 

3336 "further.") 

3337 jump_to(CQ.IRRIT1_DAYS_PER_WEEK) 

3338 

3339 elif q == CQ.IRRIT_MAND2_THINGS_PAST_MONTH: 

3340 if v == V_IRRIT_MAND2_NO: 

3341 r.decide("No irritability. Moving on.") 

3342 jump_to(CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH) 

3343 elif self.answered(q, v): 

3344 r.decide("Irritability (things) in past month; exploring " 

3345 "further.") 

3346 

3347 elif q == CQ.IRRIT1_DAYS_PER_WEEK: 

3348 if v == V_DAYS_IN_PAST_WEEK_0: 

3349 r.decide("No irritability in past week. Moving on.") 

3350 jump_to(CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH) 

3351 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3352 r.decide("Irritable on >=4 days in past week. " 

3353 "Incrementing irritability.") 

3354 r.irritability += 1 

3355 

3356 elif q == CQ.IRRIT2_GT_1H_ANY_DAY: 

3357 if self.answer_is_yes(q, v): 

3358 r.decide("Irritable for >1h on any day in past week. " 

3359 "Incrementing irritability.") 

3360 r.irritability += 1 

3361 

3362 elif q == CQ.IRRIT3_WANTED_TO_SHOUT: 

3363 if v >= V_IRRIT3_SHOUTING_WANTED_TO: 

3364 r.decide("Wanted to or did shout. Incrementing irritability.") 

3365 r.irritability += 1 

3366 

3367 elif q == CQ.IRRIT4_ARGUMENTS: 

3368 if v == V_IRRIT4_ARGUMENTS_YES_UNJUSTIFIED: 

3369 r.decide("Arguments without justification. " 

3370 "Incrementing irritability.") 

3371 r.irritability += 1 

3372 

3373 elif q == CQ.IRRIT_DUR: 

3374 # Score recent things: 

3375 if r.irritability >= 2 and r.fatigue >= 2: 

3376 r.decide("irritability >=2 and fatigue >=2. " 

3377 "Incrementing neurasthenia.") 

3378 r.neurasthenia += 1 

3379 # In the original, we had the rule "sleep_problems >=2 and 

3380 # fatigue >=2 -> incrementing neurasthenia" here, but that would mean # noqa 

3381 # we would fail to score sleep if the patient didn't report 

3382 # irritability (because if you say no at IRRIT_MAND2, you jump beyond # noqa 

3383 # this point to HYPO_MAND1). Checked with Glyn Lewis 2017-12-04, who # noqa 

3384 # agreed on 2017-12-05. Therefore, moved to IRRIT_MAND1 as above. 

3385 # Note that the only implication would have been potential small 

3386 # mis-scoring of the CFS criterion (not any of the diagnoses that 

3387 # the CIS-R reports as its primary/secondary diagnoses). 

3388 

3389 # -------------------------------------------------------------------- 

3390 # Hypochondriasis 

3391 # -------------------------------------------------------------------- 

3392 

3393 elif q == CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH: 

3394 if self.answer_is_yes(q, v): 

3395 r.decide("No worries about physical health in past month. " 

3396 "Moving on.") 

3397 jump_to(CQ.HYPO1_DAYS_PAST_WEEK) 

3398 

3399 elif q == CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS: 

3400 if self.answer_is_no(q, v): 

3401 r.decide("No worries about having a serious illness. " 

3402 "Moving on.") 

3403 jump_to(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH) 

3404 

3405 elif q == CQ.HYPO1_DAYS_PAST_WEEK: 

3406 if v == V_DAYS_IN_PAST_WEEK_0: 

3407 r.decide("No days in past week worrying about health. " 

3408 "Moving on.") 

3409 jump_to(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH) 

3410 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3411 r.decide("Worries about health on >=4 days in past week. " 

3412 "Incrementing hypochondria.") 

3413 r.hypochondria += 1 

3414 

3415 elif q == CQ.HYPO2_WORRY_TOO_MUCH: 

3416 if self.answer_is_yes(q, v): 

3417 r.decide("Worrying too much about health. " 

3418 "Incrementing hypochondria.") 

3419 r.hypochondria += 1 

3420 

3421 elif q == CQ.HYPO3_HOW_UNPLEASANT: 

3422 if v >= V_HOW_UNPLEASANT_UNPLEASANT: 

3423 r.decide("Worrying re health 'unpleasant' or worse in past " 

3424 "week. Incrementing hypochondria.") 

3425 r.hypochondria += 1 

3426 

3427 elif q == CQ.HYPO4_CAN_DISTRACT: 

3428 if self.answer_is_no(q, v): 

3429 r.decide("Cannot take mind off health worries by doing " 

3430 "something else. Incrementing hypochondria.") 

3431 r.hypochondria += 1 

3432 

3433 elif q == CQ.HYPO_DUR: 

3434 pass 

3435 

3436 # -------------------------------------------------------------------- 

3437 # Depression 

3438 # -------------------------------------------------------------------- 

3439 

3440 elif q == CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH: 

3441 if self.answer_is_no(q, v): 

3442 r.decide("Mood not low in past month. Moving to anhedonia.") 

3443 jump_to(CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH) 

3444 

3445 elif q == CQ.DEPR1_LOW_MOOD_PAST_WEEK: 

3446 pass 

3447 

3448 elif q == CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: 

3449 if (v == V_ANHEDONIA_ENJOYING_NORMALLY and 

3450 self.answer_is_no(CQ.DEPR1_LOW_MOOD_PAST_WEEK)): 

3451 r.decide("Neither low mood nor anhedonia in past month. " 

3452 "Moving on.") 

3453 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH) 

3454 

3455 elif q == CQ.DEPR2_ENJOYMENT_PAST_WEEK: 

3456 if (v == V_ANHEDONIA_ENJOYING_NORMALLY and 

3457 self.answer_is_no(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH)): 

3458 r.decide("No anhedonia in past week and no low mood in past " 

3459 "month. Moving on.") 

3460 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH) 

3461 elif v >= V_ANHEDONIA_ENJOYING_LESS: 

3462 r.decide("Partial or complete anhedonia in past week. " 

3463 "Incrementing depression. " 

3464 "Incrementing depr_crit_1_mood_anhedonia_energy. " 

3465 "Incrementing depr_crit_3_somatic_synd.") 

3466 r.depression += 1 

3467 r.depr_crit_1_mood_anhedonia_energy += 1 

3468 r.depr_crit_3_somatic_synd += 1 

3469 

3470 elif q == CQ.DEPR3_DAYS_PAST_WEEK: 

3471 if v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3472 r.decide("Low mood or anhedonia on >=4 days in past week. " 

3473 "Incrementing depression.") 

3474 r.depression += 1 

3475 

3476 elif q == CQ.DEPR4_GT_3H_ANY_DAY: 

3477 if self.answer_is_yes(q, v): 

3478 r.decide("Low mood or anhedonia for >3h/day on at least one " 

3479 "day in past week. Incrementing depression.") 

3480 r.depression += 1 

3481 if (self.int_value_for_question(CQ.DEPR3_DAYS_PAST_WEEK) and 

3482 self.answer_is_yes(CQ.DEPR1_LOW_MOOD_PAST_WEEK)): 

3483 r.decide("(A) Low mood in past week, and " 

3484 "(B) low mood or anhedonia for >3h/day on at " 

3485 "least one day in past week, and " 

3486 "(C) low mood or anhedonia on >=4 days in past " 

3487 "week. " 

3488 "Incrementing depr_crit_1_mood_anhedonia_energy.") 

3489 r.depr_crit_1_mood_anhedonia_energy += 1 

3490 

3491 elif q == CQ.DEPR_CONTENT: 

3492 pass 

3493 

3494 elif q == CQ.DEPR5_COULD_CHEER_UP: 

3495 if v >= V_DEPR5_COULD_CHEER_UP_SOMETIMES: 

3496 r.decide("'Sometimes' or 'never' cheered up by nice things. " 

3497 "Incrementing depression. " 

3498 "Incrementing depr_crit_3_somatic_synd.") 

3499 r.depression += 1 

3500 r.depr_crit_3_somatic_synd += 1 

3501 

3502 elif q == CQ.DEPR_DUR: 

3503 if v >= V_DURATION_2W_6M: 

3504 r.decide("Depressive symptoms for >=2 weeks. " 

3505 "Setting depression_at_least_2_weeks.") 

3506 r.depression_at_least_2_weeks = True 

3507 # This code was at the start of DEPTH1, but involves skipping over 

3508 # DEPTH1; since we never get to DEPTH1 without coming here, we can 

3509 # move it here: 

3510 if r.depression == 0: 

3511 r.decide("Score for 'depression' is 0; skipping over " 

3512 "depressive thought content questions.") 

3513 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH) 

3514 

3515 elif q == CQ.DEPTH1_DIURNAL_VARIATION: 

3516 if (v == V_DEPTH1_DMV_WORSE_MORNING or 

3517 v == V_DEPTH1_DMV_WORSE_EVENING): 

3518 r.decide("Diurnal mood variation present.") 

3519 r.diurnal_mood_variation = ( 

3520 DIURNAL_MOOD_VAR_WORSE_MORNING 

3521 if v == V_DEPTH1_DMV_WORSE_MORNING 

3522 else DIURNAL_MOOD_VAR_WORSE_EVENING 

3523 ) 

3524 if v == V_DEPTH1_DMV_WORSE_MORNING: 

3525 r.decide("Diurnal mood variation, worse in the mornings. " 

3526 "Incrementing depr_crit_3_somatic_synd.") 

3527 r.depr_crit_3_somatic_synd += 1 

3528 

3529 elif q == CQ.DEPTH2_LIBIDO: 

3530 if v == V_DEPTH2_LIBIDO_DECREASED: 

3531 r.decide("Libido decreased over past month. " 

3532 "Setting libido_decreased. " 

3533 "Incrementing depr_crit_3_somatic_synd.") 

3534 r.libido_decreased = True 

3535 r.depr_crit_3_somatic_synd += 1 

3536 

3537 elif q == CQ.DEPTH3_RESTLESS: 

3538 if self.answer_is_yes(q): 

3539 r.decide("Psychomotor agitation.") 

3540 r.psychomotor_changes = PSYCHOMOTOR_AGITATION 

3541 

3542 elif q == CQ.DEPTH4_SLOWED: 

3543 if self.answer_is_yes(q): 

3544 r.decide("Psychomotor retardation.") 

3545 r.psychomotor_changes = PSYCHOMOTOR_RETARDATION 

3546 if r.psychomotor_changes > PSYCHOMOTOR_NONE: 

3547 r.decide( 

3548 "Psychomotor agitation or retardation. " 

3549 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui. " 

3550 "Incrementing depr_crit_3_somatic_synd.") 

3551 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

3552 r.depr_crit_3_somatic_synd += 1 

3553 

3554 elif q == CQ.DEPTH5_GUILT: 

3555 if v >= V_DEPTH5_GUILT_SOMETIMES: 

3556 r.decide( 

3557 "Feel guilty when not at fault sometimes or often. " 

3558 "Incrementing depressive_thoughts. " 

3559 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

3560 r.depressive_thoughts += 1 

3561 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

3562 

3563 elif q == CQ.DEPTH6_WORSE_THAN_OTHERS: 

3564 if self.answer_is_yes(q, v): 

3565 r.decide( 

3566 "Feeling not as good as other people. " 

3567 "Incrementing depressive_thoughts. " 

3568 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

3569 r.depressive_thoughts += 1 

3570 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

3571 

3572 elif q == CQ.DEPTH7_HOPELESS: 

3573 if self.answer_is_yes(q, v): 

3574 r.decide("Hopelessness. " 

3575 "Incrementing depressive_thoughts. " 

3576 "Setting suicidality to " 

3577 "SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS.") 

3578 r.depressive_thoughts += 1 

3579 r.suicidality = SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS 

3580 

3581 elif q == CQ.DEPTH8_LNWL: 

3582 if v == V_DEPTH8_LNWL_NO: 

3583 r.decide("No thoughts of life not being worth living. " 

3584 "Skipping to end of depression section.") 

3585 jump_to(CQ.DEPR_OUTRO) 

3586 elif v >= V_DEPTH8_LNWL_SOMETIMES: 

3587 r.decide("Sometimes or always feeling life isn't worth living. " 

3588 "Incrementing depressive_thoughts. " 

3589 "Setting suicidality to " 

3590 "SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING.") 

3591 r.depressive_thoughts += 1 

3592 r.suicidality = SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING 

3593 

3594 elif q == CQ.DEPTH9_SUICIDE_THOUGHTS: 

3595 if v == V_DEPTH9_SUICIDAL_THOUGHTS_NO: 

3596 r.decide("No thoughts of suicide. Skipping to end of " 

3597 "depression section.") 

3598 jump_to(CQ.DEPR_OUTRO) 

3599 if v >= V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD: 

3600 r.decide("Suicidal thoughts present. " 

3601 "Setting suicidality to " 

3602 "SUICIDE_INTENT_SUICIDAL_THOUGHTS.") 

3603 r.suicidality = SUICIDE_INTENT_SUICIDAL_THOUGHTS 

3604 if v == V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD: 

3605 r.decide("Suicidal thoughts present but denies would ever act. " 

3606 "Skipping to talk-to-doctor section.") 

3607 jump_to(CQ.DOCTOR) 

3608 if v == V_DEPTH9_SUICIDAL_THOUGHTS_YES: 

3609 r.decide( 

3610 "Thoughts of suicide in past week. " 

3611 "Incrementing depressive_thoughts. " 

3612 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui.") 

3613 r.depressive_thoughts += 1 

3614 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1 

3615 

3616 elif q == CQ.DEPTH10_SUICIDE_METHOD: 

3617 if self.answer_is_yes(q, v): 

3618 r.decide("Suicidal thoughts without denying might ever act. " 

3619 "Setting suicidality to " 

3620 "SUICIDE_INTENT_SUICIDAL_PLANS.") 

3621 r.suicidality = SUICIDE_INTENT_SUICIDAL_PLANS 

3622 

3623 elif q == CQ.DOCTOR: 

3624 if v == V_DOCTOR_YES: 

3625 r.decide("Has spoken to doctor about suicidality. Skipping " 

3626 "exhortation to do so.") 

3627 jump_to(CQ.DEPR_OUTRO) 

3628 

3629 elif q == CQ.DOCTOR2_PLEASE_TALK_TO: 

3630 pass 

3631 

3632 elif q == CQ.DEPR_OUTRO: 

3633 pass 

3634 

3635 # -------------------------------------------------------------------- 

3636 # Worry/anxiety 

3637 # -------------------------------------------------------------------- 

3638 

3639 elif q == CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH: 

3640 if v >= V_NSO_SOMETIMES: 

3641 r.decide("Worrying excessively 'sometimes' or 'often'. " 

3642 "Exploring further.") 

3643 jump_to(CQ.WORRY_CONT1) 

3644 

3645 elif q == CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH: 

3646 if self.answer_is_no(q, v): 

3647 r.decide("No worries at all in the past month. Moving on.") 

3648 jump_to(CQ.ANX_MAND1_ANXIETY_PAST_MONTH) 

3649 

3650 elif q == CQ.WORRY_CONT1: 

3651 pass 

3652 

3653 elif q == CQ.WORRY1_INFO_ONLY: 

3654 pass 

3655 

3656 elif q == CQ.WORRY2_DAYS_PAST_WEEK: 

3657 if v == V_DAYS_IN_PAST_WEEK_0: 

3658 r.decide("Worry [other than re physical health] on 0 days in " 

3659 "past week. Moving on.") 

3660 jump_to(CQ.ANX_MAND1_ANXIETY_PAST_MONTH) 

3661 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3662 r.decide("Worry [other than re physical health] on >=4 days in " 

3663 "past week. Incrementing worry.") 

3664 r.worry += 1 

3665 

3666 elif q == CQ.WORRY3_TOO_MUCH: 

3667 if self.answer_is_yes(q, v): 

3668 r.decide("Worrying too much. Incrementing worry.") 

3669 r.worry += 1 

3670 

3671 elif q == CQ.WORRY4_HOW_UNPLEASANT: 

3672 if v >= V_HOW_UNPLEASANT_UNPLEASANT: 

3673 r.decide("Worry [other than re physical health] 'unpleasant' " 

3674 "or worse in past week. Incrementing worry.") 

3675 r.worry += 1 

3676 

3677 elif q == CQ.WORRY5_GT_3H_ANY_DAY: 

3678 if self.answer_is_yes(q, v): 

3679 r.decide("Worry [other than re physical health] for >3h on any " 

3680 "day in past week. Incrementing worry.") 

3681 r.worry += 1 

3682 

3683 elif q == CQ.WORRY_DUR: 

3684 pass 

3685 

3686 elif q == CQ.ANX_MAND1_ANXIETY_PAST_MONTH: 

3687 if self.answer_is_yes(q, v): 

3688 r.decide("Anxious/nervous in past month. " 

3689 "Skipping tension question.") 

3690 jump_to(CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH) 

3691 

3692 elif q == CQ.ANX_MAND2_TENSION_PAST_MONTH: 

3693 if v == V_NSO_NO: 

3694 r.decide("No tension in past month (and no anxiety, from " 

3695 "previous question). Moving on.") 

3696 jump_to(CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH) 

3697 

3698 elif q == CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH: 

3699 if self.answer_is_no(q, v): 

3700 r.decide("No phobias. Moving on to general anxiety.") 

3701 jump_to(CQ.ANX2_GENERAL_DAYS_PAST_WEEK) 

3702 elif self.answer_is_yes(q, v): 

3703 # This was in ANX_PHOBIA2; PHOBIAS_FLAG was set by arriving 

3704 # there (but that only happens when we get a 'yes' answer here). 

3705 r.decide("Phobias. Exploring further. Setting phobias flag.") 

3706 r.phobias_flag = True 

3707 

3708 elif q == CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: 

3709 if v == V_ANX_PHOBIA2_ALWAYS_SPECIFIC: 

3710 r.decide("Anxiety always specific. " 

3711 "Skipping generalized anxiety.") 

3712 jump_to(CQ.PHOBIAS_TYPE1) 

3713 

3714 elif q == CQ.ANX1_INFO_ONLY: 

3715 pass 

3716 

3717 elif q == CQ.ANX2_GENERAL_DAYS_PAST_WEEK: 

3718 if v == V_DAYS_IN_PAST_WEEK_0: 

3719 if r.phobias_flag: 

3720 r.decide("No generalized anxiety in past week. " 

3721 "Skipping further generalized anxiety questions.") 

3722 jump_to(CQ.PHOBIAS1_DAYS_PAST_WEEK) 

3723 else: 

3724 r.decide("No generalized anxiety in past week. Moving on.") 

3725 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH) 

3726 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3727 r.decide("Generalized anxiety on >=4 days in past week. " 

3728 "Incrementing anxiety.") 

3729 r.anxiety += 1 

3730 

3731 elif q == CQ.ANX3_GENERAL_HOW_UNPLEASANT: 

3732 if v >= V_HOW_UNPLEASANT_UNPLEASANT: 

3733 r.decide("Anxiety 'unpleasant' or worse in past week. " 

3734 "Incrementing anxiety.") 

3735 r.anxiety += 1 

3736 

3737 elif q == CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS: 

3738 if self.answer_is_yes(q, v): 

3739 r.decide("Physical symptoms of anxiety. " 

3740 "Setting anxiety_physical_symptoms. " 

3741 "Incrementing anxiety.") 

3742 r.anxiety_physical_symptoms = True 

3743 r.anxiety += 1 

3744 

3745 elif q == CQ.ANX5_GENERAL_GT_3H_ANY_DAY: 

3746 if self.answer_is_yes(q, v): 

3747 r.decide("Anxiety for >3h on any day in past week. " 

3748 "Incrementing anxiety.") 

3749 r.anxiety += 1 

3750 

3751 elif q == CQ.ANX_DUR_GENERAL: 

3752 if v >= V_DURATION_2W_6M: 

3753 r.decide("Anxiety for >=2 weeks. " 

3754 "Setting anxiety_at_least_2_weeks.") 

3755 r.anxiety_at_least_2_weeks = True 

3756 if r.phobias_flag: 

3757 r.decide("Phobias flag set. Exploring further.") 

3758 jump_to(CQ.PHOBIAS_TYPE1) 

3759 else: 

3760 if r.anxiety <= 1: 

3761 r.decide("Anxiety score <=1. Moving on to compulsions.") 

3762 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH) 

3763 else: 

3764 r.decide("Anxiety score >=2. Exploring panic.") 

3765 jump_to(CQ.PANIC_MAND_PAST_MONTH) 

3766 

3767 elif q == CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH: 

3768 if self.answer_is_no(q, v): 

3769 if r.anxiety <= 1: 

3770 r.decide("Anxiety score <=1. Moving on to compulsions.") 

3771 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH) 

3772 else: 

3773 r.decide("Anxiety score >=2. Exploring panic.") 

3774 jump_to(CQ.PANIC_MAND_PAST_MONTH) 

3775 

3776 elif q == CQ.PHOBIAS_TYPE1: 

3777 if v in [V_PHOBIAS_TYPE1_ALONE_PUBLIC_TRANSPORT, 

3778 V_PHOBIAS_TYPE1_FAR_FROM_HOME, 

3779 V_PHOBIAS_TYPE1_CROWDED_SHOPS]: 

3780 r.decide("Phobia type category: agoraphobia.") 

3781 r.phobias_type = PHOBIATYPES_AGORAPHOBIA 

3782 

3783 elif v in [V_PHOBIAS_TYPE1_PUBLIC_SPEAKING_EATING, 

3784 V_PHOBIAS_TYPE1_BEING_WATCHED]: 

3785 r.decide("Phobia type category: social.") 

3786 r.phobias_type = PHOBIATYPES_SOCIAL 

3787 

3788 elif v == V_PHOBIAS_TYPE1_BLOOD: 

3789 r.decide("Phobia type category: blood/injury.") 

3790 r.phobias_type = PHOBIATYPES_BLOOD_INJURY 

3791 

3792 elif v in [V_PHOBIAS_TYPE1_ANIMALS, 

3793 V_PHOBIAS_TYPE1_ENCLOSED_SPACES_HEIGHTS]: 

3794 r.decide("Phobia type category: animals/enclosed spaces/" 

3795 "heights.") 

3796 r.phobias_type = PHOBIATYPES_ANIMALS_ENCLOSED_HEIGHTS 

3797 

3798 elif v == V_PHOBIAS_TYPE1_OTHER: 

3799 r.decide("Phobia type category: other.") 

3800 r.phobias_type = PHOBIATYPES_OTHER 

3801 

3802 else: 

3803 pass 

3804 

3805 elif q == CQ.PHOBIAS1_DAYS_PAST_WEEK: 

3806 if v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3807 r.decide("Phobic anxiety on >=4 days in past week. " 

3808 "Incrementing phobias_score.") 

3809 r.phobias_score += 1 

3810 

3811 elif q == CQ.PHOBIAS2_PHYSICAL_SYMPTOMS: 

3812 if self.answer_is_yes(q, v): 

3813 r.decide("Physical symptoms during phobic anxiety in past " 

3814 "week. Incrementing phobias_score.") 

3815 r.phobias_score += 1 

3816 

3817 elif q == CQ.PHOBIAS3_AVOIDANCE: 

3818 if self.answer_is_no(q, v): # no avoidance in past week 

3819 if r.anxiety <= 1 and r.phobias_score == 0: 

3820 r.decide("No avoidance in past week; " 

3821 "anxiety <= 1 and phobias_score == 0. " 

3822 "Finishing anxiety section.") 

3823 jump_to(CQ.ANX_OUTRO) 

3824 else: 

3825 r.decide("No avoidance in past week; " 

3826 "anxiety >= 2 or phobias_score >= 1. " 

3827 "Moving to panic section.") 

3828 jump_to(CQ.PANIC_MAND_PAST_MONTH) 

3829 elif self.answer_is_yes(q, v): 

3830 r.decide("Setting phobic_avoidance.") 

3831 r.phobic_avoidance = True 

3832 

3833 elif q == CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: 

3834 if v == V_DAYS_IN_PAST_WEEK_1_TO_3: 

3835 r.decide("Phobic avoidance on 1-3 days in past week. " 

3836 "Incrementing phobias_score.") 

3837 r.phobias_score += 1 

3838 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3839 r.decide("Phobic avoidance on >=4 days in past week. " 

3840 "Adding 2 to phobias_score.") 

3841 r.phobias_score += 2 

3842 if (r.anxiety <= 1 and 

3843 self.int_value_for_question(CQ.PHOBIAS1_DAYS_PAST_WEEK) == 

3844 V_DAYS_IN_PAST_WEEK_0): 

3845 r.decide("anxiety <= 1 and no phobic anxiety in past week. " 

3846 "Finishing anxiety section.") 

3847 jump_to(CQ.ANX_OUTRO) 

3848 

3849 elif q == CQ.PHOBIAS_DUR: 

3850 pass 

3851 

3852 elif q == CQ.PANIC_MAND_PAST_MONTH: 

3853 if v == V_NSO_NO: 

3854 r.decide("No panic in the past month. Finishing anxiety " 

3855 "section.") 

3856 jump_to(CQ.ANX_OUTRO) 

3857 

3858 elif q == CQ.PANIC1_NUM_PAST_WEEK: 

3859 if v == V_PANIC1_N_PANICS_PAST_WEEK_0: 

3860 r.decide("No panic in past week. Finishing anxiety section.") 

3861 jump_to(CQ.ANX_OUTRO) 

3862 elif v == V_PANIC1_N_PANICS_PAST_WEEK_1: 

3863 r.decide("One panic in past week. Incrementing panic.") 

3864 r.panic += 1 

3865 elif v == V_PANIC1_N_PANICS_PAST_WEEK_GT_1: 

3866 r.decide("More than one panic in past week. Adding 2 to panic.") 

3867 r.panic += 2 

3868 

3869 elif q == CQ.PANIC2_HOW_UNPLEASANT: 

3870 if v >= V_HOW_UNPLEASANT_UNPLEASANT: 

3871 r.decide("Panic 'unpleasant' or worse in past week. " 

3872 "Incrementing panic.") 

3873 r.panic += 1 

3874 

3875 elif q == CQ.PANIC3_PANIC_GE_10_MIN: 

3876 if v == V_PANIC3_WORST_GE_10_MIN: 

3877 r.decide("Worst panic in past week lasted >=10 min. " 

3878 "Incrementing panic.") 

3879 r.panic += 1 

3880 

3881 elif q == CQ.PANIC4_RAPID_ONSET: 

3882 if self.answer_is_yes(q, v): 

3883 r.decide("Rapid onset of panic symptoms. " 

3884 "Setting panic_rapid_onset.") 

3885 r.panic_rapid_onset = True 

3886 

3887 elif q == CQ.PANSYM: 

3888 # Multi-way answer. All are scored 1=no, 2=yes. 

3889 n_panic_symptoms = 0 

3890 for panic_fn in PANIC_SYMPTOM_FIELDNAMES: 

3891 panic_symptom = getattr(self, panic_fn) or 0 # force to int 

3892 yes_present = panic_symptom == 2 

3893 if yes_present: 

3894 n_panic_symptoms += 1 

3895 r.decide( 

3896 f"{n_panic_symptoms} out of " 

3897 f"{NUM_PANIC_SYMPTOMS} specific panic symptoms endorsed.") 

3898 # The next bit was coded in PANIC5, but lives more naturally here: 

3899 if self.answer_is_no(CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH): 

3900 jump_to(CQ.PANIC_DUR) 

3901 

3902 elif q == CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER: 

3903 pass 

3904 

3905 elif q == CQ.PANIC_DUR: 

3906 pass 

3907 

3908 elif q == CQ.ANX_OUTRO: 

3909 pass 

3910 

3911 # -------------------------------------------------------------------- 

3912 # Compulsions and obsessions 

3913 # -------------------------------------------------------------------- 

3914 

3915 elif q == CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH: 

3916 if v == V_NSO_NO: 

3917 r.decide("No compulsions in past month. Moving to obsessions.") 

3918 jump_to(CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH) 

3919 

3920 elif q == CQ.COMP1_DAYS_PAST_WEEK: 

3921 if v == V_DAYS_IN_PAST_WEEK_0: 

3922 r.decide("No compulsions in past week. Moving to obesssions.") 

3923 jump_to(CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH) 

3924 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3925 r.decide("Obsessions on >=4 days in past week. " 

3926 "Incrementing compulsions.") 

3927 r.compulsions += 1 

3928 

3929 elif q == CQ.COMP2_TRIED_TO_STOP: 

3930 if self.answer_is_yes(q, v): 

3931 r.decide("Attempts to stop compulsions in past week. " 

3932 "Setting compulsions_tried_to_stop. " 

3933 "Incrementing compulsions.") 

3934 r.compulsions_tried_to_stop = True 

3935 r.compulsions += 1 

3936 

3937 elif q == CQ.COMP3_UPSETTING: 

3938 if self.answer_is_yes(q, v): 

3939 r.decide("Compulsions upsetting/annoying. " 

3940 "Incrementing compulsions.") 

3941 r.compulsions += 1 

3942 

3943 elif q == CQ.COMP4_MAX_N_REPETITIONS: 

3944 if v == V_COMP4_MAX_N_REPEATS_GE_3: 

3945 r.decide("At worst, >=3 repeats. Incrementing compulsions.") 

3946 r.compulsions += 1 

3947 

3948 elif q == CQ.COMP_DUR: 

3949 if v >= V_DURATION_2W_6M: 

3950 r.decide("Compulsions for >=2 weeks. " 

3951 "Setting compulsions_at_least_2_weeks.") 

3952 r.compulsions_at_least_2_weeks = True 

3953 

3954 elif q == CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH: 

3955 if v == V_NSO_NO: 

3956 r.decide("No obsessions in past month. Moving on.") 

3957 jump_to(r.get_final_page()) 

3958 

3959 elif q == CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: 

3960 if v == V_OBSESS_MAND1_GENERAL_WORRIES: 

3961 r.decide("Worrying about something in general, not the same " 

3962 "thoughts over and over again. Moving on.") 

3963 jump_to(r.get_final_page()) 

3964 

3965 elif q == CQ.OBSESS1_DAYS_PAST_WEEK: 

3966 if v == V_DAYS_IN_PAST_WEEK_0: 

3967 r.decide("No obsessions in past week. Moving on.") 

3968 jump_to(r.get_final_page()) 

3969 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE: 

3970 r.decide("Obsessions on >=4 days in past week. " 

3971 "Incrementing obsessions.") 

3972 r.obsessions += 1 

3973 

3974 elif q == CQ.OBSESS2_TRIED_TO_STOP: 

3975 if self.answer_is_yes(q, v): 

3976 r.decide("Tried to stop obsessional thoughts in past week. " 

3977 "Setting obsessions_tried_to_stop. " 

3978 "Incrementing obsessions.") 

3979 r.obsessions_tried_to_stop = True 

3980 r.obsessions += 1 

3981 

3982 elif q == CQ.OBSESS3_UPSETTING: 

3983 if self.answer_is_yes(q, v): 

3984 r.decide("Obsessions upsetting/annoying in past week. " 

3985 "Incrementing obsessions.") 

3986 r.obsessions += 1 

3987 

3988 elif q == CQ.OBSESS4_MAX_DURATION: 

3989 if v == V_OBSESS4_GE_15_MIN: 

3990 r.decide("Obsessions lasting >=15 min in past week. " 

3991 "Incrementing obsessions.") 

3992 r.obsessions += 1 

3993 

3994 elif q == CQ.OBSESS_DUR: 

3995 if v >= V_DURATION_2W_6M: 

3996 r.decide("Obsessions for >=2 weeks. " 

3997 "Setting obsessions_at_least_2_weeks.") 

3998 r.obsessions_at_least_2_weeks = True 

3999 

4000 # -------------------------------------------------------------------- 

4001 # End 

4002 # -------------------------------------------------------------------- 

4003 

4004 elif q == CQ.OVERALL1_INFO_ONLY: 

4005 pass 

4006 

4007 elif q == CQ.OVERALL2_IMPACT_PAST_WEEK: 

4008 if self.answered(q, v): 

4009 r.functional_impairment = v - 1 

4010 r.decide( 

4011 f"Setting functional_impairment to " 

4012 f"{r.functional_impairment}") 

4013 

4014 elif q == CQ.THANKS_FINISHED: 

4015 pass 

4016 

4017 elif q == CQ.END_MARKER: # this is not a page 

4018 # we've reached the end; no point thinking further 

4019 return CQ.END_MARKER 

4020 

4021 else: 

4022 pass 

4023 

4024 if next_q == -1: 

4025 # Nothing has expressed an overriding preference, so increment... 

4026 next_q = enum_to_int(q) + 1 

4027 

4028 return int_to_enum(next_q) 

4029 

4030 def get_result(self, record_decisions: bool = False) -> CisrResult: 

4031 # internal_q = CQ.START_MARKER 

4032 internal_q = CQ.APPETITE1_LOSS_PAST_MONTH # skip the preamble etc. 

4033 result = CisrResult(record_decisions) 

4034 while (not result.incomplete) and internal_q != CQ.END_MARKER: 

4035 internal_q = self.next_q(internal_q, result) 

4036 # loop until we reach the end or have incomplete data 

4037 result.finalize() 

4038 return result 

4039 

4040 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]: 

4041 result = self.get_result() 

4042 if result.incomplete: 

4043 return CTV_INCOMPLETE 

4044 return [ 

4045 CtvInfo(content=( 

4046 f"Probable primary diagnosis: " 

4047 f"{bold(result.diagnosis_1_name())} " 

4048 f"({result.diagnosis_1_icd10_code()})")), 

4049 CtvInfo(content=( 

4050 f"Probable secondary diagnosis: " 

4051 f"{bold(result.diagnosis_2_name())} " 

4052 f"({result.diagnosis_2_icd10_code()})")), 

4053 CtvInfo(content=( 

4054 f"CIS-R suicide intent: " 

4055 f"{self.get_suicide_intent(req, result, with_warning=False)}")), 

4056 ] 

4057 

4058 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]: 

4059 result = self.get_result() 

4060 return self.standard_task_summary_fields() + [ 

4061 

4062 # Diagnoses 

4063 

4064 SummaryElement( 

4065 name="diagnosis_1_code", 

4066 coltype=Integer(), 

4067 value=result.diagnosis_1, 

4068 comment="Probable primary diagnosis (CIS-R code)"), 

4069 SummaryElement( 

4070 name="diagnosis_1_text", 

4071 coltype=UnicodeText(), 

4072 value=result.diagnosis_1_name(), 

4073 comment="Probable primary diagnosis (text)"), 

4074 SummaryElement( 

4075 name="diagnosis_1_icd10", 

4076 coltype=UnicodeText(), 

4077 value=result.diagnosis_1_icd10_code(), 

4078 comment="Probable primary diagnosis (ICD-10 code/codes)"), 

4079 SummaryElement( 

4080 name="diagnosis_2_code", 

4081 coltype=Integer(), 

4082 value=result.diagnosis_2, 

4083 comment="Probable secondary diagnosis (CIS-R code)"), 

4084 SummaryElement( 

4085 name="diagnosis_2_text", 

4086 coltype=UnicodeText(), 

4087 value=result.diagnosis_2_icd10_code(), 

4088 comment="Probable secondary diagnosis (text)"), 

4089 SummaryElement( 

4090 name="diagnosis_2_icd10", 

4091 coltype=UnicodeText(), 

4092 value=result.diagnosis_2_icd10_code(), 

4093 comment="Probable secondary diagnosis (ICD-10 code/codes)"), 

4094 

4095 # Suicidality/doctell: directly encoded in data 

4096 

4097 # Total score 

4098 

4099 SummaryElement( 

4100 name="score_total", 

4101 coltype=Integer(), 

4102 value=result.get_score(), 

4103 comment=f"CIS-R total score (max. {MAX_TOTAL})"), 

4104 # Functional impairment: directly encoded in data 

4105 

4106 # Subscores 

4107 

4108 SummaryElement( 

4109 name="score_somatic_symptoms", 

4110 coltype=Integer(), 

4111 value=result.somatic_symptoms, 

4112 comment="Score: somatic symptoms (max. 4)"), 

4113 SummaryElement( 

4114 name="score_hypochondria", 

4115 coltype=Integer(), 

4116 value=result.hypochondria, 

4117 comment="Score: worry over physical health (max. 4)"), 

4118 SummaryElement( 

4119 name="score_irritability", 

4120 coltype=Integer(), 

4121 value=result.irritability, 

4122 comment="Score: irritability (max. 4)"), 

4123 SummaryElement( 

4124 name="score_concentration_poor", 

4125 coltype=Integer(), 

4126 value=result.concentration_poor, 

4127 comment="Score: poor concentration (max. 4)"), 

4128 SummaryElement( 

4129 name="score_fatigue", 

4130 coltype=Integer(), 

4131 value=result.fatigue, 

4132 comment="Score: fatigue (max. 4)"), 

4133 SummaryElement( 

4134 name="score_sleep_problems", 

4135 coltype=Integer(), 

4136 value=result.sleep_problems, 

4137 comment="Score: sleep problems (max. 4)"), 

4138 SummaryElement( 

4139 name="score_depression", 

4140 coltype=Integer(), 

4141 value=result.depression, 

4142 comment="Score: depression (max. 4)"), 

4143 SummaryElement( 

4144 name="score_depressive_thoughts", 

4145 coltype=Integer(), 

4146 value=result.depressive_thoughts, 

4147 comment="Score: depressive ideas (max. 5)"), 

4148 SummaryElement( 

4149 name="score_phobias", 

4150 coltype=Integer(), 

4151 value=result.phobias_score, 

4152 comment="Score: phobias (max. 4)"), 

4153 SummaryElement( 

4154 name="score_worry", 

4155 coltype=Integer(), 

4156 value=result.worry, 

4157 comment="Score: worry (max. 4)"), 

4158 SummaryElement( 

4159 name="score_anxiety", 

4160 coltype=Integer(), 

4161 value=result.anxiety, 

4162 comment="Score: anxiety (max. 4)"), 

4163 SummaryElement( 

4164 name="score_panic", 

4165 coltype=Integer(), 

4166 value=result.panic, 

4167 comment="Score: panic (max. 4)"), 

4168 SummaryElement( 

4169 name="score_compulsions", 

4170 coltype=Integer(), 

4171 value=result.compulsions, 

4172 comment="Score: compulsions (max. 4)"), 

4173 SummaryElement( 

4174 name="score_obsessions", 

4175 coltype=Integer(), 

4176 value=result.obsessions, 

4177 comment="Score: obsessions (max. 4)"), 

4178 

4179 # Other 

4180 

4181 SummaryElement( 

4182 name="sleep_change", 

4183 coltype=Integer(), 

4184 value=result.sleep_change, 

4185 comment=DESC_SLEEP_CHANGE), 

4186 SummaryElement( 

4187 name="weight_change", 

4188 coltype=Integer(), 

4189 value=result.weight_change, 

4190 comment=DESC_WEIGHT_CHANGE), 

4191 SummaryElement( 

4192 name="depcrit1_score", 

4193 coltype=Integer(), 

4194 value=result.depr_crit_1_mood_anhedonia_energy, 

4195 comment=DESC_DEPCRIT1), 

4196 SummaryElement( 

4197 name="depcrit2_score", 

4198 coltype=Integer(), 

4199 value=result.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui, 

4200 comment=DESC_DEPCRIT2), 

4201 SummaryElement( 

4202 name="depcrit3_score", 

4203 coltype=Integer(), 

4204 value=result.depr_crit_3_somatic_synd, 

4205 comment=DESC_DEPCRIT3), 

4206 SummaryElement( 

4207 name="depcrit3_met_somatic_syndrome", 

4208 coltype=Boolean(), 

4209 value=result.has_somatic_syndrome(), 

4210 comment=DESC_DEPCRIT3_MET), 

4211 SummaryElement( 

4212 name="neurasthenia_score", 

4213 coltype=Integer(), 

4214 value=result.neurasthenia, 

4215 comment=DESC_NEURASTHENIA_SCORE), 

4216 

4217 # Disorder flags 

4218 

4219 SummaryElement( 

4220 name="disorder_ocd", 

4221 coltype=Boolean(), 

4222 value=result.obsessive_compulsive_disorder, 

4223 comment=DISORDER_OCD), 

4224 SummaryElement( 

4225 name="disorder_depression_mild", 

4226 coltype=Boolean(), 

4227 value=result.depression_mild, 

4228 comment=DISORDER_DEPR_MILD), 

4229 SummaryElement( 

4230 name="disorder_depression_moderate", 

4231 coltype=Boolean(), 

4232 value=result.depression_moderate, 

4233 comment=DISORDER_DEPR_MOD), 

4234 SummaryElement( 

4235 name="disorder_depression_severe", 

4236 coltype=Boolean(), 

4237 value=result.depression_severe, 

4238 comment=DISORDER_DEPR_SEV), 

4239 SummaryElement( 

4240 name="disorder_cfs", 

4241 coltype=Boolean(), 

4242 value=result.chronic_fatigue_syndrome, 

4243 comment=DISORDER_CFS), 

4244 SummaryElement( 

4245 name="disorder_gad", 

4246 coltype=Boolean(), 

4247 value=result.generalized_anxiety_disorder, 

4248 comment=DISORDER_GAD), 

4249 SummaryElement( 

4250 name="disorder_agoraphobia", 

4251 coltype=Boolean(), 

4252 value=result.phobia_agoraphobia, 

4253 comment=DISORDER_AGORAPHOBIA), 

4254 SummaryElement( 

4255 name="disorder_social_phobia", 

4256 coltype=Boolean(), 

4257 value=result.phobia_social, 

4258 comment=DISORDER_SOCIAL_PHOBIA), 

4259 SummaryElement( 

4260 name="disorder_specific_phobia", 

4261 coltype=Boolean(), 

4262 value=result.phobia_specific, 

4263 comment=DISORDER_SPECIFIC_PHOBIA), 

4264 SummaryElement( 

4265 name="disorder_panic_disorder", 

4266 coltype=Boolean(), 

4267 value=result.panic_disorder, 

4268 comment=DISORDER_PANIC), 

4269 ] 

4270 

4271 def is_complete(self) -> bool: 

4272 result = self.get_result() 

4273 return not result.incomplete 

4274 

4275 def diagnosis_name(self, req: CamcopsRequest, diagnosis_code: int) -> str: 

4276 xstring_name = f"diag_{diagnosis_code}_desc" 

4277 return self.wxstring(req, xstring_name) 

4278 

4279 def diagnosis_reason(self, req: CamcopsRequest, 

4280 diagnosis_code: int) -> str: 

4281 xstring_name = f"diag_{diagnosis_code}_explan" 

4282 return self.wxstring(req, xstring_name) 

4283 

4284 def get_suicide_intent(self, req: CamcopsRequest, 

4285 result: CisrResult, 

4286 with_warning: bool = True) -> str: 

4287 if result.incomplete: 

4288 html = "TASK INCOMPLETE. SO FAR: " 

4289 else: 

4290 html = "" 

4291 html += self.wxstring(req, f"suicid_{result.suicidality}") 

4292 if (with_warning and 

4293 result.suicidality >= SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING): 

4294 html += f" <i>{self.wxstring(req, 'suicid_instruction')}</i>" 

4295 if result.suicidality != SUICIDE_INTENT_NONE: 

4296 html = bold(html) 

4297 return html 

4298 

4299 def get_doctell(self, req: CamcopsRequest) -> str: 

4300 if self.doctor is None: 

4301 return "" 

4302 return self.xstring(req, f"doctell_{self.doctor}") 

4303 # ... xstring() as may use HTML 

4304 

4305 def get_sleep_change(self, req: CamcopsRequest, result: CisrResult) -> str: 

4306 if result.sleep_change == SLEEPCHANGE_NONE: 

4307 return "" 

4308 return self.wxstring(req, f"sleepch_{result.sleep_change}") 

4309 

4310 def get_weight_change(self, req: CamcopsRequest, result: CisrResult) -> str: 

4311 if result.weight_change in [WTCHANGE_NONE_OR_APPETITE_INCREASE, 

4312 WTCHANGE_APPETITE_LOSS]: 

4313 return "" 

4314 return self.wxstring(req, f"wtchange_{result.weight_change}") 

4315 

4316 def get_impairment(self, req: CamcopsRequest, result: CisrResult) -> str: 

4317 return self.wxstring( 

4318 req, f"impair_{result.functional_impairment}") 

4319 

4320 def get_task_html(self, req: CamcopsRequest) -> str: 

4321 # Iterate only once, for efficiency, so don't use get_result(). 

4322 

4323 def qa_row(q_: CisrQuestion, qtext: str, 

4324 a_: Optional[str]) -> str: 

4325 return tr(f"{q_.value}. {qtext}", answer(a_)) 

4326 

4327 def max_text(maxval: int) -> str: 

4328 return f" (max. {maxval})" 

4329 

4330 demographics_html_list = [] # type: List[str] 

4331 question_html_list = [] # type: List[str] 

4332 q = CQ.ETHNIC # type: CisrQuestion 

4333 result = CisrResult(record_decisions=True) 

4334 while (not result.incomplete) and q != CQ.END_MARKER: 

4335 # Iterate until we get to the end or the result declares itself 

4336 # incomplete. 

4337 # noinspection PyTypeChecker 

4338 target_list = ( 

4339 demographics_html_list if q.value < CQ.HEALTH_WELLBEING.value 

4340 else question_html_list) 

4341 if q in QUESTIONS_PROMPT_ONLY: 

4342 question = self.wxstring(req, QUESTIONS_PROMPT_ONLY[q]) 

4343 target_list.append( 

4344 qa_row(q, question, NOT_APPLICABLE_TEXT)) 

4345 elif q == CQ.PANSYM: # special! 

4346 target_list.append(qa_row( 

4347 q, 

4348 self.wxstring(req, "pansym_q_prefix"), 

4349 NOT_APPLICABLE_TEXT)) 

4350 for fieldname in PANIC_SYMPTOM_FIELDNAMES: 

4351 question = self.wxstring(req, fieldname + "_q") 

4352 value = getattr(self, fieldname) 

4353 a = get_yes_no_none( 

4354 req, value == 2 if value is not None else None) 

4355 target_list.append(qa_row(q, question, a)) 

4356 else: 

4357 fieldname = fieldname_for_q(q) 

4358 assert fieldname, f"No fieldname for question {q}" 

4359 question = self.wxstring(req, fieldname + "_q") 

4360 a = self.get_textual_answer(req, q) 

4361 target_list.append(qa_row(q, question, a)) 

4362 

4363 q = self.next_q(q, result) 

4364 # loop until we reach the end or have incomplete data 

4365 result.finalize() 

4366 

4367 is_complete = not result.incomplete 

4368 is_complete_html_td = """{}<b>{}</b></td>""".format( 

4369 "<td>" if is_complete 

4370 else f"""<td class="{CssClass.INCOMPLETE}">""", 

4371 get_yes_no(req, is_complete) 

4372 ) 

4373 

4374 summary_rows = [ 

4375 

4376 subheading_spanning_two_columns("Diagnoses"), 

4377 

4378 tr( 

4379 "Probable primary diagnosis", 

4380 ( 

4381 bold(self.diagnosis_name(req, result.diagnosis_1)) + 

4382 (f" ({result.diagnosis_1_icd10_code()})" 

4383 if result.has_diagnosis_1() else "") 

4384 ) 

4385 ), 

4386 tr(italic("... summary of reasons/description"), 

4387 italic(self.diagnosis_reason(req, result.diagnosis_1))), 

4388 tr( 

4389 "Probable secondary diagnosis", 

4390 ( 

4391 bold(self.diagnosis_name(req, result.diagnosis_2)) + 

4392 (f" ({result.diagnosis_2_icd10_code()})" 

4393 if result.has_diagnosis_2() else "") 

4394 ) 

4395 ), 

4396 tr(italic("... summary of reasons/description"), 

4397 italic(self.diagnosis_reason(req, result.diagnosis_2))), 

4398 

4399 subheading_spanning_two_columns("Suicidality"), 

4400 

4401 tr(td(self.wxstring(req, "suicid_heading")), 

4402 td(self.get_suicide_intent(req, result)), 

4403 literal=True), 

4404 tr("... spoken to doctor?", 

4405 self.get_doctell(req)), 

4406 

4407 subheading_spanning_two_columns("Total score/overall impairment"), 

4408 

4409 tr( 

4410 f"CIS-R total score (max. {MAX_TOTAL}) <sup>[1]</sup>", 

4411 result.get_score() 

4412 ), 

4413 tr(self.wxstring(req, "impair_label"), 

4414 self.get_impairment(req, result)), 

4415 

4416 subheading_spanning_two_columns("Subscores contributing to total " 

4417 "<sup>[2]</sup>"), 

4418 

4419 tr(self.wxstring(req, "somatic_label") + max_text(MAX_SOMATIC), 

4420 result.somatic_symptoms), 

4421 tr(self.wxstring(req, "hypo_label") + max_text(MAX_HYPO), 

4422 result.hypochondria), 

4423 tr(self.wxstring(req, "irrit_label") + max_text(MAX_IRRIT), 

4424 result.irritability), 

4425 tr(self.wxstring(req, "conc_label") + max_text(MAX_CONC), 

4426 result.concentration_poor), 

4427 tr(self.wxstring(req, "fatigue_label") + max_text(MAX_FATIGUE), 

4428 result.fatigue), 

4429 tr(self.wxstring(req, "sleep_label") + max_text(MAX_SLEEP), 

4430 result.sleep_problems), 

4431 tr(self.wxstring(req, "depr_label") + max_text(MAX_DEPR), 

4432 result.depression), 

4433 tr(self.wxstring(req, "depthts_label") + max_text(MAX_DEPTHTS), 

4434 result.depressive_thoughts), 

4435 tr(self.wxstring(req, "phobias_label") + max_text(MAX_PHOBIAS), 

4436 result.phobias_score), 

4437 tr(self.wxstring(req, "worry_label") + max_text(MAX_WORRY), 

4438 result.worry), 

4439 tr(self.wxstring(req, "anx_label") + max_text(MAX_ANX), 

4440 result.anxiety), 

4441 tr(self.wxstring(req, "panic_label") + max_text(MAX_PANIC), 

4442 result.panic), 

4443 tr(self.wxstring(req, "comp_label") + max_text(MAX_COMP), 

4444 result.compulsions), 

4445 tr(self.wxstring(req, "obsess_label") + max_text(MAX_OBSESS), 

4446 result.obsessions), 

4447 

4448 subheading_spanning_two_columns("Other"), 

4449 

4450 tr("Sleep change", self.get_sleep_change(req, result)), 

4451 tr("Weight change", self.get_weight_change(req, result)), 

4452 tr(DESC_DEPCRIT1, result.depr_crit_1_mood_anhedonia_energy), 

4453 tr(DESC_DEPCRIT2, result.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui), 

4454 tr(DESC_DEPCRIT3, result.depr_crit_3_somatic_synd), 

4455 tr(DESC_DEPCRIT3_MET, result.has_somatic_syndrome()), # RNC 

4456 tr(DESC_NEURASTHENIA_SCORE, result.neurasthenia), 

4457 

4458 subheading_spanning_two_columns("Disorder flags"), 

4459 

4460 tr(DISORDER_OCD, result.obsessive_compulsive_disorder), 

4461 tr(DISORDER_DEPR_MILD, result.depression_mild), 

4462 tr(DISORDER_DEPR_MOD, result.depression_moderate), 

4463 tr(DISORDER_DEPR_SEV, result.depression_severe), 

4464 tr(DISORDER_CFS, result.chronic_fatigue_syndrome), 

4465 tr(DISORDER_GAD, result.generalized_anxiety_disorder), 

4466 tr(DISORDER_AGORAPHOBIA, result.phobia_agoraphobia), 

4467 tr(DISORDER_SOCIAL_PHOBIA, result.phobia_social), 

4468 tr(DISORDER_SPECIFIC_PHOBIA, result.phobia_specific), 

4469 tr(DISORDER_PANIC, result.panic_disorder), 

4470 ] 

4471 

4472 return f""" 

4473 <div class="{CssClass.HEADING}">{self.wxstring(req, "results_1")}</div> 

4474 <div>{self.wxstring(req, "results_2")}</div> 

4475 <div class="{CssClass.SUMMARY}"> 

4476 <table class="{CssClass.SUMMARY}"> 

4477 <tr> 

4478 <td width="50%">Completed?</td> 

4479 {is_complete_html_td} 

4480 </tr> 

4481 {"".join(summary_rows)} 

4482 </table> 

4483 </div> 

4484 

4485 <div class="{CssClass.FOOTNOTES}"> 

4486 [1] {self.wxstring(req, "score_note")} 

4487 [2] {self.wxstring(req, "symptom_score_note")} 

4488 </div> 

4489 

4490 <div class="{CssClass.HEADING}"> 

4491 Preamble/demographics (not contributing to diagnosis) 

4492 </div> 

4493 <table class="{CssClass.TASKDETAIL}"> 

4494 <tr> 

4495 <th width="75%">Page</th> 

4496 <th width="25%">Answer</td> 

4497 </tr> 

4498 {"".join(demographics_html_list)} 

4499 </table> 

4500 

4501 <div class="{CssClass.HEADING}"> 

4502 Data considered by algorithm (may be a subset of all data if 

4503 subject revised answers) 

4504 </div> 

4505 <table class="{CssClass.TASKDETAIL}"> 

4506 <tr> 

4507 <th width="75%">Page</th> 

4508 <th width="25%">Answer</td> 

4509 </tr> 

4510 {"".join(question_html_list)} 

4511 </table> 

4512 

4513 <div class="{CssClass.HEADING}">Decisions</div> 

4514 <pre>{"<br>".join(ws.webify("‣ " + x) for x in result.decisions)}</pre> 

4515 

4516 <div class="{CssClass.COPYRIGHT}"> 

4517 • Original papers: 

4518 

4519 ▶ Lewis G, Pelosi AJ, Aray R, Dunn G (1992). 

4520 Measuring psychiatric disorder in the community: a standardized 

4521 assessment for use by lay interviewers. 

4522 Psychological Medicine 22: 465-486. PubMed ID  

4523 <a href="https://www.ncbi.nlm.nih.gov/pubmed/1615114">1615114</a>. 

4524 

4525 ▶ Lewis G (1994). 

4526 Assessing psychiatric disorder with a human interviewer or a 

4527 computer. 

4528 J Epidemiol Community Health 48: 207-210. PubMed ID  

4529 <a href="https://www.ncbi.nlm.nih.gov/pubmed/8189180">8189180</a>. 

4530 

4531 • Source/copyright: Glyn Lewis. 

4532 

4533 ▶ The task itself is not in the reference publications, so 

4534 copyright presumed to rest with the authors (not the journals). 

4535 

4536 ▶ “There are no copyright issues with the CISR so please adapt 

4537 it for use.” — Prof. Glyn Lewis, personal communication to 

4538 Rudolf Cardinal, 27 Oct 2017. 

4539 </div> 

4540 """ # noqa