Coverage for tasks/demoquestionnaire.py : 49%

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
3"""
4camcops_server/tasks/demoquestionnaire.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
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.
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.
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/>.
25===============================================================================
27"""
29from typing import Any, Dict, Optional, Tuple, Type
31from sqlalchemy.ext.declarative import DeclarativeMeta
32from sqlalchemy.sql.schema import Column
33from sqlalchemy.sql.sqltypes import Date, Float, Integer, Time, UnicodeText
35from camcops_server.cc_modules.cc_blob import Blob, blob_relationship
36from camcops_server.cc_modules.cc_constants import CssClass
37from camcops_server.cc_modules.cc_db import add_multiple_columns
38from camcops_server.cc_modules.cc_html import answer
39from camcops_server.cc_modules.cc_request import CamcopsRequest
40from camcops_server.cc_modules.cc_sqla_coltypes import (
41 CamcopsColumn,
42 PendulumDateTimeAsIsoTextColType,
43 DiagnosticCodeColType,
44)
45from camcops_server.cc_modules.cc_task import Task
48# =============================================================================
49# TASKS: DEMO QUESTIONNAIRE
50# =============================================================================
52N_MCQ = 10 # 8 in v1; 10 in v2
53N_MCQBOOL = 3
54N_MULTIPLERESPONSE = 6
55N_BOOLTEXT = 22
56N_BOOLIMAGE = 2
57N_PICKER = 2
58N_SLIDER = 2
61def divtest(divname: str) -> str:
62 return f'<div class="{divname}">.{divname}</div>\n'
65class DemoQuestionnaireMetaclass(DeclarativeMeta):
66 # noinspection PyInitNewSignature
67 def __init__(cls: Type['DemoQuestionnaire'],
68 name: str,
69 bases: Tuple[Type, ...],
70 classdict: Dict[str, Any]) -> None:
71 add_multiple_columns(cls, "mcq", 1, N_MCQ)
72 add_multiple_columns(cls, "mcqbool", 1, N_MCQBOOL)
73 add_multiple_columns(cls, "multipleresponse", 1, N_MULTIPLERESPONSE)
74 add_multiple_columns(cls, "booltext", 1, N_BOOLTEXT)
75 add_multiple_columns(cls, "boolimage", 1, N_BOOLIMAGE)
76 add_multiple_columns(cls, "picker", 1, N_PICKER)
77 add_multiple_columns(cls, "slider", 1, N_SLIDER, Float)
78 super().__init__(name, bases, classdict)
81class DemoQuestionnaire(Task,
82 metaclass=DemoQuestionnaireMetaclass):
83 """
84 Server implementation of the demo questionnaire task.
85 """
86 __tablename__ = "demoquestionnaire"
87 shortname = "Demo"
88 is_anonymous = True
90 mcqtext_1a = Column("mcqtext_1a", UnicodeText)
91 mcqtext_1b = Column("mcqtext_1b", UnicodeText)
92 mcqtext_2a = Column("mcqtext_2a", UnicodeText)
93 mcqtext_2b = Column("mcqtext_2b", UnicodeText)
94 mcqtext_3a = Column("mcqtext_3a", UnicodeText)
95 mcqtext_3b = Column("mcqtext_3b", UnicodeText)
96 typedvar_text = Column("typedvar_text", UnicodeText)
97 typedvar_text_multiline = Column("typedvar_text_multiline", UnicodeText)
98 typedvar_text_rich = Column("typedvar_text_rich", UnicodeText) # v2
99 typedvar_int = Column("typedvar_int", Integer)
100 typedvar_real = Column("typedvar_real", Float)
101 date_only = Column("date_only", Date)
102 date_time = Column("date_time", PendulumDateTimeAsIsoTextColType)
103 thermometer = Column("thermometer", Integer)
104 diagnosticcode_code = Column("diagnosticcode_code", DiagnosticCodeColType)
105 diagnosticcode_description = CamcopsColumn(
106 "diagnosticcode_description", UnicodeText,
107 exempt_from_anonymisation=True
108 )
109 diagnosticcode2_code = Column(
110 "diagnosticcode2_code", DiagnosticCodeColType
111 ) # v2
112 diagnosticcode2_description = CamcopsColumn(
113 "diagnosticcode2_description", UnicodeText,
114 exempt_from_anonymisation=True
115 ) # v2
116 photo_blobid = CamcopsColumn(
117 "photo_blobid", Integer,
118 is_blob_id_field=True, blob_relationship_attr_name="photo"
119 )
120 # IGNORED. REMOVE WHEN ALL PRE-2.0.0 TABLETS GONE:
121 photo_rotation = Column("photo_rotation", Integer) # DEFUNCT as of v2.0.0 # noqa
122 canvas_blobid = CamcopsColumn(
123 "canvas_blobid", Integer,
124 is_blob_id_field=True, blob_relationship_attr_name="canvas"
125 )
126 canvas2_blobid = CamcopsColumn(
127 "canvas2_blobid", Integer,
128 is_blob_id_field=True, blob_relationship_attr_name="canvas2"
129 )
130 spinbox_int = Column("spinbox_int", Integer) # v2
131 spinbox_real = Column("spinbox_real", Float) # v2
132 time_only = Column("time_only", Time) # v2
134 photo = blob_relationship("DemoQuestionnaire", "photo_blobid") # type: Optional[Blob] # noqa
135 canvas = blob_relationship("DemoQuestionnaire", "canvas_blobid") # type: Optional[Blob] # noqa
136 canvas2 = blob_relationship("DemoQuestionnaire", "canvas2_blobid") # type: Optional[Blob] # noqa
138 @staticmethod
139 def longname(req: "CamcopsRequest") -> str:
140 _ = req.gettext
141 return _("Demonstration Questionnaire")
143 # noinspection PyMethodOverriding
144 @staticmethod
145 def is_complete() -> bool:
146 return True
148 def get_task_html(self, req: CamcopsRequest) -> str:
149 h = f"""
150 <div class="{CssClass.SUMMARY}">
151 <table class="{CssClass.SUMMARY}">
152 {self.get_is_complete_tr(req)}
153 </table>
154 </div>
155 <div class="{CssClass.EXPLANATION}">
156 This is a demo questionnaire, containing no genuine
157 information.
158 </div>
159 <table class="{CssClass.TASKDETAIL}">
160 <tr>
161 <th width="50%">Question</th>
162 <th width="50%">Answer</th>
163 </tr>
164 """
165 for i in range(1, N_MCQ + 1):
166 h += self.get_twocol_val_row("mcq" + str(i))
167 for i in range(1, N_MCQBOOL + 1):
168 h += self.get_twocol_bool_row(req, "mcqbool" + str(i))
169 for i in range(1, N_MULTIPLERESPONSE + 1):
170 h += self.get_twocol_bool_row(req, "multipleresponse" + str(i))
171 for i in range(1, N_BOOLTEXT + 1):
172 h += self.get_twocol_bool_row(req, "booltext" + str(i))
173 for i in range(1, N_BOOLIMAGE + 1):
174 h += self.get_twocol_bool_row(req, "boolimage" + str(i))
175 for i in range(1, N_PICKER + 1):
176 h += self.get_twocol_val_row("picker" + str(i))
177 for i in range(1, N_SLIDER + 1):
178 h += self.get_twocol_val_row("slider" + str(i))
179 h += self.get_twocol_string_row("mcqtext_1a")
180 h += self.get_twocol_string_row("mcqtext_1b")
181 h += self.get_twocol_string_row("mcqtext_2a")
182 h += self.get_twocol_string_row("mcqtext_2b")
183 h += self.get_twocol_string_row("mcqtext_3a")
184 h += self.get_twocol_string_row("mcqtext_3b")
185 h += self.get_twocol_string_row("typedvar_text")
186 h += self.get_twocol_string_row("typedvar_text_multiline")
187 h += self.get_twocol_string_row("typedvar_text_rich")
188 h += self.get_twocol_val_row("typedvar_int")
189 h += self.get_twocol_val_row("typedvar_real")
190 h += self.get_twocol_val_row("date_only")
191 h += self.get_twocol_val_row("date_time")
192 h += self.get_twocol_val_row("thermometer")
193 h += self.get_twocol_string_row("diagnosticcode_code")
194 h += self.get_twocol_string_row("diagnosticcode_description")
195 h += self.get_twocol_string_row("diagnosticcode2_code")
196 h += self.get_twocol_string_row("diagnosticcode2_description")
197 # noinspection PyTypeChecker
198 h += self.get_twocol_picture_row(self.photo, "photo")
199 # noinspection PyTypeChecker
200 h += self.get_twocol_picture_row(self.canvas, "canvas")
201 # noinspection PyTypeChecker
202 h += self.get_twocol_picture_row(self.canvas2, "canvas2")
203 h += """
204 </table>
206 <div>
207 In addition to the data (above), this task demonstrates
208 HTML/CSS styles used in the CamCOPS views.
209 </div>
211 <h1>Header 1</h1>
212 <h2>Header 2</h2>
213 <h3>Header 3</h3>
215 <div>
216 Plain div with <sup>superscript</sup> and <sub>subscript</sub>.
217 <br>
218 Answers look like this: """ + answer("Answer") + """<br>
219 Missing answers look liks this: """ + answer(None) + """<br>
220 </div>
221 """
222 h += divtest(CssClass.BAD_ID_POLICY_MILD)
223 h += divtest(CssClass.BAD_ID_POLICY_SEVERE)
224 h += divtest(CssClass.CLINICIAN)
225 h += divtest(CssClass.COPYRIGHT)
226 h += divtest(CssClass.ERROR)
227 h += divtest(CssClass.EXPLANATION)
228 h += divtest(CssClass.FOOTNOTES)
229 h += divtest(CssClass.FORMTITLE)
230 h += divtest(CssClass.GREEN)
231 h += divtest(CssClass.HEADING)
232 h += divtest(CssClass.IMPORTANT)
233 h += divtest(CssClass.INCOMPLETE)
234 h += divtest(CssClass.INDENTED)
235 h += divtest(CssClass.LIVE_ON_TABLET)
236 h += divtest(CssClass.NAVIGATION)
237 h += divtest(CssClass.OFFICE)
238 h += divtest(CssClass.PATIENT)
239 h += divtest(CssClass.RESPONDENT)
240 h += divtest(CssClass.SMALLPRINT)
241 h += divtest(CssClass.SPECIALNOTE)
242 h += divtest(CssClass.SUBHEADING)
243 h += divtest(CssClass.SUBSUBHEADING)
244 h += divtest(CssClass.SUMMARY)
245 h += divtest(CssClass.SUPERUSER)
246 h += divtest(CssClass.TASKHEADER)
247 h += divtest(CssClass.TRACKERHEADER)
248 h += divtest(CssClass.TRACKER_ALL_CONSISTENT)
249 h += divtest(CssClass.WARNING)
250 h += """
251 <table>
252 <tr>
253 <th>Standard table heading; column 1</th><th>Column 2</th>
254 </tr>
255 <tr>
256 <td>Standard table row; column 1</td><td>Column 2</td>
257 </tr>
258 </table>
260 <div>Inlined <code>code looks like this</code>.
262 <div>There are some others, too.</div>
263 """
264 return h