15.1.670. tablet_qt/tasks/dad.cpp

/*
    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/>.
*/

#include "dad.h"

#include "common/textconst.h"
#include "maths/mathfunc.h"
#include "questionnairelib/commonoptions.h"
#include "questionnairelib/questionnaire.h"
#include "questionnairelib/qugridcell.h"
#include "questionnairelib/qugridcontainer.h"
#include "questionnairelib/qumcq.h"
#include "questionnairelib/qutext.h"
#include "tasklib/taskfactory.h"
#include "tasklib/taskregistrar.h"
using mathfunc::noneNull;

const QString Dad::DAD_TABLENAME("dad");

const int YES = 1;
const int NO = 0;
const int NA = -99;
const QString HYGIENE("hygiene");
const QString DRESSING("dressing");
const QString CONTINENCE("continence");
const QString EATING("eating");
const QString MEALPREP("mealprep");
const QString TELEPHONE("telephone");
const QString OUTING("outing");
const QString FINANCE("finance");
const QString MEDICATIONS("medications");
const QString LEISURE("leisure");
const QStringList GROUPS{
    HYGIENE,
    DRESSING,
    CONTINENCE,
    EATING,
    MEALPREP,
    TELEPHONE,
    OUTING,
    FINANCE,
    MEDICATIONS,
    LEISURE,
};
const QString INIT("init");
const QString PLAN("plan");
const QString EXEC("exec");
const QStringList 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",
};
const int LEFTCOL_STRETCH = 1;
const int RIGHTCOL_STRETCH = 2;

void initializeDad(TaskFactory& factory)
{
    static TaskRegistrar<Dad> registered(factory);
}

Dad::Dad(CamcopsApp& app, DatabaseManager& db, const int load_pk) :
    Task(app, db, DAD_TABLENAME, false, true, true)  // ... anon, clin, resp
{
    for (const QString& item : ITEMS) {
        addField(item, QMetaType::fromType<int>());
    }

    load(load_pk);  // MUST ALWAYS CALL from derived Task constructor.
}

// ============================================================================
// Class info
// ============================================================================

QString Dad::shortname() const
{
    return "DAD";
}

QString Dad::longname() const
{
    return tr("Disability Assessment for Dementia");
}

QString Dad::description() const
{
    return tr("40-item clinician-administered, carer-rated scale.");
}

// ============================================================================
// Instance info
// ============================================================================

bool Dad::isComplete() const
{
    return noneNull(values(ITEMS));
}

QStringList Dad::summary() const
{
    QStringList lines;
    lines.append("Total: " + getScore(ITEMS) + ".");
    lines.append(
        "BADL ACTIVITIES: "
        "hygiene "
        + getScore(getItemsActivity(HYGIENE)) + "; dressing "
        + getScore(getItemsActivity(DRESSING)) + "; continence "
        + getScore(getItemsActivity(CONTINENCE)) + "; eating "
        + getScore(getItemsActivity(EATING)) + "."
    );
    lines.append(
        "BADL OVERALL: "
        + getScore(getItemsActivities({HYGIENE, DRESSING, CONTINENCE, EATING}))
        + "."
    );
    lines.append(
        "IADL ACTIVITIES: "
        "mealprep "
        + getScore(getItemsActivity(MEALPREP)) + "; telephone "
        + getScore(getItemsActivity(TELEPHONE)) + "; outing "
        + getScore(getItemsActivity(OUTING)) + "; finance "
        + getScore(getItemsActivity(FINANCE)) + "; medications "
        + getScore(getItemsActivity(MEDICATIONS)) + "; leisure "
        + getScore(getItemsActivity(LEISURE)) + "."
    );
    lines.append(
        "BADL OVERALL: "
        + getScore(getItemsActivities(
            {MEALPREP, TELEPHONE, OUTING, FINANCE, MEDICATIONS, LEISURE}
        ))
        + "."
    );
    lines.append(
        "PHASES: "
        "initiation "
        + getScore(getItemsPhase(INIT)) + "; planning/organisation "
        + getScore(getItemsPhase(PLAN)) + "; execution/performance "
        + getScore(getItemsPhase(EXEC)) + "."
    );
    return lines;
}

QStringList Dad::detail() const
{
    return completenessInfo() + summary();
}

OpenableWidget* Dad::editor(const bool read_only)
{
    const NameValueOptions y_n_na_options{
        {CommonOptions::yes(), YES},
        {CommonOptions::no(), NO},
        {TextConst::notApplicable(), NA},
    };

    QuPagePtr page1 = getClinicianAndRespondentDetailsPage(false);

    QVector<QuElement*> elements{
        (new QuText(
             xstring("instruction_1") + " " + getPatientName() + " "
             + xstring("instruction_2")
         ))
            ->setBold(),
    };
    for (const QString& groupname : GROUPS) {
        elements.append(
            (new QuText(xstring(groupname)))->setBold()->setItalic()
        );
        auto grid = new QuGridContainer();
        int row = 0;
        grid->setColumnStretch(0, LEFTCOL_STRETCH);
        grid->setColumnStretch(1, RIGHTCOL_STRETCH);
        for (const QString& itemname : getItemsActivity(groupname)) {
            grid->addCell(QuGridCell(new QuText(xstring(itemname)), row, 0));
            grid->addCell(QuGridCell(
                (new QuMcq(fieldRef(itemname), y_n_na_options))
                    ->setHorizontal(true),
                row,
                1
            ));
            ++row;
        }
        elements.append(grid);
    }

    QuPagePtr page2((new QuPage(elements))->setTitle(longname()));

    auto questionnaire = new Questionnaire(m_app, {page1, page2});
    questionnaire->setType(QuPage::PageType::Clinician);
    questionnaire->setReadOnly(read_only);
    return questionnaire;
}

// ============================================================================
// Task-specific calculations
// ============================================================================

QStringList Dad::getItemsActivity(const QString& activity) const
{
    QStringList activity_items;
    for (const QString& item : ITEMS) {
        if (item.startsWith(activity)) {
            activity_items.append(item);
        }
    }
    return activity_items;
}

QStringList Dad::getItemsActivities(const QStringList& activities) const
{
    QStringList activity_items;
    for (const QString& item : ITEMS) {
        for (const QString& activity : activities) {
            if (item.startsWith(activity)) {
                activity_items.append(item);
            }
        }
    }
    return activity_items;
}

QStringList Dad::getItemsPhase(const QString& phase) const
{
    QStringList phase_items;
    for (const QString& item : ITEMS) {
        if (item.contains(phase)) {
            phase_items.append(item);
        }
    }
    return phase_items;
}

QString Dad::getScore(const QStringList& fieldnames) const
{
    const QVector<QVariant> v = values(fieldnames);
    const int score = mathfunc::countWhere(v, QVector<QVariant>{YES});
    const int possible
        = mathfunc::countWhereNot(v, QVector<QVariant>{QVariant(), NA});
    return mathfunc::scoreString(score, possible);
}