Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/cgisch.py 

5 

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

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27""" 

28 

29from typing import Any, Dict, List, Optional, Tuple, Type 

30 

31from cardinal_pythonlib.stringfunc import strseq 

32from sqlalchemy.ext.declarative import DeclarativeMeta 

33 

34from camcops_server.cc_modules.cc_constants import CssClass 

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 subheading_spanning_two_columns, 

39 tr_qa, 

40 tr_span_col, 

41) 

42from camcops_server.cc_modules.cc_request import CamcopsRequest 

43from camcops_server.cc_modules.cc_summaryelement import SummaryElement 

44from camcops_server.cc_modules.cc_task import ( 

45 get_from_dict, 

46 Task, 

47 TaskHasClinicianMixin, 

48 TaskHasPatientMixin, 

49) 

50from camcops_server.cc_modules.cc_trackerhelpers import TrackerInfo 

51 

52 

53# ============================================================================= 

54# CGI-SCH 

55# ============================================================================= 

56 

57QUESTION_FRAGMENTS = ["positive", "negative", "depressive", "cognitive", 

58 "overall"] 

59 

60 

61class CgiSchMetaclass(DeclarativeMeta): 

62 """ 

63 Metaclass for :class:`CgiSch`. 

64 """ 

65 # noinspection PyInitNewSignature 

66 def __init__(cls: Type['CgiSch'], 

67 name: str, 

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

69 classdict: Dict[str, Any]) -> None: 

70 add_multiple_columns( 

71 cls, "severity", 1, 5, 

72 minimum=1, maximum=7, 

73 comment_fmt="Severity Q{n}, {s} (1-7, higher worse)", 

74 comment_strings=QUESTION_FRAGMENTS 

75 ) 

76 add_multiple_columns( 

77 cls, "change", 1, 5, 

78 pv=list(range(1, 7 + 1)) + [9], 

79 comment_fmt="Change Q{n}, {s} (1-7, higher worse, or 9 N/A)", 

80 comment_strings=QUESTION_FRAGMENTS 

81 ) 

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

83 

84 

85class CgiSch(TaskHasPatientMixin, TaskHasClinicianMixin, Task, 

86 metaclass=CgiSchMetaclass): 

87 """ 

88 Server implementation of the CGI-SCH task. 

