Coverage for tasks/khandaker_mojo_sociodemographics.py: 59%

1"""

2 camcops_server/tasks/khandaker_mojo_sociodemographics.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 typing import Any, Optional, Type

29

30 from sqlalchemy.sql.sqltypes import Integer, UnicodeText

31

32 from camcops_server.cc_modules.cc_constants import CssClass

33 from camcops_server.cc_modules.cc_html import tr_qa

34 from camcops_server.cc_modules.cc_request import CamcopsRequest

35 from camcops_server.cc_modules.cc_sqla_coltypes import (

36 camcops_column,

37 ZERO_TO_10_CHECKER,

38 ZERO_TO_FOUR_CHECKER,

39 ZERO_TO_SEVEN_CHECKER,

40 ZERO_TO_SIX_CHECKER,

41 ZERO_TO_TWO_CHECKER,

42)

43 from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin

44

45

46 class KhandakerMojoSociodemographics( # type: ignore[misc]

47 TaskHasPatientMixin,

48 Task,

49):

50 """

51 Server implementation of the Khandaker_2_MOJOSociodemographics task

52 """

53

54 __tablename__ = "khandaker_mojo_sociodemographics"

55 shortname = "Khandaker_MOJO_Sociodemographics"

56 info_filename_stem = "khandaker_mojo"

57 provides_trackers = False

58

59 FN_GENDER = "gender"

60 FN_ETHNICITY = "ethnicity"

61 FN_WITH_WHOM_LIVE = "with_whom_live"

62 FN_RELATIONSHIP_STATUS = "relationship_status"

63 FN_EDUCATION = "education"

64 FN_EMPLOYMENT = "employment"

65 FN_ACCOMMODATION = "accommodation"

66

67 FN_OTHER_GENDER = "other_gender"

68 FN_OTHER_ETHNICITY = "other_ethnicity"

69 FN_OTHER_WITH_WHOM_LIVE = "other_with_whom_live"

70 FN_OTHER_EMPLOYMENT = "other_employment"

71 FN_OTHER_ACCOMMODATION = "other_accommodation"

72

73 MANDATORY_FIELD_NAMES = [

74 FN_GENDER,

75 FN_ETHNICITY,

76 FN_WITH_WHOM_LIVE,

77 FN_RELATIONSHIP_STATUS,

78 FN_EDUCATION,

79 FN_EMPLOYMENT,

80 FN_ACCOMMODATION,

81 ]

82

83 OTHER_ANSWER_VALUES = {

84 FN_GENDER: 2,

85 FN_ETHNICITY: 10,

86 FN_WITH_WHOM_LIVE: 7,

87 FN_EMPLOYMENT: 7,

88 FN_ACCOMMODATION: 6,

89 }

90

91 @classmethod

92 def extend_columns(

93 cls: Type["KhandakerMojoSociodemographics"], **kwargs: Any

94 ) -> None:

95 setattr(

96 cls,

97 cls.FN_GENDER,

98 camcops_column(

99 cls.FN_GENDER,

100 Integer,

101 permitted_value_checker=ZERO_TO_TWO_CHECKER,

102 comment=(

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

104 ),

105 ),

106 )

107 setattr(

108 cls,

109 cls.FN_OTHER_GENDER,

110 camcops_column(

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

112 ),

113 )

114 setattr(

115 cls,

116 cls.FN_ETHNICITY,

117 camcops_column(

118 cls.FN_ETHNICITY,

119 Integer,

120 permitted_value_checker=ZERO_TO_10_CHECKER,

121 comment=(

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

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

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

125 "10 Other (specify))"

126 ),

127 ),

128 )

129 setattr(

130 cls,

131 cls.FN_OTHER_ETHNICITY,

132 camcops_column(

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

134 ),

135 )

136 setattr(

137 cls,

138 cls.FN_WITH_WHOM_LIVE,

139 camcops_column(

140 cls.FN_WITH_WHOM_LIVE,

141 Integer,

142 permitted_value_checker=ZERO_TO_SEVEN_CHECKER,

143 comment=(

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

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

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

147 ),

148 ),

149 )

150 setattr(

151 cls,

152 cls.FN_OTHER_WITH_WHOM_LIVE,

153 camcops_column(

154 cls.FN_OTHER_WITH_WHOM_LIVE,

155 UnicodeText,

156 comment="Other (specify)",

157 ),

158 )

159 setattr(

160 cls,

161 cls.FN_RELATIONSHIP_STATUS,

162 camcops_column(

163 cls.FN_RELATIONSHIP_STATUS,

164 Integer,

165 permitted_value_checker=ZERO_TO_FOUR_CHECKER,

166 comment=(

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

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

169 "4 Widowed"

170 ),

171 ),

172 )

173 setattr(

174 cls,

175 cls.FN_EDUCATION,

176 camcops_column(

177 cls.FN_EDUCATION,

178 Integer,

179 permitted_value_checker=ZERO_TO_FOUR_CHECKER,

180 comment=(

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

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

183 "4 University / Professional Qualifications"

184 ),

185 ),

186 )

187 setattr(

188 cls,

189 cls.FN_EMPLOYMENT,

190 camcops_column(

191 cls.FN_EMPLOYMENT,

192 Integer,

193 permitted_value_checker=ZERO_TO_SEVEN_CHECKER,

194 comment=(

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

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

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

198 "7 Other (specify)"

199 ),

200 ),

201 )

202 setattr(

203 cls,

204 cls.FN_OTHER_EMPLOYMENT,

205 camcops_column(

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

207 ),

208 )

209 setattr(

210 cls,

211 cls.FN_ACCOMMODATION,

212 camcops_column(

213 cls.FN_ACCOMMODATION,

214 Integer,

215 permitted_value_checker=ZERO_TO_SIX_CHECKER,

216 comment=(

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

218 "2 Rent from local authority etc, "

219 "3 Rent from landlord (private), "

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

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

222 ),

223 ),

224 )

225 setattr(

226 cls,

227 cls.FN_OTHER_ACCOMMODATION,

228 camcops_column(

229 cls.FN_OTHER_ACCOMMODATION,

230 UnicodeText,

231 comment="Other (specify)",

232 ),

233 )

234

235 @staticmethod

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

237 _ = req.gettext

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

239

240 def is_complete(self) -> bool:

241 if self.any_fields_none(self.MANDATORY_FIELD_NAMES):

242 return False

243

244 if not self.field_contents_valid():

245 return False

246

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

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

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

250 return False

251

252 return True

253

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

255 rows = ""

256

257 for field_name in self.MANDATORY_FIELD_NAMES:

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

259 answer_text = self.get_answer_text(req, field_name)

260

261 rows += tr_qa(question_text, answer_text)

262

263 html = f"""

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

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

266 {self.get_is_complete_tr(req)}

267 </table>

268 </div>

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

270 <tr>

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

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

273 </tr>

274 {rows}

275 </table>

276 """

277

278 return html

279

280 def get_answer_text(

281 self, req: CamcopsRequest, field_name: str

282 ) -> Optional[str]:

283 answer = getattr(self, field_name)

284

285 if answer is None:

286 return answer

287

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

289

290 if self.answered_other(field_name):

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

292

293 if not other_answer:

294 other_answer = "?"

295

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

297

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

299

300 def answered_other(self, field_name: str) -> bool:

301 if field_name not in self.OTHER_ANSWER_VALUES:

302 return False

303

304 other_option = self.OTHER_ANSWER_VALUES[field_name]

305

306 return getattr(self, field_name) == other_option