15.1.590. tablet_qt/tasks/acefamily.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 "acefamily.h"

#include "lib/convert.h"
#include "lib/uifunc.h"
#include "lib/version.h"
#include "maths/mathfunc.h"
#include "questionnairelib/quboolean.h"
#include "questionnairelib/questionnaire.h"
#include "questionnairelib/quheading.h"
#include "questionnairelib/qutext.h"

using mathfunc::scoreStringWithPercent;


// ============================================================================
// Class data
// ============================================================================

const QString AceFamily::ACE3_TABLENAME(QStringLiteral("ace3"));

const QString AceFamily::TASK_DEFAULT_VERSION(QStringLiteral("A"));

// Shared field names and field prefixes
const QString AceFamily::FN_TASK_EDITION(QStringLiteral("task_edition"));
const QString
    AceFamily::FN_TASK_ADDRESS_VERSION(QStringLiteral("task_address_version"));
const QString AceFamily::FN_REMOTE_ADMINISTRATION(
    QStringLiteral("remote_administration")
);
const QString AceFamily::FN_AGE_FT_EDUCATION(
    QStringLiteral("age_at_leaving_full_time_education")
);
const QString AceFamily::FN_OCCUPATION(QStringLiteral("occupation"));
const QString AceFamily::FN_HANDEDNESS(QStringLiteral("handedness"));

const QString AceFamily::FP_ATTN_TIME(QStringLiteral("attn_time"));

const QString AceFamily::FP_MEM_REPEAT_ADDR_GENERIC(
    QStringLiteral("mem_repeat_address_trial%1_%2")
);
const QString AceFamily::FP_MEM_REPEAT_ADDR_TRIAL1(
    QStringLiteral("mem_repeat_address_trial1_")
);
const QString AceFamily::FP_MEM_REPEAT_ADDR_TRIAL2(
    QStringLiteral("mem_repeat_address_trial2_")
);
const QString AceFamily::FP_MEM_REPEAT_ADDR_TRIAL3(
    QStringLiteral("mem_repeat_address_trial3_")
);
const QString
    AceFamily::FP_MEM_RECALL_ADDRESS(QStringLiteral("mem_recall_address"));

const QString AceFamily::FN_FLUENCY_ANIMALS_SCORE(
    QStringLiteral("fluency_animals_score")
);

const QString AceFamily::FN_VSP_DRAW_CLOCK(QStringLiteral("vsp_draw_clock"));

const QString AceFamily::FN_PICTURE1_BLOBID(QStringLiteral("picture1_blobid"));
const QString AceFamily::FN_PICTURE2_BLOBID(QStringLiteral("picture2_blobid"));
const QString AceFamily::FN_COMMENTS(QStringLiteral("comments"));

const QString AceFamily::TAG_PG_PREAMBLE(QStringLiteral("pg_preamble"));
const QString AceFamily::TAG_EL_CHOOSE_TASK_VERSION(
    QStringLiteral("choose_addr_version")
);
const QString
    AceFamily::TAG_EL_SHOW_TASK_VERSION(QStringLiteral("show_addr_version"));
const QString AceFamily::TAG_REMOTE(QStringLiteral("remote_instr"));
const QString AceFamily::TAG_STANDARD(QStringLiteral("std_instr"));
const QString
    AceFamily::TAG_PG_ADDRESS_LEARNING_FAMOUS(QStringLiteral("pg_addr_learn"));
const QString
    AceFamily::TAG_PG_MEM_FREE_RECALL(QStringLiteral("pg_mem_free_recall"));

const QString AceFamily::X_MINI_ACE_SCORE(QStringLiteral("mini_ace_score"));


// ============================================================================
// Local data
// ============================================================================

const Version SERVER_ACE3_ADDRESS_VARIANT_VERSION(2, 4, 15);

// ============================================================================
// AceFamily
// ============================================================================

AceFamily::AceFamily(
    CamcopsApp& app,
    DatabaseManager& db,
    const QString& tablename,
    QObject* parent
) :
    Task(app, db, tablename, false, true, false, parent),
    // ... anon, clin, resp
    m_questionnaire(nullptr)
{
    // No fields. Subclasses should add those, and call load(load_pk).
}

Version AceFamily::minimumServerVersion() const
{
    // From v2.4.15 we support ACE-III versions A/B/C (address variations).
    return SERVER_ACE3_ADDRESS_VARIANT_VERSION;
}

bool AceFamily::isTaskProperlyCreatable(QString& why_not_creatable) const
{
    if (!isServerStringVersionEnough(
            SERVER_ACE3_ADDRESS_VARIANT_VERSION, why_not_creatable
        )) {
        return false;
    }
    if (!isAddressVersionInfoValid()) {
        why_not_creatable = tr(
            "Server strings are not providing valid information about which "
            "address versions are available. Try re-fetching server info."
        );
        return false;
    }
    return true;
}

