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_sociodemographics.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, Optional, Tuple, Type 

30 

31from sqlalchemy.ext.declarative import DeclarativeMeta 

32from sqlalchemy.sql.sqltypes import Integer, UnicodeText 

33 

34from camcops_server.cc_modules.cc_constants import CssClass 

35from camcops_server.cc_modules.cc_html import tr_qa 

36from camcops_server.cc_modules.cc_request import CamcopsRequest 

37from camcops_server.cc_modules.cc_sqla_coltypes import ( 

38 CamcopsColumn, 

39 ZERO_TO_10_CHECKER, 

40 ZERO_TO_FOUR_CHECKER, 

41 ZERO_TO_SEVEN_CHECKER, 

42 ZERO_TO_SIX_CHECKER, 

43 ZERO_TO_TWO_CHECKER, 

44) 

45from camcops_server.cc_modules.cc_task import ( 

46 Task, 

47 TaskHasPatientMixin, 

48) 

49 

50 

51class KhandakerMojoSociodemographicsMetaclass(DeclarativeMeta): 

52 # noinspection PyInitNewSignature 

53 def __init__(cls: Type['KhandakerMojoSociodemographics'], 

54 name: str, 

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

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

57 setattr( 

58 cls, cls.FN_GENDER, 

59 CamcopsColumn( 

60 cls.FN_GENDER, Integer, 

61 permitted_value_checker=ZERO_TO_TWO_CHECKER, 

62 comment="Gender at birth (0 Male, 1 Female, 2 Other (specify))" 

63 ) 

64 ) 

65 setattr( 

66 cls, cls.FN_OTHER_GENDER, 

67 CamcopsColumn( 

68 cls.FN_OTHER_GENDER, UnicodeText, 

69 comment="Other (specify)" 

70 ) 

71 ) 

72 setattr( 

73 cls, cls.FN_ETHNICITY, 

74 CamcopsColumn( 

75 cls.FN_ETHNICITY, Integer, 

76 permitted_value_checker=ZERO_TO_10_CHECKER, 

77 comment=("Ethnicity (0 White, 1 Mixed, 2 Indian, 3 Pakistani, " 

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

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

80 "10 Other (specify))") 

81 ) 

82 ) 

83 setattr( 

84 cls, cls.FN_OTHER_ETHNICITY, 

85 CamcopsColumn( 

86 cls.FN_OTHER_ETHNICITY, UnicodeText, 

87 comment="Other (specify)" 

88 ) 

89 ) 

90 setattr( 

91 cls, cls.FN_WITH_WHOM_LIVE, 

92 CamcopsColumn( 

93 cls.FN_WITH_WHOM_LIVE, Integer, 

94 permitted_value_checker=ZERO_TO_SEVEN_CHECKER, 

95 comment=("0 Alone, 1 Alone with children, 2 Partner/Spouse, " 

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

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

98 ) 

99 ) 

100 setattr( 

101 cls, cls.FN_OTHER_WITH_WHOM_LIVE, 

102 CamcopsColumn( 

103 cls.FN_OTHER_WITH_WHOM_LIVE, UnicodeText, 

104 comment="Other (specify)" 

105 ) 

106 ) 

107 setattr( 

108 cls, cls.FN_RELATIONSHIP_STATUS, 

109 CamcopsColumn( 

110 cls.FN_RELATIONSHIP_STATUS, Integer, 

111 permitted_value_checker=ZERO_TO_FOUR_CHECKER, 

112 comment=("0 Single, 1 Married / Civil partnership, " 

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

114 "4 Widowed") 

115 ) 

116 ) 

117 setattr( 

118 cls, cls.FN_EDUCATION, 

119 CamcopsColumn( 

120 cls.FN_EDUCATION, Integer, 

121 permitted_value_checker=ZERO_TO_FOUR_CHECKER, 

122 comment=("0 No qualifications, 1 GCSE/O levels, 2 A levels, " 

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

124 "4 University / Professional Qualifications") 

125 ) 

126 ) 

127 setattr( 

128 cls, cls.FN_EMPLOYMENT, 

129 CamcopsColumn( 

130 cls.FN_EMPLOYMENT, Integer, 

131 permitted_value_checker=ZERO_TO_SEVEN_CHECKER, 

132 comment=("0 No unemployed, 1 No student, 2 Yes full time, " 

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

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

135 "7 Other (specify)") 

136 ) 

137 ) 

138 setattr( 

139 cls, cls.FN_OTHER_EMPLOYMENT, 

140 CamcopsColumn( 

141 cls.FN_OTHER_EMPLOYMENT, UnicodeText, 

142 comment="Other (specify)" 

143 ) 

144 ) 

145 setattr( 

146 cls, cls.FN_ACCOMMODATION, 

147 CamcopsColumn( 

148 cls.FN_ACCOMMODATION, Integer, 

149 permitted_value_checker=ZERO_TO_SIX_CHECKER, 

150 comment=("0 Own outright, 1 Own with mortgage, " 

151 "2 Rent from local authority etc, " 

152 "3 Rent from landlord (private), " 

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

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

155 ) 

156 ) 

157 setattr( 

158 cls, cls.FN_OTHER_ACCOMMODATION, 

159 CamcopsColumn( 

160 cls.FN_OTHER_ACCOMMODATION, UnicodeText, 

161 comment="Other (specify)" 

162 ) 

163 ) 

164 

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

166 

167 

168class KhandakerMojoSociodemographics( 

169 TaskHasPatientMixin, Task, 

170 metaclass=KhandakerMojoSociodemographicsMetaclass): 

171 """ 

172 Server implementation of the Khandaker_2_MOJOSociodemographics task 

173 """ 

174 __tablename__ = "khandaker_mojo_sociodemographics" 

175 shortname = "Khandaker_MOJO_Sociodemographics" 

176 provides_trackers = False 

177 

178 FN_GENDER = "gender" 

179 FN_ETHNICITY = "ethnicity" 

180 FN_WITH_WHOM_LIVE = "with_whom_live" 

181 FN_RELATIONSHIP_STATUS = "relationship_status" 

182 FN_EDUCATION = "education" 

183 FN_EMPLOYMENT = "employment" 

184 FN_ACCOMMODATION = "accommodation" 

185 

186 FN_OTHER_GENDER = "other_gender" 

187 FN_OTHER_ETHNICITY = "other_ethnicity" 

188 FN_OTHER_WITH_WHOM_LIVE = "other_with_whom_live" 

189 FN_OTHER_EMPLOYMENT = "other_employment" 

190 FN_OTHER_ACCOMMODATION = "other_accommodation" 

191 

192 MANDATORY_FIELD_NAMES = [ 

193 FN_GENDER, 

194 FN_ETHNICITY, 

195 FN_WITH_WHOM_LIVE, 

196 FN_RELATIONSHIP_STATUS, 

197 FN_EDUCATION, 

198 FN_EMPLOYMENT, 

199 FN_ACCOMMODATION, 

200 ] 

201 

202 OTHER_ANSWER_VALUES = { 

203 FN_GENDER: 2, 

204 FN_ETHNICITY: 10, 

205 FN_WITH_WHOM_LIVE: 7, 

206 FN_EMPLOYMENT: 7, 

207 FN_ACCOMMODATION: 6, 

208 } 

209 

210 @staticmethod 

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

212 _ = req.gettext 

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

214 

215 def is_complete(self) -> bool: 

216 if self.any_fields_none(self.MANDATORY_FIELD_NAMES): 

217 return False 

218 

219 if not self.field_contents_valid(): 

220 return False 

221 

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

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

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

225 return False 

226 

227 return True 

228 

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

230 rows = "" 

231 

232 for field_name in self.MANDATORY_FIELD_NAMES: 

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

234 answer_text = self.get_answer_text(req, field_name) 

235 

236 rows += tr_qa(question_text, answer_text) 

237 

238 html = f""" 

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

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

241 {self.get_is_complete_tr(req)} 

242 </table> 

243 </div> 

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

245 <tr> 

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

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

248 </tr> 

249 {rows} 

250 </table> 

251 """ 

252 

253 return html 

254 

255 def get_answer_text(self, req: CamcopsRequest, 

256 field_name: str) -> Optional[str]: 

257 answer = getattr(self, field_name) 

258 

259 if answer is None: 

260 return answer 

261 

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

263 

264 if self.answered_other(field_name): 

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

266 

267 if not other_answer: 

268 other_answer = "?" 

269 

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

271 

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

273 

274 def answered_other(self, field_name: str): 

275 if field_name not in self.OTHER_ANSWER_VALUES: 

276 return False 

277 

278 other_option = self.OTHER_ANSWER_VALUES[field_name] 

279 

280 return getattr(self, field_name) == other_option