"""
camcops_server/tasks/empsa.py
===============================================================================
Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
This file is part of CamCOPS.
CamCOPS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CamCOPS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
===============================================================================
Eating and Meal Preparation Skills Assessment (EMPSA)
"""
from typing import Optional, TYPE_CHECKING
import cardinal_pythonlib.rnc_web as ws
from cardinal_pythonlib.stringfunc import strseq
from sqlalchemy import UnicodeText
from sqlalchemy.orm import Mapped
from camcops_server.cc_modules.cc_constants import CssClass
from camcops_server.cc_modules.cc_html import answer, tr
from camcops_server.cc_modules.cc_sqla_coltypes import (
mapped_camcops_column,
ZERO_TO_10_CHECKER,
)
from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin
if TYPE_CHECKING:
from camcops_server.cc_modules.cc_request import CamcopsRequest
DP = 2
RANGE_SUFFIX = " (0 none - 10 total)"
[docs]class Empsa(TaskHasPatientMixin, Task): # type: ignore[misc]
__tablename__ = "empsa"
shortname = "EMPSA"
q1_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q1 ability (planning)" + RANGE_SUFFIX,
)
q1_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q1 motivation (planning)" + RANGE_SUFFIX,
)
q1_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q1 comments (planning)"
)
q2_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q2 ability (budget)" + RANGE_SUFFIX,
)
q2_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q2 motivation (budget)" + RANGE_SUFFIX,
)
q2_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q2 comments (budget)"
)
q3_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q3 ability (shopping)" + RANGE_SUFFIX,
)
q3_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q3 motivation (shopping)" + RANGE_SUFFIX,
)
q3_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q3 comments (shopping)"
)
q4_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q4 ability (cooking)" + RANGE_SUFFIX,
)
q4_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q4 motivation (cooking)" + RANGE_SUFFIX,
)
q4_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q4 comments (cooking)"
)
q5_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q5 ability (preparing)" + RANGE_SUFFIX,
)
q5_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q5 motivation (preparing)" + RANGE_SUFFIX,
)
q5_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q5 comments (preparing)"
)
q6_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q6 ability (portions)" + RANGE_SUFFIX,
)
q6_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q6 motivation (portions)" + RANGE_SUFFIX,
)
q6_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q6 comments (portions)"
)
q7_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q7 ability (throwing away)" + RANGE_SUFFIX,
)
q7_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q7 motivation (throwing away)" + RANGE_SUFFIX,
)
q7_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q7 comments (throwing away)"
)
q8_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q8 ability (difficult food)" + RANGE_SUFFIX,
)
q8_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q8 motivation (difficult food)" + RANGE_SUFFIX,
)
q8_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q8 comments (difficult food)"
)
q9_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q9 ability (normal pace)" + RANGE_SUFFIX,
)
q9_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q9 motivation (normal pace)" + RANGE_SUFFIX,
)
q9_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q9 comments (normal pace)"
)
q10_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q10 ability (others)" + RANGE_SUFFIX,
)
q10_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q10 motivation (others)" + RANGE_SUFFIX,
)
q10_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q10 comments (others)"
)
q11_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q11 ability (public)" + RANGE_SUFFIX,
)
q11_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q11 motivation (public)" + RANGE_SUFFIX,
)
q11_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q11 comments (public)"
)
q12_ability: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q12 ability (distress)" + RANGE_SUFFIX,
)
q12_motivation: Mapped[Optional[int]] = mapped_camcops_column(
permitted_value_checker=ZERO_TO_10_CHECKER,
comment="Q12 motivation (distress)" + RANGE_SUFFIX,
)
q12_comments: Mapped[Optional[str]] = mapped_camcops_column(
UnicodeText, comment="Q12 comments (distress)"
)
PREFIX = "q"
ABILITY_SUFFIX = "_ability"
MOTIVATION_SUFFIX = "_motivation"
COMMENTS_SUFFIX = "_comments"
ALL_ABILITY_FIELD_NAMES = strseq(PREFIX, 1, 12, ABILITY_SUFFIX)
ALL_MOTIVATION_FIELD_NAMES = strseq(PREFIX, 1, 12, MOTIVATION_SUFFIX)
ALL_MANDATORY_FIELD_NAMES = (
ALL_ABILITY_FIELD_NAMES + ALL_MOTIVATION_FIELD_NAMES
)
FIRST_Q = 1
LAST_Q = 12
MAX_SCORE = 10 # per question, or for subscales that are means
[docs] @staticmethod
def longname(req: "CamcopsRequest") -> str:
_ = req.gettext
return _("Eating and Meal Preparation Skills Assessment")
[docs] def is_complete(self) -> bool:
if self.any_fields_none(self.ALL_MANDATORY_FIELD_NAMES):
return False
return True
def ability_subscale(self) -> Optional[float]:
return self.mean_fields(self.ALL_ABILITY_FIELD_NAMES, ignorevalues=[])
def motivation_subscale(self) -> Optional[float]:
return self.mean_fields(
self.ALL_MOTIVATION_FIELD_NAMES, ignorevalues=[]
)
[docs] def get_task_html(self, req: "CamcopsRequest") -> str:
rows = self.get_task_html_rows(req)
html = """
<div class="{CssClass.SUMMARY}">
<table class="{CssClass.SUMMARY}">
{tr_is_complete}
{ability_subscale}
{motivation_subscale}
</table>
</div>
<table class="{CssClass.TASKDETAIL}">
{rows}
</table>
<div class="{CssClass.FOOTNOTES}">
[1] {ability_footnote}
[2] {motivation_footnote}
</div>
""".format(
CssClass=CssClass,
tr_is_complete=self.get_is_complete_tr(req),
ability_subscale=tr(
self.wxstring(req, "ability") + "<sup>[1]</sup>",
answer(
ws.number_to_dp(self.ability_subscale(), DP, default=None)
)
+ f" / {self.MAX_SCORE}",
),
motivation_subscale=tr(
self.wxstring(req, "motivation") + "<sup>[2]</sup>",
answer(
ws.number_to_dp(
self.motivation_subscale(), DP, default=None
),
)
+ f" / {self.MAX_SCORE}",
),
rows=rows,
ability_footnote=self.wxstring(req, "ability_footnote"),
motivation_footnote=self.wxstring(req, "motivation_footnote"),
)
return html
def get_task_html_rows(self, req: "CamcopsRequest") -> str:
task_text = self.xstring(req, "task")
ability_text = self.xstring(req, "ability")
motivation_text = self.xstring(req, "motivation")
comments_text = self.xstring(req, "comments")
header = f"""
<tr>
<th width="2%"></th>
<th width="41%">{task_text}</th>
<th width="8%">{ability_text}</th>
<th width="8%">{motivation_text}</th>
<th width="41%">{comments_text}</th>
</tr>
"""
return header + self.get_task_html_rows_for_range(
req, self.FIRST_Q, self.LAST_Q
)
def get_task_html_rows_for_range(
self, req: "CamcopsRequest", first_q: int, last_q: int
) -> str:
rows = ""
for q_num in range(first_q, last_q + 1):
q_str = f"{self.PREFIX}{q_num}"
ability_field = f"{q_str}{self.ABILITY_SUFFIX}"
motivation_field = f"{q_str}{self.MOTIVATION_SUFFIX}"
comments_field = f"{q_str}{self.COMMENTS_SUFFIX}"
question_cell = self.xstring(req, q_str)
rows += tr(
str(q_num),
question_cell,
answer(getattr(self, ability_field)),
answer(getattr(self, motivation_field)),
answer(getattr(self, comments_field), default=""),
)
return rows