"""
camcops_server/tasks/dad.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/>.
===============================================================================
"""
from typing import Any, Dict, Iterable, List, Tuple, Type
from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.sql.sqltypes import Integer
from camcops_server.cc_modules.cc_constants import (
CssClass,
DATA_COLLECTION_UNLESS_UPGRADED_DIV,
)
from camcops_server.cc_modules.cc_html import (
answer,
subheading_spanning_two_columns,
tr,
)
from camcops_server.cc_modules.cc_request import CamcopsRequest
from camcops_server.cc_modules.cc_sqla_coltypes import (
CamcopsColumn,
PermittedValueChecker,
)
from camcops_server.cc_modules.cc_summaryelement import SummaryElement
from camcops_server.cc_modules.cc_task import (
Task,
TaskHasClinicianMixin,
TaskHasPatientMixin,
TaskHasRespondentMixin,
)
YES = 1
NO = 0
NA = -99
YN_NA_CHECKER = PermittedValueChecker(permitted_values=[YES, NO, NA])
# =============================================================================
# DAD
# =============================================================================
class DadMetaclass(DeclarativeMeta):
# noinspection PyInitNewSignature
def __init__(
cls: Type["Dad"],
name: str,
bases: Tuple[Type, ...],
classdict: Dict[str, Any],
) -> None:
explan = f" ({YES} yes, {NO} no, {NA} not applicable)"
for colname in cls.ITEMS:
setattr(
cls,
colname,
CamcopsColumn(
colname,
Integer,
permitted_value_checker=YN_NA_CHECKER,
comment=colname + explan,
),
)
super().__init__(name, bases, classdict)
[docs]class Dad(
TaskHasPatientMixin,
TaskHasRespondentMixin,
TaskHasClinicianMixin,
Task,
metaclass=DadMetaclass,
):
"""
Server implementation of the DAD task.
"""
__tablename__ = "dad"
shortname = "DAD"
GROUPS = [
"hygiene",
"dressing",
"continence",
"eating",
"mealprep",
"telephone",
"outing",
"finance",
"medications",
"leisure",
]
ITEMS = [
"hygiene_init_wash",
"hygiene_init_teeth",
"hygiene_init_hair",
"hygiene_plan_wash",
"hygiene_exec_wash",
"hygiene_exec_hair",
"hygiene_exec_teeth",
"dressing_init_dress",
"dressing_plan_clothing",
"dressing_plan_order",
"dressing_exec_dress",
"dressing_exec_undress",
"continence_init_toilet",
"continence_exec_toilet",
"eating_init_eat",
"eating_plan_utensils",
"eating_exec_eat",
"mealprep_init_meal",
"mealprep_plan_meal",
"mealprep_exec_meal",
"telephone_init_phone",
"telephone_plan_dial",
"telephone_exec_conversation",
"telephone_exec_message",
"outing_init_outing",
"outing_plan_outing",
"outing_exec_reach_destination",
"outing_exec_mode_transportation",
"outing_exec_return_with_shopping",
"finance_init_interest",
"finance_plan_pay_bills",
"finance_plan_organise_correspondence",
"finance_exec_handle_money",
"medications_init_medication",
"medications_exec_take_medications",
"leisure_init_interest_leisure",
"leisure_init_interest_chores",
"leisure_plan_chores",
"leisure_exec_complete_chores",
"leisure_exec_safe_at_home",
]
[docs] @staticmethod
def longname(req: "CamcopsRequest") -> str:
_ = req.gettext
return _("Disability Assessment for Dementia")
[docs] def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
d = self.get_score_dict()
s = self.standard_task_summary_fields()
for item in d:
s.extend(
[
SummaryElement(
name=item + "_n",
coltype=Integer(),
value=d[item][0],
comment=item + " (numerator)",
),
SummaryElement(
name=item + "_d",
coltype=Integer(),
value=d[item][1],
comment=item + " (denominator)",
),
]
)
return s
# noinspection PyMethodOverriding
[docs] @staticmethod
def is_complete() -> bool:
return True
@classmethod
def get_items_activity(cls, activity: str) -> List[str]:
return [item for item in cls.ITEMS if item.startswith(activity)]
@classmethod
def get_items_activities(cls, activities: Iterable[str]) -> List[str]:
return [
item
for item in cls.ITEMS
if any(item.startswith(activity) for activity in activities)
]
@classmethod
def get_items_phase(cls, phase: str) -> List[str]:
return [item for item in cls.ITEMS if phase in item]
def get_score(self, fields: List[str]) -> Tuple[int, int]:
score = self.count_where(fields, [YES])
possible = self.count_wherenot(fields, [None, NA])
return score, possible
def get_score_dict(self) -> Dict:
total = self.get_score(self.ITEMS)
hygiene = self.get_score(self.get_items_activity("hygiene"))
dressing = self.get_score(self.get_items_activity("dressing"))
continence = self.get_score(self.get_items_activity("continence"))
eating = self.get_score(self.get_items_activity("eating"))
badl = self.get_score(
self.get_items_activities(
["hygiene", "dressing", "continence", "eating"]
)
)
mealprep = self.get_score(self.get_items_activity("mealprep"))
telephone = self.get_score(self.get_items_activity("telephone"))
outing = self.get_score(self.get_items_activity("outing"))
finance = self.get_score(self.get_items_activity("finance"))
medications = self.get_score(self.get_items_activity("medications"))
leisure = self.get_score(self.get_items_activity("leisure"))
iadl = self.get_score(
self.get_items_activities(
[
"mealprep",
"telephone",
"outing",
"finance",
"medications",
"leisure",
]
)
)
initiation = self.get_score(self.get_items_phase("init"))
planning = self.get_score(self.get_items_phase("plan"))
execution = self.get_score(self.get_items_phase("exec"))
# n for numerator, d for denominator
return dict(
total=total,
hygiene=hygiene,
dressing=dressing,
continence=continence,
eating=eating,
badl=badl,
mealprep=mealprep,
telephone=telephone,
outing=outing,
finance=finance,
medications=medications,
leisure=leisure,
iadl=iadl,
initiation=initiation,
planning=planning,
execution=execution,
)
@staticmethod
def report_score(score_tuple: Tuple[int, int]) -> str:
return f"{answer(score_tuple[0])} / {score_tuple[1]}"
def report_answer(self, field: str) -> str:
value = getattr(self, field)
if value == YES:
text = "Yes (1)"
elif value == NO:
text = "No (0)"
elif value == NA:
text = "N/A"
else:
text = None
return answer(text)
[docs] def get_task_html(self, req: CamcopsRequest) -> str:
d = self.get_score_dict()
h = f"""
<div class="{CssClass.SUMMARY}">
<table class="{CssClass.SUMMARY}">
{self.get_is_complete_tr(req)}
<tr>
<td>Total</td>
<td>{self.report_score(d['total'])}</td>
</tr>
<tr>
<td>Activity: hygiene</td>
<td>{self.report_score(d['hygiene'])}</td>
</tr>
<tr>
<td>Activity: dressing</td>
<td>{self.report_score(d['dressing'])}</td>
</tr>
<tr>
<td>Activity: continence</td>
<td>{self.report_score(d['continence'])}</td>
</tr>
<tr>
<td>Activity: eating</td>
<td>{self.report_score(d['eating'])}</td>
</tr>
<tr>
<td>Basic activities of daily living (BADLs) (hygiene,
dressing, continence, eating)</td>
<td>{self.report_score(d['badl'])}</td>
</tr>
<tr>
<td>Activity: meal preparation</td>
<td>{self.report_score(d['mealprep'])}</td>
</tr>
<tr>
<td>Activity: telephone</td>
<td>{self.report_score(d['telephone'])}</td>
</tr>
<tr>
<td>Activity: outings</td>
<td>{self.report_score(d['outing'])}</td>
</tr>
<tr>
<td>Activity: finance</td>
<td>{self.report_score(d['finance'])}</td>
</tr>
<tr>
<td>Activity: medications</td>
<td>{self.report_score(d['medications'])}</td>
</tr>
<tr>
<td>Activity: leisure</td>
<td>{self.report_score(d['leisure'])}</td>
</tr>
<tr>
<td>Instrumental activities of daily living (IADLs)
(meal prep., telephone, outings, finance, medications,
leisure)</td>
<td>{self.report_score(d['iadl'])}</td>
</tr>
<tr>
<td>Phase: initiation</td>
<td>{self.report_score(d['initiation'])}</td>
</tr>
<tr>
<td>Phase: planning/organisation</td>
<td>{self.report_score(d['planning'])}</td>
</tr>
<tr>
<td>Phase: execution/performance</td>
<td>{self.report_score(d['execution'])}</td>
</tr>
</table>
</div>
<table class="{CssClass.TASKDETAIL}">
<tr>
<th width="50%">Question (I = initiation, P = planning,
E = execution)</th>
<th width="50%">Answer</th>
</tr>
"""
for group in self.GROUPS:
h += subheading_spanning_two_columns(self.wxstring(req, group))
for item in self.ITEMS:
if not item.startswith(group):
continue
q = self.wxstring(req, item)
if "_init_" in item:
q += " (I)"
elif "_plan_" in item:
q += " (P)"
elif "_exec_" in item:
q += " (E)"
else:
# Shouldn't happen
q += " (?)"
h += tr(q, self.report_answer(item))
h += f"""
</table>
{DATA_COLLECTION_UNLESS_UPGRADED_DIV}
"""
return h