Coverage for tasks/cisr.py: 42%

1"""

2 camcops_server/tasks/cisr.py

3

4===============================================================================

5

6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

8

9 This file is part of CamCOPS.

10

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

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

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

14 (at your option) any later version.

15

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

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

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

19 GNU General Public License for more details.

20

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

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

23

24===============================================================================

25

26"""

27

28 from enum import Enum

29 import logging

30 from typing import List, Optional

31

32 from cardinal_pythonlib.classes import classproperty

33 from cardinal_pythonlib.logs import BraceStyleAdapter

34 import cardinal_pythonlib.rnc_web as ws

35 from semantic_version import Version

36 from sqlalchemy.orm import Mapped

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

38

39 from camcops_server.cc_modules.cc_constants import CssClass

40 from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo

41 from camcops_server.cc_modules.cc_html import (

42 answer,

43 bold,

44 get_yes_no,

45 get_yes_no_none,

46 italic,

47 pmid,

48 subheading_spanning_two_columns,

49 td,

50 tr,

51)

52 from camcops_server.cc_modules.cc_request import CamcopsRequest

53 from camcops_server.cc_modules.cc_sqla_coltypes import (

54 mapped_camcops_column,

55 ZERO_TO_FOUR_CHECKER,

56 ONE_TO_TWO_CHECKER,

57 ONE_TO_THREE_CHECKER,

58 ONE_TO_FOUR_CHECKER,

59 ONE_TO_FIVE_CHECKER,

60 ONE_TO_SIX_CHECKER,

61 ONE_TO_SEVEN_CHECKER,

62 ONE_TO_EIGHT_CHECKER,

63 ONE_TO_NINE_CHECKER,

64)

65 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

66 from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin

67

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

69

70

71# =============================================================================

72# Constants

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

74

75 DEBUG_SHOW_QUESTIONS_CONSIDERED = True

76

77 NOT_APPLICABLE_TEXT = "—"

78

79# -----------------------------------------------------------------------------

80# Comments for fields

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

82

83 CMT_DEMOGRAPHICS = "(Demographics) "

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

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

86 CMT_DURATION = (

87 " (1: <2 weeks; 2: 2 weeks–6 months; 3: 6 months–1 year; "

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

89)

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

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

92 CMT_NIGHTS_PER_WEEK = CMT_DAYS_PER_WEEK

93 CMT_UNPLEASANT = (

94 " (1 not at all, 2 a little unpleasant, 3 unpleasant, "

95 "4 very unpleasant)"

96)

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

98 CMT_BOTHERSOME_INTERESTING = (

99 " (1 no, 2 yes, 3 haven't done anything " "interesting)"

100)

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

102 CMT_FATIGUE_CAUSE = (

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

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

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

106)

107 CMT_STRESSORS = (

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

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

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

111)

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

113 CMT_ANHEDONIA = (

114 " (1 yes; 2 no, less enjoyment than usual; " "3 no, don't enjoy anything)"

115)

116 CMT_PANIC_SYMPTOM = "Panic symptom in past week: "

117

118# ... and results:

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

120 DESC_DEPCRIT2 = (

121 "Depressive criterion 2 (appetite/weight, concentration, "

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

123)

124 DESC_DEPCRIT3 = (

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

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

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

128 "of libido; max. 8)"

129)

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

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

132

133 DISORDER_OCD = "Obsessive–compulsive disorder"

134 DISORDER_DEPR_MILD = "Depressive episode: at least mild"

135 DISORDER_DEPR_MOD = "Depressive episode: at least moderate"

136 DISORDER_DEPR_SEV = "Depressive episode: severe"

137 DISORDER_CFS = "Chronic fatigue syndrome"

138 DISORDER_GAD = "Generalized anxiety disorder"

139 DISORDER_AGORAPHOBIA = "Agoraphobia"

140 DISORDER_SOCIAL_PHOBIA = "Social phobia"

141 DISORDER_SPECIFIC_PHOBIA = "Specific phobia"

142 DISORDER_PANIC = "Panic disorder"

143

144# -----------------------------------------------------------------------------

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

146# -----------------------------------------------------------------------------

147

148 N_DURATIONS = 5

149 N_OPTIONS_DAYS_PER_WEEK = 3

150 N_OPTIONS_NIGHTS_PER_WEEK = 3

151 N_OPTIONS_HOW_UNPLEASANT = 4

152 N_OPTIONS_FATIGUE_CAUSES = 8

153 N_OPTIONS_STRESSORS = 9

154 N_OPTIONS_NO_SOMETIMES_OFTEN = 3

155 NUM_PANIC_SYMPTOMS = 13 # from a to m

156

157# -----------------------------------------------------------------------------

158# Actual response values

159# -----------------------------------------------------------------------------

160

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

162 V_DURATION_LT_2W = 1

163 V_DURATION_2W_6M = 2

164 V_DURATION_6M_1Y = 3

165 V_DURATION_1Y_2Y = 4

166 V_DURATION_GE_2Y = 5

167

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

169 V_DAYS_IN_PAST_WEEK_0 = 1

170 V_DAYS_IN_PAST_WEEK_1_TO_3 = 2

171 V_DAYS_IN_PAST_WEEK_4_OR_MORE = 3

172

173 V_NIGHTS_IN_PAST_WEEK_0 = 1

174 V_NIGHTS_IN_PAST_WEEK_1_TO_3 = 2

175 V_NIGHTS_IN_PAST_WEEK_4_OR_MORE = 3

176

177 V_HOW_UNPLEASANT_NOT_AT_ALL = 1

178 V_HOW_UNPLEASANT_A_LITTLE = 2

179 V_HOW_UNPLEASANT_UNPLEASANT = 3

180 V_HOW_UNPLEASANT_VERY = 4

181

182 V_FATIGUE_CAUSE_SLEEP = 1

183 V_FATIGUE_CAUSE_MEDICATION = 2

184 V_FATIGUE_CAUSE_PHYSICAL_ILLNESS = 3

185 V_FATIGUE_CAUSE_OVERWORK = 4

186 V_FATIGUE_CAUSE_PSYCHOLOGICAL = 5

187 V_FATIGUE_CAUSE_EXERCISE = 6

188 V_FATIGUE_CAUSE_OTHER = 7

189 V_FATIGUE_CAUSE_DONT_KNOW = 8

190

191 V_STRESSOR_FAMILY = 1

192 V_STRESSOR_FRIENDS_COLLEAGUES = 2

193 V_STRESSOR_HOUSING = 3

194 V_STRESSOR_MONEY = 4

195 V_STRESSOR_PHYSICAL_HEALTH = 5

196 V_STRESSOR_MENTAL_HEALTH = 6

197 V_STRESSOR_WORK = 7

198 V_STRESSOR_LEGAL = 8

199 V_STRESSOR_POLITICAL_NEWS = 9

200

201 V_NSO_NO = 1

202 V_NSO_SOMETIMES = 2

203 V_NSO_OFTEN = 3

204

205 V_SLEEP_CHANGE_LT_15_MIN = 1

206 V_SLEEP_CHANGE_15_MIN_TO_1_H = 2

207 V_SLEEP_CHANGE_1_TO_3_H = 3

208 V_SLEEP_CHANGE_GT_3_H = 4

209

210 V_ANHEDONIA_ENJOYING_NORMALLY = 1

211 V_ANHEDONIA_ENJOYING_LESS = 2

212 V_ANHEDONIA_NOT_ENJOYING = 3

213

214# Specific other question values:

215

216 V_EMPSTAT_FT = 1 # unused

217 V_EMPSTAT_PT = 2 # unused

218 V_EMPSTAT_STUDENT = 3 # unused

219 V_EMPSTAT_RETIRED = 4 # unused

220 V_EMPSTAT_HOUSEPERSON = 5 # unused

221 V_EMPSTAT_UNEMPJOBSEEKER = 6 # unused

222 V_EMPSTAT_UNEMPILLHEALTH = 7 # unused

223

224 V_EMPTYPE_SELFEMPWITHEMPLOYEES = 1 # unused

225 V_EMPTYPE_SELFEMPNOEMPLOYEES = 2 # unused

226 V_EMPTYPE_EMPLOYEE = 3 # unused

227 V_EMPTYPE_SUPERVISOR = 4 # unused

228 V_EMPTYPE_MANAGER = 5 # unused

229 V_EMPTYPE_NOT_APPLICABLE = 6 # unused

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

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

232# CIS-R.)

233

234 V_HOME_OWNER = 1 # unused

235 V_HOME_TENANT = 2 # unused

236 V_HOME_RELATIVEFRIEND = 3 # unused

237 V_HOME_HOSTELCAREHOME = 4 # unused

238 V_HOME_HOMELESS = 5 # unused

239 V_HOME_OTHER = 6 # unused

240

241 V_WEIGHT2_WTLOSS_NOTTRYING = 1

242 V_WEIGHT2_WTLOSS_TRYING = 2

243

244 V_WEIGHT3_WTLOSS_GE_HALF_STONE = 1

245 V_WEIGHT3_WTLOSS_LT_HALF_STONE = 2

246

247 V_WEIGHT4_WTGAIN_YES_PREGNANT = 3

248

249 V_WEIGHT5_WTGAIN_GE_HALF_STONE = 1

250 V_WEIGHT5_WTGAIN_LT_HALF_STONE = 2

251

252 V_GPYEAR_NONE = 0

253 V_GPYEAR_1_2 = 1

254 V_GPYEAR_3_5 = 2

255 V_GPYEAR_6_10 = 3

256 V_GPYEAR_GT_10 = 4

257

258 V_ILLNESS_DIABETES = 1

259 V_ILLNESS_ASTHMA = 2

260 V_ILLNESS_ARTHRITIS = 3

261 V_ILLNESS_HEART_DISEASE = 4

262 V_ILLNESS_HYPERTENSION = 5

263 V_ILLNESS_LUNG_DISEASE = 6

264 V_ILLNESS_MORE_THAN_ONE = 7

265 V_ILLNESS_NONE = 8

266

267 V_SOMATIC_PAIN1_NEVER = 1

268 V_SOMATIC_PAIN1_SOMETIMES = 2

269 V_SOMATIC_PAIN1_ALWAYS = 3

270

271 V_SOMATIC_PAIN3_LT_3H = 1

272 V_SOMATIC_PAIN3_GT_3H = 2

273

274 V_SOMATIC_PAIN4_NOT_AT_ALL = 1

275 V_SOMATIC_PAIN4_LITTLE_UNPLEASANT = 2

276 V_SOMATIC_PAIN4_UNPLEASANT = 3

277 V_SOMATIC_PAIN4_VERY_UNPLEASANT = 4

278

279 V_SOMATIC_PAIN5_NO = 1

280 V_SOMATIC_PAIN5_YES = 2

281 V_SOMATIC_PAIN5_NOT_DONE_ANYTHING_INTERESTING = 3

282

283 V_SOMATIC_MAND2_NO = 1

284 V_SOMATIC_MAND2_YES = 2

285

286 V_SOMATIC_DIS1_NEVER = 1

287 V_SOMATIC_DIS1_SOMETIMES = 2

288 V_SOMATIC_DIS1_ALWAYS = 3

289

290 V_SOMATIC_DIS2_NONE = 1

291 V_SOMATIC_DIS2_1_TO_3_DAYS = 2

292 V_SOMATIC_DIS2_4_OR_MORE_DAYS = 3

293

294 V_SOMATIC_DIS3_LT_3H = 1

295 V_SOMATIC_DIS3_GT_3H = 2

296

297 V_SOMATIC_DIS4_NOT_AT_ALL = 1

298 V_SOMATIC_DIS4_LITTLE_UNPLEASANT = 2

299 V_SOMATIC_DIS4_UNPLEASANT = 3

300 V_SOMATIC_DIS4_VERY_UNPLEASANT = 4

301

302 V_SOMATIC_DIS5_NO = 1

303 V_SOMATIC_DIS5_YES = 2

304 V_SOMATIC_DIS5_NOT_DONE_ANYTHING_INTERESTING = 3

305

306 V_SLEEP_MAND2_NO = 1

307 V_SLEEP_MAND2_YES_BUT_NOT_A_PROBLEM = 2

308 V_SLEEP_MAND2_YES = 3

309

310 V_IRRIT_MAND2_NO = 1

311 V_IRRIT_MAND2_SOMETIMES = 2

312 V_IRRIT_MAND2_YES = 3

313

314 V_IRRIT3_SHOUTING_NO = 1

315 V_IRRIT3_SHOUTING_WANTED_TO = 2

316 V_IRRIT3_SHOUTING_DID = 3

317

318 V_IRRIT4_ARGUMENTS_NO = 1

319 V_IRRIT4_ARGUMENTS_YES_JUSTIFIED = 2

320 V_IRRIT4_ARGUMENTS_YES_UNJUSTIFIED = 3

321

322 V_DEPR5_COULD_CHEER_UP_YES = 1

323 V_DEPR5_COULD_CHEER_UP_SOMETIMES = 2

324 V_DEPR5_COULD_CHEER_UP_NO = 3

325

326 V_DEPTH1_DMV_WORSE_MORNING = 1

327 V_DEPTH1_DMV_WORSE_EVENING = 2

328 V_DEPTH1_DMV_VARIES = 3

329 V_DEPTH1_DMV_NONE = 4

330

331 V_DEPTH2_LIBIDO_NA = 1

332 V_DEPTH2_LIBIDO_NO_CHANGE = 2

333 V_DEPTH2_LIBIDO_INCREASED = 3

334 V_DEPTH2_LIBIDO_DECREASED = 4

335

336 V_DEPTH5_GUILT_NEVER = 1

337 V_DEPTH5_GUILT_WHEN_AT_FAULT = 2

338 V_DEPTH5_GUILT_SOMETIMES = 3

339 V_DEPTH5_GUILT_OFTEN = 4

340

341 V_DEPTH8_LNWL_NO = 1

342 V_DEPTH8_LNWL_SOMETIMES = 2

343 V_DEPTH8_LNWL_ALWAYS = 3

344

345 V_DEPTH9_SUICIDAL_THOUGHTS_NO = 1

346 V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD = 2

347 V_DEPTH9_SUICIDAL_THOUGHTS_YES = 3

348

349 V_DOCTOR_YES = 1

350 V_DOCTOR_NO_BUT_OTHERS = 2

351 V_DOCTOR_NO = 3

352

353 V_ANX_PHOBIA2_ALWAYS_SPECIFIC = 1

354 V_ANX_PHOBIA2_SOMETIMES_GENERAL = 2

355

356 V_PHOBIAS_TYPE1_ALONE_PUBLIC_TRANSPORT = 1

357 V_PHOBIAS_TYPE1_FAR_FROM_HOME = 2

358 V_PHOBIAS_TYPE1_PUBLIC_SPEAKING_EATING = 3

359 V_PHOBIAS_TYPE1_BLOOD = 4

360 V_PHOBIAS_TYPE1_CROWDED_SHOPS = 5

361 V_PHOBIAS_TYPE1_ANIMALS = 6

362 V_PHOBIAS_TYPE1_BEING_WATCHED = 7

363 V_PHOBIAS_TYPE1_ENCLOSED_SPACES_HEIGHTS = 8

364 V_PHOBIAS_TYPE1_OTHER = 9

365

366 V_PANIC1_N_PANICS_PAST_WEEK_0 = 1

367 V_PANIC1_N_PANICS_PAST_WEEK_1 = 2

368 V_PANIC1_N_PANICS_PAST_WEEK_GT_1 = 3

369

370 V_PANIC3_WORST_LT_10_MIN = 1

371 V_PANIC3_WORST_GE_10_MIN = 2

372

373 V_COMP4_MAX_N_REPEATS_1 = 1

374 V_COMP4_MAX_N_REPEATS_2 = 2

375 V_COMP4_MAX_N_REPEATS_GE_3 = 3

376

377 V_OBSESS_MAND1_SAME_THOUGHTS_REPEATED = 1

378 V_OBSESS_MAND1_GENERAL_WORRIES = 2

379

380 V_OBSESS4_LT_15_MIN = 1

381 V_OBSESS4_GE_15_MIN = 2

382

383 V_OVERALL_IMPAIRMENT_NONE = 1

384 V_OVERALL_IMPAIRMENT_DIFFICULT = 2

385 V_OVERALL_IMPAIRMENT_STOP_1_ACTIVITY = 3

386 V_OVERALL_IMPAIRMENT_STOP_GT_1_ACTIVITY = 4

387

388# -----------------------------------------------------------------------------

389# Internal coding, NOT answer values:

390# -----------------------------------------------------------------------------

391

392# Magic numbers from the original:

393 WTCHANGE_NONE_OR_APPETITE_INCREASE = 0

394 WTCHANGE_APPETITE_LOSS = 1

395 WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN = 2

396 WTCHANGE_WTLOSS_GE_HALF_STONE = 3

397 WTCHANGE_WTGAIN_GE_HALF_STONE = 4

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

399

400 DESC_WEIGHT_CHANGE = (

401 "Weight change "

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

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

404 f"{WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN}: "

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

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

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

408)

409

410 PHOBIATYPES_OTHER = 0

411 PHOBIATYPES_AGORAPHOBIA = 1

412 PHOBIATYPES_SOCIAL = 2

413 PHOBIATYPES_BLOOD_INJURY = 3

414 PHOBIATYPES_ANIMALS_ENCLOSED_HEIGHTS = 4

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

416# for clarity

417

418# One smaller than the answer codes:

419 OVERALL_IMPAIRMENT_NONE = 0

420 OVERALL_IMPAIRMENT_DIFFICULT = 1

421 OVERALL_IMPAIRMENT_STOP_1_ACTIVITY = 2

422 OVERALL_IMPAIRMENT_STOP_GT_1_ACTIVITY = 3

423

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

425 DIAG_0_NO_DIAGNOSIS = 0

426 DIAG_1_MIXED_ANX_DEPR_DIS_MILD = 1

427 DIAG_2_GENERALIZED_ANX_DIS_MILD = 2

428 DIAG_3_OBSESSIVE_COMPULSIVE_DIS = 3

429 DIAG_4_MIXED_ANX_DEPR_DIS = 4

430 DIAG_5_SPECIFIC_PHOBIA = 5

431 DIAG_6_SOCIAL_PHOBIA = 6

432 DIAG_7_AGORAPHOBIA = 7

433 DIAG_8_GENERALIZED_ANX_DIS = 8

434 DIAG_9_PANIC_DIS = 9

435 DIAG_10_MILD_DEPR_EPISODE = 10

436 DIAG_11_MOD_DEPR_EPISODE = 11

437 DIAG_12_SEVERE_DEPR_EPISODE = 12

438

439 SUICIDE_INTENT_NONE = 0

440 SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS = 1

441 SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING = 2

442 SUICIDE_INTENT_SUICIDAL_THOUGHTS = 3

443 SUICIDE_INTENT_SUICIDAL_PLANS = 4

444

445 SLEEPCHANGE_NONE = 0 # added

446 SLEEPCHANGE_EMW = 1

447 SLEEPCHANGE_INSOMNIA_NOT_EMW = 2

448 SLEEPCHANGE_INCREASE = 3

449

450 DESC_SLEEP_CHANGE = (

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

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

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

454 f"{SLEEPCHANGE_INCREASE}: sleep increase)"

455)

456

457 DIURNAL_MOOD_VAR_NONE = 0

458 DIURNAL_MOOD_VAR_WORSE_MORNING = 1

459 DIURNAL_MOOD_VAR_WORSE_EVENING = 2

460

461 PSYCHOMOTOR_NONE = 0

462 PSYCHOMOTOR_RETARDATION = 1

463 PSYCHOMOTOR_AGITATION = 2

464

465# Answer values or pseudo-values:

466

467 V_MISSING = 0 # Integer value of a missing answer

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

469

470 SCORE_PREFIX = "... "

471

472# -----------------------------------------------------------------------------

473# Scoring constants:

474# -----------------------------------------------------------------------------

475

476 MAX_TOTAL = 57

477

478 MAX_SOMATIC = 4

479 MAX_HYPO = 4

480 MAX_IRRIT = 4

481 MAX_CONC = 4

482 MAX_FATIGUE = 4

483 MAX_SLEEP = 4

484 MAX_DEPR = 4

485 MAX_DEPTHTS = 5

486 MAX_PHOBIAS = 4

487 MAX_WORRY = 4

488 MAX_ANX = 4

489 MAX_PANIC = 4

490 MAX_COMP = 4

491 MAX_OBSESS = 4

492 MAX_DEPCRIT1 = 3

493 MAX_DEPCRIT2 = 7

494 MAX_DEPCRIT3 = 8

495

496 SOMATIC_SYNDROME_CRITERION = 4 # number of symptoms

497

498# -----------------------------------------------------------------------------

499# Question numbers

500# -----------------------------------------------------------------------------

501

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

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

504

505 _nasty_hack_next_enum = 1 # start with 1

506

507

508 def next_enum() -> int:

509 global _nasty_hack_next_enum

510 v = _nasty_hack_next_enum

511 _nasty_hack_next_enum += 1

512 return v

513

514

515 class CisrQuestion(Enum):

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

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

518 START_MARKER = next_enum()

519

520 INTRO_1 = START_MARKER

521 INTRO_2 = next_enum()

522 INTRO_DEMOGRAPHICS = next_enum()

523

524 ETHNIC = next_enum()

525 MARRIED = next_enum()

526 EMPSTAT = next_enum()

527 EMPTYPE = next_enum()

528 HOME = next_enum()

529 HEALTH_WELLBEING = next_enum()

530

531 APPETITE1_LOSS_PAST_MONTH = next_enum()

532

533 WEIGHT1_LOSS_PAST_MONTH = next_enum()

534 WEIGHT2_TRYING_TO_LOSE = next_enum()

535 WEIGHT3_LOST_LOTS = next_enum()

536 APPETITE2_INCREASE_PAST_MONTH = next_enum()

537 WEIGHT4_INCREASE_PAST_MONTH = next_enum()

538 # WEIGHT4A = WEIGHT4 with pregnancy question; blended

539 WEIGHT5_GAINED_LOTS = next_enum()

540 GP_YEAR = next_enum()

541 DISABLE = next_enum()

542 ILLNESS = next_enum()

543

544 SOMATIC_MAND1_PAIN_PAST_MONTH = next_enum()

545 SOMATIC_PAIN1_PSYCHOL_EXAC = next_enum()

546 SOMATIC_PAIN2_DAYS_PAST_WEEK = next_enum()

547 SOMATIC_PAIN3_GT_3H_ANY_DAY = next_enum()

548 SOMATIC_PAIN4_UNPLEASANT = next_enum()

549 SOMATIC_PAIN5_INTERRUPTED_INTERESTING = next_enum()

550 SOMATIC_MAND2_DISCOMFORT = next_enum()

551 SOMATIC_DIS1_PSYCHOL_EXAC = next_enum()

552 SOMATIC_DIS2_DAYS_PAST_WEEK = next_enum()

553 SOMATIC_DIS3_GT_3H_ANY_DAY = next_enum()

554 SOMATIC_DIS4_UNPLEASANT = next_enum()

555 SOMATIC_DIS5_INTERRUPTED_INTERESTING = next_enum()

556 SOMATIC_DUR = next_enum()

557

558 FATIGUE_MAND1_TIRED_PAST_MONTH = next_enum()

559 FATIGUE_CAUSE1_TIRED = next_enum()

560 FATIGUE_TIRED1_DAYS_PAST_WEEK = next_enum()

561 FATIGUE_TIRED2_GT_3H_ANY_DAY = next_enum()

562 FATIGUE_TIRED3_HAD_TO_PUSH = next_enum()

