Coverage for tasks/elixhauserci.py: 70%

1"""

2 camcops_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

30 from typing import Any, List, Type

31

32 from sqlalchemy.sql.sqltypes import Integer

33

34 from camcops_server.cc_modules.cc_constants import CssClass

35 from camcops_server.cc_modules.cc_html import get_yes_no_unknown, tr_qa

36 from camcops_server.cc_modules.cc_request import CamcopsRequest

37 from camcops_server.cc_modules.cc_sqla_coltypes import bool_column

38 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

39 from camcops_server.cc_modules.cc_task import (

40 Task,

41 TaskHasClinicianMixin,

42 TaskHasPatientMixin,

43)

44 from camcops_server.cc_modules.cc_text import SS

45

46

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

48# ElixhauserCI

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

50

51 FIELDNAMES = [

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]

84 MAX_SCORE = len(FIELDNAMES)

85

86 CONSTRAINT_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

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