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/icd10schizotypal.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.datetimefunc import format_datetime 

32import cardinal_pythonlib.rnc_web as ws 

33from cardinal_pythonlib.stringfunc import strseq 

34from sqlalchemy.ext.declarative import DeclarativeMeta 

35from sqlalchemy.sql.schema import Column 

36from sqlalchemy.sql.sqltypes import Boolean, Date, UnicodeText 

37 

38from camcops_server.cc_modules.cc_constants import ( 

39 CssClass, 

40 DateFormat, 

41 ICD10_COPYRIGHT_DIV, 

42 PV, 

43) 

44from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo 

45from camcops_server.cc_modules.cc_db import add_multiple_columns 

46from camcops_server.cc_modules.cc_html import ( 

47 get_yes_no_none, 

48 td, 

49 tr, 

50 tr_qa, 

51) 

52from camcops_server.cc_modules.cc_request import CamcopsRequest 

53from camcops_server.cc_modules.cc_sqla_coltypes import ( 

54 BIT_CHECKER, 

55 CamcopsColumn, 

56) 

57from camcops_server.cc_modules.cc_string import AS 

58from camcops_server.cc_modules.cc_summaryelement import SummaryElement 

59from camcops_server.cc_modules.cc_task import ( 

60 Task, 

61 TaskHasClinicianMixin, 

62 TaskHasPatientMixin, 

63) 

64from camcops_server.cc_modules.cc_text import SS 

65 

66 

67# ============================================================================= 

68# Icd10Schizotypal 

69# ============================================================================= 

70 

71class Icd10SchizotypalMetaclass(DeclarativeMeta): 

72 # noinspection PyInitNewSignature 

73 def __init__(cls: Type['Icd10Schizotypal'], 

74 name: str, 

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

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

77 add_multiple_columns( 

78 cls, "a", 1, cls.N_A, Boolean, 

79 pv=PV.BIT, 

80 comment_fmt="Criterion A({n}), {s}", 

81 comment_strings=[ 

82 "inappropriate/constricted affect", 

83 "odd/eccentric/peculiar", 

84 "poor rapport/social withdrawal", 

85 "odd beliefs/magical thinking", 

86 "suspiciousness/paranoid ideas", 

87 "ruminations without inner resistance", 

88 "unusual perceptual experiences", 

89 "vague/circumstantial/metaphorical/over-elaborate/stereotyped " 

90 "thinking", 

91 "occasional transient quasi-psychotic episodes", 

92 ] 

93 ) 

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

95 

96 

97class Icd10Schizotypal(TaskHasClinicianMixin, TaskHasPatientMixin, Task, 

98 metaclass=Icd10SchizotypalMetaclass): 

99 """ 

100 Server implementation of the ICD10-SZTYP task. 

101 """ 

102 __tablename__ = "icd10schizotypal" 

103 shortname = "ICD10-SZTYP" 

104 

105 date_pertains_to = Column( 

106 "date_pertains_to", Date, 

107 comment="Date the assessment pertains to" 

108 ) 

109 comments = Column( 

110 "comments", UnicodeText, 

111 comment="Clinician's comments" 

112 ) 

113 b = CamcopsColumn( 

114 "b", Boolean, 

115 permitted_value_checker=BIT_CHECKER, 

116 comment="Criterion (B). True if: the subject has never met " 

117 "the criteria for any disorder in F20 (Schizophrenia)." 

118 ) 

119 

120 N_A = 9 

121 A_FIELDS = strseq("a", 1, N_A) 

122 

123 @staticmethod 

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

125 _ = req.gettext 

126 return "ICD-10 criteria for schizotypal disorder (F21)" 

127 

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

129 if not self.is_complete(): 

130 return CTV_INCOMPLETE 

131 c = self.meets_criteria() 

132 if c is None: 

133 category = "Unknown if met or not met" 

134 elif c: 

135 category = "Met" 

136 else: 

137 category = "Not met" 

138 infolist = [CtvInfo( 

139 content=( 

140 "Pertains to: {}. Criteria for schizotypal " 

141 "disorder: {}.".format( 

142 format_datetime(self.date_pertains_to, 

143 DateFormat.LONG_DATE), 

144 category 

145 ) 

146 ) 

147 )] 

148 if self.comments: 

149 infolist.append(CtvInfo(content=ws.webify(self.comments))) 

150 return infolist 

151 

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

153 return self.standard_task_summary_fields() + [ 

154 SummaryElement(name="meets_criteria", coltype=Boolean(), 

155 value=self.meets_criteria(), 

156 comment="Meets criteria for schizotypal disorder?"), 

157 ] 

158 

159 # Meets criteria? These also return null for unknown. 

160 def meets_criteria(self) -> Optional[bool]: 

161 if not self.is_complete(): 

162 return None 

163 return self.count_booleans(self.A_FIELDS) >= 4 and self.b 

164 

165 def is_complete(self) -> bool: 

166 return ( 

167 self.date_pertains_to is not None and 

168 self.all_fields_not_none(self.A_FIELDS) and 

169 self.b is not None and 

170 self.field_contents_valid() 

171 ) 

172 

173 def text_row(self, req: CamcopsRequest, wstringname: str) -> str: 

174 return tr(td(self.wxstring(req, wstringname)), 

175 td("", td_class=CssClass.SUBHEADING), 

176 literal=True) 

177 

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

179 q_a = self.text_row(req, "a") 

180 for i in range(1, self.N_A + 1): 

181 q_a += self.get_twocol_bool_row_true_false( 

182 req, "a" + str(i), self.wxstring(req, "a" + str(i))) 

183 q_a += self.get_twocol_bool_row_true_false( 

184 req, "b", self.wxstring(req, "b")) 

185 h = """ 

186 {clinician_comments} 

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

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

189 {tr_is_complete} 

190 {date_pertains_to} 

191 {meets_criteria} 

192 </table> 

193 </div> 

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

195 <tr> 

196 <th width="80%">Question</th> 

197 <th width="20%">Answer</th> 

198 </tr> 

199 {q_a} 

200 </table> 

201 {ICD10_COPYRIGHT_DIV} 

202 """.format( 

203 clinician_comments=self.get_standard_clinician_comments_block( 

204 req, self.comments), 

205 CssClass=CssClass, 

206 tr_is_complete=self.get_is_complete_tr(req), 

207 date_pertains_to=tr_qa( 

208 req.wappstring(AS.DATE_PERTAINS_TO), 

209 format_datetime(self.date_pertains_to, 

210 DateFormat.LONG_DATE, default=None) 

211 ), 

212 meets_criteria=tr_qa( 

213 req.sstring(SS.MEETS_CRITERIA), 

214 get_yes_no_none(req, self.meets_criteria()) 

215 ), 

216 q_a=q_a, 

217 ICD10_COPYRIGHT_DIV=ICD10_COPYRIGHT_DIV, 

218 ) 

219 return h