15.1.668. tablet_qt/tasks/deakins1healthreview.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/>.
*/

// This task doesn't bother with XML downloads or translation; it's hard-coded.

#include "deakins1healthreview.h"
#include "core/camcopsapp.h"
#include "common/textconst.h"
#include "maths/mathfunc.h"
#include "questionnairelib/commonoptions.h"
#include "questionnairelib/questionnaire.h"
#include "questionnairelib/qulineeditinteger.h"
#include "questionnairelib/qumcq.h"
#include "questionnairelib/qumcqgrid.h"
#include "questionnairelib/qumcqgridsingleboolean.h"
#include "questionnairelib/qumultipleresponse.h"
#include "questionnairelib/qutext.h"
#include "questionnairelib/qutextedit.h"
#include "tasklib/taskfactory.h"
#include "tasklib/taskregistrar.h"
#include "tasks/gmcpq.h"  // for ethnicity options
using mathfunc::noneNull;

const QString DeakinS1HealthReview::DEAKIN_S1_HEALTHREVIEW_TABLENAME(
        "deakin_1_healthreview");

const QString ETHNICITY("ethnicity");
const QString ETHNICITY_TEXT("ethnicity_text");
const QString ETHNICITY_OTHER_DETAILS("ethnicity_other_details");

const QString HANDEDNESS("handedness");

const QString EDUCATION("education");

const QString ALLERGIES("allergies");
const QString ALLERGY_ASTHMA("allergy_asthma");
const QString ALLERGY_POLLEN_DUST("allergy_pollen_dust");
const QString ALLERGY_DERMATITIS("allergy_dermatitis");
const QString ALLERGY_FOOD("allergy_food");
const QString ALLERGY_DANDER("allergy_dander");
const QString ALLERGY_OTHER("allergy_other");
const QString ALLERGY_DETAILS("allergy_details");

const QString VACCINATIONS_LAST3MONTHS("vaccinations_last3months");
const QString VACCINATION_DETAILS("vaccination_details");

const QString INFECTIONS_LAST3MONTHS("infections_last3months");
const QString INFECTION_RECENT_RESPIRATORY("infection_recent_respiratory");
const QString INFECTION_RECENT_GASTROENTERITIS("infection_recent_gastroenteritis");
const QString INFECTION_RECENT_URINARY("infection_recent_urinary");
const QString INFECTION_RECENT_SEXUAL("infection_recent_sexual");
const QString INFECTION_RECENT_HEPATITIS("infection_recent_hepatitis");
const QString INFECTION_RECENT_OTHER("infection_recent_other");
const QString INFECTION_RECENT_DETAILS("infection_recent_details");

const QString INFECTIONS_CHRONIC("infections_chronic");
const QString INFECTION_CHRONIC_RESPIRATORY("infection_chronic_respiratory");
const QString INFECTION_CHRONIC_GASTROENTERITIS("infection_chronic_gastroenteritis");
const QString INFECTION_CHRONIC_URINARY("infection_chronic_urinary");
const QString INFECTION_CHRONIC_SEXUAL("infection_chronic_sexual");
const QString INFECTION_CHRONIC_HEPATITIS("infection_chronic_hepatitis");
const QString INFECTION_CHRONIC_OTHER("infection_chronic_other");
const QString INFECTION_CHRONIC_DETAILS("infection_chronic_details");

const QString IMMUNE_DISORDERS("immune_disorders");
const QString IMMUNITY_MS("immunity_ms");
const QString IMMUNITY_SLE("immunity_sle");
const QString IMMUNITY_ARTHRITIS("immunity_arthritis");
const QString IMMUNITY_HIV("immunity_hiv");
const QString IMMUNITY_GRAVES("immunity_graves");
const QString IMMUNITY_DIABETES("immunity_diabetes");
const QString IMMUNITY_OTHER("immunity_other");
const QString IMMUNITY_DETAILS("immunity_details");

const QString FAMILY_HISTORY("family_history");
const QString FAMILYHISTORY_MS("familyhistory_ms");
const QString FAMILYHISTORY_SLE("familyhistory_sle");
const QString FAMILYHISTORY_ARTHRITIS("familyhistory_arthritis");
const QString FAMILYHISTORY_GRAVES("familyhistory_graves");
const QString FAMILYHISTORY_DIABETES("familyhistory_diabetes");
const QString FAMILYHISTORY_PSYCHOSIS_SZ("familyhistory_psychosis_sz");
const QString FAMILYHISTORY_BIPOLAR("familyhistory_bipolar");
const QString FAMILYHISTORY_DETAILS("familyhistory_details");

const QString HEALTH_ANYTHING_ELSE("health_anything_else");
const QString HEALTH_ANYTHING_ELSE_DETAILS("health_anything_else_details");

const QString DRUG_HISTORY("drug_history");
const QString FIRST_ANTIPSYCHOTIC_MEDICATION("first_antipsychotic_medication");

