Coverage for tasks/ors.py: 59%
54 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
1"""
2camcops_server/tasks/ors.py
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
11 CamCOPS is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 CamCOPS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
24===============================================================================
26- By Joe Kearney, Rudolf Cardinal.
28"""
30import datetime
31from typing import List, Optional
33from sqlalchemy.orm import Mapped
34from sqlalchemy.sql.sqltypes import UnicodeText
36from camcops_server.cc_modules.cc_constants import CssClass
37from camcops_server.cc_modules.cc_html import tr_qa
38from camcops_server.cc_modules.cc_request import CamcopsRequest
39from camcops_server.cc_modules.cc_sqla_coltypes import (
40 mapped_camcops_column,
41 ZERO_TO_10_CHECKER,
42)
43from camcops_server.cc_modules.cc_summaryelement import SummaryElement
44from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin
47# =============================================================================
48# ORS
49# =============================================================================
52class Ors(TaskHasPatientMixin, Task): # type: ignore[misc]
53 """
54 Server implementation of the PHQ9 task.
55 """
57 __tablename__ = "ors"
58 shortname = "ORS"
59 provides_trackers = True
61 COMPLETED_BY_SELF = 0
62 COMPLETED_BY_OTHER = 1
64 VAS_MIN_INT = 0
65 VAS_MAX_INT = 10
67 q_session: Mapped[Optional[int]] = mapped_camcops_column(
68 comment="Session number"
69 )
70 q_date: Mapped[Optional[datetime.date]] = mapped_camcops_column(
71 comment="Session date"
72 )
73 q_who: Mapped[Optional[int]] = mapped_camcops_column(
74 comment="Completed by"
75 )
76 q_who_other: Mapped[Optional[str]] = mapped_camcops_column(
77 UnicodeText, comment="Completed by other: who?"
78 )
79 q_individual: Mapped[Optional[float]] = mapped_camcops_column(
80 comment="Individual rating (0-10, 10 better)",
81 permitted_value_checker=ZERO_TO_10_CHECKER,
82 )
83 q_interpersonal: Mapped[Optional[float]] = mapped_camcops_column(
84 comment="Interpersonal rating (0-10, 10 better)",
85 permitted_value_checker=ZERO_TO_10_CHECKER,
86 )
87 q_social: Mapped[Optional[float]] = mapped_camcops_column(
88 comment="Social rating (0-10, 10 better)",
89 permitted_value_checker=ZERO_TO_10_CHECKER,
90 )
91 q_overall: Mapped[Optional[float]] = mapped_camcops_column(
92 comment="Overall rating (0-10, 10 better)",
93 permitted_value_checker=ZERO_TO_10_CHECKER,
94 )
96 @staticmethod
97 def longname(req: "CamcopsRequest") -> str:
98 _ = req.gettext
99 return _("Outcome Rating Scale")
101 def is_complete(self) -> bool:
102 required_always = [
103 "q_session",
104 "q_date",
105 "q_who",
106 "q_individual",
107 "q_interpersonal",
108 "q_social",
109 "q_overall",
110 ]
111 for field in required_always:
112 if getattr(self, field) is None:
113 return False
114 if self.q_who == self.COMPLETED_BY_OTHER:
115 if not self.q_who_other:
116 return False
117 return True
119 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
120 return self.standard_task_summary_fields()
122 def who(self) -> str:
123 if self.q_who == self.COMPLETED_BY_OTHER:
124 return "Other: " + (self.q_who_other or "Unknown")
125 if self.q_who == self.COMPLETED_BY_SELF:
126 return "Patient"
127 return "Unknown"
129 def get_task_html(self, req: CamcopsRequest) -> str:
130 fields = ["q_individual", "q_interpersonal", "q_social", "q_overall"]
131 q_a = ""
132 for field in fields:
133 question = field.split("_")[1].capitalize()
134 q_a += tr_qa(question, getattr(self, field))
136 return f"""
137 <div class="{CssClass.SUMMARY}">
138 <table class="{CssClass.SUMMARY}">
139 {self.get_is_complete_tr(req)}
140 {tr_qa("Session number", self.q_session)}
141 {tr_qa("Completed by", self.who())}
142 </table>
143 </div>
144 <div class="{CssClass.EXPLANATION}">
145 Scores represent a selection on a scale from
146 {self.VAS_MIN_INT} to {self.VAS_MAX_INT}
147 ({self.VAS_MAX_INT} better). Scores reflect the patient’s
148 feelings about the indicated life areas over the past week.
149 </div>
150 <table class="{CssClass.TASKDETAIL}">
151 <tr>
152 <th width="60%">Question</th>
153 <th width="40%">Answer</th>
154 </tr>
155 {q_a}
156 </table>
157 <div class="{CssClass.FOOTNOTES}">
158 </div>
159 """