563 FATIGUE_TIRED4_DURING_ENJOYABLE = next_enum()

564 FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH = next_enum()

565 FATIGUE_CAUSE2_LACK_ENERGY = next_enum()

566 FATIGUE_ENERGY1_DAYS_PAST_WEEK = next_enum()

567 FATIGUE_ENERGY2_GT_3H_ANY_DAY = next_enum()

568 FATIGUE_ENERGY3_HAD_TO_PUSH = next_enum()

569 FATIGUE_ENERGY4_DURING_ENJOYABLE = next_enum()

570 FATIGUE_DUR = next_enum()

571

572 CONC_MAND1_POOR_CONC_PAST_MONTH = next_enum()

573 CONC_MAND2_FORGETFUL_PAST_MONTH = next_enum()

574 CONC1_CONC_DAYS_PAST_WEEK = next_enum()

575 CONC2_CONC_FOR_TV_READING_CONVERSATION = next_enum()

576 CONC3_CONC_PREVENTED_ACTIVITIES = next_enum()

577 CONC_DUR = next_enum()

578 CONC4_FORGOTTEN_IMPORTANT = next_enum()

579 FORGET_DUR = next_enum()

580

581 SLEEP_MAND1_LOSS_PAST_MONTH = next_enum()

582 SLEEP_LOSE1_NIGHTS_PAST_WEEK = next_enum()

583 SLEEP_LOSE2_DIS_WORST_DURATION = (

584 next_enum()

585 ) # DIS = delayed initiation of sleep

586 SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK = next_enum()

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

588 SLEEP_CAUSE = next_enum()

589 SLEEP_MAND2_GAIN_PAST_MONTH = next_enum()

590 SLEEP_GAIN1_NIGHTS_PAST_WEEK = next_enum()

591 SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT = next_enum()

592 SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK = next_enum()

593 SLEEP_DUR = next_enum()

594

595 IRRIT_MAND1_PEOPLE_PAST_MONTH = next_enum()

596 IRRIT_MAND2_THINGS_PAST_MONTH = next_enum()

597 IRRIT1_DAYS_PER_WEEK = next_enum()

598 IRRIT2_GT_1H_ANY_DAY = next_enum()

599 IRRIT3_WANTED_TO_SHOUT = next_enum()

600 IRRIT4_ARGUMENTS = next_enum()

601 IRRIT_DUR = next_enum()

602

603 HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH = next_enum()

604 HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS = next_enum()

605 HYPO1_DAYS_PAST_WEEK = next_enum()

606 HYPO2_WORRY_TOO_MUCH = next_enum()

607 HYPO3_HOW_UNPLEASANT = next_enum()

608 HYPO4_CAN_DISTRACT = next_enum()

609 HYPO_DUR = next_enum()

610

611 DEPR_MAND1_LOW_MOOD_PAST_MONTH = next_enum()

612 DEPR1_LOW_MOOD_PAST_WEEK = next_enum()

613 DEPR_MAND2_ENJOYMENT_PAST_MONTH = next_enum()

614 DEPR2_ENJOYMENT_PAST_WEEK = next_enum()

615 DEPR3_DAYS_PAST_WEEK = next_enum()

616 DEPR4_GT_3H_ANY_DAY = next_enum()

617 DEPR_CONTENT = next_enum()

618 DEPR5_COULD_CHEER_UP = next_enum()

619 DEPR_DUR = next_enum()

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

621 DEPTH2_LIBIDO = next_enum()

622 DEPTH3_RESTLESS = next_enum()

623 DEPTH4_SLOWED = next_enum()

624 DEPTH5_GUILT = next_enum()

625 DEPTH6_WORSE_THAN_OTHERS = next_enum()

626 DEPTH7_HOPELESS = next_enum()

627 DEPTH8_LNWL = next_enum() # life not worth living

628 DEPTH9_SUICIDE_THOUGHTS = next_enum()

629 DEPTH10_SUICIDE_METHOD = next_enum()

630 DOCTOR = next_enum()

631 DOCTOR2_PLEASE_TALK_TO = next_enum()

632 DEPR_OUTRO = next_enum()

633

634 WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH = next_enum()

635 WORRY_MAND2_ANY_WORRIES_PAST_MONTH = next_enum()

636 WORRY_CONT1 = next_enum()

637 WORRY1_INFO_ONLY = next_enum()

638 WORRY2_DAYS_PAST_WEEK = next_enum()

639 WORRY3_TOO_MUCH = next_enum()

640 WORRY4_HOW_UNPLEASANT = next_enum()

641 WORRY5_GT_3H_ANY_DAY = next_enum()

642 WORRY_DUR = next_enum()

643

644 ANX_MAND1_ANXIETY_PAST_MONTH = next_enum()

645 ANX_MAND2_TENSION_PAST_MONTH = next_enum()

646 ANX_PHOBIA1_SPECIFIC_PAST_MONTH = next_enum()

647 ANX_PHOBIA2_SPECIFIC_OR_GENERAL = next_enum()

648 ANX1_INFO_ONLY = next_enum()

649 ANX2_GENERAL_DAYS_PAST_WEEK = next_enum()

650 ANX3_GENERAL_HOW_UNPLEASANT = next_enum()

651 ANX4_GENERAL_PHYSICAL_SYMPTOMS = next_enum()

652 ANX5_GENERAL_GT_3H_ANY_DAY = next_enum()

653 ANX_DUR_GENERAL = next_enum()

654

655 PHOBIAS_MAND_AVOIDANCE_PAST_MONTH = next_enum()

656 PHOBIAS_TYPE1 = next_enum()

657 PHOBIAS1_DAYS_PAST_WEEK = next_enum()

658 PHOBIAS2_PHYSICAL_SYMPTOMS = next_enum()

659 PHOBIAS3_AVOIDANCE = next_enum()

660 PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK = next_enum()

661 PHOBIAS_DUR = next_enum()

662

663 PANIC_MAND_PAST_MONTH = next_enum()

664 PANIC1_NUM_PAST_WEEK = next_enum()

665 PANIC2_HOW_UNPLEASANT = next_enum()

666 PANIC3_PANIC_GE_10_MIN = next_enum()

667 PANIC4_RAPID_ONSET = next_enum()

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

669 PANIC5_ALWAYS_SPECIFIC_TRIGGER = next_enum()

670 PANIC_DUR = next_enum()

671

672 ANX_OUTRO = next_enum()

673

674 COMP_MAND1_COMPULSIONS_PAST_MONTH = next_enum()

675 COMP1_DAYS_PAST_WEEK = next_enum()

676 COMP2_TRIED_TO_STOP = next_enum()

677 COMP3_UPSETTING = next_enum()

678 COMP4_MAX_N_REPETITIONS = next_enum()

679 COMP_DUR = next_enum()

680

681 OBSESS_MAND1_OBSESSIONS_PAST_MONTH = next_enum()

682 OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL = next_enum()

683 OBSESS1_DAYS_PAST_WEEK = next_enum()

684 OBSESS2_TRIED_TO_STOP = next_enum()

685 OBSESS3_UPSETTING = next_enum()

686 OBSESS4_MAX_DURATION = next_enum()

687 OBSESS_DUR = next_enum()

688

689 OVERALL1_INFO_ONLY = next_enum()

690 OVERALL2_IMPACT_PAST_WEEK = next_enum()

691 THANKS_FINISHED = next_enum()

692 END_MARKER = next_enum() # not a real page

693

694

695 CQ = CisrQuestion # shorthand

696

697# Demographics section

698 FN_ETHNIC = "ethnic"

699 FN_MARRIED = "married"

700 FN_EMPSTAT = "empstat"

701 FN_EMPTYPE = "emptype"

702 FN_HOME = "home"

703

704 FN_APPETITE1 = "appetite1"

705 FN_WEIGHT1 = "weight1"

706 FN_WEIGHT2 = "weight2"

707 FN_WEIGHT3 = "weight3"

708 FN_APPETITE2 = "appetite2"

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

710 FN_WEIGHT5 = "weight5"

711

712 FN_GP_YEAR = "gp_year"

713 FN_DISABLE = "disable"

714 FN_ILLNESS = "illness"

715

716 FN_SOMATIC_MAND1 = "somatic_mand1"

717 FN_SOMATIC_PAIN1 = "somatic_pain1"

718 FN_SOMATIC_PAIN2 = "somatic_pain2"

719 FN_SOMATIC_PAIN3 = "somatic_pain3"

720 FN_SOMATIC_PAIN4 = "somatic_pain4"

721 FN_SOMATIC_PAIN5 = "somatic_pain5"

722 FN_SOMATIC_MAND2 = "somatic_mand2"

723 FN_SOMATIC_DIS1 = "somatic_dis1"

724 FN_SOMATIC_DIS2 = "somatic_dis2"

725 FN_SOMATIC_DIS3 = "somatic_dis3"

726 FN_SOMATIC_DIS4 = "somatic_dis4"

727 FN_SOMATIC_DIS5 = "somatic_dis5"

728 FN_SOMATIC_DUR = "somatic_dur"

729

730 FN_FATIGUE_MAND1 = "fatigue_mand1"

731 FN_FATIGUE_CAUSE1 = "fatigue_cause1"

732 FN_FATIGUE_TIRED1 = "fatigue_tired1"

733 FN_FATIGUE_TIRED2 = "fatigue_tired2"

734 FN_FATIGUE_TIRED3 = "fatigue_tired3"

735 FN_FATIGUE_TIRED4 = "fatigue_tired4"

736 FN_FATIGUE_MAND2 = "fatigue_mand2"

737 FN_FATIGUE_CAUSE2 = "fatigue_cause2"

738 FN_FATIGUE_ENERGY1 = "fatigue_energy1"

739 FN_FATIGUE_ENERGY2 = "fatigue_energy2"

740 FN_FATIGUE_ENERGY3 = "fatigue_energy3"

741 FN_FATIGUE_ENERGY4 = "fatigue_energy4"

742 FN_FATIGUE_DUR = "fatigue_dur"

743

744 FN_CONC_MAND1 = "conc_mand1"

745 FN_CONC_MAND2 = "conc_mand2"

746 FN_CONC1 = "conc1"

747 FN_CONC2 = "conc2"

748 FN_CONC3 = "conc3"

749 FN_CONC_DUR = "conc_dur"

750 FN_CONC4 = "conc4"

751 FN_FORGET_DUR = "forget_dur"

752

753 FN_SLEEP_MAND1 = "sleep_mand1"

754 FN_SLEEP_LOSE1 = "sleep_lose1"

755 FN_SLEEP_LOSE2 = "sleep_lose2"

756 FN_SLEEP_LOSE3 = "sleep_lose3"

757 FN_SLEEP_EMW = "sleep_emw"

758 FN_SLEEP_CAUSE = "sleep_cause"

759 FN_SLEEP_MAND2 = "sleep_mand2"

760 FN_SLEEP_GAIN1 = "sleep_gain1"

761 FN_SLEEP_GAIN2 = "sleep_gain2"

762 FN_SLEEP_GAIN3 = "sleep_gain3"

763 FN_SLEEP_DUR = "sleep_dur"

764

765 FN_IRRIT_MAND1 = "irrit_mand1"

766 FN_IRRIT_MAND2 = "irrit_mand2"

767 FN_IRRIT1 = "irrit1"

768 FN_IRRIT2 = "irrit2"

769 FN_IRRIT3 = "irrit3"

770 FN_IRRIT4 = "irrit4"

771 FN_IRRIT_DUR = "irrit_dur"

772

773 FN_HYPO_MAND1 = "hypo_mand1"

774 FN_HYPO_MAND2 = "hypo_mand2"

775 FN_HYPO1 = "hypo1"

776 FN_HYPO2 = "hypo2"

777 FN_HYPO3 = "hypo3"

778 FN_HYPO4 = "hypo4"

779 FN_HYPO_DUR = "hypo_dur"

780

781 FN_DEPR_MAND1 = "depr_mand1"

782 FN_DEPR1 = "depr1"

783 FN_DEPR_MAND2 = "depr_mand2"

784 FN_DEPR2 = "depr2"

785 FN_DEPR3 = "depr3"

786 FN_DEPR4 = "depr4"

787 FN_DEPR_CONTENT = "depr_content"

788 FN_DEPR5 = "depr5"

789 FN_DEPR_DUR = "depr_dur"

790 FN_DEPTH1 = "depth1"

791 FN_DEPTH2 = "depth2"

792 FN_DEPTH3 = "depth3"

793 FN_DEPTH4 = "depth4"

794 FN_DEPTH5 = "depth5"

795 FN_DEPTH6 = "depth6"

796 FN_DEPTH7 = "depth7"

797 FN_DEPTH8 = "depth8"

798 FN_DEPTH9 = "depth9"

799 FN_DEPTH10 = "depth10"

800 FN_DOCTOR = "doctor"

801

802 FN_WORRY_MAND1 = "worry_mand1"

803 FN_WORRY_MAND2 = "worry_mand2"

804 FN_WORRY_CONT1 = "worry_cont1"

805 FN_WORRY2 = "worry2"

806 FN_WORRY3 = "worry3"

807 FN_WORRY4 = "worry4"

808 FN_WORRY5 = "worry5"

809 FN_WORRY_DUR = "worry_dur"

810

811 FN_ANX_MAND1 = "anx_mand1"

812 FN_ANX_MAND2 = "anx_mand2"

813 FN_ANX_PHOBIA1 = "anx_phobia1"

814 FN_ANX_PHOBIA2 = "anx_phobia2"

815 FN_ANX2 = "anx2"

816 FN_ANX3 = "anx3"

817 FN_ANX4 = "anx4"

818 FN_ANX5 = "anx5"

819 FN_ANX_DUR = "anx_dur"

820

821 FN_PHOBIAS_MAND = "phobias_mand"

822 FN_PHOBIAS_TYPE1 = "phobias_type1"

823 FN_PHOBIAS1 = "phobias1"

824 FN_PHOBIAS2 = "phobias2"

825 FN_PHOBIAS3 = "phobias3"

826 FN_PHOBIAS4 = "phobias4"

827 FN_PHOBIAS_DUR = "phobias_dur"

828

829 FN_PANIC_MAND = "panic_mand"

830 FN_PANIC1 = "panic1"

831 FN_PANIC2 = "panic2"

832 FN_PANIC3 = "panic3"

833 FN_PANIC4 = "panic4"

834 FN_PANSYM_A = "pansym_a"

835 FN_PANSYM_B = "pansym_b"

836 FN_PANSYM_C = "pansym_c"

837 FN_PANSYM_D = "pansym_d"

838 FN_PANSYM_E = "pansym_e"

839 FN_PANSYM_F = "pansym_f"

840 FN_PANSYM_G = "pansym_g"

841 FN_PANSYM_H = "pansym_h"

842 FN_PANSYM_I = "pansym_i"

843 FN_PANSYM_J = "pansym_j"

844 FN_PANSYM_K = "pansym_k"

845 FN_PANSYM_L = "pansym_l"

846 FN_PANSYM_M = "pansym_m"

847 FN_PANIC5 = "panic5"

848 FN_PANIC_DUR = "panic_dur"

849

850 FN_COMP_MAND1 = "comp_mand1"

851 FN_COMP1 = "comp1"

852 FN_COMP2 = "comp2"

853 FN_COMP3 = "comp3"

854 FN_COMP4 = "comp4"

855 FN_COMP_DUR = "comp_dur"

856

857 FN_OBSESS_MAND1 = "obsess_mand1"

858 FN_OBSESS_MAND2 = "obsess_mand2"

859 FN_OBSESS1 = "obsess1"

860 FN_OBSESS2 = "obsess2"

861 FN_OBSESS3 = "obsess3"

862 FN_OBSESS4 = "obsess4"

863 FN_OBSESS_DUR = "obsess_dur"

864

865 FN_OVERALL2 = "overall2"

866

867 PANIC_SYMPTOM_FIELDNAMES = [

868 FN_PANSYM_A,

869 FN_PANSYM_B,

870 FN_PANSYM_C,

871 FN_PANSYM_D,

872 FN_PANSYM_E,

873 FN_PANSYM_F,

874 FN_PANSYM_G,

875 FN_PANSYM_H,

876 FN_PANSYM_I,

877 FN_PANSYM_J,

878 FN_PANSYM_K,

879 FN_PANSYM_L,

880 FN_PANSYM_M,

881]

882

883 FIELDNAME_FOR_QUESTION = {

884 # CQ.INTRO_1: # information only

885 # CQ.INTRO_2: # information only

886 # CQ.INTRO_DEMOGRAPHICS: # information only

887 CQ.ETHNIC: FN_ETHNIC,

888 CQ.MARRIED: FN_MARRIED,

889 CQ.EMPSTAT: FN_EMPSTAT,

890 CQ.EMPTYPE: FN_EMPTYPE,

891 CQ.HOME: FN_HOME,

892 # CQ.HEALTH_WELLBEING: # information only

893 CQ.APPETITE1_LOSS_PAST_MONTH: FN_APPETITE1,

894 CQ.WEIGHT1_LOSS_PAST_MONTH: FN_WEIGHT1,

895 CQ.WEIGHT2_TRYING_TO_LOSE: FN_WEIGHT2,

896 CQ.WEIGHT3_LOST_LOTS: FN_WEIGHT3,

897 CQ.APPETITE2_INCREASE_PAST_MONTH: FN_APPETITE2,

898 CQ.WEIGHT4_INCREASE_PAST_MONTH: FN_WEIGHT4,

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

900 CQ.WEIGHT5_GAINED_LOTS: FN_WEIGHT5,

901 CQ.GP_YEAR: FN_GP_YEAR,

902 CQ.DISABLE: FN_DISABLE,

903 CQ.ILLNESS: FN_ILLNESS,

904 CQ.SOMATIC_MAND1_PAIN_PAST_MONTH: FN_SOMATIC_MAND1,

905 CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: FN_SOMATIC_PAIN1,

906 CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK: FN_SOMATIC_PAIN2,

907 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY: FN_SOMATIC_PAIN3,

908 CQ.SOMATIC_PAIN4_UNPLEASANT: FN_SOMATIC_PAIN4,

909 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: FN_SOMATIC_PAIN5,

910 CQ.SOMATIC_MAND2_DISCOMFORT: FN_SOMATIC_MAND2,

911 CQ.SOMATIC_DIS1_PSYCHOL_EXAC: FN_SOMATIC_DIS1,

912 CQ.SOMATIC_DIS2_DAYS_PAST_WEEK: FN_SOMATIC_DIS2,

913 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY: FN_SOMATIC_DIS3,

914 CQ.SOMATIC_DIS4_UNPLEASANT: FN_SOMATIC_DIS4,

915 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: FN_SOMATIC_DIS5,

916 CQ.SOMATIC_DUR: FN_SOMATIC_DUR,

917 CQ.FATIGUE_MAND1_TIRED_PAST_MONTH: FN_FATIGUE_MAND1,

918 CQ.FATIGUE_CAUSE1_TIRED: FN_FATIGUE_CAUSE1,

919 CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK: FN_FATIGUE_TIRED1,

920 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY: FN_FATIGUE_TIRED2,

921 CQ.FATIGUE_TIRED3_HAD_TO_PUSH: FN_FATIGUE_TIRED3,

922 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: FN_FATIGUE_TIRED4,

923 CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH: FN_FATIGUE_MAND2,

924 CQ.FATIGUE_CAUSE2_LACK_ENERGY: FN_FATIGUE_CAUSE2,

925 CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK: FN_FATIGUE_ENERGY1,

926 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY: FN_FATIGUE_ENERGY2,

927 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH: FN_FATIGUE_ENERGY3,

928 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: FN_FATIGUE_ENERGY4,

929 CQ.FATIGUE_DUR: FN_FATIGUE_DUR,

930 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH: FN_CONC_MAND1,

931 CQ.CONC_MAND2_FORGETFUL_PAST_MONTH: FN_CONC_MAND2,

932 CQ.CONC1_CONC_DAYS_PAST_WEEK: FN_CONC1,

933 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION: FN_CONC2,

934 CQ.CONC3_CONC_PREVENTED_ACTIVITIES: FN_CONC3,

935 CQ.CONC_DUR: FN_CONC_DUR,

936 CQ.CONC4_FORGOTTEN_IMPORTANT: FN_CONC4,

937 CQ.FORGET_DUR: FN_FORGET_DUR,

938 CQ.SLEEP_MAND1_LOSS_PAST_MONTH: FN_SLEEP_MAND1,

939 CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK: FN_SLEEP_LOSE1,

940 CQ.SLEEP_LOSE2_DIS_WORST_DURATION: FN_SLEEP_LOSE2,

941 CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK: FN_SLEEP_LOSE3,

942 CQ.SLEEP_EMW_PAST_WEEK: FN_SLEEP_EMW,

943 CQ.SLEEP_CAUSE: FN_SLEEP_CAUSE,

944 CQ.SLEEP_MAND2_GAIN_PAST_MONTH: FN_SLEEP_MAND2,

945 CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK: FN_SLEEP_GAIN1,

946 CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: FN_SLEEP_GAIN2,

947 CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK: FN_SLEEP_GAIN3,

948 CQ.SLEEP_DUR: FN_SLEEP_DUR,

949 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH: FN_IRRIT_MAND1,

950 CQ.IRRIT_MAND2_THINGS_PAST_MONTH: FN_IRRIT_MAND2,

951 CQ.IRRIT1_DAYS_PER_WEEK: FN_IRRIT1,

952 CQ.IRRIT2_GT_1H_ANY_DAY: FN_IRRIT2,

953 CQ.IRRIT3_WANTED_TO_SHOUT: FN_IRRIT3,

954 CQ.IRRIT4_ARGUMENTS: FN_IRRIT4,

955 CQ.IRRIT_DUR: FN_IRRIT_DUR,

956 CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH: FN_HYPO_MAND1,

957 CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS: FN_HYPO_MAND2,

958 CQ.HYPO1_DAYS_PAST_WEEK: FN_HYPO1,

959 CQ.HYPO2_WORRY_TOO_MUCH: FN_HYPO2,

960 CQ.HYPO3_HOW_UNPLEASANT: FN_HYPO3,

961 CQ.HYPO4_CAN_DISTRACT: FN_HYPO4,

962 CQ.HYPO_DUR: FN_HYPO_DUR,

963 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH: FN_DEPR_MAND1,

964 CQ.DEPR1_LOW_MOOD_PAST_WEEK: FN_DEPR1,

965 CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: FN_DEPR_MAND2,

966 CQ.DEPR2_ENJOYMENT_PAST_WEEK: FN_DEPR2,

967 CQ.DEPR3_DAYS_PAST_WEEK: FN_DEPR3,

968 CQ.DEPR4_GT_3H_ANY_DAY: FN_DEPR4,

969 CQ.DEPR_CONTENT: FN_DEPR_CONTENT,

970 CQ.DEPR5_COULD_CHEER_UP: FN_DEPR5,

971 CQ.DEPR_DUR: FN_DEPR_DUR,

972 CQ.DEPTH1_DIURNAL_VARIATION: FN_DEPTH1,

973 CQ.DEPTH2_LIBIDO: FN_DEPTH2,

974 CQ.DEPTH3_RESTLESS: FN_DEPTH3,

975 CQ.DEPTH4_SLOWED: FN_DEPTH4,

976 CQ.DEPTH5_GUILT: FN_DEPTH5,

977 CQ.DEPTH6_WORSE_THAN_OTHERS: FN_DEPTH6,

978 CQ.DEPTH7_HOPELESS: FN_DEPTH7,

979 CQ.DEPTH8_LNWL: FN_DEPTH8,

980 CQ.DEPTH9_SUICIDE_THOUGHTS: FN_DEPTH9,

981 CQ.DEPTH10_SUICIDE_METHOD: FN_DEPTH10,

982 CQ.DOCTOR: FN_DOCTOR,

983 # CQ.DOCTOR2_PLEASE_TALK_TO: # info only

984 # CQ.DEPR_OUTRO: # info only

985 CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH: FN_WORRY_MAND1,

986 CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH: FN_WORRY_MAND2,

987 CQ.WORRY_CONT1: FN_WORRY_CONT1,

988 # CQ.WORRY1_INFO_ONLY: # info only

989 CQ.WORRY2_DAYS_PAST_WEEK: FN_WORRY2,

990 CQ.WORRY3_TOO_MUCH: FN_WORRY3,

991 CQ.WORRY4_HOW_UNPLEASANT: FN_WORRY4,

992 CQ.WORRY5_GT_3H_ANY_DAY: FN_WORRY5,

993 CQ.WORRY_DUR: FN_WORRY_DUR,

994 CQ.ANX_MAND1_ANXIETY_PAST_MONTH: FN_ANX_MAND1,

995 CQ.ANX_MAND2_TENSION_PAST_MONTH: FN_ANX_MAND2,

996 CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH: FN_ANX_PHOBIA1,

997 CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: FN_ANX_PHOBIA2,

998 # CQ.ANX1_INFO_ONLY: # info only

999 CQ.ANX2_GENERAL_DAYS_PAST_WEEK: FN_ANX2,

1000 CQ.ANX3_GENERAL_HOW_UNPLEASANT: FN_ANX3,

1001 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS: FN_ANX4,

1002 CQ.ANX5_GENERAL_GT_3H_ANY_DAY: FN_ANX5,

1003 CQ.ANX_DUR_GENERAL: FN_ANX_DUR,

1004 CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH: FN_PHOBIAS_MAND,

1005 CQ.PHOBIAS_TYPE1: FN_PHOBIAS_TYPE1,

1006 CQ.PHOBIAS1_DAYS_PAST_WEEK: FN_PHOBIAS1,

1007 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS: FN_PHOBIAS2,

1008 CQ.PHOBIAS3_AVOIDANCE: FN_PHOBIAS3,

1009 CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: FN_PHOBIAS4,

1010 CQ.PHOBIAS_DUR: FN_PHOBIAS_DUR,

1011 CQ.PANIC_MAND_PAST_MONTH: FN_PANIC_MAND,

1012 CQ.PANIC1_NUM_PAST_WEEK: FN_PANIC1,

1013 CQ.PANIC2_HOW_UNPLEASANT: FN_PANIC2,

1014 CQ.PANIC3_PANIC_GE_10_MIN: FN_PANIC3,

1015 CQ.PANIC4_RAPID_ONSET: FN_PANIC4,

1016 # CQ.PANSYM: # multiple stems

1017 CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER: FN_PANIC5,

1018 CQ.PANIC_DUR: FN_PANIC_DUR,

1019 # CQ.ANX_OUTRO: # info only

1020 CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH: FN_COMP_MAND1,

1021 CQ.COMP1_DAYS_PAST_WEEK: FN_COMP1,

1022 CQ.COMP2_TRIED_TO_STOP: FN_COMP2,

1023 CQ.COMP3_UPSETTING: FN_COMP3,

1024 CQ.COMP4_MAX_N_REPETITIONS: FN_COMP4,

1025 CQ.COMP_DUR: FN_COMP_DUR,

1026 CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH: FN_OBSESS_MAND1,

1027 CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: FN_OBSESS_MAND2,

1028 CQ.OBSESS1_DAYS_PAST_WEEK: FN_OBSESS1,

1029 CQ.OBSESS2_TRIED_TO_STOP: FN_OBSESS2,

1030 CQ.OBSESS3_UPSETTING: FN_OBSESS3,

1031 CQ.OBSESS4_MAX_DURATION: FN_OBSESS4,

1032 CQ.OBSESS_DUR: FN_OBSESS_DUR,

1033 # CQ.OVERALL1: # info only

1034 CQ.OVERALL2_IMPACT_PAST_WEEK: FN_OVERALL2,

1035}

