Coverage for tasks/distressthermometer.py: 49%
53 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/distressthermometer.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"""
28from typing import Any, List, Optional, Type
30from cardinal_pythonlib.stringfunc import strseq
31from sqlalchemy.orm import Mapped, mapped_column
32from sqlalchemy.sql.sqltypes import UnicodeText
34from camcops_server.cc_modules.cc_constants import CssClass, PV
35from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
36from camcops_server.cc_modules.cc_db import add_multiple_columns
37from camcops_server.cc_modules.cc_html import (
38 get_yes_no_none,
39 subheading_spanning_two_columns,
40 tr_qa,
41)
42from camcops_server.cc_modules.cc_request import CamcopsRequest
43from camcops_server.cc_modules.cc_sqla_coltypes import (
44 mapped_camcops_column,
45 PermittedValueChecker,
46)
47from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin
50# =============================================================================
51# Distress Thermometer
52# =============================================================================
55class DistressThermometer( # type: ignore[misc]
56 TaskHasPatientMixin,
57 Task,
58):
59 """
60 Server implementation of the DistressThermometer task.
61 """
63 __tablename__ = "distressthermometer"
64 shortname = "Distress Thermometer"
66 @classmethod
67 def extend_columns(
68 cls: Type["DistressThermometer"], **kwargs: Any
69 ) -> None:
70 add_multiple_columns(
71 cls,
72 "q",
73 1,
74 cls.NQUESTIONS,
75 pv=PV.BIT,
76 comment_fmt="{n}. {s} (0 no, 1 yes)",
77 comment_strings=[
78 "child care",
79 "housing",
80 "insurance/financial",
81 "transportation",
82 "work/school",
83 "children",
84 "partner",
85 "close friend/relative",
86 "depression",
87 "fears",
88 "nervousness",
89 "sadness",
90 "worry",
91 "loss of interest",
92 "spiritual/religious",
93 "appearance",
94 "bathing/dressing",
95 "breathing",
96 "urination",
97 "constipation",
98 "diarrhoea",
99 "eating",
100 "fatigue",
101 "feeling swollen",
102 "fevers",
103 "getting around",
104 "indigestion",
105 "memory/concentration",
106 "mouth sores",
107 "nausea",
108 "nose dry/congested",
109 "pain",
110 "sexual",
111 "skin dry/itchy",
112 "sleep",
113 "tingling in hands/feet",
114 ],
115 )
117 distress: Mapped[Optional[int]] = mapped_camcops_column(
118 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=10),
119 comment="Distress (0 none - 10 extreme)",
120 )
121 other: Mapped[Optional[str]] = mapped_column(
122 UnicodeText, comment="Other problems"
123 )
125 NQUESTIONS = 36
126 COMPLETENESS_FIELDS = strseq("q", 1, NQUESTIONS) + ["distress"]
128 @staticmethod
129 def longname(req: "CamcopsRequest") -> str:
130 _ = req.gettext
131 return _("Distress Thermometer")
133 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
134 if self.distress is None:
135 return CTV_INCOMPLETE
136 return [CtvInfo(content=f"Overall distress: {self.distress}/10")]
138 def is_complete(self) -> bool:
139 return (
140 self.all_fields_not_none(self.COMPLETENESS_FIELDS)
141 and self.field_contents_valid()
142 )
144 def get_task_html(self, req: CamcopsRequest) -> str:
145 h = f"""
146 <div class="{CssClass.SUMMARY}">
147 <table class="{CssClass.SUMMARY}">
148 {self.get_is_complete_tr(req)}
149 {tr_qa("Overall distress (0–10)", self.distress)}
150 </table>
151 </div>
152 <div class="{CssClass.EXPLANATION}">
153 All questions relate to distress/problems “in the past week,
154 including today” (yes = problem, no = no problem).
155 </div>
156 <table class="{CssClass.TASKDETAIL}">
157 <tr>
158 <th width="50%">Question</th>
159 <th width="50%">Answer</th>
160 </tr>
161 """
162 h += tr_qa(
163 "Distress (0 no distress – 10 extreme distress)", self.distress
164 )
165 h += subheading_spanning_two_columns("Practical problems")
166 for i in range(1, 5 + 1):
167 h += tr_qa(
168 f"{i}. {self.wxstring(req, 'q' + str(i))}",
169 get_yes_no_none(req, getattr(self, "q" + str(i))),
170 )
171 h += subheading_spanning_two_columns("Family problems")
172 for i in range(6, 8 + 1):
173 h += tr_qa(
174 f"{i}. {self.wxstring(req, 'q' + str(i))}",
175 get_yes_no_none(req, getattr(self, "q" + str(i))),
176 )
177 h += subheading_spanning_two_columns("Emotional problems")
178 for i in range(9, 14 + 1):
179 h += tr_qa(
180 f"{i}. {self.wxstring(req, 'q' + str(i))}",
181 get_yes_no_none(req, getattr(self, "q" + str(i))),
182 )
183 h += subheading_spanning_two_columns("Spiritual problems")
184 for i in range(15, 15 + 1):
185 h += tr_qa(
186 f"{i}. {self.wxstring(req, 'q' + str(i))}",
187 get_yes_no_none(req, getattr(self, "q" + str(i))),
188 )
189 h += subheading_spanning_two_columns("Physical problems")
190 for i in range(16, self.NQUESTIONS + 1):
191 h += tr_qa(
192 f"{i}. {self.wxstring(req, 'q' + str(i))}",
193 get_yes_no_none(req, getattr(self, "q" + str(i))),
194 )
195 h += subheading_spanning_two_columns("Other problems")
196 h += tr_qa(self.wxstring(req, "other_s"), self.other)
197 h += """
198 </table>
199 """
200 return h