Coverage for tasks/demoquestionnaire.py: 49%

138 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/demoquestionnaire.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""" 

29 

30from typing import Any, Dict, Optional, Tuple, Type 

31 

32from sqlalchemy.ext.declarative import DeclarativeMeta 

33from sqlalchemy.sql.schema import Column 

34from sqlalchemy.sql.sqltypes import Date, Float, Integer, Time, UnicodeText 

35 

36from camcops_server.cc_modules.cc_blob import Blob, blob_relationship 

37from camcops_server.cc_modules.cc_constants import CssClass 

38from camcops_server.cc_modules.cc_db import add_multiple_columns 

39from camcops_server.cc_modules.cc_html import answer 

40from camcops_server.cc_modules.cc_request import CamcopsRequest 

41from camcops_server.cc_modules.cc_sqla_coltypes import ( 

42 CamcopsColumn, 

43 PendulumDateTimeAsIsoTextColType, 

44 DiagnosticCodeColType, 

45) 

46from camcops_server.cc_modules.cc_task import Task 

47 

48 

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

50# TASKS: DEMO QUESTIONNAIRE 

51# ============================================================================= 

52 

53N_MCQ = 10 # 8 in v1; 10 in v2 

54N_MCQBOOL = 3 

55N_MULTIPLERESPONSE = 6 

56N_BOOLTEXT = 22 

57N_BOOLIMAGE = 2 

58N_PICKER = 2 

59N_SLIDER = 2 

60 

61 

62def divtest(divname: str) -> str: 

63 return f'<div class="{divname}">.{divname}</div>\n' 

64 

65 

66class DemoQuestionnaireMetaclass(DeclarativeMeta): 

67 # noinspection PyInitNewSignature 

68 def __init__( 

69 cls: Type["DemoQuestionnaire"], 

70 name: str, 

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

72 classdict: Dict[str, Any], 

73 ) -> None: 

74 add_multiple_columns(cls, "mcq", 1, N_MCQ) 

75 add_multiple_columns(cls, "mcqbool", 1, N_MCQBOOL) 

76 add_multiple_columns(cls, "multipleresponse", 1, N_MULTIPLERESPONSE) 

77 add_multiple_columns(cls, "booltext", 1, N_BOOLTEXT) 

78 add_multiple_columns(cls, "boolimage", 1, N_BOOLIMAGE) 

79 add_multiple_columns(cls, "picker", 1, N_PICKER) 

80 add_multiple_columns(cls, "slider", 1, N_SLIDER, Float) 

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

82 

83 

84class DemoQuestionnaire(Task, metaclass=DemoQuestionnaireMetaclass): 

85 """ 

86 Server implementation of the demo questionnaire task. 

