RideSummary Compare Mode

.. you can now compare intervals or date ranges via
   the ride summary window.
This commit is contained in:
Mark Liversedge
2013-12-30 17:08:17 +00:00
parent adfd9e4f89
commit 0aee3c2e14
2 changed files with 694 additions and 24 deletions

View File

@@ -85,7 +85,8 @@ RideSummaryWindow::RideSummaryWindow(Context *context, bool ridesummary) :
connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideItemChanged()));
connect(context->athlete, SIGNAL(zonesChanged()), this, SLOT(refresh()));
connect(context, SIGNAL(intervalsChanged()), this, SLOT(refresh()));
connect(context, SIGNAL(compareIntervalsStateChanged(bool)), this, SLOT(compareChanged(bool)));
connect(context, SIGNAL(compareIntervalsStateChanged(bool)), this, SLOT(compareChanged()));
connect(context, SIGNAL(compareIntervalsChanged()), this, SLOT(compareChanged()));
} else {
@@ -94,7 +95,8 @@ RideSummaryWindow::RideSummaryWindow(Context *context, bool ridesummary) :
connect(context, SIGNAL(rideDeleted(RideItem*)), this, SLOT(refresh()));
connect(context, SIGNAL(filterChanged()), this, SLOT(refresh()));
connect(context, SIGNAL(homeFilterChanged()), this, SLOT(refresh()));
connect(context, SIGNAL(compareDateRangesStateChanged(bool)), this, SLOT(compareChanged(bool)));
connect(context, SIGNAL(compareDateRangesStateChanged(bool)), this, SLOT(compareChanged()));
connect(context, SIGNAL(compareDateRangesChanged()), this, SLOT(compareChanged()));
// date settings
connect(dateSetting, SIGNAL(useCustomRange(DateRange)), this, SLOT(useCustomRange(DateRange)));
@@ -124,16 +126,20 @@ RideSummaryWindow::setFilter(QStringList list)
#endif
void
RideSummaryWindow::compareChanged(bool state)
RideSummaryWindow::compareChanged()
{
// don't do much for now
repaint();
// don't do much for now -- just refresh
// we get called if the items to compare changed
// or if compare mode is switched off / on
refresh();
GcWindow::repaint();
}
void
RideSummaryWindow::rideSelected()
{
refresh();
// so long as you can see me and I'm not in compare mode...
if (!isCompare() && isVisible()) refresh();
}
void
@@ -158,6 +164,7 @@ RideSummaryWindow::rideItemChanged()
void
RideSummaryWindow::metadataChanged()
{
if (!isCompare() && isVisible()) refresh();
refresh();
}
@@ -166,25 +173,52 @@ RideSummaryWindow::refresh()
{
if (!amVisible()) return; // only if you can see me!
// if we're summarising a ride but have no ride to summarise
if (ridesummary && !myRideItem) {
rideSummary->page()->mainFrame()->setHtml("");
return;
}
if (isCompare()) {
if (ridesummary) {
RideItem *rideItem = myRideItem;
setSubTitle(rideItem->dateTime.toString(tr("dddd MMMM d, yyyy, h:mm AP")));
} else {
setSubTitle(tr("Compare")); // fallback to this
if (ridesummary) {
if (myDateRange.name != "") setSubTitle(myDateRange.name);
else {
setSubTitle(myDateRange.from.toString("dddd MMMM d yyyy") +
" - " +
myDateRange.to.toString("dddd MMMM d yyyy"));
if (context->compareIntervals.count() == 2) {
setSubTitle(QString("%2 on %1 vs %4 on %3")
.arg(context->compareIntervals.at(0).data->startTime().toString("dd MMM yy"))
.arg(context->compareIntervals.at(0).name)
.arg(context->compareIntervals.at(1).data->startTime().toString("dd MMM yy"))
.arg(context->compareIntervals.at(1).name));
} else if (context->compareIntervals.count() > 2) {
setSubTitle(QString("%2 on %1 vs %3 others")
.arg(context->compareIntervals.at(0).data->startTime().toString("dd MMM yy"))
.arg(context->compareIntervals.at(0).name)
.arg(context->compareIntervals.count()-1));
}
} else {
// summary of seasons
}
rideSummary->page()->mainFrame()->setHtml(htmlCompareSummary());
} else {
// if we're summarising a ride but have no ride to summarise
if (ridesummary && !myRideItem) {
setSubTitle(tr("Summary"));
rideSummary->page()->mainFrame()->setHtml("");
return;
}
if (ridesummary) {
RideItem *rideItem = myRideItem;
setSubTitle(rideItem->dateTime.toString(tr("dddd MMMM d, yyyy, h:mm AP")));
} else {
if (myDateRange.name != "") setSubTitle(myDateRange.name);
else {
setSubTitle(myDateRange.from.toString("dddd MMMM d yyyy") +
" - " +
myDateRange.to.toString("dddd MMMM d yyyy"));
}
}
rideSummary->page()->mainFrame()->setHtml(htmlSummary());
}
rideSummary->page()->mainFrame()->setHtml(htmlSummary());
}
QString
@@ -807,6 +841,641 @@ RideSummaryWindow::htmlSummary() const
return summary;
}
QString
RideSummaryWindow::htmlCompareSummary() const
{
QString summary;
// SETUP ALL THE METRICS WE WILL SHOW
// All the metrics we will display -- same as non compare mode FOR NOW
static const QStringList columnNames = QStringList() << tr("Totals") << tr("Averages") << tr("Maximums") << tr("Metrics*");
static QStringList totalColumn = QStringList()
<< "workout_time"
<< "time_riding"
<< "total_distance"
<< "total_work"
<< "elevation_gain";
if (!ridesummary) totalColumn << "ride_count"; // number of rides
static const QStringList rtotalColumn = QStringList()
<< "workout_time"
<< "total_distance"
<< "total_work"
<< "elevation_gain";
QStringList averageColumn = QStringList() // not const as modified below..
<< "average_speed"
<< "average_power"
<< "average_hr"
<< "average_cad";
QStringList maximumColumn = QStringList() // not const as modified below..
<< "max_speed"
<< "max_power"
<< "max_heartrate"
<< "max_cadence";
#if 0 // XXX do /any/ of them have temperature -or- do they /all/ need to ???
// show average and max temp if it is available (in ride summary mode)
if (ridesummary && (ride->areDataPresent()->temp || ride->getTag("Temperature", "-") != "-")) {
averageColumn << "average_temp";
maximumColumn << "max_temp";
}
#endif
// users determine the metrics to display
QString s = appsettings->value(this, GC_SETTINGS_SUMMARY_METRICS, GC_SETTINGS_SUMMARY_METRICS_DEFAULT).toString();
if (s == "") s = GC_SETTINGS_SUMMARY_METRICS_DEFAULT;
QStringList metricColumn = s.split(",");
s = appsettings->value(this, GC_SETTINGS_BESTS_METRICS, GC_SETTINGS_BESTS_METRICS_DEFAULT).toString();
if (s == "") s = GC_SETTINGS_BESTS_METRICS_DEFAULT;
QStringList bestsColumn = s.split(",");
static const QStringList timeInZones = QStringList()
<< "time_in_zone_L1"
<< "time_in_zone_L2"
<< "time_in_zone_L3"
<< "time_in_zone_L4"
<< "time_in_zone_L5"
<< "time_in_zone_L6"
<< "time_in_zone_L7"
<< "time_in_zone_L8"
<< "time_in_zone_L9"
<< "time_in_zone_L10";
static const QStringList timeInZonesHR = QStringList()
<< "time_in_zone_H1"
<< "time_in_zone_H2"
<< "time_in_zone_H3"
<< "time_in_zone_H4"
<< "time_in_zone_H5"
<< "time_in_zone_H6"
<< "time_in_zone_H7"
<< "time_in_zone_H8";
if (ridesummary) {
//
// SUMMARISING INTERVALS SO ALWAYS COMPUTE METRICS ON DEMAND
//
QList<SummaryMetrics> intervalMetrics;
QStringList worklist;
worklist += totalColumn;
worklist += averageColumn;
worklist += maximumColumn;
worklist += metricColumn;
worklist += timeInZones;
worklist += timeInZonesHR;
// go calculate them then...
RideMetricFactory &factory = RideMetricFactory::instance();
for (int j=0; j<context->compareIntervals.count(); j++) {
SummaryMetrics metrics;
// calculate using the source context of course!
QHash<QString, RideMetricPtr> computed = RideMetric::computeMetrics(
context->compareIntervals.at(j).sourceContext,
context->compareIntervals.at(j).data,
context->compareIntervals.at(j).sourceContext->athlete->zones(),
context->compareIntervals.at(j).sourceContext->athlete->hrZones(),
worklist);
for(int i = 0; i < worklist.count(); ++i) {
if (worklist[i] != "") {
RideMetricPtr m = computed.value(worklist[i]);
if (m) metrics.setForSymbol(worklist[i], m->value(true));
else metrics.setForSymbol(worklist[i], 0.00);
}
}
intervalMetrics << metrics;
}
// LETS FORMAT THE HTML
summary = "<center>";
// used for alternate shding
QColor color = QApplication::palette().alternateBase().color();
color = QColor::fromHsv(color.hue(), color.saturation() * 2, color.value());
//
// TOTALS, AVERAGES, MAX, METRICS
//
for (int v = 0; v < columnNames.count(); ++v) {
QString columnName;
QStringList metricsList;
switch (v) { // slightly different order when summarising for compare...
// we want the metrics towards the top of the screen as they
// are more important than the maximums (generally anyway)
case 0: metricsList = totalColumn; columnName = columnNames[0]; break;
case 1: metricsList = metricColumn; columnName = columnNames[3]; break;
case 2: metricsList = averageColumn; columnName = columnNames[1]; break;
default:
case 3: metricsList = maximumColumn; columnName = columnNames[2]; break;
}
//
// Repeat for each 'column' (now separate paragraphs)
//
summary += "<h3>" + columnName + "</h3>";
// table of metrics
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";
// first row is a row of headings
summary += "<tr>";
summary += "<td><b></b></td>"; // removed the text as its blinking obvious.. but left code in
// case we ever come back here or use it for other things.
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (QString symbol, metricsList) {
const RideMetric *m = factory.rideMetric(symbol);
QString name, units;
if (m->units(context->athlete->useMetricUnits) != "seconds") units = m->units(context->athlete->useMetricUnits);
if (units != "") name = QString("%1 (%2)").arg(m->name()).arg(units);
else name = QString("%1").arg(m->name());
name = name.replace(QRegExp(tr("^(Average|Max) ")), ""); // average/max on average/max is dumb
summary += "<td align=\"center\" colspan=2><b>" + name + "</b></td>";
summary += "<td>&nbsp;</td>"; // spacing
}
summary += "</tr>";
// then one row for each interval
int counter = 0;
foreach (SummaryMetrics metrics, intervalMetrics) {
// alternating shading
if (counter%2) summary += "<tr bgcolor='" + color.name() + "'>";
else summary += "<tr>";
summary += "<td>" + context->compareIntervals[counter].name + "</td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (QString symbol, metricsList) {
// the values ...
const RideMetric *m = factory.rideMetric(symbol);
// get value and convert if needed (use local context for units)
double value = metrics.getForSymbol(symbol)
* (context->athlete->useMetricUnits ? 1 : m->conversion())
+ (context->athlete->useMetricUnits ? 0 : m->conversionSum());
// use right precision
QString strValue = QString("%1").arg(value, 0, 'f', m->precision());
// or maybe its a duration (worry about local lang or translated)
if (m->units(true) == "seconds" || m->units(true) == tr("seconds"))
strValue = time_to_string((int)value);
summary += "<td align=\"center\">" + strValue + "</td>";
// delta to first entry
if (counter) {
// calculate me vs the original
double value0 = intervalMetrics[0].getForSymbol(symbol)
* (context->athlete->useMetricUnits ? 1 : m->conversion())
+ (context->athlete->useMetricUnits ? 0 : m->conversionSum());
value -= value0; // delta
// use right precision
QString strValue = QString("%1%2").arg(value >= 0 ? "+" : "") // - sign added anyway
.arg(value, 0, 'f', m->precision());
// or maybe its a duration (worry about local lang or translated)
if (m->units(true) == "seconds" || m->units(true) == tr("seconds"))
strValue = QString(value >= 0 ? "+" : "-" ) + time_to_string(fabs(value));
summary += "<td align=\"center\">" + strValue + "</td>";
} else {
summary += "<td align=\"center\"></td>";
}
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
counter++;
}
summary += "</table>";
}
//
// TIME IN POWER ZONES
//
if (context->athlete->zones()) { // use my zones
// get from end if period
int rangeidx = context->athlete->zones()->whichRange(QDate::currentDate()); // use current zone names et al
if (rangeidx > -1) {
// get the list of zones
ZoneRange range = const_cast<Zones*>(context->athlete->zones())->getZoneRange(rangeidx);
QList<ZoneInfo> zones = range.zones;
// we've got a range and a count of zones so all is well
// we need to throw up a table of time in zone for each interval
summary += tr("<h3>Power Zones</h3>");
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";
// lets get some headings
summary += "<tr><td></td>"; // ne need to have a heading for the interval name
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (ZoneInfo zone, zones) {
summary += QString("<td colspan=\"2\" align=\"center\"><b>%1 (%2)</b></td>").arg(zone.desc).arg(zone.name);
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
// now the sumamry
int counter = 0;
foreach (SummaryMetrics metrics, intervalMetrics) {
if (counter%2) summary += "<tr bgcolor='" + color.name() + "'>";
else summary += "<tr>";
summary += "<td>" + context->compareIntervals[counter].name + "</td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
int idx=0;
foreach (ZoneInfo zone, zones) {
int timeZone = metrics.getForSymbol(timeInZones[idx]);
int dt = timeZone - intervalMetrics[0].getForSymbol(timeInZones[idx]);
idx++;
// time and then +time
summary += QString("<td align=\"center\">%1</td>").arg(time_to_string(timeZone));
if (counter) summary += QString("<td align=\"center\">%1%2</td>")
.arg(dt>0 ? "+" : "-")
.arg(time_to_string(fabs(dt)));
else summary += "<td></td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
counter++;
}
// done
summary += "</table>";
}
}
//
// TIME IN HR ZONES
//
if (context->athlete->hrZones()) { // use my zones
// get from end if period
int rangeidx = context->athlete->hrZones()->whichRange(QDate::currentDate()); // use current zone names et al
if (rangeidx > -1) {
// get the list of zones
HrZoneRange range = const_cast<HrZones*>(context->athlete->hrZones())->getHrZoneRange(rangeidx);
QList<HrZoneInfo> zones = range.zones;
// we've got a range and a count of zones so all is well
// we need to throw up a table of time in zone for each interval
summary += tr("<h3>Heartrate Zones</h3>");
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";
// lets get some headings
summary += "<tr><td></td>"; // ne need to have a heading for the interval name
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (HrZoneInfo zone, zones) {
summary += QString("<td colspan=\"2\" align=\"center\"><b>%1 (%2)</b></td>").arg(zone.desc).arg(zone.name);
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
// now the sumamry
int counter = 0;
foreach (SummaryMetrics metrics, intervalMetrics) {
if (counter%2) summary += "<tr bgcolor='" + color.name() + "'>";
else summary += "<tr>";
summary += "<td>" + context->compareIntervals[counter].name + "</td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
int idx=0;
foreach (HrZoneInfo zone, zones) {
int timeZone = metrics.getForSymbol(timeInZonesHR[idx]);
int dt = timeZone - intervalMetrics[0].getForSymbol(timeInZonesHR[idx]);
idx++;
// time and then +time
summary += QString("<td align=\"center\">%1</td>").arg(time_to_string(timeZone));
if (counter) summary += QString("<td align=\"center\">%1%2</td>")
.arg(dt>0 ? "+" : "-")
.arg(time_to_string(fabs(dt)));
else summary += "<td></td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
counter++;
}
// done
summary += "</table>";
}
}
} else { // DATE RANGE COMPARE
// LETS FORMAT THE HTML
summary = "<center>";
// get metric details here ...
RideMetricFactory &factory = RideMetricFactory::instance();
// used for alternate shding
QColor color = QApplication::palette().alternateBase().color();
color = QColor::fromHsv(color.hue(), color.saturation() * 2, color.value());
//
// TOTALS, AVERAGES, MAX, METRICS
//
for (int v = 0; v < columnNames.count(); ++v) {
QString columnName;
QStringList metricsList;
switch (v) { // slightly different order when summarising for compare...
// we want the metrics towards the top of the screen as they
// are more important than the maximums (generally anyway)
case 0: metricsList = totalColumn; columnName = columnNames[0]; break;
case 1: metricsList = metricColumn; columnName = columnNames[3]; break;
case 2: metricsList = averageColumn; columnName = columnNames[1]; break;
default:
case 3: metricsList = maximumColumn; columnName = columnNames[2]; break;
}
//
// Repeat for each 'column' (now separate paragraphs)
//
summary += "<h3>" + columnName + "</h3>";
// table of metrics
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";
// first row is a row of headings
summary += "<tr>";
summary += "<td><b></b></td>"; // removed the text as its blinking obvious.. but left code in
// case we ever come back here or use it for other things.
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (QString symbol, metricsList) {
const RideMetric *m = factory.rideMetric(symbol);
QString name, units;
if (m->units(context->athlete->useMetricUnits) != "seconds") units = m->units(context->athlete->useMetricUnits);
if (units != "") name = QString("%1 (%2)").arg(m->name()).arg(units);
else name = QString("%1").arg(m->name());
name = name.replace(QRegExp(tr("^(Average|Max) ")), ""); // average/max on average/max is dumb
summary += "<td align=\"center\" colspan=2><b>" + name + "</b></td>";
summary += "<td>&nbsp;</td>"; // spacing
}
summary += "</tr>";
// then one row for each interval
int counter = 0;
foreach (CompareDateRange dr, context->compareDateRanges) {
// alternating shading
if (counter%2) summary += "<tr bgcolor='" + color.name() + "'>";
else summary += "<tr>";
summary += "<td>" + dr.name + "</td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (QString symbol, metricsList) {
// the values ...
const RideMetric *m = factory.rideMetric(symbol);
// get value and convert if needed (use local context for units)
double value = SummaryMetrics::getAggregated(context, symbol, dr.metrics, QStringList(), false,
context->athlete->useMetricUnits, true).toDouble();
// use right precision
QString strValue = QString("%1").arg(value, 0, 'f', m->precision());
// or maybe its a duration (worry about local lang or translated)
if (m->units(true) == "seconds" || m->units(true) == tr("seconds"))
strValue = time_to_string((int)value);
summary += "<td align=\"center\">" + strValue + "</td>";
// delta to first entry
if (counter) {
// calculate me vs the original
double value0 = SummaryMetrics::getAggregated(context, symbol,
context->compareDateRanges[0].metrics, QStringList(), false,
context->athlete->useMetricUnits, true).toDouble();
value -= value0; // delta
// use right precision
QString strValue = QString("%1%2").arg(value >= 0 ? "+" : "") // - sign added anyway
.arg(value, 0, 'f', m->precision());
// or maybe its a duration (worry about local lang or translated)
if (m->units(true) == "seconds" || m->units(true) == tr("seconds"))
strValue = QString(value >= 0 ? "+" : "-" ) + time_to_string(fabs(value));
summary += "<td align=\"center\">" + strValue + "</td>";
} else {
summary += "<td align=\"center\"></td>";
}
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
counter++;
}
summary += "</table>";
}
//
// TIME IN POWER ZONES
//
if (context->athlete->zones()) { // use my zones
// get from end if period
int rangeidx = context->athlete->zones()->whichRange(QDate::currentDate()); // use current zone names et al
if (rangeidx > -1) {
// get the list of zones
ZoneRange range = const_cast<Zones*>(context->athlete->zones())->getZoneRange(rangeidx);
QList<ZoneInfo> zones = range.zones;
// we've got a range and a count of zones so all is well
// we need to throw up a table of time in zone for each interval
summary += tr("<h3>Power Zones</h3>");
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";
// lets get some headings
summary += "<tr><td></td>"; // ne need to have a heading for the interval name
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (ZoneInfo zone, zones) {
summary += QString("<td colspan=\"2\" align=\"center\"><b>%1 (%2)</b></td>").arg(zone.desc).arg(zone.name);
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
// now the sumamry
int counter = 0;
foreach (CompareDateRange dr, context->compareDateRanges) {
if (counter%2) summary += "<tr bgcolor='" + color.name() + "'>";
else summary += "<tr>";
summary += "<td>" + dr.name + "</td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
int idx=0;
foreach (ZoneInfo zone, zones) {
int timeZone = SummaryMetrics::getAggregated(context, timeInZones[idx], dr.metrics, QStringList(), false,
context->athlete->useMetricUnits, true).toInt();
int dt = timeZone - SummaryMetrics::getAggregated(context, timeInZones[idx],
context->compareDateRanges[0].metrics, QStringList(), false,
context->athlete->useMetricUnits, true).toInt();
idx++;
// time and then +time
summary += QString("<td align=\"center\">%1</td>").arg(time_to_string(timeZone));
if (counter) summary += QString("<td align=\"center\">%1%2</td>")
.arg(dt>0 ? "+" : "-")
.arg(time_to_string(fabs(dt)));
else summary += "<td></td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
counter++;
}
// done
summary += "</table>";
}
}
//
// TIME IN HR ZONES
//
if (context->athlete->hrZones()) { // use my zones
// get from end if period
int rangeidx = context->athlete->hrZones()->whichRange(QDate::currentDate()); // use current zone names et al
if (rangeidx > -1) {
// get the list of zones
HrZoneRange range = const_cast<HrZones*>(context->athlete->hrZones())->getHrZoneRange(rangeidx);
QList<HrZoneInfo> zones = range.zones;
// we've got a range and a count of zones so all is well
// we need to throw up a table of time in zone for each interval
summary += tr("<h3>Heartrate Zones</h3>");
summary += "<table align=\"center\" width=\"80%\" border=\"0\">";
// lets get some headings
summary += "<tr><td></td>"; // ne need to have a heading for the interval name
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
foreach (HrZoneInfo zone, zones) {
summary += QString("<td colspan=\"2\" align=\"center\"><b>%1 (%2)</b></td>").arg(zone.desc).arg(zone.name);
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
// now the sumamry
int counter = 0;
foreach (CompareDateRange dr, context->compareDateRanges) {
if (counter%2) summary += "<tr bgcolor='" + color.name() + "'>";
else summary += "<tr>";
summary += "<td>" + dr.name + "</td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
int idx=0;
foreach (HrZoneInfo zone, zones) {
int timeZone = SummaryMetrics::getAggregated(context, timeInZonesHR[idx], dr.metrics, QStringList(), false,
context->athlete->useMetricUnits, true).toInt();
int dt = timeZone - SummaryMetrics::getAggregated(context, timeInZonesHR[idx],
context->compareDateRanges[0].metrics, QStringList(), false,
context->athlete->useMetricUnits, true).toInt();
idx++;
// time and then +time
summary += QString("<td align=\"center\">%1</td>").arg(time_to_string(timeZone));
if (counter) summary += QString("<td align=\"center\">%1%2</td>")
.arg(dt>0 ? "+" : "-")
.arg(time_to_string(fabs(dt)));
else summary += "<td></td>";
summary += "<td bgcolor='white'>&nbsp;</td>"; // spacing
}
summary += "</tr>";
counter++;
}
// done
summary += "</table>";
}
}
}
// add the usual disclaimers etc at the bottom
summary += "<br><hr width=\"80%\">";
// The extra <center> works around a bug in QT 4.3.1,
// which will otherwise put the following above the <hr>.
summary += tr("<br>BikeScore is a trademark of Dr. Philip "
"Friere Skiba, PhysFarm Training Systems LLC");
summary += tr("<br>TSS, NP and IF are trademarks of Peaksware LLC</center>");
return summary;
}
void
RideSummaryWindow::useCustomRange(DateRange range)
{

View File

@@ -99,12 +99,13 @@ class RideSummaryWindow : public GcChartWindow
void setFilter(QStringList);
#endif
// compare mode started
void compareChanged(bool);
// compare mode started or items to compare changed
void compareChanged();
protected:
QString htmlSummary() const;
QString htmlSummary() const; // summary of a ride or a date range
QString htmlCompareSummary() const; // comparing intervals or seasons
Context *context;
QWebView *rideSummary;