1036

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

1038 QUESTIONS_1_NO_2_YES = [

1039 CQ.APPETITE1_LOSS_PAST_MONTH,

1040 CQ.WEIGHT1_LOSS_PAST_MONTH,

1041 CQ.WEIGHT2_TRYING_TO_LOSE,

1042 CQ.APPETITE2_INCREASE_PAST_MONTH,

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

1044 CQ.SOMATIC_MAND1_PAIN_PAST_MONTH,

1045 CQ.SOMATIC_MAND2_DISCOMFORT,

1046 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY,

1047 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING, # also has other options

1048 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY,

1049 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING, # also has other options

1050 CQ.FATIGUE_MAND1_TIRED_PAST_MONTH,

1051 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY,

1052 CQ.FATIGUE_TIRED3_HAD_TO_PUSH,

1053 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE, # also has other options

1054 CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH,

1055 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY,

1056 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH,

1057 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE, # also has other options

1058 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH,

1059 CQ.CONC_MAND2_FORGETFUL_PAST_MONTH,

1060 CQ.CONC3_CONC_PREVENTED_ACTIVITIES,

1061 CQ.CONC4_FORGOTTEN_IMPORTANT,

1062 CQ.SLEEP_MAND1_LOSS_PAST_MONTH,

1063 CQ.SLEEP_EMW_PAST_WEEK,

1064 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH,

1065 CQ.IRRIT2_GT_1H_ANY_DAY,

1066 CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH,

1067 CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS,

1068 CQ.HYPO2_WORRY_TOO_MUCH,

1069 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH,

1070 CQ.DEPR1_LOW_MOOD_PAST_WEEK,

1071 CQ.DEPR4_GT_3H_ANY_DAY,

1072 CQ.DEPTH3_RESTLESS,

1073 CQ.DEPTH4_SLOWED,

1074 CQ.DEPTH6_WORSE_THAN_OTHERS,

1075 CQ.DEPTH7_HOPELESS,

1076 CQ.DEPTH10_SUICIDE_METHOD,

1077 CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH,

1078 CQ.WORRY3_TOO_MUCH,

1079 CQ.WORRY5_GT_3H_ANY_DAY,

1080 CQ.ANX_MAND1_ANXIETY_PAST_MONTH,

1081 CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH,

1082 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS,

1083 CQ.ANX5_GENERAL_GT_3H_ANY_DAY,

1084 CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH,

1085 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS,

1086 CQ.PHOBIAS3_AVOIDANCE,

1087 CQ.PANIC4_RAPID_ONSET,

1088 CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER,

1089 CQ.COMP2_TRIED_TO_STOP,

1090 CQ.COMP3_UPSETTING,

1091 CQ.OBSESS2_TRIED_TO_STOP,

1092 CQ.OBSESS3_UPSETTING,

1093]

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

1095 QUESTIONS_1_YES_2_NO = [

1096 CQ.DISABLE,

1097 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION,

1098 CQ.HYPO4_CAN_DISTRACT,

1099]

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

1101 QUESTIONS_YN_SPECIFIC_TEXT = [

1102 CQ.WEIGHT2_TRYING_TO_LOSE,

1103 CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY,

1104 CQ.SOMATIC_DIS3_GT_3H_ANY_DAY,

1105 CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY,

1106 CQ.FATIGUE_TIRED3_HAD_TO_PUSH,

1107 CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY,

1108 CQ.FATIGUE_ENERGY3_HAD_TO_PUSH,

1109 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH,

1110 CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION,

1111 CQ.CONC4_FORGOTTEN_IMPORTANT,

1112 CQ.SLEEP_EMW_PAST_WEEK,

1113 CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH,

1114 CQ.IRRIT2_GT_1H_ANY_DAY,

1115 CQ.HYPO2_WORRY_TOO_MUCH,

1116 CQ.HYPO4_CAN_DISTRACT,

1117 CQ.DEPR1_LOW_MOOD_PAST_WEEK,

1118 CQ.DEPR4_GT_3H_ANY_DAY,

1119 CQ.DEPTH6_WORSE_THAN_OTHERS,

1120 CQ.DEPTH7_HOPELESS,

1121 CQ.WORRY3_TOO_MUCH,

1122 CQ.WORRY5_GT_3H_ANY_DAY,

1123 CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS,

1124 CQ.ANX5_GENERAL_GT_3H_ANY_DAY,

1125 CQ.PHOBIAS2_PHYSICAL_SYMPTOMS,

1126 CQ.PHOBIAS3_AVOIDANCE,

1127 CQ.COMP2_TRIED_TO_STOP,

1128 CQ.COMP3_UPSETTING,

1129 CQ.OBSESS2_TRIED_TO_STOP,

1130 CQ.OBSESS3_UPSETTING,

1131]

1132# Demographics questions (not used by algorithm)

1133 QUESTIONS_DEMOGRAPHICS = [

1134 CQ.ETHNIC,

1135 CQ.MARRIED,

1136 CQ.EMPSTAT,

1137 CQ.EMPTYPE,

1138 CQ.HOME,

1139]

1140# "Questions" that are just a prompt screen

1141 QUESTIONS_PROMPT_ONLY = {

1142 # Maps questions to their prompt's xstring name

1143 CQ.INTRO_1: "intro_1",

1144 CQ.INTRO_2: "intro_2",

1145 CQ.INTRO_DEMOGRAPHICS: "intro_demographics_statement",

1146 CQ.HEALTH_WELLBEING: "health_wellbeing_statement",

1147 CQ.DOCTOR2_PLEASE_TALK_TO: "doctor2",

1148 CQ.DEPR_OUTRO: "depr_outro",

1149 CQ.WORRY1_INFO_ONLY: "worry1",

1150 CQ.ANX1_INFO_ONLY: "anx1",

1151 CQ.ANX_OUTRO: "anx_outro",

1152 CQ.OVERALL1_INFO_ONLY: "overall1",

1153 CQ.THANKS_FINISHED: "end",

1154}

1155# "How many days per week" questions

1156# "Overall duration" questions

1157 QUESTIONS_OVERALL_DURATION = [

1158 CQ.SOMATIC_DUR,

1159 CQ.FATIGUE_DUR,

1160 CQ.CONC_DUR,

1161 CQ.FORGET_DUR,

1162 CQ.SLEEP_DUR,

1163 CQ.IRRIT_DUR,

1164 CQ.HYPO_DUR,

1165 CQ.DEPR_DUR,

1166 CQ.WORRY_DUR,

1167 CQ.ANX_DUR_GENERAL,

1168 CQ.PHOBIAS_DUR,

1169 CQ.PANIC_DUR,

1170 CQ.COMP_DUR,

1171 CQ.OBSESS_DUR,

1172]

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

1174 QUESTIONS_MULTIWAY = {

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

1176 CQ.WEIGHT3_LOST_LOTS: (1, 2),

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

1178 CQ.WEIGHT5_GAINED_LOTS: (1, 2),

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

1180 CQ.ILLNESS: (1, 8),

1181 CQ.SOMATIC_PAIN1_PSYCHOL_EXAC: (1, 3),

1182 CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING: (1, 3),

1183 CQ.SOMATIC_DIS1_PSYCHOL_EXAC: (1, 3),

1184 CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING: (1, 3),

1185 CQ.FATIGUE_TIRED4_DURING_ENJOYABLE: (1, 3),

1186 CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE: (1, 3),

1187 CQ.SLEEP_LOSE2_DIS_WORST_DURATION: (1, 4),

1188 CQ.SLEEP_CAUSE: (1, 6),

1189 CQ.SLEEP_MAND2_GAIN_PAST_MONTH: (1, 3),

1190 CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT: (1, 4),

1191 CQ.IRRIT_MAND2_THINGS_PAST_MONTH: (1, 3),

1192 CQ.IRRIT3_WANTED_TO_SHOUT: (1, 3),

1193 CQ.IRRIT4_ARGUMENTS: (1, 3),

1194 CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH: (1, 3),

1195 CQ.DEPR2_ENJOYMENT_PAST_WEEK: (1, 3),

1196 CQ.DEPR5_COULD_CHEER_UP: (1, 3),

1197 CQ.DEPTH1_DIURNAL_VARIATION: (1, 4),

1198 CQ.DEPTH2_LIBIDO: (1, 4),

1199 CQ.DEPTH5_GUILT: (1, 4),

1200 CQ.DEPTH8_LNWL: (1, 3),

1201 CQ.DEPTH9_SUICIDE_THOUGHTS: (1, 3),

1202 CQ.DOCTOR: (1, 3),

1203 CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL: (1, 2),

1204 CQ.PHOBIAS_TYPE1: (1, 9),

1205 CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK: (1, 3),

1206 CQ.PANIC_MAND_PAST_MONTH: (1, 3),

1207 CQ.PANIC1_NUM_PAST_WEEK: (1, 3),

1208 CQ.PANIC2_HOW_UNPLEASANT: (1, 3),

1209 CQ.PANIC3_PANIC_GE_10_MIN: (1, 2),

1210 CQ.COMP4_MAX_N_REPETITIONS: (1, 3),

1211 CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL: (1, 2),

1212 CQ.OBSESS4_MAX_DURATION: (1, 2),

1213 CQ.OVERALL2_IMPACT_PAST_WEEK: (1, 4),

1214}

1215 QUESTIONS_MULTIWAY_WITH_EXTRA_STEM = {

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

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

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

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

1220 CQ.EMPTYPE: (

1221 1,

1222 7,

1223 ), # 7 includes our additional "not applicable" + "prefer not to say" # noqa

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

1225}

1226 QUESTIONS_DAYS_PER_WEEK = [

1227 CQ.SOMATIC_PAIN2_DAYS_PAST_WEEK,

1228 CQ.SOMATIC_DIS2_DAYS_PAST_WEEK,

1229 CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK,

1230 CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK,

1231 CQ.CONC1_CONC_DAYS_PAST_WEEK,

1232 CQ.IRRIT1_DAYS_PER_WEEK,

1233 CQ.HYPO1_DAYS_PAST_WEEK,

1234 CQ.DEPR3_DAYS_PAST_WEEK,

1235 CQ.WORRY2_DAYS_PAST_WEEK,

1236 CQ.ANX2_GENERAL_DAYS_PAST_WEEK,

1237 CQ.PHOBIAS1_DAYS_PAST_WEEK,

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

1239 # not this: CQ.PANIC1_FREQUENCY

1240 CQ.COMP1_DAYS_PAST_WEEK,

1241 CQ.OBSESS1_DAYS_PAST_WEEK,

1242]

1243 QUESTIONS_NIGHTS_PER_WEEK = [

1244 CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK,

1245 CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK,

1246 CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK, # (*) see below

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

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

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

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

1251 CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK,

1252]

1253 QUESTIONS_HOW_UNPLEASANT_STANDARD = [

1254 CQ.SOMATIC_PAIN4_UNPLEASANT,

1255 CQ.SOMATIC_DIS4_UNPLEASANT,

1256 CQ.HYPO3_HOW_UNPLEASANT,

1257 CQ.WORRY4_HOW_UNPLEASANT,

1258 CQ.ANX3_GENERAL_HOW_UNPLEASANT,

1259]

1260 QUESTIONS_FATIGUE_CAUSES = [

1261 CQ.FATIGUE_CAUSE1_TIRED,

1262 CQ.FATIGUE_CAUSE2_LACK_ENERGY,

1263]

1264 QUESTIONS_STRESSORS = [CQ.DEPR_CONTENT, CQ.WORRY_CONT1]

1265 QUESTIONS_NO_SOMETIMES_OFTEN = [

1266 CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH,

1267 CQ.ANX_MAND2_TENSION_PAST_MONTH,

1268 CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH,

1269 CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH,

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

1271 # CQ.PANIC_MAND_PAST_MONTH

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

1273]

1274

1275

1276# =============================================================================

1277# Ancillary functions

1278# =============================================================================

1279

1280

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

1282 return FIELDNAME_FOR_QUESTION.get(q, "")

1283

1284

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

1286 return qe.value

1287

1288

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

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

1291 return CisrQuestion(qi)

1292

1293

1294 def get_caveat(req: Optional[CamcopsRequest]) -> str:

1295 if req is None:

1296 return ""

1297 _ = req.gettext

1298 return _("CIS-R suggestion ONLY")

1299

1300

1301# =============================================================================

1302# CisrResult

1303# =============================================================================

1304

1305

1306 class CisrResult(object):

1307 def __init__(

1308 self, req: Optional[CamcopsRequest], record_decisions: bool = False

1309 ) -> None:

1310 caveat = get_caveat(req)

1311 self.caveat_prefix = f"[{caveat}:] " if caveat else ""

1312 self.caveat_suffix = f" [{caveat}]" if caveat else ""

1313 self.incomplete = False

1314 self.record_decisions = record_decisions

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

1316

1317 # Symptom scoring

1318 self.depression = 0 # DEPR in original

1319 self.depr_crit_1_mood_anhedonia_energy = 0 # DEPCRIT1

1320 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui = 0 # DEPCRIT2

1321 self.depr_crit_3_somatic_synd = 0 # DEPCRIT3

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

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

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

1325 # reactions to events or activities that normally produce an

1326 # emotional response".

1327 self.weight_change = (

1328 WTCHANGE_NONE_OR_APPETITE_INCREASE # WTCHANGE IN original

1329 )

1330 self.somatic_symptoms = 0 # SOMATIC in original

1331 self.fatigue = 0 # FATIGUE in original

1332 self.neurasthenia = 0 # NEURAS in original

1333 self.concentration_poor = 0 # CONC in original

1334 self.sleep_problems = 0 # SLEEP in original

1335 self.sleep_change = SLEEPCHANGE_NONE # SLEEPCH in original

1336 self.depressive_thoughts = 0 # DEPTHTS in original

1337 self.irritability = 0 # IRRIT in original

1338 self.diurnal_mood_variation = DIURNAL_MOOD_VAR_NONE # DVM in original

1339 self.libido_decreased = False # LIBID in original

1340 self.psychomotor_changes = PSYCHOMOTOR_NONE # PSYCHMOT in original

1341 self.suicidality = (

1342 SUICIDE_INTENT_NONE

1343 ) # type: int # SUICID in original

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

1345

1346 self.hypochondria = 0 # HYPO in original

1347 self.worry = 0 # WORRY in original

1348 self.anxiety = 0 # ANX in original

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

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

1351 self.phobias_flag = False # PHOBIAS_FLAG in original

1352 self.phobias_score = 0 # PHOBIAS in original

1353 self.phobias_type = 0 # PHOBIAS_TYPE in original

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

1355 self.panic = 0 # PANIC in original

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

1357 self.panic_symptoms_total = 0 # PANSYTOT in original

1358

1359 self.compulsions = 0 # COMP in original

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

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

1362 self.obsessions = 0 # OBSESS in original

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

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

1365

1366 self.functional_impairment = 0 # IMPAIR in original

1367

1368 # Disorder flags

1369 self.obsessive_compulsive_disorder = False # OBCOMP in original

1370 self.depression_mild = False # DEPRMILD in original

1371 self.depression_moderate = False # DEPRMOD in original

1372 self.depression_severe = False # DEPRSEV in original

1373 self.chronic_fatigue_syndrome = False # CFS in original

1374 self.generalized_anxiety_disorder = False # GAD in original

1375 self.phobia_agoraphobia = False # PHOBAG in original

1376 self.phobia_social = False # PHOBSOC in original

1377 self.phobia_specific = False # PHOBSPEC in original

1378 self.panic_disorder = False # PANICD in original

1379

1380 # Final "diagnoses" (suggestions)

1381 self.diagnosis_1 = DIAG_0_NO_DIAGNOSIS # DIAG1 in original

1382 self.diagnosis_2 = DIAG_0_NO_DIAGNOSIS # DIAG2 in original

1383

1384 # -------------------------------------------------------------------------

1385 # Overall scoring

1386 # -------------------------------------------------------------------------

1387

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

1389 return (

1390 self.somatic_symptoms

1391 + self.fatigue

1392 + self.concentration_poor

1393 + self.sleep_problems

1394 + self.irritability

1395 + self.hypochondria

1396 + self.depression

1397 + self.depressive_thoughts

1398 + self.worry

1399 + self.anxiety

1400 + self.phobias_score

1401 + self.panic

1402 + self.compulsions

1403 + self.obsessions

1404 )

1405

1406 def needs_impairment_question(self) -> bool:

1407 # code in OVERALL1 in original

1408 threshold = 2 # for all symptoms

1409 return (

1410 self.somatic_symptoms >= threshold

1411 or self.hypochondria >= threshold

1412 or self.fatigue >= threshold

1413 or self.sleep_problems >= threshold

1414 or self.irritability >= threshold

1415 or self.concentration_poor >= threshold

1416 or self.depression >= threshold

1417 or self.depressive_thoughts >= threshold

1418 or self.phobias_score >= threshold

1419 or self.worry >= threshold

1420 or self.anxiety >= threshold

1421 or self.panic >= threshold

1422 or self.compulsions >= threshold

1423 or self.obsessions >= threshold

1424 )

1425

1426 def has_somatic_syndrome(self) -> bool:

1427 return self.depr_crit_3_somatic_synd >= SOMATIC_SYNDROME_CRITERION

1428

1429 def get_final_page(self) -> CisrQuestion:

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

1431 return (

1432 CQ.OVERALL1_INFO_ONLY

1433 if self.needs_impairment_question()

1434 else CQ.THANKS_FINISHED

1435 )

1436

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

1438 if self.record_decisions:

1439 self.decisions.append(decision)

1440

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

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

1443

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

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

1446

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

1448 if self.incomplete:

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

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

1451 # severe + incomplete information).

1452 return "INFORMATION INCOMPLETE"

1453

1454 if diagnosis_code == DIAG_0_NO_DIAGNOSIS:

1455 return "No diagnosis identified"

1456 elif diagnosis_code == DIAG_1_MIXED_ANX_DEPR_DIS_MILD:

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

1458 elif diagnosis_code == DIAG_2_GENERALIZED_ANX_DIS_MILD:

1459 return "Generalized anxiety disorder (mild)"

1460 elif diagnosis_code == DIAG_3_OBSESSIVE_COMPULSIVE_DIS:

1461 return "Obsessive–compulsive disorder"

1462 elif diagnosis_code == DIAG_4_MIXED_ANX_DEPR_DIS:

1463 return "Mixed anxiety and depressive disorder"

1464 elif diagnosis_code == DIAG_5_SPECIFIC_PHOBIA:

1465 return "Specific (isolated) phobia"

1466 elif diagnosis_code == DIAG_6_SOCIAL_PHOBIA:

1467 return "Social phobia"

1468 elif diagnosis_code == DIAG_7_AGORAPHOBIA:

1469 return "Agoraphobia"

1470 elif diagnosis_code == DIAG_8_GENERALIZED_ANX_DIS:

1471 return "Generalized anxiety disorder"

1472 elif diagnosis_code == DIAG_9_PANIC_DIS:

1473 return "Panic disorder"

1474 elif diagnosis_code == DIAG_10_MILD_DEPR_EPISODE:

1475 return "Mild depressive episode"

1476 elif diagnosis_code == DIAG_11_MOD_DEPR_EPISODE:

1477 return "Moderate depressive episode"

1478 elif diagnosis_code == DIAG_12_SEVERE_DEPR_EPISODE:

1479 return "Severe depressive episode"

1480 else:

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

1482

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

1484 if self.incomplete:

1485 return ""

1486