87 """ 

88 

89 __tablename__ = "demoquestionnaire" 

90 shortname = "Demo" 

91 is_anonymous = True 

92 

93 mcqtext_1a = Column("mcqtext_1a", UnicodeText) 

94 mcqtext_1b = Column("mcqtext_1b", UnicodeText) 

95 mcqtext_2a = Column("mcqtext_2a", UnicodeText) 

96 mcqtext_2b = Column("mcqtext_2b", UnicodeText) 

97 mcqtext_3a = Column("mcqtext_3a", UnicodeText) 

98 mcqtext_3b = Column("mcqtext_3b", UnicodeText) 

99 typedvar_text = Column("typedvar_text", UnicodeText) 

100 typedvar_text_multiline = Column("typedvar_text_multiline", UnicodeText) 

101 typedvar_text_rich = Column("typedvar_text_rich", UnicodeText) # v2 

102 typedvar_int = Column("typedvar_int", Integer) 

103 typedvar_real = Column("typedvar_real", Float) 

104 date_only = Column("date_only", Date) 

105 date_time = Column("date_time", PendulumDateTimeAsIsoTextColType) 

106 thermometer = Column("thermometer", Integer) 

107 diagnosticcode_code = Column("diagnosticcode_code", DiagnosticCodeColType) 

108 diagnosticcode_description = CamcopsColumn( 

109 "diagnosticcode_description", 

110 UnicodeText, 

111 exempt_from_anonymisation=True, 

112 ) 

113 diagnosticcode2_code = Column( 

114 "diagnosticcode2_code", DiagnosticCodeColType 

115 ) # v2 

116 diagnosticcode2_description = CamcopsColumn( 

117 "diagnosticcode2_description", 

118 UnicodeText, 

119 exempt_from_anonymisation=True, 

120 ) # v2 

121 photo_blobid = CamcopsColumn( 

122 "photo_blobid", 

123 Integer, 

124 is_blob_id_field=True, 

125 blob_relationship_attr_name="photo", 

126 ) 

127 # IGNORED. REMOVE WHEN ALL PRE-2.0.0 TABLETS GONE: 

128 photo_rotation = Column( 

129 "photo_rotation", Integer 

130 ) # DEFUNCT as of v2.0.0 # noqa 

131 canvas_blobid = CamcopsColumn( 

132 "canvas_blobid", 

133 Integer, 

134 is_blob_id_field=True, 

135 blob_relationship_attr_name="canvas", 

136 ) 

137 canvas2_blobid = CamcopsColumn( 

138 "canvas2_blobid", 

139 Integer, 

140 is_blob_id_field=True, 

141 blob_relationship_attr_name="canvas2", 

142 ) 

143 spinbox_int = Column("spinbox_int", Integer) # v2 

144 spinbox_real = Column("spinbox_real", Float) # v2 

145 time_only = Column("time_only", Time) # v2 

146 

147 photo = blob_relationship( 

148 "DemoQuestionnaire", "photo_blobid" 

149 ) # type: Optional[Blob] # noqa 

150 canvas = blob_relationship( 

151 "DemoQuestionnaire", "canvas_blobid" 

152 ) # type: Optional[Blob] # noqa 

153 canvas2 = blob_relationship( 

154 "DemoQuestionnaire", "canvas2_blobid" 

155 ) # type: Optional[Blob] # noqa 

156 

157 @staticmethod 

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

159 _ = req.gettext 

160 return _("Demonstration Questionnaire") 

161 

162 # noinspection PyMethodOverriding 

163 @staticmethod 

164 def is_complete() -> bool: 

165 return True 

166 

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

168 h = f""" 

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

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

171 {self.get_is_complete_tr(req)} 

172 </table> 

173 </div> 

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

175 This is a demo questionnaire, containing no genuine 

176 information. 

177 </div> 

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

179 <tr> 

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

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

182 </tr> 

183 """ 

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

185 h += self.get_twocol_val_row("mcq" + str(i)) 

186 for i in range(1, N_MCQBOOL + 1): 

187 h += self.get_twocol_bool_row(req, "mcqbool" + str(i)) 

188 for i in range(1, N_MULTIPLERESPONSE + 1): 

189 h += self.get_twocol_bool_row(req, "multipleresponse" + str(i)) 

190 for i in range(1, N_BOOLTEXT + 1): 

191 h += self.get_twocol_bool_row(req, "booltext" + str(i)) 

192 for i in range(1, N_BOOLIMAGE + 1): 

193 h += self.get_twocol_bool_row(req, "boolimage" + str(i)) 

194 for i in range(1, N_PICKER + 1): 

195 h += self.get_twocol_val_row("picker" + str(i)) 

196 for i in range(1, N_SLIDER + 1): 

197 h += self.get_twocol_val_row("slider" + str(i)) 

198 h += self.get_twocol_string_row("mcqtext_1a") 

199 h += self.get_twocol_string_row("mcqtext_1b") 

200 h += self.get_twocol_string_row("mcqtext_2a") 

201 h += self.get_twocol_string_row("mcqtext_2b") 

202 h += self.get_twocol_string_row("mcqtext_3a") 

203 h += self.get_twocol_string_row("mcqtext_3b") 

204 h += self.get_twocol_string_row("typedvar_text") 

205 h += self.get_twocol_string_row("typedvar_text_multiline") 

206 h += self.get_twocol_string_row("typedvar_text_rich") 

207 h += self.get_twocol_val_row("typedvar_int") 

208 h += self.get_twocol_val_row("typedvar_real") 

209 h += self.get_twocol_val_row("date_only") 

210 h += self.get_twocol_val_row("date_time") 

211 h += self.get_twocol_val_row("thermometer") 

212 h += self.get_twocol_string_row("diagnosticcode_code") 

213 h += self.get_twocol_string_row("diagnosticcode_description") 

214 h += self.get_twocol_string_row("diagnosticcode2_code") 

215 h += self.get_twocol_string_row("diagnosticcode2_description") 

216 # noinspection PyTypeChecker 

217 h += self.get_twocol_picture_row(self.photo, "photo") 

218 # noinspection PyTypeChecker 

219 h += self.get_twocol_picture_row(self.canvas, "canvas") 

220 # noinspection PyTypeChecker 

221 h += self.get_twocol_picture_row(self.canvas2, "canvas2") 

222 h += ( 

223 """ 

