Coverage for tasks/zbi.py: 65%
51 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/tasks/zbi.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
13 CamCOPS is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 CamCOPS is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
26===============================================================================
28"""
30from typing import Any, Dict, List, Tuple, Type
32from cardinal_pythonlib.stringfunc import strseq
33from sqlalchemy.ext.declarative import DeclarativeMeta
34from sqlalchemy.sql.sqltypes import Integer
36from camcops_server.cc_modules.cc_constants import (
37 CssClass,
38 DATA_COLLECTION_UNLESS_UPGRADED_DIV,
39)
40from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
41from camcops_server.cc_modules.cc_db import add_multiple_columns
42from camcops_server.cc_modules.cc_html import answer, tr
43from camcops_server.cc_modules.cc_request import CamcopsRequest
44from camcops_server.cc_modules.cc_string import AS
45from camcops_server.cc_modules.cc_summaryelement import SummaryElement
46from camcops_server.cc_modules.cc_task import (
47 get_from_dict,
48 Task,
49 TaskHasPatientMixin,
50 TaskHasRespondentMixin,
51)
54# =============================================================================
55# ZBI
56# =============================================================================
59class Zbi12Metaclass(DeclarativeMeta):
60 # noinspection PyInitNewSignature
61 def __init__(
62 cls: Type["Zbi12"],
63 name: str,
64 bases: Tuple[Type, ...],
65 classdict: Dict[str, Any],
66 ) -> None:
67 add_multiple_columns(
68 cls,
69 "q",
70 1,
71 cls.NQUESTIONS,
72 minimum=cls.MIN_PER_Q,
73 maximum=cls.MAX_PER_Q,
74 comment_fmt="Q{n}, {s} (0-4, higher worse)",
75 comment_strings=[
76 "insufficient time for self", # 1
77 "stressed with other responsibilities",
78 "angry",
79 "other relationships affected",
80 "strained", # 5
81 "health suffered",
82 "insufficient privacy",
83 "social life suffered",
84 "lost control",
85 "uncertain", # 10
86 "should do more",
87 "could care better",
88 ],
89 )
90 super().__init__(name, bases, classdict)
93class Zbi12(
94 TaskHasRespondentMixin, TaskHasPatientMixin, Task, metaclass=Zbi12Metaclass
95):
96 """
97 Server implementation of the ZBI-12 task.
98 """
100 __tablename__ = "zbi12"
101 shortname = "ZBI-12"
102 info_filename_stem = "zbi"
104 MIN_PER_Q = 0
105 MAX_PER_Q = 4
106 NQUESTIONS = 12
107 TASK_FIELDS = strseq("q", 1, NQUESTIONS)
108 MAX_TOTAL = MAX_PER_Q * NQUESTIONS
110 @staticmethod
111 def longname(req: "CamcopsRequest") -> str:
112 _ = req.gettext
113 return _("Zarit Burden Interview-12")
115 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
116 return self.standard_task_summary_fields() + [
117 SummaryElement(
118 name="total_score",
119 coltype=Integer(),
120 value=self.total_score(),
121 comment=f"Total score (/ {self.MAX_TOTAL})",
122 )
123 ]
125 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
126 if not self.is_complete():
127 return CTV_INCOMPLETE
128 return [
129 CtvInfo(
130 content=f"ZBI-12 total score "
131 f"{self.total_score()}/{self.MAX_TOTAL}"
132 )
133 ]
135 def total_score(self) -> int:
136 return self.sum_fields(self.TASK_FIELDS)
138 def is_complete(self) -> bool:
139 return (
140 self.field_contents_valid()
141 and self.is_respondent_complete()
142 and self.all_fields_not_none(self.TASK_FIELDS)
143 )
145 def get_task_html(self, req: CamcopsRequest) -> str:
146 option_dict = {None: None}
147 for a in range(self.MIN_PER_Q, self.MAX_PER_Q + 1):
148 option_dict[a] = req.wappstring(AS.ZBI_A_PREFIX + str(a))
149 h = f"""
150 <div class="{CssClass.SUMMARY}">
151 <table class="{CssClass.SUMMARY}">
152 {self.get_is_complete_tr(req)}
153 <tr>
154 <td>Total score (/ {self.MAX_TOTAL})</td>
155 <td>{answer(self.total_score())}</td>
156 </td>
157 </table>
158 </div>
159 <table class="{CssClass.TASKDETAIL}">
160 <tr>
161 <th width="75%">Question</th>
162 <th width="25%">Answer ({self.MIN_PER_Q}–{self.MAX_PER_Q})
163 </th>
164 </tr>
165 """
166 for q in range(1, self.NQUESTIONS + 1):
167 a = getattr(self, "q" + str(q))
168 fa = (
169 f"{a}: {get_from_dict(option_dict, a)}"
170 if a is not None
171 else None
172 )
173 h += tr(self.wxstring(req, "q" + str(q)), answer(fa))
174 h += (
175 """
176 </table>
177 """
178 + DATA_COLLECTION_UNLESS_UPGRADED_DIV
179 )
180 return h