Coverage for tasks/chit.py: 63%
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/chit.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**Cambridge-Chicago Compulsivity Trait Scale task.**
30"""
32from camcops_server.cc_modules.cc_constants import CssClass
33from camcops_server.cc_modules.cc_db import add_multiple_columns
34from camcops_server.cc_modules.cc_html import (
35 tr_qa,
36 get_yes_no_unknown,
37 tr,
38 answer,
39)
40from camcops_server.cc_modules.cc_request import CamcopsRequest
41from camcops_server.cc_modules.cc_sqla_coltypes import BoolColumn
42from camcops_server.cc_modules.cc_summaryelement import SummaryElement
43from camcops_server.cc_modules.cc_task import (
44 TaskHasPatientMixin,
45 Task,
46 get_from_dict,
47)
48from camcops_server.cc_modules.cc_text import SS
49from cardinal_pythonlib.stringfunc import strseq
50from sqlalchemy import Integer
51from sqlalchemy.ext.declarative import DeclarativeMeta
52from typing import List, Type, Tuple, Dict, Any
55class ChitMetaclass(DeclarativeMeta):
56 # noinspection PyInitNewSignature
57 def __init__(
58 cls: Type["Chit"],
59 name: str,
60 bases: Tuple[Type, ...],
61 classdict: Dict[str, Any],
62 ) -> None:
63 add_multiple_columns(
64 cls,
65 "q",
66 1,
67 cls.N_SCORED_QUESTIONS,
68 minimum=0,
69 maximum=3,
70 comment_fmt="Q{n} ({s}) (0 strongly disagree - 3 strongly agree)",
71 comment_strings=[
72 "hate unfinished task",
73 "just right",
74 "keep doing task",
75 "get stuck",
76 "habit",
77 "addictive",
78 "stubborn rigid",
79 "urges",
80 "rewarding things",
81 "hard moving",
82 "higher standards",
83 "improvement",
84 "complete",
85 "avoid situations",
86 "hobby",
87 ],
88 )
90 setattr(
91 cls,
92 "q16",
93 BoolColumn("q16", comment="Q16 (negative effect) (0 no, 1 yes)"),
94 )
96 super().__init__(name, bases, classdict)
99class Chit(TaskHasPatientMixin, Task, metaclass=ChitMetaclass):
100 __tablename__ = "chit"
101 shortname = "CHI-T"
103 N_SCORED_QUESTIONS = 15
104 N_QUESTIONS = 16
105 MAX_SCORE_MAIN = 3 * N_SCORED_QUESTIONS
106 SCORED_QUESTIONS = strseq("q", 1, N_SCORED_QUESTIONS)
107 ALL_QUESTIONS = strseq("q", 1, N_QUESTIONS)
109 @staticmethod
110 def longname(req: "CamcopsRequest") -> str:
111 _ = req.gettext
112 return _("Cambridge–Chicago Compulsivity Trait Scale")
114 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
115 return self.standard_task_summary_fields() + [
116 SummaryElement(
117 name="total",
118 coltype=Integer(),
119 value=self.total_score(),
120 comment=f"Total score (/{self.MAX_SCORE_MAIN})",
121 )
122 ]
124 def is_complete(self) -> bool:
125 if self.any_fields_none(self.ALL_QUESTIONS):
126 return False
127 if not self.field_contents_valid():
128 return False
129 return True
131 def total_score(self) -> int:
132 return self.sum_fields(self.SCORED_QUESTIONS)
134 def get_task_html(self, req: CamcopsRequest) -> str:
135 score_dict = {
136 None: None,
137 0: "0 — " + self.wxstring(req, "a0"),
138 1: "1 — " + self.wxstring(req, "a1"),
139 2: "2 — " + self.wxstring(req, "a2"),
140 3: "3 — " + self.wxstring(req, "a3"),
141 }
143 rows = ""
144 for i in range(1, self.N_SCORED_QUESTIONS + 1):
145 q_field = "q" + str(i)
146 question_cell = "{}. {}".format(i, self.wxstring(req, q_field))
147 answer_cell = get_from_dict(score_dict, getattr(self, q_field))
149 rows += tr_qa(question_cell, answer_cell)
151 rows += tr_qa(
152 "16. " + self.wxstring(req, "q16"), get_yes_no_unknown(req, "q16")
153 )
155 html = """
156 <div class="{CssClass.SUMMARY}">
157 <table class="{CssClass.SUMMARY}">
158 {tr_is_complete}
159 {total_score}
160 </table>
161 </div>
162 <table class="{CssClass.TASKDETAIL}">
163 <tr>
164 <th width="60%">Question</th>
165 <th width="40%">Answer</th>
166 </tr>
167 {rows}
168 </table>
169 <div class="{CssClass.FOOTNOTES}">
170 [1] Sum for questions 1–15.
171 </div>
172 """.format(
173 CssClass=CssClass,
174 tr_is_complete=self.get_is_complete_tr(req),
175 total_score=tr(
176 req.sstring(SS.TOTAL_SCORE) + " <sup>[1]</sup>",
177 answer(self.total_score()) + f" / {self.MAX_SCORE_MAIN}",
178 ),
179 rows=rows,
180 )
181 return html