Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/khandaker_mojo_medical.py 

5 

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

7 

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

9 

10 This file is part of CamCOPS. 

11 

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

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

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

15 (at your option) any later version. 

16 

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

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

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

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

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

24 

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

26 

27""" 

28 

29from typing import Any, Dict, Tuple, Type 

30 

31 

32from sqlalchemy.ext.declarative import DeclarativeMeta 

33from sqlalchemy.sql.sqltypes import Date, Float, Integer, UnicodeText 

34 

35from camcops_server.cc_modules.cc_constants import CssClass 

36from camcops_server.cc_modules.cc_html import tr_qa 

37from camcops_server.cc_modules.cc_request import CamcopsRequest 

38from camcops_server.cc_modules.cc_sqla_coltypes import ( 

39 BoolColumn, 

40 CamcopsColumn, 

41 ZERO_TO_TWO_CHECKER, 

42) 

43from camcops_server.cc_modules.cc_task import ( 

44 Task, 

45 TaskHasPatientMixin, 

46) 

47 

48 

49class KhandakerMojoMedicalMetaclass(DeclarativeMeta): 

50 # noinspection PyInitNewSignature 

51 def __init__(cls: Type['KhandakerMojoMedical'], 

52 name: str, 

53 bases: Tuple[Type, ...], 

54 classdict: Dict[str, Any]) -> None: 

55 setattr( 

56 cls, cls.FN_DIAGNOSIS, 

57 CamcopsColumn( 

58 cls.FN_DIAGNOSIS, Integer, 

59 permitted_value_checker=ZERO_TO_TWO_CHECKER, 

60 comment=("Diagnosis (0 Rheumatoid Arthritis, " 

61 "1 Ankylosing Spondylitis, 2 Sjögren’s Syndrome)") 

62 ) 

63 ) 

64 setattr( 

65 cls, cls.FN_DIAGNOSIS_DATE, 

66 CamcopsColumn( 

67 cls.FN_DIAGNOSIS_DATE, Date, 

68 comment=("Date of first diagnosis (may be approx from " 

69 "'duration of illness (years))'") 

70 ) 

71 ) 

72 setattr( 

73 cls, cls.FN_DIAGNOSIS_DATE_APPROXIMATE, 

74 BoolColumn( 

75 cls.FN_DIAGNOSIS_DATE_APPROXIMATE, 

76 comment="True if diagnosis date was derived from duration" 

77 ) 

78 ) 

79 setattr( 

80 cls, cls.FN_HAS_FIBROMYALGIA, 

81 BoolColumn( 

82 cls.FN_HAS_FIBROMYALGIA, 

83 comment="Do you have a diagnosis of fibromyalgia?" 

84 ) 

85 ) 

86 setattr( 

87 cls, cls.FN_IS_PREGNANT, 

88 BoolColumn( 

89 cls.FN_IS_PREGNANT, 

90 comment=("Are you, or is there any possibility that you might " 

91 "be pregnant?") 

92 ) 

93 ) 

94 setattr( 

95 cls, cls.FN_HAS_INFECTION_PAST_MONTH, 

96 BoolColumn( 

97 cls.FN_HAS_INFECTION_PAST_MONTH, 

98 comment=("Do you currently have an infection, or had " 

99 "treatment for an infection (e.g antibiotics) " 

100 "in the past month?") 

101 ) 

102 ) 

103 setattr( 

104 cls, cls.FN_HAD_INFECTION_TWO_MONTHS_PRECEDING, 

105 BoolColumn( 

106 cls.FN_HAD_INFECTION_TWO_MONTHS_PRECEDING, 

107 comment=("Have you had an infection, or had treatment for " 

108 "an infection (e.g antibiotics) in the 2 months " 

109 "preceding last month?"), 

110 constraint_name="ck_kh2mm_had_infection" 

111 ) 

112 ) 

113 setattr( 

114 cls, cls.FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE, 

115 BoolColumn( 

116 cls.FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE, 

117 comment=("Do you have a current diagnosis of alcohol or " 

118 "substance dependence?"), 

119 constraint_name="ck_kh2mm_has_alcohol" 

120 ) 

121 ) 

122 setattr( 

123 cls, cls.FN_SMOKING_STATUS, 

124 CamcopsColumn( 

125 cls.FN_SMOKING_STATUS, Integer, 

126 permitted_value_checker=ZERO_TO_TWO_CHECKER, 

127 comment=("What is your smoking status? (0 Never smoked, " 

128 "1 Ex-smoker, 2 Current smoker)") 

129 ) 

130 ) 

131 setattr( 

132 cls, cls.FN_ALCOHOL_UNITS_PER_WEEK, 

133 CamcopsColumn( 

134 cls.FN_ALCOHOL_UNITS_PER_WEEK, Float, 

135 comment=("How much alcohol do you drink per week? (medium " 

136 "glass of wine = 2 units, pint of beer at 4.5% = " 

137 "2.5 units, 25ml of spirits at 40% = 1 unit)") 

138 ) 

139 ) 

140 setattr( 

141 cls, cls.FN_DEPRESSION, 

142 BoolColumn( 

143 cls.FN_DEPRESSION, 

144 comment=("Have you had any of the following conditions " 

145 "diagnosed by a doctor?") 

146 ) 

147 ) 

148 setattr( 

149 cls, cls.FN_BIPOLAR_DISORDER, 

150 BoolColumn( 

151 cls.FN_BIPOLAR_DISORDER, 

152 comment=("Have you had any of the following conditions " 

153 "diagnosed by a doctor?") 

154 ) 

155 ) 

156 setattr( 

157 cls, cls.FN_SCHIZOPHRENIA, 

158 BoolColumn( 

159 cls.FN_SCHIZOPHRENIA, 

160 comment=("Have you had any of the following conditions " 

161 "diagnosed by a doctor?") 

162 ) 

163 ) 

164 setattr( 

165 cls, cls.FN_AUTISM, 

166 BoolColumn( 

167 cls.FN_AUTISM, 

168 comment=("Have you had any of the following conditions " 

169 "diagnosed by a doctor?") 

170 ) 

171 ) 

172 setattr( 

173 cls, cls.FN_PTSD, 

174 BoolColumn( 

175 cls.FN_PTSD, 

176 comment=("Have you had any of the following conditions " 

177 "diagnosed by a doctor?") 

178 ) 

179 ) 

180 setattr( 

181 cls, cls.FN_ANXIETY, 

182 BoolColumn( 

183 cls.FN_ANXIETY, 

184 comment=("Have you had any of the following conditions " 

185 "diagnosed by a doctor?") 

186 ) 

187 ) 

188 setattr( 

189 cls, cls.FN_PERSONALITY_DISORDER, 

190 BoolColumn( 

191 cls.FN_PERSONALITY_DISORDER, 

192 comment=("Have you had any of the following conditions " 

193 "diagnosed by a doctor?") 

194 ) 

195 ) 

196 setattr( 

197 cls, cls.FN_INTELLECTUAL_DISABILITY, 

198 BoolColumn( 

199 cls.FN_INTELLECTUAL_DISABILITY, 

200 comment=("Have you had any of the following conditions " 

201 "diagnosed by a doctor?") 

202 ) 

203 ) 

204 setattr( 

205 cls, cls.FN_OTHER_MENTAL_ILLNESS, 

206 BoolColumn( 

207 cls.FN_OTHER_MENTAL_ILLNESS, 

208 comment=("Have you had any of the following conditions " 

209 "diagnosed by a doctor?") 

210 ) 

211 ) 

212 setattr( 

213 cls, cls.FN_OTHER_MENTAL_ILLNESS_DETAILS, 

214 CamcopsColumn( 

215 cls.FN_OTHER_MENTAL_ILLNESS_DETAILS, UnicodeText, 

216 comment="If other, please list here" 

217 ) 

218 ) 

219 setattr( 

220 cls, cls.FN_HOSPITALISED_IN_LAST_YEAR, 

221 BoolColumn( 

222 cls.FN_HOSPITALISED_IN_LAST_YEAR, 

223 comment=("Have you had a physical or mental illness " 

224 "requiring hospitalisation in the previous 12 " 

225 "months?") 

226 ) 

227 ) 

228 setattr( 

229 cls, cls.FN_HOSPITALISATION_DETAILS, 

230 CamcopsColumn( 

231 cls.FN_HOSPITALISATION_DETAILS, UnicodeText, 

232 comment=("If yes, please list here (name of illness, number " 

233 "of hospitilisations and duration):") 

234 ) 

235 ) 

236 setattr( 

237 cls, cls.FN_FAMILY_DEPRESSION, 

238 BoolColumn( 

239 cls.FN_FAMILY_DEPRESSION, 

240 comment=("Has anyone in your immediate family " 

241 "(parents, siblings or children) had any of the " 

242 "following conditions diagnosed by a doctor?") 

243 ) 

244 ) 

245 setattr( 

246 cls, cls.FN_FAMILY_BIPOLAR_DISORDER, 

247 BoolColumn( 

248 cls.FN_FAMILY_BIPOLAR_DISORDER, 

249 comment=("Has anyone in your immediate family " 

250 "(parents, siblings or children) had any of the " 

251 "following conditions diagnosed by a doctor?") 

252 ) 

253 ) 

254 setattr( 

255 cls, cls.FN_FAMILY_SCHIZOPHRENIA, 

256 BoolColumn( 

257 cls.FN_FAMILY_SCHIZOPHRENIA, 

258 comment=("Has anyone in your immediate family " 

259 "(parents, siblings or children) had any of the " 

260 "following conditions diagnosed by a doctor?") 

261 ) 

262 ) 

263 setattr( 

264 cls, cls.FN_FAMILY_AUTISM, 

265 BoolColumn( 

266 cls.FN_FAMILY_AUTISM, 

267 comment=("Has anyone in your immediate family " 

268 "(parents, siblings or children) had any of the " 

269 "following conditions diagnosed by a doctor?") 

270 ) 

271 ) 

272 setattr( 

273 cls, cls.FN_FAMILY_PTSD, 

274 BoolColumn( 

275 cls.FN_FAMILY_PTSD, 

276 comment=("Has anyone in your immediate family " 

277 "(parents, siblings or children) had any of the " 

278 "following conditions diagnosed by a doctor?") 

279 ) 

280 ) 

281 setattr( 

282 cls, cls.FN_FAMILY_ANXIETY, 

283 BoolColumn( 

284 cls.FN_FAMILY_ANXIETY, 

285 comment=("Has anyone in your immediate family " 

286 "(parents, siblings or children) had any of the " 

287 "following conditions diagnosed by a doctor?") 

288 ) 

289 ) 

290 setattr( 

291 cls, cls.FN_FAMILY_PERSONALITY_DISORDER, 

292 BoolColumn( 

293 cls.FN_FAMILY_PERSONALITY_DISORDER, 

294 comment=("Has anyone in your immediate family " 

295 "(parents, siblings or children) had any of the " 

296 "following conditions diagnosed by a doctor?") 

297 ) 

298 ) 

299 setattr( 

300 cls, cls.FN_FAMILY_INTELLECTUAL_DISABILITY, 

301 BoolColumn( 

302 cls.FN_FAMILY_INTELLECTUAL_DISABILITY, 

303 comment=("Has anyone in your immediate family " 

304 "(parents, siblings or children) had any of the " 

305 "following conditions diagnosed by a doctor?"), 

306 constraint_name="ck_kh2mm_fam_int_dis" 

307 ) 

308 ) 

309 setattr( 

310 cls, cls.FN_FAMILY_OTHER_MENTAL_ILLNESS, 

311 BoolColumn( 

312 cls.FN_FAMILY_OTHER_MENTAL_ILLNESS, 

313 comment=("Has anyone in your immediate family " 

314 "(parents, siblings or children) had any of the " 

315 "following conditions diagnosed by a doctor?") 

316 ) 

317 ) 

318 setattr( 

319 cls, cls.FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS, 

320 CamcopsColumn( 

321 cls.FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS, UnicodeText, 

322 comment="If other, please list here" 

323 ) 

324 ) 

325 

326 super().__init__(name, bases, classdict) 

327 

328 

329class KhandakerMojoMedical( 

330 TaskHasPatientMixin, Task, 

331 metaclass=KhandakerMojoMedicalMetaclass): 

332 """ 

