mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
RideSummary Compare Mode
.. you can now compare intervals or date ranges via the ride summary window.
This commit is contained in:
@@ -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'> </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> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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'> </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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user