Source code for camcops_server.tasks.apeq_cpft_perinatal

#!/usr/bin/env python

"""
camcops_server/tasks/apeq_cpft_perinatal.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 Dict, List, Optional, Tuple, Type

from cardinal_pythonlib.classes import classproperty

from pyramid.renderers import render_to_response
from pyramid.response import Response
from sqlalchemy.sql.expression import and_, column, select
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import Integer, UnicodeText

from camcops_server.cc_modules.cc_constants import CssClass
from camcops_server.cc_modules.cc_html import tr_qa
from camcops_server.cc_modules.cc_pyramid import ViewParam
from camcops_server.cc_modules.cc_report import (
    DateTimeFilteredReportMixin,
    PercentageSummaryReportMixin,
    Report,
)
from camcops_server.cc_modules.cc_request import CamcopsRequest
from camcops_server.cc_modules.cc_sqla_coltypes import (
    CamcopsColumn,
    ZERO_TO_FIVE_CHECKER,
    ZERO_TO_TWO_CHECKER,
)
from camcops_server.cc_modules.cc_task import Task
from camcops_server.cc_modules.cc_spreadsheet import SpreadsheetPage


# =============================================================================
# APEQCPFTPerinatal
# =============================================================================


[docs]class APEQCPFTPerinatal(Task): """ Server implementation of the APEQ-CPFT-Perinatal task. """ __tablename__ = "apeq_cpft_perinatal" shortname = "APEQ-CPFT-Perinatal" FIRST_MAIN_Q = 1 LAST_MAIN_Q = 6 FN_QPREFIX = "q" MAIN_EXPLANATION = " (0 no, 1 yes to some extent, 2 yes)" q1 = CamcopsColumn( "q1", Integer, permitted_value_checker=ZERO_TO_TWO_CHECKER, comment="Q1. Treated with respect/dignity" + MAIN_EXPLANATION, ) q2 = CamcopsColumn( "q2", Integer, permitted_value_checker=ZERO_TO_TWO_CHECKER, comment="Q2. Felt listened to" + MAIN_EXPLANATION, ) q3 = CamcopsColumn( "q3", Integer, permitted_value_checker=ZERO_TO_TWO_CHECKER, comment="Q3. Needs were understood" + MAIN_EXPLANATION, ) q4 = CamcopsColumn( "q4", Integer, permitted_value_checker=ZERO_TO_TWO_CHECKER, comment="Q4. Given info about team" + MAIN_EXPLANATION, ) q5 = CamcopsColumn( "q5", Integer, permitted_value_checker=ZERO_TO_TWO_CHECKER, comment="Q5. Family considered/included" + MAIN_EXPLANATION, ) q6 = CamcopsColumn( "q6", Integer, permitted_value_checker=ZERO_TO_TWO_CHECKER, comment="Q6. Views on treatment taken into account" + MAIN_EXPLANATION, ) ff_rating = CamcopsColumn( "ff_rating", Integer, permitted_value_checker=ZERO_TO_FIVE_CHECKER, comment="How likely to recommend service to friends and family " "(0 don't know, 1 extremely unlikely, 2 unlikely, " "3 neither likely nor unlikely, 4 likely, 5 extremely likely)", ) ff_why = Column( "ff_why", UnicodeText, comment="Why was friends/family rating given as it was?", ) comments = Column("comments", UnicodeText, comment="General comments") REQUIRED_FIELDS = ["q1", "q2", "q3", "q4", "q5", "q6", "ff_rating"]
[docs] @staticmethod def longname(req: "CamcopsRequest") -> str: _ = req.gettext return _( "Assessment Patient Experience Questionnaire for " "CPFT Perinatal Services" )
[docs] def is_complete(self) -> bool: return self.all_fields_not_none(self.REQUIRED_FIELDS)
[docs] def get_task_html(self, req: CamcopsRequest) -> str: options_main = {None: "?"} # type: Dict[Optional[int], str] for o in range(0, 2 + 1): options_main[o] = self.wxstring(req, f"main_a{o}") options_ff = {None: "?"} # type: Dict[Optional[int], str] for o in range(0, 5 + 1): options_ff[o] = self.wxstring(req, f"ff_a{o}") qlines = [] # type: List[str] for qnum in range(self.FIRST_MAIN_Q, self.LAST_MAIN_Q + 1): xstring_attr_name = f"q{qnum}" qlines.append( tr_qa( self.wxstring(req, xstring_attr_name), options_main.get(getattr(self, xstring_attr_name)), ) ) q_a = "".join(qlines) return f""" <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {self.get_is_complete_tr(req)} </table> </div> <table class="{CssClass.TASKDETAIL}"> <tr> <th width="60%">Question</th> <th width="40%">Answer</th> </tr> {q_a} {tr_qa(self.wxstring(req, "q_ff_rating"), options_ff.get(self.ff_rating))} {tr_qa(self.wxstring(req, "q_ff_why"), self.ff_why or "")} {tr_qa(self.wxstring(req, "q_comments"), self.comments or "")} </table> """
def get_main_options(self, req: "CamcopsRequest") -> List[str]: options = [] for n in range(0, 2 + 1): options.append(self.wxstring(req, f"main_a{n}")) return options def get_ff_options(self, req: "CamcopsRequest") -> List[str]: options = [] for n in range(0, 5 + 1): options.append(self.wxstring(req, f"ff_a{n}")) return options
# ============================================================================= # Reports # =============================================================================
[docs]class APEQCPFTPerinatalReport( DateTimeFilteredReportMixin, Report, PercentageSummaryReportMixin ): """ Provides a summary of each question, x% of people said each response etc. Then a summary of the comments. """ COL_Q = 0 COL_TOTAL = 1 COL_RESPONSE_START = 2 COL_FF_WHY = 1
[docs] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.task = APEQCPFTPerinatal() # dummy task, never written to DB
[docs] @classproperty def task_class(self) -> Type["Task"]: return APEQCPFTPerinatal
# noinspection PyMethodParameters @classproperty def report_id(cls) -> str: return "apeq_cpft_perinatal" @classmethod def title(cls, req: "CamcopsRequest") -> str: _ = req.gettext return _("APEQ CPFT Perinatal — Question summaries") # noinspection PyMethodParameters @classproperty def superuser_only(cls) -> bool: return False @classmethod def get_specific_http_query_keys(cls) -> List[str]: return [ViewParam.START_DATETIME, ViewParam.END_DATETIME] def render_html(self, req: "CamcopsRequest") -> Response: cell_format = "{0:.1f}%" return render_to_response( "apeq_cpft_perinatal_report.mako", dict( title=self.title(req), report_id=self.report_id, start_datetime=self.start_datetime, end_datetime=self.end_datetime, main_column_headings=self._get_main_column_headings(req), main_rows=self._get_main_rows(req, cell_format=cell_format), ff_column_headings=self._get_ff_column_headings(req), ff_rows=self._get_ff_rows(req, cell_format=cell_format), ff_why_rows=self._get_ff_why_rows(req), comments=self._get_comments(req), ), request=req, ) def get_spreadsheet_pages( self, req: "CamcopsRequest" ) -> List[SpreadsheetPage]: _ = req.gettext main_page = self.get_spreadsheet_page( name=_("Main questions"), column_names=self._get_main_column_headings(req), rows=self._get_main_rows(req), ) ff_page = self.get_spreadsheet_page( name=_("Friends and family question"), column_names=self._get_ff_column_headings(req), rows=self._get_ff_rows(req), ) ff_why_page = self.get_spreadsheet_page( name=_("Reasons given for the above responses"), column_names=[_("Response"), _("Reason")], rows=self._get_ff_why_rows(req), ) comments_page = self.get_spreadsheet_page( name=_("Comments"), column_names=[_("Comment")], rows=self._get_comment_rows(req), ) return [main_page, ff_page, ff_why_page, comments_page] def _get_main_column_headings(self, req: "CamcopsRequest") -> List[str]: _ = req.gettext names = [ _("Question"), _("Total responses"), ] + self.task.get_main_options(req) return names def _get_main_rows( self, req: "CamcopsRequest", cell_format: str = "{}" ) -> List[List[str]]: """ Percentage of people who answered x for each question """ column_dict = {} qnums = range(self.task.FIRST_MAIN_Q, self.task.LAST_MAIN_Q + 1) for qnum in qnums: column_name = f"{self.task.FN_QPREFIX}{qnum}" column_dict[column_name] = self.task.wxstring(req, column_name) return self.get_percentage_summaries( req, column_dict=column_dict, num_answers=3, cell_format=cell_format, ) def _get_ff_column_headings(self, req: "CamcopsRequest") -> List[str]: _ = req.gettext return [ _("Question"), _("Total responses"), ] + self.task.get_ff_options(req) def _get_ff_rows( self, req: "CamcopsRequest", cell_format: str = "{}" ) -> List[List[str]]: """ Percentage of people who answered x for the friends/family question """ return self.get_percentage_summaries( req, column_dict={ "ff_rating": self.task.wxstring( req, f"{self.task.FN_QPREFIX}_ff_rating" ) }, num_answers=6, cell_format=cell_format, ) def _get_ff_why_rows(self, req: "CamcopsRequest") -> List[List[str]]: """ Reasons for giving a particular answer to the friends/family question """ options = self.task.get_ff_options(req) wheres = [ column("ff_rating").isnot(None), column("ff_why").isnot(None), ] self.add_task_report_filters(wheres) # noinspection PyUnresolvedReferences query = ( select([column("ff_rating"), column("ff_why")]) .select_from(self.task.__table__) .where(and_(*wheres)) .order_by("ff_why") ) rows = [] for result in req.dbsession.execute(query).fetchall(): rows.append([options[result[0]], result[1]]) return rows def _get_comment_rows(self, req: "CamcopsRequest") -> List[Tuple[str]]: """ A list of all the additional comments, as rows. """ wheres = [column("comments").isnot(None)] self.add_task_report_filters(wheres) # noinspection PyUnresolvedReferences query = ( select([column("comments")]) .select_from(self.task.__table__) .where(and_(*wheres)) ) comment_rows = [] for result in req.dbsession.execute(query).fetchall(): comment_rows.append(result) return comment_rows def _get_comments(self, req: "CamcopsRequest") -> List[str]: """ A list of all the additional comments. """ return [x[0] for x in self._get_comment_rows(req)]