Coverage for tasks/elixhauserci.py: 70%
37 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/elixhauserci.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**Elixhauser Comorbidity Index task.**
28"""
30from typing import Any, List, Type
32from sqlalchemy.sql.sqltypes import Integer
34from camcops_server.cc_modules.cc_constants import CssClass
35from camcops_server.cc_modules.cc_html import get_yes_no_unknown, tr_qa
36from camcops_server.cc_modules.cc_request import CamcopsRequest
37from camcops_server.cc_modules.cc_sqla_coltypes import bool_column
38from camcops_server.cc_modules.cc_summaryelement import SummaryElement
39from camcops_server.cc_modules.cc_task import (
40 Task,
41 TaskHasClinicianMixin,
42 TaskHasPatientMixin,
43)
44from camcops_server.cc_modules.cc_text import SS
47# =============================================================================
48# ElixhauserCI
49# =============================================================================
51FIELDNAMES = [
52 "congestive_heart_failure",
53 "cardiac_arrhythmias",
54 "valvular_disease",
55 "pulmonary_circulation_disorders",
56 "peripheral_vascular_disorders",
57 "hypertension_uncomplicated",
58 "hypertension_complicated",
59 "paralysis",
60 "other_neurological_disorders",
61 "chronic_pulmonary_disease",
62 "diabetes_uncomplicated",
63 "diabetes_complicated",
64 "hypothyroidism",
65 "renal_failure",
66 "liver_disease",
67 "peptic_ulcer_disease_exc_bleeding",
68 "aids_hiv",
69 "lymphoma",
70 "metastatic_cancer",
71 "solid_tumor_without_metastasis",
72 "rheumatoid_arthritis_collagen_vascular_diseases",
73 "coagulopathy",
74 "obesity",
75 "weight_loss",
76 "fluid_electrolyte_disorders",
77 "blood_loss_anemia",
78 "deficiency_anemia",
79 "alcohol_abuse",
80 "drug_abuse",
81 "psychoses",
82 "depression",
83]
84MAX_SCORE = len(FIELDNAMES)
86CONSTRAINT_NAME_MAP = {
87 "pulmonary_circulation_disorders": "ck_elixhauserci_pulm_circ",
88 "peptic_ulcer_disease_exc_bleeding": "ck_elixhauserci_peptic",
89 "solid_tumor_without_metastasis": "ck_elixhauserci_tumour_no_mets",
90 "rheumatoid_arthritis_collagen_vascular_diseases": "ck_elixhauserci_ra_cvd", # noqa
91}
94class ElixhauserCI( # type: ignore[misc]
95 TaskHasPatientMixin,
96 TaskHasClinicianMixin,
97 Task,
98):
99 __tablename__ = "elixhauserci"
100 shortname = "ElixhauserCI"
102 @classmethod
103 def extend_columns(cls: Type["ElixhauserCI"], **kwargs: Any) -> None:
104 for colname in FIELDNAMES:
105 constraint_name = CONSTRAINT_NAME_MAP.get(colname)
106 setattr(
107 cls,
108 colname,
109 bool_column(
110 colname,
111 comment="Disease present (0 no, 1 yes)",
112 constraint_name=constraint_name,
113 ),
114 )
116 @staticmethod
117 def longname(req: "CamcopsRequest") -> str:
118 _ = req.gettext
119 return _("Elixhauser Comorbidity Index")
121 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
122 return self.standard_task_summary_fields() + [
123 SummaryElement(
124 name="total",
125 coltype=Integer(),
126 value=self.total_score(),
127 comment=f"Total score (out of {MAX_SCORE})",
128 )
129 ]
131 def is_complete(self) -> bool:
132 return self.all_fields_not_none(FIELDNAMES)
134 def total_score(self) -> int:
135 return self.count_booleans(FIELDNAMES)
137 def get_task_html(self, req: CamcopsRequest) -> str:
138 score = self.total_score()
139 q_a = ""
140 for f in FIELDNAMES:
141 v = getattr(self, f)
142 q_a += tr_qa(self.wxstring(req, f), get_yes_no_unknown(req, v))
143 return f"""
144 <div class="{CssClass.SUMMARY}">
145 <table class="{CssClass.SUMMARY}">
146 {self.get_is_complete_tr(req)}
147 <tr>
148 <td>{req.sstring(SS.TOTAL_SCORE)}</td>
149 <td><b>{score}</b> / {MAX_SCORE}</td>
150 </tr>
151 </table>
152 </div>
153 <table class="{CssClass.TASKDETAIL}">
154 <tr>
155 <th width="50%">Question</th>
156 <th width="50%">Answer</th>
157 </tr>
158 {q_a}
159 </table>
160 """