15.1.786. tablet_qt/tasks/panss.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 "panss.h"
#include "common/textconst.h"
#include "maths/mathfunc.h"
#include "lib/stringfunc.h"
#include "questionnairelib/questionnaire.h"
#include "questionnairelib/qumcqgrid.h"
#include "questionnairelib/qutext.h"
#include "tasklib/taskfactory.h"
#include "tasklib/taskregistrar.h"
using mathfunc::noneNull;
using mathfunc::sumInt;
using mathfunc::scorePhrase;
using mathfunc::totalScorePhrase;
using stringfunc::standardResult;
using stringfunc::strnum;
using stringfunc::strseq;

const int N_P = 7;
const int N_N = 7;
const int N_G = 16;
const int MAX_P = 49;
const int MAX_N = 49;
const int MAX_G = 112;
const int MAX_TOTAL = 210;
const QString P_PREFIX("p");
const QString N_PREFIX("n");
const QString G_PREFIX("g");

const QString Panss::PANSS_TABLENAME("panss");


void initializePanss(TaskFactory& factory)
{
    static TaskRegistrar<Panss> registered(factory);
}


Panss::Panss(CamcopsApp& app, DatabaseManager& db, const int load_pk) :
    Task(app, db, PANSS_TABLENAME, false, true, false)  // ... anon, clin, resp
{
    addFields(strseq(P_PREFIX, 1, N_P), QMetaType::fromType<int>());
    addFields(strseq(N_PREFIX, 1, N_N), QMetaType::fromType<int>());
    addFields(strseq(G_PREFIX, 1, N_G), QMetaType::fromType<int>());

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


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

QString Panss::shortname() const
{
    return "PANSS";
}


QString Panss::longname() const
{
    return tr("Positive and Negative Syndrome Scale");
}


QString Panss::description() const
{
    return tr("Scale for positive (7 items) and negative symptoms of "
              "schizophrenia (7 items), and general psychopathology "
              "(16 items).");
}


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

bool Panss::isComplete() const
{
    return noneNull(values(strseq(P_PREFIX, 1, N_P))) &&
            noneNull(values(strseq(N_PREFIX, 1, N_N))) &&
            noneNull(values(strseq(G_PREFIX, 1, N_G)));
}


QStringList Panss::summary() const
{
    const int p = getP();
    const int n = getN();
    const int g = getG();
    const int composite = p - n;
    const int total = p + g + n;
    return QStringList{
        scorePhrase(xstring("p"), p, MAX_P),
        scorePhrase(xstring("n"), n, MAX_N),
        scorePhrase(xstring("g"), g, MAX_G),
        standardResult(xstring("composite"), QString::number(composite)),
        totalScorePhrase(total, MAX_TOTAL),
    };
}


QStringList Panss::detail() const
{
    QStringList lines = completenessInfo();
    lines += fieldSummaries("p", "_s", " ", P_PREFIX, 1, N_P);
    lines += fieldSummaries("n", "_s", " ", N_PREFIX, 1, N_N);
    lines += fieldSummaries("g", "_s", " ", G_PREFIX, 1, N_G);
    lines.append("");
    lines += summary();
    return lines;
}


OpenableWidget* Panss::editor(const bool read_only)
{
    const NameValueOptions panss_options{
        {xstring("option1"), 1},
        {xstring("option2"), 2},
        {xstring("option3"), 3},
        {xstring("option4"), 4},
        {xstring("option5"), 5},
        {xstring("option6"), 6},
        {xstring("option7"), 7},
    };

    auto addqf = [this](QVector<QuestionWithOneField>& qf,
                        const QString& fieldprefix,
                        const QString& xstringprefix,
                        int q) -> void {
        qf.append(QuestionWithOneField(
                      xstring(xstringprefix + QString::number(q) + "_s"),
                      fieldRef(strnum(fieldprefix, q))));
    };
    auto boldtext = [](const QString& text) -> QuElement* {
        return (new QuText(text))->setBold(true);
    };

    QVector<QuestionWithOneField> p_qfields;
    QVector<QuestionWithOneField> n_qfields;
    QVector<QuestionWithOneField> g_qfields;
    for (int i = 1; i <= N_P; ++i) {
        addqf(p_qfields, P_PREFIX, "p", i);
    }
    for (int i = 1; i <= N_N; ++i) {
        addqf(n_qfields, N_PREFIX, "n", i);
    }
    for (int i = 1; i <= N_G; ++i) {
        addqf(g_qfields, G_PREFIX, "g", i);
    }

    QVector<QuPagePtr> pages;
    pages.append(getClinicianDetailsPage());

    pages.append(QuPagePtr((new QuPage{
        boldtext(TextConst::dataCollectionOnlyAnnouncement()),
        new QuMcqGrid(p_qfields, panss_options),
    })->setTitle(longname() + " (P)")));

    pages.append(QuPagePtr((new QuPage{
        boldtext(TextConst::dataCollectionOnlyAnnouncement()),
        new QuMcqGrid(n_qfields, panss_options),
    })->setTitle(longname() + " (N)")));

    pages.append(QuPagePtr((new QuPage{
        boldtext(TextConst::dataCollectionOnlyAnnouncement()),
        new QuMcqGrid(g_qfields, panss_options),
    })->setTitle(longname() + " (G)")));

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


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

int Panss::getP() const
{
    return sumInt(values(strseq(P_PREFIX, 1, N_P)));
}


int Panss::getN() const
{
    return sumInt(values(strseq(N_PREFIX, 1, N_N)));
}


int Panss::getG() const
{
    return sumInt(values(strseq(G_PREFIX, 1, N_G)));
}