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