333 Server implementation of the KhandakerMojoMedical task 

334 """ 

335 __tablename__ = "khandaker_mojo_medical" 

336 shortname = "Khandaker_MOJO_Medical" 

337 provides_trackers = False 

338 

339 # Section 1: General Information 

340 FN_DIAGNOSIS = "diagnosis" 

341 FN_DIAGNOSIS_DATE = "diagnosis_date" 

342 FN_DIAGNOSIS_DATE_APPROXIMATE = "diagnosis_date_approximate" 

343 FN_HAS_FIBROMYALGIA = "has_fibromyalgia" 

344 FN_IS_PREGNANT = "is_pregnant" 

345 FN_HAS_INFECTION_PAST_MONTH = "has_infection_past_month" 

346 FN_HAD_INFECTION_TWO_MONTHS_PRECEDING = "had_infection_two_months_preceding" # noqa 

347 FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE = "has_alcohol_substance_dependence" 

348 FN_SMOKING_STATUS = "smoking_status" 

349 FN_ALCOHOL_UNITS_PER_WEEK = "alcohol_units_per_week" 

350 

351 # Section 2: Medical History 

352 FN_DEPRESSION = "depression" 

353 FN_BIPOLAR_DISORDER = "bipolar_disorder" 

354 FN_SCHIZOPHRENIA = "schizophrenia" 

355 FN_AUTISM = "autism" 

356 FN_PTSD = "ptsd" 

357 FN_ANXIETY = "anxiety" 

358 FN_PERSONALITY_DISORDER = "personality_disorder" 

359 FN_INTELLECTUAL_DISABILITY = "intellectual_disability" 

360 FN_OTHER_MENTAL_ILLNESS = "other_mental_illness" 

361 FN_OTHER_MENTAL_ILLNESS_DETAILS = "other_mental_illness_details" 

362 FN_HOSPITALISED_IN_LAST_YEAR = "hospitalised_in_last_year" 

363 FN_HOSPITALISATION_DETAILS = "hospitalisation_details" 

364 

365 # Section 3: Family history 

366 FN_FAMILY_DEPRESSION = "family_depression" 

367 FN_FAMILY_BIPOLAR_DISORDER = "family_bipolar_disorder" 

368 FN_FAMILY_SCHIZOPHRENIA = "family_schizophrenia" 

369 FN_FAMILY_AUTISM = "family_autism" 

370 FN_FAMILY_PTSD = "family_ptsd" 

371 FN_FAMILY_ANXIETY = "family_anxiety" 

372 FN_FAMILY_PERSONALITY_DISORDER = "family_personality_disorder" 

373 FN_FAMILY_INTELLECTUAL_DISABILITY = "family_intellectual_disability" 

374 FN_FAMILY_OTHER_MENTAL_ILLNESS = "family_other_mental_illness" 

375 FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS = "family_other_mental_illness_details" # noqa 

376 

377 MANDATORY_FIELD_NAMES_1 = [ 

378 FN_DIAGNOSIS, 

379 FN_DIAGNOSIS_DATE, 

380 FN_HAS_FIBROMYALGIA, 

381 FN_IS_PREGNANT, 

382 FN_HAS_INFECTION_PAST_MONTH, 

383 FN_HAD_INFECTION_TWO_MONTHS_PRECEDING, 

384 FN_HAS_ALCOHOL_SUBSTANCE_DEPENDENCE, 

385 FN_SMOKING_STATUS, 

386 FN_ALCOHOL_UNITS_PER_WEEK, 

387 ] 

388 

389 MANDATORY_FIELD_NAMES_2 = [ 

390 FN_DEPRESSION, 

391 FN_BIPOLAR_DISORDER, 

392 FN_SCHIZOPHRENIA, 

393 FN_AUTISM, 

394 FN_PTSD, 

395 FN_ANXIETY, 

396 FN_PERSONALITY_DISORDER, 

397 FN_INTELLECTUAL_DISABILITY, 

398 FN_OTHER_MENTAL_ILLNESS, 

399 FN_HOSPITALISED_IN_LAST_YEAR, 

400 ] 

401 

402 MANDATORY_FIELD_NAMES_3 = [ 

403 FN_FAMILY_DEPRESSION, 

404 FN_FAMILY_BIPOLAR_DISORDER, 

405 FN_FAMILY_SCHIZOPHRENIA, 

406 FN_FAMILY_AUTISM, 

407 FN_FAMILY_PTSD, 

408 FN_FAMILY_ANXIETY, 

409 FN_FAMILY_PERSONALITY_DISORDER, 

410 FN_FAMILY_INTELLECTUAL_DISABILITY, 

411 FN_FAMILY_OTHER_MENTAL_ILLNESS, 

412 ] 

413 

414 MANDATORY_FIELD_NAMES = (MANDATORY_FIELD_NAMES_1 + 

415 MANDATORY_FIELD_NAMES_2 + 

416 MANDATORY_FIELD_NAMES_3) 

417 

418 # If the answer is yes to any of these, we need to have the details 

419 DETAILS_FIELDS = { 

420 FN_OTHER_MENTAL_ILLNESS: FN_OTHER_MENTAL_ILLNESS_DETAILS, 

421 FN_HOSPITALISED_IN_LAST_YEAR: FN_HOSPITALISATION_DETAILS, 

422 FN_FAMILY_OTHER_MENTAL_ILLNESS: FN_FAMILY_OTHER_MENTAL_ILLNESS_DETAILS, 

423 } 

424 

425 MULTI_CHOICE_FIELD_NAMES = [ 

426 FN_DIAGNOSIS, 

427 FN_SMOKING_STATUS, 

428 ] 

429 

430 @staticmethod 

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

432 _ = req.gettext 

433 return _("Khandaker GM — MOJO — Medical questionnaire") 

434 

435 def is_complete(self) -> bool: 

436 if self.any_fields_none(self.MANDATORY_FIELD_NAMES): 

437 return False 

438 

439 if not self.field_contents_valid(): 

440 return False 

441 

442 for field_name, details_field_name in self.DETAILS_FIELDS.items(): 

443 if getattr(self, field_name): 

444 if getattr(self, details_field_name) is None: 

445 return False 

446 

447 return True 

448 

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

450 heading_1 = self.xstring(req, "general_information_title") 

451 

452 rows_1 = "" 

453 for field_name in self.MANDATORY_FIELD_NAMES_1: 

454 rows_1 += self.get_rows(req, field_name) 

455 

456 heading_2 = self.xstring(req, "medical_history_title") 

457 

458 rows_2 = "" 

459 for field_name in self.MANDATORY_FIELD_NAMES_2: 

460 rows_2 += self.get_rows(req, field_name) 

461 

462 heading_3 = self.xstring(req, "family_history_title") 

463 

464 rows_3 = "" 

465 for field_name in self.MANDATORY_FIELD_NAMES_3: 

466 rows_3 += self.get_rows(req, field_name) 

467 

468 html = f""" 

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

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

