Coverage for tasks/gmcpq.py: 45%
105 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/gmcpq.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"""
30import cardinal_pythonlib.rnc_web as ws
31from sqlalchemy.sql.schema import Column
32from sqlalchemy.sql.sqltypes import Integer, UnicodeText
34from camcops_server.cc_modules.cc_constants import (
35 CssClass,
36 POSSIBLE_SEX_VALUES,
37)
38from camcops_server.cc_modules.cc_html import (
39 get_yes_no_none,
40 subheading_spanning_two_columns,
41 td,
42 tr,
43 tr_qa,
44)
45from camcops_server.cc_modules.cc_request import CamcopsRequest
46from camcops_server.cc_modules.cc_sqla_coltypes import (
47 BIT_CHECKER,
48 CamcopsColumn,
49 ONE_TO_FIVE_CHECKER,
50 PermittedValueChecker,
51 ZERO_TO_FIVE_CHECKER,
52)
53from camcops_server.cc_modules.cc_sqla_coltypes import SexColType
54from camcops_server.cc_modules.cc_task import get_from_dict, Task
55from camcops_server.cc_modules.cc_text import SS
58# =============================================================================
59# GMCPQ
60# =============================================================================
63class GMCPQ(Task):
64 """
65 Server implementation of the GMC-PQ task.
66 """
68 __tablename__ = "gmcpq"
69 shortname = "GMC-PQ"
71 RATING_TEXT = " (1 poor - 5 very good, 0 does not apply)"
72 AGREE_TEXT = " (1 strongly disagree - 5 strongly agree, 0 does not apply)"
74 doctor = Column("doctor", UnicodeText, comment="Doctor's name")
75 q1 = CamcopsColumn(
76 "q1",
77 Integer,
78 permitted_value_checker=PermittedValueChecker(minimum=1, maximum=4),
79 comment="Filling in questionnaire for... (1 yourself, "
80 "2 child, 3 spouse/partner, 4 other relative/friend)",
81 )
82 q2a = CamcopsColumn(
83 "q2a",
84 Integer,
85 permitted_value_checker=BIT_CHECKER,
86 comment="Reason: advice? (0 no, 1 yes)",
87 )
88 q2b = CamcopsColumn(
89 "q2b",
90 Integer,
91 permitted_value_checker=BIT_CHECKER,
92 comment="Reason: one-off problem? (0 no, 1 yes)",
93 )
94 q2c = CamcopsColumn(
95 "q2c",
96 Integer,
97 permitted_value_checker=BIT_CHECKER,
98 comment="Reason: ongoing problem? (0 no, 1 yes)",
99 )
100 q2d = CamcopsColumn(
101 "q2d",
102 Integer,
103 permitted_value_checker=BIT_CHECKER,
104 comment="Reason: routine check? (0 no, 1 yes)",
105 )
106 q2e = CamcopsColumn(
107 "q2e",
108 Integer,
109 permitted_value_checker=BIT_CHECKER,
110 comment="Reason: treatment? (0 no, 1 yes)",
111 )
112 q2f = CamcopsColumn(
113 "q2f",
114 Integer,
115 permitted_value_checker=BIT_CHECKER,
116 comment="Reason: other? (0 no, 1 yes)",
117 )
118 q2f_details = Column(
119 "q2f_details", UnicodeText, comment="Reason, other, details"
120 )
121 q3 = CamcopsColumn(
122 "q3",
123 Integer,
124 permitted_value_checker=ONE_TO_FIVE_CHECKER,
125 comment="How important to health/wellbeing was the reason "
126 "(1 not very - 5 very)",
127 )
128 q4a = CamcopsColumn(
129 "q4a",
130 Integer,
131 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
132 comment="How good: being polite" + RATING_TEXT,
133 )
134 q4b = CamcopsColumn(
135 "q4b",
136 Integer,
137 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
138 comment="How good: making you feel at ease" + RATING_TEXT,
139 )
140 q4c = CamcopsColumn(
141 "q4c",
142 Integer,
143 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
144 comment="How good: listening" + RATING_TEXT,
145 )
146 q4d = CamcopsColumn(
147 "q4d",
148 Integer,
149 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
150 comment="How good: assessing medical condition" + RATING_TEXT,
151 )
152 q4e = CamcopsColumn(
153 "q4e",
154 Integer,
155 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
156 comment="How good: explaining" + RATING_TEXT,
157 )
158 q4f = CamcopsColumn(
159 "q4f",
160 Integer,
161 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
162 comment="How good: involving you in decisions" + RATING_TEXT,
163 )
164 q4g = CamcopsColumn(
165 "q4g",
166 Integer,
167 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
168 comment="How good: providing/arranging treatment" + RATING_TEXT,
169 )
170 q5a = CamcopsColumn(
171 "q5a",
172 Integer,
173 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
174 comment="Agree/disagree: will keep info confidential" + AGREE_TEXT,
175 )
176 q5b = CamcopsColumn(
177 "q5b",
178 Integer,
179 permitted_value_checker=ZERO_TO_FIVE_CHECKER,
180 comment="Agree/disagree: honest/trustworthy" + AGREE_TEXT,
181 )
182 q6 = CamcopsColumn(
183 "q6",
184 Integer,
185 permitted_value_checker=BIT_CHECKER,
186 comment="Confident in doctor's ability to provide care (0 no, 1 yes)",
187 )
188 q7 = CamcopsColumn(
189 "q7",
190 Integer,
191 permitted_value_checker=BIT_CHECKER,
192 comment="Would be completely happy to see this doctor again "
193 "(0 no, 1 yes)",
194 )
195 q8 = CamcopsColumn(
196 "q8",
197 Integer,
198 permitted_value_checker=BIT_CHECKER,
199 comment="Was this visit with your usual doctor (0 no, 1 yes)",
200 )
201 q9 = Column("q9", UnicodeText, comment="Other comments")
202 q10 = CamcopsColumn(
203 "q10",
204 SexColType,
205 permitted_value_checker=PermittedValueChecker(
206 permitted_values=POSSIBLE_SEX_VALUES
207 ),
208 comment="Sex of rater (M, F, X)",
209 )
210 q11 = CamcopsColumn(
211 "q11",
212 Integer,
213 permitted_value_checker=ONE_TO_FIVE_CHECKER,
214 comment="Age (1 = under 15, 2 = 15-20, 3 = 21-40, "
215 "4 = 40-60, 5 = 60 or over", # yes, I know it's daft
216 )
217 q12 = CamcopsColumn(
218 "q12",
219 Integer,
220 permitted_value_checker=PermittedValueChecker(minimum=1, maximum=16),
221 comment="Ethnicity (1 = White British, 2 = White Irish, "
222 "3 = White other, 4 = Mixed W/B Caribbean, "
223 "5 = Mixed W/B African, 6 = Mixed W/Asian, 7 = Mixed other, "
224 "8 = Asian/Asian British - Indian, 9 = A/AB - Pakistani, "
225 "10 = A/AB - Bangladeshi, 11 = A/AB - other, "
226 "12 = Black/Black British - Caribbean, 13 = B/BB - African, "
227 "14 = B/BB - other, 15 = Chinese, 16 = other)",
228 )
229 q12_details = Column(
230 "q12_details", UnicodeText, comment="Ethnic group, other, details"
231 )
233 @staticmethod
234 def longname(req: "CamcopsRequest") -> str:
235 _ = req.gettext
236 return _("GMC Patient Questionnaire")
238 def is_complete(self) -> bool:
239 return (
240 self.is_field_not_none("q1")
241 and self.is_field_not_none("q3")
242 and self.is_field_not_none("q4a")
243 and self.is_field_not_none("q4b")
244 and self.is_field_not_none("q4c")
245 and self.is_field_not_none("q4d")
246 and self.is_field_not_none("q4e")
247 and self.is_field_not_none("q4f")
248 and self.is_field_not_none("q4g")
249 and self.is_field_not_none("q5a")
250 and self.is_field_not_none("q5b")
251 and self.is_field_not_none("q6")
252 and self.is_field_not_none("q7")
253 and self.is_field_not_none("q8")
254 and self.field_contents_valid()
255 )
257 def get_task_html(self, req: CamcopsRequest) -> str:
258 dict_q1 = {None: None}
259 dict_q3 = {None: None}
260 dict_q4 = {None: None}
261 dict_q5 = {None: None}
262 dict_q11 = {None: None}
263 dict_q12 = {None: None}
264 for option in range(1, 5):
265 dict_q1[option] = self.wxstring(req, "q1_option" + str(option))
266 for option in range(1, 6):
267 dict_q3[option] = self.wxstring(req, "q3_option" + str(option))
268 dict_q11[option] = self.wxstring(req, "q11_option" + str(option))
269 for option in range(0, 6):
270 prefix = str(option) + " – " if option > 0 else ""
271 dict_q4[option] = prefix + self.wxstring(
272 req, "q4_option" + str(option)
273 )
274 dict_q5[option] = prefix + self.wxstring(
275 req, "q5_option" + str(option)
276 )
277 for option in range(1, 17):
278 dict_q12[option] = self.wxstring(
279 req, "ethnicity_option" + str(option)
280 )
281 h = f"""
282 <div class="{CssClass.SUMMARY}">
283 <table class="{CssClass.SUMMARY}">
284 {self.get_is_complete_tr(req)}
285 </table>
286 </div>
287 <table class="{CssClass.TASKDETAIL}">
288 <tr>
289 <th width="60%">Question</th>
290 <th width="40%">Answer</th>
291 </tr>
292 """
293 ell = "… " # horizontal ellipsis
294 sep_row = subheading_spanning_two_columns("<br>")
295 blank_cell = td("", td_class=CssClass.SUBHEADING)
296 h += tr_qa(self.wxstring(req, "q_doctor"), ws.webify(self.doctor))
297 h += sep_row
298 h += tr_qa(self.wxstring(req, "q1"), get_from_dict(dict_q1, self.q1))
299 h += tr(td(self.wxstring(req, "q2")), blank_cell, literal=True)
300 h += tr_qa(
301 ell + self.wxstring(req, "q2_a"),
302 get_yes_no_none(req, self.q2a),
303 default="",
304 )
305 h += tr_qa(
306 ell + self.wxstring(req, "q2_b"),
307 get_yes_no_none(req, self.q2b),
308 default="",
309 )
310 h += tr_qa(
311 ell + self.wxstring(req, "q2_c"),
312 get_yes_no_none(req, self.q2c),
313 default="",
314 )
315 h += tr_qa(
316 ell + self.wxstring(req, "q2_d"),
317 get_yes_no_none(req, self.q2d),
318 default="",
319 )
320 h += tr_qa(
321 ell + self.wxstring(req, "q2_e"),
322 get_yes_no_none(req, self.q2e),
323 default="",
324 )
325 h += tr_qa(
326 ell + self.wxstring(req, "q2_f"),
327 get_yes_no_none(req, self.q2f),
328 default="",
329 )
330 h += tr_qa(
331 ell + ell + self.wxstring(req, "q2f_s"),
332 ws.webify(self.q2f_details),
333 )
334 h += tr_qa(self.wxstring(req, "q3"), get_from_dict(dict_q3, self.q3))
335 h += tr(td(self.wxstring(req, "q4")), blank_cell, literal=True)
336 h += tr_qa(
337 ell + self.wxstring(req, "q4_a"), get_from_dict(dict_q4, self.q4a)
338 )
339 h += tr_qa(
340 ell + self.wxstring(req, "q4_b"), get_from_dict(dict_q4, self.q4b)
341 )
342 h += tr_qa(
343 ell + self.wxstring(req, "q4_c"), get_from_dict(dict_q4, self.q4c)
344 )
345 h += tr_qa(
346 ell + self.wxstring(req, "q4_d"), get_from_dict(dict_q4, self.q4d)
347 )
348 h += tr_qa(
349 ell + self.wxstring(req, "q4_e"), get_from_dict(dict_q4, self.q4e)
350 )
351 h += tr_qa(
352 ell + self.wxstring(req, "q4_f"), get_from_dict(dict_q4, self.q4f)
353 )
354 h += tr_qa(
355 ell + self.wxstring(req, "q4_g"), get_from_dict(dict_q4, self.q4g)
356 )
357 h += tr(td(self.wxstring(req, "q5")), blank_cell, literal=True)
358 h += tr_qa(
359 ell + self.wxstring(req, "q5_a"), get_from_dict(dict_q5, self.q5a)
360 )
361 h += tr_qa(
362 ell + self.wxstring(req, "q5_b"), get_from_dict(dict_q5, self.q5b)
363 )
364 h += tr_qa(self.wxstring(req, "q6"), get_yes_no_none(req, self.q6))
365 h += tr_qa(self.wxstring(req, "q7"), get_yes_no_none(req, self.q7))
366 h += tr_qa(self.wxstring(req, "q8"), get_yes_no_none(req, self.q8))
367 h += tr_qa(self.wxstring(req, "q9_s"), ws.webify(self.q9))
368 h += sep_row
369 h += tr_qa(req.sstring(SS.SEX), ws.webify(self.q10))
370 h += tr_qa(
371 self.wxstring(req, "q11"), get_from_dict(dict_q11, self.q11)
372 )
373 h += tr_qa(
374 self.wxstring(req, "q12"), get_from_dict(dict_q12, self.q12)
375 )
376 h += tr_qa(
377 ell + self.wxstring(req, "ethnicity_other_s"),
378 ws.webify(self.q12_details),
379 )
380 h += """
381 </table>
382 """
383 return h