Coverage for tasks/service_satisfaction.py: 69%

1"""

2 camcops_server/tasks/service_satisfaction.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 from sqlalchemy.orm import Mapped, mapped_column

31 from sqlalchemy.sql.sqltypes import UnicodeText

32

33 from camcops_server.cc_modules.cc_constants import CssClass

34 from camcops_server.cc_modules.cc_html import tr_qa

35 from camcops_server.cc_modules.cc_request import CamcopsRequest

36 from camcops_server.cc_modules.cc_sqla_coltypes import (

37 mapped_camcops_column,

38 ZERO_TO_FOUR_CHECKER,

39)

40 from camcops_server.cc_modules.cc_string import AS

41 from camcops_server.cc_modules.cc_task import (

42 get_from_dict,

43 Task,

44 TaskHasPatientMixin,

45)

46

47

48# =============================================================================

49# Abstract base class

50# =============================================================================

51

52

53 class AbstractSatisfaction(object):

54 # noinspection PyMethodParameters

55 service: Mapped[Optional[str]] = mapped_column(

56 "service", UnicodeText, comment="Clinical service being rated"

57 )

58

59 # noinspection PyMethodParameters

60 rating: Mapped[Optional[int]] = mapped_camcops_column(

61 "rating",

62 permitted_value_checker=ZERO_TO_FOUR_CHECKER,

63 comment="Rating (0 very poor - 4 excellent)",

64 )

65

66 # noinspection PyMethodParameters

67 good: Mapped[Optional[str]] = mapped_column(

68 "good", UnicodeText, comment="What has been good?"

69 )

70

71 # noinspection PyMethodParameters

72 bad: Mapped[Optional[str]] = mapped_column(

73 "bad", UnicodeText, comment="What could be improved?"

74 )

75

76 TASK_FIELDS = ["service", "rating", "good", "bad"]

77

78 def is_complete(self) -> bool:

79 # noinspection PyUnresolvedReferences

80 return self.rating is not None and self.field_contents_valid() # type: ignore[attr-defined] # noqa: E501

81 # ... self.field_contents_valid() is from Task, and we are a mixin

82

83 def get_rating_text(self, req: CamcopsRequest) -> Optional[str]:

84 ratingdict = {

85 None: None,

86 0: req.wappstring(AS.SATIS_RATING_A_PREFIX + "0"),

87 1: req.wappstring(AS.SATIS_RATING_A_PREFIX + "1"),

88 2: req.wappstring(AS.SATIS_RATING_A_PREFIX + "2"),

89 3: req.wappstring(AS.SATIS_RATING_A_PREFIX + "3"),

90 4: req.wappstring(AS.SATIS_RATING_A_PREFIX + "4"),

91 }

92 return get_from_dict(ratingdict, self.rating)

93

94 def get_common_task_html(

95 self, req: CamcopsRequest, rating_q: str, good_q: str, bad_q: str

96 ) -> str:

97 if self.rating is not None:

98 r = f"{self.rating}. {self.get_rating_text(req)}"

99 else:

100 r = None

101

102 is_complete_row = self.get_is_complete_tr(req) # type: ignore[attr-defined] # noqa: E501

103

104 # noinspection PyUnresolvedReferences

105 return f"""

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

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

108 {is_complete_row}

109 </table>

110 </div>

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

112 <tr>

113 <th width="50%">Question</th>

114 <th width="50%">Answer</th>

115 </tr>

116 {tr_qa(req.wappstring(AS.SATIS_SERVICE_BEING_RATED),

117 self.service)}

118 {tr_qa(f"{rating_q} {self.service}?", r)}

119 {tr_qa(good_q, self.good)}

120 {tr_qa(bad_q, self.bad)}

121 </table>

122 """

123 # ... self.get_is_complete_tr() is from Task, and we are a mixin

124

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

126 raise NotImplementedError("implement in subclass")

127

128

129# =============================================================================

130# PatientSatisfaction

131# =============================================================================

132

133

134 class PatientSatisfaction(TaskHasPatientMixin, AbstractSatisfaction, Task): # type: ignore[misc] # noqa: E501

135 """

136 Server implementation of the PatientSatisfaction task.

137 """

138

139 __tablename__ = "pt_satis"

140 shortname = "PatientSatisfaction"

141 info_filename_stem = "pss"

142

143 @staticmethod

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

145 _ = req.gettext

146 return _("Patient Satisfaction Scale")

147

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

149 return self.get_common_task_html(

150 req,

151 req.wappstring(AS.SATIS_PT_RATING_Q),

152 req.wappstring(AS.SATIS_GOOD_Q),

153 req.wappstring(AS.SATIS_BAD_Q),

154 )

155

156

157# =============================================================================

158# ReferrerSatisfactionGen

159# =============================================================================

160

161

162 class ReferrerSatisfactionGen(AbstractSatisfaction, Task):

163 """

164 Server implementation of the ReferrerSatisfactionSurvey task.

165 """

166

167 __tablename__ = "ref_satis_gen"

168 shortname = "ReferrerSatisfactionSurvey"

169 info_filename_stem = "rss"

170

171 @staticmethod

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

173 _ = req.gettext

174 return _("Referrer Satisfaction Scale, survey")

175

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

177 return self.get_common_task_html(

178 req,

179 req.wappstring(AS.SATIS_REF_GEN_RATING_Q),

180 req.wappstring(AS.SATIS_GOOD_Q),

181 req.wappstring(AS.SATIS_BAD_Q),

182 )

183

184

185# =============================================================================

186# ReferrerSatisfactionSpec

187# =============================================================================

188

189

190 class ReferrerSatisfactionSpec( # type: ignore[misc]

191 TaskHasPatientMixin, AbstractSatisfaction, Task

192):

193 """

194 Server implementation of the ReferrerSatisfactionSpecific task.

195 """

196

197 __tablename__ = "ref_satis_spec"

198 shortname = "ReferrerSatisfactionSpecific"

199 info_filename_stem = "rss"

200

201 @staticmethod

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

203 _ = req.gettext

204 return _("Referrer Satisfaction Scale, patient-specific")

205

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

207 return self.get_common_task_html(

208 req,

209 req.wappstring(AS.SATIS_REF_SPEC_RATING_Q),

210 req.wappstring(AS.SATIS_GOOD_Q),

211 req.wappstring(AS.SATIS_BAD_Q),

212 )