Coverage for tasks/khandaker_mojo_medicationtherapy.py : 62%

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/khandaker_mojo_medicationtherapy.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"""
30from typing import List, Optional, Type, TYPE_CHECKING
32from sqlalchemy.sql.sqltypes import Float, Integer, UnicodeText
34from camcops_server.cc_modules.cc_constants import CssClass
35from camcops_server.cc_modules.cc_db import (
36 ancillary_relationship,
37 GenericTabletRecordMixin,
38 TaskDescendant,
39)
40from camcops_server.cc_modules.cc_html import answer, tr_qa
41from camcops_server.cc_modules.cc_sqlalchemy import Base
42from camcops_server.cc_modules.cc_sqla_coltypes import (
43 CamcopsColumn,
44)
45from camcops_server.cc_modules.cc_task import (
46 Task,
47 TaskHasPatientMixin,
48)
50if TYPE_CHECKING:
51 from camcops_server.cc_modules.cc_request import CamcopsRequest
54class KhandakerMojoTableItem(GenericTabletRecordMixin, TaskDescendant, Base):
55 __abstract__ = True
57 def any_fields_none(self) -> bool:
58 for f in self.mandatory_fields():
59 if getattr(self, f) is None:
60 return True
61 return False
63 @classmethod
64 def mandatory_fields(cls) -> List[str]:
65 raise NotImplementedError
67 def get_response_option(self, req: "CamcopsRequest") -> Optional[str]:
68 # Reads "self.response" from derived class.
69 # noinspection PyUnresolvedReferences
70 response = self.response # type: Optional[int]
71 if response is None:
72 return None
73 return self.task_ancestor().xstring(req, f"response_{response}")
75 # -------------------------------------------------------------------------
76 # TaskDescendant overrides
77 # -------------------------------------------------------------------------
79 @classmethod
80 def task_ancestor_class(cls) -> Optional[Type["Task"]]:
81 return KhandakerMojoMedicationTherapy
83 def task_ancestor(self) -> Optional["KhandakerMojoMedicationTherapy"]:
84 # Reads "self.medicationtable_id" from derived class.
85 # noinspection PyUnresolvedReferences
86 return KhandakerMojoMedicationTherapy.get_linked(
87 self.medicationtable_id, self)
90class KhandakerMojoMedicationItem(KhandakerMojoTableItem):
91 __tablename__ = "khandaker_mojo_medication_item"
93 medicationtable_id = CamcopsColumn(
94 "medicationtable_id", Integer, nullable=False,
95 comment="FK to medicationtable"
96 )
97 seqnum = CamcopsColumn(
98 "seqnum", Integer, nullable=False,
99 comment="Sequence number of this medication"
100 )
101 brand_name = CamcopsColumn(
102 "brand_name", UnicodeText,
103 comment="Brand name"
104 )
105 chemical_name = CamcopsColumn(
106 "chemical_name", UnicodeText,
107 comment="Chemical name for study team"
108 )
109 dose = CamcopsColumn(
110 "dose", UnicodeText,
111 comment="Dose"
112 )
113 frequency = CamcopsColumn(
114 "frequency", UnicodeText,
115 comment="Frequency"
116 )
117 duration_months = CamcopsColumn(
118 "duration_months", Float,
119 comment="Duration (months)"
120 )
121 indication = CamcopsColumn(
122 "indication", UnicodeText,
123 comment="Indication (what is the medication used for?)"
124 )
125 response = CamcopsColumn(
126 "response", Integer,
127 comment=("1 = treats all symptoms, "
128 "2 = most symptoms, "
129 "3 = some symptoms, "
130 "4 = no symptoms)")
131 )
133 @classmethod
134 def mandatory_fields(cls) -> List[str]:
135 return [
136 "brand_name",
137 "chemical_name",
138 "dose",
139 "frequency",
140 "duration_months",
141 "indication",
142 "response",
143 ]
145 def get_html_table_row(self, req: "CamcopsRequest") -> str:
146 return f"""
147 <tr>
148 <td>{answer(self.chemical_name)}</td>
149 <td>{answer(self.brand_name)}</td>
150 <td>{answer(self.dose)}</td>
151 <td>{answer(self.frequency)}</td>
152 <td>{answer(self.duration_months)}</td>
153 <td>{answer(self.indication)}</td>
154 <td>{answer(self.get_response_option(req))}</td>
155 </tr>
156 """
159class KhandakerMojoTherapyItem(KhandakerMojoTableItem):
160 __tablename__ = "khandaker_mojo_therapy_item"
162 medicationtable_id = CamcopsColumn(
163 "medicationtable_id", Integer, nullable=False,
164 comment="FK to medicationtable"
165 )
166 seqnum = CamcopsColumn(
167 "seqnum", Integer, nullable=False,
168 comment="Sequence number of this therapy"
169 )
170 therapy = CamcopsColumn(
171 "therapy", UnicodeText,
172 comment="Therapy"
173 )
174 frequency = CamcopsColumn(
175 "frequency", UnicodeText,
176 comment="Frequency"
177 )
178 sessions_completed = CamcopsColumn(
179 "sessions_completed", Integer,
180 comment="Sessions completed"
181 )
182 sessions_planned = CamcopsColumn(
183 "sessions_planned", Integer,
184 comment="Sessions planned"
185 )
186 indication = CamcopsColumn(
187 "indication", UnicodeText,
188 comment="Indication (what is the medication used for?)"
189 )
190 response = CamcopsColumn(
191 "response", Integer,
192 comment=("1 = treats all symptoms, "
193 "2 = most symptoms, "
194 "3 = some symptoms, "
195 "4 = no symptoms)")
196 )
198 @classmethod
199 def mandatory_fields(cls) -> List[str]:
200 return [
201 "therapy",
202 "frequency",
203 "sessions_completed",
204 "sessions_planned",
205 "indication",
206 "response",
207 ]
209 def get_html_table_row(self, req: "CamcopsRequest") -> str:
210 return f"""
211 <tr>
212 <td>{answer(self.therapy)}</td>
213 <td>{answer(self.frequency)}</td>
214 <td>{answer(self.sessions_completed)}</td>
215 <td>{answer(self.sessions_planned)}</td>
216 <td>{answer(self.indication)}</td>
217 <td>{answer(self.get_response_option(req))}</td>
218 </tr>
219 """
222class KhandakerMojoMedicationTherapy(TaskHasPatientMixin, Task):
223 """
224 Server implementation of the KhandakerMojoMedicationTherapy task
225 """
226 __tablename__ = "khandaker_mojo_medicationtherapy"
227 shortname = "Khandaker_MOJO_MedicationTherapy"
228 provides_trackers = False
230 medication_items = ancillary_relationship(
231 parent_class_name="KhandakerMojoMedicationTherapy",
232 ancillary_class_name="KhandakerMojoMedicationItem",
233 ancillary_fk_to_parent_attr_name="medicationtable_id",
234 ancillary_order_by_attr_name="seqnum"
235 ) # type: List[KhandakerMojoMedicationItem]
237 therapy_items = ancillary_relationship(
238 parent_class_name="KhandakerMojoMedicationTherapy",
239 ancillary_class_name="KhandakerMojoTherapyItem",
240 ancillary_fk_to_parent_attr_name="medicationtable_id",
241 ancillary_order_by_attr_name="seqnum"
242 ) # type: List[KhandakerMojoTherapyItem]
244 @staticmethod
245 def longname(req: "CamcopsRequest") -> str:
246 _ = req.gettext
247 return _("Khandaker GM — MOJO — Medications and therapies")
249 def is_complete(self) -> bool:
250 # Whilst it's almost certain that anyone completing this task would be
251 # on some kind of medication, we have no way of knowing when all
252 # medication has been added to the table
253 for item in self.medication_items:
254 if item.any_fields_none():
255 return False
257 for item in self.therapy_items:
258 if item.any_fields_none():
259 return False
261 return True
263 def get_num_medication_items(self) -> int:
264 return len(self.medication_items)
266 def get_num_therapy_items(self) -> int:
267 return len(self.therapy_items)
269 def get_task_html(self, req: "CamcopsRequest") -> str:
270 html = f"""
271 <div class="{CssClass.SUMMARY}">
272 <table class="{CssClass.SUMMARY}">
273 {self.get_is_complete_tr(req)}
274 {tr_qa("Number of medications",
275 self.get_num_medication_items())}
276 {tr_qa("Number of therapies",
277 self.get_num_therapy_items())}
278 </table>
279 </div>
280 <table class="{CssClass.TASKDETAIL}">
281 <tr>
282 <th>{self.xstring(req, "chemical_name")}</th>
283 <th>{self.xstring(req, "brand_name")}</th>
284 <th>{self.xstring(req, "dose")}</th>
285 <th>{self.xstring(req, "frequency")}</th>
286 <th>{self.xstring(req, "duration_months")}</th>
287 <th>{self.xstring(req, "indication")}</th>
288 <th>{self.xstring(req, "response")}</th>
289 </tr>
290 """
291 for item in self.medication_items:
292 html += item.get_html_table_row(req)
294 html += f"""
295 </table>
296 <table class="{CssClass.TASKDETAIL}">
297 <tr>
298 <th>{self.xstring(req, "therapy")}</th>
299 <th>{self.xstring(req, "frequency")}</th>
300 <th>{self.xstring(req, "sessions_completed")}</th>
301 <th>{self.xstring(req, "sessions_planned")}</th>
302 <th>{self.xstring(req, "indication")}</th>
303 <th>{self.xstring(req, "response")}</th>
304 </tr>
305 """
307 for item in self.therapy_items:
308 html += item.get_html_table_row(req)
310 html += """
311 </table>
312 """
314 return html