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/gmcpq.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 

29import cardinal_pythonlib.rnc_web as ws 

30from sqlalchemy.sql.schema import Column 

31from sqlalchemy.sql.sqltypes import Integer, UnicodeText 

32 

33from camcops_server.cc_modules.cc_constants import ( 

34 CssClass, 

35 POSSIBLE_SEX_VALUES, 

36) 

37from camcops_server.cc_modules.cc_html import ( 

38 get_yes_no_none, 

39 subheading_spanning_two_columns, 

40 td, 

41 tr, 

42 tr_qa, 

43) 

44from camcops_server.cc_modules.cc_request import CamcopsRequest 

45from camcops_server.cc_modules.cc_sqla_coltypes import ( 

46 BIT_CHECKER, 

47 CamcopsColumn, 

48 ONE_TO_FIVE_CHECKER, 

49 PermittedValueChecker, 

50 ZERO_TO_FIVE_CHECKER, 

51) 

52from camcops_server.cc_modules.cc_sqla_coltypes import SexColType 

53from camcops_server.cc_modules.cc_task import get_from_dict, Task 

54from camcops_server.cc_modules.cc_text import SS 

55 

56 

57# ============================================================================= 

58# GMCPQ 

59# ============================================================================= 

60 

61class GMCPQ(Task): 

62 """ 

63 Server implementation of the GMC-PQ task. 

64 """ 

65 __tablename__ = "gmcpq" 

66 shortname = "GMC-PQ" 

67 

68 RATING_TEXT = " (1 poor - 5 very good, 0 does not apply)" 

69 AGREE_TEXT = " (1 strongly disagree - 5 strongly agree, 0 does not apply)" 

70 

71 doctor = Column( 

72 "doctor", UnicodeText, 

73 comment="Doctor's name" 

74 ) 

75 q1 = CamcopsColumn( 

76 "q1", Integer, 

77 permitted_value_checker=PermittedValueChecker(minimum=1, maximum=4), 

78 comment="Filling in questionnaire for... (1 yourself, " 

79 "2 child, 3 spouse/partner, 4 other relative/friend)" 

80 ) 

81 q2a = CamcopsColumn( 

82 "q2a", Integer, 

83 permitted_value_checker=BIT_CHECKER, 

84 comment="Reason: advice? (0 no, 1 yes)" 

85 ) 

86 q2b = CamcopsColumn( 

87 "q2b", Integer, 

88 permitted_value_checker=BIT_CHECKER, 

89 comment="Reason: one-off problem? (0 no, 1 yes)" 

90 ) 

91 q2c = CamcopsColumn( 

92 "q2c", Integer, 

93 permitted_value_checker=BIT_CHECKER, 

94 comment="Reason: ongoing problem? (0 no, 1 yes)") 

95 q2d = CamcopsColumn( 

96 "q2d", Integer, 

97 permitted_value_checker=BIT_CHECKER, 

98 comment="Reason: routine check? (0 no, 1 yes)" 

99 ) 

100 q2e = CamcopsColumn( 

101 "q2e", Integer, 

102 permitted_value_checker=BIT_CHECKER, 

103 comment="Reason: treatment? (0 no, 1 yes)" 

104 ) 

105 q2f = CamcopsColumn( 

106 "q2f", Integer, 

107 permitted_value_checker=BIT_CHECKER, 

108 comment="Reason: other? (0 no, 1 yes)" 

109 ) 

110 q2f_details = Column( 

111 "q2f_details", UnicodeText, 

112 comment="Reason, other, details" 

113 ) 

114 q3 = CamcopsColumn( 

115 "q3", Integer, 

116 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

117 comment="How important to health/wellbeing was the reason " 

118 "(1 not very - 5 very)" 

119 ) 

120 q4a = CamcopsColumn( 

121 "q4a", Integer, 

122 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

123 comment="How good: being polite" + RATING_TEXT 

124 ) 

125 q4b = CamcopsColumn( 

126 "q4b", Integer, 

127 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

128 comment="How good: making you feel at ease" + RATING_TEXT 

129 ) 

130 q4c = CamcopsColumn( 

131 "q4c", Integer, 

132 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

133 comment="How good: listening" + RATING_TEXT 

134 ) 

135 q4d = CamcopsColumn( 

136 "q4d", Integer, 

137 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

138 comment="How good: assessing medical condition" + RATING_TEXT 

139 ) 

140 q4e = CamcopsColumn( 

141 "q4e", Integer, 

142 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

143 comment="How good: explaining" + RATING_TEXT 

144 ) 