1487 if diagnosis_code == DIAG_0_NO_DIAGNOSIS:

1488 return ""

1489 elif diagnosis_code == DIAG_1_MIXED_ANX_DEPR_DIS_MILD:

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

1491 elif diagnosis_code == DIAG_2_GENERALIZED_ANX_DIS_MILD:

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

1493 elif diagnosis_code == DIAG_3_OBSESSIVE_COMPULSIVE_DIS:

1494 return "Obsessive–compulsive disorder"

1495 elif diagnosis_code == DIAG_4_MIXED_ANX_DEPR_DIS:

1496 return "F41.2"

1497 elif diagnosis_code == DIAG_5_SPECIFIC_PHOBIA:

1498 return "F40.2"

1499 elif diagnosis_code == DIAG_6_SOCIAL_PHOBIA:

1500 return "F40.1"

1501 elif diagnosis_code == DIAG_7_AGORAPHOBIA:

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

1503 elif diagnosis_code == DIAG_8_GENERALIZED_ANX_DIS:

1504 return "F41.1"

1505 elif diagnosis_code == DIAG_9_PANIC_DIS:

1506 return "F41.0"

1507 elif diagnosis_code == DIAG_10_MILD_DEPR_EPISODE:

1508 if self.has_somatic_syndrome():

1509 return "F32.01"

1510 else:

1511 return "F32.00"

1512 elif diagnosis_code == DIAG_11_MOD_DEPR_EPISODE:

1513 if self.has_somatic_syndrome():

1514 return "F32.11"

1515 else:

1516 return "F32.10"

1517 elif diagnosis_code == DIAG_12_SEVERE_DEPR_EPISODE:

1518 return "F32.2 or F32.3"

1519 else:

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

1521

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

1523 return not self.incomplete and diagnosis_code != DIAG_0_NO_DIAGNOSIS

1524

1525 def has_diagnosis_1(self) -> bool:

1526 return self.has_diagnosis(self.diagnosis_1)

1527

1528 def has_diagnosis_2(self) -> bool:

1529 return self.has_diagnosis(self.diagnosis_1)

1530

1531 def diagnosis_1_name(self) -> str:

1532 return self.diagnosis_name(self.diagnosis_1)

1533

1534 def diagnosis_1_icd10_code(self) -> str:

1535 return self.diagnosis_icd10_code(self.diagnosis_1)

1536

1537 def diagnosis_2_name(self) -> str:

1538 return self.diagnosis_name(self.diagnosis_2)

1539

1540 def diagnosis_2_icd10_code(self) -> str:

1541 return self.diagnosis_icd10_code(self.diagnosis_2)

1542

1543 def finalize(self) -> None:

1544 at_least_1_activity_impaired = (

1545 self.functional_impairment >= OVERALL_IMPAIRMENT_STOP_1_ACTIVITY

1546 )

1547 score = self.get_score()

1548

1549 # GAD

1550 if (

1551 self.anxiety >= 2

1552 and self.anxiety_physical_symptoms

1553 and self.anxiety_at_least_2_weeks

1554 ):

1555 self.decide(

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

1557 "anxiety for at least 2 weeks. "

1558 "Setting generalized_anxiety_disorder."

1559 )

1560 self.generalized_anxiety_disorder = True

1561

1562 # Panic

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

1564 self.decide(

1565 "Panic score >= 3 AND panic_rapid_onset. "

1566 "Setting panic_disorder."

1567 )

1568 self.panic_disorder = True

1569

1570 # Phobias

1571 if (

1572 self.phobias_type == PHOBIATYPES_AGORAPHOBIA

1573 and self.phobic_avoidance

1574 and self.phobias_score >= 2

1575 ):

1576 self.decide(

1577 "Phobia type is agoraphobia AND phobic avoidance AND"

1578 "phobia score >= 2. Setting phobia_agoraphobia."

1579 )

1580 self.phobia_agoraphobia = True

1581 if (

1582 self.phobias_type == PHOBIATYPES_SOCIAL

1583 and self.phobic_avoidance

1584 and self.phobias_score >= 2

1585 ):

1586 self.decide(

1587 "Phobia type is social AND phobic avoidance AND"

1588 "phobia score >= 2. Setting phobia_social."

1589 )

1590 self.phobia_social = True

1591 if (

1592 self.phobias_type == PHOBIATYPES_SOCIAL

1593 and self.phobic_avoidance

1594 and self.phobias_score >= 2

1595 ):

1596 self.decide(

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

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

1599 "Setting phobia_specific."

1600 )

1601 self.phobia_specific = True

1602

1603 # OCD

1604 if (

1605 self.obsessions + self.compulsions >= 6

1606 and self.obsessions_tried_to_stop

1607 and self.obsessions_at_least_2_weeks

1608 and at_least_1_activity_impaired

1609 ):

1610 self.decide(

1611 "obsessions + compulsions >= 6 AND "

1612 "tried to stop obsessions AND "

1613 "obsessions for at least 2 weeks AND "

1614 "at least 1 activity impaired. "

1615 "Setting obsessive_compulsive_disorder."

1616 )

1617 self.obsessive_compulsive_disorder = True

1618 if (

1619 self.obsessions + self.compulsions >= 6

1620 and self.compulsions_tried_to_stop

1621 and self.compulsions_at_least_2_weeks

1622 and at_least_1_activity_impaired

1623 ):

1624 self.decide(

1625 "obsessions + compulsions >= 6 AND "

1626 "tried to stop compulsions AND "

1627 "compulsions for at least 2 weeks AND "

1628 "at least 1 activity impaired. "

1629 "Setting obsessive_compulsive_disorder."

1630 )

1631 self.obsessive_compulsive_disorder = True

1632 if (

1633 self.obsessions == 4

1634 and self.obsessions_tried_to_stop

1635 and self.obsessions_at_least_2_weeks

1636 and at_least_1_activity_impaired

1637 ):

1638 # NOTE: 4 is the maximum for obsessions

1639 self.decide(

1640 "obsessions == 4 AND "

1641 "tried to stop obsessions AND "

1642 "obsessions for at least 2 weeks AND "

1643 "at least 1 activity impaired. "

1644 "Setting obsessive_compulsive_disorder."

1645 )

1646 self.obsessive_compulsive_disorder = True

1647 if (

1648 self.compulsions == 4

1649 and self.compulsions_tried_to_stop

1650 and self.compulsions_at_least_2_weeks

1651 and at_least_1_activity_impaired

1652 ):

1653 # NOTE: 4 is the maximum for compulsions

1654 self.decide(

1655 "compulsions == 4 AND "

1656 "tried to stop compulsions AND "

1657 "compulsions for at least 2 weeks AND "

1658 "at least 1 activity impaired. "

1659 "Setting obsessive_compulsive_disorder."

1660 )

1661 self.obsessive_compulsive_disorder = True

1662

1663 # Depression

1664 if (

1665 self.depression_at_least_2_weeks

1666 and self.depr_crit_1_mood_anhedonia_energy > 1

1667 and self.depr_crit_1_mood_anhedonia_energy

1668 + self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui

1669 > 3

1670 ):

1671 self.decide(

1672 "Depressive symptoms >=2 weeks AND "

1673 "depr_crit_1_mood_anhedonia_energy > 1 AND "

1674 "depr_crit_1_mood_anhedonia_energy + "

1675 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 3. "

1676 "Setting depression_mild."

1677 )

1678 self.depression_mild = True

1679 if (

1680 self.depression_at_least_2_weeks

1681 and self.depr_crit_1_mood_anhedonia_energy > 1

1682 and (

1683 self.depr_crit_1_mood_anhedonia_energy

1684 + self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui

1685 )

1686 > 5

1687 ):

1688 self.decide(

1689 "Depressive symptoms >=2 weeks AND "

1690 "depr_crit_1_mood_anhedonia_energy > 1 AND "

1691 "depr_crit_1_mood_anhedonia_energy + "

1692 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 5. "

1693 "Setting depression_moderate."

1694 )

1695 self.depression_moderate = True

1696 if (

1697 self.depression_at_least_2_weeks

1698 and self.depr_crit_1_mood_anhedonia_energy == 3

1699 and self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 4

1700 ):

1701 self.decide(

1702 "Depressive symptoms >=2 weeks AND "

1703 "depr_crit_1_mood_anhedonia_energy == 3 AND "

1704 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui > 4. "

1705 "Setting depression_severe."

1706 )

1707 self.depression_severe = True

1708

1709 # CFS

1710 if self.neurasthenia >= 2:

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

1712 # was always true.

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

1714 self.chronic_fatigue_syndrome = True

1715

1716 # Final diagnostic hierarchy

1717

1718 # ... primary diagnosis

1719 if score >= 12:

1720 self.decide(

1721 "Total score >= 12. Setting diagnosis_1 to "

1722 "DIAG_1_MIXED_ANX_DEPR_DIS_MILD."

1723 )

1724 self.diagnosis_1 = DIAG_1_MIXED_ANX_DEPR_DIS_MILD

1725 if self.generalized_anxiety_disorder:

1726 self.decide(

1727 "generalized_anxiety_disorder is true. Setting "

1728 "diagnosis_1 to DIAG_2_GENERALIZED_ANX_DIS_MILD."

1729 )

1730 self.diagnosis_1 = DIAG_2_GENERALIZED_ANX_DIS_MILD

1731 if self.obsessive_compulsive_disorder:

1732 self.decide(

1733 "obsessive_compulsive_disorder is true. Setting "

1734 "diagnosis_1 to DIAG_3_OBSESSIVE_COMPULSIVE_DIS."

1735 )

1736 self.diagnosis_1 = DIAG_3_OBSESSIVE_COMPULSIVE_DIS

1737 if score >= 20:

1738 self.decide(

1739 "Total score >= 20. Setting diagnosis_1 to "

1740 "DIAG_4_MIXED_ANX_DEPR_DIS."

1741 )

1742 self.diagnosis_1 = DIAG_4_MIXED_ANX_DEPR_DIS

1743 if self.phobia_specific:

1744 self.decide(

1745 "phobia_specific is true. Setting diagnosis_1 to "

1746 "DIAG_5_SPECIFIC_PHOBIA."

1747 )

1748 self.diagnosis_1 = DIAG_5_SPECIFIC_PHOBIA

1749 if self.phobia_social:

1750 self.decide(

1751 "phobia_social is true. Setting diagnosis_1 to "

1752 "DIAG_6_SOCIAL_PHOBIA."

1753 )

1754 self.diagnosis_1 = DIAG_6_SOCIAL_PHOBIA

1755 if self.phobia_agoraphobia:

1756 self.decide(

1757 "phobia_agoraphobia is true. Setting diagnosis_1 to "

1758 "DIAG_7_AGORAPHOBIA."

1759 )

1760 self.diagnosis_1 = DIAG_7_AGORAPHOBIA

1761 if self.generalized_anxiety_disorder and score >= 20:

1762 self.decide(

1763 "generalized_anxiety_disorder is true AND "

1764 "score >= 20. Setting diagnosis_1 to "

1765 "DIAG_8_GENERALIZED_ANX_DIS."

1766 )

1767 self.diagnosis_1 = DIAG_8_GENERALIZED_ANX_DIS

1768 if self.panic_disorder:

1769 self.decide(

1770 "panic_disorder is true. Setting diagnosis_1 to "

1771 "DIAG_9_PANIC_DIS."

1772 )

1773 self.diagnosis_1 = DIAG_9_PANIC_DIS

1774 if self.depression_mild:

1775 self.decide(

1776 "depression_mild is true. Setting diagnosis_1 to "

1777 "DIAG_10_MILD_DEPR_EPISODE."

1778 )

1779 self.diagnosis_1 = DIAG_10_MILD_DEPR_EPISODE

1780 if self.depression_moderate:

1781 self.decide(

1782 "depression_moderate is true. Setting diagnosis_1 to "

1783 "DIAG_11_MOD_DEPR_EPISODE."

1784 )

1785 self.diagnosis_1 = DIAG_11_MOD_DEPR_EPISODE

1786 if self.depression_severe:

1787 self.decide(

1788 "depression_severe is true. Setting diagnosis_1 to "

1789 "DIAG_12_SEVERE_DEPR_EPISODE."

1790 )

1791 self.diagnosis_1 = DIAG_12_SEVERE_DEPR_EPISODE

1792

1793 # ... secondary diagnosis

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

1795 self.decide(

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

1797 "Setting diagnosis_2 to DIAG_1_MIXED_ANX_DEPR_DIS_MILD."

1798 )

1799 self.diagnosis_2 = DIAG_1_MIXED_ANX_DEPR_DIS_MILD

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

1801 self.decide(

1802 "generalized_anxiety_disorder is true AND "

1803 "diagnosis_1 >= 3. "

1804 "Setting diagnosis_2 to DIAG_2_GENERALIZED_ANX_DIS_MILD."

1805 )

1806 self.diagnosis_2 = DIAG_2_GENERALIZED_ANX_DIS_MILD

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

1808 self.decide(

1809 "obsessive_compulsive_disorder is true AND "

1810 "diagnosis_1 >= 4. "

1811 "Setting diagnosis_2 to DIAG_3_OBSESSIVE_COMPULSIVE_DIS."

1812 )

1813 self.diagnosis_2 = DIAG_3_OBSESSIVE_COMPULSIVE_DIS

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

1815 self.decide(

1816 "score >= 20 AND diagnosis_1 >= 5. "

1817 "Setting diagnosis_2 to DIAG_4_MIXED_ANX_DEPR_DIS."

1818 )

1819 self.diagnosis_2 = DIAG_4_MIXED_ANX_DEPR_DIS

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

1821 self.decide(

1822 "phobia_specific is true AND diagnosis_1 >= 6. "

1823 "Setting diagnosis_2 to DIAG_5_SPECIFIC_PHOBIA."

1824 )

1825 self.diagnosis_2 = DIAG_5_SPECIFIC_PHOBIA

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

1827 self.decide(

1828 "phobia_social is true AND diagnosis_1 >= 7. "

1829 "Setting diagnosis_2 to DIAG_6_SOCIAL_PHOBIA."

1830 )

1831 self.diagnosis_2 = DIAG_6_SOCIAL_PHOBIA

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

1833 self.decide(

1834 "phobia_agoraphobia is true AND diagnosis_1 >= 8. "

1835 "Setting diagnosis_2 to DIAG_7_AGORAPHOBIA."

1836 )

1837 self.diagnosis_2 = DIAG_7_AGORAPHOBIA

1838 if (

1839 self.generalized_anxiety_disorder

1840 and score >= 20

1841 and self.diagnosis_1 >= 9

1842 ):

1843 self.decide(

1844 "generalized_anxiety_disorder is true AND "

1845 "score >= 20 AND "

1846 "diagnosis_1 >= 9. "

1847 "Setting diagnosis_2 to DIAG_8_GENERALIZED_ANX_DIS."

1848 )

1849 self.diagnosis_2 = DIAG_8_GENERALIZED_ANX_DIS

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

1851 self.decide(

1852 "panic_disorder is true AND diagnosis_1 >= 9. "

1853 "Setting diagnosis_2 to DIAG_9_PANIC_DIS."

1854 )

1855 self.diagnosis_2 = DIAG_9_PANIC_DIS

1856

1857 # In summary:

1858 self.decide("FINISHED.")

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

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

1861 self._showint(

1862 "depr_crit_1_mood_anhedonia_energy",

1863 self.depr_crit_1_mood_anhedonia_energy,

1864 )

1865 self._showint(

1866 "depr_crit_2_app_cnc_slp_mtr_glt_wth_sui",

1867 self.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui,

1868 )

1869 self._showint(

1870 "depr_crit_3_somatic_synd", self.depr_crit_3_somatic_synd

1871 )

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

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

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

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

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

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

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

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

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

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

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

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

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

1885 self._showbool(

1886 "depression_at_least_2_weeks", self.depression_at_least_2_weeks

1887 )

1888

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

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

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

1892 self._showbool(

1893 "anxiety_physical_symptoms", self.anxiety_physical_symptoms

1894 )

1895 self._showbool(

1896 "anxiety_at_least_2_weeks", self.anxiety_at_least_2_weeks

1897 )

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

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

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

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

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

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

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

1905

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

1907 self._showbool(

1908 "compulsions_tried_to_stop", self.compulsions_tried_to_stop

1909 )

1910 self._showbool(

1911 "compulsions_at_least_2_weeks", self.compulsions_at_least_2_weeks

1912 )

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

1914 self._showbool(

1915 "obsessions_tried_to_stop", self.obsessions_tried_to_stop

1916 )

1917 self._showbool(

1918 "obsessions_at_least_2_weeks", self.obsessions_at_least_2_weeks

1919 )

1920

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

1922

1923 # Disorder flags

1924 self._showbool(

1925 "obsessive_compulsive_disorder", self.obsessive_compulsive_disorder

1926 )

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

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

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

1930 self._showbool(

1931 "chronic_fatigue_syndrome", self.chronic_fatigue_syndrome

1932 )

1933 self._showbool(

1934 "generalized_anxiety_disorder", self.generalized_anxiety_disorder

1935 )

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

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

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

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

1940

1941 caveat_prefix = self.caveat_prefix

1942 self.decide(f"--- {caveat_prefix}Final possible diagnoses:")

1943 self.decide(

1944 f"{caveat_prefix}Possible primary diagnosis: "

1945 + self.diagnosis_name(self.diagnosis_1)

1946 )

1947 self.decide(

1948 f"{caveat_prefix}Possible secondary diagnosis: "

1949 + self.diagnosis_name(self.diagnosis_2)

1950 )

1951

1952

1953# =============================================================================

1954# CISR

1955# =============================================================================

1956

1957

1958 class Cisr(TaskHasPatientMixin, Task): # type: ignore[misc]

1959 """

1960 Server implementation of the CIS-R task.

1961 """

1962

1963 __tablename__ = "cisr"

1964 shortname = "CIS-R"

1965 provides_trackers = False

1966

1967 # Demographics

1968

1969 ethnic: Mapped[Optional[int]] = mapped_camcops_column(

1970 comment=(

1971 CMT_DEMOGRAPHICS

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

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

1974 ),

1975 permitted_value_checker=ONE_TO_SEVEN_CHECKER,

1976 )

1977 married: Mapped[Optional[int]] = mapped_camcops_column(

1978 comment=(

1979 CMT_DEMOGRAPHICS

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

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

1982 ),

1983 permitted_value_checker=ONE_TO_SIX_CHECKER,

1984 )

1985 empstat: Mapped[Optional[int]] = mapped_camcops_column(

1986 comment=(

1987 CMT_DEMOGRAPHICS

1988 + "Current employment status (1 working full time, "

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

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

1991 "8 prefer not to say)"

1992 ),

1993 permitted_value_checker=ONE_TO_EIGHT_CHECKER,

1994 )

1995 emptype: Mapped[Optional[int]] = mapped_camcops_column(

1996 comment=(

1997 CMT_DEMOGRAPHICS + "Current/last paid employment "

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

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

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

2001 "7 prefer not to say)"

2002 ),

2003 permitted_value_checker=ONE_TO_SEVEN_CHECKER,

2004 )

2005 home: Mapped[Optional[int]] = mapped_camcops_column(

2006 comment=(

2007 CMT_DEMOGRAPHICS

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

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

2010 "7 prefer not to say)"

2011 ),

2012 permitted_value_checker=ONE_TO_SEVEN_CHECKER,

2013 )

2014

2015 # Appetite/weight

2016

2017 appetite1: Mapped[Optional[int]] = mapped_camcops_column(

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

2019 permitted_value_checker=ONE_TO_TWO_CHECKER,

2020 )

2021 weight1: Mapped[Optional[int]] = mapped_camcops_column(

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

2023 permitted_value_checker=ONE_TO_TWO_CHECKER,

2024 )

2025 weight2: Mapped[Optional[int]] = mapped_camcops_column(

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

2027 permitted_value_checker=ONE_TO_TWO_CHECKER,

2028 )

2029 weight3: Mapped[Optional[int]] = mapped_camcops_column(

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

2031 permitted_value_checker=ONE_TO_TWO_CHECKER,

2032 )

2033 appetite2: Mapped[Optional[int]] = mapped_camcops_column(

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

2035 permitted_value_checker=ONE_TO_TWO_CHECKER,

2036 )

2037 weight4: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2040 permitted_value_checker=ONE_TO_THREE_CHECKER,

2041 )

2042 weight5: Mapped[Optional[int]] = mapped_camcops_column(

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

2044 permitted_value_checker=ONE_TO_TWO_CHECKER,

2045 )

2046

2047 # Somatic problems

2048

2049 gp_year: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2052 permitted_value_checker=ZERO_TO_FOUR_CHECKER,

2053 )

2054 disable: Mapped[Optional[int]] = mapped_camcops_column(

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

2056 permitted_value_checker=ONE_TO_TWO_CHECKER,

2057 )

2058 illness: Mapped[Optional[int]] = mapped_camcops_column(

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

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

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

2062 permitted_value_checker=ONE_TO_EIGHT_CHECKER,

2063 )

2064

2065 somatic_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2067 permitted_value_checker=ONE_TO_TWO_CHECKER,

2068 )

2069 somatic_pain1: Mapped[Optional[int]] = mapped_camcops_column(

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

2071 "stressed" + CMT_NEVER_SOMETIMES_ALWAYS,

2072 permitted_value_checker=ONE_TO_THREE_CHECKER,

2073 )

2074 somatic_pain2: Mapped[Optional[int]] = mapped_camcops_column(

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

2076 permitted_value_checker=ONE_TO_THREE_CHECKER,

2077 )

2078 somatic_pain3: Mapped[Optional[int]] = mapped_camcops_column(

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

2080 permitted_value_checker=ONE_TO_TWO_CHECKER,

2081 )

2082 somatic_pain4: Mapped[Optional[int]] = mapped_camcops_column(

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

2084 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2085 )

2086 somatic_pain5: Mapped[Optional[int]] = mapped_camcops_column(

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

2088 "week?" + CMT_BOTHERSOME_INTERESTING,

2089 permitted_value_checker=ONE_TO_THREE_CHECKER,

2090 )

2091 somatic_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

2093 permitted_value_checker=ONE_TO_TWO_CHECKER,

2094 )

2095 somatic_dis1: Mapped[Optional[int]] = mapped_camcops_column(

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

2097 "stressed" + CMT_NEVER_SOMETIMES_ALWAYS,

2098 permitted_value_checker=ONE_TO_THREE_CHECKER,

2099 )

2100 somatic_dis2: Mapped[Optional[int]] = mapped_camcops_column(

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

2102 permitted_value_checker=ONE_TO_THREE_CHECKER,

2103 )

2104 somatic_dis3: Mapped[Optional[int]] = mapped_camcops_column(

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

2106 + CMT_1_NO_2_YES,

2107 permitted_value_checker=ONE_TO_TWO_CHECKER,

2108 )

2109 somatic_dis4: Mapped[Optional[int]] = mapped_camcops_column(

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

2111 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2112 )

2113 somatic_dis5: Mapped[Optional[int]] = mapped_camcops_column(

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

2115 "past week?" + CMT_BOTHERSOME_INTERESTING,

2116 permitted_value_checker=ONE_TO_THREE_CHECKER,

2117 )

2118 somatic_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2120 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2121 )

2122

2123 # Fatigue/lacking energy

2124

2125 fatigue_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2127 permitted_value_checker=ONE_TO_TWO_CHECKER,

2128 )

2129 fatigue_cause1: Mapped[Optional[int]] = mapped_camcops_column(

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

2131 permitted_value_checker=ONE_TO_EIGHT_CHECKER,

2132 )

