Coverage for tasks/khandaker_mojo_sociodemographics.py : 60%

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
3"""
4camcops_server/tasks/khandaker_mojo_sociodemographics.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
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.
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.
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/>.
25===============================================================================
27"""
29from typing import Any, Dict, Optional, Tuple, Type
31from sqlalchemy.ext.declarative import DeclarativeMeta
32from sqlalchemy.sql.sqltypes import Integer, UnicodeText
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)
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 )
165 super().__init__(name, bases, classdict)
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
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"
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"
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 ]
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 }
210 @staticmethod
211 def longname(req: "CamcopsRequest") -> str:
212 _ = req.gettext
213 return _("Khandaker GM — MOJO — Sociodemographics")
215 def is_complete(self) -> bool:
216 if self.any_fields_none(self.MANDATORY_FIELD_NAMES):
217 return False
219 if not self.field_contents_valid():
220 return False
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
227 return True
229 def get_task_html(self, req: CamcopsRequest) -> str:
230 rows = ""
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)
236 rows += tr_qa(question_text, answer_text)
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 """
253 return html
255 def get_answer_text(self, req: CamcopsRequest,
256 field_name: str) -> Optional[str]:
257 answer = getattr(self, field_name)
259 if answer is None:
260 return answer
262 answer_text = self.xstring(req, f"{field_name}_option{answer}")
264 if self.answered_other(field_name):
265 other_answer = getattr(self, f"other_{field_name}")
267 if not other_answer:
268 other_answer = "?"
270 answer_text = f"{answer_text} — {other_answer}"
272 return f"{answer} — {answer_text}"
274 def answered_other(self, field_name: str):
275 if field_name not in self.OTHER_ANSWER_VALUES:
276 return False
278 other_option = self.OTHER_ANSWER_VALUES[field_name]
280 return getattr(self, field_name) == other_option