Coverage for tasks/icd10schizotypal.py : 59%

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/icd10schizotypal.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 Any, Dict, List, Optional, Tuple, Type
31from cardinal_pythonlib.datetimefunc import format_datetime
32import cardinal_pythonlib.rnc_web as ws
33from cardinal_pythonlib.stringfunc import strseq
34from sqlalchemy.ext.declarative import DeclarativeMeta
35from sqlalchemy.sql.schema import Column
36from sqlalchemy.sql.sqltypes import Boolean, Date, UnicodeText
38from camcops_server.cc_modules.cc_constants import (
39 CssClass,
40 DateFormat,
41 ICD10_COPYRIGHT_DIV,
42 PV,
43)
44from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
45from camcops_server.cc_modules.cc_db import add_multiple_columns
46from camcops_server.cc_modules.cc_html import (
47 get_yes_no_none,
48 td,
49 tr,
50 tr_qa,
51)
52from camcops_server.cc_modules.cc_request import CamcopsRequest
53from camcops_server.cc_modules.cc_sqla_coltypes import (
54 BIT_CHECKER,
55 CamcopsColumn,
56)
57from camcops_server.cc_modules.cc_string import AS
58from camcops_server.cc_modules.cc_summaryelement import SummaryElement
59from camcops_server.cc_modules.cc_task import (
60 Task,
61 TaskHasClinicianMixin,
62 TaskHasPatientMixin,
63)
64from camcops_server.cc_modules.cc_text import SS
67# =============================================================================
68# Icd10Schizotypal
69# =============================================================================
71class Icd10SchizotypalMetaclass(DeclarativeMeta):
72 # noinspection PyInitNewSignature
73 def __init__(cls: Type['Icd10Schizotypal'],
74 name: str,
75 bases: Tuple[Type, ...],
76 classdict: Dict[str, Any]) -> None:
77 add_multiple_columns(
78 cls, "a", 1, cls.N_A, Boolean,
79 pv=PV.BIT,
80 comment_fmt="Criterion A({n}), {s}",
81 comment_strings=[
82 "inappropriate/constricted affect",
83 "odd/eccentric/peculiar",
84 "poor rapport/social withdrawal",
85 "odd beliefs/magical thinking",
86 "suspiciousness/paranoid ideas",
87 "ruminations without inner resistance",
88 "unusual perceptual experiences",
89 "vague/circumstantial/metaphorical/over-elaborate/stereotyped "
90 "thinking",
91 "occasional transient quasi-psychotic episodes",
92 ]
93 )
94 super().__init__(name, bases, classdict)
97class Icd10Schizotypal(TaskHasClinicianMixin, TaskHasPatientMixin, Task,
98 metaclass=Icd10SchizotypalMetaclass):
99 """
100 Server implementation of the ICD10-SZTYP task.
101 """
102 __tablename__ = "icd10schizotypal"
103 shortname = "ICD10-SZTYP"
105 date_pertains_to = Column(
106 "date_pertains_to", Date,
107 comment="Date the assessment pertains to"
108 )
109 comments = Column(
110 "comments", UnicodeText,
111 comment="Clinician's comments"
112 )
113 b = CamcopsColumn(
114 "b", Boolean,
115 permitted_value_checker=BIT_CHECKER,
116 comment="Criterion (B). True if: the subject has never met "
117 "the criteria for any disorder in F20 (Schizophrenia)."
118 )
120 N_A = 9
121 A_FIELDS = strseq("a", 1, N_A)
123 @staticmethod
124 def longname(req: "CamcopsRequest") -> str:
125 _ = req.gettext
126 return "ICD-10 criteria for schizotypal disorder (F21)"
128 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
129 if not self.is_complete():
130 return CTV_INCOMPLETE
131 c = self.meets_criteria()
132 if c is None:
133 category = "Unknown if met or not met"
134 elif c:
135 category = "Met"
136 else:
137 category = "Not met"
138 infolist = [CtvInfo(
139 content=(
140 "Pertains to: {}. Criteria for schizotypal "
141 "disorder: {}.".format(
142 format_datetime(self.date_pertains_to,
143 DateFormat.LONG_DATE),
144 category
145 )
146 )
147 )]
148 if self.comments:
149 infolist.append(CtvInfo(content=ws.webify(self.comments)))
150 return infolist
152 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
153 return self.standard_task_summary_fields() + [
154 SummaryElement(name="meets_criteria", coltype=Boolean(),
155 value=self.meets_criteria(),
156 comment="Meets criteria for schizotypal disorder?"),
157 ]
159 # Meets criteria? These also return null for unknown.
160 def meets_criteria(self) -> Optional[bool]:
161 if not self.is_complete():
162 return None
163 return self.count_booleans(self.A_FIELDS) >= 4 and self.b
165 def is_complete(self) -> bool:
166 return (
167 self.date_pertains_to is not None and
168 self.all_fields_not_none(self.A_FIELDS) and
169 self.b is not None and
170 self.field_contents_valid()
171 )
173 def text_row(self, req: CamcopsRequest, wstringname: str) -> str:
174 return tr(td(self.wxstring(req, wstringname)),
175 td("", td_class=CssClass.SUBHEADING),
176 literal=True)
178 def get_task_html(self, req: CamcopsRequest) -> str:
179 q_a = self.text_row(req, "a")
180 for i in range(1, self.N_A + 1):
181 q_a += self.get_twocol_bool_row_true_false(
182 req, "a" + str(i), self.wxstring(req, "a" + str(i)))
183 q_a += self.get_twocol_bool_row_true_false(
184 req, "b", self.wxstring(req, "b"))
185 h = """
186 {clinician_comments}
187 <div class="{CssClass.SUMMARY}">
188 <table class="{CssClass.SUMMARY}">
189 {tr_is_complete}
190 {date_pertains_to}
191 {meets_criteria}
192 </table>
193 </div>
194 <table class="{CssClass.TASKDETAIL}">
195 <tr>
196 <th width="80%">Question</th>
197 <th width="20%">Answer</th>
198 </tr>
199 {q_a}
200 </table>
201 {ICD10_COPYRIGHT_DIV}
202 """.format(
203 clinician_comments=self.get_standard_clinician_comments_block(
204 req, self.comments),
205 CssClass=CssClass,
206 tr_is_complete=self.get_is_complete_tr(req),
207 date_pertains_to=tr_qa(
208 req.wappstring(AS.DATE_PERTAINS_TO),
209 format_datetime(self.date_pertains_to,
210 DateFormat.LONG_DATE, default=None)
211 ),
212 meets_criteria=tr_qa(
213 req.sstring(SS.MEETS_CRITERIA),
214 get_yes_no_none(req, self.meets_criteria())
215 ),
216 q_a=q_a,
217 ICD10_COPYRIGHT_DIV=ICD10_COPYRIGHT_DIV,
218 )
219 return h