2133 fatigue_tired1: Mapped[Optional[int]] = mapped_camcops_column(

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

2135 permitted_value_checker=ONE_TO_THREE_CHECKER,

2136 )

2137 fatigue_tired2: Mapped[Optional[int]] = mapped_camcops_column(

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

2139 permitted_value_checker=ONE_TO_TWO_CHECKER,

2140 )

2141 fatigue_tired3: Mapped[Optional[int]] = mapped_camcops_column(

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

2143 "past week" + CMT_1_NO_2_YES,

2144 permitted_value_checker=ONE_TO_TWO_CHECKER,

2145 )

2146 fatigue_tired4: Mapped[Optional[int]] = mapped_camcops_column(

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

2148 permitted_value_checker=ONE_TO_THREE_CHECKER,

2149 )

2150 fatigue_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

2152 permitted_value_checker=ONE_TO_TWO_CHECKER,

2153 )

2154 fatigue_cause2: Mapped[Optional[int]] = mapped_camcops_column(

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

2156 permitted_value_checker=ONE_TO_EIGHT_CHECKER,

2157 )

2158 fatigue_energy1: Mapped[Optional[int]] = mapped_camcops_column(

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

2160 permitted_value_checker=ONE_TO_THREE_CHECKER,

2161 )

2162 fatigue_energy2: Mapped[Optional[int]] = mapped_camcops_column(

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

2164 + CMT_1_NO_2_YES,

2165 permitted_value_checker=ONE_TO_TWO_CHECKER,

2166 )

2167 fatigue_energy3: Mapped[Optional[int]] = mapped_camcops_column(

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

2169 "things done in past week" + CMT_1_NO_2_YES,

2170 permitted_value_checker=ONE_TO_TWO_CHECKER,

2171 )

2172 fatigue_energy4: Mapped[Optional[int]] = mapped_camcops_column(

2173 comment="Lacking energy during an enjoyable activity"

2174 + CMT_DURING_ENJOYABLE,

2175 permitted_value_checker=ONE_TO_THREE_CHECKER,

2176 )

2177 fatigue_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2179 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2180 )

2181

2182 # Concentration/memory

2183

2184 conc_mand1: Mapped[Optional[int]] = mapped_camcops_column(

2185 comment="Problems in concentrating during past monnth?"

2186 + CMT_1_NO_2_YES,

2187 permitted_value_checker=ONE_TO_TWO_CHECKER,

2188 )

2189 conc_mand2: Mapped[Optional[int]] = mapped_camcops_column(

2190 comment="Problems with forgetting things during past month?"

2191 + CMT_1_NO_2_YES,

2192 permitted_value_checker=ONE_TO_TWO_CHECKER,

2193 )

2194 conc1: Mapped[Optional[int]] = mapped_camcops_column(

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

2196 + CMT_DAYS_PER_WEEK,

2197 permitted_value_checker=ONE_TO_THREE_CHECKER,

2198 )

2199 conc2: Mapped[Optional[int]] = mapped_camcops_column(

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

2201 "conversation" + CMT_1_YES_2_NO,

2202 permitted_value_checker=ONE_TO_TWO_CHECKER,

2203 )

2204 conc3: Mapped[Optional[int]] = mapped_camcops_column(

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

2206 "with things in past week" + CMT_1_NO_2_YES,

2207 permitted_value_checker=ONE_TO_TWO_CHECKER,

2208 )

2209 conc_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2211 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2212 )

2213 conc4: Mapped[Optional[int]] = mapped_camcops_column(

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

2215 permitted_value_checker=ONE_TO_TWO_CHECKER,

2216 )

2217 forget_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2219 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2220 )

2221

2222 # Sleep

2223

2224 sleep_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2226 permitted_value_checker=ONE_TO_TWO_CHECKER,

2227 )

2228 sleep_lose1: Mapped[Optional[int]] = mapped_camcops_column(

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

2230 + CMT_NIGHTS_PER_WEEK,

2231 permitted_value_checker=ONE_TO_THREE_CHECKER,

2232 )

2233 sleep_lose2: Mapped[Optional[int]] = mapped_camcops_column(

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

2235 "get to sleep?" + CMT_SLEEP_CHANGE,

2236 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2237 )

2238 sleep_lose3: Mapped[Optional[int]] = mapped_camcops_column(

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

2240 "get to sleep?" + CMT_NIGHTS_PER_WEEK,

2241 permitted_value_checker=ONE_TO_THREE_CHECKER,

2242 )

2243 sleep_emw: Mapped[Optional[int]] = mapped_camcops_column(

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

2245 "week?" + CMT_1_NO_2_YES,

2246 permitted_value_checker=ONE_TO_TWO_CHECKER,

2247 )

2248 sleep_cause: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2251 permitted_value_checker=ONE_TO_SIX_CHECKER,

2252 )

2253 sleep_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2256 permitted_value_checker=ONE_TO_THREE_CHECKER,

2257 )

2258 sleep_gain1: Mapped[Optional[int]] = mapped_camcops_column(

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

2260 + CMT_NIGHTS_PER_WEEK,

2261 permitted_value_checker=ONE_TO_THREE_CHECKER,

2262 )

2263 sleep_gain2: Mapped[Optional[int]] = mapped_camcops_column(

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

2265 "usual?" + CMT_SLEEP_CHANGE,

2266 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2267 )

2268 sleep_gain3: Mapped[Optional[int]] = mapped_camcops_column(

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

2270 "than usual?" + CMT_NIGHTS_PER_WEEK,

2271 permitted_value_checker=ONE_TO_THREE_CHECKER,

2272 )

2273 sleep_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2275 + CMT_DURATION,

2276 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2277 )

2278

2279 # Irritability

2280

2281 irrit_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2283 + CMT_1_NO_2_YES,

2284 permitted_value_checker=ONE_TO_TWO_CHECKER,

2285 )

2286 irrit_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2289 permitted_value_checker=ONE_TO_THREE_CHECKER,

2290 )

2291 irrit1: Mapped[Optional[int]] = mapped_camcops_column(

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

2293 + CMT_DAYS_PER_WEEK,

2294 permitted_value_checker=ONE_TO_THREE_CHECKER,

2295 )

2296 irrit2: Mapped[Optional[int]] = mapped_camcops_column(

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

2298 "week?" + CMT_1_NO_2_YES,

2299 permitted_value_checker=ONE_TO_TWO_CHECKER,

2300 )

2301 irrit3: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2304 permitted_value_checker=ONE_TO_THREE_CHECKER,

2305 )

2306 irrit4: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2309 permitted_value_checker=ONE_TO_THREE_CHECKER,

2310 )

2311 irrit_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2313 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2314 )

2315

2316 # Hypochondriasis

2317

2318 hypo_mand1: Mapped[Optional[int]] = mapped_camcops_column(

2319 comment="Worried about physical health in past month?"

2320 + CMT_1_NO_2_YES,

2321 permitted_value_checker=ONE_TO_TWO_CHECKER,

2322 )

2323 hypo_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

2325 permitted_value_checker=ONE_TO_TWO_CHECKER,

2326 )

2327 hypo1: Mapped[Optional[int]] = mapped_camcops_column(

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

2329 "days in past week?" + CMT_DAYS_PER_WEEK,

2330 permitted_value_checker=ONE_TO_THREE_CHECKER,

2331 )

2332 hypo2: Mapped[Optional[int]] = mapped_camcops_column(

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

2334 permitted_value_checker=ONE_TO_TWO_CHECKER,

2335 )

2336 hypo3: Mapped[Optional[int]] = mapped_camcops_column(

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

2338 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2339 )

2340 hypo4: Mapped[Optional[int]] = mapped_camcops_column(

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

2342 + CMT_1_YES_2_NO,

2343 permitted_value_checker=ONE_TO_TWO_CHECKER,

2344 )

2345 hypo_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2347 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2348 )

2349

2350 # Depression

2351

2352 depr_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2354 permitted_value_checker=ONE_TO_TWO_CHECKER,

2355 )

2356 depr1: Mapped[Optional[int]] = mapped_camcops_column(

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

2358 permitted_value_checker=ONE_TO_TWO_CHECKER,

2359 )

2360 depr_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

2362 "as much as usual?" + CMT_ANHEDONIA,

2363 permitted_value_checker=ONE_TO_THREE_CHECKER,

2364 )

2365 depr2: Mapped[Optional[int]] = mapped_camcops_column(

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

2367 "as much as usual?" + CMT_ANHEDONIA,

2368 permitted_value_checker=ONE_TO_THREE_CHECKER,

2369 )

2370 depr3: Mapped[Optional[int]] = mapped_camcops_column(

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

2372 "week" + CMT_DAYS_PER_WEEK,

2373 permitted_value_checker=ONE_TO_THREE_CHECKER,

2374 )

2375 depr4: Mapped[Optional[int]] = mapped_camcops_column(

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

2377 "week?" + CMT_1_NO_2_YES,

2378 permitted_value_checker=ONE_TO_TWO_CHECKER,

2379 )

2380 depr_content: Mapped[Optional[int]] = mapped_camcops_column(

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

2382 + CMT_STRESSORS,

2383 permitted_value_checker=ONE_TO_NINE_CHECKER,

2384 )

2385 depr5: Mapped[Optional[int]] = mapped_camcops_column(

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

2387 "nice things/company make you happier? "

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

2389 permitted_value_checker=ONE_TO_THREE_CHECKER,

2390 )

2391 depr_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2393 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2394 )

2395 depth1: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2398 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2399 )

2400 depth2: Mapped[Optional[int]] = mapped_camcops_column(

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

2402 "3 increased, 4 decreased)",

2403 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2404 )

2405 depth3: Mapped[Optional[int]] = mapped_camcops_column(

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

2407 permitted_value_checker=ONE_TO_TWO_CHECKER,

2408 )

2409 depth4: Mapped[Optional[int]] = mapped_camcops_column(

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

2411 permitted_value_checker=ONE_TO_TWO_CHECKER,

2412 )

2413 depth5: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2416 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2417 )

2418 depth6: Mapped[Optional[int]] = mapped_camcops_column(

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

2420 + CMT_1_NO_2_YES,

2421 permitted_value_checker=ONE_TO_TWO_CHECKER,

2422 )

2423 depth7: Mapped[Optional[int]] = mapped_camcops_column(

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

2425 permitted_value_checker=ONE_TO_TWO_CHECKER,

2426 )

2427 depth8: Mapped[Optional[int]] = mapped_camcops_column(

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

2429 "3 always)",

2430 permitted_value_checker=ONE_TO_THREE_CHECKER,

2431 )

2432 depth9: Mapped[Optional[int]] = mapped_camcops_column(

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

2434 "never commit suicide; 3 yes)",

2435 permitted_value_checker=ONE_TO_THREE_CHECKER,

2436 )

2437 depth10: Mapped[Optional[int]] = mapped_camcops_column(

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

2439 permitted_value_checker=ONE_TO_TWO_CHECKER,

2440 )

2441 doctor: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2444 "people; 3 no)",

2445 permitted_value_checker=ONE_TO_THREE_CHECKER,

2446 )

2447

2448 # Worry/generalized anxiety

2449

2450 worry_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2452 permitted_value_checker=ONE_TO_THREE_CHECKER,

2453 )

2454 worry_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

2456 permitted_value_checker=ONE_TO_TWO_CHECKER,

2457 )

2458 worry_cont1: Mapped[Optional[int]] = mapped_camcops_column(

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

2460 permitted_value_checker=ONE_TO_NINE_CHECKER,

2461 )

2462 worry2: Mapped[Optional[int]] = mapped_camcops_column(

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

2464 "many days in past week" + CMT_DAYS_PER_WEEK,

2465 permitted_value_checker=ONE_TO_THREE_CHECKER,

2466 )

2467 worry3: Mapped[Optional[int]] = mapped_camcops_column(

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

2469 permitted_value_checker=ONE_TO_TWO_CHECKER,

2470 )

2471 worry4: Mapped[Optional[int]] = mapped_camcops_column(

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

2473 "health)" + CMT_UNPLEASANT,

2474 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2475 )

2476 worry5: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2479 permitted_value_checker=ONE_TO_TWO_CHECKER,

2480 )

2481 worry_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2483 "long?" + CMT_DURATION,

2484 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2485 )

2486

2487 anx_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2489 permitted_value_checker=ONE_TO_TWO_CHECKER,

2490 )

2491 anx_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

2493 + CMT_NO_SOMETIMES_OFTEN,

2494 permitted_value_checker=ONE_TO_THREE_CHECKER,

2495 )

2496 anx_phobia1: Mapped[Optional[int]] = mapped_camcops_column(

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

2498 permitted_value_checker=ONE_TO_TWO_CHECKER,

2499 )

2500 anx_phobia2: Mapped[Optional[int]] = mapped_camcops_column(

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

2502 "2 sometimes general)",

2503 permitted_value_checker=ONE_TO_TWO_CHECKER,

2504 )

2505 anx2: Mapped[Optional[int]] = mapped_camcops_column(

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

2507 + CMT_DAYS_PER_WEEK,

2508 permitted_value_checker=ONE_TO_THREE_CHECKER,

2509 )

2510 anx3: Mapped[Optional[int]] = mapped_camcops_column(

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

2512 + CMT_UNPLEASANT,

2513 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2514 )

2515 anx4: Mapped[Optional[int]] = mapped_camcops_column(

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

2517 "week?" + CMT_1_NO_2_YES,

2518 permitted_value_checker=ONE_TO_TWO_CHECKER,

2519 )

2520 anx5: Mapped[Optional[int]] = mapped_camcops_column(

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

2522 "week?" + CMT_1_NO_2_YES,

2523 permitted_value_checker=ONE_TO_TWO_CHECKER,

2524 )

2525 anx_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2527 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2528 )

2529

2530 # Specific phobias

2531

2532 phobias_mand: Mapped[Optional[int]] = mapped_camcops_column(

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

2534 permitted_value_checker=ONE_TO_TWO_CHECKER,

2535 )

2536 phobias_type1: Mapped[Optional[int]] = mapped_camcops_column(

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

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

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

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

2541 permitted_value_checker=ONE_TO_NINE_CHECKER,

2542 )

2543 phobias1: Mapped[Optional[int]] = mapped_camcops_column(

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

2545 permitted_value_checker=ONE_TO_THREE_CHECKER,

2546 )

2547 phobias2: Mapped[Optional[int]] = mapped_camcops_column(

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

2549 + CMT_1_NO_2_YES,

2550 permitted_value_checker=ONE_TO_TWO_CHECKER,

2551 )

2552 phobias3: Mapped[Optional[int]] = mapped_camcops_column(

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

2554 permitted_value_checker=ONE_TO_TWO_CHECKER,

2555 )

2556 phobias4: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2559 permitted_value_checker=ONE_TO_THREE_CHECKER,

2560 )

2561 phobias_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2563 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2564 )

2565

2566 # Panic

2567

2568 panic_mand: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2571 permitted_value_checker=ONE_TO_THREE_CHECKER,

2572 )

2573 panic1: Mapped[Optional[int]] = mapped_camcops_column(

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: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2581 "unpleasant)",

2582 permitted_value_checker=ONE_TO_THREE_CHECKER,

2583 )

2584 panic3: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2587 permitted_value_checker=ONE_TO_TWO_CHECKER,

2588 )

2589 panic4: Mapped[Optional[int]] = mapped_camcops_column(

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

2591 permitted_value_checker=ONE_TO_TWO_CHECKER,

2592 )

2593 pansym_a: Mapped[Optional[int]] = mapped_camcops_column(

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

2595 permitted_value_checker=ONE_TO_TWO_CHECKER,

2596 )

2597 pansym_b: Mapped[Optional[int]] = mapped_camcops_column(

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

2599 permitted_value_checker=ONE_TO_TWO_CHECKER,

2600 )

2601 pansym_c: Mapped[Optional[int]] = mapped_camcops_column(

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

2603 permitted_value_checker=ONE_TO_TWO_CHECKER,

2604 )

2605 pansym_d: Mapped[Optional[int]] = mapped_camcops_column(

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

2607 permitted_value_checker=ONE_TO_TWO_CHECKER,

2608 )

2609 pansym_e: Mapped[Optional[int]] = mapped_camcops_column(

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

2611 permitted_value_checker=ONE_TO_TWO_CHECKER,

2612 )

2613 pansym_f: Mapped[Optional[int]] = mapped_camcops_column(

2614 comment=(

2615 CMT_PANIC_SYMPTOM

2616 + "chest pain/pressure/discomfort"

2617 + CMT_1_NO_2_YES

2618 ),

2619 permitted_value_checker=ONE_TO_TWO_CHECKER,

2620 )

2621 pansym_g: Mapped[Optional[int]] = mapped_camcops_column(

2622 comment=CMT_PANIC_SYMPTOM + "nausea" + CMT_1_NO_2_YES,

2623 permitted_value_checker=ONE_TO_TWO_CHECKER,

2624 )

2625 pansym_h: Mapped[Optional[int]] = mapped_camcops_column(

2626 comment=(

2627 CMT_PANIC_SYMPTOM

2628 + "dizzy/unsteady/lightheaded/faint"

2629 + CMT_1_NO_2_YES

2630 ),

2631 permitted_value_checker=ONE_TO_TWO_CHECKER,

2632 )

2633 pansym_i: Mapped[Optional[int]] = mapped_camcops_column(

2634 comment=(

2635 CMT_PANIC_SYMPTOM

2636 + "derealization/depersonalization"

2637 + CMT_1_NO_2_YES

2638 ),

2639 permitted_value_checker=ONE_TO_TWO_CHECKER,

2640 )

2641 pansym_j: Mapped[Optional[int]] = mapped_camcops_column(

2642 comment=(

2643 CMT_PANIC_SYMPTOM + "losing control/going crazy" + CMT_1_NO_2_YES

2644 ),

2645 permitted_value_checker=ONE_TO_TWO_CHECKER,

2646 )

2647 pansym_k: Mapped[Optional[int]] = mapped_camcops_column(

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

2649 permitted_value_checker=ONE_TO_TWO_CHECKER,

2650 )

2651 pansym_l: Mapped[Optional[int]] = mapped_camcops_column(

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

2653 permitted_value_checker=ONE_TO_TWO_CHECKER,

2654 )

2655 pansym_m: Mapped[Optional[int]] = mapped_camcops_column(

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

2657 permitted_value_checker=ONE_TO_TWO_CHECKER,

2658 )

2659 panic5: Mapped[Optional[int]] = mapped_camcops_column(

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

2661 + CMT_1_NO_2_YES,

2662 permitted_value_checker=ONE_TO_TWO_CHECKER,

2663 )

2664 panic_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2666 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2667 )

2668

2669 # Compulsions

2670

2671 comp_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2673 permitted_value_checker=ONE_TO_THREE_CHECKER,

2674 )

2675 comp1: Mapped[Optional[int]] = mapped_camcops_column(

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

2677 permitted_value_checker=ONE_TO_THREE_CHECKER,

2678 )

2679 comp2: Mapped[Optional[int]] = mapped_camcops_column(

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

2681 permitted_value_checker=ONE_TO_TWO_CHECKER,

2682 )

2683 comp3: Mapped[Optional[int]] = mapped_camcops_column(

2684 comment="Compulsions: upsetting/annoying in past week"

2685 + CMT_1_NO_2_YES,

2686 permitted_value_checker=ONE_TO_TWO_CHECKER,

2687 )

2688 comp4: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2691 "3: three or more repeats)",

2692 permitted_value_checker=ONE_TO_THREE_CHECKER,

2693 )

2694 comp_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2696 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2697 )

2698

2699 # Obsessions

2700

2701 obsess_mand1: Mapped[Optional[int]] = mapped_camcops_column(

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

2703 permitted_value_checker=ONE_TO_THREE_CHECKER,

2704 )

2705 obsess_mand2: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2708 "general)",

2709 permitted_value_checker=ONE_TO_TWO_CHECKER,

2710 )

2711 obsess1: Mapped[Optional[int]] = mapped_camcops_column(

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

2713 permitted_value_checker=ONE_TO_THREE_CHECKER,

2714 )

2715 obsess2: Mapped[Optional[int]] = mapped_camcops_column(

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

2717 permitted_value_checker=ONE_TO_TWO_CHECKER,

2718 )

2719 obsess3: Mapped[Optional[int]] = mapped_camcops_column(

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

2721 permitted_value_checker=ONE_TO_TWO_CHECKER,

2722 )

2723 obsess4: Mapped[Optional[int]] = mapped_camcops_column(

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

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

2726 permitted_value_checker=ONE_TO_TWO_CHECKER,

2727 )

2728 obsess_dur: Mapped[Optional[int]] = mapped_camcops_column(

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

2730 permitted_value_checker=ONE_TO_FIVE_CHECKER,

2731 )

2732

2733 # Overall impact

2734

2735 overall2: Mapped[Optional[int]] = mapped_camcops_column(

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

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

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

2739 "have stopped >1 activity)",

2740 permitted_value_checker=ONE_TO_FOUR_CHECKER,

2741 )

2742

2743 # -------------------------------------------------------------------------

2744 # Functions

2745 # -------------------------------------------------------------------------

2746

2747 @staticmethod

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

2749 _ = req.gettext

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

2751

2752 # noinspection PyMethodParameters

2753 @classproperty

2754 def minimum_client_version(cls) -> Version:

2755 return Version("2.2.0")

2756

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

2758 fieldname = fieldname_for_q(q)

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

2760 return getattr(self, fieldname)

2761

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

2763 value = self.value_for_question(q)

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

2765

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

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

2768 value = self.int_value_for_question(q)

2769 if q in QUESTIONS_1_NO_2_YES:

2770 return value == 1

2771 elif q in QUESTIONS_1_YES_2_NO:

2772 return value == 2

2773 else:

2774 raise ValueError(

2775 "answer_is_no() called for inappropriate " f"question {q}"

2776 )

2777

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

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

2780 value = self.int_value_for_question(q)

2781 if q in QUESTIONS_1_NO_2_YES:

2782 return value == 2

2783 elif q in QUESTIONS_1_YES_2_NO:

2784 return value == 1

2785 else:

2786 raise ValueError(

2787 "answer_is_yes() called for inappropriate " f"question {q}"

2788 )

2789

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

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

2792 value = self.int_value_for_question(q)

2793 return value != V_MISSING

2794

2795 def get_textual_answer(

2796 self, req: CamcopsRequest, q: CisrQuestion

2797 ) -> Optional[str]:

2798 value = self.value_for_question(q)

2799 if value is None:

2800 return None

2801 if value == V_MISSING and q != CisrQuestion.GP_YEAR:

2802 # Note that 0 is a legitimate answer value for GP_YEAR.

2803 return None

2804 if q in QUESTIONS_1_NO_2_YES:

2805 return get_yes_no(req, value == 2)

2806 elif q in QUESTIONS_1_YES_2_NO:

2807 return get_yes_no(req, value == 1)

2808 elif q in QUESTIONS_PROMPT_ONLY:

2809 return NOT_APPLICABLE_TEXT

2810 fieldname = fieldname_for_q(q)

2811 if (

2812 q in QUESTIONS_YN_SPECIFIC_TEXT

2813 or q in QUESTIONS_MULTIWAY

2814 or q in QUESTIONS_MULTIWAY_WITH_EXTRA_STEM

2815 ):

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

2817 elif q in QUESTIONS_OVERALL_DURATION:

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

2819 elif q in QUESTIONS_DAYS_PER_WEEK:

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

2821 elif q in QUESTIONS_NIGHTS_PER_WEEK:

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

