Coverage for tasks/apeqpt.py: 47%

60 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-08 23:14 +0000

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/apeqpt.py 

5 

6=============================================================================== 

7 

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

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

10 

11 This file is part of CamCOPS. 

12 

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. 

17 

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. 

22 

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

25 

26=============================================================================== 

27 

28- By Joe Kearney, Rudolf Cardinal. 

29 

30""" 

31 

32from typing import Dict, List 

33 

34from sqlalchemy.sql.sqltypes import Integer, UnicodeText 

35 

36from camcops_server.cc_modules.cc_constants import CssClass 

37from camcops_server.cc_modules.cc_fhir import ( 

38 FHIRAnsweredQuestion, 

39 FHIRAnswerType, 

40 FHIRQuestionType, 

41) 

42from camcops_server.cc_modules.cc_html import tr_qa 

43from camcops_server.cc_modules.cc_request import CamcopsRequest 

44from camcops_server.cc_modules.cc_sqla_coltypes import ( 

45 CamcopsColumn, 

46 PendulumDateTimeAsIsoTextColType, 

47 ZERO_TO_ONE_CHECKER, 

48 ZERO_TO_TWO_CHECKER, 

49 ZERO_TO_FOUR_CHECKER, 

50) 

51from camcops_server.cc_modules.cc_summaryelement import SummaryElement 

52from camcops_server.cc_modules.cc_task import get_from_dict, Task 

53 

54 

55# ============================================================================= 

56# APEQPT 

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

58 

59 

60class Apeqpt(Task): 

61 """ 

62 Server implementation of the APEQPT task. 

63 """ 

64 

65 __tablename__ = "apeqpt" 

66 shortname = "APEQPT" 

67 provides_trackers = True 

68 

69 # todo: remove q_datetime (here and in the C++) -- it duplicates when_created # noqa 

70 q_datetime = CamcopsColumn( 

71 "q_datetime", 

72 PendulumDateTimeAsIsoTextColType, 

73 comment="Date/time the assessment tool was completed", 

74 ) 

75 

76 N_CHOICE_QUESTIONS = 3 

77 q1_choice = CamcopsColumn( 

78 "q1_choice", 

79 Integer, 

80 comment="Enough information was provided (0 no, 1 yes)", 

81 permitted_value_checker=ZERO_TO_ONE_CHECKER, 

82 ) 

83 q2_choice = CamcopsColumn( 

84 "q2_choice", 

85 Integer, 

86 comment="Treatment preference (0 no, 1 yes)", 

87 permitted_value_checker=ZERO_TO_ONE_CHECKER, 

88 ) 

89 q3_choice = CamcopsColumn( 

90 "q3_choice", 

91 Integer, 

92 comment="Preference offered (0 no, 1 yes, 2 N/A)", 

93 permitted_value_checker=ZERO_TO_TWO_CHECKER, 

94 ) 

95 

96 q1_satisfaction = CamcopsColumn( 

97 "q1_satisfaction", 

98 Integer, 

99 comment=( 

100 "Patient satisfaction (0 not at all satisfied - " 

101 "4 completely satisfied)" 

102 ), 

103 permitted_value_checker=ZERO_TO_FOUR_CHECKER, 

104 ) 

105 q2_satisfaction = CamcopsColumn( 

106 "q2_satisfaction", UnicodeText, comment="Service experience" 

107 ) 

108 

109 MAIN_QUESTIONS = [ 

110 "q_datetime", 

111 "q1_choice", 

112 "q2_choice", 

113 "q3_choice", 

114 "q1_satisfaction", 

115 ] 

116 

117 @staticmethod 

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

119 _ = req.gettext 

120 return _( 

121 "Assessment Patient Experience Questionnaire " 

122 "for Psychological Therapies" 

123 ) 

124 

125 def is_complete(self) -> bool: 

126 if self.any_fields_none(self.MAIN_QUESTIONS): 

127 return False 

128 if not self.field_contents_valid(): 

129 return False 

130 return True 

131 

132 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]: 

133 return self.standard_task_summary_fields() 

134 

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

136 c_dict = { 

137 0: "0 — " + self.wxstring(req, "a0_choice"), 

138 1: "1 — " + self.wxstring(req, "a1_choice"), 

139 2: "2 — " + self.wxstring(req, "a2_choice"), 

140 } 

141 s_dict = { 

142 0: "0 — " + self.wxstring(req, "a0_satisfaction"), 

143 1: "1 — " + self.wxstring(req, "a1_satisfaction"), 

144 2: "2 — " + self.wxstring(req, "a2_satisfaction"), 

145 3: "3 — " + self.wxstring(req, "a3_satisfaction"), 

146 4: "4 — " + self.wxstring(req, "a4_satisfaction"), 

147 } 

148 q_a = "" 

149 for i in range(1, self.N_CHOICE_QUESTIONS + 1): 

150 nstr = str(i) 

151 q_a += tr_qa( 

152 self.wxstring(req, "q" + nstr + "_choice"), 

153 get_from_dict(c_dict, getattr(self, "q" + nstr + "_choice")), 

154 ) 

155 

156 q_a += tr_qa( 

157 self.wxstring(req, "q1_satisfaction"), 

158 get_from_dict(s_dict, self.q1_satisfaction), 

159 ) 

160 q_a += tr_qa( 

161 self.wxstring(req, "q2_satisfaction"), 

162 self.q2_satisfaction, 

163 default="", 

164 ) 

165 

166 return f""" 

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

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

