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
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/tasks/khandaker_mojo_sociodemographics.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
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.
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.
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/>.
26===============================================================================
28"""
30from typing import Any, Dict, Optional, Tuple, Type
32from sqlalchemy.ext.declarative import DeclarativeMeta
33from sqlalchemy.sql.sqltypes import Integer, UnicodeText
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
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 )
197 super().__init__(name, bases, classdict)
200class KhandakerMojoSociodemographics(
201 TaskHasPatientMixin,
202 Task,
203 metaclass=KhandakerMojoSociodemographicsMetaclass,
204):
205 """
206 Server implementation of the Khandaker_2_MOJOSociodemographics task
207 """
209 __tablename__ = "khandaker_mojo_sociodemographics"
210 shortname = "Khandaker_MOJO_Sociodemographics"
211 info_filename_stem = "khandaker_mojo"
212 provides_trackers = False
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"
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"
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 ]
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 }
246 @staticmethod
247 def longname(req: "CamcopsRequest") -> str:
248 _ = req.gettext
249 return _("Khandaker GM — MOJO — Sociodemographics")
251 def is_complete(self) -> bool:
252 if self.any_fields_none(self.MANDATORY_FIELD_NAMES):
253 return False
255 if not self.field_contents_valid():
256 return False
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
263 return True
265 def get_task_html(self, req: CamcopsRequest) -> str:
266 rows = ""
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)
272 rows += tr_qa(question_text, answer_text)
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 """
289 return html
291 def get_answer_text(
292 self, req: CamcopsRequest, field_name: str
293 ) -> Optional[str]:
294 answer = getattr(self, field_name)
296 if answer is None:
297 return answer
299 answer_text = self.xstring(req, f"{field_name}_option{answer}")
301 if self.answered_other(field_name):
302 other_answer = getattr(self, f"other_{field_name}")
304 if not other_answer:
305 other_answer = "?"
307 answer_text = f"{answer_text} — {other_answer}"
309 return f"{answer} — {answer_text}"
311 def answered_other(self, field_name: str):
312 if field_name not in self.OTHER_ANSWER_VALUES:
313 return False
315 other_option = self.OTHER_ANSWER_VALUES[field_name]
317 return getattr(self, field_name) == other_option