Deprecate TrainingPeaks

.. from December 7th 2016 read access (for downloading
   from TrainingPeaks) has been revoked, even for paid
   up members.

.. write access (upload) is not revoked.

.. This is essentially anti-competitive behaviour and
   returns TrainingPeaks and their products, once again
   to that of a 'closed' product.

.. We will no longer support their service since we
   cannot and will not promote such behaviours now
   or ever.

.. We are now considering the best way to purge all
   other references to their trademarks and remove
   any indication that their products are endorsed.
This commit is contained in:
Mark Liversedge
2016-11-27 12:10:57 +00:00
parent fb14bce879
commit a74c2f9c38
15 changed files with 2 additions and 94 deletions

246
deprecated/TPDownload.cpp Normal file
View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "TPDownload.h"
#include <QString>
#include "Settings.h"
#include "RideFile.h"
#include "PwxRideFile.h"
#include <QtXml>
#include <QtCore/QCoreApplication>
#include <QtCore/QLocale>
static const QString tptypestrings[]= {
"SharedFree", "CoachedFree", "SelfCoachedPremium",
"SharedSelfCoachedPremium", "CoachedPremium", "SharedCoachedPremium"
};
//
// Get athletes available for download
//
TPAthlete::TPAthlete(QObject *parent) : QObject(parent), http(this), waiting(false)
{
connect(&http, SIGNAL(responseReady(const QtSoapMessage &)),
this, SLOT(getResponse(const QtSoapMessage &)));
}
void
TPAthlete::list(int type, QString user, QString pass)
{
// if currently downloading fail!
if (waiting == true) {
return;
}
// setup the soap message
http.setHost("www.trainingpeaks.com", true);
http.setAction("http://www.trainingpeaks.com/TPWebServices/GetAccessibleAthletes");
current.setMethod("GetAccessibleAthletes", "http://www.trainingpeaks.com/TPWebServices/");
current.addMethodArgument("username", "", user);
current.addMethodArgument("password", "", pass);
current.addMethodArgument("types", "",
tptypestrings[type].toLatin1().constData());
// do it!
waiting = true;
http.submitRequest(current, "/tpwebservices/service.asmx");
}
void TPAthlete::getResponse(const QtSoapMessage &message)
{
waiting = false;
QString resultStr;
QList< QMap<QString, QString> > athletelist;
if (message.isFault()) {
resultStr = tr("Error:") + qPrintable(message.faultString().toString());
}
else {
const QtSoapType &response = message.returnValue();
if (response.isValid()) {
// lets take look at the payload as a DomDocument
// I tried to use the QtSoapType routines to walk
// through the response tree but couldn't get them
// to work. This code could be simplified if we
// use the QtSoap routines instead.
QDomDocument doc;
QDomElement results = response.toDomElement(doc);
QMap<QString, QString> athlete;
for (QDomElement personbase = results.firstChildElement("PersonBase");
!personbase.isNull();
personbase = personbase.nextSiblingElement()) {
athlete.clear();
// shove all the personbase attribs into a QMap
for (QDomNode child = personbase.firstChild();
!child.isNull();
child = child.nextSibling()) {
athlete.insert(child.nodeName(), child.toElement().text());
}
athletelist << athlete;
}
}
}
// return what we got (empty if non-valid response)
completed(resultStr, athletelist);
}
//
// List workouts
//
TPWorkout::TPWorkout(QObject *parent) : QObject(parent), http(this), waiting(false)
{
connect(&http, SIGNAL(responseReady(const QtSoapMessage &)),
this, SLOT(getResponse(const QtSoapMessage &)));
}
void
TPWorkout::list(int id, QDate from, QDate to, QString user, QString pass)
{
// if currently downloading fail!
if (waiting == true) return;
current = QtSoapMessage(); // reset
// setup the soap message
http.setHost("www.trainingpeaks.com", true);
http.setAction("http://www.trainingpeaks.com/TPWebServices/GetWorkoutsForAccessibleAthlete");
current.setMethod("GetWorkoutsForAccessibleAthlete", "http://www.trainingpeaks.com/TPWebServices/");
current.addMethodArgument("username", "", user);
current.addMethodArgument("password", "", pass);
current.addMethodArgument("personId", "", QString("%1").arg(id).toLatin1().constData());
current.addMethodArgument("startDate", "", from.toString("yyyy-MM-dd").toLatin1().constData());
current.addMethodArgument("endDate", "", to.toString("yyyy-MM-dd").toLatin1().constData());
// do it!
waiting = true;
http.submitRequest(current, "/tpwebservices/service.asmx");
}
void TPWorkout::getResponse(const QtSoapMessage &message)
{
waiting = false;
QList< QMap<QString, QString> > workoutlist;
if (!message.isFault()) {
const QtSoapType &response = message.returnValue();
if (response.isValid()) {
// lets take look at the payload as a DomDocument
// I tried to use the QtSoapType routines to walk
// through the response tree but couldn't get them
// to work. This code could be simplified if we
// use the QtSoap routines instead.
QDomDocument doc;
QDomElement results = response.toDomElement(doc);
QMap<QString, QString> athlete;
for (QDomElement personbase = results.firstChildElement("Workout");
!personbase.isNull();
personbase = personbase.nextSiblingElement()) {
athlete.clear();
// shove all the personbase attribs into a QMap
for (QDomNode child = personbase.firstChild();
!child.isNull();
child = child.nextSibling()) {
athlete.insert(child.nodeName(), child.toElement().text());
}
workoutlist << athlete;
}
}
}
// return what we got (empty if failed)
completed(workoutlist);
}
//
// Workout download
//
TPDownload::TPDownload(QObject *parent) : QObject(parent), http(this), downloading(false)
{
connect(&http, SIGNAL(responseReady(const QtSoapMessage &)),
this, SLOT(getResponse(const QtSoapMessage &)));
}
void
TPDownload::download(QString cyclist, int personId, int workoutId)
{
// if currently downloading fail!
if (downloading == true) return;
current = QtSoapMessage();
// setup the soap message
http.setHost("www.trainingpeaks.com", true);
http.setAction("http://www.trainingpeaks.com/TPWebServices/GetExtendedWorkoutsForAccessibleAthlete");
current.setMethod("GetExtendedWorkoutsForAccessibleAthlete", "http://www.trainingpeaks.com/TPWebServices/");
current.addMethodArgument("username", "", appsettings->cvalue(cyclist, GC_TPUSER).toString());
current.addMethodArgument("password", "", appsettings->cvalue(cyclist, GC_TPPASS).toString());
current.addMethodArgument("personId", "", personId);
QtSoapStruct *ints = new QtSoapStruct; // gets taken over by message and free'd there
// if you pass a pointer it all goes boom!
// create a little snippit: <workoutIds><int>xx</int></workoutIds>
QDomDocument meh;
QDomNode workoutIds = meh.createElement("workoutIds");
QDomElement elint = meh.createElement("int");
QDomText text = meh.createTextNode(QString("%1").arg(workoutId));
elint.appendChild(text);
workoutIds.appendChild(elint);
// turn it into the QtSoapStruct needed by the QtSoapMessage
ints->parse(workoutIds);
// add as method arguments
current.addMethodArgument(ints);
// do it!
downloading = true;
http.submitRequest(current, "/tpwebservices/service.asmx");
return;
}
void TPDownload::getResponse(const QtSoapMessage &message)
{
downloading = false;
QString result;
QDomDocument pwx("PWX");
if (!message.isFault()) {
const QtSoapType &response = message.returnValue();
QDomNode workout = response.toDomElement(pwx).firstChild();
pwx.appendChild(workout);
}
completed(pwx);
}

90
deprecated/TPDownload.h Normal file
View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _Gc_Download_h
#define _Gc_Download_h
#include "GoldenCheetah.h"
#include <qtsoap.h>
#include <QString>
#include "RideFile.h"
// get list of accessible athletes
class TPAthlete : public QObject
{
Q_OBJECT
G_OBJECT
public:
TPAthlete(QObject *parent = 0);
void list(int type, QString user, QString pass);
signals:
void completed(QString, QList<QMap<QString,QString> >);
private slots:
void getResponse(const QtSoapMessage &);
private:
QtSoapHttpTransport http;
bool waiting;
QtSoapMessage current;
};
// get workout list
class TPWorkout : public QObject
{
Q_OBJECT
G_OBJECT
public:
TPWorkout(QObject *parent = 0);
void list(int id, QDate from, QDate to, QString user, QString pass);
signals:
void completed(QList<QMap<QString,QString> >);
private slots:
void getResponse(const QtSoapMessage &);
private:
QtSoapHttpTransport http;
bool waiting;
QtSoapMessage current;
};
// uploader to trainingpeaks.com
class TPDownload : public QObject
{
Q_OBJECT
G_OBJECT
public:
TPDownload(QObject *parent = 0);
void download(QString cyclist, int PersonId, int WorkoutId);
signals:
void completed(QDomDocument);
private slots:
void getResponse(const QtSoapMessage &);
private:
QtSoapHttpTransport http;
bool downloading;
QtSoapMessage current;
};
#endif

View File

@@ -0,0 +1,928 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "TPDownloadDialog.h"
#include "TPDownload.h"
#include "MainWindow.h"
#include "Context.h"
#include "Athlete.h"
#include "PwxRideFile.h"
#include "JsonRideFile.h"
#include "Settings.h"
#include "Units.h"
TPDownloadDialog::TPDownloadDialog(Context *context) : QDialog(context->mainWindow, Qt::Dialog), context(context), downloading(false), aborted(false)
{
setWindowTitle(tr("Synchronise TrainingPeaks.com"));
athleter = new TPAthlete(this);
connect (athleter, SIGNAL(completed(QString, QList<QMap<QString,QString> >)), this, SLOT(completedAthlete(QString, QList<QMap<QString,QString> >)));
athleter->list(appsettings->cvalue(context->athlete->cyclist, GC_TPTYPE, "0").toInt(),
appsettings->cvalue(context->athlete->cyclist, GC_TPUSER, "null").toString(),
appsettings->cvalue(context->athlete->cyclist, GC_TPPASS, "null").toString());
setMinimumSize(850,450);
QWidget::hide(); // don't show just yet...
QApplication::processEvents();
}
void
TPDownloadDialog::completedAthlete(QString errorStr, QList<QMap<QString, QString> >athletes)
{
// did we get any athletes?
if (athletes.count() == 0) {
QWidget::hide(); // meh
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Download from TrainingPeaks.com"));
if (errorStr.size() != 0) { // Something went wrong, so there should be a fault message
msgBox.setText(errorStr);
}
else {
msgBox.setText(tr("You must be a premium member to download from TrainingPeaks. Please check your cyclist configurations are correct on the Passwords tab."));
}
msgBox.setIcon(QMessageBox::Critical);
msgBox.exec();
reject();
return;
}
// setup tabs
tabs = new QTabWidget(this);
QWidget * upload = new QWidget(this);
QWidget * download = new QWidget(this);
QWidget * sync = new QWidget(this);
tabs->addTab(download, tr("Download"));
tabs->addTab(upload, tr("Upload"));
tabs->addTab(sync, tr("Synchronize"));
tabs->setCurrentIndex(2);
QVBoxLayout *downloadLayout = new QVBoxLayout(download);
QVBoxLayout *uploadLayout = new QVBoxLayout(upload);
QVBoxLayout *syncLayout = new QVBoxLayout(sync);
workouter = new TPWorkout(this);
connect (workouter, SIGNAL(completed(QList<QMap<QString,QString> >)), this,
SLOT(completedWorkout(QList<QMap<QString,QString> >)));
downloader = new TPDownload(this);
connect (downloader, SIGNAL(completed(QDomDocument)), this,
SLOT(completedDownload(QDomDocument)));
uploader = new TPUpload(this);
connect (uploader, SIGNAL(completed(QString)), this,
SLOT(completedUpload(QString)));
// OK! Lets build up that dialog box
athlete = athletes[0];
// combo box
athleteCombo = new QComboBox(this);
for (int i=0; i< athletes.count(); i++) {
QString name = QString("%1 %2").arg(athletes[i].value("FirstName"))
.arg(athletes[i].value("LastName"));
int id = athletes[i].value("PersonId").toInt();
athleteCombo->addItem(name, id);
}
athleteCombo->setCurrentIndex(0);
QLabel *fromLabel = new QLabel(tr("From:"), this);
QLabel *toLabel = new QLabel(tr("To:"), this);
from = new QDateEdit(this);
from->setDate(QDate::currentDate().addMonths(-1));
from->setCalendarPopup(true);
to = new QDateEdit(this);
to->setDate(QDate::currentDate());
to->setCalendarPopup(true);
// Buttons
refreshButton = new QPushButton(tr("Refresh List"), this);
cancelButton = new QPushButton(tr("Close"),this);
downloadButton = new QPushButton(tr("Download"),this);
selectAll = new QCheckBox(tr("Select all"), this);
selectAll->setChecked(Qt::Unchecked);
// ride list
rideList = new QTreeWidget(this);
rideList->headerItem()->setText(0, " ");
rideList->headerItem()->setText(1, "Workout Id");
rideList->headerItem()->setText(2, "Date");
rideList->headerItem()->setText(3, "Time");
rideList->headerItem()->setText(4, "Duration");
rideList->headerItem()->setText(5, "Distance");
rideList->headerItem()->setText(6, "Exists");
rideList->headerItem()->setText(7, "Status");
rideList->setColumnCount(8);
rideList->setSelectionMode(QAbstractItemView::SingleSelection);
rideList->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
rideList->setUniformRowHeights(true);
rideList->setIndentation(0);
rideList->header()->resizeSection(0,20);
rideList->header()->resizeSection(1,90);
rideList->header()->resizeSection(2,100);
rideList->header()->resizeSection(3,100);
rideList->header()->resizeSection(4,100);
rideList->header()->resizeSection(5,70);
rideList->header()->resizeSection(6,50);
rideList->setSortingEnabled(true);
downloadLayout->addWidget(selectAll);
downloadLayout->addWidget(rideList);
selectAllUp = new QCheckBox(tr("Select all"), this);
selectAllUp->setChecked(Qt::Unchecked);
// ride list
rideListUp = new QTreeWidget(this);
rideListUp->headerItem()->setText(0, " ");
rideListUp->headerItem()->setText(1, "File");
rideListUp->headerItem()->setText(2, "Date");
rideListUp->headerItem()->setText(3, "Time");
rideListUp->headerItem()->setText(4, "Duration");
rideListUp->headerItem()->setText(5, "Distance");
rideListUp->headerItem()->setText(6, "Exists");
rideListUp->headerItem()->setText(7, "Status");
rideListUp->setColumnCount(8);
rideListUp->setSelectionMode(QAbstractItemView::SingleSelection);
rideListUp->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
rideListUp->setUniformRowHeights(true);
rideListUp->setIndentation(0);
rideListUp->header()->resizeSection(0,20);
rideListUp->header()->resizeSection(1,200);
rideListUp->header()->resizeSection(2,100);
rideListUp->header()->resizeSection(3,100);
rideListUp->header()->resizeSection(4,100);
rideListUp->header()->resizeSection(5,70);
rideListUp->header()->resizeSection(6,50);
rideListUp->setSortingEnabled(true);
uploadLayout->addWidget(selectAllUp);
uploadLayout->addWidget(rideListUp);
selectAllSync = new QCheckBox(tr("Select all"), this);
selectAllSync->setChecked(Qt::Unchecked);
syncMode = new QComboBox(this);
syncMode->addItem(tr("Keep all do not delete"));
syncMode->addItem(tr("Keep TP.com but delete Local"));
syncMode->addItem(tr("Keep Local but delete TP.com"));
QHBoxLayout *syncList = new QHBoxLayout;
syncList->addWidget(selectAllSync);
syncList->addStretch();
syncList->addWidget(syncMode);
// ride list
rideListSync = new QTreeWidget(this);
rideListSync->headerItem()->setText(0, " ");
rideListSync->headerItem()->setText(1, "Source");
rideListSync->headerItem()->setText(2, "Date");
rideListSync->headerItem()->setText(3, "Time");
rideListSync->headerItem()->setText(4, "Duration");
rideListSync->headerItem()->setText(5, "Distance");
rideListSync->headerItem()->setText(6, "Action");
rideListSync->headerItem()->setText(7, "Status");
rideListSync->setColumnCount(8);
rideListSync->setSelectionMode(QAbstractItemView::SingleSelection);
rideListSync->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
rideListSync->setUniformRowHeights(true);
rideListSync->setIndentation(0);
rideListSync->header()->resizeSection(0,20);
rideListSync->header()->resizeSection(1,200);
rideListSync->header()->resizeSection(2,100);
rideListSync->header()->resizeSection(3,100);
rideListSync->header()->resizeSection(4,100);
rideListSync->header()->resizeSection(5,70);
rideListSync->header()->resizeSection(6,100);
rideListSync->setSortingEnabled(true);
syncLayout->addLayout(syncList);
syncLayout->addWidget(rideListSync);
// show progress
progressBar = new QProgressBar(this);
progressLabel = new QLabel("Initial", this);
overwrite = new QCheckBox(tr("Overwrite existing files"), this);
// layout the widget now...
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QHBoxLayout *topline = new QHBoxLayout;
topline->addWidget(athleteCombo);
topline->addStretch();
topline->addWidget(fromLabel);
topline->addWidget(from);
topline->addWidget(toLabel);
topline->addWidget(to);
topline->addStretch();
topline->addWidget(refreshButton);
QHBoxLayout *botline = new QHBoxLayout;
botline->addWidget(progressLabel);
botline->addStretch();
botline->addWidget(overwrite);
botline->addWidget(cancelButton);
botline->addWidget(downloadButton);
mainLayout->addLayout(topline);
mainLayout->addWidget(tabs);
mainLayout->addWidget(progressBar);
mainLayout->addLayout(botline);
connect (cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
connect (refreshButton, SIGNAL(clicked()), this, SLOT(refreshClicked()));
connect (selectAll, SIGNAL(stateChanged(int)), this, SLOT(selectAllChanged(int)));
connect (selectAllUp, SIGNAL(stateChanged(int)), this, SLOT(selectAllUpChanged(int)));
connect (selectAllSync, SIGNAL(stateChanged(int)), this, SLOT(selectAllSyncChanged(int)));
connect (downloadButton, SIGNAL(clicked()), this, SLOT(downloadClicked()));
connect (tabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
QWidget::show();
// refresh anyway
refreshClicked();
}
void
TPDownloadDialog::cancelClicked()
{
reject();
}
void
TPDownloadDialog::refreshClicked()
{
progressLabel->setText(tr("Downloading list..."));
progressBar->setMinimum(0);
progressBar->setMaximum(1);
progressBar->setValue(0);
// wipe out current
foreach (QTreeWidgetItem *curr, rideList->invisibleRootItem()->takeChildren()) {
QCheckBox *check = (QCheckBox*)rideList->itemWidget(curr, 0);
QCheckBox *exists = (QCheckBox*)rideList->itemWidget(curr, 6);
delete check;
delete exists;
delete curr;
}
foreach (QTreeWidgetItem *curr, rideListUp->invisibleRootItem()->takeChildren()) {
QCheckBox *check = (QCheckBox*)rideListUp->itemWidget(curr, 0);
QCheckBox *exists = (QCheckBox*)rideListUp->itemWidget(curr, 6);
delete check;
delete exists;
delete curr;
}
foreach (QTreeWidgetItem *curr, rideListSync->invisibleRootItem()->takeChildren()) {
QCheckBox *check = (QCheckBox*)rideListSync->itemWidget(curr, 0);
delete check;
delete curr;
}
// First lets kick off a download of ridefile lookups
// since that can take a while
workouter->list(
athleteCombo->itemData(athleteCombo->currentIndex()).toInt(),
from->date(),
to->date(),
appsettings->cvalue(context->athlete->cyclist, GC_TPUSER, "null").toString(),
appsettings->cvalue(context->athlete->cyclist, GC_TPPASS, "null").toString());
// Whilst we wait for the results lets fill the map of existing rideFiles
// (but ignore seconds since they aren't reliable)
rideFiles.clear();
Specification specification;
specification.setDateRange(DateRange(from->date(), to->date()));
foreach(RideItem *item, context->athlete->rideCache->rides()) {
if (specification.pass(item))
rideFiles << QFileInfo(item->fileName).baseName().mid(0,14);
}
}
void
TPDownloadDialog::tabChanged(int idx)
{
if (downloadButton->text() == "Abort") return;
switch (idx) {
case 0 : // download
downloadButton->setText(tr("Download"));
refreshCount();
break;
case 1 : // upload
downloadButton->setText(tr("Upload"));
refreshUpCount();
break;
case 2 : // synchronise
downloadButton->setText(tr("Synchronize"));
refreshSyncCount();
break;
}
}
void
TPDownloadDialog::completedWorkout(QList<QMap<QString, QString> >workouts)
{
useMetricUnits = context->athlete->useMetricUnits;
//
// Setup the upload list
//
uploadFiles.clear();
for(int i=0; i<workouts.count(); i++) {
QTreeWidgetItem *add;
add = new QTreeWidgetItem(rideList->invisibleRootItem());
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
QCheckBox *check = new QCheckBox("", this);
connect (check, SIGNAL(stateChanged(int)), this, SLOT(refreshCount()));
rideList->setItemWidget(add, 0, check);
add->setText(1, workouts[i].value("WorkoutId"));
add->setTextAlignment(1, Qt::AlignCenter);
QDateTime ridedatetime(QDateTime::fromString(workouts[i].value("WorkoutDay"), Qt::ISODate).date(),
QDateTime::fromString(workouts[i].value("StartTime"), Qt::ISODate).time());
add->setText(2, ridedatetime.toString("MMM d, yyyy"));
add->setTextAlignment(2, Qt::AlignLeft);
add->setText(3, ridedatetime.toString("hh:mm:ss"));
add->setTextAlignment(3, Qt::AlignCenter);
long secs = workouts[i].value("TimeTotalInSeconds").toInt();
QChar zero = QLatin1Char ( '0' );
QString duration = QString("%1:%2:%3").arg(secs/3600,2,10,zero)
.arg(secs%3600/60,2,10,zero)
.arg(secs%60,2,10,zero);
add->setText(4, duration);
add->setTextAlignment(4, Qt::AlignCenter);
double distance = workouts[i].value("DistanceInMeters").toDouble() / 1000.00;
if (useMetricUnits) {
add->setText(5, QString("%1 km").arg(distance, 0, 'f', 1));
} else {
add->setText(5, QString("%1 mi").arg(distance*MILES_PER_KM, 0, 'f', 1));
}
add->setTextAlignment(5, Qt::AlignRight);
QString targetnosuffix = QString ( "%1_%2_%3_%4_%5_%6" )
.arg ( ridedatetime.date().year(), 4, 10, zero )
.arg ( ridedatetime.date().month(), 2, 10, zero )
.arg ( ridedatetime.date().day(), 2, 10, zero )
.arg ( ridedatetime.time().hour(), 2, 10, zero )
.arg ( ridedatetime.time().minute(), 2, 10, zero )
.arg ( ridedatetime.time().second(), 2, 10, zero );
uploadFiles << targetnosuffix.mid(0,14);
// exists? - we ignore seconds, since TP seems to do odd
// things to date times and loses seconds (?)
QCheckBox *exists = new QCheckBox("", this);
exists->setEnabled(false);
rideList->setItemWidget(add, 6, exists);
add->setTextAlignment(6, Qt::AlignCenter);
if (rideFiles.contains(targetnosuffix.mid(0,14))) exists->setChecked(Qt::Checked);
else {
exists->setChecked(Qt::Unchecked);
// doesn't exist -- add it to the sync list too then
QTreeWidgetItem *sync = new QTreeWidgetItem(rideListSync->invisibleRootItem());
QCheckBox *check = new QCheckBox("", this);
connect (check, SIGNAL(stateChanged(int)), this, SLOT(refreshSyncCount()));
rideListSync->setItemWidget(sync, 0, check);
sync->setText(1, workouts[i].value("WorkoutId"));
sync->setTextAlignment(1, Qt::AlignCenter);
sync->setText(2, ridedatetime.toString("MMM d, yyyy"));
sync->setTextAlignment(2, Qt::AlignLeft);
sync->setText(3, ridedatetime.toString("hh:mm:ss"));
sync->setTextAlignment(3, Qt::AlignCenter);
sync->setText(4, duration);
sync->setTextAlignment(4, Qt::AlignCenter);
sync->setText(5, QString("%1 km").arg(distance, 0, 'f', 1));
sync->setTextAlignment(5, Qt::AlignRight);
sync->setText(6, tr("Download"));
sync->setTextAlignment(6, Qt::AlignLeft);
sync->setText(7, "");
}
add->setText(7, "");
}
//
// Now setup the upload list
//
Specification specification;
specification.setDateRange(DateRange(from->date(), to->date()));
for(int i=0; i<context->athlete->rideCache->rides().count(); i++) {
RideItem *ride = context->athlete->rideCache->rides().at(i);
if (!specification.pass(ride)) continue;
QTreeWidgetItem *add;
add = new QTreeWidgetItem(rideListUp->invisibleRootItem());
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
QCheckBox *check = new QCheckBox("", this);
connect (check, SIGNAL(stateChanged(int)), this, SLOT(refreshUpCount()));
rideListUp->setItemWidget(add, 0, check);
add->setText(1, ride->fileName);
add->setTextAlignment(1, Qt::AlignLeft);
add->setText(2, ride->dateTime.toString("MMM d, yyyy"));
add->setTextAlignment(2, Qt::AlignLeft);
add->setText(3, ride->dateTime.toString("hh:mm:ss"));
add->setTextAlignment(3, Qt::AlignCenter);
long secs = ride->getForSymbol("workout_time");
QChar zero = QLatin1Char ( '0' );
QString duration = QString("%1:%2:%3").arg(secs/3600,2,10,zero)
.arg(secs%3600/60,2,10,zero)
.arg(secs%60,2,10,zero);
add->setText(4, duration);
add->setTextAlignment(4, Qt::AlignCenter);
double distance = ride->getForSymbol("total_distance");
add->setText(5, QString("%1 km").arg(distance, 0, 'f', 1));
add->setTextAlignment(5, Qt::AlignRight);
// exists? - we ignore seconds, since TP seems to do odd
// things to date times and loses seconds (?)
QCheckBox *exists = new QCheckBox("", this);
exists->setEnabled(false);
rideListUp->setItemWidget(add, 6, exists);
add->setTextAlignment(6, Qt::AlignCenter);
QString targetnosuffix = QString ( "%1_%2_%3_%4_%5_%6" )
.arg ( ride->dateTime.date().year(), 4, 10, zero )
.arg ( ride->dateTime.date().month(), 2, 10, zero )
.arg ( ride->dateTime.date().day(), 2, 10, zero )
.arg ( ride->dateTime.time().hour(), 2, 10, zero )
.arg ( ride->dateTime.time().minute(), 2, 10, zero )
.arg ( ride->dateTime.time().second(), 2, 10, zero );
// check if on TP.com already
if (uploadFiles.contains(targetnosuffix.mid(0,14))) exists->setChecked(Qt::Checked);
else {
exists->setChecked(Qt::Unchecked);
// doesn't exist -- add it to the sync list too then
QTreeWidgetItem *sync = new QTreeWidgetItem(rideListSync->invisibleRootItem());
QCheckBox *check = new QCheckBox("", this);
connect (check, SIGNAL(stateChanged(int)), this, SLOT(refreshSyncCount()));
rideListSync->setItemWidget(sync, 0, check);
sync->setText(1, ride->fileName);
sync->setTextAlignment(1, Qt::AlignCenter);
sync->setText(2, ride->dateTime.toString("MMM d, yyyy"));
sync->setTextAlignment(2, Qt::AlignLeft);
sync->setText(3, ride->dateTime.toString("hh:mm:ss"));
sync->setTextAlignment(3, Qt::AlignCenter);
sync->setText(4, duration);
sync->setTextAlignment(4, Qt::AlignCenter);
sync->setText(5, QString("%1 km").arg(distance, 0, 'f', 1));
sync->setTextAlignment(5, Qt::AlignRight);
sync->setText(6, tr("Upload"));
sync->setTextAlignment(6, Qt::AlignLeft);
sync->setText(7, "");
}
add->setText(7, "");
}
// refresh the progress label
tabChanged(tabs->currentIndex());
}
void
TPDownloadDialog::selectAllChanged(int state)
{
for (int i=0; i<rideList->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideList->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideList->itemWidget(curr, 0);
check->setChecked(state);
}
}
void
TPDownloadDialog::selectAllUpChanged(int state)
{
for (int i=0; i<rideListUp->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListUp->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListUp->itemWidget(curr, 0);
check->setChecked(state);
}
}
void
TPDownloadDialog::selectAllSyncChanged(int state)
{
for (int i=0; i<rideListSync->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListSync->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListSync->itemWidget(curr, 0);
check->setChecked(state);
}
}
void
TPDownloadDialog::refreshUpCount()
{
int selected = 0;
for (int i=0; i<rideListUp->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListUp->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListUp->itemWidget(curr, 0);
if (check->isChecked()) selected++;
}
progressLabel->setText(QString("%1 of %2 selected").arg(selected)
.arg(rideListUp->invisibleRootItem()->childCount()));
}
void
TPDownloadDialog::refreshSyncCount()
{
int selected = 0;
for (int i=0; i<rideListSync->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListSync->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListSync->itemWidget(curr, 0);
if (check->isChecked()) selected++;
}
progressLabel->setText(QString("%1 of %2 selected").arg(selected)
.arg(rideListSync->invisibleRootItem()->childCount()));
}
void
TPDownloadDialog::refreshCount()
{
int selected = 0;
for (int i=0; i<rideList->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideList->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideList->itemWidget(curr, 0);
if (check->isChecked()) selected++;
}
progressLabel->setText(QString("%1 of %2 selected").arg(selected)
.arg(rideList->invisibleRootItem()->childCount()));
}
void
TPDownloadDialog::downloadClicked()
{
if (downloading == true) {
rideList->setSortingEnabled(true);
rideListUp->setSortingEnabled(true);
progressLabel->setText("");
downloadButton->setText("Download");
downloading=false;
aborted=true;
cancelButton->show();
return;
} else {
rideList->setSortingEnabled(false);
rideListUp->setSortingEnabled(true);
downloading=true;
aborted=false;
downloadButton->setText("Abort");
cancelButton->hide();
}
// keeping track of progress...
downloadcounter = 0;
successful = 0;
downloadtotal = 0;
listindex = 0;
QTreeWidget *which = NULL;
switch(tabs->currentIndex()) {
case 0 : which = rideList; break;
case 1 : which = rideListUp; break;
default:
case 2 : which = rideListSync; break;
}
for (int i=0; i<which->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = which->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)which->itemWidget(curr, 0);
if (check->isChecked()) {
downloadtotal++;
}
}
if (downloadtotal) {
progressBar->setMaximum(downloadtotal);
progressBar->setMinimum(0);
progressBar->setValue(0);
}
// even if nothing to download this
// cleans up variables et al
sync = false;
switch(tabs->currentIndex()) {
case 0 : downloadNext(); break;
case 1 : uploadNext(); break;
case 2 : sync = true; syncNext(); break;
}
}
bool
TPDownloadDialog::syncNext()
{
// the actual download/upload is kicked off using the uploader / downloader
// if in sync mode the completedDownload / completedUpload functions
// just call completedSync to get the next Sync done
for (int i=listindex; i<rideListSync->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListSync->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListSync->itemWidget(curr, 0);
if (check->isChecked()) {
listindex = i+1; // start from the next one
progressLabel->setText(QString(tr("Processed %1 of %2")).arg(downloadcounter).arg(downloadtotal));
if (curr->text(6) == "Download") {
curr->setText(7, tr("Downloading"));
rideListSync->setCurrentItem(curr);
downloader->download(
context->athlete->cyclist,
athleteCombo->itemData(athleteCombo->currentIndex()).toInt(),
curr->text(1).toInt()
);
QApplication::processEvents();
} else {
curr->setText(7, tr("Uploading"));
rideListSync->setCurrentItem(curr);
// read in the file
QStringList errors;
QFile file(context->athlete->home->activities().canonicalPath() + "/" + curr->text(1));
RideFile *ride = RideFileFactory::instance().openRideFile(context, file, errors);
if (ride) {
uploader->upload(context, ride);
delete ride; // clean up!
QApplication::processEvents();
return true;
} else {
curr->setText(7, "Parse failure");
QApplication::processEvents();
}
}
return true;
}
}
//
// Our work is done!
//
rideList->setSortingEnabled(true);
rideListUp->setSortingEnabled(true);
rideListSync->setSortingEnabled(true);
progressLabel->setText(tr("Sync complete"));
downloadButton->setText("Synchronize");
downloading=false;
aborted=false;
sync=false;
cancelButton->show();
selectAllSync->setChecked(Qt::Unchecked);
for (int i=0; i<rideListSync->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListSync->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListSync->itemWidget(curr, 0);
check->setChecked(false);
}
progressLabel->setText(QString(tr("Processed %1 of %2 successfully")).arg(successful).arg(downloadtotal));
return false;
}
bool
TPDownloadDialog::downloadNext()
{
for (int i=listindex; i<rideList->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideList->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideList->itemWidget(curr, 0);
QCheckBox *exists = (QCheckBox*)rideList->itemWidget(curr, 6);
// skip existing if overwrite not set
if (check->isChecked() && exists->isChecked() && !overwrite->isChecked()) {
curr->setText(7, tr("File exists"));
progressBar->setValue(++downloadcounter);
continue;
}
if (check->isChecked()) {
listindex = i+1; // start from the next one
curr->setText(7, tr("Downloading"));
rideList->setCurrentItem(curr);
progressLabel->setText(QString(tr("Downloaded %1 of %2")).arg(downloadcounter).arg(downloadtotal));
downloader->download(
context->athlete->cyclist,
athleteCombo->itemData(athleteCombo->currentIndex()).toInt(),
curr->text(1).toInt()
);
QApplication::processEvents();
return true;
}
}
//
// Our work is done!
//
rideList->setSortingEnabled(true);
rideListUp->setSortingEnabled(true);
progressLabel->setText(tr("Downloads complete"));
downloadButton->setText("Download");
downloading=false;
aborted=false;
cancelButton->show();
selectAll->setChecked(Qt::Unchecked);
for (int i=0; i<rideList->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideList->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideList->itemWidget(curr, 0);
check->setChecked(false);
}
progressLabel->setText(QString(tr("Downloaded %1 of %2 successfully")).arg(successful).arg(downloadtotal));
return false;
}
void
TPDownloadDialog::completedDownload(QDomDocument pwx)
{
QTreeWidget *which = sync ? rideListSync : rideList;
// was abort pressed?
if (aborted == true) {
QTreeWidgetItem *curr = which->invisibleRootItem()->child(listindex-1);
curr->setText(7, tr("Aborted"));
return;
}
// update status...
// validate (parse)
QStringList errors;
PwxFileReader reader;
RideFile *ride = reader.PwxFromDomDoc(pwx, errors);
progressBar->setValue(++downloadcounter);
QTreeWidgetItem *curr = which->invisibleRootItem()->child(listindex-1);
if (ride) {
if (saveRide(ride, pwx, errors) == true) {
curr->setText(7, tr("Saved"));
successful++;
} else {
curr->setText(7, errors.join(","));
}
} else {
QString err = "Failed:" + errors.join(",");
curr->setText(7, err);
}
QApplication::processEvents();
if (sync)
syncNext();
else
downloadNext();
}
bool
TPDownloadDialog::uploadNext()
{
for (int i=listindex; i<rideListUp->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListUp->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListUp->itemWidget(curr, 0);
QCheckBox *exists = (QCheckBox*)rideListUp->itemWidget(curr, 6);
// skip existing if overwrite not set
if (check->isChecked() && exists->isChecked() && !overwrite->isChecked()) {
curr->setText(7, tr("File exists"));
progressBar->setValue(++downloadcounter);
continue;
}
if (check->isChecked()) {
listindex = i+1; // start from the next one
curr->setText(7, tr("Uploading"));
rideListUp->setCurrentItem(curr);
progressLabel->setText(QString(tr("Uploaded %1 of %2")).arg(downloadcounter).arg(downloadtotal));
// read in the file
QStringList errors;
QFile file(context->athlete->home->activities().canonicalPath() + "/" + curr->text(1));
RideFile *ride = RideFileFactory::instance().openRideFile(context, file, errors);
if (ride) {
uploader->upload(context, ride);
delete ride; // clean up!
QApplication::processEvents();
return true;
} else {
curr->setText(7, "Parse failure");
QApplication::processEvents();
}
}
}
//
// Our work is done!
//
rideList->setSortingEnabled(true);
rideListUp->setSortingEnabled(true);
progressLabel->setText(tr("Uploads complete"));
downloadButton->setText("Upload");
downloading=false;
aborted=false;
cancelButton->show();
selectAllUp->setChecked(Qt::Unchecked);
for (int i=0; i<rideListUp->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *curr = rideListUp->invisibleRootItem()->child(i);
QCheckBox *check = (QCheckBox*)rideListUp->itemWidget(curr, 0);
check->setChecked(false);
}
progressLabel->setText(QString(tr("Uploaded %1 of %2 successfully")).arg(successful).arg(downloadtotal));
return false;
}
void
TPDownloadDialog::completedUpload(QString result)
{
QTreeWidget *which = sync ? rideListSync : rideListUp;
// was abort pressed?
if (aborted == true) {
QTreeWidgetItem *curr = which->invisibleRootItem()->child(listindex-1);
curr->setText(7, tr("Aborted"));
return;
}
progressBar->setValue(++downloadcounter);
QTreeWidgetItem *curr = which->invisibleRootItem()->child(listindex-1);
curr->setText(7, result);
if (result == tr("Upload successful")) successful++;
QApplication::processEvents();
if (sync)
syncNext();
else
uploadNext();
}
bool
TPDownloadDialog::saveRide(RideFile *ride, QDomDocument &, QStringList &errors)
{
QDateTime ridedatetime = ride->startTime();
QChar zero = QLatin1Char ( '0' );
QString targetnosuffix = QString ( "%1_%2_%3_%4_%5_%6" )
.arg ( ridedatetime.date().year(), 4, 10, zero )
.arg ( ridedatetime.date().month(), 2, 10, zero )
.arg ( ridedatetime.date().day(), 2, 10, zero )
.arg ( ridedatetime.time().hour(), 2, 10, zero )
.arg ( ridedatetime.time().minute(), 2, 10, zero )
.arg ( ridedatetime.time().second(), 2, 10, zero );
QString filename = context->athlete->home->activities().canonicalPath() + "/" + targetnosuffix + ".json";
// exists?
QFileInfo fileinfo(filename);
if (fileinfo.exists() && overwrite->isChecked() == false) {
errors << "File exists";
return false;
}
JsonFileReader reader;
QFile file(filename);
reader.writeRideFile(context, ride, file);
// add to the ride list
rideFiles<<targetnosuffix;
context->athlete->addRide(fileinfo.fileName(), true);
return true;
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _GC_TPDownloadDialog_h
#define _GC_TPDownloadDialog_h 1
#include "GoldenCheetah.h"
#include <QtGui>
#include <QDialog>
#include <QCheckBox>
#include <QHeaderView>
#include <QProgressBar>
#include <QMessageBox>
#include <QTreeWidget>
#include <QTabWidget>
#include <QLabel>
#include "RideCache.h"
#include "RideItem.h"
#include "RideFile.h"
#include "TPDownload.h"
#include "TPUpload.h"
class Context;
class TPDownloadDialog : public QDialog
{
Q_OBJECT
G_OBJECT
public:
TPDownloadDialog(Context *context);
protected:
// cached state
QSettings *settings;
bool useMetricUnits;
public slots:
void completedAthlete(QString, QList<QMap<QString,QString> >);
void completedWorkout(QList<QMap<QString,QString> >);
void completedDownload(QDomDocument);
void completedUpload(QString);
void cancelClicked();
void refreshClicked();
void tabChanged(int);
void downloadClicked();
void refreshCount();
void refreshUpCount();
void refreshSyncCount();
void selectAllChanged(int);
void selectAllUpChanged(int);
void selectAllSyncChanged(int);
private:
Context *context;
TPDownload *downloader;
TPUpload *uploader;
TPAthlete *athleter;
TPWorkout *workouter;
bool downloading;
bool sync;
bool aborted;
// Quick lists for checking if file exists
// locally (rideFiles) or remotely (uploadFiles)
QStringList rideFiles;
QStringList uploadFiles;
// keeping track of progress...
int downloadcounter, // *x* of n downloading
downloadtotal, // x of *n* downloading
successful, // how many downloaded ok?
listindex; // where in rideList we've got to
bool saveRide(RideFile *, QDomDocument &, QStringList &);
bool syncNext(); // kick off another download/upload
// returns false if none left
bool downloadNext(); // kick off another download
// returns false if none left
bool uploadNext(); // kick off another upload
// returns false if none left
// tabs - Upload/Download
QTabWidget *tabs;
// athlete selection
QMap<QString, QString> athlete;
QComboBox *athleteCombo;
QPushButton *refreshButton;
QPushButton *cancelButton;
QPushButton *downloadButton;
QDateEdit *from, *to;
// Download
QCheckBox *selectAll;
QTreeWidget *rideList;
// Upload
QCheckBox *selectAllUp;
QTreeWidget *rideListUp;
// Sync
QCheckBox *selectAllSync;
QTreeWidget *rideListSync;
QComboBox *syncMode;
// show progress
QProgressBar *progressBar;
QLabel *progressLabel;
QCheckBox *overwrite;
};
#endif // _GC_TPDownloadDialog_h

128
deprecated/TPUpload.cpp Normal file
View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "TPUpload.h"
#include "Context.h"
#include "Athlete.h"
#include <QString>
#include "Settings.h"
#include "RideFile.h"
#include "PwxRideFile.h"
#include <QtXml>
#include <QtCore/QCoreApplication>
#include <QtCore/QLocale>
#ifdef Q_CC_MSVC
#include <QtZlib\zlib.h>
#else
#include <zlib.h>
#endif
//
// Utility function to create a QByteArray of data in GZIP format
// This is essentially the same as qCompress but creates it in
// GZIP format (with recquisite headers) instead of ZLIB's format
// which has less filename info in the header
//
static QByteArray zCompress(const QByteArray &source)
{
// int size is source.size()
// const char *data is source.data()
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
// note that (15+16) below means windowbits+_16_ adds the gzip header/footer
deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
// input data
strm.avail_in = source.size();
strm.next_in = (Bytef *)source.data();
// output data - on stack not heap, will be released
QByteArray dest(source.size()/2, '\0'); // should compress by 50%, if not don't bother
strm.avail_out = source.size()/2;
strm.next_out = (Bytef *)dest.data();
// now compress!
deflate(&strm, Z_FINISH);
// return byte array on the stack
return QByteArray(dest.data(), (source.size()/2) - strm.avail_out);
}
TPUpload::TPUpload(QObject *parent) : QObject(parent), http(this), uploading(false)
{
connect(&http, SIGNAL(responseReady(const QtSoapMessage &)),
this, SLOT(getResponse(const QtSoapMessage &)));
}
int
TPUpload::upload(Context *context, const RideFile *ride)
{
// if currently uploading fail!
if (uploading == true) return 0;
// create the file in .pwx format for upload
QString uploadfile(QDir::tempPath() + "/tpupload.pwx");
QFile file(uploadfile);
PwxFileReader reader;
reader.writeRideFile(context, ride, file);
// read the whole thing back and encode as base64binary
file.open(QFile::ReadOnly);
QTextStream stream(&file);
QString thelot = stream.readAll();
QString pwxFile = zCompress(thelot.toUtf8()).toBase64(); // bleck!
file.close();
// setup the soap message
current = QtSoapMessage();
http.setHost("www.trainingpeaks.com", true);
http.setAction("http://www.trainingpeaks.com/TPWebServices/ImportFileForUser");
current.setMethod("ImportFileForUser", "http://www.trainingpeaks.com/TPWebServices/");
current.addMethodArgument("username", "", appsettings->cvalue(context->athlete->cyclist, GC_TPUSER).toString());
current.addMethodArgument("password", "", appsettings->cvalue(context->athlete->cyclist, GC_TPPASS).toString());
current.addMethodArgument("byteData", "", pwxFile);
// do it!
uploading = true;
http.submitRequest(current, "/tpwebservices/service.asmx");
return pwxFile.size();
}
void TPUpload::getResponse(const QtSoapMessage &message)
{
uploading = false;
QString result;
if (message.isFault()) {
result = tr("Error:") + qPrintable(message.faultString().toString());
} else {
// SOAP call succeeded, but was the file accepted?
if (message.returnValue().toString() == "true") result = tr("Upload successful");
else result = tr("Upload failed - file rejected");
}
completed(result);
}

48
deprecated/TPUpload.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _Gc_TPUpload_h
#define _Gc_TPUpload_h
#include "GoldenCheetah.h"
#include <qtsoap.h>
#include <QString>
#include "RideFile.h"
// uploader to trainingpeaks.com
class TPUpload : public QObject
{
Q_OBJECT
G_OBJECT
public:
TPUpload(QObject *parent = 0);
int upload(Context *context, const RideFile *ride);
signals:
void completed(QString);
private slots:
void getResponse(const QtSoapMessage &);
private:
QtSoapHttpTransport http;
bool uploading;
QtSoapMessage current;
};
#endif

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "TPUploadDialog.h"
#include "Context.h"
#include "GcRideFile.h"
#include "RideItem.h"
#include "RideFile.h"
#include "TPUpload.h"
#include "Settings.h"
TPUploadDialog::TPUploadDialog(QString cyclist, const RideFile *ride, Context *context) :
context(context), cyclist(cyclist), ride(ride)
{
setWindowTitle(tr("Upload to TrainingPeaks.com"));
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QHBoxLayout *button = new QHBoxLayout;
statusLabel = new QLabel(tr("Upload in progress..."));
statusLabel->setWordWrap(true);
progressBar = new QProgressBar(this);
progressBar->setMinimum(0);
progressBar->setMaximum(0);
cancelButton = new QPushButton(tr("Abort"));
button->addStretch();
button->addWidget(cancelButton);
button->addStretch();
mainLayout->addWidget(statusLabel);
mainLayout->addWidget(progressBar);
mainLayout->addLayout(button);
uploader = new TPUpload(this);
connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
connect(uploader, SIGNAL(completed(QString)),this, SLOT(completed(QString)));
uploading = true;
int size = uploader->upload(context, ride);
statusLabel->setText(QString(tr("Uploading (%1 bytes)...")).arg(size));
setMinimumWidth(250);
}
void
TPUploadDialog::cancelClicked()
{
delete uploader;
reject();
}
void
TPUploadDialog::completed(QString results)
{
uploading = false;
progressBar->setMaximum(1);
progressBar->setValue(1);
statusLabel->setText(results);
cancelButton->setText(tr("Close"));
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _GC_TPUploadDialog_h
#define _GC_TPUploadDialog_h 1
#include "GoldenCheetah.h"
#include <QtGui>
#include <QDialog>
#include <QLabel>
#include <QProgressBar>
#include "RideFile.h"
#include "TPUpload.h"
class Context;
class TPUploadDialog : public QDialog
{
Q_OBJECT
G_OBJECT
public:
TPUploadDialog(QString cyclist, const RideFile *ride, Context *context);
public slots:
void cancelClicked();
void completed(QString);
private:
Context *context;
QString cyclist;
const RideFile *ride;
QLabel *statusLabel;
QProgressBar *progressBar;
QPushButton *cancelButton;
bool uploading;
TPUpload *uploader;
};
#endif // _GC_TPUploadDialog_h