Coverage for tasks/gmcpq.py: 45%

1"""

2 camcops_server/tasks/gmcpq.py

3

4===============================================================================

5

6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

8

9 This file is part of CamCOPS.

10

11 CamCOPS is free software: you can redistribute it and/or modify

12 it under the terms of the GNU General Public License as published by

13 the Free Software Foundation, either version 3 of the License, or

14 (at your option) any later version.

15

16 CamCOPS is distributed in the hope that it will be useful,

17 but WITHOUT ANY WARRANTY; without even the implied warranty of

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

19 GNU General Public License for more details.

20

21 You should have received a copy of the GNU General Public License

22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.

23

24===============================================================================

25

26"""

27

28 from typing import Optional

29

30 import cardinal_pythonlib.rnc_web as ws

31 from sqlalchemy.orm import Mapped, mapped_column

32 from sqlalchemy.sql.sqltypes import Integer, UnicodeText

33

34 from camcops_server.cc_modules.cc_constants import (

35 CssClass,

36 POSSIBLE_SEX_VALUES,

37)

38 from 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)

45 from camcops_server.cc_modules.cc_request import CamcopsRequest

46 from camcops_server.cc_modules.cc_sqla_coltypes import (

47 BIT_CHECKER,

48 mapped_camcops_column,

49 ONE_TO_FIVE_CHECKER,

50 PermittedValueChecker,

51 ZERO_TO_FIVE_CHECKER,

52)

53 from camcops_server.cc_modules.cc_sqla_coltypes import SexColType

54 from camcops_server.cc_modules.cc_task import get_from_dict, Task

55 from camcops_server.cc_modules.cc_text import SS

56

57

58# =============================================================================

59# GMCPQ

60# =============================================================================

61

62

63 class GMCPQ(Task):

64 """

65 Server implementation of the GMC-PQ task.

66 """

67

68 __tablename__ = "gmcpq"

69 shortname = "GMC-PQ"

70

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)"

73

74 doctor: Mapped[Optional[str]] = mapped_column(

75 UnicodeText, comment="Doctor's name"

76 )

