Migrate TreeMap LTMPopup to use RideCache

.. the other half, LTMWindow using LTMPopup is now commented
   out and will need to be fixed when LTMWindow migrates to RideCache
This commit is contained in:
Mark Liversedge
2014-12-17 14:36:21 +00:00
parent 0697af7e9a
commit bf7c80b73d
8 changed files with 160 additions and 38 deletions

View File

@@ -19,6 +19,8 @@
#include "LTMPopup.h"
#include "MainWindow.h"
#include "Athlete.h"
#include "Specification.h"
#include "RideCache.h"
LTMPopup::LTMPopup(Context *context) : QWidget(context->mainWindow), context(context)
{
@@ -108,7 +110,7 @@ LTMPopup::setTitle(QString s)
}
void
LTMPopup::setData(QList<SummaryMetrics>data, const RideMetric *metric, QString title)
LTMPopup::setData(Specification spec, const RideMetric *metric, QString title)
{
// create the ride list
int count = 0;
@@ -129,15 +131,15 @@ LTMPopup::setData(QList<SummaryMetrics>data, const RideMetric *metric, QString t
rides->setHorizontalHeaderItem(1,h);
// now add rows to the table for each entry
foreach(SummaryMetrics x, data) {
foreach(RideItem *item, context->athlete->rideCache->rides()) {
QDateTime rideDate = x.getRideDate();
if (!spec.pass(item)) continue;
// we'll select it for summary aggregation
selected << x;
// what rides were selected ?
selected << item->fileName;
// date/time
QTableWidgetItem *t = new QTableWidgetItem(rideDate.toString(tr("ddd, dd MMM yy hh:mmA")));
QTableWidgetItem *t = new QTableWidgetItem(item->dateTime.toString(tr("ddd, dd MMM yy hh:mmA")));
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
t->setTextAlignment(Qt::AlignHCenter);
rides->setRowCount(count+1);
@@ -145,13 +147,15 @@ LTMPopup::setData(QList<SummaryMetrics>data, const RideMetric *metric, QString t
rides->setRowHeight(count, 14);
// metrics
QString value = x.getStringForSymbol(metric->symbol(), context->athlete->useMetricUnits);
double d = item->getForSymbol(metric->symbol());
const_cast<RideMetric *>(metric)->setValue(d);
QString value = metric->toString(context->athlete->useMetricUnits);
h = new QTableWidgetItem(value,QTableWidgetItem::Type);
h->setFlags(t->flags() & (~Qt::ItemIsEditable));
h->setTextAlignment(Qt::AlignHCenter);
rides->setItem(count, 1, h);
count++;
}
@@ -184,8 +188,9 @@ LTMPopup::setData(QList<SummaryMetrics>data, const RideMetric *metric, QString t
}
void
LTMPopup::setData(LTMSettings &settings, QDate start, QDate end, QTime time)
LTMPopup::setData(LTMSettings &, QDate, QDate, QTime)
{
#if 0 // DISABLED IN LTM WINDOW UNTIL IT IS MIGRATED TO RIDECACHE
// set the title
QString _title;
switch (settings.groupBy) {
@@ -342,12 +347,12 @@ LTMPopup::setData(LTMSettings &settings, QDate start, QDate end, QTime time)
if (nonRideMetrics) _title += QString(tr(" / non ride-related metrics skipped"));
setTitle(_title);
rideSelected();
#endif
}
QString
LTMPopup::setSummaryHTML(SummaryMetrics &results)
LTMPopup::setSummaryHTML(RideItem *item)
{
// where we construct the text
QString summaryText("");
@@ -435,12 +440,13 @@ LTMPopup::setSummaryHTML(SummaryMetrics &results)
const RideMetric *metric = RideMetricFactory::instance().rideMetric(metricname);
QStringList empty; // filter list not used at present
QList<SummaryMetrics> resultList1;
resultList1 << results;
// use getAggregated even if it's only 1 file to have consistent value treatment
QString value = SummaryMetrics::getAggregated(context, metricname, resultList1, empty, false, context->athlete->useMetricUnits);
// use getAggregate even if it's only 1 file to have consistent value treatment
double d = item->getForSymbol(metricname, true);
QString value;
if (metric) {
const_cast<RideMetric*>(metric)->setValue(d);
value = metric->toString(context->athlete->useMetricUnits);
}
// Maximum Max and Average Average looks nasty, remove from name for display
QString s = metric ? metric->name().replace(QRegExp(tr("^(Average|Max) ")), "") : "unknown";
@@ -475,21 +481,30 @@ LTMPopup::setSummaryHTML(SummaryMetrics &results)
return summaryText;
}
void
LTMPopup::rideSelected()
{
// which ride is selected
int index = 0;
foreach (QTableWidgetItem *item, rides->selectedItems())
index = item->row();
if (rides->selectedItems().count())
index = rides->selectedItems().first()->row();
// do we have any rides and is the index within bounds
if (selected.count() > index) {
// update summary
metrics->setText(setSummaryHTML(selected[index]));
notes->setText(selected[index].getText("Notes", ""));
RideItem *have = context->athlete->rideCache->getRide(selected[index]);
if (have) {
// update summary
metrics->setText(""); //! stop crash (?)
metrics->setText(setSummaryHTML(have));
notes->setText(""); //! stop crash (?)
notes->setText(have->getText("Notes", ""));
}
}
resizeEvent(NULL);
}

View File

@@ -37,6 +37,7 @@
#include <QTableWidget>
#include <QTableWidgetItem>
class Specification;
class LTMPopup : public QWidget
{
Q_OBJECT
@@ -52,7 +53,7 @@ class LTMPopup : public QWidget
void setData(LTMSettings &settings, QDate start, QDate end, QTime time);
// when called from a TreeMap chart
void setData(QList<SummaryMetrics>data, const RideMetric *metric, QString title);
void setData(Specification spec, const RideMetric *metric, QString title);
signals:
@@ -69,11 +70,9 @@ class LTMPopup : public QWidget
QTextEdit *metrics;
QTextEdit *notes;
QList<SummaryMetrics> selected;
// builds HTML text for the selected ride
QString setSummaryHTML(SummaryMetrics &results);
// builds HTML text for the selected ride(s)
QString setSummaryHTML(RideItem*);
QStringList selected; // filenames
};
// some geometry stuff to make Mac displays nice

View File

@@ -23,6 +23,7 @@
#include "Context.h"
#include "Athlete.h"
#include "RideFileCache.h"
#include "Specification.h"
#include "Route.h"
#include "RouteWindow.h"
@@ -325,6 +326,92 @@ RideCache::refresh()
}
}
QString
RideCache::getAggregate(QString name, Specification spec, bool useMetricUnits, bool nofmt)
{
// get the metric details, so we can convert etc
const RideMetric *metric = RideMetricFactory::instance().rideMetric(name);
if (!metric) return QString("%1 unknown").arg(name);
// what we will return
double rvalue = 0;
double rcount = 0; // using double to avoid rounding issues with int when dividing
// loop through and aggregate
foreach (RideItem *item, rides()) {
// skip filtered rides
if (!spec.pass(item)) continue;
// get this value
double value = item->getForSymbol(name);
double count = item->getForSymbol("workout_time"); // for averaging
// check values are bounded, just in case
if (isnan(value) || isinf(value)) value = 0;
// imperial / metric conversion
if (useMetricUnits == false) {
value *= metric->conversion();
value += metric->conversionSum();
}
// do we aggregate zero values ?
bool aggZero = metric->aggregateZero();
// set aggZero to false and value to zero if is temperature and -255
if (metric->symbol() == "average_temp" && value == RideFile::NoTemp) {
value = 0;
aggZero = false;
}
switch (metric->type()) {
case RideMetric::Total:
rvalue += value;
break;
case RideMetric::Average:
{
// average should be calculated taking into account
// the duration of the ride, otherwise high value but
// short rides will skew the overall average
if (value || aggZero) {
rvalue += value*count;
rcount += count;
}
break;
}
case RideMetric::Low:
{
if (value < rvalue) rvalue = value;
break;
}
case RideMetric::Peak:
{
if (value > rvalue) rvalue = value;
break;
}
}
}
// now compute the average
if (metric->type() == RideMetric::Average) {
if (rcount) rvalue = rvalue / rcount;
}
// Format appropriately
QString result;
if (metric->units(useMetricUnits) == "seconds" ||
metric->units(useMetricUnits) == tr("seconds")) {
if (nofmt) result = QString("%1").arg(rvalue);
else result = time_to_string(rvalue);
} else result = QString("%1").arg(rvalue, 0, 'f', metric->precision());
// 0 temp from aggregate means no values
if ((metric->symbol() == "average_temp" || metric->symbol() == "max_temp") && result == "0.0") result = "-";
return result;
}
class RollingBests {
private:

View File

@@ -38,6 +38,7 @@
class Context;
class RideCacheBackgroundRefresh;
class Specification;
class RideCache : public QObject
{
@@ -53,6 +54,9 @@ class RideCache : public QObject
QList<QDateTime> getAllDates();
QStringList getAllFilenames();
// get an aggregate applying the passed spec
QString getAggregate(QString name, Specification spec, bool useMetricUnits, bool nofmt=false);
// metadata
QHash<QString,int> getRankedValues(QString name); // metadata
QStringList getDistinctValues(QString name); // metadata

View File

@@ -70,7 +70,7 @@ RideMetric::computeMetrics(const Context *context, const RideFile *ride, const Z
}
QString
RideMetric::toString(bool useMetricUnits)
RideMetric::toString(bool useMetricUnits) const
{
if (isTime()) return time_to_string(value(useMetricUnits));
return QString("%1").arg(value(useMetricUnits), 0, 'f', precision());

View File

@@ -118,7 +118,7 @@ public:
virtual bool isTime() const { return false; }
// Convert value to string, taking into account metric pref
virtual QString toString(bool useMetricUnits);
virtual QString toString(bool useMetricUnits) const;
// Fill in the value of the ride metric using the mapping provided. For
// example, average speed might be specified by the mapping

View File

@@ -72,6 +72,9 @@ class Specification
void setDateRange(DateRange dr);
void setFilterSet(FilterSet fs);
DateRange dateRange() { return dr; }
FilterSet filterSet() { return fs; }
private:
DateRange dr;
FilterSet fs;

View File

@@ -22,6 +22,7 @@
#include "LTMSettings.h"
#include "Context.h"
#include "Athlete.h"
#include "RideCache.h"
#include "Settings.h"
#include "math.h"
#include "Units.h" // for MILES_PER_KM
@@ -270,27 +271,40 @@ TreeMapWindow::fieldSelected(int)
void
TreeMapWindow::cellClicked(QString f1, QString f2)
{
QList<SummaryMetrics> cell;
QStringList match;
// create a list of activities in this cell
int count = 0;
foreach(SummaryMetrics x, results) {
foreach(RideItem *item, context->athlete->rideCache->rides()) {
// honour the settings
if (!settings.specification.pass(item)) continue;
// text may either not exists, then "unknown" or just be "" but f1, f2 don't know ""
QString x1 = x.getText(settings.field1, tr("(unknown)"));
QString x2 = x.getText(settings.field2, tr("(unknown)"));
QString x1 = item->getText(settings.field1, tr("(unknown)"));
QString x2 = item->getText(settings.field2, tr("(unknown)"));
if (x1 == "") x1 = tr("(unknown)");
if (x2 == "") x2 = tr("(unknown)");
// now we can compare and append
// match !
if (x1 == f1 && x2 == f2) {
cell.append(x);
match << item->fileName;
count++;
}
}
// create a specification for ours
Specification spec;
spec.setDateRange(settings.specification.dateRange());
FilterSet fs = settings.specification.filterSet();
fs.addFilter(true, match);
spec.setFilterSet(fs);
// and the metric to display
const RideMetricFactory &factory = RideMetricFactory::instance();
const RideMetric *metric = factory.rideMetric(settings.symbol);
ltmPopup->setData(cell, metric, QString(tr("%1 ride%2")).arg(count).arg(count == 1 ? "" : tr("s")));
ltmPopup->setData(spec, metric, QString(tr("%1 ride%2")).arg(count).arg(count == 1 ? "" : tr("s")));
popup->show();
}