const QString RECREATIONAL_DRUG_IN_LAST_3_MONTHS("recreational_drug_in_last_3_months");
const QString RECDRUG_TOBACCO_FREQUENCY("recdrug_tobacco_frequency");
const QString RECDRUG_TOBACCO_CIGSPERWEEK("recdrug_tobacco_cigsperweek");
const QString RECDRUG_TOBACCO_PREVHEAVY("recdrug_tobacco_prevheavy");
const QString RECDRUG_CANNABIS_FREQUENCY("recdrug_cannabis_frequency");
const QString RECDRUG_CANNABIS_JOINTSPERWEEK("recdrug_cannabis_jointsperweek");
const QString RECDRUG_CANNABIS_PREVHEAVY("recdrug_cannabis_prevheavy");
const QString RECDRUG_ALCOHOL_FREQUENCY("recdrug_alcohol_frequency");
const QString RECDRUG_ALCOHOL_UNITSPERWEEK("recdrug_alcohol_unitsperweek");
const QString RECDRUG_ALCOHOL_PREVHEAVY("recdrug_alcohol_prevheavy");
const QString RECDRUG_MDMA_FREQUENCY("recdrug_mdma_frequency");
const QString RECDRUG_MDMA_PREVHEAVY("recdrug_mdma_prevheavy");
const QString RECDRUG_COCAINE_FREQUENCY("recdrug_cocaine_frequency");
const QString RECDRUG_COCAINE_PREVHEAVY("recdrug_cocaine_prevheavy");
const QString RECDRUG_CRACK_FREQUENCY("recdrug_crack_frequency");
const QString RECDRUG_CRACK_PREVHEAVY("recdrug_crack_prevheavy");
const QString RECDRUG_HEROIN_FREQUENCY("recdrug_heroin_frequency");
const QString RECDRUG_HEROIN_PREVHEAVY("recdrug_heroin_prevheavy");
const QString RECDRUG_METHADONE_FREQUENCY("recdrug_methadone_frequency");
const QString RECDRUG_METHADONE_PREVHEAVY("recdrug_methadone_prevheavy");
const QString RECDRUG_AMPHETAMINES_FREQUENCY("recdrug_amphetamines_frequency");
const QString RECDRUG_AMPHETAMINES_PREVHEAVY("recdrug_amphetamines_prevheavy");
const QString RECDRUG_BENZODIAZEPINES_FREQUENCY("recdrug_benzodiazepines_frequency");
const QString RECDRUG_BENZODIAZEPINES_PREVHEAVY("recdrug_benzodiazepines_prevheavy");
const QString RECDRUG_KETAMINE_FREQUENCY("recdrug_ketamine_frequency");
const QString RECDRUG_KETAMINE_PREVHEAVY("recdrug_ketamine_prevheavy");
const QString RECDRUG_LEGALHIGHS_FREQUENCY("recdrug_legalhighs_frequency");
const QString RECDRUG_LEGALHIGHS_PREVHEAVY("recdrug_legalhighs_prevheavy");
const QString RECDRUG_INHALANTS_FREQUENCY("recdrug_inhalants_frequency");
const QString RECDRUG_INHALANTS_PREVHEAVY("recdrug_inhalants_prevheavy");
const QString RECDRUG_HALLUCINOGENS_FREQUENCY("recdrug_hallucinogens_frequency");
const QString RECDRUG_HALLUCINOGENS_PREVHEAVY("recdrug_hallucinogens_prevheavy");
const QString RECDRUG_DETAILS("recdrug_details");
const QString RECDRUG_PREVHEAVY("recdrug_prevheavy");
const QString RECDRUG_PREVHEAVY_DETAILS("recdrug_prevheavy_details");

const QString MRI_CLAUSTROPHOBIC("mri_claustrophobic");
const QString MRI_DIFFICULTY_LYING_1_HOUR("mri_difficulty_lying_1_hour");
const QString MRI_NONREMOVABLE_METAL("mri_nonremovable_metal");
const QString MRI_METAL_FROM_OPERATIONS("mri_metal_from_operations");
const QString MRI_TATTOOS_NICOTINE_PATCHES("mri_tattoos_nicotine_patches");
const QString MRI_WORKED_WITH_METAL("mri_worked_with_metal");
const QString MRI_PREVIOUS_BRAIN_SCAN("mri_previous_brain_scan");
const QString MRI_PREVIOUS_BRAIN_SCAN_DETAILS("mri_previous_brain_scan_details");
const QString OTHER_RELEVANT_THINGS("other_relevant_things");
const QString OTHER_RELEVANT_THINGS_DETAILS("other_relevant_things_details");
const QString WILLING_TO_PARTICIPATE_IN_FURTHER_STUDIES("willing_to_participate_in_further_studies");