145 q4f = CamcopsColumn( 

146 "q4f", Integer, 

147 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

148 comment="How good: involving you in decisions" + RATING_TEXT 

149 ) 

150 q4g = CamcopsColumn( 

151 "q4g", Integer, 

152 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

153 comment="How good: providing/arranging treatment" + RATING_TEXT 

154 ) 

155 q5a = CamcopsColumn( 

156 "q5a", Integer, 

157 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

158 comment="Agree/disagree: will keep info confidential" + AGREE_TEXT 

159 ) 

160 q5b = CamcopsColumn( 

161 "q5b", Integer, 

162 permitted_value_checker=ZERO_TO_FIVE_CHECKER, 

163 comment="Agree/disagree: honest/trustworthy" + AGREE_TEXT 

164 ) 

165 q6 = CamcopsColumn( 

166 "q6", Integer, 

167 permitted_value_checker=BIT_CHECKER, 

168 comment="Confident in doctor's ability to provide care (0 no, 1 yes)" 

169 ) 

170 q7 = CamcopsColumn( 

171 "q7", Integer, 

172 permitted_value_checker=BIT_CHECKER, 

173 comment="Would be completely happy to see this doctor again " 

174 "(0 no, 1 yes)" 

175 ) 

176 q8 = CamcopsColumn( 

177 "q8", Integer, 

178 permitted_value_checker=BIT_CHECKER, 

179 comment="Was this visit with your usual doctor (0 no, 1 yes)" 

180 ) 

181 q9 = Column( 

182 "q9", UnicodeText, 

183 comment="Other comments" 

184 ) 

185 q10 = CamcopsColumn( 

186 "q10", SexColType, 

187 permitted_value_checker=PermittedValueChecker( 

188 permitted_values=POSSIBLE_SEX_VALUES), 

189 comment="Sex of rater (M, F, X)" 

190 ) 

191 q11 = CamcopsColumn( 

192 "q11", Integer, 

193 permitted_value_checker=ONE_TO_FIVE_CHECKER, 

194 comment="Age (1 = under 15, 2 = 15-20, 3 = 21-40, " 

195 "4 = 40-60, 5 = 60 or over" # yes, I know it's daft 

196 ) 

197 q12 = CamcopsColumn( 

198 "q12", Integer, 

199 permitted_value_checker=PermittedValueChecker(minimum=1, maximum=16), 

200 comment="Ethnicity (1 = White British, 2 = White Irish, " 

201 "3 = White other, 4 = Mixed W/B Caribbean, " 

202 "5 = Mixed W/B African, 6 = Mixed W/Asian, 7 = Mixed other, " 

203 "8 = Asian/Asian British - Indian, 9 = A/AB - Pakistani, " 

204 "10 = A/AB - Bangladeshi, 11 = A/AB - other, " 

205 "12 = Black/Black British - Caribbean, 13 = B/BB - African, " 

206 "14 = B/BB - other, 15 = Chinese, 16 = other)" 

207 ) 

208 q12_details = Column( 

209 "q12_details", UnicodeText, 

210 comment="Ethnic group, other, details" 

211 ) 

212 

213 @staticmethod 

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

215 _ = req.gettext 

216 return _("GMC Patient Questionnaire") 

217 

218 def is_complete(self) -> bool: 

219 return ( 

220 self.is_field_not_none("q1") and 

221 self.is_field_not_none("q3") and 

222 self.is_field_not_none("q4a") and 

223 self.is_field_not_none("q4b") and 

224 self.is_field_not_none("q4c") and 

225 self.is_field_not_none("q4d") and 

226 self.is_field_not_none("q4e") and 

227 self.is_field_not_none("q4f") and 

228 self.is_field_not_none("q4g") and 

229 self.is_field_not_none("q5a") and 

230 self.is_field_not_none("q5b") and 

231 self.is_field_not_none("q6") and 

232 self.is_field_not_none("q7") and 

233 self.is_field_not_none("q8") and 

234 self.field_contents_valid() 

235 ) 

236 

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

238 dict_q1 = {None: None} 

239 dict_q3 = {None: None} 

240 dict_q4 = {None: None} 

241 dict_q5 = {None: None} 

242 dict_q11 = {None: None} 

243 dict_q12 = {None: None} 

244 for option in range(1, 5): 

245 dict_q1[option] = self.wxstring(req, "q1_option" + str(option)) 

246 for option in range(1, 6): 

247 dict_q3[option] = self.wxstring(req, "q3_option" + str(option)) 

248 dict_q11[option] = self.wxstring(req, "q11_option" + str(option)) 

249 for option in range(0, 6): 

250 prefix = str(option) + " – " if option > 0 else "" 

