FilterParser: Add ability to filter by rating
Playlists can now be filtered by the rating from 0-5 like: rating:0 rating:<3 rating:!=0 or by a float value, like: rating:f0.1 rating:>=f0.5
This commit is contained in:
parent
6af8e6c25b
commit
1deacaecf9
|
@ -56,6 +56,7 @@ PlaylistFilter::PlaylistFilter(QObject *parent)
|
||||||
column_names_["filename"] = Playlist::Column_Filename;
|
column_names_["filename"] = Playlist::Column_Filename;
|
||||||
column_names_["grouping"] = Playlist::Column_Grouping;
|
column_names_["grouping"] = Playlist::Column_Grouping;
|
||||||
column_names_["comment"] = Playlist::Column_Comment;
|
column_names_["comment"] = Playlist::Column_Comment;
|
||||||
|
column_names_["rating"] = Playlist::Column_Rating;
|
||||||
|
|
||||||
numerical_columns_ << Playlist::Column_Year
|
numerical_columns_ << Playlist::Column_Year
|
||||||
<< Playlist::Column_OriginalYear
|
<< Playlist::Column_OriginalYear
|
||||||
|
|
|
@ -117,6 +117,67 @@ class LexicalLeComparator : public SearchTermComparator {
|
||||||
QString search_term_;
|
QString search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Float Comparators are for the rating
|
||||||
|
class FloatEqComparator : public SearchTermComparator {
|
||||||
|
public:
|
||||||
|
explicit FloatEqComparator(const float value) : search_term_(value) {}
|
||||||
|
bool Matches(const QString &element) const override {
|
||||||
|
return search_term_ == element.toFloat();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
float search_term_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatNeComparator : public SearchTermComparator {
|
||||||
|
public:
|
||||||
|
explicit FloatNeComparator(const float &value) : search_term_(value) {}
|
||||||
|
bool Matches(const QString &element) const override {
|
||||||
|
return search_term_ != element.toFloat();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
float search_term_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatGtComparator : public SearchTermComparator {
|
||||||
|
public:
|
||||||
|
explicit FloatGtComparator(const float &value) : search_term_(value) {}
|
||||||
|
bool Matches(const QString &element) const override {
|
||||||
|
return element.toFloat() > search_term_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
float search_term_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatGeComparator : public SearchTermComparator {
|
||||||
|
public:
|
||||||
|
explicit FloatGeComparator(const float &value) : search_term_(value) {}
|
||||||
|
bool Matches(const QString &element) const override {
|
||||||
|
return element.toFloat() >= search_term_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
float search_term_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatLtComparator : public SearchTermComparator {
|
||||||
|
public:
|
||||||
|
explicit FloatLtComparator(const float &value) : search_term_(value) {}
|
||||||
|
bool Matches(const QString &element) const override {
|
||||||
|
return element.toFloat() < search_term_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
float search_term_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatLeComparator : public SearchTermComparator {
|
||||||
|
public:
|
||||||
|
explicit FloatLeComparator(const float &value) : search_term_(value) {}
|
||||||
|
bool Matches(const QString &element) const override {
|
||||||
|
return element.toFloat() <= search_term_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
float search_term_;
|
||||||
|
};
|
||||||
|
|
||||||
class GtComparator : public SearchTermComparator {
|
class GtComparator : public SearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit GtComparator(const int value) : search_term_(value) {}
|
explicit GtComparator(const int value) : search_term_(value) {}
|
||||||
|
@ -454,7 +515,34 @@ FilterTree *FilterParser::createSearchTermTreeNode(const QString &col, const QSt
|
||||||
// here comes a mess :/
|
// here comes a mess :/
|
||||||
// well, not that much of a mess, but so many options -_-
|
// well, not that much of a mess, but so many options -_-
|
||||||
SearchTermComparator *cmp = nullptr;
|
SearchTermComparator *cmp = nullptr;
|
||||||
if (prefix == "!=" || prefix == "<>") {
|
|
||||||
|
// Handle the float based Rating Column
|
||||||
|
if (columns_[col] == Playlist::Column_Rating) {
|
||||||
|
float parsedSearch = parseRating(search);
|
||||||
|
|
||||||
|
if (prefix == "=") {
|
||||||
|
cmp = new FloatEqComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
else if (prefix == "!=" || prefix == "<>") {
|
||||||
|
cmp = new FloatNeComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
else if (prefix == ">") {
|
||||||
|
cmp = new FloatGtComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
else if (prefix == ">=") {
|
||||||
|
cmp = new FloatGeComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
else if (prefix == "<") {
|
||||||
|
cmp = new FloatLtComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
else if (prefix == "<=") {
|
||||||
|
cmp = new FloatLeComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmp = new FloatEqComparator(parsedSearch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (prefix == "!=" || prefix == "<>") {
|
||||||
cmp = new NeComparator(search);
|
cmp = new NeComparator(search);
|
||||||
}
|
}
|
||||||
else if (!col.isEmpty() && columns_.contains(col) && numerical_columns_.contains(columns_[col])) {
|
else if (!col.isEmpty() && columns_.contains(col) && numerical_columns_.contains(columns_[col])) {
|
||||||
|
@ -504,6 +592,7 @@ FilterTree *FilterParser::createSearchTermTreeNode(const QString &col, const QSt
|
||||||
cmp = new DefaultComparator(search);
|
cmp = new DefaultComparator(search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columns_.contains(col)) {
|
if (columns_.contains(col)) {
|
||||||
if (columns_[col] == Playlist::Column_Length) {
|
if (columns_[col] == Playlist::Column_Length) {
|
||||||
cmp = new DropTailComparatorDecorator(cmp);
|
cmp = new DropTailComparatorDecorator(cmp);
|
||||||
|
@ -552,3 +641,38 @@ int FilterParser::parseTime(const QString &time_str) {
|
||||||
seconds = seconds * 60 + accum;
|
seconds = seconds * 60 + accum;
|
||||||
return seconds;
|
return seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The rating column contains the rating as a float from 0-1 or -1 if unrated.
|
||||||
|
// If the rating is a number from 0-5, map it to 0-1
|
||||||
|
// To use float values directly, the search term can be prefixed with "f" (rating:>f0.2)
|
||||||
|
// If search is 0, or by default, uses -1
|
||||||
|
float FilterParser::parseRating(const QString &rating_str) {
|
||||||
|
if (rating_str.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
float rating = -1;
|
||||||
|
bool ok = false;
|
||||||
|
float rating_input = rating_str.toFloat(&ok);
|
||||||
|
// is valid int from 0-5: convert to float
|
||||||
|
if (ok && rating_input >= 0 && rating_input <= 5) {
|
||||||
|
rating = rating_input / 5.;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the search is a float
|
||||||
|
else if (rating_str.at(0) == 'f') {
|
||||||
|
QString rating_float = rating_str;
|
||||||
|
rating_float = rating_float.remove(0, 1);
|
||||||
|
|
||||||
|
ok = false;
|
||||||
|
rating_float.toFloat(&ok);
|
||||||
|
if (ok) {
|
||||||
|
rating = rating_float.toFloat(&ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Songs with zero rating have -1 in the DB
|
||||||
|
if (rating == 0) {
|
||||||
|
rating = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rating;
|
||||||
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ class FilterParser {
|
||||||
|
|
||||||
FilterTree *createSearchTermTreeNode(const QString &col, const QString &prefix, const QString &search) const;
|
FilterTree *createSearchTermTreeNode(const QString &col, const QString &prefix, const QString &search) const;
|
||||||
static int parseTime(const QString &time_str);
|
static int parseTime(const QString &time_str);
|
||||||
|
static float parseRating(const QString &rating_str);
|
||||||
|
|
||||||
QString::const_iterator iter_;
|
QString::const_iterator iter_;
|
||||||
QString::const_iterator end_;
|
QString::const_iterator end_;
|
||||||
|
|
Loading…
Reference in New Issue