const QString STR_DETAILS_IF_YES("If you answered YES, please give details:");
const QString STR_DETAILS("Details:");
const QString TICK_ANY_THAT_APPLY("Tick any that apply:");
const QStringList DRUGLIST{  // order is important
    "tobacco",
    "cannabis",
    "alcohol",
    "Ecstasy (MDMA)",
    "cocaine",
    "crack cocaine",
    "amphetamines",
    "heroin",
    "methadone (heroin substitute)",
    "benzodiazepines",
    "ketamine",
    "legal highs (e.g. Salvia)",
    "inhalants",
    "hallucinogens",
};
const QStringList INFECTIONLIST{  // order is important
    "respiratory infection",
    "gastroenteritis",
    "urinary tract infection",
    "sexually transmitted infection",
    "hepatitis",
    "other"
};
const QString PT_ETHNICITY("eth");
const QString PT_ALLERGY("all");
const QString PT_VACCINES("vac");
const QString PT_ACUTE_INFECTIONS("acinf");
const QString PT_CHRONIC_INFECTIONS("chinf");
const QString PT_IMMUNE("imm");
const QString PT_FH("fh");
const QString PT_HEALTH_OTHER("ho");
const QString PT_RECDRUGS("recdrug");
const QString PT_MRI("mri");
const QString ET_ETHNICITY_OTHER("eth_other");
const QString ET_ALLERGY("all");
const QString ET_VACCINES("vacc");
const QString ET_ACUTE_INFECTIONS("acinf");
const QString ET_CHRONIC_INFECTIONS("chinf");
const QString ET_IMMUNE("imm");
const QString ET_FH("fh");
const QString ET_HEALTH_OTHER("ho");
const QString ET_RECDRUGS("recdrug");
const QString ET_PREVSCAN("prevscan");
const QString ET_OTHERDETAILS("otherdetails");


void initializeDeakinS1HealthReview(TaskFactory& factory)
{
    static TaskRegistrar<DeakinS1HealthReview> registered(factory);
}