2823 elif q in QUESTIONS_HOW_UNPLEASANT_STANDARD:

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

2825 elif q in QUESTIONS_FATIGUE_CAUSES:

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

2827 elif q in QUESTIONS_STRESSORS:

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

2829 elif q in QUESTIONS_NO_SOMETIMES_OFTEN:

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

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

2832

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

2834 # See equivalent in the C++ code.

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

2836

2837 v = V_MISSING # integer value

2838 if DEBUG_SHOW_QUESTIONS_CONSIDERED:

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

2840 fieldname = fieldname_for_q(q)

2841 if fieldname: # eliminates prompt-only questions

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

2843 if var_q is None:

2844 if q not in QUESTIONS_DEMOGRAPHICS:

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

2846 # demographic information. Otherwise:

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

2848 r.incomplete = True

2849 else:

2850 v = int(var_q)

2851

2852 next_q = -1

2853

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

2855 nonlocal next_q

2856 next_q = enum_to_int(qe)

2857

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

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

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

2861

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

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

2864 # in sequence). Clarity is key.

2865

2866 # ---------------------------------------------------------------------

2867 # Demographics/preamble

2868 # ---------------------------------------------------------------------

2869

2870 if q in QUESTIONS_DEMOGRAPHICS or q in QUESTIONS_PROMPT_ONLY:

2871 # Nothing special

2872 pass

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

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

2875 # switch() instead.

2876

2877 # --------------------------------------------------------------------

2878 # Appetite/weight

2879 # --------------------------------------------------------------------

2880

2881 elif q == CQ.APPETITE1_LOSS_PAST_MONTH:

2882 if self.answer_is_no(q, v):

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

2884 jump_to(CQ.APPETITE2_INCREASE_PAST_MONTH)

2885 elif self.answer_is_yes(q, v):

2886 r.decide(

2887 "Loss of appetite in past month. "

2888 "Incrementing depr_crit_3_somatic_synd."

2889 )

2890 r.depr_crit_3_somatic_synd += 1

2891 r.weight_change = WTCHANGE_APPETITE_LOSS

2892

2893 elif q == CQ.WEIGHT1_LOSS_PAST_MONTH:

2894 if self.answer_is_no(q, v):

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

2896 jump_to(CQ.GP_YEAR)

2897

2898 elif q == CQ.WEIGHT2_TRYING_TO_LOSE:

2899 if v == V_WEIGHT2_WTLOSS_TRYING:

2900 # Trying to lose weight. Move on.

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

2902 elif v == V_WEIGHT2_WTLOSS_NOTTRYING:

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

2904 r.weight_change = WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN

2905

2906 elif q == CQ.WEIGHT3_LOST_LOTS:

2907 if v == V_WEIGHT3_WTLOSS_GE_HALF_STONE:

2908 r.decide(

2909 "Weight loss ≥0.5st in past month. "

2910 "Incrementing depr_crit_3_somatic_synd."

2911 )

2912 r.weight_change = WTCHANGE_WTLOSS_GE_HALF_STONE

2913 r.depr_crit_3_somatic_synd += 1

2914 r.decide(

2915 "Loss of weight, so skipping appetite/weight gain "

2916 "questions."

2917 )

2918 jump_to(CQ.GP_YEAR)

2919

2920 elif q == CQ.APPETITE2_INCREASE_PAST_MONTH:

2921 if self.answer_is_no(q, v):

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

2923 jump_to(CQ.GP_YEAR)

2924

2925 elif q == CQ.WEIGHT4_INCREASE_PAST_MONTH:

2926 if self.answer_is_yes(q, v):

2927 r.decide("Weight gain.")

2928 r.weight_change = WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN

2929 elif self.answered(q, v):

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

2931 jump_to(CQ.GP_YEAR)

2932

2933 elif q == CQ.WEIGHT5_GAINED_LOTS:

2934 if (

2935 v == V_WEIGHT5_WTGAIN_GE_HALF_STONE

2936 and r.weight_change == WTCHANGE_NONDELIBERATE_WTLOSS_OR_WTGAIN

2937 ):

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

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

2940 r.weight_change = WTCHANGE_WTGAIN_GE_HALF_STONE

2941

2942 # --------------------------------------------------------------------

2943 # Somatic symptoms

2944 # --------------------------------------------------------------------

2945

2946 elif q == CQ.GP_YEAR:

2947 # Score the preceding block:

2948 if (

2949 r.weight_change == WTCHANGE_WTLOSS_GE_HALF_STONE

2950 and self.answer_is_yes(CQ.APPETITE1_LOSS_PAST_MONTH)

2951 ):

2952 r.decide(

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

2954 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

2955 )

2956 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

2957 if (

2958 r.weight_change == WTCHANGE_WTGAIN_GE_HALF_STONE

2959 and self.answer_is_yes(CQ.APPETITE2_INCREASE_PAST_MONTH)

2960 ):

2961 r.decide(

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

2963 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

2964 )

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(

2991 "Pain on >=4 of last 7 days. "

2992 "Incrementing somatic_symptoms."

2993 )

2994 r.somatic_symptoms += 1

2995

2996 elif q == CQ.SOMATIC_PAIN3_GT_3H_ANY_DAY:

2997 if self.answer_is_yes(q, v):

2998 r.decide(

2999 "Pain for >3h on any day in past week. "

3000 "Incrementing somatic_symptoms."

3001 )

3002 r.somatic_symptoms += 1

3003

3004 elif q == CQ.SOMATIC_PAIN4_UNPLEASANT:

3005 if v >= V_HOW_UNPLEASANT_UNPLEASANT:

3006 r.decide(

3007 "Pain 'unpleasant' or worse in past week. "

3008 "Incrementing somatic_symptoms."

3009 )

3010 r.somatic_symptoms += 1

3011

3012 elif q == CQ.SOMATIC_PAIN5_INTERRUPTED_INTERESTING:

3013 if self.answer_is_yes(q, v):

3014 r.decide(

3015 "Pain interrupted an interesting activity in past "

3016 "week. "

3017 "Incrementing somatic_symptoms."

3018 )

3019 r.somatic_symptoms += 1

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

3021 jump_to(CQ.SOMATIC_DUR) # skip SOMATIC_MAND2

3022

3023 elif q == CQ.SOMATIC_MAND2_DISCOMFORT:

3024 if self.answer_is_no(q, v):

3025 r.decide("No discomfort.")

3026 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH)

3027

3028 elif q == CQ.SOMATIC_DIS1_PSYCHOL_EXAC:

3029 if v == V_SOMATIC_DIS1_NEVER:

3030 r.decide(

3031 "Discomfort never exacerbated by being "

3032 "low/anxious/stressed."

3033 )

3034 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH)

3035

3036 elif q == CQ.SOMATIC_DIS2_DAYS_PAST_WEEK:

3037 if v == V_DAYS_IN_PAST_WEEK_0:

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

3039 jump_to(CQ.FATIGUE_MAND1_TIRED_PAST_MONTH)

3040 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3041 r.decide(

3042 "Discomfort on >=4 days in past week. "

3043 "Incrementing somatic_symptoms."

3044 )

3045 r.somatic_symptoms += 1

3046

3047 elif q == CQ.SOMATIC_DIS3_GT_3H_ANY_DAY:

3048 if self.answer_is_yes(q, v):

3049 r.decide(

3050 "Discomfort for >3h on any day in past week. "

3051 "Incrementing somatic_symptoms."

3052 )

3053 r.somatic_symptoms += 1

3054

3055 elif q == CQ.SOMATIC_DIS4_UNPLEASANT:

3056 if v >= V_HOW_UNPLEASANT_UNPLEASANT:

3057 r.decide(

3058 "Discomfort 'unpleasant' or worse in past week. "

3059 "Incrementing somatic_symptoms."

3060 )

3061 r.somatic_symptoms += 1

3062

3063 elif q == CQ.SOMATIC_DIS5_INTERRUPTED_INTERESTING:

3064 if self.answer_is_yes(q, v):

3065 r.decide(

3066 "Discomfort interrupted an interesting activity in "

3067 "past "

3068 "week. Incrementing somatic_symptoms."

3069 )

3070 r.somatic_symptoms += 1

3071

3072 # --------------------------------------------------------------------

3073 # Fatigue/energy

3074 # --------------------------------------------------------------------

3075

3076 elif q == CQ.FATIGUE_MAND1_TIRED_PAST_MONTH:

3077 if self.answer_is_no(q, v):

3078 r.decide("Not tired.")

3079 jump_to(CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH)

3080

3081 elif q == CQ.FATIGUE_CAUSE1_TIRED:

3082 if v == V_FATIGUE_CAUSE_EXERCISE:

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

3084 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)

3085

3086 elif q == CQ.FATIGUE_TIRED1_DAYS_PAST_WEEK:

3087 if v == V_DAYS_IN_PAST_WEEK_0:

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

3089 jump_to(CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH)

3090 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3091 r.decide(

3092 "Tired on >=4 days in past week. " "Incrementing fatigue."

3093 )

3094 r.fatigue += 1

3095

3096 elif q == CQ.FATIGUE_TIRED2_GT_3H_ANY_DAY:

3097 if self.answer_is_yes(q, v):

3098 r.decide(

3099 "Tired for >3h on any day in past week. "

3100 "Incrementing fatigue."

3101 )

3102 r.fatigue += 1

3103

3104 elif q == CQ.FATIGUE_TIRED3_HAD_TO_PUSH:

3105 if self.answer_is_yes(q, v):

3106 r.decide(

3107 "Tired enough to have to push self during past week. "

3108 "Incrementing fatigue."

3109 )

3110 r.fatigue += 1

3111

3112 elif q == CQ.FATIGUE_TIRED4_DURING_ENJOYABLE:

3113 if self.answer_is_yes(q, v):

3114 r.decide(

3115 "Tired during an enjoyable activity during past "

3116 "week. "

3117 "Incrementing fatigue."

3118 )

3119 r.fatigue += 1

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

3121 jump_to(CQ.FATIGUE_DUR) # skip FATIGUE_MAND2

3122

3123 elif q == CQ.FATIGUE_MAND2_LACK_ENERGY_PAST_MONTH:

3124 if self.answer_is_no(q, v):

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

3126 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)

3127

3128 elif q == CQ.FATIGUE_CAUSE2_LACK_ENERGY:

3129 if v == V_FATIGUE_CAUSE_EXERCISE:

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

3131 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)

3132

3133 elif q == CQ.FATIGUE_ENERGY1_DAYS_PAST_WEEK:

3134 if v == V_DAYS_IN_PAST_WEEK_0:

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

3136 jump_to(CQ.CONC_MAND1_POOR_CONC_PAST_MONTH)

3137 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3138 r.decide(

3139 "Lacking in energy on >=4 days in past week. "

3140 "Incrementing fatigue."

3141 )

3142 r.fatigue += 1

3143

3144 elif q == CQ.FATIGUE_ENERGY2_GT_3H_ANY_DAY:

3145 if self.answer_is_yes(q, v):

3146 r.decide(

3147 "Lacking in energy for >3h on any day in past week. "

3148 "Incrementing fatigue."

3149 )

3150 r.fatigue += 1

3151

3152 elif q == CQ.FATIGUE_ENERGY3_HAD_TO_PUSH:

3153 if self.answer_is_yes(q, v):

3154 r.decide(

3155 "Lacking in energy enough to have to push self during "

3156 "past week. Incrementing fatigue."

3157 )

3158 r.fatigue += 1

3159

3160 elif q == CQ.FATIGUE_ENERGY4_DURING_ENJOYABLE:

3161 if self.answer_is_yes(q, v):

3162 r.decide(

3163 "Lacking in energy during an enjoyable activity "

3164 "during "

3165 "past week. Incrementing fatigue."

3166 )

3167 r.fatigue += 1

3168

3169 elif q == CQ.FATIGUE_DUR:

3170 # Score preceding:

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

3172 r.decide(

3173 "somatic >= 2 and fatigue >= 2. "

3174 "Incrementing neurasthenia."

3175 )

3176 r.neurasthenia += 1

3177

3178 # --------------------------------------------------------------------

3179 # Concentration/memory

3180 # --------------------------------------------------------------------

3181

3182 elif q == CQ.CONC_MAND1_POOR_CONC_PAST_MONTH:

3183 # Score preceding:

3184 if r.fatigue >= 2:

3185 r.decide(

3186 "fatigue >= 2. "

3187 "Incrementing depr_crit_1_mood_anhedonia_energy."

3188 )

3189 r.depr_crit_1_mood_anhedonia_energy += 1

3190

3191 elif q == CQ.CONC_MAND2_FORGETFUL_PAST_MONTH:

3192 if self.answer_is_no(

3193 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH

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

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

3196 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH)

3197

3198 elif q == CQ.CONC1_CONC_DAYS_PAST_WEEK:

3199 if v == V_DAYS_IN_PAST_WEEK_0:

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

3201 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH)

3202 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3203 r.decide(

3204 "Problems with concentration/memory problems on >=4 "

3205 "days in past week. Incrementing concentration_poor."

3206 )

3207 r.concentration_poor += 1

3208 if self.answer_is_no(

3209 CQ.CONC_MAND1_POOR_CONC_PAST_MONTH

3210 ) and self.answer_is_yes(CQ.CONC_MAND2_FORGETFUL_PAST_MONTH):

3211 r.decide(

3212 "Forgetfulness, not concentration, problems; skip "

3213 "over more detailed concentration questions."

3214 )

3215 jump_to(

3216 CQ.CONC4_FORGOTTEN_IMPORTANT

3217 ) # skip CONC2, CONC3, CONC_DUR

3218

3219 elif q == CQ.CONC2_CONC_FOR_TV_READING_CONVERSATION:

3220 if self.answer_is_no(q, v):

3221 r.decide(

3222 "Couldn't concentrate on at least one of {TV, "

3223 "newspaper, "

3224 "conversation}. Incrementing concentration_poor."

3225 )

3226 r.concentration_poor += 1

3227

3228 elif q == CQ.CONC3_CONC_PREVENTED_ACTIVITIES:

3229 if self.answer_is_yes(q, v):

3230 r.decide(

3231 "Problems with concentration stopped usual/desired "

3232 "activity. Incrementing concentration_poor."

3233 )

3234 r.concentration_poor += 1

3235

3236 elif q == CQ.CONC_DUR:

3237 if self.answer_is_no(CQ.CONC_MAND2_FORGETFUL_PAST_MONTH):

3238 jump_to(CQ.SLEEP_MAND1_LOSS_PAST_MONTH)

3239

3240 elif q == CQ.CONC4_FORGOTTEN_IMPORTANT:

3241 if self.answer_is_yes(q, v):

3242 r.decide(

3243 "Forgotten something important in past week. "

3244 "Incrementing concentration_poor."

3245 )

3246 r.concentration_poor += 1

3247

3248 elif q == CQ.FORGET_DUR:

3249 pass

3250

3251 # --------------------------------------------------------------------

3252 # Sleep

3253 # --------------------------------------------------------------------

3254

3255 elif q == CQ.SLEEP_MAND1_LOSS_PAST_MONTH:

3256 # Score previous block:

3257 if r.concentration_poor >= 2:

3258 r.decide(

3259 "concentration >= 2. "

3260 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

3261 )

3262 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

3263 # This question:

3264 if self.answer_is_no(q, v):

3265 r.decide(

3266 "No problems with sleep loss in past month. " "Moving on."

3267 )

3268 jump_to(CQ.SLEEP_MAND2_GAIN_PAST_MONTH)

3269

3270 elif q == CQ.SLEEP_LOSE1_NIGHTS_PAST_WEEK:

3271 if v == V_NIGHTS_IN_PAST_WEEK_0:

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

3273 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)

3274 elif v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:

3275 r.decide(

3276 "Problems with sleep on >=4 nights in past week. "

3277 "Incrementing sleep_problems."

3278 )

3279 r.sleep_problems += 1

3280

3281 elif q == CQ.SLEEP_LOSE2_DIS_WORST_DURATION:

3282 if v == V_SLEEP_CHANGE_LT_15_MIN:

3283 r.decide(

3284 "Less than 15min maximum delayed initiation of sleep "

3285 "in past week. Moving on."

3286 )

3287 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)

3288 elif v == V_SLEEP_CHANGE_15_MIN_TO_1_H:

3289 r.decide(

3290 "15min-1h maximum delayed initiation of sleep in past "

3291 "week. Incrementing sleep_problems."

3292 )

3293 r.sleep_problems += 1

3294 elif v == V_SLEEP_CHANGE_1_TO_3_H or v == V_SLEEP_CHANGE_GT_3_H:

3295 r.decide(

3296 ">=1h maximum delayed initiation of sleep in past "

3297 "week. Adding 2 to sleep_problems."

3298 )

3299 r.sleep_problems += 2

3300

3301 elif q == CQ.SLEEP_LOSE3_NIGHTS_GT_3H_DIS_PAST_WEEK:

3302 if v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:

3303 r.decide(

3304 ">=4 nights in past week with >=3h delayed "

3305 "initiation of "

3306 "sleep. Incrementing sleep_problems."

3307 )

3308 r.sleep_problems += 1

3309

3310 elif q == CQ.SLEEP_EMW_PAST_WEEK:

3311 if self.answer_is_yes(q, v):

3312 r.decide(

3313 "EMW of >2h in past week. "

3314 "Setting sleep_change to SLEEPCHANGE_EMW. "

3315 "Incrementing depr_crit_3_somatic_synd."

3316 )

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

3318 # "yes" (2) answer).

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

3320 r.sleep_change = SLEEPCHANGE_EMW # LIKELY REDUNDANT.

3321 r.depr_crit_3_somatic_synd += 1

3322 if r.sleep_problems >= 1:

3323 r.decide(

3324 "EMW of >2h in past week and sleep_problems >= 1; "

3325 "setting sleep_change to SLEEPCHANGE_EMW."

3326 )

3327 r.sleep_change = SLEEPCHANGE_EMW

3328 elif self.answer_is_no(q, v):

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

3330 if r.sleep_problems >= 1:

3331 r.decide(

3332 "No EMW of >2h in past week, and sleep_problems "

3333 ">= 1. Setting sleep_change to "

3334 "SLEEPCHANGE_INSOMNIA_NOT_EMW."

3335 )

3336 r.sleep_change = SLEEPCHANGE_INSOMNIA_NOT_EMW

3337

3338 elif q == CQ.SLEEP_CAUSE:

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

3340 jump_to(CQ.SLEEP_DUR)

3341

3342 elif q == CQ.SLEEP_MAND2_GAIN_PAST_MONTH:

3343 if (

3344 v == V_SLEEP_MAND2_NO

3345 or v == V_SLEEP_MAND2_YES_BUT_NOT_A_PROBLEM

3346 ):

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

3348 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)

3349

3350 elif q == CQ.SLEEP_GAIN1_NIGHTS_PAST_WEEK:

3351 if v == V_NIGHTS_IN_PAST_WEEK_0:

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

3353 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)

3354 elif v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:

3355 r.decide(

3356 "Problems with sleep [gain] on >=4 nights in past "

3357 "week. Incrementing sleep_problems."

3358 )

3359 r.sleep_problems += 1

3360

3361 elif q == CQ.SLEEP_GAIN2_EXTRA_ON_LONGEST_NIGHT:

3362 if v == V_SLEEP_CHANGE_LT_15_MIN:

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

3364 jump_to(CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH)

3365 elif v == V_SLEEP_CHANGE_15_MIN_TO_1_H:

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

3367 r.sleep_problems += 1

3368 elif v >= V_SLEEP_CHANGE_1_TO_3_H:

3369 r.decide(

3370 "Sleep gain >=1h. "

3371 "Adding 2 to sleep_problems. "

3372 "Setting sleep_change to SLEEPCHANGE_INCREASE."

3373 )

3374 r.sleep_problems += 2

3375 r.sleep_change = SLEEPCHANGE_INCREASE

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

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

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

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

3380 # was a redundant test (always true).

3381

3382 elif q == CQ.SLEEP_GAIN3_NIGHTS_GT_3H_EXTRA_PAST_WEEK:

3383 if v == V_NIGHTS_IN_PAST_WEEK_4_OR_MORE:

3384 r.decide(

3385 "Sleep gain of >3h on >=4 nights in past week. "

3386 "Incrementing sleep_problems."

3387 )

3388 r.sleep_problems += 1

3389

3390 elif q == CQ.SLEEP_DUR:

3391 pass

3392

3393 # --------------------------------------------------------------------

3394 # Irritability

3395 # --------------------------------------------------------------------

3396

3397 elif q == CQ.IRRIT_MAND1_PEOPLE_PAST_MONTH:

3398 # Score previous block:

3399 if r.sleep_problems >= 2:

3400 r.decide(

3401 "sleep_problems >= 2. "

3402 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

3403 )

3404 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

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

3406 # discussion there:

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

3408 r.decide(

3409 "sleep_problems >=2 and fatigue >=2. "

3410 "Incrementing neurasthenia."

3411 )

3412 r.neurasthenia += 1

3413 # This question:

3414 if self.answer_is_yes(q, v):

3415 r.decide(

3416 "Irritability (people) in past month; exploring "

3417 "further."

3418 )

3419 jump_to(CQ.IRRIT1_DAYS_PER_WEEK)

3420

3421 elif q == CQ.IRRIT_MAND2_THINGS_PAST_MONTH:

3422 if v == V_IRRIT_MAND2_NO:

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

3424 jump_to(CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH)

3425 elif self.answered(q, v):

3426 r.decide(

3427 "Irritability (things) in past month; exploring "

3428 "further."

3429 )

3430

3431 elif q == CQ.IRRIT1_DAYS_PER_WEEK:

3432 if v == V_DAYS_IN_PAST_WEEK_0:

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

3434 jump_to(CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH)

3435 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3436 r.decide(

3437 "Irritable on >=4 days in past week. "

3438 "Incrementing irritability."

3439 )

3440 r.irritability += 1

3441

3442 elif q == CQ.IRRIT2_GT_1H_ANY_DAY:

3443 if self.answer_is_yes(q, v):

3444 r.decide(

3445 "Irritable for >1h on any day in past week. "

3446 "Incrementing irritability."

3447 )

3448 r.irritability += 1

3449

3450 elif q == CQ.IRRIT3_WANTED_TO_SHOUT:

3451 if v >= V_IRRIT3_SHOUTING_WANTED_TO:

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

3453 r.irritability += 1

3454

3455 elif q == CQ.IRRIT4_ARGUMENTS:

3456 if v == V_IRRIT4_ARGUMENTS_YES_UNJUSTIFIED:

3457 r.decide(

3458 "Arguments without justification. "

3459 "Incrementing irritability."

3460 )

3461 r.irritability += 1

3462

3463 elif q == CQ.IRRIT_DUR:

3464 # Score recent things:

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

3466 r.decide(

3467 "irritability >=2 and fatigue >=2. "

3468 "Incrementing neurasthenia."

3469 )

3470 r.neurasthenia += 1

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

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

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

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

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

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

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

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

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

3480

3481 # --------------------------------------------------------------------

3482 # Hypochondriasis

3483 # --------------------------------------------------------------------

3484

3485 elif q == CQ.HYPO_MAND1_WORRIED_RE_HEALTH_PAST_MONTH:

3486 if self.answer_is_yes(q, v):

3487 r.decide(

3488 "No worries about physical health in past month. "

3489 "Moving on."

3490 )

3491 jump_to(CQ.HYPO1_DAYS_PAST_WEEK)

3492

3493 elif q == CQ.HYPO_MAND2_WORRIED_RE_SERIOUS_ILLNESS:

3494 if self.answer_is_no(q, v):

