Coverage for tasks/epds.py : 60%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
3"""
4camcops_server/tasks/epds.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
12 CamCOPS is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
17 CamCOPS is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
25===============================================================================
27**EPDS task.**
29"""
31from typing import Any, Dict, List, Tuple, Type
33from cardinal_pythonlib.stringfunc import strseq
34from sqlalchemy.ext.declarative import DeclarativeMeta
35from sqlalchemy.sql.sqltypes import Integer
37from camcops_server.cc_modules.cc_constants import CssClass
38from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
39from camcops_server.cc_modules.cc_db import add_multiple_columns
40from camcops_server.cc_modules.cc_html import get_yes_no, tr_qa
41from camcops_server.cc_modules.cc_request import CamcopsRequest
42from camcops_server.cc_modules.cc_summaryelement import SummaryElement
43from camcops_server.cc_modules.cc_task import (
44 get_from_dict,
45 Task,
46 TaskHasPatientMixin,
47)
48from camcops_server.cc_modules.cc_text import SS
49from camcops_server.cc_modules.cc_trackerhelpers import (
50 TrackerInfo,
51 TrackerLabel,
52)
55# =============================================================================
56# EPDS
57# =============================================================================
59class EpdsMetaclass(DeclarativeMeta):
60 # noinspection PyInitNewSignature
61 def __init__(cls: Type['Epds'],
62 name: str,
63 bases: Tuple[Type, ...],
64 classdict: Dict[str, Any]) -> None:
65 add_multiple_columns(cls, "q", 1, cls.NQUESTIONS)
66 super().__init__(name, bases, classdict)
69class Epds(TaskHasPatientMixin, Task, metaclass=EpdsMetaclass):
70 __tablename__ = "epds"
71 shortname = "EPDS"
72 provides_trackers = True
74 NQUESTIONS = 10
75 TASK_FIELDS = strseq("q", 1, NQUESTIONS)
76 MAX_TOTAL = 30
77 CUTOFF_1_GREATER_OR_EQUAL = 10 # Cox et al. 1987, PubMed ID 3651732.
78 CUTOFF_2_GREATER_OR_EQUAL = 13 # Cox et al. 1987, PubMed ID 3651732.
80 @staticmethod
81 def longname(req: "CamcopsRequest") -> str:
82 _ = req.gettext
83 return _("Edinburgh Postnatal Depression Scale")
85 def get_trackers(self, req: CamcopsRequest) -> List[TrackerInfo]:
86 return [TrackerInfo(
87 value=self.total_score(),
88 plot_label="EPDS total score (rating depressive symptoms)",
89 axis_label=f"Total score (out of {self.MAX_TOTAL})",
90 axis_min=-0.5,
91 axis_max=self.MAX_TOTAL + 0.5,
92 horizontal_lines=[
93 self.CUTOFF_2_GREATER_OR_EQUAL - 0.5,
94 self.CUTOFF_1_GREATER_OR_EQUAL - 0.5,
95 ],
96 horizontal_labels=[
97 TrackerLabel(self.CUTOFF_2_GREATER_OR_EQUAL,
98 "likely depression"),
99 TrackerLabel(self.CUTOFF_1_GREATER_OR_EQUAL,
100 "possible depression"),
101 ]
102 )]
104 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
105 if not self.is_complete():
106 return CTV_INCOMPLETE
107 text = f"EPDS total: {self.total_score()}/{self.MAX_TOTAL}"
108 return [CtvInfo(content=text)]
110 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
111 return self.standard_task_summary_fields() + [
112 SummaryElement(
113 name="total", coltype=Integer(),
114 value=self.total_score(),
115 comment=f"Total score (out of {self.MAX_TOTAL})"
116 ),
117 ]
119 def is_complete(self) -> bool:
120 return self.all_fields_not_none(self.TASK_FIELDS)
122 def total_score(self) -> int:
123 return self.sum_fields(self.TASK_FIELDS)
125 def get_task_html(self, req: CamcopsRequest) -> str:
126 score = self.total_score()
127 above_cutoff_1 = score >= 10
128 above_cutoff_2 = score >= 13
129 answer_dicts = []
130 for q in range(1, self.NQUESTIONS + 1):
131 d = {None: "?"}
132 for option in range(0, 4):
133 d[option] = (
134 str(option) + " — " +
135 self.wxstring(req, "q" + str(q) + "_option" + str(option)))
136 answer_dicts.append(d)
138 q_a = ""
139 for q in range(1, self.NQUESTIONS + 1):
140 q_a += tr_qa(
141 self.wxstring(req, "q" + str(q) + "_question"),
142 get_from_dict(answer_dicts[q - 1], getattr(self, "q" + str(q)))
143 )
145 return f"""
146 <div class="{CssClass.SUMMARY}">
147 <table class="{CssClass.SUMMARY}">
148 {self.get_is_complete_tr(req)}
149 <tr>
150 <td>{req.sstring(SS.TOTAL_SCORE)}</td>
151 <td><b>{score}</b> / {self.MAX_TOTAL}</td>
152 </tr>
153 <tr>
154 <td>{self.wxstring(req, "above_cutoff_1")}
155 <sup>[1]</sup></td>
156 <td><b>{get_yes_no(req, above_cutoff_1)}</b></td>
157 </tr>
158 <tr>
159 <td>{self.wxstring(req, "above_cutoff_2")}
160 <sup>[2]</sup></td>
161 <td><b>{get_yes_no(req, above_cutoff_2)}</b></td>
162 </tr>
163 </table>
164 </div>
165 <div class="{CssClass.EXPLANATION}">
166 Ratings are over the last week.
167 <b>{self.wxstring(req, "always_look_at_suicide")}</b>
168 </div>
169 <table class="{CssClass.TASKDETAIL}">
170 <tr>
171 <th width="50%">Question</th>
172 <th width="50%">Answer</th>
173 </tr>
174 {q_a}
175 </table>
176 <div class="{CssClass.FOOTNOTES}">
177 [1] ≥{self.CUTOFF_1_GREATER_OR_EQUAL}.
178 [2] ≥{self.CUTOFF_2_GREATER_OR_EQUAL}.
179 (Cox et al. 1987, PubMed ID 3651732.)
180 </div>
181 <div class="{CssClass.COPYRIGHT}">
182 Edinburgh Postnatal Depression Scale:
183 © 1987 The Royal College of Psychiatrists. The Edinburgh
184 Postnatal Depression Scale may be photocopied by individual
185 researchers or clinicians for their own use without seeking
186 permission from the publishers. The scale must be copied in
187 full and all copies must acknowledge the following source: Cox,
188 J.L., Holden, J.M., & Sagovsky, R. (1987). Detection of
189 postnatal depression. Development of the 10-item Edinburgh
190 Postnatal Depression Scale. British Journal of Psychiatry, 150,
191 782-786. Written permission must be obtained from the Royal
192 College of Psychiatrists for copying and distribution to others
193 or for republication (in print, online or by any other medium).
194 </div>
195 """