169 {self.get_is_complete_tr(req)} 

170 </table> 

171 </div> 

172 <div class="{CssClass.EXPLANATION}"> 

173 Patient satisfaction rating for service provided. The service 

174 is rated on choice offered and general satisfaction. 

175 </div> 

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

177 <tr> 

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

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

180 </tr> 

181 {q_a} 

182 </table> 

183 """ 

184 

185 def get_fhir_questionnaire( 

186 self, req: "CamcopsRequest" 

187 ) -> List[FHIRAnsweredQuestion]: 

188 items = [] # type: List[FHIRAnsweredQuestion] 

189 

190 yes_no_options = {} # type: Dict[int, str] 

191 for index in range(2): 

192 yes_no_options[index] = self.wxstring(req, f"a{index}_choice") 

193 items.append( 

194 FHIRAnsweredQuestion( 

195 qname="q1_choice", 

196 qtext=self.wxstring(req, "q1_choice"), 

197 qtype=FHIRQuestionType.CHOICE, 

198 answer_type=FHIRAnswerType.INTEGER, 

199 answer=self.q1_choice, 

200 answer_options=yes_no_options, 

201 ) 

202 ) 

203 items.append( 

204 FHIRAnsweredQuestion( 

205 qname="q2_choice", 

206 qtext=self.wxstring(req, "q2_choice"), 

207 qtype=FHIRQuestionType.CHOICE, 

208 answer_type=FHIRAnswerType.INTEGER, 

209 answer=self.q2_choice, 

210 answer_options=yes_no_options, 

211 ) 

212 ) 

213 

214 yes_no_na_options = yes_no_options.copy() 

215 yes_no_na_options[2] = self.wxstring(req, "a2_choice") 

216 items.append( 

217 FHIRAnsweredQuestion( 

218 qname="q3_choice", 

219 qtext=self.wxstring(req, "q3_choice"), 

220 qtype=FHIRQuestionType.CHOICE, 

221 answer_type=FHIRAnswerType.INTEGER, 

222 answer=self.q3_choice, 

223 answer_options=yes_no_na_options, 

224 ) 

225 ) 

226 

227 satisfaction_options = {} # type: Dict[int, str] 

228 for index in range(5): 

229 satisfaction_options[index] = self.wxstring( 

230 req, f"a{index}_satisfaction" 

231 ) 

232 items.append( 

233 FHIRAnsweredQuestion( 

234 qname="q1_satisfaction", 

235 qtext=self.xstring(req, "q1_satisfaction"), 

236 qtype=FHIRQuestionType.CHOICE, 

237 answer_type=FHIRAnswerType.INTEGER, 

238 answer=self.q1_satisfaction, 

239 answer_options=satisfaction_options, 

240 ) 

241 ) 

242 

243 items.append( 

244 FHIRAnsweredQuestion( 

245 qname="q2_satisfaction", 

246 qtext=self.xstring(req, "q2_satisfaction"), 

247 qtype=FHIRQuestionType.STRING, 

248 answer_type=FHIRAnswerType.STRING, 

249 answer=self.q2_satisfaction, 

250 ) 

251 ) 

252 

253 return items