Coverage for tasks/apeqpt.py: 48%

1"""

2 camcops_server/tasks/apeqpt.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- By Joe Kearney, Rudolf Cardinal.

27

28"""

29

30 from typing import Dict, List, Optional

31

32 from pendulum import DateTime as Pendulum

33 from sqlalchemy.orm import Mapped

34 from sqlalchemy.sql.sqltypes import UnicodeText

35

36 from camcops_server.cc_modules.cc_constants import CssClass

37 from camcops_server.cc_modules.cc_fhir import (

38 FHIRAnsweredQuestion,

39 FHIRAnswerType,

40 FHIRQuestionType,

41)

42 from camcops_server.cc_modules.cc_html import tr_qa

43 from camcops_server.cc_modules.cc_request import CamcopsRequest

44 from camcops_server.cc_modules.cc_sqla_coltypes import (

45 mapped_camcops_column,

46 PendulumDateTimeAsIsoTextColType,

47 ZERO_TO_ONE_CHECKER,

48 ZERO_TO_TWO_CHECKER,

49 ZERO_TO_FOUR_CHECKER,

50)

51 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

52 from camcops_server.cc_modules.cc_task import get_from_dict, Task

53

54

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

56# APEQPT

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

58

59

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

71 PendulumDateTimeAsIsoTextColType,

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

73 )

74

75 N_CHOICE_QUESTIONS = 3

76 q1_choice: Mapped[Optional[int]] = mapped_camcops_column(

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

78 permitted_value_checker=ZERO_TO_ONE_CHECKER,

79 )

80 q2_choice: Mapped[Optional[int]] = mapped_camcops_column(

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

82 permitted_value_checker=ZERO_TO_ONE_CHECKER,

83 )

84 q3_choice: Mapped[Optional[int]] = mapped_camcops_column(

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

86 permitted_value_checker=ZERO_TO_TWO_CHECKER,

87 )

88

89 q1_satisfaction: Mapped[Optional[int]] = mapped_camcops_column(

90 comment=(

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

92 "4 completely satisfied)"

93 ),

94 permitted_value_checker=ZERO_TO_FOUR_CHECKER,

95 )

96 q2_satisfaction: Mapped[Optional[str]] = mapped_camcops_column(

97 UnicodeText, comment="Service experience"

98 )

99

100 MAIN_QUESTIONS = [

101 "q_datetime",

102 "q1_choice",

103 "q2_choice",

104 "q3_choice",

105 "q1_satisfaction",

106 ]

107

108 @staticmethod

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

110 _ = req.gettext

111 return _(

112 "Assessment Patient Experience Questionnaire "

113 "for Psychological Therapies"

114 )

115

116 def is_complete(self) -> bool:

117 if self.any_fields_none(self.MAIN_QUESTIONS):

118 return False

119 if not self.field_contents_valid():

120 return False

121 return True

122

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

124 return self.standard_task_summary_fields()

125

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

127 c_dict = {

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

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

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

131 }

132 s_dict = {

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

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

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

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

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

138 }

139 q_a = ""

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

141 nstr = str(i)

142 q_a += tr_qa(

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

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

145 )

146

147 q_a += tr_qa(

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

149 get_from_dict(s_dict, self.q1_satisfaction),

150 )

151 q_a += tr_qa(

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

153 self.q2_satisfaction,

154 default="",

155 )

156

157 return f"""

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

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

160 {self.get_is_complete_tr(req)}

161 </table>

162 </div>

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

164 Patient satisfaction rating for service provided. The service

165 is rated on choice offered and general satisfaction.

166 </div>

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

168 <tr>

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

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

171 </tr>

172 {q_a}

173 </table>

174 """

175

176 def get_fhir_questionnaire(

177 self, req: "CamcopsRequest"

178 ) -> List[FHIRAnsweredQuestion]:

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

180

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

182 for index in range(2):

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

184 items.append(

185 FHIRAnsweredQuestion(

186 qname="q1_choice",

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

188 qtype=FHIRQuestionType.CHOICE,

189 answer_type=FHIRAnswerType.INTEGER,

190 answer=self.q1_choice,

191 answer_options=yes_no_options,

192 )

193 )

194 items.append(

195 FHIRAnsweredQuestion(

196 qname="q2_choice",

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

198 qtype=FHIRQuestionType.CHOICE,

199 answer_type=FHIRAnswerType.INTEGER,

200 answer=self.q2_choice,

201 answer_options=yes_no_options,

202 )

203 )

204

205 yes_no_na_options = yes_no_options.copy()

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

207 items.append(

208 FHIRAnsweredQuestion(

209 qname="q3_choice",

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

211 qtype=FHIRQuestionType.CHOICE,

212 answer_type=FHIRAnswerType.INTEGER,

213 answer=self.q3_choice,

214 answer_options=yes_no_na_options,

215 )

216 )

217

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

219 for index in range(5):

220 satisfaction_options[index] = self.wxstring(

221 req, f"a{index}_satisfaction"

222 )

223 items.append(

224 FHIRAnsweredQuestion(

225 qname="q1_satisfaction",

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

227 qtype=FHIRQuestionType.CHOICE,

228 answer_type=FHIRAnswerType.INTEGER,

229 answer=self.q1_satisfaction,

230 answer_options=satisfaction_options,

231 )

232 )

233

234 items.append(

235 FHIRAnsweredQuestion(

236 qname="q2_satisfaction",

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

238 qtype=FHIRQuestionType.STRING,

239 answer_type=FHIRAnswerType.STRING,

240 answer=self.q2_satisfaction,

241 )

242 )

243

244 return items