From eca0c41c9f5551ec2fb388e5fafa14b28b7be0dc Mon Sep 17 00:00:00 2001 From: Bionus Date: Sun, 5 Aug 2018 13:45:41 +0200 Subject: [PATCH] Add support for 'age' post-filter (fix #1354) --- lib/src/models/filtering/meta-filter.cpp | 119 +++++++++++++----- .../src/models/filtering/meta-filter-test.cpp | 20 +++ tests/src/models/filtering/meta-filter-test.h | 1 + 3 files changed, 107 insertions(+), 33 deletions(-) diff --git a/lib/src/models/filtering/meta-filter.cpp b/lib/src/models/filtering/meta-filter.cpp index 96e2a6a0b..518279e6e 100644 --- a/lib/src/models/filtering/meta-filter.cpp +++ b/lib/src/models/filtering/meta-filter.cpp @@ -1,7 +1,9 @@ #include "meta-filter.h" #include #include +#include #include +#include MetaFilter::MetaFilter(QString type, QString val, bool invert) @@ -22,7 +24,7 @@ bool MetaFilter::compare(const Filter& rhs) const return m_type == other->m_type && m_val == other->m_val; } -static int toDate(const QString &text) +static int dateToInt(const QString &text) { QDateTime date = QDateTime::fromString(text, "yyyy-MM-dd"); if (date.isValid()) @@ -33,6 +35,68 @@ static int toDate(const QString &text) return 0; } +static int stringToInt(const QString &text) +{ return text.toInt(); } + +// FIXME(Bionus): remove globals +static QDateTime ageToDateImage; +static QDateTime ageToDateTestNow; +static QDateTime ageToDate(const QString &text) +{ + static QRegularExpression rx("^(\\d+)(\\w+)$"); + auto match = rx.match(text); + if (!match.hasMatch()) + return QDateTime(); + + const int count = match.captured(1).toInt(); + const QString type = match.captured(2); + + // Define "now" with the correct timezone + QDateTime base; + if (ageToDateTestNow.isValid()) + { base = ageToDateTestNow; } + else + { + base = QDateTime::currentDateTimeUtc(); + base.setTimeZone(ageToDateImage.timeZone()); + } + + if (type.startsWith("y")) + return base.addYears(-count); + if (type.startsWith("mo")) + return base.addMonths(-count); + if (type.startsWith("w")) + return base.addDays(-(count * 7)); + if (type.startsWith("d")) + return base.addDays(-count); + if (type.startsWith("h")) + return base.addSecs(-(count * 60 * 60)); + if (type.startsWith("mi")) + return base.addSecs(-(count * 60)); + if (type.startsWith("s")) + return base.addSecs(-count); + + return QDateTime(); +} + +template +static bool rangeCheck(T (*converter)(const QString &), T input, const QString &val) +{ + if (val.startsWith("..") || val.startsWith("<=")) + { return input <= converter(val.right(val.size() - 2)); } + if (val.endsWith("..")) + { return input >= converter(val.left(val.size() - 2)); } + if (val.startsWith(">=")) + { return input >= converter(val.right(val.size() - 2)); } + if (val.startsWith("<")) + { return input < converter(val.right(val.size() - 1)); } + if (val.startsWith(">")) + { return input > converter(val.right(val.size() - 1)); } + if (val.contains("..")) + { return input >= converter(val.left(val.indexOf(".."))) && input <= converter(val.right(val.size() - val.indexOf("..") - 2)); } + return input == converter(val); +} + QString MetaFilter::match(const QMap &tokens, bool invert) const { if (m_invert) @@ -52,6 +116,25 @@ QString MetaFilter::match(const QMap &tokens, bool invert) const return QString(); } + // Non-token metas + if (m_type == "age") + { + if (!tokens.contains("date")) + { return QObject::tr("An image needs a date to be filtered by age"); } + + const QDateTime &date = tokens["date"].value().toDateTime(); + ageToDateImage = date; + ageToDateTestNow = tokens["TESTS_now"].value().toDateTime(); + bool cond = rangeCheck(ageToDate, date, m_val); + + if (cond && !invert) + { return QObject::tr("image's %1 does not match").arg(m_type); } + if (!cond && invert) + { return QObject::tr("image's %1 match").arg(m_type); } + + return QString(); + } + // Meta tokens if (!tokens.contains(m_type)) { @@ -72,39 +155,9 @@ QString MetaFilter::match(const QMap &tokens, bool invert) const bool cond; if (token.type() == QVariant::DateTime) - { - if (m_val.startsWith("..") || m_val.startsWith("<=")) - { cond = input <= toDate(m_val.right(m_val.size() - 2)); } - else if (m_val.endsWith("..")) - { cond = input >= toDate(m_val.left(m_val.size() - 2)); } - else if (m_val.startsWith(">=")) - { cond = input >= toDate(m_val.right(m_val.size() - 2)); } - else if (m_val.startsWith("<")) - { cond = input < toDate(m_val.right(m_val.size() - 1)); } - else if (m_val.startsWith(">")) - { cond = input > toDate(m_val.right(m_val.size() - 1)); } - else if (m_val.contains("..")) - { cond = input >= toDate(m_val.left(m_val.indexOf(".."))) && input <= toDate(m_val.right(m_val.size() - m_val.indexOf("..") - 2)); } - else - { cond = input == toDate(m_val); } - } + { cond = rangeCheck(dateToInt, input, m_val); } else - { - if (m_val.startsWith("..") || m_val.startsWith("<=")) - { cond = input <= m_val.rightRef(m_val.size() - 2).toInt(); } - else if (m_val.endsWith("..")) - { cond = input >= m_val.leftRef(m_val.size() - 2).toInt(); } - else if (m_val.startsWith(">=")) - { cond = input >= m_val.rightRef(m_val.size() - 2).toInt(); } - else if (m_val.startsWith("<")) - { cond = input < m_val.rightRef(m_val.size() - 1).toInt(); } - else if (m_val.startsWith(">")) - { cond = input > m_val.rightRef(m_val.size() - 1).toInt(); } - else if (m_val.contains("..")) - { cond = input >= m_val.leftRef(m_val.indexOf("..")).toInt() && input <= m_val.rightRef(m_val.size() - m_val.indexOf("..") - 2).toInt(); } - else - { cond = input == m_val.toInt(); } - } + { cond = rangeCheck(stringToInt, input, m_val); } if (!cond && !invert) { return QObject::tr("image's %1 does not match").arg(m_type); } diff --git a/tests/src/models/filtering/meta-filter-test.cpp b/tests/src/models/filtering/meta-filter-test.cpp index aaf4fe909..3fb04f45c 100644 --- a/tests/src/models/filtering/meta-filter-test.cpp +++ b/tests/src/models/filtering/meta-filter-test.cpp @@ -134,5 +134,25 @@ void MetaFilterTest::testMatchString() QCOMPARE(MetaFilter("meta", "nok", true).match(tokens), QString()); } +void MetaFilterTest::testMatchAge() +{ + QMap tokens; + QCOMPARE(MetaFilter("age", "1year..1day").match(tokens), QString("An image needs a date to be filtered by age")); + + tokens.insert("date", Token(QDateTime(QDate(2016, 8, 18)))); + tokens.insert("TESTS_now", Token(QDateTime(QDate(2016, 10, 16)))); + + // Basic + QCOMPARE(MetaFilter("age", ">=2hours").match(tokens), QString()); + QCOMPARE(MetaFilter("age", ">1day").match(tokens), QString()); + QCOMPARE(MetaFilter("age", ">1mo").match(tokens), QString()); + QCOMPARE(MetaFilter("age", ">=1y").match(tokens), QString("image's age does not match")); + QCOMPARE(MetaFilter("age", "<1year").match(tokens), QString()); + + // Invert + QCOMPARE(MetaFilter("age", ">=1y", true).match(tokens), QString()); + QCOMPARE(MetaFilter("age", "<1year", true).match(tokens), QString("image's age match")); +} + QTEST_MAIN(MetaFilterTest) diff --git a/tests/src/models/filtering/meta-filter-test.h b/tests/src/models/filtering/meta-filter-test.h index 1f33d0a9f..d2854a3ef 100644 --- a/tests/src/models/filtering/meta-filter-test.h +++ b/tests/src/models/filtering/meta-filter-test.h @@ -18,6 +18,7 @@ class MetaFilterTest : public TestSuite void testMatchRating(); void testMatchSource(); void testMatchString(); + void testMatchAge(); }; #endif // META_FILTER_TEST_H