89 """ 

90 __tablename__ = "cgisch" 

91 shortname = "CGI-SCH" 

92 provides_trackers = True 

93 

94 TASK_FIELDS = strseq("severity", 1, 5) + strseq("change", 1, 5) 

95 

96 @staticmethod 

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

98 _ = req.gettext 

99 return _("Clinical Global Impression – Schizophrenia") 

100 

101 # noinspection PyUnresolvedReferences 

102 def get_trackers(self, req: CamcopsRequest) -> List[TrackerInfo]: 

103 prefix = "CGI-SCH severity: " 

104 ylabel = "Score (1-7)" 

105 return [ 

106 TrackerInfo( 

107 value=self.severity1, 

108 plot_label=prefix + "positive symptoms", 

109 axis_label=ylabel, 

110 axis_min=0.5, 

111 axis_max=7.5 

112 ), 

113 TrackerInfo( 

114 value=self.severity2, 

115 plot_label=prefix + "negative symptoms", 

116 axis_label=ylabel, 

117 axis_min=0.5, 

118 axis_max=7.5 

119 ), 

120 TrackerInfo( 

121 value=self.severity3, 

122 plot_label=prefix + "depressive symptoms", 

123 axis_label=ylabel, 

124 axis_min=0.5, 

125 axis_max=7.5 

126 ), 

127 TrackerInfo( 

128 value=self.severity4, 

129 plot_label=prefix + "cognitive symptoms", 

130 axis_label=ylabel, 

131 axis_min=0.5, 

132 axis_max=7.5 

133 ), 

134 TrackerInfo( 

135 value=self.severity5, 

136 plot_label=prefix + "overall severity", 

137 axis_label=ylabel, 

138 axis_min=0.5, 

139 axis_max=7.5 

140 ), 

141 ] 

142 

143 # noinspection PyUnresolvedReferences 

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

145 if not self.is_complete(): 

146 return CTV_INCOMPLETE 

147 return [CtvInfo( 

148 content=( 

149 f"CGI-SCH. Severity: positive {self.severity1}, " 

150 f"negative {self.severity2}, depressive {self.severity3}, " 

151 f"cognitive {self.severity4}, overall {self.severity5}. " 

152 f"Change: positive {self.change1}, negative {self.change2}, " 

153 f"depressive {self.change3}, cognitive {self.change4}, " 

154 f"overall {self.change5}." 

155 ) 

156 )] 

157 

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

159 # pylint: disable=unused-argument 

160 return self.standard_task_summary_fields() 

161 

162 def is_complete(self) -> bool: 

163 return ( 

164 self.all_fields_not_none(self.TASK_FIELDS) and 

165 self.field_contents_valid() 

166 ) 

167 

168 # noinspection PyUnresolvedReferences 

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

170 severity_dict = { 

171 None: None, 

172 1: self.wxstring(req, "i_option1"), 

173 2: self.wxstring(req, "i_option2"), 

174 3: self.wxstring(req, "i_option3"), 

175 4: self.wxstring(req, "i_option4"), 

176 5: self.wxstring(req, "i_option5"), 

177 6: self.wxstring(req, "i_option6"), 

178 7: self.wxstring(req, "i_option7"), 

179 } 

180 change_dict = { 

181 None: None, 

182 1: self.wxstring(req, "ii_option1"), 

183 2: self.wxstring(req, "ii_option2"), 

184 3: self.wxstring(req, "ii_option3"), 

185 4: self.wxstring(req, "ii_option4"), 

186 5: self.wxstring(req, "ii_option5"), 

187 6: self.wxstring(req, "ii_option6"), 

188 7: self.wxstring(req, "ii_option7"), 

189 9: self.wxstring(req, "ii_option9"), 

190 } 

191 

192 def tr_severity(xstring_name: str, value: Optional[int]) -> str: 

193 return tr_qa(self.wxstring(req, xstring_name), 

194 get_from_dict(severity_dict, value)) 

195 

196 def tr_change(xstring_name: str, value: Optional[int]) -> str: 

197 return tr_qa(self.wxstring(req, xstring_name), 

198 get_from_dict(change_dict, value)) 

199 

200 return f""" 

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

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

203 {self.get_is_complete_tr(req)} 

204 </table> 

205 </div> 

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

207 <tr> 

208 <th width="70%">Question</th> 

209 <th width="30%">Answer <sup>[1]</sup></th> 

210 </tr> 

211 {subheading_spanning_two_columns(self.wxstring(req, "i_title"))} 

212 {tr_span_col(self.wxstring(req, "i_question"), cols=2)} 

213 {tr_severity("q1", self.severity1)} 

214 {tr_severity("q2", self.severity2)} 

215 {tr_severity("q3", self.severity3)} 

216 {tr_severity("q4", self.severity4)} 

217 {tr_severity("q5", self.severity5)} 

218  

219 {subheading_spanning_two_columns(self.wxstring(req, "ii_title"))} 

220 {tr_span_col(self.wxstring(req, "ii_question"), cols=2)} 

221 {tr_change("q1", self.change1)} 

222 {tr_change("q2", self.change2)} 

223 {tr_change("q3", self.change3)} 

224 {tr_change("q4", self.change4)} 

225 {tr_change("q5", self.change5)} 

226 </table> 

227 <div class="{CssClass.FOOTNOTES}"> 

228 [1] All questions are scored 1–7, or 9 (not applicable, for 

229 change questions). 

230 {self.wxstring(req, "ii_postscript")} 

231 </div> 

232 

233 """ # noqa