Coverage for tasks/epds.py: 58%
55 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
1"""
2camcops_server/tasks/epds.py
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
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.
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.
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/>.
24===============================================================================
26**EPDS task.**
28"""
30from typing import Any, cast, List, Optional, Type
32from cardinal_pythonlib.stringfunc import strseq
33from sqlalchemy.sql.sqltypes import Integer
35from camcops_server.cc_modules.cc_constants import CssClass
36from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
37from camcops_server.cc_modules.cc_db import add_multiple_columns
38from camcops_server.cc_modules.cc_html import get_yes_no, tr_qa
39from camcops_server.cc_modules.cc_request import CamcopsRequest
40from camcops_server.cc_modules.cc_summaryelement import SummaryElement
41from camcops_server.cc_modules.cc_task import (
42 get_from_dict,
43 Task,
44 TaskHasPatientMixin,
45)
46from camcops_server.cc_modules.cc_text import SS
47from camcops_server.cc_modules.cc_trackerhelpers import (
48 TrackerInfo,
49 TrackerLabel,
50)
53# =============================================================================
54# EPDS
55# =============================================================================
58class Epds( # type: ignore[misc]
59 TaskHasPatientMixin,
60 Task,
61):
62 __tablename__ = "epds"
63 shortname = "EPDS"
64 provides_trackers = True
66 NQUESTIONS = 10
68 @classmethod
69 def extend_columns(cls: Type["Epds"], **kwargs: Any) -> None:
70 add_multiple_columns(cls, "q", 1, cls.NQUESTIONS)
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.
77 @staticmethod
78 def longname(req: "CamcopsRequest") -> str:
79 _ = req.gettext
80 return _("Edinburgh Postnatal Depression Scale")
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 ]
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)]
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 ]
121 def is_complete(self) -> bool:
122 return self.all_fields_not_none(self.TASK_FIELDS)
124 def total_score(self) -> int:
125 return cast(int, self.sum_fields(self.TASK_FIELDS))
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)
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 )
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] ≥{self.CUTOFF_1_GREATER_OR_EQUAL}.
186 [2] ≥{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 """