Coverage for tasks/epds.py: 58%

1"""

2 camcops_server/tasks/epds.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**EPDS task.**

27

28"""

29

30 from typing import Any, cast, List, Optional, Type

31

32 from cardinal_pythonlib.stringfunc import strseq

33 from sqlalchemy.sql.sqltypes import Integer

34

35 from camcops_server.cc_modules.cc_constants import CssClass

36 from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo

37 from camcops_server.cc_modules.cc_db import add_multiple_columns

38 from camcops_server.cc_modules.cc_html import get_yes_no, tr_qa

39 from camcops_server.cc_modules.cc_request import CamcopsRequest

40 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

41 from camcops_server.cc_modules.cc_task import (

42 get_from_dict,

43 Task,

44 TaskHasPatientMixin,

45)

46 from camcops_server.cc_modules.cc_text import SS

47 from camcops_server.cc_modules.cc_trackerhelpers import (

48 TrackerInfo,

49 TrackerLabel,

50)

51

52

53# =============================================================================

54# EPDS

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

56

57

58 class Epds( # type: ignore[misc]

59 TaskHasPatientMixin,

60 Task,

61):

62 __tablename__ = "epds"

63 shortname = "EPDS"

64 provides_trackers = True

65

66 NQUESTIONS = 10

67

68 @classmethod

69 def extend_columns(cls: Type["Epds"], **kwargs: Any) -> None:

70 add_multiple_columns(cls, "q", 1, cls.NQUESTIONS)

71

72 TASK_FIELDS = strseq("q", 1, NQUESTIONS)

73 MAX_TOTAL = 30

74 CUTOFF_1_GREATER_OR_EQUAL = 10 # Cox et al. 1987, PubMed ID 3651732.

75 CUTOFF_2_GREATER_OR_EQUAL = 13 # Cox et al. 1987, PubMed ID 3651732.

76

77 @staticmethod

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

79 _ = req.gettext

80 return _("Edinburgh Postnatal Depression Scale")

81

82 def get_trackers(self, req: CamcopsRequest) -> List[TrackerInfo]:

83 return [

84 TrackerInfo(

85 value=self.total_score(),

86 plot_label="EPDS total score (rating depressive symptoms)",

87 axis_label=f"Total score (out of {self.MAX_TOTAL})",

88 axis_min=-0.5,

89 axis_max=self.MAX_TOTAL + 0.5,

90 horizontal_lines=[

91 self.CUTOFF_2_GREATER_OR_EQUAL - 0.5,

92 self.CUTOFF_1_GREATER_OR_EQUAL - 0.5,

93 ],

94 horizontal_labels=[

95 TrackerLabel(

96 self.CUTOFF_2_GREATER_OR_EQUAL, "likely depression"

97 ),

98 TrackerLabel(

99 self.CUTOFF_1_GREATER_OR_EQUAL, "possible depression"

100 ),

101 ],

102 )

103 ]

104

105 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:

106 if not self.is_complete():

107 return CTV_INCOMPLETE

108 text = f"EPDS total: {self.total_score()}/{self.MAX_TOTAL}"

109 return [CtvInfo(content=text)]

110

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

112 return self.standard_task_summary_fields() + [

113 SummaryElement(

114 name="total",

115 coltype=Integer(),

116 value=self.total_score(),

117 comment=f"Total score (out of {self.MAX_TOTAL})",

118 )

119 ]

120

121 def is_complete(self) -> bool:

122 return self.all_fields_not_none(self.TASK_FIELDS)

123

124 def total_score(self) -> int:

125 return cast(int, self.sum_fields(self.TASK_FIELDS))

126

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

128 score = self.total_score()

129 above_cutoff_1 = score >= 10

130 above_cutoff_2 = score >= 13

131 answer_dicts = []

132 for q in range(1, self.NQUESTIONS + 1):

133 d: dict[Optional[int], Optional[str]] = {None: "?"}

134 for option in range(0, 4):

135 d[option] = (

136 str(option)

137 + " — "

138 + self.wxstring(

139 req, "q" + str(q) + "_option" + str(option)

140 )

141 )

142 answer_dicts.append(d)

143

144 q_a = ""

145 for q in range(1, self.NQUESTIONS + 1):

146 q_a += tr_qa(

147 self.wxstring(req, "q" + str(q) + "_question"),

148 get_from_dict(

149 answer_dicts[q - 1], getattr(self, "q" + str(q))

150 ),

151 )

152

153 return f"""

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

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

156 {self.get_is_complete_tr(req)}

157 <tr>

158 <td>{req.sstring(SS.TOTAL_SCORE)}</td>

159 <td><b>{score}</b> / {self.MAX_TOTAL}</td>

160 </tr>

161 <tr>

162 <td>{self.wxstring(req, "above_cutoff_1")}

163 <sup>[1]</sup></td>

164 <td><b>{get_yes_no(req, above_cutoff_1)}</b></td>

165 </tr>

166 <tr>

167 <td>{self.wxstring(req, "above_cutoff_2")}

168 <sup>[2]</sup></td>

169 <td><b>{get_yes_no(req, above_cutoff_2)}</b></td>

170 </tr>

171 </table>

172 </div>

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

174 Ratings are over the last week.

175 <b>{self.wxstring(req, "always_look_at_suicide")}</b>

176 </div>

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

178 <tr>

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

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

181 </tr>

182 {q_a}

183 </table>

184 <div class="{CssClass.FOOTNOTES}">

185 [1] &ge;{self.CUTOFF_1_GREATER_OR_EQUAL}.

186 [2] &ge;{self.CUTOFF_2_GREATER_OR_EQUAL}.

187 (Cox et al. 1987, PubMed ID 3651732.)

188 </div>

189 <div class="{CssClass.COPYRIGHT}">

190 Edinburgh Postnatal Depression Scale:

191 © 1987 The Royal College of Psychiatrists. The Edinburgh

192 Postnatal Depression Scale may be photocopied by individual

193 researchers or clinicians for their own use without seeking

194 permission from the publishers. The scale must be copied in

195 full and all copies must acknowledge the following source: Cox,

196 J.L., Holden, J.M., & Sagovsky, R. (1987). Detection of

197 postnatal depression. Development of the 10-item Edinburgh

198 Postnatal Depression Scale. British Journal of Psychiatry, 150,

199 782-786. Written permission must be obtained from the Royal

200 College of Psychiatrists for copying and distribution to others

201 or for republication (in print, online or by any other medium).

202 </div>

203 """