Coverage for tasks/elixhauserci.py: 72%

40 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-08 23:14 +0000

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/elixhauserci.py 

5 

6=============================================================================== 

7 

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

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

10 

11 This file is part of CamCOPS. 

12 

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. 

17 

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. 

22 

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

25 

26=============================================================================== 

27 

28**Elixhauser Comorbidity Index task.** 

29 

30""" 

31 

32from typing import Any, Dict, List, Tuple, Type 

33 

34from sqlalchemy.ext.declarative import DeclarativeMeta 

35from sqlalchemy.sql.sqltypes import Integer 

36 

37from camcops_server.cc_modules.cc_constants import CssClass 

38from camcops_server.cc_modules.cc_html import get_yes_no_unknown, tr_qa 

39from camcops_server.cc_modules.cc_request import CamcopsRequest 

40from camcops_server.cc_modules.cc_sqla_coltypes import BoolColumn 

41from camcops_server.cc_modules.cc_summaryelement import SummaryElement 

42from camcops_server.cc_modules.cc_task import ( 

43 Task, 

44 TaskHasClinicianMixin, 

45 TaskHasPatientMixin, 

46) 

47from camcops_server.cc_modules.cc_text import SS 

48 

49 

50# ============================================================================= 

51# ElixhauserCI 

52# ============================================================================= 

53 

54FIELDNAMES = [ 

55 "congestive_heart_failure", 

56 "cardiac_arrhythmias", 

57 "valvular_disease", 

58 "pulmonary_circulation_disorders", 

59 "peripheral_vascular_disorders", 

60 "hypertension_uncomplicated", 

61 "hypertension_complicated", 

62 "paralysis", 

63 "other_neurological_disorders", 

64 "chronic_pulmonary_disease", 

65 "diabetes_uncomplicated", 

66 "diabetes_complicated", 

67 "hypothyroidism", 

68 "renal_failure", 

69 "liver_disease", 

70 "peptic_ulcer_disease_exc_bleeding", 

71 "aids_hiv", 

72 "lymphoma", 

73 "metastatic_cancer", 

74 "solid_tumor_without_metastasis", 

75 "rheumatoid_arthritis_collagen_vascular_diseases", 

76 "coagulopathy", 

77 "obesity", 

78 "weight_loss", 

79 "fluid_electrolyte_disorders", 

80 "blood_loss_anemia", 

81 "deficiency_anemia", 

82 "alcohol_abuse", 

83 "drug_abuse", 

84 "psychoses", 

85 "depression", 

86] 

87MAX_SCORE = len(FIELDNAMES) 

88 

89CONSTRAINT_NAME_MAP = { 

90 "pulmonary_circulation_disorders": "ck_elixhauserci_pulm_circ", 

91 "peptic_ulcer_disease_exc_bleeding": "ck_elixhauserci_peptic", 

92 "solid_tumor_without_metastasis": "ck_elixhauserci_tumour_no_mets", 

93 "rheumatoid_arthritis_collagen_vascular_diseases": "ck_elixhauserci_ra_cvd", # noqa 

94} 

95 

96 

97class ElixhauserCIMetaclass(DeclarativeMeta): 

98 # noinspection PyInitNewSignature 

99 def __init__( 

100 cls: Type["ElixhauserCI"], 

101 name: str, 

102 bases: Tuple[Type, ...], 

103 classdict: Dict[str, Any], 

104 ) -> None: 

105 for colname in FIELDNAMES: 

106 constraint_name = CONSTRAINT_NAME_MAP.get(colname) 

107 setattr( 

108 cls, 

109 colname, 

110 BoolColumn( 

111 colname, 

112 comment="Disease present (0 no, 1 yes)", 

113 constraint_name=constraint_name, 

114 ), 

115 ) 

116 super().__init__(name, bases, classdict) 

117 

118 

119class ElixhauserCI( 

120 TaskHasPatientMixin, 

121 TaskHasClinicianMixin, 

122 Task, 

123 metaclass=ElixhauserCIMetaclass, 

124): 

125 __tablename__ = "elixhauserci" 

126 shortname = "ElixhauserCI" 

127 

128 @staticmethod 

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

130 _ = req.gettext 

131 return _("Elixhauser Comorbidity Index") 

132 

133 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]: 

134 return self.standard_task_summary_fields() + [ 

135 SummaryElement( 

136 name="total", 

137 coltype=Integer(), 

138 value=self.total_score(), 

139 comment=f"Total score (out of {MAX_SCORE})", 

140 ) 

141 ] 

142 

143 def is_complete(self) -> bool: 

144 return self.all_fields_not_none(FIELDNAMES) 

145 

146 def total_score(self) -> int: 

147 return self.count_booleans(FIELDNAMES) 

148 

149 def get_task_html(self, req: CamcopsRequest) -> str: 

150 score = self.total_score() 

151 q_a = "" 

152 for f in FIELDNAMES: 

153 v = getattr(self, f) 

154 q_a += tr_qa(self.wxstring(req, f), get_yes_no_unknown(req, v)) 

155 return f""" 

156 <div class="{CssClass.SUMMARY}"> 

157 <table class="{CssClass.SUMMARY}"> 

158 {self.get_is_complete_tr(req)} 

159 <tr> 

160 <td>{req.sstring(SS.TOTAL_SCORE)}</td> 

161 <td><b>{score}</b> / {MAX_SCORE}</td> 

162 </tr> 

163 </table> 

164 </div> 

165 <table class="{CssClass.TASKDETAIL}"> 

166 <tr> 

167 <th width="50%">Question</th> 

168 <th width="50%">Answer</th> 

169 </tr> 

170 {q_a} 

171 </table> 

172 """