251 dict_q4[option] = prefix + self.wxstring(req, 

252 "q4_option" + str(option)) 

253 dict_q5[option] = prefix + self.wxstring(req, 

254 "q5_option" + str(option)) 

255 for option in range(1, 17): 

256 dict_q12[option] = self.wxstring(req, 

257 "ethnicity_option" + str(option)) 

258 h = f""" 

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

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

261 {self.get_is_complete_tr(req)} 

262 </table> 

263 </div> 

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

265 <tr> 

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

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

268 </tr> 

269 """ 

270 ell = "&hellip; " # horizontal ellipsis 

271 sep_row = subheading_spanning_two_columns("<br>") 

272 blank_cell = td("", td_class=CssClass.SUBHEADING) 

273 h += tr_qa(self.wxstring(req, "q_doctor"), ws.webify(self.doctor)) 

274 h += sep_row 

275 h += tr_qa(self.wxstring(req, "q1"), get_from_dict(dict_q1, self.q1)) 

276 h += tr(td(self.wxstring(req, "q2")), blank_cell, literal=True) 

277 h += tr_qa(ell + self.wxstring(req, "q2_a"), 

278 get_yes_no_none(req, self.q2a), 

279 default="") 

280 h += tr_qa(ell + self.wxstring(req, "q2_b"), 

281 get_yes_no_none(req, self.q2b), 

282 default="") 

283 h += tr_qa(ell + self.wxstring(req, "q2_c"), 

284 get_yes_no_none(req, self.q2c), 

285 default="") 

286 h += tr_qa(ell + self.wxstring(req, "q2_d"), 

287 get_yes_no_none(req, self.q2d), 

288 default="") 

289 h += tr_qa(ell + self.wxstring(req, "q2_e"), 

290 get_yes_no_none(req, self.q2e), 

291 default="") 

292 h += tr_qa(ell + self.wxstring(req, "q2_f"), 

293 get_yes_no_none(req, self.q2f), 

294 default="") 

295 h += tr_qa(ell + ell + self.wxstring(req, "q2f_s"), 

296 ws.webify(self.q2f_details)) 

297 h += tr_qa(self.wxstring(req, "q3"), get_from_dict(dict_q3, self.q3)) 

298 h += tr(td(self.wxstring(req, "q4")), blank_cell, literal=True) 

299 h += tr_qa(ell + self.wxstring(req, "q4_a"), 

300 get_from_dict(dict_q4, self.q4a)) 

301 h += tr_qa(ell + self.wxstring(req, "q4_b"), 

302 get_from_dict(dict_q4, self.q4b)) 

303 h += tr_qa(ell + self.wxstring(req, "q4_c"), 

304 get_from_dict(dict_q4, self.q4c)) 

305 h += tr_qa(ell + self.wxstring(req, "q4_d"), 

306 get_from_dict(dict_q4, self.q4d)) 

307 h += tr_qa(ell + self.wxstring(req, "q4_e"), 

308 get_from_dict(dict_q4, self.q4e)) 

309 h += tr_qa(ell + self.wxstring(req, "q4_f"), 

310 get_from_dict(dict_q4, self.q4f)) 

311 h += tr_qa(ell + self.wxstring(req, "q4_g"), 

312 get_from_dict(dict_q4, self.q4g)) 

313 h += tr(td(self.wxstring(req, "q5")), blank_cell, literal=True) 

314 h += tr_qa(ell + self.wxstring(req, "q5_a"), 

315 get_from_dict(dict_q5, self.q5a)) 

316 h += tr_qa(ell + self.wxstring(req, "q5_b"), 

317 get_from_dict(dict_q5, self.q5b)) 

318 h += tr_qa(self.wxstring(req, "q6"), get_yes_no_none(req, self.q6)) 

319 h += tr_qa(self.wxstring(req, "q7"), get_yes_no_none(req, self.q7)) 

320 h += tr_qa(self.wxstring(req, "q8"), get_yes_no_none(req, self.q8)) 

321 h += tr_qa(self.wxstring(req, "q9_s"), ws.webify(self.q9)) 

322 h += sep_row 

323 h += tr_qa(req.sstring(SS.SEX), ws.webify(self.q10)) 

324 h += tr_qa(self.wxstring(req, "q11"), 

325 get_from_dict(dict_q11, self.q11)) 

326 h += tr_qa(self.wxstring(req, "q12"), 

327 get_from_dict(dict_q12, self.q12)) 

328 h += tr_qa(ell + self.wxstring(req, "ethnicity_other_s"), 

329 ws.webify(self.q12_details)) 

330 h += """ 

331 </table> 

332 """ 

333 return h