Skip to content

Commit

Permalink
Add support for 'age' post-filter (fix #1354)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bionus committed Aug 5, 2018
1 parent 49cc804 commit eca0c41
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 33 deletions.
119 changes: 86 additions & 33 deletions lib/src/models/filtering/meta-filter.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "meta-filter.h"
#include <QDateTime>
#include <QRegExp>
#include <QRegularExpression>
#include <QStringBuilder>
#include <QTimeZone>


MetaFilter::MetaFilter(QString type, QString val, bool invert)
Expand All @@ -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())
Expand All @@ -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 <typename T>
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<QString, Token> &tokens, bool invert) const
{
if (m_invert)
Expand All @@ -52,6 +116,25 @@ QString MetaFilter::match(const QMap<QString, Token> &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))
{
Expand All @@ -72,39 +155,9 @@ QString MetaFilter::match(const QMap<QString, Token> &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); }
Expand Down
20 changes: 20 additions & 0 deletions tests/src/models/filtering/meta-filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,25 @@ void MetaFilterTest::testMatchString()
QCOMPARE(MetaFilter("meta", "nok", true).match(tokens), QString());
}

void MetaFilterTest::testMatchAge()
{
QMap<QString, Token> 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)
1 change: 1 addition & 0 deletions tests/src/models/filtering/meta-filter-test.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class MetaFilterTest : public TestSuite
void testMatchRating();
void testMatchSource();
void testMatchString();
void testMatchAge();
};

#endif // META_FILTER_TEST_H

0 comments on commit eca0c41

Please sign in to comment.