Coverage for tasks/icd10mixed.py: 62%
50 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/icd10mixed.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"""
28import datetime
29from typing import List, Optional
31from cardinal_pythonlib.datetimefunc import format_datetime
32from cardinal_pythonlib.typetests import is_false
33import cardinal_pythonlib.rnc_web as ws
34from sqlalchemy.orm import Mapped, mapped_column
35from sqlalchemy.sql.sqltypes import Boolean, UnicodeText
37from camcops_server.cc_modules.cc_constants import (
38 CssClass,
39 DateFormat,
40 ICD10_COPYRIGHT_DIV,
41)
42from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
43from camcops_server.cc_modules.cc_html import get_true_false_none, tr_qa
44from camcops_server.cc_modules.cc_request import CamcopsRequest
45from camcops_server.cc_modules.cc_sqla_coltypes import (
46 BIT_CHECKER,
47 mapped_camcops_column,
48)
49from camcops_server.cc_modules.cc_string import AS
50from camcops_server.cc_modules.cc_summaryelement import SummaryElement
51from camcops_server.cc_modules.cc_task import (
52 Task,
53 TaskHasClinicianMixin,
54 TaskHasPatientMixin,
55)
56from camcops_server.cc_modules.cc_text import SS
59# =============================================================================
60# Icd10Mixed
61# =============================================================================
64class Icd10Mixed(TaskHasClinicianMixin, TaskHasPatientMixin, Task): # type: ignore[misc] # noqa: E501
65 """
66 Server implementation of the ICD10-MIXED task.
67 """
69 __tablename__ = "icd10mixed"
70 shortname = "ICD10-MIXED"
71 info_filename_stem = "icd"
73 date_pertains_to: Mapped[Optional[datetime.date]] = mapped_column(
74 comment="Date the assessment pertains to"
75 )
76 comments: Mapped[Optional[str]] = mapped_column(
77 UnicodeText, comment="Clinician's comments"
78 )
79 mixture_or_rapid_alternation: Mapped[Optional[bool]] = (
80 mapped_camcops_column(
81 permitted_value_checker=BIT_CHECKER,
82 comment="The episode is characterized by either a mixture or "
83 "a rapid alternation (i.e. within a few hours) of hypomanic, "
84 "manic and depressive symptoms.",
85 )
86 )
87 duration_at_least_2_weeks: Mapped[Optional[bool]] = mapped_camcops_column(
88 permitted_value_checker=BIT_CHECKER,
89 comment="Both manic and depressive symptoms must be prominent"
90 " most of the time during a period of at least two weeks.",
91 )
93 @staticmethod
94 def longname(req: "CamcopsRequest") -> str:
95 _ = req.gettext
96 return _(
97 "ICD-10 symptomatic criteria for a mixed affective episode "
98 "(as in e.g. F06.3, F25, F38.00, F31.6)"
99 )
101 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
102 if not self.is_complete():
103 return CTV_INCOMPLETE
104 category = (
105 "Meets" if self.meets_criteria() else "Does not meet"
106 ) + " criteria for mixed affective episode"
107 infolist = [
108 CtvInfo(
109 content="Pertains to: {}. {}.".format(
110 format_datetime(
111 self.date_pertains_to, DateFormat.LONG_DATE
112 ),
113 category,
114 )
115 )
116 ]
117 if self.comments:
118 infolist.append(CtvInfo(content=ws.webify(self.comments)))
119 return infolist
121 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
122 return self.standard_task_summary_fields() + [
123 SummaryElement(
124 name="meets_criteria",
125 coltype=Boolean(),
126 value=self.meets_criteria(),
127 comment="Meets criteria for a mixed affective episode?",
128 )
129 ]
131 # Meets criteria? These also return null for unknown.
132 def meets_criteria(self) -> Optional[bool]:
133 if (
134 self.mixture_or_rapid_alternation
135 and self.duration_at_least_2_weeks
136 ):
137 return True
138 if is_false(self.mixture_or_rapid_alternation):
139 return False
140 if is_false(self.duration_at_least_2_weeks):
141 return False
142 return None
144 def is_complete(self) -> bool:
145 return (
146 self.meets_criteria() is not None and self.field_contents_valid()
147 )
149 def get_task_html(self, req: CamcopsRequest) -> str:
150 return """
151 {clinician_comments}
152 <div class="{CssClass.SUMMARY}">
153 <table class="{CssClass.SUMMARY}">
154 {tr_is_complete}
155 {date_pertains_to}
156 {meets_criteria}
157 </table>
158 </div>
159 <div class="{CssClass.EXPLANATION}">
160 {icd10_symptomatic_disclaimer}
161 </div>
162 <table class="{CssClass.TASKDETAIL}">
163 <tr>
164 <th width="80%">Question</th>
165 <th width="20%">Answer</th>
166 </tr>
167 {mixture_or_rapid_alternation}
168 {duration_at_least_2_weeks}
169 </table>
170 {ICD10_COPYRIGHT_DIV}
171 """.format(
172 clinician_comments=self.get_standard_clinician_comments_block(
173 req, self.comments
174 ),
175 CssClass=CssClass,
176 tr_is_complete=self.get_is_complete_tr(req),
177 date_pertains_to=tr_qa(
178 req.wappstring(AS.DATE_PERTAINS_TO),
179 format_datetime(
180 self.date_pertains_to, DateFormat.LONG_DATE, default=None
181 ),
182 ),
183 meets_criteria=tr_qa(
184 req.sstring(SS.MEETS_CRITERIA),
185 get_true_false_none(req, self.meets_criteria()),
186 ),
187 icd10_symptomatic_disclaimer=req.wappstring(
188 AS.ICD10_SYMPTOMATIC_DISCLAIMER
189 ),
190 mixture_or_rapid_alternation=self.get_twocol_bool_row_true_false(
191 req, "mixture_or_rapid_alternation", self.wxstring(req, "a")
192 ),
193 duration_at_least_2_weeks=self.get_twocol_bool_row_true_false(
194 req, "duration_at_least_2_weeks", self.wxstring(req, "b")
195 ),
196 ICD10_COPYRIGHT_DIV=ICD10_COPYRIGHT_DIV,
197 )