diff --git a/src/DataFilter.cpp b/src/DataFilter.cpp index 28ff6bc49..490a3efa8 100644 --- a/src/DataFilter.cpp +++ b/src/DataFilter.cpp @@ -102,7 +102,7 @@ void Leaf::validateFilter(DataFilter *df, Leaf *leaf) QString lookup = df->lookupMap.value(*(leaf->lvalue.n), ""); if (lookup == "") { - DataFiltererrors << QString("Unknown: %1").arg(*(leaf->lvalue.n)); + DataFiltererrors << QString("%1 is unknown").arg(*(leaf->lvalue.n)); } } break; @@ -113,13 +113,13 @@ void Leaf::validateFilter(DataFilter *df, Leaf *leaf) bool lhsType = Leaf::isNumber(df, leaf->lvalue.l); bool rhsType = Leaf::isNumber(df, leaf->rvalue.l); if (lhsType != rhsType) { - DataFiltererrors << QString("Type mismatch"); + DataFiltererrors << QString("comparing strings with numbers"); } // what about using string operations on a lhs/rhs that // are numeric? if ((lhsType || rhsType) && leaf->op >= MATCHES && leaf->op <= CONTAINS) { - DataFiltererrors << "Mixing string operations with numbers"; + DataFiltererrors << "using a string operations with a number"; } validateFilter(df, leaf->lvalue.l); @@ -165,13 +165,28 @@ QStringList DataFilter::parseFilter(QString query) if (DataFiltererrors.count() > 0) { // nope // Bzzzt, malformed - qDebug()<<"parse filter errors:"<print(treeRoot); + emit parseGood(); + + // get all fields... + QList allRides = main->metricDB->getAllMetricsFor(QDateTime(), QDateTime()); + + filenames.clear(); + + for (int i=0; ieval(this, treeRoot, allRides.at(i)); + if (result) { + filenames << allRides.at(i).getFileName(); + } + } } errors = DataFiltererrors; @@ -204,8 +219,10 @@ void DataFilter::configUpdate() // now add the ride metadata fields -- should be the same generally foreach(FieldDefinition field, main->rideMetadata()->getFields()) { QString underscored = field.name; - lookupMap.insert(field.name.replace(" ","_"), field.name); - lookupType.insert(field.name.replace(" ","_"), (field.type > 2)); // true if is number + if (!main->specialFields.isMetric(underscored)) { + lookupMap.insert(underscored.replace(" ","_"), field.name); + lookupType.insert(underscored.replace(" ","_"), (field.type > 2)); // true if is number + } } #if 0 @@ -218,39 +235,187 @@ void DataFilter::configUpdate() #endif } -bool Leaf::eval(Leaf *leaf, SummaryMetrics m) +bool Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) { switch(leaf->type) { case Leaf::Logical : + { switch (leaf->op) { case AND : - return (eval(leaf->lvalue.l, m) && eval(leaf->rvalue.l, m)); + return (eval(df, leaf->lvalue.l, m) && eval(df, leaf->rvalue.l, m)); case OR : - return (eval(leaf->lvalue.l, m) || eval(leaf->rvalue.l, m)); + return (eval(df, leaf->lvalue.l, m) || eval(df, leaf->rvalue.l, m)); } + } + break; case Leaf::Operation : { + double lhsdouble, rhsdouble; + QString lhsstring, rhsstring; + bool lhsisNumber, rhsisNumber; + + // GET LHS VALUE + switch (leaf->lvalue.l->type) { + + case Leaf::Symbol : + { + QString rename; + // get symbol value + if ((lhsisNumber = df->lookupType.value(*(leaf->lvalue.l->lvalue.n))) == true) { + // numeric + lhsdouble = m.getForSymbol(rename=df->lookupMap.value(*(leaf->lvalue.l->lvalue.n),"")); + //qDebug()<<"symbol" << *(leaf->lvalue.l->lvalue.n) << "is" << lhsdouble << "via" << rename; + } else { + // string + lhsstring = m.getText(rename=df->lookupMap.value(*(leaf->lvalue.l->lvalue.n),""), ""); + //qDebug()<<"symbol" << *(leaf->lvalue.l->lvalue.n) << "is" << lhsstring << "via" << rename; + } + } + break; + + case Leaf::Float : + lhsisNumber = true; + lhsdouble = leaf->lvalue.l->lvalue.f; + break; + + case Leaf::Integer : + lhsisNumber = true; + lhsdouble = leaf->lvalue.l->lvalue.i; + break; + + case Leaf::String : + lhsisNumber = false; + lhsstring = *(leaf->lvalue.l->lvalue.s); + break; + + default: + break; + } + + // GET RHS VALUE + switch (leaf->rvalue.l->type) { + + case Leaf::Symbol : + { + QString rename; + // get symbol value + if ((rhsisNumber=df->lookupType.value(*(leaf->rvalue.l->lvalue.n))) == true) { + // numeric + rhsdouble = m.getForSymbol(rename=df->lookupMap.value(*(leaf->rvalue.l->lvalue.n),"")); + //qDebug()<<"symbol" << *(leaf->rvalue.l->lvalue.n) << "is" << rhsdouble << "via" << rename; + } else { + // string + rhsstring = m.getText(rename=df->lookupMap.value(*(leaf->rvalue.l->lvalue.n),""), "notfound"); + //qDebug()<<"symbol" << *(leaf->rvalue.l->lvalue.n) << "is" << rhsstring << "via" << rename; + } + } + break; + + case Leaf::Float : + rhsisNumber = true; + rhsdouble = leaf->rvalue.l->lvalue.f; + break; + + case Leaf::Integer : + rhsisNumber = true; + rhsdouble = leaf->rvalue.l->lvalue.i; + break; + + case Leaf::String : + rhsisNumber = false; + rhsstring = *(leaf->rvalue.l->lvalue.s); + break; + + default: + break; + } + + // NOW PERFORM OPERATION switch (leaf->op) { case EQ: + { + if (lhsisNumber) { + return lhsdouble == rhsdouble; + } else { + return lhsstring == rhsstring; + } + } + break; + case NEQ: + { + if (lhsisNumber) { + return lhsdouble != rhsdouble; + } else { + return lhsstring != rhsstring; + } + } + break; + case LT: + { + if (lhsisNumber) { + return lhsdouble < rhsdouble; + } else { + return lhsstring < rhsstring; + } + } + break; case LTE: + { + if (lhsisNumber) { + return lhsdouble <= rhsdouble; + } else { + return lhsstring <= rhsstring; + } + } + break; case GT: + { + if (lhsisNumber) { + return lhsdouble > rhsdouble; + } else { + return lhsstring > rhsstring; + } + } + break; case GTE: + { + if (lhsisNumber) { + return lhsdouble >= rhsdouble; + } else { + return lhsstring >= rhsstring; + } + } + break; + case MATCHES: + return QRegExp(rhsstring).exactMatch(lhsstring); + break; + case ENDSWITH: + return lhsstring.endsWith(rhsstring); + break; + case BEGINSWITH: + return lhsstring.endsWith(rhsstring); + break; + case CONTAINS: + return lhsstring.endsWith(rhsstring); + break; + default: break; } } - break; - default: + break; + + default: // we don't need to evaluate any lower - they are leaf nodes handled above break; } return false; diff --git a/src/DataFilter.h b/src/DataFilter.h index 6e15b1384..c6218ac26 100644 --- a/src/DataFilter.h +++ b/src/DataFilter.h @@ -48,7 +48,7 @@ class Leaf { Leaf() : type(none) { } // evaluate against a SummaryMetric - bool eval(Leaf *, SummaryMetrics); + bool eval(DataFilter *df, Leaf *, SummaryMetrics); // tree traversal etc void print(Leaf *); // print leaf and all children @@ -77,6 +77,9 @@ class DataFilter : public QObject public: DataFilter(QObject *parent, MainWindow *main); + QStringList &files() { return filenames; } + + // used by Leaf QMap lookupMap; QMap lookupType; // true if a number, false if a string @@ -87,10 +90,14 @@ class DataFilter : public QObject //void setData(); // set the file list from the current filter + signals: + void parseGood(); + void parseBad(QStringList erorrs); + private: MainWindow *main; Leaf *treeRoot; QStringList errors; - QStringList files; + QStringList filenames; }; diff --git a/src/DataFilter.y b/src/DataFilter.y index 0a8b480cd..ef747d73e 100644 --- a/src/DataFilter.y +++ b/src/DataFilter.y @@ -114,7 +114,8 @@ lop : AND value : SYMBOL { $$ = new Leaf(); $$->type = Leaf::Symbol; $$->lvalue.n = new QString(DataFiltertext); } | STRING { $$ = new Leaf(); $$->type = Leaf::String; - $$->lvalue.s = new QString(DataFiltertext); } + QString s2(DataFiltertext); + $$->lvalue.s = new QString(s2.mid(1,s2.length()-2)); } | FLOAT { $$ = new Leaf(); $$->type = Leaf::Float; $$->lvalue.f = QString(DataFiltertext).toFloat(); } | INTEGER { $$ = new Leaf(); $$->type = Leaf::Integer; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 1cc8dccc3..df118e0ea 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -462,9 +462,11 @@ MainWindow::MainWindow(const QDir &home) : toolbuttons->addWidget(searchBox); //toolbuttons->addStretch(); connect(searchBox, SIGNAL(submitQuery(QString)), this, SLOT(searchSubmitted(QString))); - connect(searchBox, SIGNAL(submitFilter(QString)), datafilter, SLOT(parseFilter(QString))); + connect(searchBox, SIGNAL(submitFilter(QString)), this, SLOT(filterSubmitted(QString))); connect(searchBox, SIGNAL(clearQuery()), this, SLOT(searchCleared())); - connect(searchBox, SIGNAL(clearFilter()), datafilter, SLOT(clearFilter())); + connect(searchBox, SIGNAL(clearFilter()), this, SLOT(filterCleared())); + connect(datafilter, SIGNAL(parseGood()), searchBox, SLOT(setGood())); + connect(datafilter, SIGNAL(parseBad(QStringList)), searchBox, SLOT(setBad(QStringList))); #endif @@ -2240,4 +2242,15 @@ void MainWindow::searchCleared() { emit searchClear(); } +void MainWindow::filterSubmitted(QString query) +{ + QStringList errors = datafilter->parseFilter(query); + if (errors.count() == 0) + emit searchResults(datafilter->files()); +} + +void MainWindow::filterCleared() +{ + emit searchClear(); +} #endif diff --git a/src/MainWindow.h b/src/MainWindow.h index 49a3442c4..5d90d57f8 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -332,6 +332,8 @@ class MainWindow : public QMainWindow #endif #ifdef GC_HAVE_LUCENE + void filterSubmitted(QString); + void filterCleared(); void searchSubmitted(QString); void searchCleared(); #endif diff --git a/src/SearchBox.cpp b/src/SearchBox.cpp index c35554d05..aedb1f8f0 100644 --- a/src/SearchBox.cpp +++ b/src/SearchBox.cpp @@ -129,6 +129,8 @@ void SearchBox::updateCloseButton(const QString& text) clearButton->setVisible(!text.isEmpty()); if (mode == Search) searchSubmit(); // only do search as you type in search mode + + setGood(); // if user changing then don't stay red - wait till resubmitted } void SearchBox::searchSubmit() @@ -142,8 +144,29 @@ void SearchBox::searchSubmit() void SearchBox::clearClicked() { mode == Search ? clearQuery() : clearFilter(); + setGood(); } +void SearchBox::setBad(QStringList errors) +{ + QPalette pal; + pal.setColor(QPalette::Text, Qt::red); + setPalette(pal); + + setToolTip(errors.join(" and ")); +} + +void SearchBox::setGood() +{ + QPalette pal; + pal.setColor(QPalette::Text, Qt::black); + setPalette(pal); + + setToolTip(""); +} + + + // Drag and drop columns from the chooser... void SearchBox::dragEnterEvent(QDragEnterEvent *event) diff --git a/src/SearchBox.h b/src/SearchBox.h index 864e38c28..1f91cb58f 100644 --- a/src/SearchBox.h +++ b/src/SearchBox.h @@ -51,6 +51,10 @@ private slots: void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); + // highlight errors etc + void setBad(QStringList errors); + void setGood(); + signals: // text search mode void submitQuery(QString); diff --git a/src/SummaryMetrics.h b/src/SummaryMetrics.h index 7c556a023..f81e3d12f 100644 --- a/src/SummaryMetrics.h +++ b/src/SummaryMetrics.h @@ -28,27 +28,27 @@ class SummaryMetrics { public: // filename - QString getFileName() { return fileName; } + QString getFileName() const { return fileName; } void setFileName(QString fileName) { this->fileName = fileName; } // Identifier - QString getId() { return id; } + QString getId() const { return id; } void setId(QString id) { this->id = id; } // ride date - QDateTime getRideDate() { return rideDate; } + QDateTime getRideDate() const { return rideDate; } void setRideDate(QDateTime rideDate) { this->rideDate = rideDate; } // for non-rides, ie. measures use same field but overload - QDateTime getDateTime() { return rideDate; } + QDateTime getDateTime() const { return rideDate; } void setDateTime(QDateTime dateTime) { this->rideDate = dateTime; } // metric values void setForSymbol(QString symbol, double v) { value.insert(symbol, v); } double getForSymbol(QString symbol) const { return value.value(symbol, 0.0); } - void setText(QString name, QString v) { texts.insert(name, v); } - QString getText(QString name, QString fallback) { return texts.value(name, fallback); } + void setText(QString name, QString v) { text.insert(name, v); } + QString getText(QString name, QString fallback) { return text.value(name, fallback); } // convert to string, using format supplied // replaces ${...:units} or ${...} with unit string @@ -62,13 +62,14 @@ class SummaryMetrics QString getUnitsForSymbol(QString symbol, bool UseMetric) const; QMap &values() { return value; } + QMap &texts() { return text; } private: QString fileName; QString id; QDateTime rideDate; QMap value; - QMap texts; + QMap text; };