Skip to content

Commit

Permalink
Add sport field to RideItem/RideDB as primary field
Browse files Browse the repository at this point in the history
Exiting isRun/isSwim are preserved and new isRide/isXtrain added.
Use them in DataFilter to provide isRide, isRun, isSwim and isXtrain
and in RideSummary for better filtering of activities in rides, runs
swims and xtrains.
Part 1 of #3280
  • Loading branch information
amtriathlon committed Apr 6, 2020
1 parent f0315cc commit 42fa49c
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 24 deletions.
122 changes: 115 additions & 7 deletions src/Charts/RideSummaryWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,8 @@ RideSummaryWindow::htmlSummary()
pmc = context->athlete->getPMCFor(
rideItem->isSwim ? "swimscore" :
rideItem->isRun ? "govss" :
"coggan_tss");
rideItem->isBike ? "coggan_tss" :
"triscore");
} else {
// For data range use base metric for single sport if homogeneous
// or combined if mixed
Expand Down Expand Up @@ -1158,9 +1159,11 @@ RideSummaryWindow::htmlSummary()
int runs = 0;
int rides = 0;
int swims = 0;
int xtrains = 0;
int totalruns = 0;
int totalrides = 0;
int totalswims = 0;
int totalxtrains = 0;

foreach (RideItem *item, context->athlete->rideCache->rides()) {

Expand All @@ -1169,8 +1172,10 @@ RideSummaryWindow::htmlSummary()
totalruns++;
} else if (item->isSwim) {
totalswims++;
} else {
} else if (item->isBike) {
totalrides++;
} else {
totalxtrains++;
}

if (!specification.pass(item)) continue;
Expand All @@ -1180,21 +1185,24 @@ RideSummaryWindow::htmlSummary()
runs++;
} else if (item->isSwim) {
swims++;
} else {
} else if (item->isBike) {
rides++;
} else {
xtrains++;
}
activities++;
}

// Select relevant metrics for activities of each sport
QStringList rideMetrics, runMetrics, swimMetrics;
QStringList rideMetrics, runMetrics, swimMetrics, xtrainMetrics;
for (j = 0; j< metricColumn.count(); ++j) {
QString symbol = metricColumn[j];
const RideMetric *m = factory.rideMetric(symbol);

if (context->athlete->rideCache->isMetricRelevantForRides(specification, m, RideCache::OnlyRides)) rideMetrics << symbol;
if (context->athlete->rideCache->isMetricRelevantForRides(specification, m, RideCache::OnlyRuns)) runMetrics << symbol;
if (context->athlete->rideCache->isMetricRelevantForRides(specification, m, RideCache::OnlySwims)) swimMetrics << symbol;
if (context->athlete->rideCache->isMetricRelevantForRides(specification, m, RideCache::OnlyXtrains)) xtrainMetrics << symbol;
}

// some people have a LOT of metrics, so we only show so many since
Expand All @@ -1207,6 +1215,7 @@ RideSummaryWindow::htmlSummary()
int rideCols = rideMetrics.count() > 7 ? 7 : rideMetrics.count();
int runCols = runMetrics.count() > 7 ? 7 : runMetrics.count();
int swimCols = swimMetrics.count() > 7 ? 7 : swimMetrics.count();
int xtrainCols = xtrainMetrics.count() > 7 ? 7 : xtrainMetrics.count();

