Coverage for tasks/demoquestionnaire.py: 49%
138 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/tasks/demoquestionnaire.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
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.
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.
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/>.
26===============================================================================
28"""
30from typing import Any, Dict, Optional, Tuple, Type
32from sqlalchemy.ext.declarative import DeclarativeMeta
33from sqlalchemy.sql.schema import Column
34from sqlalchemy.sql.sqltypes import Date, Float, Integer, Time, UnicodeText
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
49# =============================================================================
50# TASKS: DEMO QUESTIONNAIRE
51# =============================================================================
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
62def divtest(divname: str) -> str:
63 return f'<div class="{divname}">.{divname}</div>\n'
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)
84class DemoQuestionnaire(Task, metaclass=DemoQuestionnaireMetaclass):
85 """
86 Server implementation of the demo questionnaire task.
87 """
89 __tablename__ = "demoquestionnaire"
90 shortname = "Demo"
91 is_anonymous = True
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
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
157 @staticmethod
158 def longname(req: "CamcopsRequest") -> str:
159 _ = req.gettext
160 return _("Demonstration Questionnaire")
162 # noinspection PyMethodOverriding
163 @staticmethod
164 def is_complete() -> bool:
165 return True
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>
226 <div>
227 In addition to the data (above), this task demonstrates
228 HTML/CSS styles used in the CamCOPS views.
229 </div>
231 <h1>Header 1</h1>
232 <h2>Header 2</h2>
233 <h3>Header 3</h3>
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>
285 <div>Inlined <code>code looks like this</code>.
287 <div>There are some others, too.</div>
288 """
289 return h