Coverage for tasks/distressthermometer.py: 49%

53 statements  

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

1""" 

2camcops_server/tasks/distressthermometer.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""" 

27 

28from typing import Any, List, Optional, Type 

29 

30from cardinal_pythonlib.stringfunc import strseq 

31from sqlalchemy.orm import Mapped, mapped_column 

32from sqlalchemy.sql.sqltypes import UnicodeText 

33 

34from camcops_server.cc_modules.cc_constants import CssClass, PV 

35from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo 

36from camcops_server.cc_modules.cc_db import add_multiple_columns 

37from camcops_server.cc_modules.cc_html import ( 

38 get_yes_no_none, 

39 subheading_spanning_two_columns, 

40 tr_qa, 

41) 

42from camcops_server.cc_modules.cc_request import CamcopsRequest 

43from camcops_server.cc_modules.cc_sqla_coltypes import ( 

44 mapped_camcops_column, 

45 PermittedValueChecker, 

46) 

47from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin 

48 

49 

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

51# Distress Thermometer 

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

53 

54 

55class DistressThermometer( # type: ignore[misc] 

56 TaskHasPatientMixin, 

57 Task, 

58): 

59 """ 

60 Server implementation of the DistressThermometer task. 

61 """ 

62 

63 __tablename__ = "distressthermometer" 

64 shortname = "Distress Thermometer" 

65 

66 @classmethod 

67 def extend_columns( 

68 cls: Type["DistressThermometer"], **kwargs: Any 

69 ) -> None: 

70 add_multiple_columns( 

71 cls, 

72 "q", 

73 1, 

74 cls.NQUESTIONS, 

75 pv=PV.BIT, 

76 comment_fmt="{n}. {s} (0 no, 1 yes)", 

77 comment_strings=[ 

78 "child care", 

79 "housing", 

80 "insurance/financial", 

81 "transportation", 

82 "work/school", 

83 "children", 

84 "partner", 

85 "close friend/relative", 

86 "depression", 

87 "fears", 

88 "nervousness", 

89 "sadness", 

90 "worry", 

91 "loss of interest", 

92 "spiritual/religious", 

93 "appearance", 

94 "bathing/dressing", 

95 "breathing", 

96 "urination", 

97 "constipation", 

98 "diarrhoea", 

99 "eating", 

100 "fatigue", 

101 "feeling swollen", 

102 "fevers", 

103 "getting around", 

104 "indigestion", 

105 "memory/concentration", 

106 "mouth sores", 

107 "nausea", 

108 "nose dry/congested", 

109 "pain", 

110 "sexual", 

111 "skin dry/itchy", 

112 "sleep", 

113 "tingling in hands/feet", 

114 ], 

115 ) 

116 

117 distress: Mapped[Optional[int]] = mapped_camcops_column( 

118 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=10), 

119 comment="Distress (0 none - 10 extreme)", 

120 ) 

121 other: Mapped[Optional[str]] = mapped_column( 

122 UnicodeText, comment="Other problems" 

123 ) 

124 

125 NQUESTIONS = 36 

126 COMPLETENESS_FIELDS = strseq("q", 1, NQUESTIONS) + ["distress"] 

127 

128 @staticmethod 

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

130 _ = req.gettext 

131 return _("Distress Thermometer") 

132 

133 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]: 

134 if self.distress is None: 

135 return CTV_INCOMPLETE 

136 return [CtvInfo(content=f"Overall distress: {self.distress}/10")] 

137 

138 def is_complete(self) -> bool: 

139 return ( 

140 self.all_fields_not_none(self.COMPLETENESS_FIELDS) 

141 and self.field_contents_valid() 

142 ) 

143 

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

145 h = f""" 

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

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

148 {self.get_is_complete_tr(req)} 

149 {tr_qa("Overall distress (0–10)", self.distress)} 

150 </table> 

151 </div> 

152 <div class="{CssClass.EXPLANATION}"> 

153 All questions relate to distress/problems “in the past week, 

154 including today” (yes = problem, no = no problem). 

155 </div> 

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

157 <tr> 

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

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

160 </tr> 

161 """ 

162 h += tr_qa( 

163 "Distress (0 no distress – 10 extreme distress)", self.distress 

164 ) 

165 h += subheading_spanning_two_columns("Practical problems") 

166 for i in range(1, 5 + 1): 

167 h += tr_qa( 

168 f"{i}. {self.wxstring(req, 'q' + str(i))}", 

169 get_yes_no_none(req, getattr(self, "q" + str(i))), 

170 ) 

171 h += subheading_spanning_two_columns("Family problems") 

172 for i in range(6, 8 + 1): 

173 h += tr_qa( 

174 f"{i}. {self.wxstring(req, 'q' + str(i))}", 

175 get_yes_no_none(req, getattr(self, "q" + str(i))), 

176 ) 

177 h += subheading_spanning_two_columns("Emotional problems") 

178 for i in range(9, 14 + 1): 

179 h += tr_qa( 

180 f"{i}. {self.wxstring(req, 'q' + str(i))}", 

181 get_yes_no_none(req, getattr(self, "q" + str(i))), 

182 ) 

183 h += subheading_spanning_two_columns("Spiritual problems") 

184 for i in range(15, 15 + 1): 

185 h += tr_qa( 

186 f"{i}. {self.wxstring(req, 'q' + str(i))}", 

187 get_yes_no_none(req, getattr(self, "q" + str(i))), 

188 ) 

189 h += subheading_spanning_two_columns("Physical problems") 

190 for i in range(16, self.NQUESTIONS + 1): 

191 h += tr_qa( 

192 f"{i}. {self.wxstring(req, 'q' + str(i))}", 

193 get_yes_no_none(req, getattr(self, "q" + str(i))), 

194 ) 

195 h += subheading_spanning_two_columns("Other problems") 

196 h += tr_qa(self.wxstring(req, "other_s"), self.other) 

197 h += """ 

198 </table> 

199 """ 

200 return h