Coverage for tasks/khandaker_mojo_sociodemographics.py: 60%

81 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-08 23:14 +0000

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/khandaker_mojo_sociodemographics.py 

5 

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

7 

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

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

10 

11 This file is part of CamCOPS. 

12 

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

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

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

16 (at your option) any later version. 

17 

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

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

20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

21 GNU General Public License for more details. 

22 

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

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

25 

26=============================================================================== 

27 

28""" 

29 

30from typing import Any, Dict, Optional, Tuple, Type 

31 

32from sqlalchemy.ext.declarative import DeclarativeMeta 

33from sqlalchemy.sql.sqltypes import 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 CamcopsColumn, 

40 ZERO_TO_10_CHECKER, 

41 ZERO_TO_FOUR_CHECKER, 

42 ZERO_TO_SEVEN_CHECKER, 

43 ZERO_TO_SIX_CHECKER, 

44 ZERO_TO_TWO_CHECKER, 

45) 

46from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin 

47 

48 

49class KhandakerMojoSociodemographicsMetaclass(DeclarativeMeta): 

50 # noinspection PyInitNewSignature 

51 def __init__( 

52 cls: Type["KhandakerMojoSociodemographics"], 

53 name: str, 

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

55 classdict: Dict[str, Any], 

56 ) -> None: 

57 setattr( 

58 cls, 

59 cls.FN_GENDER, 

60 CamcopsColumn( 

61 cls.FN_GENDER, 

62 Integer, 

63 permitted_value_checker=ZERO_TO_TWO_CHECKER, 

64 comment=( 

65 "Gender at birth (0 Male, 1 Female, 2 Other (specify))" 

66 ), 

67 ), 

68 ) 

69 setattr( 

70 cls, 

71 cls.FN_OTHER_GENDER, 

72 CamcopsColumn( 

73 cls.FN_OTHER_GENDER, UnicodeText, comment="Other (specify)" 

74 ), 

75 ) 

76 setattr( 

77 cls, 

78 cls.FN_ETHNICITY, 

79 CamcopsColumn( 

80 cls.FN_ETHNICITY, 

81 Integer, 

82 permitted_value_checker=ZERO_TO_10_CHECKER, 

83 comment=( 

84 "Ethnicity (0 White, 1 Mixed, 2 Indian, 3 Pakistani, " 

85 "4 Bangladeshi, 5 Other Asian, 6 Black Caribbean, " 

86 "7 Black African, 8 Black Other, 9 Chinese, " 

87 "10 Other (specify))" 

88 ), 

89 ), 

90 ) 

91 setattr( 

92 cls, 

93 cls.FN_OTHER_ETHNICITY, 

94 CamcopsColumn( 

95 cls.FN_OTHER_ETHNICITY, UnicodeText, comment="Other (specify)" 

96 ), 

97 ) 

98 setattr( 

99 cls, 

100 cls.FN_WITH_WHOM_LIVE, 

101 CamcopsColumn( 

102 cls.FN_WITH_WHOM_LIVE, 

103 Integer, 

104 permitted_value_checker=ZERO_TO_SEVEN_CHECKER, 

105 comment=( 

106 "0 Alone, 1 Alone with children, 2 Partner/Spouse, " 

107 "3 Partner/Spouse and children, 4 Parents, " 

108 "5 Other family, 6 Friends, 7 Other (specify)" 

109 ), 

110 ), 

111 ) 

112 setattr( 

113 cls, 

114 cls.FN_OTHER_WITH_WHOM_LIVE, 

115 CamcopsColumn( 

116 cls.FN_OTHER_WITH_WHOM_LIVE, 

117 UnicodeText, 

118 comment="Other (specify)", 

119 ), 

120 ) 

121 setattr( 

122 cls, 

123 cls.FN_RELATIONSHIP_STATUS, 

124 CamcopsColumn( 

125 cls.FN_RELATIONSHIP_STATUS, 

126 Integer, 

127 permitted_value_checker=ZERO_TO_FOUR_CHECKER, 

128 comment=( 

129 "0 Single, 1 Married / Civil partnership, " 

130 "2 In steady relationship, 3 Divorced / separated, " 

131 "4 Widowed" 

132 ), 

133 ), 

134 ) 

135 setattr( 

136 cls, 

137 cls.FN_EDUCATION, 

138 CamcopsColumn( 

139 cls.FN_EDUCATION, 

140 Integer, 

141 permitted_value_checker=ZERO_TO_FOUR_CHECKER, 

142 comment=( 

143 "0 No qualifications, 1 GCSE/O levels, 2 A levels, " 

144 "3 Vocational/college (B. Tecs/NVQs etc), " 

145 "4 University / Professional Qualifications" 

146 ), 

147 ), 

148 ) 

149 setattr( 

150 cls, 

151 cls.FN_EMPLOYMENT, 

152 CamcopsColumn( 

153 cls.FN_EMPLOYMENT, 

154 Integer, 

155 permitted_value_checker=ZERO_TO_SEVEN_CHECKER, 

156 comment=( 

157 "0 No unemployed, 1 No student, 2 Yes full time, " 

158 "3 Yes part time, 4 Full time homemaker, " 

159 "5 Self employed, 6 Not working for medical reasons, " 

160 "7 Other (specify)" 

161 ), 

162 ), 

163 ) 

164 setattr( 

165 cls, 

166 cls.FN_OTHER_EMPLOYMENT, 

167 CamcopsColumn( 

168 cls.FN_OTHER_EMPLOYMENT, UnicodeText, comment="Other (specify)" 

169 ), 

170 ) 

171 setattr( 

172 cls, 

173 cls.FN_ACCOMMODATION, 

174 CamcopsColumn( 

175 cls.FN_ACCOMMODATION, 

176 Integer, 

177 permitted_value_checker=ZERO_TO_SIX_CHECKER, 

178 comment=( 

179 "0 Own outright, 1 Own with mortgage, " 

180 "2 Rent from local authority etc, " 

181 "3 Rent from landlord (private), " 

182 "4 Shared ownership (part rent, part mortgage)" 

183 "5 Live rent free, 6 Other (specify)" 

184 ), 

185 ), 

186 ) 

187 setattr( 

188 cls, 

189 cls.FN_OTHER_ACCOMMODATION, 

190 CamcopsColumn( 

191 cls.FN_OTHER_ACCOMMODATION, 

192 UnicodeText, 

193 comment="Other (specify)", 

194 ), 

195 ) 

196 

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

198 

199 

200class KhandakerMojoSociodemographics( 

201 TaskHasPatientMixin, 

202 Task, 

203 metaclass=KhandakerMojoSociodemographicsMetaclass, 

204): 

205 """ 