QString AceFamily::xstringTaskname() const
{
    return ACE3_TABLENAME;
}

// ============================================================================
// Cosmetic support functions
// ============================================================================

QString AceFamily::scorePercent(int score, int out_of) const
{
    return ": " + scoreStringWithPercent(score, out_of) + ".";
}

// ============================================================================
// Task address version support functions
// ============================================================================

QStringList AceFamily::rawAddressVersionsAvailable() const
{
    const QString x = QString(QStringLiteral("task_address_versions"));
    const QString csv_data = xstring(x);
    return convert::csvStringToQStringList(csv_data);
}

bool AceFamily::isAddressVersionInfoValid(const QStringList& versions) const
{
    // Must be a sequence of capital letters like A, B, C, ...
    const int n = versions.size();
    if (n < 1 || n > 26) {
        return false;
    }
    int base = 'A';
    for (int i = 0; i < n; ++i) {
        const QString& v = versions[i];
        const char c = base + i;
        const QString expected(c);
        if (v != expected) {
            return false;
        }
    }
    return true;
}

bool AceFamily::isAddressVersionInfoValid() const
{
    const QStringList versions = rawAddressVersionsAvailable();
    return isAddressVersionInfoValid(versions);
}

QStringList AceFamily::addressVersionsAvailable() const
{
    const QStringList versions = rawAddressVersionsAvailable();
    if (isAddressVersionInfoValid(versions)) {
        return versions;
    }
    // Default for duff data:
    return QStringList{TASK_DEFAULT_VERSION};
}

QString AceFamily::targetAddressComponent(const int component) const
{
    Q_ASSERT(component >= 1 && component <= N_MEM_REPEAT_RECALL_ADDR);
    const QString task_address_version = taskAddressVersion();
    const QString x = QString(QStringLiteral("task_%1_target_address_%2"))
                          .arg(task_address_version)
                          .arg(component);
    return xstring(x);
}

// ============================================================================
// Automatic tag generation
// ============================================================================

QString AceFamily::tagAddressRegistration(int trial, int component) const
{
    return QString(QStringLiteral("addr_reg_%1_%2")).arg(trial).arg(component);
}

QString AceFamily::tagAddressFreeRecall(int component) const
{
    return QString(QStringLiteral("addr_recall_%1")).arg(component);
}

// ============================================================================
// Editor assistance functions
// ============================================================================

QuElement* AceFamily::textRaw(const QString& string) const
{
    return new QuText(string);
};

QuElement* AceFamily::text(const QString& stringname) const
{
    return textRaw(xstring(stringname));
};

QuElement* AceFamily::explanation(const QString& stringname) const
{
    return (new QuText(xstring(stringname)))->setItalic();
};

QuElement* AceFamily::stdExplan(const QString& stringname) const
{
    return explanation(stringname)->addTag(TAG_STANDARD);
};

QuElement* AceFamily::remExplan(const QString& stringname) const
{
    return explanation(stringname)->addTag(TAG_REMOTE);
};

QuElement* AceFamily::heading(const QString& stringname) const
{
    return new QuHeading(xstring(stringname));
};

QuElement* AceFamily::subheading(const QString& stringname) const
{
    return (new QuText(xstring(stringname)))->setBold()->setBig();
};

QuElement* AceFamily::instructionRaw(const QString& string) const
{
    return (new QuText(string))->setBold();
};

QuElement* AceFamily::instruction(const QString& stringname) const
{
    return instructionRaw(xstring(stringname));
};

QuElement* AceFamily::stdInstruct(const QString& stringname) const
{
    return instruction(stringname)->addTag(TAG_STANDARD);
};

QuElement* AceFamily::remInstruct(const QString& stringname) const
{
    return instruction(stringname)->addTag(TAG_REMOTE);
};

QuElement* AceFamily::boolean(
    const QString& stringname,
    const QString& fieldname,
    bool mandatory,
    bool bold
)
{
    return (new QuBoolean(xstring(stringname), fieldRef(fieldname, mandatory)))
        ->setBold(bold);
};

QuElement* AceFamily::boolimg(
    const QString& filenamestem, const QString& fieldname, bool mandatory
)
{
    return new QuBoolean(
        uifunc::resourceFilename(filenamestem),
        QSize(),
        fieldRef(fieldname, mandatory)
    );
};

QuElement* AceFamily::warning(const QString& string) const
{
    return (new QuText(string))->setWarning();
};