Coverage for tasks/elixhauserci.py: 70%

37 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-15 14:23 +0100

1""" 

2camcops_server/tasks/elixhauserci.py 

3 

4=============================================================================== 

5 

6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry. 

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk). 

8 

9 This file is part of CamCOPS. 

10 

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. 

15 

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. 

20 

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/>. 

23 

24=============================================================================== 

25 

26**Elixhauser Comorbidity Index task.** 

27 

28""" 

29 

30from typing import Any, List, Type 

31 

32from sqlalchemy.sql.sqltypes import Integer 

33 

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 

45 

46 

47# ============================================================================= 

48# ElixhauserCI 

49# ============================================================================= 

50 

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) 

85 

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} 

92 

93 

94class ElixhauserCI( # type: ignore[misc] 

95 TaskHasPatientMixin, 

96 TaskHasClinicianMixin, 

97 Task, 

98): 

99 __tablename__ = "elixhauserci" 

100 shortname = "ElixhauserCI" 

101 

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 ) 

115 

116 @staticmethod 

117 def longname(req: "CamcopsRequest") -> str: 

118 _ = req.gettext 

119 return _("Elixhauser Comorbidity Index") 

120 

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 ] 

130 

131 def is_complete(self) -> bool: 

132 return self.all_fields_not_none(FIELDNAMES) 

133 

134 def total_score(self) -> int: 

135 return self.count_booleans(FIELDNAMES) 

136 

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 """