206 Server implementation of the Khandaker_2_MOJOSociodemographics task 

207 """ 

208 

209 __tablename__ = "khandaker_mojo_sociodemographics" 

210 shortname = "Khandaker_MOJO_Sociodemographics" 

211 info_filename_stem = "khandaker_mojo" 

212 provides_trackers = False 

213 

214 FN_GENDER = "gender" 

215 FN_ETHNICITY = "ethnicity" 

216 FN_WITH_WHOM_LIVE = "with_whom_live" 

217 FN_RELATIONSHIP_STATUS = "relationship_status" 

218 FN_EDUCATION = "education" 

219 FN_EMPLOYMENT = "employment" 

220 FN_ACCOMMODATION = "accommodation" 

221 

222 FN_OTHER_GENDER = "other_gender" 

223 FN_OTHER_ETHNICITY = "other_ethnicity" 

224 FN_OTHER_WITH_WHOM_LIVE = "other_with_whom_live" 

225 FN_OTHER_EMPLOYMENT = "other_employment" 

226 FN_OTHER_ACCOMMODATION = "other_accommodation" 

227 

228 MANDATORY_FIELD_NAMES = [ 

229 FN_GENDER, 

230 FN_ETHNICITY, 

231 FN_WITH_WHOM_LIVE, 

232 FN_RELATIONSHIP_STATUS, 

233 FN_EDUCATION, 

234 FN_EMPLOYMENT, 

235 FN_ACCOMMODATION, 

236 ] 

237 

238 OTHER_ANSWER_VALUES = { 

239 FN_GENDER: 2, 

240 FN_ETHNICITY: 10, 

241 FN_WITH_WHOM_LIVE: 7, 

242 FN_EMPLOYMENT: 7, 

243 FN_ACCOMMODATION: 6, 

244 } 

245 

246 @staticmethod 

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

248 _ = req.gettext 

249 return _("Khandaker GM — MOJO — Sociodemographics") 

250 

251 def is_complete(self) -> bool: 

252 if self.any_fields_none(self.MANDATORY_FIELD_NAMES): 

253 return False 

254 

255 if not self.field_contents_valid(): 

256 return False 

257 

258 for name, other_option in self.OTHER_ANSWER_VALUES.items(): 

259 if getattr(self, name) == other_option: 

260 if getattr(self, f"other_{name}") is None: 

261 return False 

262 

263 return True 

264 

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

266 rows = "" 

267 

268 for field_name in self.MANDATORY_FIELD_NAMES: 

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

270 answer_text = self.get_answer_text(req, field_name) 

271 

272 rows += tr_qa(question_text, answer_text) 

273 

274 html = f""" 

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

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

277 {self.get_is_complete_tr(req)} 

278 </table> 

279 </div> 

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

281 <tr> 

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

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

284 </tr> 

285 {rows} 

286 </table> 

287 """ 

288 

289 return html 

290 

291 def get_answer_text( 

292 self, req: CamcopsRequest, field_name: str 

293 ) -> Optional[str]: 

294 answer = getattr(self, field_name) 

295 

296 if answer is None: 

297 return answer 

298 

299 answer_text = self.xstring(req, f"{field_name}_option{answer}") 

300 

301 if self.answered_other(field_name): 

302 other_answer = getattr(self, f"other_{field_name}") 

303 

304 if not other_answer: 

305 other_answer = "?" 

306 

307 answer_text = f"{answer_text} — {other_answer}" 

308 

309 return f"{answer} — {answer_text}" 

310 

311 def answered_other(self, field_name: str): 

312 if field_name not in self.OTHER_ANSWER_VALUES: 

313 return False 

314 

315 other_option = self.OTHER_ANSWER_VALUES[field_name] 

316 

317 return getattr(self, field_name) == other_option