//Rides first
if (context->ishomefiltered || context->isfiltered || filtered) {
Expand Down Expand Up @@ -1278,7 +1287,7 @@ RideSummaryWindow::htmlSummary()
// apply the filter if there is one active
if (!specification.pass(ride)) continue;

if (ride->isRun || ride->isSwim) continue;
if (!ride->isBike) continue;

if (even) summary += "<tr>";
else {
Expand Down Expand Up @@ -1407,7 +1416,7 @@ RideSummaryWindow::htmlSummary()
}
summary += "</table><br>";

//Swims Last
// and Swims
if (context->ishomefiltered || context->isfiltered || filtered) {

// "n of x activities" shown in header of list when filtered
Expand Down Expand Up @@ -1506,6 +1515,105 @@ RideSummaryWindow::htmlSummary()
}
summary += "</table><br>";

//xtrains last
if (context->ishomefiltered || context->isfiltered || filtered) {

// "n of x activities" shown in header of list when filtered
summary += ("<p><h3>" +
QString(tr("%1 of %2")).arg(xtrains).arg(totalxtrains)
+ (totalxtrains == 1 ? tr(" xtrain") : tr(" xtrains")) +
"</h3><p>");
} else {

// just "n activities" shown in header of list when not filtered
summary += ("<p><h3>" +
QString("%1").arg(xtrains) + (xtrains == 1 ? tr(" xtrain") : tr(" xtrains")) +
"</h3><p>");
}

// table of activities
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";

// header row 1 - name
summary += "<tr>";
summary += tr("<td align=\"center\">Date</td>");
for (j = 0; j< totalCols; ++j) {
QString symbol = rtotalColumn[j];
const RideMetric *m = factory.rideMetric(symbol);

summary += QString("<td align=\"center\">%1</td>").arg(addTooltip(m->name(), m->description()));
}
for (j = 0; j< xtrainCols; ++j) {
QString symbol = xtrainMetrics[j];
const RideMetric *m = factory.rideMetric(symbol);

summary += QString("<td align=\"center\">%1</td>").arg(addTooltip(m->name(), m->description()));
}
summary += "</tr>";

// header row 2 - units
summary += "<tr>";
summary += tr("<td align=\"center\"></td>"); // date no units
for (j = 0; j< totalCols; ++j) {
QString symbol = rtotalColumn[j];
const RideMetric *m = factory.rideMetric(symbol);

QString units = m->units(useMetricUnits);
if (units == "seconds" || units == tr("seconds")) units = "";
summary += QString("<td align=\"center\">%1</td>").arg(units);
}
for (j = 0; j< xtrainCols; ++j) {
QString symbol = xtrainMetrics[j];
const RideMetric *m = factory.rideMetric(symbol);

QString units = m->units(useMetricUnits);
if (units == "seconds" || units == tr("seconds")) units = "";
summary += QString("<td align=\"center\">%1</td>").arg(units);
}
summary += "</tr>";

// activities 1 per row - in reverse order
even = false;

// iterate once again
ridelist.toBack();
while (ridelist.hasPrevious()) {

RideItem *ride = ridelist.previous();

// apply the filter if there is one active
if (!specification.pass(ride)) continue;

if (ride->isBike || ride->isRun || ride->isSwim) continue;

if (even) summary += "<tr>";
else {
summary += "<tr bgcolor='" + altColor.name() + "'>";
}
even = !even;

// date of xtrain
summary += QString("<td align=\"center\">%1</td>")
.arg(ride->dateTime.date().toString(tr("dd MMM yyyy")));

for (j = 0; j< totalCols; ++j) {
QString symbol = rtotalColumn[j];

// get this value
QString value = ride->getStringForSymbol(symbol,useMetricUnits);
summary += QString("<td align=\"center\">%1</td>").arg(value);
}
for (j = 0; j< xtrainCols; ++j) {
QString symbol = xtrainMetrics[j];

// get this value
QString value = ride->getStringForSymbol(symbol,useMetricUnits);
summary += QString("<td align=\"center\">%1</td>").arg(value);
}
summary += "</tr>";
}
summary += "</table><br>";

}

// summarise errors reading file if it was a ride summary
Expand Down Expand Up @@ -1881,7 +1989,7 @@ RideSummaryWindow::htmlCompareSummary() const
nActivities++;
if (metrics->isRun) nRuns++;
else if (metrics->isSwim) nSwims++;
else nRides++;
else if (metrics->isBike) nRides++;
}

