Coverage for tasks/cgi_task.py : 57%

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/cgi_task.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"""
29from typing import Dict, List
31from sqlalchemy.sql.sqltypes import Integer
33from camcops_server.cc_modules.cc_constants import CssClass
34from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
35from camcops_server.cc_modules.cc_html import (
36 answer,
37 italic,
38 tr,
39 tr_qa,
40)
41from camcops_server.cc_modules.cc_request import CamcopsRequest
42from camcops_server.cc_modules.cc_sqla_coltypes import (
43 CamcopsColumn,
44 PermittedValueChecker,
45)
46from camcops_server.cc_modules.cc_summaryelement import SummaryElement
47from camcops_server.cc_modules.cc_task import (
48 get_from_dict,
49 Task,
50 TaskHasClinicianMixin,
51 TaskHasPatientMixin,
52)
53from camcops_server.cc_modules.cc_trackerhelpers import TrackerInfo
56# =============================================================================
57# CGI
58# =============================================================================
60class Cgi(TaskHasPatientMixin, TaskHasClinicianMixin, Task):
61 """
62 Server implementation of the CGI task.
63 """
64 __tablename__ = "cgi"
65 shortname = "CGI"
66 provides_trackers = True
68 q1 = CamcopsColumn(
69 "q1", Integer,
70 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=7),
71 comment="Q1. Severity (1-7, higher worse, 0 not assessed)"
72 )
73 q2 = CamcopsColumn(
74 "q2", Integer,
75 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=7),
76 comment="Q2. Global improvement (1-7, higher worse, 0 not assessed)"
77 )
78 q3t = CamcopsColumn(
79 "q3t", Integer,
80 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=4),
81 comment="Q3T. Therapeutic effects (1-4, higher worse, 0 not assessed)"
82 )
83 q3s = CamcopsColumn(
84 "q3s", Integer,
85 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=4),
86 comment="Q3S. Side effects (1-4, higher worse, 0 not assessed)"
87 )
88 q3 = CamcopsColumn(
89 "q3", Integer,
90 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=16),
91 comment="Q3 (calculated). Efficacy index [(Q3T - 1) * 4 + Q3S]."
92 )
94 TASK_FIELDS = ["q1", "q2", "q3t", "q3s", "q3"]
95 MAX_SCORE = 30
97 @staticmethod
98 def longname(req: "CamcopsRequest") -> str:
99 _ = req.gettext
100 return _("Clinical Global Impressions")
102 def get_trackers(self, req: CamcopsRequest) -> List[TrackerInfo]:
103 return [TrackerInfo(
104 value=self.total_score(),
105 plot_label="CGI total score",
106 axis_label=f"Total score (out of {self.MAX_SCORE})",
107 axis_min=-0.5,
108 axis_max=self.MAX_SCORE + 0.5
109 )]
111 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
112 if not self.is_complete():
113 return CTV_INCOMPLETE
114 return [CtvInfo(
115 content=f"CGI total score {self.total_score()}/{self.MAX_SCORE}"
116 )]
118 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
119 return self.standard_task_summary_fields() + [
120 SummaryElement(name="total",
121 coltype=Integer(),
122 value=self.total_score()),
123 ]
125 def is_complete(self) -> bool:
126 if not (self.all_fields_not_none(self.TASK_FIELDS) and
127 self.field_contents_valid()):
128 return False
129 # Requirement for everything to be non-zero removed in v2.0.0
130 # if self.q1 == 0 or self.q2 == 0 or self.q3t == 0 or self.q3s == 0:
131 # return False
132 return True
134 def total_score(self) -> int:
135 return self.sum_fields(["q1", "q2", "q3"])
137 def get_task_html(self, req: CamcopsRequest) -> str:
138 q1_dict = {
139 None: None,
140 0: self.wxstring(req, "q1_option0"),
141 1: self.wxstring(req, "q1_option1"),
142 2: self.wxstring(req, "q1_option2"),
143 3: self.wxstring(req, "q1_option3"),
144 4: self.wxstring(req, "q1_option4"),
145 5: self.wxstring(req, "q1_option5"),
146 6: self.wxstring(req, "q1_option6"),
147 7: self.wxstring(req, "q1_option7"),
148 }
149 q2_dict = {
150 None: None,
151 0: self.wxstring(req, "q2_option0"),
152 1: self.wxstring(req, "q2_option1"),
153 2: self.wxstring(req, "q2_option2"),
154 3: self.wxstring(req, "q2_option3"),
155 4: self.wxstring(req, "q2_option4"),
156 5: self.wxstring(req, "q2_option5"),
157 6: self.wxstring(req, "q2_option6"),
158 7: self.wxstring(req, "q2_option7"),
159 }
160 q3t_dict = {
161 None: None,
162 0: self.wxstring(req, "q3t_option0"),
163 1: self.wxstring(req, "q3t_option1"),
164 2: self.wxstring(req, "q3t_option2"),
165 3: self.wxstring(req, "q3t_option3"),
166 4: self.wxstring(req, "q3t_option4"),
167 }
168 q3s_dict = {
169 None: None,
170 0: self.wxstring(req, "q3s_option0"),
171 1: self.wxstring(req, "q3s_option1"),
172 2: self.wxstring(req, "q3s_option2"),
173 3: self.wxstring(req, "q3s_option3"),
174 4: self.wxstring(req, "q3s_option4"),
175 }
177 tr_total_score = tr(
178 "Total score <sup>[1]</sup>",
179 answer(self.total_score())
180 )
181 tr_q1 = tr_qa(
182 self.wxstring(req, "q1_s") + " <sup>[2]</sup>",
183 get_from_dict(q1_dict, self.q1)
184 )
185 tr_q2 = tr_qa(
186 self.wxstring(req, "q2_s") + " <sup>[2]</sup>",
187 get_from_dict(q2_dict, self.q2)
188 )
189 tr_q3t = tr_qa(
190 self.wxstring(req, "q3t_s") + " <sup>[3]</sup>",
191 get_from_dict(q3t_dict, self.q3t)
192 )
193 tr_q3s = tr_qa(
194 self.wxstring(req, "q3s_s") + " <sup>[3]</sup>",
195 get_from_dict(q3s_dict, self.q3s)
196 )
197 tr_q3 = tr(
198 f"""
199 {self.wxstring(req, "q3_s")} <sup>[4]</sup>
200 <div class="{CssClass.SMALLPRINT}">
201 [(Q3T – 1) × 4 + Q3S]
202 </div>
203 """,
204 answer(self.q3, formatter_answer=italic)
205 )
206 return f"""
207 <div class="{CssClass.SUMMARY}">
208 <table class="{CssClass.SUMMARY}">
209 {self.get_is_complete_tr(req)}
210 {tr_total_score}
211 </table>
212 </div>
213 <table class="{CssClass.TASKDETAIL}">
214 <tr>
215 <th width="30%">Question</th>
216 <th width="70%">Answer</th>
217 </tr>
218 {tr_q1}
219 {tr_q2}
220 {tr_q3t}
221 {tr_q3s}
222 {tr_q3}
223 </table>
224 <div class="{CssClass.FOOTNOTES}">
225 [1] Total score: Q1 + Q2 + Q3. Range 3–30 when complete.
226 [2] Questions 1 and 2 are scored 1–7 (0 for not assessed).
227 [3] Questions 3T and 3S are scored 1–4 (0 for not assessed).
228 [4] Q3 is scored 1–16 if Q3T/Q3S complete.
229 </div>
230 """
233# =============================================================================
234# CGI-I
235# =============================================================================
237class CgiI(TaskHasPatientMixin, TaskHasClinicianMixin, Task):
238 __tablename__ = "cgi_i"
239 shortname = "CGI-I"
240 extrastring_taskname = "cgi" # shares with CGI
242 q = CamcopsColumn(
243 "q", Integer,
244 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=7),
245 comment="Global improvement (1-7, higher worse)"
246 )
248 TASK_FIELDS = ["q"]
250 @staticmethod
251 def longname(req: "CamcopsRequest") -> str:
252 _ = req.gettext
253 return _("Clinical Global Impressions – Improvement")
255 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
256 if not self.is_complete():
257 return CTV_INCOMPLETE
258 return [CtvInfo(
259 content="CGI-I rating: {}".format(self.get_rating_text(req))
260 )]
262 def is_complete(self) -> bool:
263 return (self.all_fields_not_none(self.TASK_FIELDS) and
264 self.field_contents_valid())
266 def get_rating_text(self, req: CamcopsRequest) -> str:
267 qdict = self.get_q_dict(req)
268 return get_from_dict(qdict, self.q)
270 def get_q_dict(self, req: CamcopsRequest) -> Dict:
271 return {
272 None: None,
273 0: self.wxstring(req, "q2_option0"),
274 1: self.wxstring(req, "q2_option1"),
275 2: self.wxstring(req, "q2_option2"),
276 3: self.wxstring(req, "q2_option3"),
277 4: self.wxstring(req, "q2_option4"),
278 5: self.wxstring(req, "q2_option5"),
279 6: self.wxstring(req, "q2_option6"),
280 7: self.wxstring(req, "q2_option7"),
281 }
283 def get_task_html(self, req: CamcopsRequest) -> str:
284 return f"""
285 <div class="{CssClass.SUMMARY}">
286 <table class="{CssClass.SUMMARY}">
287 {self.get_is_complete_tr(req)}
288 </table>
289 </div>
290 <table class="{CssClass.TASKDETAIL}">
291 <tr>
292 <th width="50%">Question</th>
293 <th width="50%">Answer</th>
294 </tr>
295 {tr_qa(self.wxstring(req, "i_q"), self.get_rating_text(req))}
296 </table>
297 """