3495 r.decide(

3496 "No worries about having a serious illness. " "Moving on."

3497 )

3498 jump_to(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH)

3499

3500 elif q == CQ.HYPO1_DAYS_PAST_WEEK:

3501 if v == V_DAYS_IN_PAST_WEEK_0:

3502 r.decide(

3503 "No days in past week worrying about health. " "Moving on."

3504 )

3505 jump_to(CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH)

3506 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3507 r.decide(

3508 "Worries about health on >=4 days in past week. "

3509 "Incrementing hypochondria."

3510 )

3511 r.hypochondria += 1

3512

3513 elif q == CQ.HYPO2_WORRY_TOO_MUCH:

3514 if self.answer_is_yes(q, v):

3515 r.decide(

3516 "Worrying too much about health. "

3517 "Incrementing hypochondria."

3518 )

3519 r.hypochondria += 1

3520

3521 elif q == CQ.HYPO3_HOW_UNPLEASANT:

3522 if v >= V_HOW_UNPLEASANT_UNPLEASANT:

3523 r.decide(

3524 "Worrying re health 'unpleasant' or worse in past "

3525 "week. Incrementing hypochondria."

3526 )

3527 r.hypochondria += 1

3528

3529 elif q == CQ.HYPO4_CAN_DISTRACT:

3530 if self.answer_is_no(q, v):

3531 r.decide(

3532 "Cannot take mind off health worries by doing "

3533 "something else. Incrementing hypochondria."

3534 )

3535 r.hypochondria += 1

3536

3537 elif q == CQ.HYPO_DUR:

3538 pass

3539

3540 # --------------------------------------------------------------------

3541 # Depression

3542 # --------------------------------------------------------------------

3543

3544 elif q == CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH:

3545 if self.answer_is_no(q, v):

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

3547 jump_to(CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH)

3548

3549 elif q == CQ.DEPR1_LOW_MOOD_PAST_WEEK:

3550 pass

3551

3552 elif q == CQ.DEPR_MAND2_ENJOYMENT_PAST_MONTH:

3553 if v == V_ANHEDONIA_ENJOYING_NORMALLY and self.answer_is_no(

3554 CQ.DEPR1_LOW_MOOD_PAST_WEEK

3555 ):

3556 r.decide(

3557 "Neither low mood nor anhedonia in past month. "

3558 "Moving on."

3559 )

3560 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH)

3561

3562 elif q == CQ.DEPR2_ENJOYMENT_PAST_WEEK:

3563 if v == V_ANHEDONIA_ENJOYING_NORMALLY and self.answer_is_no(

3564 CQ.DEPR_MAND1_LOW_MOOD_PAST_MONTH

3565 ):

3566 r.decide(

3567 "No anhedonia in past week and no low mood in past "

3568 "month. Moving on."

3569 )

3570 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH)

3571 elif v >= V_ANHEDONIA_ENJOYING_LESS:

3572 r.decide(

3573 "Partial or complete anhedonia in past week. "

3574 "Incrementing depression. "

3575 "Incrementing depr_crit_1_mood_anhedonia_energy. "

3576 "Incrementing depr_crit_3_somatic_synd."

3577 )

3578 r.depression += 1

3579 r.depr_crit_1_mood_anhedonia_energy += 1

3580 r.depr_crit_3_somatic_synd += 1

3581

3582 elif q == CQ.DEPR3_DAYS_PAST_WEEK:

3583 if v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3584 r.decide(

3585 "Low mood or anhedonia on >=4 days in past week. "

3586 "Incrementing depression."

3587 )

3588 r.depression += 1

3589

3590 elif q == CQ.DEPR4_GT_3H_ANY_DAY:

3591 if self.answer_is_yes(q, v):

3592 r.decide(

3593 "Low mood or anhedonia for >3h/day on at least one "

3594 "day in past week. Incrementing depression."

3595 )

3596 r.depression += 1

3597 if self.int_value_for_question(

3598 CQ.DEPR3_DAYS_PAST_WEEK

3599 ) and self.answer_is_yes(CQ.DEPR1_LOW_MOOD_PAST_WEEK):

3600 r.decide(

3601 "(A) Low mood in past week, and "

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

3603 "least one day in past week, and "

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

3605 "week. "

3606 "Incrementing depr_crit_1_mood_anhedonia_energy."

3607 )

3608 r.depr_crit_1_mood_anhedonia_energy += 1

3609

3610 elif q == CQ.DEPR_CONTENT:

3611 pass

3612

3613 elif q == CQ.DEPR5_COULD_CHEER_UP:

3614 if v >= V_DEPR5_COULD_CHEER_UP_SOMETIMES:

3615 r.decide(

3616 "'Sometimes' or 'never' cheered up by nice things. "

3617 "Incrementing depression. "

3618 "Incrementing depr_crit_3_somatic_synd."

3619 )

3620 r.depression += 1

3621 r.depr_crit_3_somatic_synd += 1

3622

3623 elif q == CQ.DEPR_DUR:

3624 if v >= V_DURATION_2W_6M:

3625 r.decide(

3626 "Depressive symptoms for >=2 weeks. "

3627 "Setting depression_at_least_2_weeks."

3628 )

3629 r.depression_at_least_2_weeks = True

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

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

3632 # move it here:

3633 if r.depression == 0:

3634 r.decide(

3635 "Score for 'depression' is 0; skipping over "

3636 "depressive thought content questions."

3637 )

3638 jump_to(CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH)

3639

3640 elif q == CQ.DEPTH1_DIURNAL_VARIATION:

3641 if (

3642 v == V_DEPTH1_DMV_WORSE_MORNING

3643 or v == V_DEPTH1_DMV_WORSE_EVENING

3644 ):

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

3646 r.diurnal_mood_variation = (

3647 DIURNAL_MOOD_VAR_WORSE_MORNING

3648 if v == V_DEPTH1_DMV_WORSE_MORNING

3649 else DIURNAL_MOOD_VAR_WORSE_EVENING

3650 )

3651 if v == V_DEPTH1_DMV_WORSE_MORNING:

3652 r.decide(

3653 "Diurnal mood variation, worse in the mornings. "

3654 "Incrementing depr_crit_3_somatic_synd."

3655 )

3656 r.depr_crit_3_somatic_synd += 1

3657

3658 elif q == CQ.DEPTH2_LIBIDO:

3659 if v == V_DEPTH2_LIBIDO_DECREASED:

3660 r.decide(

3661 "Libido decreased over past month. "

3662 "Setting libido_decreased. "

3663 "Incrementing depr_crit_3_somatic_synd."

3664 )

3665 r.libido_decreased = True

3666 r.depr_crit_3_somatic_synd += 1

3667

3668 elif q == CQ.DEPTH3_RESTLESS:

3669 if self.answer_is_yes(q):

3670 r.decide("Psychomotor agitation.")

3671 r.psychomotor_changes = PSYCHOMOTOR_AGITATION

3672

3673 elif q == CQ.DEPTH4_SLOWED:

3674 if self.answer_is_yes(q):

3675 r.decide("Psychomotor retardation.")

3676 r.psychomotor_changes = PSYCHOMOTOR_RETARDATION

3677 if r.psychomotor_changes > PSYCHOMOTOR_NONE:

3678 r.decide(

3679 "Psychomotor agitation or retardation. "

3680 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui. "

3681 "Incrementing depr_crit_3_somatic_synd."

3682 )

3683 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

3684 r.depr_crit_3_somatic_synd += 1

3685

3686 elif q == CQ.DEPTH5_GUILT:

3687 if v >= V_DEPTH5_GUILT_SOMETIMES:

3688 r.decide(

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

3690 "Incrementing depressive_thoughts. "

3691 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

3692 )

3693 r.depressive_thoughts += 1

3694 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

3695

3696 elif q == CQ.DEPTH6_WORSE_THAN_OTHERS:

3697 if self.answer_is_yes(q, v):

3698 r.decide(

3699 "Feeling not as good as other people. "

3700 "Incrementing depressive_thoughts. "

3701 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

3702 )

3703 r.depressive_thoughts += 1

3704 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

3705

3706 elif q == CQ.DEPTH7_HOPELESS:

3707 if self.answer_is_yes(q, v):

3708 r.decide(

3709 "Hopelessness. "

3710 "Incrementing depressive_thoughts. "

3711 "Setting suicidality to "

3712 "SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS."

3713 )

3714 r.depressive_thoughts += 1

3715 r.suicidality = SUICIDE_INTENT_HOPELESS_NO_SUICIDAL_THOUGHTS

3716

3717 elif q == CQ.DEPTH8_LNWL:

3718 if v == V_DEPTH8_LNWL_NO:

3719 r.decide(

3720 "No thoughts of life not being worth living. "

3721 "Skipping to end of depression section."

3722 )

3723 jump_to(CQ.DEPR_OUTRO)

3724 elif v >= V_DEPTH8_LNWL_SOMETIMES:

3725 r.decide(

3726 "Sometimes or always feeling life isn't worth living. "

3727 "Incrementing depressive_thoughts. "

3728 "Setting suicidality to "

3729 "SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING."

3730 )

3731 r.depressive_thoughts += 1

3732 r.suicidality = SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING

3733

3734 elif q == CQ.DEPTH9_SUICIDE_THOUGHTS:

3735 if v == V_DEPTH9_SUICIDAL_THOUGHTS_NO:

3736 r.decide(

3737 "No thoughts of suicide. Skipping to end of "

3738 "depression section."

3739 )

3740 jump_to(CQ.DEPR_OUTRO)

3741 if v >= V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD:

3742 r.decide(

3743 "Suicidal thoughts present. "

3744 "Setting suicidality to "

3745 "SUICIDE_INTENT_SUICIDAL_THOUGHTS."

3746 )

3747 r.suicidality = SUICIDE_INTENT_SUICIDAL_THOUGHTS

3748 if v == V_DEPTH9_SUICIDAL_THOUGHTS_YES_BUT_NEVER_WOULD:

3749 r.decide(

3750 "Suicidal thoughts present but denies would ever act. "

3751 "Skipping to talk-to-doctor section."

3752 )

3753 jump_to(CQ.DOCTOR)

3754 if v == V_DEPTH9_SUICIDAL_THOUGHTS_YES:

3755 r.decide(

3756 "Thoughts of suicide in past week. "

3757 "Incrementing depressive_thoughts. "

3758 "Incrementing depr_crit_2_app_cnc_slp_mtr_glt_wth_sui."

3759 )

3760 r.depressive_thoughts += 1

3761 r.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui += 1

3762

3763 elif q == CQ.DEPTH10_SUICIDE_METHOD:

3764 if self.answer_is_yes(q, v):

3765 r.decide(

3766 "Suicidal thoughts without denying might ever act. "

3767 "Setting suicidality to "

3768 "SUICIDE_INTENT_SUICIDAL_PLANS."

3769 )

3770 r.suicidality = SUICIDE_INTENT_SUICIDAL_PLANS

3771

3772 elif q == CQ.DOCTOR:

3773 if v == V_DOCTOR_YES:

3774 r.decide(

3775 "Has spoken to doctor about suicidality. Skipping "

3776 "exhortation to do so."

3777 )

3778 jump_to(CQ.DEPR_OUTRO)

3779

3780 elif q == CQ.DOCTOR2_PLEASE_TALK_TO:

3781 pass

3782

3783 elif q == CQ.DEPR_OUTRO:

3784 pass

3785

3786 # --------------------------------------------------------------------

3787 # Worry/anxiety

3788 # --------------------------------------------------------------------

3789

3790 elif q == CQ.WORRY_MAND1_MORE_THAN_NEEDED_PAST_MONTH:

3791 if v >= V_NSO_SOMETIMES:

3792 r.decide(

3793 "Worrying excessively 'sometimes' or 'often'. "

3794 "Exploring further."

3795 )

3796 jump_to(CQ.WORRY_CONT1)

3797

3798 elif q == CQ.WORRY_MAND2_ANY_WORRIES_PAST_MONTH:

3799 if self.answer_is_no(q, v):

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

3801 jump_to(CQ.ANX_MAND1_ANXIETY_PAST_MONTH)

3802

3803 elif q == CQ.WORRY_CONT1:

3804 pass

3805

3806 elif q == CQ.WORRY1_INFO_ONLY:

3807 pass

3808

3809 elif q == CQ.WORRY2_DAYS_PAST_WEEK:

3810 if v == V_DAYS_IN_PAST_WEEK_0:

3811 r.decide(

3812 "Worry [other than re physical health] on 0 days in "

3813 "past week. Moving on."

3814 )

3815 jump_to(CQ.ANX_MAND1_ANXIETY_PAST_MONTH)

3816 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3817 r.decide(

3818 "Worry [other than re physical health] on >=4 days in "

3819 "past week. Incrementing worry."

3820 )

3821 r.worry += 1

3822

3823 elif q == CQ.WORRY3_TOO_MUCH:

3824 if self.answer_is_yes(q, v):

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

3826 r.worry += 1

3827

3828 elif q == CQ.WORRY4_HOW_UNPLEASANT:

3829 if v >= V_HOW_UNPLEASANT_UNPLEASANT:

3830 r.decide(

3831 "Worry [other than re physical health] 'unpleasant' "

3832 "or worse in past week. Incrementing worry."

3833 )

3834 r.worry += 1

3835

3836 elif q == CQ.WORRY5_GT_3H_ANY_DAY:

3837 if self.answer_is_yes(q, v):

3838 r.decide(

3839 "Worry [other than re physical health] for >3h on any "

3840 "day in past week. Incrementing worry."

3841 )

3842 r.worry += 1

3843

3844 elif q == CQ.WORRY_DUR:

3845 pass

3846

3847 elif q == CQ.ANX_MAND1_ANXIETY_PAST_MONTH:

3848 if self.answer_is_yes(q, v):

3849 r.decide(

3850 "Anxious/nervous in past month. "

3851 "Skipping tension question."

3852 )

3853 jump_to(CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH)

3854

3855 elif q == CQ.ANX_MAND2_TENSION_PAST_MONTH:

3856 if v == V_NSO_NO:

3857 r.decide(

3858 "No tension in past month (and no anxiety, from "

3859 "previous question). Moving on."

3860 )

3861 jump_to(CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH)

3862

3863 elif q == CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH:

3864 if self.answer_is_no(q, v):

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

3866 jump_to(CQ.ANX2_GENERAL_DAYS_PAST_WEEK)

3867 elif self.answer_is_yes(q, v):

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

3869 # there (but that only happens when we get a 'yes' answer

3870 # here).

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

3872 r.phobias_flag = True

3873

3874 elif q == CQ.ANX_PHOBIA2_SPECIFIC_OR_GENERAL:

3875 if v == V_ANX_PHOBIA2_ALWAYS_SPECIFIC:

3876 r.decide(

3877 "Anxiety always specific. " "Skipping generalized anxiety."

3878 )

3879 jump_to(CQ.PHOBIAS_TYPE1)

3880

3881 elif q == CQ.ANX1_INFO_ONLY:

3882 pass

3883

3884 elif q == CQ.ANX2_GENERAL_DAYS_PAST_WEEK:

3885 if v == V_DAYS_IN_PAST_WEEK_0:

3886 if r.phobias_flag:

3887 r.decide(

3888 "No generalized anxiety in past week. "

3889 "Skipping further generalized anxiety questions."

3890 )

3891 jump_to(CQ.PHOBIAS1_DAYS_PAST_WEEK)

3892 else:

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

3894 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH)

3895 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3896 r.decide(

3897 "Generalized anxiety on >=4 days in past week. "

3898 "Incrementing anxiety."

3899 )

3900 r.anxiety += 1

3901

3902 elif q == CQ.ANX3_GENERAL_HOW_UNPLEASANT:

3903 if v >= V_HOW_UNPLEASANT_UNPLEASANT:

3904 r.decide(

3905 "Anxiety 'unpleasant' or worse in past week. "

3906 "Incrementing anxiety."

3907 )

3908 r.anxiety += 1

3909

3910 elif q == CQ.ANX4_GENERAL_PHYSICAL_SYMPTOMS:

3911 if self.answer_is_yes(q, v):

3912 r.decide(

3913 "Physical symptoms of anxiety. "

3914 "Setting anxiety_physical_symptoms. "

3915 "Incrementing anxiety."

3916 )

3917 r.anxiety_physical_symptoms = True

3918 r.anxiety += 1

3919

3920 elif q == CQ.ANX5_GENERAL_GT_3H_ANY_DAY:

3921 if self.answer_is_yes(q, v):

3922 r.decide(

3923 "Anxiety for >3h on any day in past week. "

3924 "Incrementing anxiety."

3925 )

3926 r.anxiety += 1

3927

3928 elif q == CQ.ANX_DUR_GENERAL:

3929 if v >= V_DURATION_2W_6M:

3930 r.decide(

3931 "Anxiety for >=2 weeks. "

3932 "Setting anxiety_at_least_2_weeks."

3933 )

3934 r.anxiety_at_least_2_weeks = True

3935 if r.phobias_flag:

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

3937 jump_to(CQ.PHOBIAS_TYPE1)

3938 else:

3939 if r.anxiety <= 1:

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

3941 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH)

3942 else:

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

3944 jump_to(CQ.PANIC_MAND_PAST_MONTH)

3945

3946 elif q == CQ.PHOBIAS_MAND_AVOIDANCE_PAST_MONTH:

3947 if self.answer_is_no(q, v):

3948 if r.anxiety <= 1:

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

3950 jump_to(CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH)

3951 else:

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

3953 jump_to(CQ.PANIC_MAND_PAST_MONTH)

3954

3955 elif q == CQ.PHOBIAS_TYPE1:

3956 if v in (

3957 V_PHOBIAS_TYPE1_ALONE_PUBLIC_TRANSPORT,

3958 V_PHOBIAS_TYPE1_FAR_FROM_HOME,

3959 V_PHOBIAS_TYPE1_CROWDED_SHOPS,

3960 ):

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

3962 r.phobias_type = PHOBIATYPES_AGORAPHOBIA

3963

3964 elif v in (

3965 V_PHOBIAS_TYPE1_PUBLIC_SPEAKING_EATING,

3966 V_PHOBIAS_TYPE1_BEING_WATCHED,

3967 ):

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

3969 r.phobias_type = PHOBIATYPES_SOCIAL

3970

3971 elif v == V_PHOBIAS_TYPE1_BLOOD:

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

3973 r.phobias_type = PHOBIATYPES_BLOOD_INJURY

3974

3975 elif v in (

3976 V_PHOBIAS_TYPE1_ANIMALS,

3977 V_PHOBIAS_TYPE1_ENCLOSED_SPACES_HEIGHTS,

3978 ):

3979 r.decide(

3980 "Phobia type category: animals/enclosed spaces/" "heights."

3981 )

3982 r.phobias_type = PHOBIATYPES_ANIMALS_ENCLOSED_HEIGHTS

3983

3984 elif v == V_PHOBIAS_TYPE1_OTHER:

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

3986 r.phobias_type = PHOBIATYPES_OTHER

3987

3988 else:

3989 pass

3990

3991 elif q == CQ.PHOBIAS1_DAYS_PAST_WEEK:

3992 if v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

3993 r.decide(

3994 "Phobic anxiety on >=4 days in past week. "

3995 "Incrementing phobias_score."

3996 )

3997 r.phobias_score += 1

3998

3999 elif q == CQ.PHOBIAS2_PHYSICAL_SYMPTOMS:

4000 if self.answer_is_yes(q, v):

4001 r.decide(

4002 "Physical symptoms during phobic anxiety in past "

4003 "week. Incrementing phobias_score."

4004 )

4005 r.phobias_score += 1

4006

4007 elif q == CQ.PHOBIAS3_AVOIDANCE:

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

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

4010 r.decide(

4011 "No avoidance in past week; "

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

4013 "Finishing anxiety section."

4014 )

4015 jump_to(CQ.ANX_OUTRO)

4016 else:

4017 r.decide(

4018 "No avoidance in past week; "

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

4020 "Moving to panic section."

4021 )

4022 jump_to(CQ.PANIC_MAND_PAST_MONTH)

4023 elif self.answer_is_yes(q, v):

4024 r.decide("Setting phobic_avoidance.")

4025 r.phobic_avoidance = True

4026

4027 elif q == CQ.PHOBIAS4_AVOIDANCE_DAYS_PAST_WEEK:

4028 if v == V_DAYS_IN_PAST_WEEK_1_TO_3:

4029 r.decide(

4030 "Phobic avoidance on 1-3 days in past week. "

4031 "Incrementing phobias_score."

4032 )

4033 r.phobias_score += 1

4034 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

4035 r.decide(

4036 "Phobic avoidance on >=4 days in past week. "

4037 "Adding 2 to phobias_score."

4038 )

4039 r.phobias_score += 2

4040 if (

4041 r.anxiety <= 1

4042 and self.int_value_for_question(CQ.PHOBIAS1_DAYS_PAST_WEEK)

4043 == V_DAYS_IN_PAST_WEEK_0

4044 ):

4045 r.decide(

4046 "anxiety <= 1 and no phobic anxiety in past week. "

4047 "Finishing anxiety section."

4048 )

4049 jump_to(CQ.ANX_OUTRO)

4050

4051 elif q == CQ.PHOBIAS_DUR:

4052 pass

4053

4054 elif q == CQ.PANIC_MAND_PAST_MONTH:

4055 if v == V_NSO_NO:

4056 r.decide(

4057 "No panic in the past month. Finishing anxiety " "section."

4058 )

4059 jump_to(CQ.ANX_OUTRO)

4060

4061 elif q == CQ.PANIC1_NUM_PAST_WEEK:

4062 if v == V_PANIC1_N_PANICS_PAST_WEEK_0:

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

4064 jump_to(CQ.ANX_OUTRO)

4065 elif v == V_PANIC1_N_PANICS_PAST_WEEK_1:

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

4067 r.panic += 1

4068 elif v == V_PANIC1_N_PANICS_PAST_WEEK_GT_1:

4069 r.decide(

4070 "More than one panic in past week. Adding 2 to panic."

4071 )

4072 r.panic += 2

4073

4074 elif q == CQ.PANIC2_HOW_UNPLEASANT:

4075 if v >= V_HOW_UNPLEASANT_UNPLEASANT:

4076 r.decide(

4077 "Panic 'unpleasant' or worse in past week. "

4078 "Incrementing panic."

4079 )

4080 r.panic += 1

4081

4082 elif q == CQ.PANIC3_PANIC_GE_10_MIN:

4083 if v == V_PANIC3_WORST_GE_10_MIN:

4084 r.decide(

4085 "Worst panic in past week lasted >=10 min. "

4086 "Incrementing panic."

4087 )

4088 r.panic += 1

4089

4090 elif q == CQ.PANIC4_RAPID_ONSET:

4091 if self.answer_is_yes(q, v):

4092 r.decide(

4093 "Rapid onset of panic symptoms. "

4094 "Setting panic_rapid_onset."

4095 )

4096 r.panic_rapid_onset = True

4097

4098 elif q == CQ.PANSYM:

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

4100 n_panic_symptoms = 0

4101 for panic_fn in PANIC_SYMPTOM_FIELDNAMES:

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

4103 yes_present = panic_symptom == 2

4104 if yes_present:

4105 n_panic_symptoms += 1

4106 r.decide(

4107 f"{n_panic_symptoms} out of "

4108 f"{NUM_PANIC_SYMPTOMS} specific panic symptoms endorsed."

4109 )

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

4111 if self.answer_is_no(CQ.ANX_PHOBIA1_SPECIFIC_PAST_MONTH):

4112 jump_to(CQ.PANIC_DUR)

4113