DeakinS1HealthReview::DeakinS1HealthReview(
        CamcopsApp& app, DatabaseManager& db, const int load_pk) :
    Task(app, db, DEAKIN_S1_HEALTHREVIEW_TABLENAME, false, false, false)  // ... anon, clin, resp
{
    addField(ETHNICITY, QMetaType::fromType<int>());
    addField(ETHNICITY_TEXT, QMetaType::fromType<QString>());  // SEEMS TO BE UNUSED!
    addField(ETHNICITY_OTHER_DETAILS, QMetaType::fromType<QString>());

    addField(HANDEDNESS, QMetaType::fromType<QString>());

    addField(EDUCATION, QMetaType::fromType<QString>());

    addField(ALLERGIES, QMetaType::fromType<bool>());
    addField(ALLERGY_ASTHMA, QMetaType::fromType<bool>());
    addField(ALLERGY_POLLEN_DUST, QMetaType::fromType<bool>());
    addField(ALLERGY_DERMATITIS, QMetaType::fromType<bool>());
    addField(ALLERGY_FOOD, QMetaType::fromType<bool>());
    addField(ALLERGY_DANDER, QMetaType::fromType<bool>());
    addField(ALLERGY_OTHER, QMetaType::fromType<bool>());
    addField(ALLERGY_DETAILS, QMetaType::fromType<QString>());

    addField(VACCINATIONS_LAST3MONTHS, QMetaType::fromType<bool>());
    addField(VACCINATION_DETAILS, QMetaType::fromType<QString>());

    addField(INFECTIONS_LAST3MONTHS, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_RESPIRATORY, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_GASTROENTERITIS, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_URINARY, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_SEXUAL, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_HEPATITIS, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_OTHER, QMetaType::fromType<bool>());
    addField(INFECTION_RECENT_DETAILS, QMetaType::fromType<QString>());

    addField(INFECTIONS_CHRONIC, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_RESPIRATORY, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_GASTROENTERITIS, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_URINARY, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_SEXUAL, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_HEPATITIS, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_OTHER, QMetaType::fromType<bool>());
    addField(INFECTION_CHRONIC_DETAILS, QMetaType::fromType<QString>());

    addField(IMMUNE_DISORDERS, QMetaType::fromType<bool>());
    addField(IMMUNITY_MS, QMetaType::fromType<bool>());
    addField(IMMUNITY_SLE, QMetaType::fromType<bool>());
    addField(IMMUNITY_ARTHRITIS, QMetaType::fromType<bool>());
    addField(IMMUNITY_HIV, QMetaType::fromType<bool>());
    addField(IMMUNITY_GRAVES, QMetaType::fromType<bool>());
    addField(IMMUNITY_DIABETES, QMetaType::fromType<bool>());
    addField(IMMUNITY_OTHER, QMetaType::fromType<bool>());
    addField(IMMUNITY_DETAILS, QMetaType::fromType<QString>());

    addField(FAMILY_HISTORY, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_MS, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_SLE, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_ARTHRITIS, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_GRAVES, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_DIABETES, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_PSYCHOSIS_SZ, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_BIPOLAR, QMetaType::fromType<bool>());
    addField(FAMILYHISTORY_DETAILS, QMetaType::fromType<QString>());

    addField(HEALTH_ANYTHING_ELSE, QMetaType::fromType<bool>());
    addField(HEALTH_ANYTHING_ELSE_DETAILS, QMetaType::fromType<QString>());

    addField(DRUG_HISTORY, QMetaType::fromType<QString>());
    addField(FIRST_ANTIPSYCHOTIC_MEDICATION, QMetaType::fromType<QString>());

    addField(RECREATIONAL_DRUG_IN_LAST_3_MONTHS, QMetaType::fromType<bool>());
    addField(RECDRUG_TOBACCO_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_TOBACCO_CIGSPERWEEK, QMetaType::fromType<int>());
    addField(RECDRUG_TOBACCO_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_CANNABIS_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_CANNABIS_JOINTSPERWEEK, QMetaType::fromType<int>());
    addField(RECDRUG_CANNABIS_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_ALCOHOL_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_ALCOHOL_UNITSPERWEEK, QMetaType::fromType<int>());
    addField(RECDRUG_ALCOHOL_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_MDMA_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_MDMA_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_COCAINE_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_COCAINE_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_CRACK_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_CRACK_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_HEROIN_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_HEROIN_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_METHADONE_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_METHADONE_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_AMPHETAMINES_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_AMPHETAMINES_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_BENZODIAZEPINES_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_BENZODIAZEPINES_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_KETAMINE_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_KETAMINE_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_LEGALHIGHS_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_LEGALHIGHS_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_INHALANTS_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_INHALANTS_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_HALLUCINOGENS_FREQUENCY, QMetaType::fromType<int>());
    addField(RECDRUG_HALLUCINOGENS_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_DETAILS, QMetaType::fromType<QString>());
    addField(RECDRUG_PREVHEAVY, QMetaType::fromType<bool>());
    addField(RECDRUG_PREVHEAVY_DETAILS, QMetaType::fromType<QString>());

    addField(MRI_CLAUSTROPHOBIC, QMetaType::fromType<bool>());
    addField(MRI_DIFFICULTY_LYING_1_HOUR, QMetaType::fromType<bool>());
    addField(MRI_NONREMOVABLE_METAL, QMetaType::fromType<bool>());
    addField(MRI_METAL_FROM_OPERATIONS, QMetaType::fromType<bool>());
    addField(MRI_TATTOOS_NICOTINE_PATCHES, QMetaType::fromType<bool>());
    addField(MRI_WORKED_WITH_METAL, QMetaType::fromType<bool>());
    addField(MRI_PREVIOUS_BRAIN_SCAN, QMetaType::fromType<bool>());
    addField(MRI_PREVIOUS_BRAIN_SCAN_DETAILS, QMetaType::fromType<QString>());
    addField(OTHER_RELEVANT_THINGS, QMetaType::fromType<bool>());
    addField(OTHER_RELEVANT_THINGS_DETAILS, QMetaType::fromType<QString>());
    addField(WILLING_TO_PARTICIPATE_IN_FURTHER_STUDIES, QMetaType::fromType<bool>());

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


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

QString DeakinS1HealthReview::shortname() const
{
    return "Deakin_S1_HealthReview";
}


QString DeakinS1HealthReview::longname() const
{
    return tr("Deakin JB — Antibody-mediated psychosis study — health review");
}


QString DeakinS1HealthReview::description() const
{
    return tr("Health review for antibody-mediated psychosis study.");
}


QString DeakinS1HealthReview::infoFilenameStem() const
{
    return "deakin_s1_healthreview";
}


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

bool DeakinS1HealthReview::isComplete() const
{
    return noneNull(values(QStringList{
       ETHNICITY,
       HANDEDNESS,
       EDUCATION,
       ALLERGIES,
       VACCINATIONS_LAST3MONTHS,
       INFECTIONS_LAST3MONTHS,
       INFECTIONS_CHRONIC,
       IMMUNE_DISORDERS,
       HEALTH_ANYTHING_ELSE,
       RECREATIONAL_DRUG_IN_LAST_3_MONTHS,
       RECDRUG_PREVHEAVY,
       MRI_CLAUSTROPHOBIC,
       MRI_DIFFICULTY_LYING_1_HOUR,
       MRI_NONREMOVABLE_METAL,
       MRI_METAL_FROM_OPERATIONS,
       MRI_TATTOOS_NICOTINE_PATCHES,
       MRI_WORKED_WITH_METAL,
       MRI_PREVIOUS_BRAIN_SCAN,
       OTHER_RELEVANT_THINGS,
       WILLING_TO_PARTICIPATE_IN_FURTHER_STUDIES,
    }));
}


QStringList DeakinS1HealthReview::summary() const
{
    return QStringList{TextConst::noSummarySeeFacsimile()};
}


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


OpenableWidget* DeakinS1HealthReview::editor(const bool read_only)
{
    const NameValueOptions yn_options = CommonOptions::yesNoBoolean();
    const NameValueOptions handedness_options = NameValueOptions{
        {"Left hand", "L"},
        {"Right hand", "R"},
    };
    const NameValueOptions education_options = NameValueOptions{
        {"None", "none"},
        {"CSE", "cse"},
        {"GCSE", "gcse"},
        {"O-Level", "olevel"},
        {"A-Level", "alevel"},
        {"Vocational qualification, NVQ — full time", "nvq_fulltime"},
        {"Vocational qualification, NVQ — part time", "nvq_parttime"},
        {"Degree qualification — diploma", "degree_diploma"},
        {"Degree qualification — bachelor’s", "degree_bachelor"},
        {"Degree qualification — other", "degree_other"},
        {"Postgraduate qualification — master’s", "postgrad_masters"},
        {"Postgraduate qualification — PhD", "postgrad_phd"},
    };

    auto text = [](const QString& text) -> QuElement* {
        return new QuText(text);
    };
    auto boldtext = [](const QString& text) -> QuElement* {
        return (new QuText(text))->setBold();
    };
    auto mcq = [this](const QString& fieldname,
                      const NameValueOptions& options) -> QuElement* {
        return new QuMcq(fieldRef(fieldname), options);
    };
    auto mcqhoriz = [this](const QString& fieldname,
                           const NameValueOptions& options) -> QuElement* {
        return (new QuMcq(fieldRef(fieldname), options))->setHorizontal(true);
    };
    auto qf = [this](const QString& fieldname,
                     const QString& question) -> QuestionWithOneField {
        return QuestionWithOneField(fieldRef(fieldname), question);
    };
    auto q2f = [this](const QString& fieldname1,
                      const QString& fieldname2,
                      const QString& question) -> QuestionWithTwoFields {
        return QuestionWithTwoFields(question, fieldRef(fieldname1),
                                     fieldRef(fieldname2));
    };
    auto yn = [this, &yn_options](const QString& fieldname) -> QuElement* {
        return (new QuMcq(fieldRef(fieldname), yn_options))
                ->setHorizontal(true);
    };
    auto lineedit = [this](const QString& fieldname) -> QuElement* {
        return new QuLineEdit(fieldRef(fieldname));
    };
    auto multiline = [this](const QString& fieldname,
                            bool mandatory = true) -> QuElement* {
        return new QuTextEdit(fieldRef(fieldname, mandatory));
    };
    auto intedit = [this](const QString& fieldname) -> QuElement* {
        return (new QuLineEditInteger(fieldRef(fieldname), 0, 1000))
                ->setHint("");
    };
    auto watch = [this](const QString& fieldname) -> void {
        connect(fieldRef(fieldname).data(), &FieldRef::valueChanged,
                this, &DeakinS1HealthReview::updateMandatory);
    };

    QVector<QuPagePtr> pages;

    pages.append(QuPagePtr((new QuPage{
        boldtext("Please enter your ethnicity:"),
        mcq(ETHNICITY, GmcPq::ethnicityOptions(m_app)),
        text(m_app.xstring(GmcPq::GMCPQ_TABLENAME, "ethnicity_other_s")),
        lineedit(ETHNICITY_OTHER_DETAILS),
    })->setTitle("Ethnicity")));
    watch(ETHNICITY);

    pages.append(QuPagePtr((new QuPage{
        boldtext("I prefer to use my:"),
        mcqhoriz(HANDEDNESS, handedness_options),
    })->setTitle("Handedness")));

    pages.append(QuPagePtr((new QuPage{
        boldtext("Please enter your highest level of education, or nearest "
                 "equivalent:"),
        mcqhoriz(EDUCATION, education_options),
    })->setTitle("Education")));

    QuMultipleResponse* mr_allergies = new QuMultipleResponse{
        qf(ALLERGY_ASTHMA, "asthma"),
        qf(ALLERGY_POLLEN_DUST, "pollen/dust"),
        qf(ALLERGY_DERMATITIS, "dermatitis"),
        qf(ALLERGY_FOOD, "food allergy"),
        qf(ALLERGY_DANDER, "animal dander"),
        qf(ALLERGY_OTHER, "other"),
    };
    pages.append(QuPagePtr((new QuPage{
        boldtext("Do you have any allergies?"),
        yn(ALLERGIES),
        boldtext(STR_DETAILS_IF_YES),
        mr_allergies,
        text(STR_DETAILS),
        multiline(ALLERGY_DETAILS),
    })->setTitle("Allergies")));
    watch(ALLERGIES);
    watch(ALLERGY_OTHER);
    connect(this, &DeakinS1HealthReview::setAllergyMinimum,
            mr_allergies, &QuMultipleResponse::setMinimumAnswers);

    pages.append(QuPagePtr((new QuPage{
        boldtext("Have you had any vaccinations or inoculations in the last 3 "
                 "months?"),
        yn(VACCINATIONS_LAST3MONTHS),
        boldtext(STR_DETAILS_IF_YES),
        text(STR_DETAILS),
        multiline(VACCINATION_DETAILS),
    })->setTitle("Recent vaccinations")));
    connect(fieldRef(VACCINATIONS_LAST3MONTHS).data(), &FieldRef::valueChanged,
            this, &DeakinS1HealthReview::updateMandatory);

    QuMultipleResponse* mr_recent_infection = new QuMultipleResponse{
        qf(INFECTION_RECENT_RESPIRATORY, INFECTIONLIST[0]),
        qf(INFECTION_RECENT_GASTROENTERITIS, INFECTIONLIST[1]),
        qf(INFECTION_RECENT_URINARY, INFECTIONLIST[2]),
        qf(INFECTION_RECENT_SEXUAL, INFECTIONLIST[3]),
        qf(INFECTION_RECENT_HEPATITIS, INFECTIONLIST[4]),
        qf(INFECTION_RECENT_OTHER, INFECTIONLIST[5]),
    };
    pages.append(QuPagePtr((new QuPage{
        boldtext("Have you had any infectious diseases in the last 3 months?"),
        yn(INFECTIONS_LAST3MONTHS),
        boldtext(STR_DETAILS_IF_YES),
        mr_recent_infection,
        text(STR_DETAILS),
        multiline(INFECTION_RECENT_DETAILS),
    })->setTitle("Recent infections")));
    watch(INFECTIONS_LAST3MONTHS);
    watch(INFECTION_RECENT_OTHER);
    connect(this, &DeakinS1HealthReview::setRecentInfectionsMinimum,
            mr_recent_infection, &QuMultipleResponse::setMinimumAnswers);

    QuMultipleResponse* mr_chronic_infection = new QuMultipleResponse{
        qf(INFECTION_CHRONIC_RESPIRATORY, INFECTIONLIST[0]),
        qf(INFECTION_CHRONIC_GASTROENTERITIS, INFECTIONLIST[1]),
        qf(INFECTION_CHRONIC_URINARY, INFECTIONLIST[2]),
        qf(INFECTION_CHRONIC_SEXUAL, INFECTIONLIST[3]),
        qf(INFECTION_CHRONIC_HEPATITIS, INFECTIONLIST[4]),
        qf(INFECTION_CHRONIC_OTHER, INFECTIONLIST[5]),
    };
    pages.append(QuPagePtr((new QuPage{
        boldtext("Are you currently experiencing or have you ever experienced "
                 "any chronic infections?"),
        yn(INFECTIONS_CHRONIC),
        boldtext(STR_DETAILS_IF_YES),
        mr_chronic_infection,
        text(STR_DETAILS),
        multiline(INFECTION_CHRONIC_DETAILS),
    })->setTitle("Chronic infections")));
    watch(INFECTIONS_CHRONIC);
    watch(INFECTION_CHRONIC_OTHER);
    connect(this, &DeakinS1HealthReview::setChronicInfectionsMinimum,
            mr_chronic_infection, &QuMultipleResponse::setMinimumAnswers);

    QuMultipleResponse* mr_immune = new QuMultipleResponse{
        qf(IMMUNITY_MS, "multiple sclerosis"),
        qf(IMMUNITY_SLE, "lupus"),
        qf(IMMUNITY_ARTHRITIS, "arthritis"),
        qf(IMMUNITY_HIV, "HIV/AIDS"),
        qf(IMMUNITY_GRAVES, "Graves’ disease"),
        qf(IMMUNITY_DIABETES, "diabetes"),
        qf(IMMUNITY_OTHER, "other"),
    };
    pages.append(QuPagePtr((new QuPage{
        boldtext("Do you have any immune disorders?"),
        yn(IMMUNE_DISORDERS),
        boldtext(STR_DETAILS_IF_YES),
        mr_immune,
        text(STR_DETAILS),
        multiline(IMMUNITY_DETAILS),
    })->setTitle("Immune disorders")));
    watch(IMMUNE_DISORDERS);
    watch(IMMUNITY_OTHER);
    connect(this, &DeakinS1HealthReview::setImmuneMinimum,
            mr_immune, &QuMultipleResponse::setMinimumAnswers);

    QuMultipleResponse* mr_fh_immune = new QuMultipleResponse{
        qf(FAMILYHISTORY_MS, "multiple sclerosis"),
        qf(FAMILYHISTORY_SLE, "lupus"),
        qf(FAMILYHISTORY_ARTHRITIS, "arthritis"),
        qf(FAMILYHISTORY_GRAVES, "Graves’ disease"),
        qf(FAMILYHISTORY_PSYCHOSIS_SZ, "psychosis/schizophrenia"),
        qf(FAMILYHISTORY_BIPOLAR, "mania/bipolar affective disorder"),
    };
    pages.append(QuPagePtr((new QuPage{
        boldtext("Does anyone in your family have any of the disorders listed "
                 "below?"),
        yn(FAMILY_HISTORY),
        boldtext(STR_DETAILS_IF_YES),
        mr_fh_immune,
        text(STR_DETAILS),
        multiline(FAMILYHISTORY_DETAILS),
    })->setTitle("Family history")));
    watch(FAMILY_HISTORY);
    connect(this, &DeakinS1HealthReview::setFHImmuneMinimum,
            mr_fh_immune, &QuMultipleResponse::setMinimumAnswers);

    pages.append(QuPagePtr((new QuPage{
        boldtext("Is there any other information about your general health "
                 "that we should know?"),
        yn(HEALTH_ANYTHING_ELSE),
        boldtext(STR_DETAILS_IF_YES),
        multiline(HEALTH_ANYTHING_ELSE_DETAILS),
    })->setTitle("Other aspects of health")));

    pages.append(QuPagePtr((new QuPage{
        boldtext("If you are taking prescribed medication please list below:"),
        multiline(DRUG_HISTORY, false),
        boldtext("If you are taking antipsychotic medication, when did you "
                 "first take a medication of this kind?"),
        multiline(FIRST_ANTIPSYCHOTIC_MEDICATION, false),
    })->setTitle("Medication")));

    pages.append(QuPagePtr((new QuPage{
        boldtext("Please answer the following questions about any history you "
                 "may have with drug taking. It is very important that you "
                 "are honest, because this history may affect your blood "
                 "sample. Previous drug taking will not necessarily exclude "
                 "you, and all information will be kept completely "
                 "confidential."),
        boldtext("Have you taken any recreational drugs in the last 3 months? "
                 "(Recreational drugs include drugs used only occasionally "
                 "without being dependent on them.)"),
        yn(RECREATIONAL_DRUG_IN_LAST_3_MONTHS),
        boldtext("Have you ever had a period of very heavy use of any of the "
                 "drugs listed below?"),
        text(DRUGLIST.join(", ")),
        yn(RECDRUG_PREVHEAVY),
        boldtext("If you answered YES to either question, please give details "
                 "(A–E below)."),
        boldtext("(A) Please use the grid below to specify which drugs you "
                 "used in the past 3 months, and how often."),
        boldtext("(B) If you have ever had a period of very heavy use of any "
                 "of these drugs, please tick its “Previous heavy use?” box."),
        (new QuMcqGridSingleBoolean(
            QVector<QuestionWithTwoFields>{
                q2f(RECDRUG_TOBACCO_FREQUENCY, RECDRUG_TOBACCO_PREVHEAVY, DRUGLIST[0]),
                q2f(RECDRUG_CANNABIS_FREQUENCY, RECDRUG_CANNABIS_PREVHEAVY, DRUGLIST[1]),
                q2f(RECDRUG_ALCOHOL_FREQUENCY, RECDRUG_ALCOHOL_PREVHEAVY, DRUGLIST[2]),

                q2f(RECDRUG_MDMA_FREQUENCY, RECDRUG_MDMA_PREVHEAVY, DRUGLIST[3]),
                q2f(RECDRUG_COCAINE_FREQUENCY, RECDRUG_COCAINE_PREVHEAVY, DRUGLIST[4]),
                q2f(RECDRUG_CRACK_FREQUENCY, RECDRUG_CRACK_PREVHEAVY, DRUGLIST[5]),
                q2f(RECDRUG_AMPHETAMINES_FREQUENCY, RECDRUG_AMPHETAMINES_PREVHEAVY, DRUGLIST[6]),

                q2f(RECDRUG_HEROIN_FREQUENCY, RECDRUG_HEROIN_PREVHEAVY, DRUGLIST[7]),
                q2f(RECDRUG_METHADONE_FREQUENCY, RECDRUG_METHADONE_PREVHEAVY, DRUGLIST[8]),
                q2f(RECDRUG_BENZODIAZEPINES_FREQUENCY, RECDRUG_BENZODIAZEPINES_PREVHEAVY, DRUGLIST[9]),

                q2f(RECDRUG_KETAMINE_FREQUENCY, RECDRUG_KETAMINE_PREVHEAVY, DRUGLIST[10]),
                q2f(RECDRUG_LEGALHIGHS_FREQUENCY, RECDRUG_LEGALHIGHS_PREVHEAVY, DRUGLIST[11]),
                q2f(RECDRUG_INHALANTS_FREQUENCY, RECDRUG_INHALANTS_PREVHEAVY, DRUGLIST[12]),
                q2f(RECDRUG_HALLUCINOGENS_FREQUENCY, RECDRUG_HALLUCINOGENS_PREVHEAVY, DRUGLIST[13]),
            },
            NameValueOptions{
                {"Did not use", 0},
                {"Occasionally", 1},
                {"Monthly", 2},
                {"Weekly", 3},
                {"Daily", 4},
            },
            "Previous heavy use?"  // boolean label
        ))->setSubtitles(QVector<McqGridSubtitle>{
            McqGridSubtitle(3, ""),
            McqGridSubtitle(7, ""),
            McqGridSubtitle(10, ""),
        }),
        boldtext("(C) Please give any further details of your recreational "
                 "drug use in the previous 3 months:"),
        multiline(RECDRUG_DETAILS),
        boldtext("(D) If you have used tobacco, cannabis, or alcohol in the "
                 "last 3 months, please give the quantities:"),
        text("Tobacco – cigarettes per week:"),
        intedit(RECDRUG_TOBACCO_CIGSPERWEEK),
        text("Cannabis – joints per week:"),
        intedit(RECDRUG_CANNABIS_JOINTSPERWEEK),
        text("Alcohol – units per week:"),
        intedit(RECDRUG_ALCOHOL_UNITSPERWEEK),
        boldtext("(E) If you have had a period of very heavy drug use, please "
                 "give details about when this was and how long you used the "
                 "drug heavily:"),
        multiline(RECDRUG_PREVHEAVY_DETAILS),
    })->setTitle("Recreational drug use")));
    watch(RECREATIONAL_DRUG_IN_LAST_3_MONTHS);
    watch(RECDRUG_PREVHEAVY);

    pages.append(QuPagePtr((new QuPage{
        new QuMcqGrid(QVector<QuestionWithOneField>{
            qf(MRI_CLAUSTROPHOBIC,
               "Are you claustrophobic, or have difficulties in small spaces "
               "(e.g. lifts, confined spaces)?"),
            qf(MRI_DIFFICULTY_LYING_1_HOUR,
               "Would you have any difficulties with lying down for 1 hour "
               "(e.g. problems with your back, neck, bladder, etc.)?"),
            qf(MRI_NONREMOVABLE_METAL,
               "Is there any metal in your body which is not removable (e.g. "
               "piercings, splinters, etc.)?"),
            qf(MRI_METAL_FROM_OPERATIONS,
               "Have you ever had any operations where metal has been left in "
               "your body?"),
            qf(MRI_TATTOOS_NICOTINE_PATCHES,
               "Do you have any tattoos or nicotine patches?"),
            qf(MRI_WORKED_WITH_METAL,
               "Have you ever worked with metal (e.g. as a machinist, "
               "metalworker, etc.)?"),
            qf(MRI_PREVIOUS_BRAIN_SCAN,
               "Have you ever had any form of brain scan before? If so, "
               "please give details below."),
            qf(OTHER_RELEVANT_THINGS,
               "Are there any points you feel may be relevant to your "
               "participation in the study? If so, please give details "
               "below."),
        }, yn_options),
        text("Details of previous brain scans, if applicable:"),
        multiline(MRI_PREVIOUS_BRAIN_SCAN_DETAILS, false),
        text("Any other points you feel may be relevant to your "
             "participation, if applicable:"),
        multiline(OTHER_RELEVANT_THINGS_DETAILS, false),
        text("Finally:"),
        new QuMcqGrid(QVector<QuestionWithOneField>{
            qf(WILLING_TO_PARTICIPATE_IN_FURTHER_STUDIES,
               "Would you be willing to participate in further studies run by "
               "our department?"),
        }, yn_options),
    })->setTitle("Questions related to MRI scanning")));

    pages.append(QuPagePtr((new QuPage{
        boldtext(TextConst::thankYou()),
    })->setTitle(TextConst::finished())));

    updateMandatory();

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


// ============================================================================
// Signal handlers
// ============================================================================

void DeakinS1HealthReview::updateMandatory()
{
    // This could be more efficient with lots of signal handlers, but...

    fieldRef(ETHNICITY_OTHER_DETAILS)->setMandatory(
                GmcPq::ethnicityOther(valueInt(ETHNICITY)));

    emit setAllergyMinimum(valueInt(ALLERGIES));
    fieldRef(ALLERGY_DETAILS)->setMandatory(valueBool(ALLERGY_OTHER));

    fieldRef(VACCINATION_DETAILS)->setMandatory(
                valueBool(VACCINATIONS_LAST3MONTHS));

    emit setRecentInfectionsMinimum(valueInt(INFECTIONS_LAST3MONTHS));
    fieldRef(INFECTION_RECENT_DETAILS)->setMandatory(
                valueBool(INFECTION_RECENT_OTHER));

    emit setChronicInfectionsMinimum(valueInt(INFECTIONS_CHRONIC));
    fieldRef(INFECTION_CHRONIC_DETAILS)->setMandatory(
                valueBool(INFECTION_CHRONIC_OTHER));

    emit setImmuneMinimum(valueInt(IMMUNE_DISORDERS));
    fieldRef(IMMUNITY_DETAILS)->setMandatory(valueBool(IMMUNITY_OTHER));

    emit setFHImmuneMinimum(valueInt(FAMILY_HISTORY));
    fieldRef(FAMILYHISTORY_DETAILS)->setMandatory(valueBool(FAMILY_HISTORY));

    fieldRef(HEALTH_ANYTHING_ELSE_DETAILS)->setMandatory(
                valueBool(HEALTH_ANYTHING_ELSE));

    fieldRef(HEALTH_ANYTHING_ELSE_DETAILS)->setMandatory(
                valueBool(HEALTH_ANYTHING_ELSE));

    const bool recent_drugs = valueBool(RECREATIONAL_DRUG_IN_LAST_3_MONTHS);
    const bool heavy_drugs = valueBool(RECDRUG_PREVHEAVY);
    const bool drugs = recent_drugs || heavy_drugs;

    fieldRef(RECDRUG_TOBACCO_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_TOBACCO_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_CANNABIS_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_CANNABIS_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_ALCOHOL_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_ALCOHOL_PREVHEAVY)->setMandatory(drugs);

    fieldRef(RECDRUG_MDMA_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_MDMA_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_COCAINE_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_COCAINE_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_CRACK_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_CRACK_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_AMPHETAMINES_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_AMPHETAMINES_PREVHEAVY)->setMandatory(drugs);

    fieldRef(RECDRUG_HEROIN_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_HEROIN_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_METHADONE_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_METHADONE_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_BENZODIAZEPINES_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_BENZODIAZEPINES_PREVHEAVY)->setMandatory(drugs);

    fieldRef(RECDRUG_KETAMINE_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_KETAMINE_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_LEGALHIGHS_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_LEGALHIGHS_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_INHALANTS_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_INHALANTS_PREVHEAVY)->setMandatory(drugs);
    fieldRef(RECDRUG_HALLUCINOGENS_FREQUENCY)->setMandatory(drugs);
    fieldRef(RECDRUG_HALLUCINOGENS_PREVHEAVY)->setMandatory(drugs);

    fieldRef(RECDRUG_DETAILS)->setMandatory(recent_drugs);

    fieldRef(RECDRUG_TOBACCO_CIGSPERWEEK)->setMandatory(recent_drugs);
    fieldRef(RECDRUG_TOBACCO_CIGSPERWEEK)->setMandatory(recent_drugs);
    fieldRef(RECDRUG_TOBACCO_CIGSPERWEEK)->setMandatory(recent_drugs);

    fieldRef(RECDRUG_PREVHEAVY_DETAILS)->setMandatory(heavy_drugs);
}