471 {self.get_is_complete_tr(req)} 

472 </table> 

473 </div> 

474 <h3>{heading_1}</h3> 

475 <table class="{CssClass.TASKDETAIL}"> 

476 <tr> 

477 <th width="60%">Question</th> 

478 <th width="40%">Answer</th> 

479 </tr> 

480 {rows_1} 

481 </table> 

482 <h3>{heading_2}</h3> 

483 <table class="{CssClass.TASKDETAIL}"> 

484 <tr> 

485 <th width="60%">Question</th> 

486 <th width="40%">Answer</th> 

487 </tr> 

488 {rows_2} 

489 </table> 

490 <h3>{heading_3}</h3> 

491 <table class="{CssClass.TASKDETAIL}"> 

492 <tr> 

493 <th width="60%">Question</th> 

494 <th width="40%">Answer</th> 

495 </tr> 

496 {rows_3} 

497 </table> 

498 """ 

499 

500 return html 

501 

502 def get_rows(self, req: CamcopsRequest, field_name: str) -> str: 

503 rows = "" 

504 

505 question_text = self.xstring(req, f"q_{field_name}") 

506 answer = getattr(self, field_name) 

507 

508 answer_text = answer 

509 

510 if answer is not None and ( 

511 field_name in self.MULTI_CHOICE_FIELD_NAMES): 

512 answer_text = self.xstring(req, f"{field_name}_{answer}") 

513 

514 rows += tr_qa(question_text, answer_text) 

515 

516 if answer and field_name in self.DETAILS_FIELDS: 

517 details_field_name = self.DETAILS_FIELDS[field_name] 

518 details_question_text = self.xstring( 

519 req, f"q_{details_field_name}") 

520 details_answer = getattr(self, details_field_name) 

521 

522 rows += tr_qa(details_question_text, details_answer) 

523 

524 if field_name == self.FN_DIAGNOSIS_DATE: 

525 rows += tr_qa( 

526 self.xstring( 

527 req, f"q_{self.FN_DIAGNOSIS_DATE_APPROXIMATE}" 

528 ), 

529 getattr(self, self.FN_DIAGNOSIS_DATE_APPROXIMATE) 

530 ) 

531 

532 return rows