77 q1: Mapped[Optional[int]] = mapped_camcops_column(

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: Mapped[Optional[int]] = mapped_camcops_column(

83 permitted_value_checker=BIT_CHECKER,

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

85 )

86 q2b: Mapped[Optional[int]] = mapped_camcops_column(

87 permitted_value_checker=BIT_CHECKER,

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

89 )

90 q2c: Mapped[Optional[int]] = mapped_camcops_column(

91 permitted_value_checker=BIT_CHECKER,

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

93 )

94 q2d: Mapped[Optional[int]] = mapped_camcops_column(

95 permitted_value_checker=BIT_CHECKER,

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

97 )

98 q2e: Mapped[Optional[int]] = mapped_camcops_column(

99 permitted_value_checker=BIT_CHECKER,

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

101 )

102 q2f: Mapped[Optional[int]] = mapped_camcops_column(

103 permitted_value_checker=BIT_CHECKER,

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

105 )

106 q2f_details: Mapped[Optional[str]] = mapped_column(

107 UnicodeText, comment="Reason, other, details"

108 )

109 q3: Mapped[Optional[int]] = mapped_camcops_column(

110 permitted_value_checker=ONE_TO_FIVE_CHECKER,

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

112 "(1 not very - 5 very)",

113 )

114 q4a: Mapped[Optional[int]] = mapped_camcops_column(

115 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

116 comment="How good: being polite" + RATING_TEXT,

117 )

118 q4b: Mapped[Optional[int]] = mapped_camcops_column(

119 "q4b",

120 Integer,

121 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

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

123 )

124 q4c: Mapped[Optional[int]] = mapped_camcops_column(

125 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

126 comment="How good: listening" + RATING_TEXT,

127 )

128 q4d: Mapped[Optional[int]] = mapped_camcops_column(

129 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

130 comment="How good: assessing medical condition" + RATING_TEXT,

131 )

132 q4e: Mapped[Optional[int]] = mapped_camcops_column(

133 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

134 comment="How good: explaining" + RATING_TEXT,

135 )

136 q4f: Mapped[Optional[int]] = mapped_camcops_column(

137 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

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

139 )

140 q4g: Mapped[Optional[int]] = mapped_camcops_column(

141 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

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

143 )

144 q5a: Mapped[Optional[int]] = mapped_camcops_column(

145 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

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

147 )

148 q5b: Mapped[Optional[int]] = mapped_camcops_column(

149 permitted_value_checker=ZERO_TO_FIVE_CHECKER,

150 comment="Agree/disagree: honest/trustworthy" + AGREE_TEXT,

151 )

152 q6: Mapped[Optional[int]] = mapped_camcops_column(

153 permitted_value_checker=BIT_CHECKER,

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

155 )

156 q7: Mapped[Optional[int]] = mapped_camcops_column(

157 permitted_value_checker=BIT_CHECKER,

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

159 "(0 no, 1 yes)",

160 )

161 q8: Mapped[Optional[int]] = mapped_camcops_column(

162 permitted_value_checker=BIT_CHECKER,

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

164 )

165 q9: Mapped[Optional[str]] = mapped_column(

166 UnicodeText, comment="Other comments"

167 )

168 q10: Mapped[Optional[str]] = mapped_camcops_column(

169 SexColType,

170 permitted_value_checker=PermittedValueChecker(

171 permitted_values=POSSIBLE_SEX_VALUES

172 ),

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

174 )

175 q11: Mapped[Optional[int]] = mapped_camcops_column(

176 permitted_value_checker=ONE_TO_FIVE_CHECKER,

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

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

179 )

180 q12: Mapped[Optional[int]] = mapped_camcops_column(

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

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

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

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

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

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

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

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

189 )

190 q12_details: Mapped[Optional[str]] = mapped_column(

191 UnicodeText, comment="Ethnic group, other, details"

192 )

193

194 @staticmethod

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

196 _ = req.gettext

197 return _("GMC Patient Questionnaire")

198

199 def is_complete(self) -> bool:

200 return (

201 self.is_field_not_none("q1")

202 and self.is_field_not_none("q3")

203 and self.is_field_not_none("q4a")

204 and self.is_field_not_none("q4b")

205 and self.is_field_not_none("q4c")

206 and self.is_field_not_none("q4d")

207 and self.is_field_not_none("q4e")

208 and self.is_field_not_none("q4f")

209 and self.is_field_not_none("q4g")

210 and self.is_field_not_none("q5a")

211 and self.is_field_not_none("q5b")

212 and self.is_field_not_none("q6")

213 and self.is_field_not_none("q7")

214 and self.is_field_not_none("q8")

215 and self.field_contents_valid()

216 )

217

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

219 dict_q1: dict[Optional[int], Optional[str]] = {None: None}

220 dict_q3: dict[Optional[int], Optional[str]] = {None: None}

221 dict_q4: dict[Optional[int], Optional[str]] = {None: None}

222 dict_q5: dict[Optional[int], Optional[str]] = {None: None}

223 dict_q11: dict[Optional[int], Optional[str]] = {None: None}

224 dict_q12: dict[Optional[int], Optional[str]] = {None: None}

225 for option in range(1, 5):

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

227 for option in range(1, 6):

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

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

230 for option in range(0, 6):

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

232 dict_q4[option] = prefix + self.wxstring(

233 req, "q4_option" + str(option)

234 )

235 dict_q5[option] = prefix + self.wxstring(

236 req, "q5_option" + str(option)

237 )

238 for option in range(1, 17):

239 dict_q12[option] = self.wxstring(

240 req, "ethnicity_option" + str(option)

241 )

242 h = f"""

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

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

245 {self.get_is_complete_tr(req)}

246 </table>

247 </div>

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

249 <tr>

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

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

252 </tr>

253 """

254 ell = "&hellip; " # horizontal ellipsis

255 sep_row = subheading_spanning_two_columns("<br>")

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

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

258 h += sep_row

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

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

261 h += tr_qa(

262 ell + self.wxstring(req, "q2_a"),

263 get_yes_no_none(req, self.q2a),

264 default="",

265 )

266 h += tr_qa(

267 ell + self.wxstring(req, "q2_b"),

268 get_yes_no_none(req, self.q2b),

269 default="",

270 )

271 h += tr_qa(

272 ell + self.wxstring(req, "q2_c"),

273 get_yes_no_none(req, self.q2c),

274 default="",

275 )

276 h += tr_qa(

277 ell + self.wxstring(req, "q2_d"),

278 get_yes_no_none(req, self.q2d),

279 default="",

280 )

281 h += tr_qa(

282 ell + self.wxstring(req, "q2_e"),

283 get_yes_no_none(req, self.q2e),

284 default="",

285 )

286 h += tr_qa(

287 ell + self.wxstring(req, "q2_f"),

288 get_yes_no_none(req, self.q2f),

289 default="",

290 )

291 h += tr_qa(

292 ell + ell + self.wxstring(req, "q2f_s"),

293 ws.webify(self.q2f_details),

294 )

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

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

297 h += tr_qa(

298 ell + self.wxstring(req, "q4_a"), get_from_dict(dict_q4, self.q4a)

299 )

300 h += tr_qa(

301 ell + self.wxstring(req, "q4_b"), get_from_dict(dict_q4, self.q4b)

302 )

303 h += tr_qa(

304 ell + self.wxstring(req, "q4_c"), get_from_dict(dict_q4, self.q4c)

305 )

306 h += tr_qa(

307 ell + self.wxstring(req, "q4_d"), get_from_dict(dict_q4, self.q4d)

308 )

309 h += tr_qa(

310 ell + self.wxstring(req, "q4_e"), get_from_dict(dict_q4, self.q4e)

311 )

312 h += tr_qa(

313 ell + self.wxstring(req, "q4_f"), get_from_dict(dict_q4, self.q4f)

314 )

315 h += tr_qa(

316 ell + self.wxstring(req, "q4_g"), get_from_dict(dict_q4, self.q4g)

317 )

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

319 h += tr_qa(

320 ell + self.wxstring(req, "q5_a"), get_from_dict(dict_q5, self.q5a)

321 )

322 h += tr_qa(

323 ell + self.wxstring(req, "q5_b"), get_from_dict(dict_q5, self.q5b)

324 )

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

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

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

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

329 h += sep_row

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

331 h += tr_qa(

332 self.wxstring(req, "q11"), get_from_dict(dict_q11, self.q11)

333 )

334 h += tr_qa(

335 self.wxstring(req, "q12"), get_from_dict(dict_q12, self.q12)

336 )

337 h += tr_qa(

338 ell + self.wxstring(req, "ethnicity_other_s"),

339 ws.webify(self.q12_details),

340 )

341 h += """

342 </table>

343 """

344 return h