Coverage for tasks/cpft_lps.py : 50%

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/cpft_lps.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"""
29import logging
30from typing import Any, List, Optional, Type
32from cardinal_pythonlib.classes import classproperty
33from cardinal_pythonlib.datetimefunc import format_datetime
34from cardinal_pythonlib.logs import BraceStyleAdapter
35import cardinal_pythonlib.rnc_web as ws
36import pyramid.httpexceptions as exc
37from sqlalchemy.sql.expression import and_, exists, select
38from sqlalchemy.sql.selectable import SelectBase
39from sqlalchemy.sql.schema import Column
40from sqlalchemy.sql.sqltypes import Date, Integer, UnicodeText
42from camcops_server.cc_modules.cc_constants import (
43 CssClass,
44 DateFormat,
45 INVALID_VALUE,
46)
47from camcops_server.cc_modules.cc_ctvinfo import CtvInfo
48from camcops_server.cc_modules.cc_forms import (
49 LinkingIdNumSelector,
50 ReportParamSchema,
51)
52from camcops_server.cc_modules.cc_html import (
53 answer,
54 get_yes_no_none,
55 subheading_spanning_four_columns,
56 subheading_spanning_two_columns,
57 tr_qa,
58 tr_span_col,
59)
60from camcops_server.cc_modules.cc_nhs import (
61 get_nhs_dd_ethnic_category_code,
62 get_nhs_dd_person_marital_status,
63 PV_NHS_ETHNIC_CATEGORY,
64 PV_NHS_MARITAL_STATUS
65)
66from camcops_server.cc_modules.cc_patient import Patient
67from camcops_server.cc_modules.cc_patientidnum import PatientIdNum
68from camcops_server.cc_modules.cc_pyramid import ViewParam
69from camcops_server.cc_modules.cc_report import Report
70from camcops_server.cc_modules.cc_request import CamcopsRequest
71from camcops_server.cc_modules.cc_sqla_coltypes import (
72 BoolColumn,
73 CamcopsColumn,
74 CharColType,
75 PendulumDateTimeAsIsoTextColType,
76 DiagnosticCodeColType,
77 PermittedValueChecker,
78)
79from camcops_server.cc_modules.cc_task import (
80 Task,
81 TaskHasClinicianMixin,
82 TaskHasPatientMixin,
83)
84from camcops_server.tasks.psychiatricclerking import PsychiatricClerking
86log = BraceStyleAdapter(logging.getLogger(__name__))
89# =============================================================================
90# CPFT_LPS_Referral
91# =============================================================================
93class CPFTLPSReferral(TaskHasPatientMixin, Task):
94 """
95 Server implementation of the CPFT_LPS_Referral task.
96 """
97 __tablename__ = "cpft_lps_referral"
98 shortname = "CPFT_LPS_Referral"
100 referral_date_time = Column("referral_date_time",
101 PendulumDateTimeAsIsoTextColType)
102 lps_division = CamcopsColumn(
103 "lps_division", UnicodeText,
104 exempt_from_anonymisation=True)
105 referral_priority = CamcopsColumn(
106 "referral_priority", UnicodeText,
107 exempt_from_anonymisation=True)
108 referral_method = CamcopsColumn(
109 "referral_method", UnicodeText,
110 exempt_from_anonymisation=True)
111 referrer_name = Column("referrer_name", UnicodeText)
112 referrer_contact_details = Column("referrer_contact_details", UnicodeText)
113 referring_consultant = Column("referring_consultant", UnicodeText)
114 referring_specialty = CamcopsColumn(
115 "referring_specialty", UnicodeText,
116 exempt_from_anonymisation=True)
117 referring_specialty_other = Column("referring_specialty_other", UnicodeText)
118 patient_location = Column("patient_location", UnicodeText)
119 admission_date = Column("admission_date", Date)
120 estimated_discharge_date = Column("estimated_discharge_date", Date)
121 patient_aware_of_referral = BoolColumn("patient_aware_of_referral")
122 interpreter_required = BoolColumn("interpreter_required")
123 sensory_impairment = BoolColumn("sensory_impairment")
124 marital_status_code = CamcopsColumn(
125 "marital_status_code", CharColType,
126 permitted_value_checker=PermittedValueChecker(
127 permitted_values=PV_NHS_MARITAL_STATUS)
128 )
129 ethnic_category_code = CamcopsColumn(
130 "ethnic_category_code", CharColType,
131 permitted_value_checker=PermittedValueChecker(
132 permitted_values=PV_NHS_ETHNIC_CATEGORY)
133 )
134 admission_reason_overdose = BoolColumn("admission_reason_overdose")
135 admission_reason_self_harm_not_overdose = BoolColumn(
136 "admission_reason_self_harm_not_overdose",
137 constraint_name="ck_cpft_lps_referral_arshno"
138 )
139 admission_reason_confusion = BoolColumn("admission_reason_confusion")
140 admission_reason_trauma = BoolColumn("admission_reason_trauma")
141 admission_reason_falls = BoolColumn("admission_reason_falls")
142 admission_reason_infection = BoolColumn("admission_reason_infection")
143 admission_reason_poor_adherence = BoolColumn(
144 "admission_reason_poor_adherence",
145 constraint_name="ck_cpft_lps_referral_adpa"
146 )
147 admission_reason_other = BoolColumn("admission_reason_other")
148 existing_psychiatric_teams = Column("existing_psychiatric_teams",
149 UnicodeText)
150 care_coordinator = Column("care_coordinator", UnicodeText)
151 other_contact_details = Column("other_contact_details", UnicodeText)
152 referral_reason = Column("referral_reason", UnicodeText)
154 @staticmethod
155 def longname(req: "CamcopsRequest") -> str:
156 _ = req.gettext
157 return _("CPFT LPS – referral")
159 def is_complete(self) -> bool:
160 return bool(
161 self.patient_location and
162 self.referral_reason and
163 self.field_contents_valid()
164 )
166 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
167 return [CtvInfo(
168 heading=ws.webify(self.wxstring(req, "f_referral_reason_t")),
169 content=self.referral_reason
170 )]
172 @staticmethod
173 def four_column_row(q1: str, a1: Any,
174 q2: str, a2: Any,
175 default: str = "") -> str:
176 return f"""
177 <tr>
178 <td>{q1}</td><td>{answer(a1, default=default)}</td>
179 <td>{q2}</td><td>{answer(a2, default=default)}</td>
180 </tr>
181 """
183 @staticmethod
184 def tr_qa(q: str, a: Any, default: str = "") -> str:
185 return f"""
186 <tr>
187 <td colspan="2">{q}</td>
188 <td colspan="2"><b>{default if a is None else a}</b></td>
189 </tr>
190 """
192 def get_task_html(self, req: CamcopsRequest) -> str:
193 person_marital_status = get_nhs_dd_person_marital_status(req)
194 ethnic_category_code = get_nhs_dd_ethnic_category_code(req)
195 if self.lps_division == "G":
196 banner_class = CssClass.BANNER_REFERRAL_GENERAL_ADULT
197 division_name = self.wxstring(req, "service_G")
198 elif self.lps_division == "O":
199 banner_class = CssClass.BANNER_REFERRAL_OLD_AGE
200 division_name = self.wxstring(req, "service_O")
201 elif self.lps_division == "S":
202 banner_class = CssClass.BANNER_REFERRAL_SUBSTANCE_MISUSE
203 division_name = self.wxstring(req, "service_S")
204 else:
205 banner_class = ""
206 division_name = None
208 if self.referral_priority == "R":
209 priority_name = self.wxstring(req, "priority_R")
210 elif self.referral_priority == "U":
211 priority_name = self.wxstring(req, "priority_U")
212 elif self.referral_priority == "E":
213 priority_name = self.wxstring(req, "priority_E")
214 else:
215 priority_name = None
217 potential_admission_reasons = [
218 "admission_reason_overdose",
219 "admission_reason_self_harm_not_overdose",
220 "admission_reason_confusion",
221 "admission_reason_trauma",
222 "admission_reason_falls",
223 "admission_reason_infection",
224 "admission_reason_poor_adherence",
225 "admission_reason_other",
226 ]
227 admission_reasons = []
228 for r in potential_admission_reasons:
229 if getattr(self, r):
230 admission_reasons.append(self.wxstring(req, "f_" + r))
232 h = f"""
233 <div class="{CssClass.BANNER} {banner_class}">
234 {answer(division_name, default_for_blank_strings=True)}
235 referral at {
236 answer(format_datetime(
237 self.referral_date_time,
238 DateFormat.SHORT_DATETIME_WITH_DAY_NO_TZ,
239 default=None))}
240 </div>
241 <div class="{CssClass.SUMMARY}">
242 <table class="{CssClass.SUMMARY}">
243 {self.get_is_complete_tr(req)}
244 </table>
245 </div>
246 <table class="{CssClass.TASKDETAIL}">
247 <col width="25%">
248 <col width="25%">
249 <col width="25%">
250 <col width="25%">
251 """
252 h += subheading_spanning_four_columns(
253 self.wxstring(req, "t_about_referral"))
254 h += """
255 <tr>
256 <td>{q_method}</td>
257 <td>{a_method}</td>
258 <td>{q_priority}</td>
259 <td class="{CssClass.HIGHLIGHT}">{a_priority}</td>
260 </tr>
261 """.format(
262 CssClass=CssClass,
263 q_method=self.wxstring(req, "f_referral_method"),
264 a_method=answer(self.referral_method),
265 q_priority=self.wxstring(req, "f_referral_priority"),
266 a_priority=(
267 answer(self.referral_priority, default_for_blank_strings=True) + # noqa
268 ": " + answer(priority_name)
269 )
270 )
271 h += self.four_column_row(
272 self.wxstring(req, "f_referrer_name"),
273 self.referrer_name,
274 self.wxstring(req, "f_referring_specialty"),
275 self.referring_specialty
276 )
277 h += self.four_column_row(
278 self.wxstring(req, "f_referrer_contact_details"),
279 self.referrer_contact_details,
280 self.wxstring(req, "f_referring_specialty_other"),
281 self.referring_specialty_other
282 )
283 h += self.four_column_row(
284 self.wxstring(req, "f_referring_consultant"),
285 self.referring_consultant,
286 "",
287 ""
288 )
289 h += subheading_spanning_four_columns(
290 self.wxstring(req, "t_patient"))
291 h += """
292 <tr>
293 <td>{q_when}</td>
294 <td>{a_when}</td>
295 <td>{q_where}</td>
296 <td class="{CssClass.HIGHLIGHT}">{a_where}</td>
297 </tr>
298 """.format(
299 CssClass=CssClass,
300 q_when=self.wxstring(req, "f_admission_date"),
301 a_when=answer(
302 format_datetime(self.admission_date, DateFormat.LONG_DATE,
303 default=None), ""),
304 q_where=self.wxstring(req, "f_patient_location"),
305 a_where=answer(self.patient_location),
306 )
307 h += self.four_column_row(
308 self.wxstring(req, "f_estimated_discharge_date"),
309 format_datetime(self.estimated_discharge_date,
310 DateFormat.LONG_DATE, ""),
311 self.wxstring(req, "f_patient_aware_of_referral"),
312 get_yes_no_none(req, self.patient_aware_of_referral)
313 )
314 h += self.four_column_row(
315 self.wxstring(req, "f_marital_status"),
316 person_marital_status.get(self.marital_status_code, INVALID_VALUE),
317 self.wxstring(req, "f_interpreter_required"),
318 get_yes_no_none(req, self.interpreter_required)
319 )
320 h += self.four_column_row(
321 self.wxstring(req, "f_ethnic_category"),
322 ethnic_category_code.get(self.ethnic_category_code, INVALID_VALUE),
323 self.wxstring(req, "f_sensory_impairment"),
324 get_yes_no_none(req, self.sensory_impairment)
325 )
326 h += subheading_spanning_four_columns(
327 self.wxstring(req, "t_admission_reason"))
328 h += tr_span_col(answer(", ".join(admission_reasons), ""), cols=4)
329 h += subheading_spanning_four_columns(
330 self.wxstring(req, "t_other_people"))
331 h += self.tr_qa(
332 self.wxstring(req, "f_existing_psychiatric_teams"),
333 self.existing_psychiatric_teams, "")
334 h += self.tr_qa(
335 self.wxstring(req, "f_care_coordinator"),
336 self.care_coordinator, "")
337 h += self.tr_qa(
338 self.wxstring(req, "f_other_contact_details"),
339 self.other_contact_details, "")
340 h += subheading_spanning_four_columns(
341 self.wxstring(req, "t_referral_reason"))
342 h += tr_span_col(answer(self.referral_reason, ""), cols=4)
343 h += """
344 </table>
345 """
346 return h
349# =============================================================================
350# CPFT_LPS_ResetResponseClock
351# =============================================================================
353class CPFTLPSResetResponseClock(TaskHasPatientMixin, TaskHasClinicianMixin,
354 Task):
355 """
356 Server implementation of the CPFT_LPS_ResetResponseClock task.
357 """
358 __tablename__ = "cpft_lps_resetresponseclock"
359 shortname = "CPFT_LPS_ResetResponseClock"
361 reset_start_time_to = Column(
362 "reset_start_time_to", PendulumDateTimeAsIsoTextColType
363 )
364 reason = Column("reason", UnicodeText)
366 @staticmethod
367 def longname(req: "CamcopsRequest") -> str:
368 _ = req.gettext
369 return _("CPFT LPS – reset response clock")
371 def is_complete(self) -> bool:
372 return bool(
373 self.reset_start_time_to and
374 self.reason and
375 self.field_contents_valid()
376 )
378 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
379 return [CtvInfo(content=self.reason)]
381 def get_task_html(self, req: CamcopsRequest) -> str:
382 h = f"""
383 <div class="{CssClass.SUMMARY}">
384 <table class="{CssClass.SUMMARY}">
385 {self.get_is_complete_tr(req)}
386 </table>
387 </div>
388 <table class="{CssClass.TASKDETAIL}">
389 <col width="25%">
390 <col width="75%">
391 """
392 h += tr_qa(
393 self.wxstring(req, "to"),
394 format_datetime(self.reset_start_time_to,
395 DateFormat.LONG_DATETIME_WITH_DAY,
396 default=None))
397 h += tr_qa(self.wxstring(req, "reason"), self.reason)
398 h += """
399 </table>
400 """
401 return h
404# =============================================================================
405# CPFT_LPS_Discharge
406# =============================================================================
408class CPFTLPSDischarge(TaskHasPatientMixin, TaskHasClinicianMixin, Task):
409 """
410 Server implementation of the CPFT_LPS_Discharge task.
411 """
412 __tablename__ = "cpft_lps_discharge"
413 shortname = "CPFT_LPS_Discharge"
415 discharge_date = Column("discharge_date", Date)
416 discharge_reason_code = CamcopsColumn(
417 "discharge_reason_code", UnicodeText,
418 exempt_from_anonymisation=True)
420 leaflet_or_discharge_card_given = BoolColumn(
421 "leaflet_or_discharge_card_given",
422 constraint_name="ck_cpft_lps_discharge_lodcg"
423 )
424 frequent_attender = BoolColumn("frequent_attender")
425 patient_wanted_copy_of_letter = BoolColumn(
426 # Was previously text! That wasn't right.
427 "patient_wanted_copy_of_letter",
428 )
429 gaf_at_first_assessment = CamcopsColumn(
430 "gaf_at_first_assessment", Integer,
431 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=100)
432 )
433 gaf_at_discharge = CamcopsColumn(
434 "gaf_at_discharge", Integer,
435 permitted_value_checker=PermittedValueChecker(minimum=0, maximum=100)
436 )
438 referral_reason_self_harm_overdose = BoolColumn(
439 "referral_reason_self_harm_overdose",
440 constraint_name="ck_cpft_lps_discharge_rrshoverdose"
441 )
442 referral_reason_self_harm_other = BoolColumn(
443 "referral_reason_self_harm_other",
444 constraint_name="ck_cpft_lps_discharge_rrshother"
445 )
446 referral_reason_suicidal_ideas = BoolColumn(
447 "referral_reason_suicidal_ideas",
448 constraint_name="ck_cpft_lps_discharge_rrsuicidal"
449 )
450 referral_reason_behavioural_disturbance = BoolColumn(
451 "referral_reason_behavioural_disturbance",
452 constraint_name="ck_cpft_lps_discharge_behavdisturb"
453 )
454 referral_reason_low_mood = BoolColumn("referral_reason_low_mood")
455 referral_reason_elevated_mood = BoolColumn("referral_reason_elevated_mood")
456 referral_reason_psychosis = BoolColumn("referral_reason_psychosis")
457 referral_reason_pre_transplant = BoolColumn(
458 "referral_reason_pre_transplant",
459 constraint_name="ck_cpft_lps_discharge_pretransplant"
460 )
461 referral_reason_post_transplant = BoolColumn(
462 "referral_reason_post_transplant",
463 constraint_name="ck_cpft_lps_discharge_posttransplant"
464 )
465 referral_reason_delirium = BoolColumn("referral_reason_delirium")
466 referral_reason_anxiety = BoolColumn("referral_reason_anxiety")
467 referral_reason_somatoform_mus = BoolColumn(
468 "referral_reason_somatoform_mus",
469 constraint_name="ck_cpft_lps_discharge_mus"
470 )
471 referral_reason_motivation_adherence = BoolColumn(
472 "referral_reason_motivation_adherence",
473 constraint_name="ck_cpft_lps_discharge_motivadherence"
474 )
475 referral_reason_capacity = BoolColumn("referral_reason_capacity")
476 referral_reason_eating_disorder = BoolColumn(
477 "referral_reason_eating_disorder",
478 constraint_name="ck_cpft_lps_discharge_eatingdis"
479 )
480 referral_reason_safeguarding = BoolColumn("referral_reason_safeguarding")
481 referral_reason_discharge_placement = BoolColumn(
482 "referral_reason_discharge_placement",
483 constraint_name="ck_cpft_lps_discharge_dcplacement"
484 )
485 referral_reason_cognitive_problem = BoolColumn(
486 "referral_reason_cognitive_problem",
487 constraint_name="ck_cpft_lps_discharge_cognitiveprob"
488 )
489 referral_reason_substance_alcohol = BoolColumn(
490 "referral_reason_substance_alcohol",
491 constraint_name="ck_cpft_lps_discharge_alcohol"
492 )
493 referral_reason_substance_other = BoolColumn(
494 "referral_reason_substance_other",
495 constraint_name="ck_cpft_lps_discharge_substanceother"
496 )
497 referral_reason_other = BoolColumn("referral_reason_other")
498 referral_reason_transplant_organ = CamcopsColumn(
499 "referral_reason_transplant_organ", UnicodeText,
500 exempt_from_anonymisation=True
501 )
502 referral_reason_other_detail = Column(
503 "referral_reason_other_detail", UnicodeText
504 )
506 diagnosis_no_active_mental_health_problem = BoolColumn(
507 "diagnosis_no_active_mental_health_problem",
508 constraint_name="ck_cpft_lps_discharge_nomhprob"
509 )
510 diagnosis_psych_1_icd10code = Column(
511 "diagnosis_psych_1_icd10code", DiagnosticCodeColType
512 )
513 diagnosis_psych_1_description = CamcopsColumn(
514 "diagnosis_psych_1_description", UnicodeText,
515 exempt_from_anonymisation=True
516 )
517 diagnosis_psych_2_icd10code = Column(
518 "diagnosis_psych_2_icd10code", DiagnosticCodeColType
519 )
520 diagnosis_psych_2_description = CamcopsColumn(
521 "diagnosis_psych_2_description", UnicodeText,
522 exempt_from_anonymisation=True
523 )
524 diagnosis_psych_3_icd10code = Column(
525 "diagnosis_psych_3_icd10code", DiagnosticCodeColType
526 )
527 diagnosis_psych_3_description = CamcopsColumn(
528 "diagnosis_psych_3_description", UnicodeText,
529 exempt_from_anonymisation=True
530 )
531 diagnosis_psych_4_icd10code = Column(
532 "diagnosis_psych_4_icd10code", DiagnosticCodeColType
533 )
534 diagnosis_psych_4_description = CamcopsColumn(
535 "diagnosis_psych_4_description", UnicodeText,
536 exempt_from_anonymisation=True
537 )
538 diagnosis_medical_1 = Column("diagnosis_medical_1", UnicodeText)
539 diagnosis_medical_2 = Column("diagnosis_medical_2", UnicodeText)
540 diagnosis_medical_3 = Column("diagnosis_medical_3", UnicodeText)
541 diagnosis_medical_4 = Column("diagnosis_medical_4", UnicodeText)
543 management_assessment_diagnostic = BoolColumn(
544 "management_assessment_diagnostic",
545 constraint_name="ck_cpft_lps_discharge_mx_ass_diag"
546 )
547 management_medication = BoolColumn("management_medication")
548 management_specialling_behavioural_disturbance = BoolColumn(
549 "management_specialling_behavioural_disturbance",
550 # Constraint name too long for MySQL unless we do this:
551 constraint_name="ck_cpft_lps_discharge_msbd"
552 )
553 management_supportive_patient = BoolColumn("management_supportive_patient")
554 management_supportive_carers = BoolColumn("management_supportive_carers")
555 management_supportive_staff = BoolColumn("management_supportive_staff")
556 management_nursing_management = BoolColumn("management_nursing_management")
557 management_therapy_cbt = BoolColumn("management_therapy_cbt")
558 management_therapy_cat = BoolColumn("management_therapy_cat")
559 management_therapy_other = BoolColumn("management_therapy_other")
560 management_treatment_adherence = BoolColumn(
561 "management_treatment_adherence",
562 constraint_name="ck_cpft_lps_discharge_mx_rx_adhere"
563 )
564 management_capacity = BoolColumn("management_capacity")
565 management_education_patient = BoolColumn("management_education_patient")
566 management_education_carers = BoolColumn("management_education_carers")
567 management_education_staff = BoolColumn("management_education_staff")
568 management_accommodation_placement = BoolColumn(
569 "management_accommodation_placement",
570 constraint_name="ck_cpft_lps_discharge_accom"
571 )
572 management_signposting_external_referral = BoolColumn(
573 "management_signposting_external_referral",
574 constraint_name="ck_cpft_lps_discharge_mx_signpostrefer"
575 )
576 management_mha_s136 = BoolColumn("management_mha_s136")
577 management_mha_s5_2 = BoolColumn("management_mha_s5_2")
578 management_mha_s2 = BoolColumn("management_mha_s2")
579 management_mha_s3 = BoolColumn("management_mha_s3")
580 management_complex_case_conference = BoolColumn(
581 "management_complex_case_conference",
582 constraint_name="ck_cpft_lps_discharge_caseconf"
583 )
584 management_other = BoolColumn("management_other")
585 management_other_detail = Column("management_other_detail", UnicodeText)
587 outcome = CamcopsColumn(
588 "outcome", UnicodeText,
589 exempt_from_anonymisation=True
590 )
591 outcome_hospital_transfer_detail = Column(
592 "outcome_hospital_transfer_detail", UnicodeText
593 )
594 outcome_other_detail = Column("outcome_other_detail", UnicodeText)
596 @staticmethod
597 def longname(req: "CamcopsRequest") -> str:
598 _ = req.gettext
599 return _("CPFT LPS – discharge")
601 def is_complete(self) -> bool:
602 return bool(
603 self.discharge_date and
604 self.discharge_reason_code and
605 # self.outcome and # v2.0.0
606 self.field_contents_valid()
607 )
609 def get_discharge_reason(self, req: CamcopsRequest) -> Optional[str]:
610 if self.discharge_reason_code == "F":
611 return self.wxstring(req, "reason_code_F")
612 elif self.discharge_reason_code == "A":
613 return self.wxstring(req, "reason_code_A")
614 elif self.discharge_reason_code == "O":
615 return self.wxstring(req, "reason_code_O")
616 elif self.discharge_reason_code == "C":
617 return self.wxstring(req, "reason_code_C")
618 else:
619 return None
621 def get_referral_reasons(self, req: CamcopsRequest) -> List[str]:
622 potential_referral_reasons = [
623 "referral_reason_self_harm_overdose",
624 "referral_reason_self_harm_other",
625 "referral_reason_suicidal_ideas",
626 "referral_reason_behavioural_disturbance",
627 "referral_reason_low_mood",
628 "referral_reason_elevated_mood",
629 "referral_reason_psychosis",
630 "referral_reason_pre_transplant",
631 "referral_reason_post_transplant",
632 "referral_reason_delirium",
633 "referral_reason_anxiety",
634 "referral_reason_somatoform_mus",
635 "referral_reason_motivation_adherence",
636 "referral_reason_capacity",
637 "referral_reason_eating_disorder",
638 "referral_reason_safeguarding",
639 "referral_reason_discharge_placement",
640 "referral_reason_cognitive_problem",
641 "referral_reason_substance_alcohol",
642 "referral_reason_substance_other",
643 "referral_reason_other",
644 ]
645 referral_reasons = []
646 for r in potential_referral_reasons:
647 if getattr(self, r):
648 referral_reasons.append(self.wxstring(req, "" + r))
649 return referral_reasons
651 def get_managements(self, req: CamcopsRequest) -> List[str]:
652 potential_managements = [
653 "management_assessment_diagnostic",
654 "management_medication",
655 "management_specialling_behavioural_disturbance",
656 "management_supportive_patient",
657 "management_supportive_carers",
658 "management_supportive_staff",
659 "management_nursing_management",
660 "management_therapy_cbt",
661 "management_therapy_cat",
662 "management_therapy_other",
663 "management_treatment_adherence",
664 "management_capacity",
665 "management_education_patient",
666 "management_education_carers",
667 "management_education_staff",
668 "management_accommodation_placement",
669 "management_signposting_external_referral",
670 "management_mha_s136",
671 "management_mha_s5_2",
672 "management_mha_s2",
673 "management_mha_s3",
674 "management_complex_case_conference",
675 "management_other",
676 ]
677 managements = []
678 for r in potential_managements:
679 if getattr(self, r):
680 managements.append(self.wxstring(req, "" + r))
681 return managements
683 def get_psychiatric_diagnoses(self, req: CamcopsRequest) -> List[str]:
684 psychiatric_diagnoses = [
685 self.wxstring(req, "diagnosis_no_active_mental_health_problem")
686 ] if self.diagnosis_no_active_mental_health_problem else []
687 for i in range(1, 4 + 1): # magic number
688 if getattr(self, "diagnosis_psych_" + str(i) + "_icd10code"):
689 psychiatric_diagnoses.append(
690 ws.webify(getattr(self, "diagnosis_psych_" +
691 str(i) + "_icd10code")) +
692 " – " +
693 ws.webify(getattr(self, "diagnosis_psych_" +
694 str(i) + "_description"))
695 )
696 return psychiatric_diagnoses
698 def get_medical_diagnoses(self) -> List[str]:
699 medical_diagnoses = []
700 for i in range(1, 4 + 1): # magic number
701 if getattr(self, "diagnosis_medical_" + str(i)):
702 medical_diagnoses.append(
703 ws.webify(getattr(self, "diagnosis_medical_" + str(i))))
704 return medical_diagnoses
706 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
707 diagnoses = self.get_psychiatric_diagnoses(req) + \
708 self.get_medical_diagnoses()
709 return [
710 CtvInfo(
711 heading=ws.webify(self.wxstring(req, "discharge_reason")),
712 content=self.get_discharge_reason(req)
713 ),
714 CtvInfo(
715 heading=ws.webify(
716 self.wxstring(req, "referral_reason_t")),
717 content=", ".join(self.get_referral_reasons(req))
718 ),
719 CtvInfo(
720 heading=ws.webify(self.wxstring(req, "diagnoses_t")),
721 content=", ".join(diagnoses)
722 ),
723 CtvInfo(
724 heading=ws.webify(self.wxstring(req, "management_t")),
725 content=", ".join(self.get_managements(req))
726 ),
727 CtvInfo(
728 heading=ws.webify(self.wxstring(req, "outcome_t")),
729 content=self.outcome
730 ),
731 ]
733 def get_task_html(self, req: CamcopsRequest) -> str:
734 h = f"""
735 <div class="{CssClass.SUMMARY}">
736 <table class="{CssClass.SUMMARY}">
737 {self.get_is_complete_tr(req)}
738 </table>
739 </div>
740 <table class="{CssClass.TASKDETAIL}">
741 <col width="40%">
742 <col width="60%">
743 """
744 h += tr_qa(self.wxstring(req, "discharge_date"),
745 format_datetime(self.discharge_date,
746 DateFormat.LONG_DATE_WITH_DAY,
747 default=None), "")
748 h += tr_qa(self.wxstring(req, "discharge_reason"),
749 self.get_discharge_reason(req), "")
750 h += tr_qa(self.wxstring(req, "leaflet_or_discharge_card_given"),
751 get_yes_no_none(req, self.leaflet_or_discharge_card_given),
752 "")
753 h += tr_qa(self.wxstring(req, "frequent_attender"),
754 get_yes_no_none(req, self.frequent_attender), "")
755 h += tr_qa(self.wxstring(req, "patient_wanted_copy_of_letter"),
756 self.patient_wanted_copy_of_letter, "")
757 h += tr_qa(self.wxstring(req, "gaf_at_first_assessment"),
758 self.gaf_at_first_assessment, "")
759 h += tr_qa(self.wxstring(req, "gaf_at_discharge"),
760 self.gaf_at_discharge, "")
762 h += subheading_spanning_two_columns(
763 self.wxstring(req, "referral_reason_t"))
764 h += tr_span_col(answer(", ".join(self.get_referral_reasons(req))),
765 cols=2)
766 h += tr_qa(self.wxstring(req, "referral_reason_transplant_organ"),
767 self.referral_reason_transplant_organ, "")
768 h += tr_qa(self.wxstring(req, "referral_reason_other_detail"),
769 self.referral_reason_other_detail, "")
771 h += subheading_spanning_two_columns(
772 self.wxstring(req, "diagnoses_t"))
773 h += tr_qa(self.wxstring(req, "psychiatric_t"),
774 "\n".join(self.get_psychiatric_diagnoses(req)), "")
775 h += tr_qa(self.wxstring(req, "medical_t"),
776 "\n".join(self.get_medical_diagnoses()), "")
778 h += subheading_spanning_two_columns(self.wxstring(req, "management_t"))
779 h += tr_span_col(answer(", ".join(self.get_managements(req))), cols=2)
780 h += tr_qa(self.wxstring(req, "management_other_detail"),
781 self.management_other_detail, "")
783 h += subheading_spanning_two_columns(self.wxstring(req, "outcome_t"))
784 h += tr_qa(self.wxstring(req, "outcome_t"),
785 self.outcome, "")
786 h += tr_qa(self.wxstring(req, "outcome_hospital_transfer_detail"),
787 self.outcome_hospital_transfer_detail, "")
788 h += tr_qa(self.wxstring(req, "outcome_other_detail"),
789 self.outcome_other_detail, "")
791 h += """
792 </table>
793 """
794 return h
797# =============================================================================
798# Reports
799# =============================================================================
801class LPSReportSchema(ReportParamSchema):
802 which_idnum = LinkingIdNumSelector() # must match ViewParam.WHICH_IDNUM
805class LPSReportReferredNotDischarged(Report):
806 # noinspection PyMethodParameters
807 @classproperty
808 def report_id(cls) -> str:
809 return "cpft_lps_referred_not_subsequently_discharged"
811 @classmethod
812 def title(cls, req: "CamcopsRequest") -> str:
813 _ = req.gettext
814 return _("CPFT LPS – referred but not yet discharged")
816 # noinspection PyMethodParameters
817 @classproperty
818 def superuser_only(cls) -> bool:
819 return False
821 @staticmethod
822 def get_paramform_schema_class() -> Type[ReportParamSchema]:
823 return LPSReportSchema
825 # noinspection PyProtectedMember,PyUnresolvedReferences
826 def get_query(self, req: CamcopsRequest) -> SelectBase:
827 which_idnum = req.get_int_param(ViewParam.WHICH_IDNUM, 1)
828 if which_idnum is None:
829 raise exc.HTTPBadRequest(
830 f"{ViewParam.WHICH_IDNUM} not specified")
832 group_ids = req.user.ids_of_groups_user_may_report_on
834 # Step 1: link referral and patient
835 p1 = Patient.__table__.alias("p1")
836 i1 = PatientIdNum.__table__.alias("i1")
837 desc = req.get_id_shortdesc(which_idnum)
838 select_fields = [
839 CPFTLPSReferral.lps_division,
840 CPFTLPSReferral.referral_date_time,
841 CPFTLPSReferral.referral_priority,
842 p1.c.surname,
843 p1.c.forename,
844 p1.c.dob,
845 i1.c.idnum_value.label(desc),
846 CPFTLPSReferral.patient_location,
847 ]
848 select_from = p1.join(CPFTLPSReferral.__table__, and_(
849 p1.c._current == True, # noqa: E712
850 CPFTLPSReferral.patient_id == p1.c.id,
851 CPFTLPSReferral._device_id == p1.c._device_id,
852 CPFTLPSReferral._era == p1.c._era,
853 CPFTLPSReferral._current == True,
854 ))
855 select_from = select_from.join(i1, and_(
856 i1.c.patient_id == p1.c.id,
857 i1.c._device_id == p1.c._device_id,
858 i1.c._era == p1.c._era,
859 i1.c._current == True, # noqa: E712
860 ))
861 wheres = [
862 i1.c.which_idnum == which_idnum,
863 ]
864 if not req.user.superuser:
865 # Restrict to accessible groups
866 wheres.append(CPFTLPSReferral._group_id.in_(group_ids))
868 # Step 2: not yet discharged
869 p2 = Patient.__table__.alias("p2")
870 i2 = PatientIdNum.__table__.alias("i2")
871 discharge = (
872 select(['*'])
873 .select_from(
874 p2.join(CPFTLPSDischarge.__table__, and_(
875 p2.c._current == True, # noqa: E712
876 CPFTLPSDischarge.patient_id == p2.c.id,
877 CPFTLPSDischarge._device_id == p2.c._device_id,
878 CPFTLPSDischarge._era == p2.c._era,
879 CPFTLPSDischarge._current == True,
880 )).join(i2, and_(
881 i2.c.patient_id == p2.c.id,
882 i2.c._device_id == p2.c._device_id,
883 i2.c._era == p2.c._era,
884 i2.c._current == True,
885 ))
886 )
887 .where(and_(
888 # Link on ID to main query: same patient
889 i2.c.which_idnum == which_idnum,
890 i2.c.idnum_value == i1.c.idnum_value,
891 # Discharge later than referral
892 (CPFTLPSDischarge.discharge_date >=
893 CPFTLPSReferral.referral_date_time),
894 ))
895 ) # nopep8
896 if not req.user.superuser:
897 # Restrict to accessible groups
898 discharge = discharge.where(
899 CPFTLPSDischarge._group_id.in_(group_ids))
901 wheres.append(~exists(discharge))
903 # Finish up
904 order_by = [
905 CPFTLPSReferral.lps_division,
906 CPFTLPSReferral.referral_date_time,
907 CPFTLPSReferral.referral_priority,
908 ]
909 query = select(select_fields) \
910 .select_from(select_from) \
911 .where(and_(*wheres)) \
912 .order_by(*order_by)
913 return query
916class LPSReportReferredNotClerkedOrDischarged(Report):
917 # noinspection PyMethodParameters
918 @classproperty
919 def report_id(cls) -> str:
920 return "cpft_lps_referred_not_subsequently_clerked_or_discharged"
922 @classmethod
923 def title(cls, req: "CamcopsRequest") -> str:
924 _ = req.gettext
925 return _("CPFT LPS – referred but not yet fully assessed or discharged") # noqa
927 # noinspection PyMethodParameters
928 @classproperty
929 def superuser_only(cls) -> bool:
930 return False
932 @staticmethod
933 def get_paramform_schema_class() -> Type[ReportParamSchema]:
934 return LPSReportSchema
936 # noinspection PyProtectedMember
937 def get_query(self, req: CamcopsRequest) -> SelectBase:
938 which_idnum = req.get_int_param(ViewParam.WHICH_IDNUM, 1)
939 if which_idnum is None:
940 raise exc.HTTPBadRequest(
941 f"{ViewParam.WHICH_IDNUM} not specified")
943 group_ids = req.user.ids_of_groups_user_may_report_on
945 # Step 1: link referral and patient
946 # noinspection PyUnresolvedReferences
947 p1 = Patient.__table__.alias("p1")
948 # noinspection PyUnresolvedReferences
949 i1 = PatientIdNum.__table__.alias("i1")
950 desc = req.get_id_shortdesc(which_idnum)
951 select_fields = [
952 CPFTLPSReferral.lps_division,
953 CPFTLPSReferral.referral_date_time,
954 CPFTLPSReferral.referral_priority,
955 p1.c.surname,
956 p1.c.forename,
957 p1.c.dob,
958 i1.c.idnum_value.label(desc),
959 CPFTLPSReferral.patient_location,
960 ]
961 # noinspection PyUnresolvedReferences
962 select_from = p1.join(CPFTLPSReferral.__table__, and_(
963 p1.c._current == True, # noqa: E712
964 CPFTLPSReferral.patient_id == p1.c.id,
965 CPFTLPSReferral._device_id == p1.c._device_id,
966 CPFTLPSReferral._era == p1.c._era,
967 CPFTLPSReferral._current == True,
968 ))
969 select_from = select_from.join(i1, and_(
970 i1.c.patient_id == p1.c.id,
971 i1.c._device_id == p1.c._device_id,
972 i1.c._era == p1.c._era,
973 i1.c._current == True, # noqa: E712
974 )) # nopep8
975 wheres = [
976 i1.c.which_idnum == which_idnum,
977 ]
978 if not req.user.superuser:
979 # Restrict to accessible groups
980 wheres.append(CPFTLPSReferral._group_id.in_(group_ids))
982 # Step 2: not yet discharged
983 # noinspection PyUnresolvedReferences
984 p2 = Patient.__table__.alias("p2")
985 # noinspection PyUnresolvedReferences
986 i2 = PatientIdNum.__table__.alias("i2")
987 # noinspection PyUnresolvedReferences
988 discharge = (
989 select(['*'])
990 .select_from(
991 p2.join(CPFTLPSDischarge.__table__, and_(
992 p2.c._current == True, # noqa: E712
993 CPFTLPSDischarge.patient_id == p2.c.id,
994 CPFTLPSDischarge._device_id == p2.c._device_id,
995 CPFTLPSDischarge._era == p2.c._era,
996 CPFTLPSDischarge._current == True,
997 )).join(i2, and_(
998 i2.c.patient_id == p2.c.id,
999 i2.c._device_id == p2.c._device_id,
1000 i2.c._era == p2.c._era,
1001 i2.c._current == True,
1002 ))
1003 )
1004 .where(and_(
1005 # Link on ID to main query: same patient
1006 i2.c.which_idnum == which_idnum,
1007 i2.c.idnum_value == i1.c.idnum_value,
1008 # Discharge later than referral
1009 (CPFTLPSDischarge.discharge_date >=
1010 CPFTLPSReferral.referral_date_time),
1011 ))
1012 ) # nopep8
1013 if not req.user.superuser:
1014 # Restrict to accessible groups
1015 discharge = discharge.where(
1016 CPFTLPSDischarge._group_id.in_(group_ids))
1017 wheres.append(~exists(discharge))
1019 # Step 3: not yet clerked
1020 # noinspection PyUnresolvedReferences
1021 p3 = Patient.__table__.alias("p3")
1022 # noinspection PyUnresolvedReferences
1023 i3 = PatientIdNum.__table__.alias("i3")
1024 # noinspection PyUnresolvedReferences
1025 clerking = (
1026 select(['*'])
1027 .select_from(
1028 p3.join(PsychiatricClerking.__table__, and_(
1029 p3.c._current == True, # noqa: E712
1030 PsychiatricClerking.patient_id == p3.c.id,
1031 PsychiatricClerking._device_id == p3.c._device_id,
1032 PsychiatricClerking._era == p3.c._era,
1033 PsychiatricClerking._current == True,
1034 )).join(i3, and_(
1035 i3.c.patient_id == p3.c.id,
1036 i3.c._device_id == p3.c._device_id,
1037 i3.c._era == p3.c._era,
1038 i3.c._current == True,
1039 ))
1040 )
1041 .where(and_(
1042 # Link on ID to main query: same patient
1043 i3.c.which_idnum == which_idnum,
1044 i3.c.idnum_value == i1.c.idnum_value,
1045 # Discharge later than referral
1046 (PsychiatricClerking.when_created >=
1047 CPFTLPSReferral.referral_date_time),
1048 ))
1049 ) # nopep8
1050 if not req.user.superuser:
1051 # Restrict to accessible groups
1052 clerking = clerking.where(
1053 PsychiatricClerking._group_id.in_(group_ids))
1054 wheres.append(~exists(clerking))
1056 # Finish up
1057 order_by = [
1058 CPFTLPSReferral.lps_division,
1059 CPFTLPSReferral.referral_date_time,
1060 CPFTLPSReferral.referral_priority,
1061 ]
1062 query = (
1063 select(select_fields)
1064 .select_from(select_from)
1065 .where(and_(*wheres))
1066 .order_by(*order_by)
1067 )
1068 return query