Commit 5f8d313a authored by Volker Krause's avatar Volker Krause
Browse files

Allow to directly show a membership card associated with a train ticket

This tries to match the program membership data we have from the ticket
(which can be rather minimal) to the set of membership cards we have, and
if we find one unique match, offers to show that.
parent 879e5fb9
Pipeline #170526 passed with stage
in 3 minutes and 7 seconds
......@@ -51,6 +51,7 @@ private Q_SLOTS:
QCOMPARE(idx.data(PassManager::PassTypeRole).toInt(), PassManager::ProgramMembership);
QVERIFY(!idx.data(PassManager::PassDataRole).toByteArray().isEmpty());
QCOMPARE(idx.data(PassManager::NameRole).toString(), QLatin1String("BahnCard 25 (2. Kl.) (BC25)"));
QVERIFY(!mgr.pass(passId).isNull());
{
// test persistence
......@@ -67,6 +68,44 @@ private Q_SLOTS:
QVERIFY(mgr.remove(passId));
QCOMPARE(mgr.rowCount(), 0);
}
void testPassMatching_data()
{
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("number");
QTest::addColumn<bool>("matches");
QTest::newRow("empty") << QString() << QString() << false;
QTest::newRow("generic-name") << QStringLiteral("BahnCard") << QString() << true;
QTest::newRow("generic-name-case-mismatch") << QStringLiteral("Bahncard") << QString() << true;
QTest::newRow("number-match") << QStringLiteral("BahnCard") << QStringLiteral("7081123456789012") << true;
QTest::newRow("number-mismatch") << QStringLiteral("BahnCard") << QStringLiteral("7081123456789013") << false;
QTest::newRow("name-mismatch") << QStringLiteral("CartaFRECCIA") << QString() << false;
QTest::newRow("abbr-match") << QStringLiteral("BC25") << QString() << true;
QTest::newRow("abbr-mismatch") << QStringLiteral("BC50") << QString() << false;
}
void testPassMatching()
{
QFETCH(QString, name);
QFETCH(QString, number);
QFETCH(bool, matches);
PassManager mgr;
Test::clearAll(&mgr);
QVERIFY(mgr.import(JsonLdDocument::fromJsonSingular(QJsonDocument::fromJson(Test::readFile(QStringLiteral(SOURCE_DIR "/data/bahncard.json"))).object())));
const auto passId = mgr.index(0, 0).data(PassManager::PassIdRole).toString();
QVERIFY(!passId.isEmpty());
ProgramMembership program;
program.setProgramName(name);
program.setMembershipNumber(number);
if (matches) {
QCOMPARE(mgr.findMatchingPass(program), passId);
} else {
QCOMPARE(mgr.findMatchingPass(program), QString());
}
}
};
QTEST_GUILESS_MAIN(PassManagerTest)
......
......@@ -16,10 +16,6 @@ Kirigami.ScrollablePage {
id: root
title: i18n("Passes and Programs")
Component {
id: programMembershipPage
App.ProgramMembershipPage {}
}
Component {
id: pkpassComponent
App.GenericPkPassPage {}
......
......@@ -281,6 +281,13 @@ App.DetailsPage {
text: root.currentReservation.programMembershipUsed.membershipNumber
visible: text
}
QQC2.Button {
id: passButton
property string passId: PassManager.findMatchingPass(root.currentReservation.programMembershipUsed)
text: i18n("Show")
visible: passId
onClicked: applicationWindow().pageStack.push(programMembershipPage, { programMembership: PassManager.pass(passButton.passId), passId: passButton.passId })
}
// booking details
Kirigami.Separator {
......
......@@ -207,6 +207,10 @@ Kirigami.ApplicationWindow {
tripGroupManager: TripGroupManager
}
}
Component {
id: programMembershipPage
App.ProgramMembershipPage {}
}
Component {
id: passComponent
App.PassPage {}
......
......@@ -90,6 +90,44 @@ bool PassManager::import(const QVector<QVariant> &passes)
return result;
}
QString PassManager::findMatchingPass(const QVariant &pass) const
{
if (JsonLd::isA<KItinerary::ProgramMembership>(pass)) {
const auto program = pass.value<KItinerary::ProgramMembership>();
if (!program.membershipNumber().isEmpty()) {
const auto it = std::find_if(m_entries.begin(), m_entries.end(), [program](const auto &entry) {
return entry.data.template value<KItinerary::ProgramMembership>().membershipNumber() == program.membershipNumber();
});
return it == m_entries.end() ? QString() : (*it).id;
}
if (!program.programName().isEmpty()) {
// unique substring match
QString id;
for (const auto &entry : m_entries) {
const auto name = entry.data.value<KItinerary::ProgramMembership>().programName();
if (name.isEmpty() || (!name.contains(program.programName(), Qt::CaseInsensitive) && !(program.programName().contains(name, Qt::CaseInsensitive)))) {
continue;
}
if (id.isEmpty()) {
id = entry.id;
} else {
return {};
}
}
return id;
}
}
return QString();
}
QVariant PassManager::pass(const QString &passId) const
{
const auto it = std::find_if(m_entries.begin(), m_entries.end(), [passId](const auto &entry) {
return entry.id == passId;
});
return it == m_entries.end() ? QVariant(): (*it).data;
}
QVariant PassManager::data(const QModelIndex &index, int role) const
{
if (!checkIndex(index)) {
......
......@@ -36,6 +36,16 @@ public:
bool import(const QVariant &pass, const QString &id = {});
bool import(const QVector<QVariant> &passes);
/** Returns the pass id for the pass that is the closest match
* to the given ProgramMembership object.
* This is useful for finding the membership pass based on potentially
* incomplete data from a ticket.
* An empty string is returned if no matching membership is found.
*/
Q_INVOKABLE QString findMatchingPass(const QVariant &pass) const;
/** Returns the pass object for @p passId. */
Q_INVOKABLE QVariant pass(const QString &passId) const;
int rowCount(const QModelIndex &parent = {}) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment