API Fetch Activity format=...

.. when retreiving an activity using the API
   you can now specify the format you want the
   data to be returned in.

   it can be one of;
   tcx - garmin training centre xml
   pwx - training peaks xml
   json - goldencheetah json
   csv - all available data (not powertap csv)

.. along the way the file writers for the respective
   formats now accept a NULL context to work standalone.
   this may be useful as a file conversion tool.
This commit is contained in:
Mark Liversedge
2015-09-08 21:13:17 +01:00
parent 1d1508c524
commit d95ec38593
4 changed files with 258 additions and 164 deletions

View File

@@ -22,6 +22,11 @@
#include "GcUpgrade.h"
#include "RideDB.h"
#include "RideFile.h"
#include "CsvRideFile.h"
#include <QTemporaryFile>
void
APIWebService::service(HttpRequest &request, HttpResponse &response)
{
@@ -170,15 +175,98 @@ APIWebService::listActivity(QString athlete, QStringList paths, HttpRequest &req
QFile file(filename);
if (file.exists() && file.open(QFile::ReadOnly | QFile::Text)) {
// read in the whole thing
QTextStream in(&file);
// GC .JSON is stored in UTF-8 with BOM(Byte order mark) for identification
in.setCodec ("UTF-8");
contents = in.readAll();
// close as we will open properly below
file.close();
// write back in one hit
response.write(contents.toLocal8Bit(), true);
// what format to use ?
QString format(request.getParameter("format"));
if (format == "") {
// read in the whole thing
QTextStream in(&file);
// GC .JSON is stored in UTF-8 with BOM(Byte order mark) for identification
in.setCodec ("UTF-8");
contents = in.readAll();
file.close();
// write back in one hit
response.write(contents.toLocal8Bit(), true);
} else {
// lets go with tcx/pwx as xml, full csv (not powertap) and GC json
QStringList formats;
formats << "tcx"; // garmin training centre
formats << "csv"; // full csv list (not powertap)
formats << "json"; // gc json
formats << "pwx"; // gc json
// unsupported format
if (!formats.contains(format)) {
response.setStatus(500);
response.write("unsupported format; we support:");
foreach(QString fmt, formats) {
response.write(" ");
response.write(fmt.toLocal8Bit());
}
response.write("\r\n");
return;
}
// lets read the file in as a ridefile
QStringList errors;
RideFile *f = RideFileFactory::instance().openRideFile(NULL, file, errors);
// error reading (!)
if (f == NULL) {
response.setStatus(500);
foreach(QString error, errors) {
response.write(error.toLocal8Bit());
response.write("\r\n");
}
return;
}
// write out to a temporary file in
// the format requested
bool success;
QTemporaryFile tempfile; // deletes file when goes out of scope
QString tempname;
if (tempfile.open()) tempname = tempfile.fileName();
else {
response.setStatus(500);
response.write("error opening temporary file");
return;
}
QFile out(tempname);
if (format == "csv") {
CsvFileReader writer;
success = writer.writeRideFile(NULL, f, out, CsvFileReader::gc);
} else {
success = RideFileFactory::instance().writeRideFile(NULL, f, out, format);
}
if (success) {
// read in the whole thing
out.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&out);
in.setCodec ("UTF-8");
contents = in.readAll();
out.close();
// write back in one hit
response.write(contents.toLocal8Bit(), true);
return;
} else {
response.setStatus(500);
response.write("unable to write output, internal error.\n");
return;
}
}
} else {

View File

@@ -567,7 +567,7 @@ PwxFileReader::writeRideFile(Context *context, const RideFile *ride, QFile &file
// athlete details
QDomElement athlete = doc.createElement("athlete");
QDomElement name = doc.createElement("name");
text = doc.createTextNode(context->athlete->cyclist); name.appendChild(text);
text = doc.createTextNode(context ? context->athlete->cyclist : "athlete"); name.appendChild(text);
athlete.appendChild(name);
double cyclistweight = ride->getTag("Weight", "0.0").toDouble();
if (cyclistweight) {

View File

@@ -616,9 +616,11 @@ RideFile *RideFileFactory::openRideFile(Context *context, QFile &file,
// Construct the summary text used on the calendar
QString calendarText;
foreach (FieldDefinition field, context->athlete->rideMetadata()->getFields()) {
if (field.diary == true && result->getTag(field.name, "") != "") {
calendarText += field.calendarText(result->getTag(field.name, ""));
if (context) { // will be null in standalone open
foreach (FieldDefinition field, context->athlete->rideMetadata()->getFields()) {
if (field.diary == true && result->getTag(field.name, "") != "") {
calendarText += field.calendarText(result->getTag(field.name, ""));
}
}
}
result->setTag("Calendar Text", calendarText);
@@ -627,7 +629,7 @@ RideFile *RideFileFactory::openRideFile(Context *context, QFile &file,
result->setTag("Filename", QFileInfo(file.fileName()).fileName());
result->setTag("Device", result->deviceType());
result->setTag("File Format", result->fileFormat());
result->setTag("Athlete", context->athlete->cyclist);
if (context) result->setTag("Athlete", context->athlete->cyclist);
result->setTag("Year", result->startTime().toString("yyyy"));
result->setTag("Month", result->startTime().toString("MMMM"));
result->setTag("Weekday", result->startTime().toString("ddd"));
@@ -656,7 +658,7 @@ RideFile *RideFileFactory::openRideFile(Context *context, QFile &file,
DataProcessorFactory::instance().autoProcess(result);
// calculate derived data series -- after data fixers applied above
result->recalculateDerivedSeries();
if (context) result->recalculateDerivedSeries();
// what data is present - after processor in case 'derived' or adjusted
QString flags;

View File

@@ -109,54 +109,57 @@ TcxFileReader::toByteArray(Context *context, const RideFile *ride, bool withAlt,
QStringList worklist = QStringList();
for (int i=0; metrics[i];i++) worklist << metrics[i];
QHash<QString, RideMetricPtr> computed = RideMetric::computeMetrics(context, ride, context->athlete->zones(), context->athlete->hrZones(), worklist);
QHash<QString, RideMetricPtr> computed;
if (context) { // can't do this standalone
computed = RideMetric::computeMetrics(context, ride, context->athlete->zones(), context->athlete->hrZones(), worklist);
QDomElement lap_time = doc.createElement("TotalTimeSeconds");
text = doc.createTextNode(QString("%1").arg(computed.value("workout_time")->value(true)));
//text = doc.createTextNode(ride->dataPoints().last()->secs);
lap_time.appendChild(text);
lap.appendChild(lap_time);
QDomElement lap_time = doc.createElement("TotalTimeSeconds");
text = doc.createTextNode(QString("%1").arg(computed.value("workout_time")->value(true)));
//text = doc.createTextNode(ride->dataPoints().last()->secs);
lap_time.appendChild(text);
lap.appendChild(lap_time);
QDomElement lap_distance = doc.createElement("DistanceMeters");
text = doc.createTextNode(QString("%1").arg(1000*computed.value("total_distance")->value(true)));
//text = doc.createTextNode(ride->dataPoints().last()->km);
lap_distance.appendChild(text);
lap.appendChild(lap_distance);
QDomElement lap_distance = doc.createElement("DistanceMeters");
text = doc.createTextNode(QString("%1").arg(1000*computed.value("total_distance")->value(true)));
//text = doc.createTextNode(ride->dataPoints().last()->km);
lap_distance.appendChild(text);
lap.appendChild(lap_distance);
QDomElement max_speed = doc.createElement("MaximumSpeed");
text = doc.createTextNode(QString("%1")
.arg(computed.value("max_speed")->value(true) / 3.6));
max_speed.appendChild(text);
lap.appendChild(max_speed);
QDomElement max_speed = doc.createElement("MaximumSpeed");
text = doc.createTextNode(QString("%1")
.arg(computed.value("max_speed")->value(true) / 3.6));
max_speed.appendChild(text);
lap.appendChild(max_speed);
QDomElement lap_calories = doc.createElement("Calories");
text = doc.createTextNode(QString("%1").arg((int)computed.value("total_work")->value(true)));
lap_calories.appendChild(text);
lap.appendChild(lap_calories);
QDomElement lap_calories = doc.createElement("Calories");
text = doc.createTextNode(QString("%1").arg((int)computed.value("total_work")->value(true)));
lap_calories.appendChild(text);
lap.appendChild(lap_calories);
QDomElement avg_heartrate = doc.createElement("AverageHeartRateBpm");
QDomElement value = doc.createElement("Value");
text = doc.createTextNode(QString("%1").arg((int)computed.value("average_hr")->value(true)));
value.appendChild(text);
avg_heartrate.appendChild(value);
lap.appendChild(avg_heartrate);
QDomElement avg_heartrate = doc.createElement("AverageHeartRateBpm");
QDomElement value = doc.createElement("Value");
text = doc.createTextNode(QString("%1").arg((int)computed.value("average_hr")->value(true)));
value.appendChild(text);
avg_heartrate.appendChild(value);
lap.appendChild(avg_heartrate);
QDomElement max_heartrate = doc.createElement("MaximumHeartRateBpm");
value = doc.createElement("Value");
text = doc.createTextNode(QString("%1").arg((int)computed.value("max_heartrate")->value(true)));
value.appendChild(text);
max_heartrate.appendChild(value);
lap.appendChild(max_heartrate);
QDomElement max_heartrate = doc.createElement("MaximumHeartRateBpm");
value = doc.createElement("Value");
text = doc.createTextNode(QString("%1").arg((int)computed.value("max_heartrate")->value(true)));
value.appendChild(text);
max_heartrate.appendChild(value);
lap.appendChild(max_heartrate);
QDomElement lap_intensity = doc.createElement("Intensity");
text = doc.createTextNode("Active");
lap_intensity.appendChild(text);
lap.appendChild(lap_intensity);
QDomElement lap_intensity = doc.createElement("Intensity");
text = doc.createTextNode("Active");
lap_intensity.appendChild(text);
lap.appendChild(lap_intensity);
QDomElement lap_triggerMethod = doc.createElement("TriggerMethod");
text = doc.createTextNode("Manual");
lap_triggerMethod.appendChild(text);
lap.appendChild(lap_triggerMethod);
QDomElement lap_triggerMethod = doc.createElement("TriggerMethod");
text = doc.createTextNode("Manual");
lap_triggerMethod.appendChild(text);
lap.appendChild(lap_triggerMethod);
}
// samples
// data points: timeoffset, dist, hr, spd, pwr, torq, cad, lat, lon, alt
@@ -263,144 +266,145 @@ TcxFileReader::toByteArray(Context *context, const RideFile *ride, bool withAlt,
}
// Activity Extensions
QDomElement extensions = doc.createElement("Extensions");
lap.appendChild(extensions);
QDomElement lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
if (context) {
// Activity Extensions
QDomElement extensions = doc.createElement("Extensions");
lap.appendChild(extensions);
QDomElement lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
QDomElement max_cad = doc.createElement("MaxBikeCadence");
text = doc.createTextNode(QString("%1").arg(computed.value("max_cadence")->value(true)));
max_cad.appendChild(text);
lx.appendChild(max_cad);
QDomElement max_cad = doc.createElement("MaxBikeCadence");
text = doc.createTextNode(QString("%1").arg(computed.value("max_cadence")->value(true)));
max_cad.appendChild(text);
lx.appendChild(max_cad);
lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
QDomElement avg_speed = doc.createElement("AvgSpeed");
text = doc.createTextNode(QString("%1")
.arg(computed.value("average_speed")->value(true) / 3.6));
avg_speed.appendChild(text);
lx.appendChild(avg_speed);
QDomElement avg_speed = doc.createElement("AvgSpeed");
text = doc.createTextNode(QString("%1")
.arg(computed.value("average_speed")->value(true) / 3.6));
avg_speed.appendChild(text);
lx.appendChild(avg_speed);
lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
QDomElement avg_power = doc.createElement("AvgWatts");
text = doc.createTextNode(QString("%1").arg((int)computed.value("average_power")->value(true)));
avg_power.appendChild(text);
lx.appendChild(avg_power);
QDomElement avg_power = doc.createElement("AvgWatts");
text = doc.createTextNode(QString("%1").arg((int)computed.value("average_power")->value(true)));
avg_power.appendChild(text);
lx.appendChild(avg_power);
lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
lx = doc.createElement("LX");
lx.setAttribute("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
extensions.appendChild(lx);
QDomElement max_power = doc.createElement("MaxWatts");
text = doc.createTextNode(QString("%1").arg((int)computed.value("max_power")->value(true)));
max_power.appendChild(text);
lx.appendChild(max_power);
QDomElement max_power = doc.createElement("MaxWatts");
text = doc.createTextNode(QString("%1").arg((int)computed.value("max_power")->value(true)));
max_power.appendChild(text);
lx.appendChild(max_power);
// Creator - Device
QDomElement creator = doc.createElement("Creator");
creator.setAttribute("xsi:type", "Device_t");
activity.appendChild(creator);
// Creator - Device
QDomElement creator = doc.createElement("Creator");
creator.setAttribute("xsi:type", "Device_t");
activity.appendChild(creator);
QDomElement creator_name = doc.createElement("Name");
if (ride->deviceType() != "")
text = doc.createTextNode(ride->deviceType());
else
text = doc.createTextNode("Unknown");
creator_name.appendChild(text);
creator.appendChild(creator_name);
QDomElement creator_name = doc.createElement("Name");
if (ride->deviceType() != "")
text = doc.createTextNode(ride->deviceType());
else
text = doc.createTextNode("Unknown");
creator_name.appendChild(text);
creator.appendChild(creator_name);
QDomElement creator_unitId = doc.createElement("UnitId");
text = doc.createTextNode("0");
creator_unitId.appendChild(text);
creator.appendChild(creator_unitId);
QDomElement creator_unitId = doc.createElement("UnitId");
text = doc.createTextNode("0");
creator_unitId.appendChild(text);
creator.appendChild(creator_unitId);
QDomElement creator_productId = doc.createElement("ProductID");
text = doc.createTextNode("0");
creator_productId.appendChild(text);
creator.appendChild(creator_productId);
QDomElement creator_productId = doc.createElement("ProductID");
text = doc.createTextNode("0");
creator_productId.appendChild(text);
creator.appendChild(creator_productId);
QDomElement creator_version = doc.createElement("Version");
creator.appendChild(creator_version);
QDomElement creator_version = doc.createElement("Version");
creator.appendChild(creator_version);
QDomElement creator_version_version_major = doc.createElement("VersionMajor");
text = doc.createTextNode("3");
creator_version_version_major.appendChild(text);
creator_version.appendChild(creator_version_version_major);
QDomElement creator_version_version_major = doc.createElement("VersionMajor");
text = doc.createTextNode("3");
creator_version_version_major.appendChild(text);
creator_version.appendChild(creator_version_version_major);
QDomElement creator_version_version_minor = doc.createElement("VersionMinor");
text = doc.createTextNode("0");
creator_version_version_minor.appendChild(text);
creator_version.appendChild(creator_version_version_minor);
QDomElement creator_version_version_minor = doc.createElement("VersionMinor");
text = doc.createTextNode("0");
creator_version_version_minor.appendChild(text);
creator_version.appendChild(creator_version_version_minor);
QDomElement creator_version_build_major = doc.createElement("BuildMajor");
text = doc.createTextNode("0");
creator_version_build_major.appendChild(text);
creator_version.appendChild(creator_version_build_major);
QDomElement creator_version_build_major = doc.createElement("BuildMajor");
text = doc.createTextNode("0");
creator_version_build_major.appendChild(text);
creator_version.appendChild(creator_version_build_major);
QDomElement creator_version_build_minor = doc.createElement("BuildMinor");
text = doc.createTextNode("0");
creator_version_build_minor.appendChild(text);
creator_version.appendChild(creator_version_build_minor);
QDomElement creator_version_build_minor = doc.createElement("BuildMinor");
text = doc.createTextNode("0");
creator_version_build_minor.appendChild(text);
creator_version.appendChild(creator_version_build_minor);
// Author - Application
QDomElement author = doc.createElement("Author");
author.setAttribute("xsi:type", "Application_t");
tcx.appendChild(author);
// Author - Application
QDomElement author = doc.createElement("Author");
author.setAttribute("xsi:type", "Application_t");
tcx.appendChild(author);
QDomElement author_name = doc.createElement("Name");
text = doc.createTextNode("GoldenCheetah");
author_name.appendChild(text);
author.appendChild(author_name);
QDomElement author_name = doc.createElement("Name");
text = doc.createTextNode("GoldenCheetah");
author_name.appendChild(text);
author.appendChild(author_name);
QDomElement author_build = doc.createElement("Build");
author.appendChild(author_build);
QDomElement author_build = doc.createElement("Build");
author.appendChild(author_build);
QDomElement author_version = doc.createElement("Version");
author_build.appendChild(author_version);
QDomElement author_version = doc.createElement("Version");
author_build.appendChild(author_version);
QDomElement author_version_version_major = doc.createElement("VersionMajor");
text = doc.createTextNode("3");
author_version_version_major.appendChild(text);
author_version.appendChild(author_version_version_major);
QDomElement author_version_version_major = doc.createElement("VersionMajor");
text = doc.createTextNode("3");
author_version_version_major.appendChild(text);
author_version.appendChild(author_version_version_major);
QDomElement author_version_version_minor = doc.createElement("VersionMinor");
text = doc.createTextNode("0");
author_version_version_minor.appendChild(text);
author_version.appendChild(author_version_version_minor);
QDomElement author_version_version_minor = doc.createElement("VersionMinor");
text = doc.createTextNode("0");
author_version_version_minor.appendChild(text);
author_version.appendChild(author_version_version_minor);
QDomElement author_version_build_major = doc.createElement("BuildMajor");
text = doc.createTextNode("0");
author_version_build_major.appendChild(text);
author_version.appendChild(author_version_build_major);
QDomElement author_version_build_major = doc.createElement("BuildMajor");
text = doc.createTextNode("0");
author_version_build_major.appendChild(text);
author_version.appendChild(author_version_build_major);
QDomElement author_version_build_minor = doc.createElement("BuildMinor");
text = doc.createTextNode("0");
author_version_build_minor.appendChild(text);
author_version.appendChild(author_version_build_minor);
QDomElement author_version_build_minor = doc.createElement("BuildMinor");
text = doc.createTextNode("0");
author_version_build_minor.appendChild(text);
author_version.appendChild(author_version_build_minor);
QDomElement author_type = doc.createElement("Type");
text = doc.createTextNode("Beta"); // was GC_VERSION but should be Interna | Alpha | Beta | Release
author_type.appendChild(text);
author_build.appendChild(author_type);
QDomElement author_type = doc.createElement("Type");
text = doc.createTextNode("Beta"); // was GC_VERSION but should be Interna | Alpha | Beta | Release
author_type.appendChild(text);
author_build.appendChild(author_type);
QDomElement author_lang = doc.createElement("LangID");
QVariant lang = appsettings->value(NULL, GC_LANG, QLocale::system().name());
text = doc.createTextNode(lang.toString());
author_lang.appendChild(text);
author.appendChild(author_lang);
QDomElement author_part_number = doc.createElement("PartNumber");
text = doc.createTextNode("000-00000-00");
author_part_number.appendChild(text);
author.appendChild(author_part_number);
QDomElement author_lang = doc.createElement("LangID");
QVariant lang = appsettings->value(NULL, GC_LANG, QLocale::system().name());
text = doc.createTextNode(lang.toString());
author_lang.appendChild(text);
author.appendChild(author_lang);
QDomElement author_part_number = doc.createElement("PartNumber");
text = doc.createTextNode("000-00000-00");
author_part_number.appendChild(text);
author.appendChild(author_part_number);
}
return doc.toByteArray(4);
}