4114 elif q == CQ.PANIC5_ALWAYS_SPECIFIC_TRIGGER:

4115 pass

4116

4117 elif q == CQ.PANIC_DUR:

4118 pass

4119

4120 elif q == CQ.ANX_OUTRO:

4121 pass

4122

4123 # --------------------------------------------------------------------

4124 # Compulsions and obsessions

4125 # --------------------------------------------------------------------

4126

4127 elif q == CQ.COMP_MAND1_COMPULSIONS_PAST_MONTH:

4128 if v == V_NSO_NO:

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

4130 jump_to(CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH)

4131

4132 elif q == CQ.COMP1_DAYS_PAST_WEEK:

4133 if v == V_DAYS_IN_PAST_WEEK_0:

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

4135 jump_to(CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH)

4136 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

4137 r.decide(

4138 "Obsessions on >=4 days in past week. "

4139 "Incrementing compulsions."

4140 )

4141 r.compulsions += 1

4142

4143 elif q == CQ.COMP2_TRIED_TO_STOP:

4144 if self.answer_is_yes(q, v):

4145 r.decide(

4146 "Attempts to stop compulsions in past week. "

4147 "Setting compulsions_tried_to_stop. "

4148 "Incrementing compulsions."

4149 )

4150 r.compulsions_tried_to_stop = True

4151 r.compulsions += 1

4152

4153 elif q == CQ.COMP3_UPSETTING:

4154 if self.answer_is_yes(q, v):

4155 r.decide(

4156 "Compulsions upsetting/annoying. "

4157 "Incrementing compulsions."

4158 )

4159 r.compulsions += 1

4160

4161 elif q == CQ.COMP4_MAX_N_REPETITIONS:

4162 if v == V_COMP4_MAX_N_REPEATS_GE_3:

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

4164 r.compulsions += 1

4165

4166 elif q == CQ.COMP_DUR:

4167 if v >= V_DURATION_2W_6M:

4168 r.decide(

4169 "Compulsions for >=2 weeks. "

4170 "Setting compulsions_at_least_2_weeks."

4171 )

4172 r.compulsions_at_least_2_weeks = True

4173

4174 elif q == CQ.OBSESS_MAND1_OBSESSIONS_PAST_MONTH:

4175 if v == V_NSO_NO:

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

4177 jump_to(r.get_final_page())

4178

4179 elif q == CQ.OBSESS_MAND2_SAME_THOUGHTS_OR_GENERAL:

4180 if v == V_OBSESS_MAND1_GENERAL_WORRIES:

4181 r.decide(

4182 "Worrying about something in general, not the same "

4183 "thoughts over and over again. Moving on."

4184 )

4185 jump_to(r.get_final_page())

4186

4187 elif q == CQ.OBSESS1_DAYS_PAST_WEEK:

4188 if v == V_DAYS_IN_PAST_WEEK_0:

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

4190 jump_to(r.get_final_page())

4191 elif v == V_DAYS_IN_PAST_WEEK_4_OR_MORE:

4192 r.decide(

4193 "Obsessions on >=4 days in past week. "

4194 "Incrementing obsessions."

4195 )

4196 r.obsessions += 1

4197

4198 elif q == CQ.OBSESS2_TRIED_TO_STOP:

4199 if self.answer_is_yes(q, v):

4200 r.decide(

4201 "Tried to stop obsessional thoughts in past week. "

4202 "Setting obsessions_tried_to_stop. "

4203 "Incrementing obsessions."

4204 )

4205 r.obsessions_tried_to_stop = True

4206 r.obsessions += 1

4207

4208 elif q == CQ.OBSESS3_UPSETTING:

4209 if self.answer_is_yes(q, v):

4210 r.decide(

4211 "Obsessions upsetting/annoying in past week. "

4212 "Incrementing obsessions."

4213 )

4214 r.obsessions += 1

4215

4216 elif q == CQ.OBSESS4_MAX_DURATION:

4217 if v == V_OBSESS4_GE_15_MIN:

4218 r.decide(

4219 "Obsessions lasting >=15 min in past week. "

4220 "Incrementing obsessions."

4221 )

4222 r.obsessions += 1

4223

4224 elif q == CQ.OBSESS_DUR:

4225 if v >= V_DURATION_2W_6M:

4226 r.decide(

4227 "Obsessions for >=2 weeks. "

4228 "Setting obsessions_at_least_2_weeks."

4229 )

4230 r.obsessions_at_least_2_weeks = True

4231

4232 # --------------------------------------------------------------------

4233 # End

4234 # --------------------------------------------------------------------

4235

4236 elif q == CQ.OVERALL1_INFO_ONLY:

4237 pass

4238

4239 elif q == CQ.OVERALL2_IMPACT_PAST_WEEK:

4240 if self.answered(q, v):

4241 r.functional_impairment = v - 1

4242 r.decide(

4243 f"Setting functional_impairment to "

4244 f"{r.functional_impairment}"

4245 )

4246

4247 elif q == CQ.THANKS_FINISHED:

4248 pass

4249

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

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

4252 return CQ.END_MARKER

4253

4254 else:

4255 pass

4256

4257 if next_q == -1:

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

4259 next_q = enum_to_int(q) + 1

4260

4261 return int_to_enum(next_q)

4262

4263 def get_result(

4264 self,

4265 req: Optional[CamcopsRequest] = None,

4266 record_decisions: bool = False,

4267 ) -> CisrResult:

4268 # internal_q = CQ.START_MARKER

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

4270 result = CisrResult(req, record_decisions)

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

4272 internal_q = self.next_q(internal_q, result)

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

4274 result.finalize()

4275 return result

4276

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

4278 res = self.get_result(req)

4279 if res.incomplete:

4280 return CTV_INCOMPLETE

4281 return [

4282 CtvInfo(

4283 content=(

4284 f"{res.caveat_prefix}Possible primary diagnosis: "

4285 f"{bold(res.diagnosis_1_name())} "

4286 f"({res.diagnosis_1_icd10_code()})"

4287 )

4288 ),

4289 CtvInfo(

4290 content=(

4291 f"{res.caveat_prefix}Possible secondary diagnosis: "

4292 f"{bold(res.diagnosis_2_name())} "

4293 f"({res.diagnosis_2_icd10_code()})"

4294 )

4295 ),

4296 CtvInfo(

4297 content=(

4298 f"CIS-R suicide intent: "

4299 f"{self.get_suicide_intent(req, res, with_warning=False)}"

4300 )

4301 ),

4302 ]

4303

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

4305 result = self.get_result(req)

4306 return self.standard_task_summary_fields() + [

4307 # Diagnoses

4308 SummaryElement(

4309 name="diagnosis_1_code",

4310 coltype=Integer(),

4311 value=result.diagnosis_1,

4312 comment="Possible primary diagnosis (CIS-R code)",

4313 ),

4314 SummaryElement(

4315 name="diagnosis_1_text",

4316 coltype=UnicodeText(),

4317 value=result.diagnosis_1_name(),

4318 comment="Possible primary diagnosis (text)",

4319 ),

4320 SummaryElement(

4321 name="diagnosis_1_icd10",

4322 coltype=UnicodeText(),

4323 value=result.diagnosis_1_icd10_code(),

4324 comment="Possible primary diagnosis (ICD-10 code/codes)",

4325 ),

4326 SummaryElement(

4327 name="diagnosis_2_code",

4328 coltype=Integer(),

4329 value=result.diagnosis_2,

4330 comment="Possible secondary diagnosis (CIS-R code)",

4331 ),

4332 SummaryElement(

4333 name="diagnosis_2_text",

4334 coltype=UnicodeText(),

4335 value=result.diagnosis_2_icd10_code(),

4336 comment="Possible secondary diagnosis (text)",

4337 ),

4338 SummaryElement(

4339 name="diagnosis_2_icd10",

4340 coltype=UnicodeText(),

4341 value=result.diagnosis_2_icd10_code(),

4342 comment="Possible secondary diagnosis (ICD-10 code/codes)",

4343 ),

4344 # Suicidality/doctell: directly encoded in data

4345 # Total score

4346 SummaryElement(

4347 name="score_total",

4348 coltype=Integer(),

4349 value=result.get_score(),

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

4351 ),

4352 # Functional impairment: directly encoded in data

4353 # Subscores

4354 SummaryElement(

4355 name="score_somatic_symptoms",

4356 coltype=Integer(),

4357 value=result.somatic_symptoms,

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

4359 ),

4360 SummaryElement(

4361 name="score_hypochondria",

4362 coltype=Integer(),

4363 value=result.hypochondria,

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

4365 ),

4366 SummaryElement(

4367 name="score_irritability",

4368 coltype=Integer(),

4369 value=result.irritability,

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

4371 ),

4372 SummaryElement(

4373 name="score_concentration_poor",

4374 coltype=Integer(),

4375 value=result.concentration_poor,

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

4377 ),

4378 SummaryElement(

4379 name="score_fatigue",

4380 coltype=Integer(),

4381 value=result.fatigue,

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

4383 ),

4384 SummaryElement(

4385 name="score_sleep_problems",

4386 coltype=Integer(),

4387 value=result.sleep_problems,

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

4389 ),

4390 SummaryElement(

4391 name="score_depression",

4392 coltype=Integer(),

4393 value=result.depression,

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

4395 ),

4396 SummaryElement(

4397 name="score_depressive_thoughts",

4398 coltype=Integer(),

4399 value=result.depressive_thoughts,

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

4401 ),

4402 SummaryElement(

4403 name="score_phobias",

4404 coltype=Integer(),

4405 value=result.phobias_score,

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

4407 ),

4408 SummaryElement(

4409 name="score_worry",

4410 coltype=Integer(),

4411 value=result.worry,

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

4413 ),

4414 SummaryElement(

4415 name="score_anxiety",

4416 coltype=Integer(),

4417 value=result.anxiety,

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

4419 ),

4420 SummaryElement(

4421 name="score_panic",

4422 coltype=Integer(),

4423 value=result.panic,

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

4425 ),

4426 SummaryElement(

4427 name="score_compulsions",

4428 coltype=Integer(),

4429 value=result.compulsions,

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

4431 ),

4432 SummaryElement(

4433 name="score_obsessions",

4434 coltype=Integer(),

4435 value=result.obsessions,

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

4437 ),

4438 # Other

4439 SummaryElement(

4440 name="sleep_change",

4441 coltype=Integer(),

4442 value=result.sleep_change,

4443 comment=DESC_SLEEP_CHANGE,

4444 ),

4445 SummaryElement(

4446 name="weight_change",

4447 coltype=Integer(),

4448 value=result.weight_change,

4449 comment=DESC_WEIGHT_CHANGE,

4450 ),

4451 SummaryElement(

4452 name="depcrit1_score",

4453 coltype=Integer(),

4454 value=result.depr_crit_1_mood_anhedonia_energy,

4455 comment=DESC_DEPCRIT1,

4456 ),

4457 SummaryElement(

4458 name="depcrit2_score",

4459 coltype=Integer(),

4460 value=result.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui,

4461 comment=DESC_DEPCRIT2,

4462 ),

4463 SummaryElement(

4464 name="depcrit3_score",

4465 coltype=Integer(),

4466 value=result.depr_crit_3_somatic_synd,

4467 comment=DESC_DEPCRIT3,

4468 ),

4469 SummaryElement(

4470 name="depcrit3_met_somatic_syndrome",

4471 coltype=Boolean(),

4472 value=result.has_somatic_syndrome(),

4473 comment=DESC_DEPCRIT3_MET,

4474 ),

4475 SummaryElement(

4476 name="neurasthenia_score",

4477 coltype=Integer(),

4478 value=result.neurasthenia,

4479 comment=DESC_NEURASTHENIA_SCORE,

4480 ),

4481 # Disorder flags

4482 SummaryElement(

4483 name="disorder_ocd",

4484 coltype=Boolean(),

4485 value=result.obsessive_compulsive_disorder,

4486 comment=DISORDER_OCD,

4487 ),

4488 SummaryElement(

4489 name="disorder_depression_mild",

4490 coltype=Boolean(),

4491 value=result.depression_mild,

4492 comment=DISORDER_DEPR_MILD,

4493 ),

4494 SummaryElement(

4495 name="disorder_depression_moderate",

4496 coltype=Boolean(),

4497 value=result.depression_moderate,

4498 comment=DISORDER_DEPR_MOD,

4499 ),

4500 SummaryElement(

4501 name="disorder_depression_severe",

4502 coltype=Boolean(),

4503 value=result.depression_severe,

4504 comment=DISORDER_DEPR_SEV,

4505 ),

4506 SummaryElement(

4507 name="disorder_cfs",

4508 coltype=Boolean(),

4509 value=result.chronic_fatigue_syndrome,

4510 comment=DISORDER_CFS,

4511 ),

4512 SummaryElement(

4513 name="disorder_gad",

4514 coltype=Boolean(),

4515 value=result.generalized_anxiety_disorder,

4516 comment=DISORDER_GAD,

4517 ),

4518 SummaryElement(

4519 name="disorder_agoraphobia",

4520 coltype=Boolean(),

4521 value=result.phobia_agoraphobia,

4522 comment=DISORDER_AGORAPHOBIA,

4523 ),

4524 SummaryElement(

4525 name="disorder_social_phobia",

4526 coltype=Boolean(),

4527 value=result.phobia_social,

4528 comment=DISORDER_SOCIAL_PHOBIA,

4529 ),

4530 SummaryElement(

4531 name="disorder_specific_phobia",

4532 coltype=Boolean(),

4533 value=result.phobia_specific,

4534 comment=DISORDER_SPECIFIC_PHOBIA,

4535 ),

4536 SummaryElement(

4537 name="disorder_panic_disorder",

4538 coltype=Boolean(),

4539 value=result.panic_disorder,

4540 comment=DISORDER_PANIC,

4541 ),

4542 ]

4543

4544 def is_complete(self) -> bool:

4545 result = self.get_result()

4546 return not result.incomplete

4547

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

4549 xstring_name = f"diag_{diagnosis_code}_desc"

4550 return self.wxstring(req, xstring_name)

4551

4552 def diagnosis_reason(

4553 self, req: CamcopsRequest, diagnosis_code: int

4554 ) -> str:

4555 xstring_name = f"diag_{diagnosis_code}_explan"

4556 return self.wxstring(req, xstring_name)

4557

4558 def get_suicide_intent(

4559 self,

4560 req: CamcopsRequest,

4561 result: CisrResult,

4562 with_warning: bool = True,

4563 ) -> str:

4564 if result.incomplete:

4565 html = "TASK INCOMPLETE. SO FAR: "

4566 else:

4567 html = ""

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

4569 if (

4570 with_warning

4571 and result.suicidality >= SUICIDE_INTENT_LIFE_NOT_WORTH_LIVING

4572 ):

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

4574 if result.suicidality != SUICIDE_INTENT_NONE:

4575 html = bold(html)

4576 return html

4577

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

4579 if self.doctor is None:

4580 return ""

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

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

4583

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

4585 if result.sleep_change == SLEEPCHANGE_NONE:

4586 return ""

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

4588

4589 def get_weight_change(

4590 self, req: CamcopsRequest, result: CisrResult

4591 ) -> str:

4592 if result.weight_change in (

4593 WTCHANGE_NONE_OR_APPETITE_INCREASE,

4594 WTCHANGE_APPETITE_LOSS,

4595 ):

4596 return ""

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

4598

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

4600 return self.wxstring(req, f"impair_{result.functional_impairment}")

4601

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

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

4604

4605 def qa_row(q_: CisrQuestion, qtext: str, a_: Optional[str]) -> str:

4606 return tr(

4607 f"{q_.value}. {qtext}", answer(a_, formatter_answer=bold)

4608 )

4609

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

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

4612

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

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

4615 q = CQ.ETHNIC # type: CisrQuestion

4616 result = CisrResult(req, record_decisions=True)

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

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

4619 # incomplete.

4620 # noinspection PyTypeChecker

4621 target_list = (

4622 demographics_html_list

4623 if q.value < CQ.HEALTH_WELLBEING.value

4624 else question_html_list

4625 )

4626 if q in QUESTIONS_PROMPT_ONLY:

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

4628 target_list.append(qa_row(q, question, NOT_APPLICABLE_TEXT))

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

4630 target_list.append(

4631 qa_row(

4632 q,

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

4634 NOT_APPLICABLE_TEXT,

4635 )

4636 )

4637 for fieldname in PANIC_SYMPTOM_FIELDNAMES:

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

4639 value = getattr(self, fieldname)

4640 a = get_yes_no_none(

4641 req, value == 2 if value is not None else None

4642 )

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

4644 else:

4645 fieldname = fieldname_for_q(q)

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

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

4648 a = self.get_textual_answer(req, q)

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

4650

4651 q = self.next_q(q, result)

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

4653 result.finalize()

4654 caveat_p = result.caveat_prefix

4655 caveat_s = result.caveat_suffix

4656

4657 is_complete = not result.incomplete

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

4659 (

4660 "<td>"

4661 if is_complete

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

4663 ),

4664 get_yes_no(req, is_complete),

4665 )

4666

4667 summary_rows = [

4668 subheading_spanning_two_columns(f"Possible diagnoses{caveat_s}"),

4669 tr(

4670 f"{caveat_p}Possible primary diagnosis",

4671 (

4672 bold(self.diagnosis_name(req, result.diagnosis_1))

4673 + (

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

4675 if result.has_diagnosis_1()

4676 else ""

4677 )

4678 ),

4679 ),

4680 tr(

4681 italic(f"... {caveat_p}summary of reasons/description"),

4682 italic(

4683 caveat_p + self.diagnosis_reason(req, result.diagnosis_1)

4684 ),

4685 ),

4686 tr(

4687 f"{caveat_p}Possible secondary diagnosis",

4688 (

4689 bold(self.diagnosis_name(req, result.diagnosis_2))

4690 + (

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

4692 if result.has_diagnosis_2()

4693 else ""

4694 )

4695 ),

4696 ),

4697 tr(

4698 italic(f"... {caveat_p}summary of reasons/description"),

4699 italic(

4700 caveat_p + self.diagnosis_reason(req, result.diagnosis_2)

4701 ),

4702 ),

4703 subheading_spanning_two_columns("Suicidality"),

4704 tr(

4705 td(self.wxstring(req, "suicid_heading")),

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

4707 literal=True,

4708 ),

4709 tr("... spoken to doctor?", self.get_doctell(req)),

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

4711 tr(

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

4713 result.get_score(),

4714 ),

4715 tr(

4716 self.wxstring(req, "impair_label"),

4717 self.get_impairment(req, result),

4718 ),

4719 subheading_spanning_two_columns(

4720 "Subscores contributing to total " "<sup>[2]</sup>"

4721 ),

4722 tr(

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

4724 result.somatic_symptoms,

4725 ),

4726 tr(

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

4728 result.hypochondria,

4729 ),

4730 tr(

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

4732 result.irritability,

4733 ),

4734 tr(

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

4736 result.concentration_poor,

4737 ),

4738 tr(

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

4740 result.fatigue,

4741 ),

4742 tr(

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

4744 result.sleep_problems,

4745 ),

4746 tr(

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

4748 result.depression,

4749 ),

4750 tr(

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

4752 result.depressive_thoughts,

4753 ),

4754 tr(

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

4756 result.phobias_score,

4757 ),

4758 tr(

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

4760 result.worry,

4761 ),

4762 tr(

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

4764 result.anxiety,

4765 ),

4766 tr(

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

4768 result.panic,

4769 ),

4770 tr(

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

4772 result.compulsions,

4773 ),

4774 tr(

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

4776 result.obsessions,

4777 ),

4778 subheading_spanning_two_columns("Other"),

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

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

4781 tr(DESC_DEPCRIT1, result.depr_crit_1_mood_anhedonia_energy),

4782 tr(DESC_DEPCRIT2, result.depr_crit_2_app_cnc_slp_mtr_glt_wth_sui),

4783 tr(DESC_DEPCRIT3, result.depr_crit_3_somatic_synd),

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

4785 tr(DESC_NEURASTHENIA_SCORE, result.neurasthenia),

4786 subheading_spanning_two_columns(f"Disorder flags{caveat_s}"),

4787 tr(DISORDER_OCD, result.obsessive_compulsive_disorder),

4788 tr(DISORDER_DEPR_MILD, result.depression_mild),

4789 tr(DISORDER_DEPR_MOD, result.depression_moderate),

4790 tr(DISORDER_DEPR_SEV, result.depression_severe),

4791 tr(DISORDER_CFS, result.chronic_fatigue_syndrome),

4792 tr(DISORDER_GAD, result.generalized_anxiety_disorder),

4793 tr(DISORDER_AGORAPHOBIA, result.phobia_agoraphobia),

4794 tr(DISORDER_SOCIAL_PHOBIA, result.phobia_social),

4795 tr(DISORDER_SPECIFIC_PHOBIA, result.phobia_specific),

4796 tr(DISORDER_PANIC, result.panic_disorder),

4797 ]

4798

4799 return f"""

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

4801 <div>

4802 <b>Important note:</b> The CIS-R is a structured automated

4803 interview, yielding suggested possible ICD-10 diagnoses for

4804 depressive and anxiety disorders. These suggestions may be

4805 wrong; for example, the algorithm fails to consider many

4806 exclusion criteria (e.g. relating to psychoactive substance use

4807 or organic mental disorder). The suggestions are not a

4808 substitute for diagnosis by a qualified clinician. Original

4809 CIS-R warning:

4810 </div>

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

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

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

4814 <tr>

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

4816 {is_complete_html_td}

4817 </tr>

4818 {"".join(summary_rows)}

4819 </table>

4820 </div>

4821

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

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

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

4825 </div>

4826

4827 <div class="{CssClass.HEADING}">

4828 Preamble/demographics (not contributing to algorithm)

4829 </div>

4830 <table class="{CssClass.TASKDETAIL}">

4831 <tr>

4832 <th width="75%">Page</th>

4833 <th width="25%">Answer</td>

4834 </tr>

4835 {"".join(demographics_html_list)}

4836 </table>

4837

4838 <div class="{CssClass.HEADING}">

4839 Data considered by algorithm (may be a subset of all data if

4840 subject revised answers)

4841 </div>

4842 <table class="{CssClass.TASKDETAIL}">

4843 <tr>

4844 <th width="75%">Page</th>

4845 <th width="25%">Answer</td>

4846 </tr>

4847 {"".join(question_html_list)}

4848 </table>

4849

4850 <div class="{CssClass.HEADING}">Decisions</div>

4851 <pre>{"<br>".join(ws.webify("‣ " + x) for x in result.decisions)}</pre>

4852

4853 <div class="{CssClass.COPYRIGHT}">

4854 • Original papers:

4855

4856 ▶ Lewis G, Pelosi AJ, Aray R, Dunn G (1992).

4857 Measuring psychiatric disorder in the community: a standardized

4858 assessment for use by lay interviewers.

4859 Psychological Medicine 22: 465-486. {pmid(1615114)}.

4860

4861 ▶ Lewis G (1994).

4862 Assessing psychiatric disorder with a human interviewer or a

4863 computer.

4864 J Epidemiol Community Health 48: 207-210. {pmid(8189180)}.

4865

4866 • Source/copyright: Glyn Lewis.

4867

4868 ▶ The task itself is not in the reference publications, so

4869 copyright presumed to rest with the authors (not the journals).

4870

4871 ▶ “There are no copyright issues with the CISR so please adapt

4872 it for use.” — Prof. Glyn Lewis, personal communication to

4873 Rudolf Cardinal, 27 Oct 2017.

4874 </div>

4875 """ # noqa