224 </table> 

225 

226 <div> 

227 In addition to the data (above), this task demonstrates 

228 HTML/CSS styles used in the CamCOPS views. 

229 </div> 

230 

231 <h1>Header 1</h1> 

232 <h2>Header 2</h2> 

233 <h3>Header 3</h3> 

234 

235 <div> 

236 Plain div with <sup>superscript</sup> and <sub>subscript</sub>. 

237 <br> 

238 Answers look like this: """ 

239 + answer("Answer") 

240 + """<br> 

241 Missing answers look liks this: """ 

242 + answer(None) 

243 + """<br> 

244 </div> 

245 """ 

246 ) 

247 h += divtest(CssClass.BAD_ID_POLICY_MILD) 

248 h += divtest(CssClass.BAD_ID_POLICY_SEVERE) 

249 h += divtest(CssClass.CLINICIAN) 

250 h += divtest(CssClass.COPYRIGHT) 

251 h += divtest(CssClass.ERROR) 

252 h += divtest(CssClass.EXPLANATION) 

253 h += divtest(CssClass.FOOTNOTES) 

254 h += divtest(CssClass.FORMTITLE) 

255 h += divtest(CssClass.GREEN) 

256 h += divtest(CssClass.HEADING) 

257 h += divtest(CssClass.IMPORTANT) 

258 h += divtest(CssClass.INCOMPLETE) 

259 h += divtest(CssClass.INDENTED) 

260 h += divtest(CssClass.LIVE_ON_TABLET) 

261 h += divtest(CssClass.NAVIGATION) 

262 h += divtest(CssClass.OFFICE) 

263 h += divtest(CssClass.PATIENT) 

264 h += divtest(CssClass.RESPONDENT) 

265 h += divtest(CssClass.SMALLPRINT) 

266 h += divtest(CssClass.SPECIALNOTE) 

267 h += divtest(CssClass.SUBHEADING) 

268 h += divtest(CssClass.SUBSUBHEADING) 

269 h += divtest(CssClass.SUMMARY) 

270 h += divtest(CssClass.SUPERUSER) 

271 h += divtest(CssClass.TASKHEADER) 

272 h += divtest(CssClass.TRACKERHEADER) 

273 h += divtest(CssClass.TRACKER_ALL_CONSISTENT) 

274 h += divtest(CssClass.WARNING) 

275 h += """ 

276 <table> 

277 <tr> 

278 <th>Standard table heading; column 1</th><th>Column 2</th> 

279 </tr> 

280 <tr> 

281 <td>Standard table row; column 1</td><td>Column 2</td> 

282 </tr> 

283 </table> 

284 

285 <div>Inlined <code>code looks like this</code>. 

286 

287 <div>There are some others, too.</div> 

288 """ 

289 return h