//
Expand Down
26 changes: 21 additions & 5 deletions src/Core/DataFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ DataFilter::builtins()
{
QStringList returning;

// add special functions
returning <<"isRide"<<"isSwim"<<"isXtrain"; // isRun is included in RideNavigator

for(int i=0; DataFilterFunctions[i].parameters != -1; i++) {

if (i == 30) { // special case 'estimate' we describe it
Expand Down Expand Up @@ -579,7 +582,8 @@ DataFilter::colorSyntax(QTextDocument *document, int pos)
!sym.compare("Current", Qt::CaseInsensitive) ||
!sym.compare("RECINTSECS", Qt::CaseInsensitive) ||
!sym.compare("NA", Qt::CaseInsensitive) ||
sym == "isSwim" || sym == "isRun") {
sym == "isRide" || sym == "isSwim" ||
sym == "isRun" || sym == "isXtrain") {
isfunction = found = true;
}

Expand Down Expand Up @@ -1212,9 +1216,9 @@ bool Leaf::isNumber(DataFilterRuntime *df, Leaf *leaf)
{
QString symbol = *(leaf->lvalue.n);
if (df->symbols.contains(symbol)) return true;
if (symbol == "isRun") return true;
if (symbol == "isRide" || symbol == "isSwim" ||
symbol == "isRun" || symbol == "isXtrain") return true;
if (symbol == "x" || symbol == "i") return true;
else if (symbol == "isSwim") return true;
else if (!symbol.compare("Date", Qt::CaseInsensitive)) return true;
else if (!symbol.compare("Today", Qt::CaseInsensitive)) return true;
else if (!symbol.compare("Current", Qt::CaseInsensitive)) return true;
Expand Down Expand Up @@ -1296,7 +1300,9 @@ void Leaf::validateFilter(Context *context, DataFilterRuntime *df, Leaf *leaf)
symbol.compare("RECINTSECS", Qt::CaseInsensitive) &&
symbol.compare("NA", Qt::CaseInsensitive) &&
!df->dataSeriesSymbols.contains(symbol) &&
symbol != "isSwim" && symbol != "isRun" && !isCoggan(symbol)) {
symbol != "isRide" && symbol != "isSwim" &&
symbol != "isRun" && symbol != "isXtrain" &&
!isCoggan(symbol)) {

// unknown, is it user defined ?
if (!df->symbols.contains(symbol)) {
Expand Down Expand Up @@ -1927,7 +1933,9 @@ void Leaf::validateFilter(Context *context, DataFilterRuntime *df, Leaf *leaf)
!symbol.compare("Device", Qt::CaseInsensitive) ||
!symbol.compare("NA", Qt::CaseInsensitive) ||
df->dataSeriesSymbols.contains(symbol) ||
symbol == "isSwim" || symbol == "isRun" || isCoggan(symbol)) {
symbol == "isRide" || symbol == "isSwim" ||
symbol == "isRun" || symbol == "isXtrain" ||
isCoggan(symbol)) {
DataFiltererrors << QString(tr("%1 is not supported in isset/set/unset operations")).arg(symbol);
leaf->inerror = true;
}
Expand Down Expand Up @@ -4182,6 +4190,10 @@ Result Leaf::eval(DataFilterRuntime *df, Leaf *leaf, float x, long it, RideItem
lhsdouble = x;
lhsisNumber = true;

} else if (symbol == "isRide") {
lhsdouble = m->isBike ? 1 : 0;
lhsisNumber = true;

} else if (symbol == "isRun") {
lhsdouble = m->isRun ? 1 : 0;
lhsisNumber = true;
Expand All @@ -4190,6 +4202,10 @@ Result Leaf::eval(DataFilterRuntime *df, Leaf *leaf, float x, long it, RideItem
lhsdouble = m->isSwim ? 1 : 0;
lhsisNumber = true;

} else if (symbol == "isXtrain") {
lhsdouble = m->isXtrain ? 1 : 0;
lhsisNumber = true;

} else if (!symbol.compare("NA", Qt::CaseInsensitive)) {

lhsdouble = RideFile::NA;
Expand Down
5 changes: 3 additions & 2 deletions src/Core/RideCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ RideCache::getRideTypeCounts(Specification specification, int& nActivities,
nActivities++;
if (ride->isSwim) nSwims++;
else if (ride->isRun) nRuns++;
else nRides++;
else if (ride->isBike) nRides++;
}
}

Expand All @@ -775,9 +775,10 @@ RideCache::isMetricRelevantForRides(Specification specification,
if (!specification.pass(ride)) continue;

// skip non selected sports when restriction supplied
if ((sport == OnlyRides) && (ride->isSwim || ride->isRun)) continue;
if ((sport == OnlyRides) && !ride->isBike) continue;
if ((sport == OnlyRuns) && !ride->isRun) continue;
if ((sport == OnlySwims) && !ride->isSwim) continue;
if ((sport == OnlyXtrains) && !ride->isXtrain) continue;

if (metric->isRelevantForRide(ride)) return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Core/RideCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class RideCache : public QObject
void getRideTypeCounts(Specification specification, int& nActivities,
int& nRides, int& nRuns, int& nSwims);
// Check if metric is relevant for some activity matching specification
enum SportRestriction { AnySport, OnlyRides, OnlyRuns, OnlySwims };
enum SportRestriction { AnySport, OnlyRides, OnlyRuns, OnlySwims, OnlyXtrains };
bool isMetricRelevantForRides(Specification specification,
const RideMetric* metric,
SportRestriction sport=AnySport);
Expand Down
3 changes: 2 additions & 1 deletion src/Core/RideDB.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@
// 1.7 20 Feb 17 Mark Liversedge Metric count (if nonzero) added
// 1.8 22 Feb 17 Mark Liversedge Metric stdmean() stdvariance() added
// 1.9 29 Jul 18 Mark Liversedge Mark intervals as performance tests
// 2.0 04 Apr 20 Ale Martinez sport replaces isRun and isSwim

#define RIDEDB_VERSION "1.9"
#define RIDEDB_VERSION "2.0"

class APIWebService;
class HttpResponse;
Expand Down
13 changes: 9 additions & 4 deletions src/Core/RideDB.y
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,14 @@ ride_tuple: string ':' string {
else if ($1 == "dbversion") jc->item.dbversion = $3.toInt();
else if ($1 == "udbversion") jc->item.udbversion = $3.toInt();
else if ($1 == "color") jc->item.color = QColor($3);
else if ($1 == "isRun") jc->item.isRun = $3.toInt();
else if ($1 == "isSwim") jc->item.isSwim = $3.toInt();
else if ($1 == "sport") {
jc->item.sport = ($3);
jc->item.isBike=jc->item.isRun=jc->item.isSwim=jc->item.isXtrain=false;
if ($3 == "Bike") jc->item.isBike = true;
else if ($3 == "Run") jc->item.isRun = true;
else if ($3 == "Swim") jc->item.isSwim = true;
else jc->item.isXtrain = true;
}
else if ($1 == "present") jc->item.present = $3;
else if ($1 == "overrides") jc->item.overrides_ = $3.split(",");
else if ($1 == "weight") jc->item.weight = $3.toDouble();
Expand Down Expand Up @@ -546,8 +552,7 @@ void RideCache::save(bool opendata, QString filename)
stream << "\t\t\"udbversion\":\"" <<item->udbversion <<"\",\n";
stream << "\t\t\"color\":\"" <<item->color.name() <<"\",\n";
stream << "\t\t\"present\":\"" <<item->present <<"\",\n";
stream << "\t\t\"isRun\":\"" <<item->isRun <<"\",\n";
stream << "\t\t\"isSwim\":\"" <<item->isSwim <<"\",\n";
stream << "\t\t\"sport\":\"" <<item->sport <<"\",\n";
stream << "\t\t\"weight\":\"" <<item->weight <<"\",\n";

if (item->zoneRange >= 0) stream << "\t\t\"zonerange\":\"" <<item->zoneRange <<"\",\n";
Expand Down
12 changes: 9 additions & 3 deletions src/Core/RideItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@
RideItem::RideItem()
:
ride_(NULL), fileCache_(NULL), context(NULL), isdirty(false), isstale(true), isedit(false), skipsave(false), path(""), fileName(""),
color(QColor(1,1,1)), isRun(false), isSwim(false), samples(false), zoneRange(-1), hrZoneRange(-1), paceZoneRange(-1), fingerprint(0), metacrc(0), crc(0), timestamp(0), dbversion(0), udbversion(0), weight(0) {
color(QColor(1,1,1)), sport(""), isBike(false), isRun(false), isSwim(false), isXtrain(false), samples(false), zoneRange(-1), hrZoneRange(-1), paceZoneRange(-1), fingerprint(0), metacrc(0), crc(0), timestamp(0), dbversion(0), udbversion(0), weight(0) {
metrics_.fill(0, RideMetricFactory::instance().metricCount());
count_.fill(0, RideMetricFactory::instance().metricCount());
}

RideItem::RideItem(RideFile *ride, Context *context)
:
ride_(ride), fileCache_(NULL), context(context), isdirty(false), isstale(true), isedit(false), skipsave(false), path(""), fileName(""),
color(QColor(1,1,1)), isRun(false), isSwim(false), samples(false), zoneRange(-1), hrZoneRange(-1), paceZoneRange(-1), fingerprint(0), metacrc(0), crc(0), timestamp(0), dbversion(0), udbversion(0), weight(0)
color(QColor(1,1,1)), sport(""), isBike(false), isRun(false), isSwim(false), isXtrain(false), samples(false), zoneRange(-1), hrZoneRange(-1), paceZoneRange(-1), fingerprint(0), metacrc(0), crc(0), timestamp(0), dbversion(0), udbversion(0), weight(0)
{
metrics_.fill(0, RideMetricFactory::instance().metricCount());
count_.fill(0, RideMetricFactory::instance().metricCount());
Expand All @@ -62,7 +62,7 @@ RideItem::RideItem(RideFile *ride, Context *context)
RideItem::RideItem(QString path, QString fileName, QDateTime &dateTime, Context *context, bool planned)
:
ride_(NULL), fileCache_(NULL), context(context), isdirty(false), isstale(true), isedit(false), skipsave(false), path(path), fileName(fileName),
dateTime(dateTime), color(QColor(1,1,1)), planned(planned), isRun(false), isSwim(false), samples(false), zoneRange(-1), hrZoneRange(-1), paceZoneRange(-1), fingerprint(0),
dateTime(dateTime), color(QColor(1,1,1)), planned(planned), sport(""), isBike(false), isRun(false), isSwim(false), isXtrain(false), samples(false), zoneRange(-1), hrZoneRange(-1), paceZoneRange(-1), fingerprint(0),
metacrc(0), crc(0), timestamp(0), dbversion(0), udbversion(0), weight(0)
{
metrics_.fill(0, RideMetricFactory::instance().metricCount());
Expand Down Expand Up @@ -121,8 +121,11 @@ RideItem::setFrom(RideItem&here, bool temp) // used when loading cache/rideDB.js
udbversion = here.udbversion;
color = here.color;
present = here.present;
sport = here.sport;
isBike = here.isBike;
isRun = here.isRun;
isSwim = here.isSwim;
isXtrain = here.isXtrain;
weight = here.weight;
overrides_ = here.overrides_;
samples = here.samples;
Expand Down Expand Up @@ -584,8 +587,11 @@ RideItem::refresh()
getWeight();

// first class stuff
sport = f->sport();
isBike = f->isBike();
isRun = f->isRun();
isSwim = f->isSwim();
isXtrain = f->isXtrain();
color = context->athlete->colorEngine->colorFor(f->getTag(context->athlete->rideMetadata()->getColorField(), ""));
present = f->getTag("Data", "");
samples = f->dataPoints().count() > 0;
Expand Down
Loading

0 comments on commit 42fa49c

Please sign in to comment.