ShareDialog: Add upload to CyclingAnalytics and SelfLoops

This commit is contained in:
Damien
2013-11-02 23:13:48 +01:00
parent 7ef01eacd8
commit 3b62333880
8 changed files with 646 additions and 67 deletions

View File

@@ -896,7 +896,7 @@ MainWindow::MainWindow(const QDir &home) :
connect(tweetAction, SIGNAL(triggered(bool)), this, SLOT(tweetRide()));
rideMenu->addAction(tweetAction);
shareAction = new QAction(tr("Share (Strava, RideWithGPS)..."), this);
shareAction = new QAction(tr("Share..."), this);
connect(shareAction, SIGNAL(triggered(bool)), this, SLOT(share()));
rideMenu->addAction(shareAction);
#endif

View File

@@ -17,8 +17,6 @@
*/
#include "OAuthDialog.h"
//#include "Athlete.h"
//#include "Context.h"
#include "Settings.h"
#include <QHttp>
#include <QUrl>
@@ -35,14 +33,27 @@ OAuthDialog::OAuthDialog(MainWindow *mainWindow, OAuthSite site) :
layout->setContentsMargins(2,0,2,2);
setLayout(layout);
QString urlstr = QString("https://www.strava.com/oauth/authorize?");
urlstr.append("client_id=83&");
urlstr.append("scope=view_private,write&");
QString urlstr = "";
if (site == STRAVA) {
urlstr = QString("https://www.strava.com/oauth/authorize?");
urlstr.append("client_id=").append(GC_STRAVA_CLIENT_ID).append("&");
urlstr.append("scope=view_private,write&");
}
else if (site == TWITTER) {
urlstr = QString("http://api.twitter.com/oauth/request_token?");
// TODO
}
else if (site == CYCLING_ANALYTICS) {
urlstr = QString("https://www.cyclinganalytics.com/api/auth?");
urlstr.append("client_id=").append(GC_CYCLINGANALYTICS_CLIENT_ID).append("&");
urlstr.append("scope=modify_rides&");
}
urlstr.append("redirect_uri=http://www.goldencheetah.org/&");
urlstr.append("response_type=code&");
urlstr.append("approval_prompt=force");
url = QUrl(urlstr);
url = QUrl(urlstr);
view = new QWebView();
view->setContentsMargins(0,0,0,0);
view->page()->view()->setContentsMargins(0,0,0,0);
@@ -62,25 +73,39 @@ OAuthDialog::OAuthDialog(MainWindow *mainWindow, OAuthSite site) :
void
OAuthDialog::urlChanged(const QUrl &url)
{
//qDebug() << url.toString();
if (url.toString().startsWith("http://www.goldencheetah.org/?state=&code=")) {
QString code = url.toString().right(40);
//qDebug() << "code" << code;
const char *request_token_uri = "https://www.strava.com/oauth/token?";
if (url.toString().startsWith("http://www.goldencheetah.org/?state=&code=") ||
url.toString().startsWith("http://www.goldencheetah.org/?code=")) {
QString code = url.toString().right(url.toString().length()-url.toString().indexOf("code=")-5);
QByteArray data;
QUrl params;
QString urlstr = "";
params.addQueryItem("code", code);
params.addQueryItem("client_id", GC_STRAVA_CLIENT_ID);
if (site == STRAVA) {
urlstr = QString("https://www.strava.com/oauth/token?");
params.addQueryItem("client_id", GC_STRAVA_CLIENT_ID);
#ifdef GC_STRAVA_CLIENT_SECRET
params.addQueryItem("client_secret", GC_STRAVA_CLIENT_SECRET);
params.addQueryItem("client_secret", GC_STRAVA_CLIENT_SECRET);
#endif
params.addQueryItem("redirect_uri", "http://www.goldencheetah.org/");
params.addQueryItem("redirect_uri", "http://www.goldencheetah.org/");
}
else if (site == TWITTER) {
urlstr = QString("http://api.twitter.com/oauth/token?");
// TODO
}
else if (site == CYCLING_ANALYTICS) {
urlstr = QString("https://www.cyclinganalytics.com/api/token?");
params.addQueryItem("client_id", GC_CYCLINGANALYTICS_CLIENT_ID);
#ifdef GC_CYCLINGANALYTICS_CLIENT_SECRET
params.addQueryItem("client_secret", GC_CYCLINGANALYTICS_CLIENT_SECRET);
#endif
params.addQueryItem("grant_type", "authorization_code");
}
params.addQueryItem("code", code);
data = params.encodedQuery();
QUrl url = QUrl( request_token_uri);
QUrl url = QUrl( urlstr);
QNetworkRequest request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
@@ -93,14 +118,23 @@ void
OAuthDialog::loadFinished()
{
if (requestToken) {
int i = view->page()->mainFrame()->toHtml().indexOf("{\"access_token\":\"")+17;
int j = view->page()->mainFrame()->toHtml().indexOf("\"", i);
if (i>16 && j>-1) {
QString access_token = view->page()->mainFrame()->toHtml().mid(i,j-i);
//qDebug() << "token" << access_token;
appsettings->setCValue(mainWindow->cyclist, GC_STRAVA_TOKEN, access_token);
accept();
int at = view->page()->mainFrame()->toHtml().indexOf("\"access_token\":");
if (at>-1) {
int i = view->page()->mainFrame()->toHtml().indexOf("\"", at+15);
int j = view->page()->mainFrame()->toHtml().indexOf("\"", i+1);
if (i>-1 && j>-1) {
QString access_token = view->page()->mainFrame()->toHtml().mid(i+1,j-i-1);
if (site == STRAVA) {
appsettings->setCValue(mainWindow->cyclist, GC_STRAVA_TOKEN, access_token);
}
else if (site == TWITTER) {
// TODO
}
else if (site == CYCLING_ANALYTICS) {
appsettings->setCValue(mainWindow->cyclist, GC_CYCLINGANALYTICS_TOKEN, access_token);
}
accept();
}
}
}
}

View File

@@ -34,7 +34,8 @@ class OAuthDialog : public QDialog
public:
typedef enum {
STRAVA,
TWITTER
TWITTER,
CYCLING_ANALYTICS
} OAuthSite;
OAuthDialog(MainWindow *mainWindow, OAuthSite site);

View File

@@ -302,7 +302,10 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
//XXX deprecated QLabel *struserLabel = new QLabel(tr("Username"));
//XXX deprecated QLabel *strpassLabel = new QLabel(tr("Password"));
QLabel *strauthLabel = new QLabel(tr("Authorise"));
QLabel *strpinLabel = new QLabel(tr("PIN"));
QLabel *can = new QLabel(tr("Cycling Analytics"));
can->setFont(current);
QLabel *canauthLabel = new QLabel(tr("Authorise"));
QLabel *rwgps = new QLabel(tr("RideWithGPS"));
rwgps->setFont(current);
@@ -316,6 +319,12 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
QLabel *ttbuserLabel = new QLabel(tr("Username"));
QLabel *ttbpassLabel = new QLabel(tr("Password"));
QLabel *sel = new QLabel(tr("Selfloops"));
sel->setFont(current);
QLabel *seluserLabel = new QLabel(tr("Username"));
QLabel *selpassLabel = new QLabel(tr("Password"));
QLabel *wip = new QLabel(tr("Withings Wifi Scales"));
wip->setFont(current);
@@ -392,6 +401,18 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
stravaAuthorised->setFixedHeight(16);
stravaAuthorised->setFixedWidth(16);
cyclingAnalyticsAuthorise = new QPushButton("Authorise", this);
#ifndef GC_CYCLINGANALYTICS_CLIENT_SECRET
cyclingAnalyticsAuthorise->setEnabled(false);
#endif
cyclingAnalyticsAuthorised = new QPushButton(this);
cyclingAnalyticsAuthorised->setContentsMargins(0,0,0,0);
cyclingAnalyticsAuthorised->setIcon(passwords.scaled(16,16));
cyclingAnalyticsAuthorised->setIconSize(QSize(16,16));
cyclingAnalyticsAuthorised->setFixedHeight(16);
cyclingAnalyticsAuthorised->setFixedWidth(16);
rideWithGPSUser = new QLineEdit(this);
rideWithGPSUser->setText(appsettings->cvalue(mainWindow->cyclist, GC_RWGPSUSER, "").toString());
@@ -406,6 +427,13 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
ttbPass->setEchoMode(QLineEdit::Password);
ttbPass->setText(appsettings->cvalue(mainWindow->cyclist, GC_TTBPASS, "").toString());
selUser = new QLineEdit(this);
selUser->setText(appsettings->cvalue(mainWindow->cyclist, GC_SELUSER, "").toString());
selPass = new QLineEdit(this);
selPass->setEchoMode(QLineEdit::Password);
selPass->setText(appsettings->cvalue(mainWindow->cyclist, GC_SELPASS, "").toString());
wiURL = new QLineEdit(this);
wiURL->setText(appsettings->cvalue(mainWindow->cyclist, GC_WIURL, "http://wbsapi.withings.net/").toString());
@@ -455,27 +483,32 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
grid->addWidget(twpinLabel, 12,0);
grid->addWidget(str, 13,0);
grid->addWidget(strauthLabel, 14,0);
grid->addWidget(strpinLabel, 15,0);
grid->addWidget(rwgps, 17,0);
grid->addWidget(rwgpsuserLabel, 18,0);
grid->addWidget(rwgpspassLabel, 19,0);
grid->addWidget(wip, 20,0);
grid->addWidget(wiurlLabel, 21,0);
grid->addWidget(wiuserLabel, 22,0);
grid->addWidget(wipassLabel, 23,0);
grid->addWidget(zeo, 24,0);
grid->addWidget(zeourlLabel, 25,0);
grid->addWidget(zeouserLabel, 26,0);
grid->addWidget(zeopassLabel, 27,0);
grid->addWidget(webcal, 28, 0);
grid->addWidget(wcurlLabel, 29, 0);
grid->addWidget(dv, 30,0);
grid->addWidget(dvurlLabel, 31,0);
grid->addWidget(dvuserLabel, 32,0);
grid->addWidget(dvpassLabel, 33,0);
grid->addWidget(ttb, 34,0);
grid->addWidget(ttbuserLabel, 35,0);
grid->addWidget(ttbpassLabel, 36,0);
grid->addWidget(can, 15,0);
grid->addWidget(canauthLabel, 16,0);
grid->addWidget(rwgps, 18,0);
grid->addWidget(rwgpsuserLabel, 19,0);
grid->addWidget(rwgpspassLabel, 20,0);
grid->addWidget(wip, 21,0);
grid->addWidget(wiurlLabel, 22,0);
grid->addWidget(wiuserLabel, 23,0);
grid->addWidget(wipassLabel, 24,0);
grid->addWidget(zeo, 25,0);
grid->addWidget(zeourlLabel, 26,0);
grid->addWidget(zeouserLabel, 27,0);
grid->addWidget(zeopassLabel, 28,0);
grid->addWidget(webcal, 29, 0);
grid->addWidget(wcurlLabel, 30, 0);
grid->addWidget(dv, 31,0);
grid->addWidget(dvurlLabel, 32,0);
grid->addWidget(dvuserLabel, 33,0);
grid->addWidget(dvpassLabel, 34,0);
grid->addWidget(ttb, 35,0);
grid->addWidget(ttbuserLabel, 36,0);
grid->addWidget(ttbpassLabel, 37,0);
grid->addWidget(sel, 38,0);
grid->addWidget(seluserLabel, 39,0);
grid->addWidget(selpassLabel, 40,0);
grid->addWidget(tpURL, 1, 1, 0);
grid->addWidget(tpUser, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
@@ -493,26 +526,37 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
grid->addWidget(stravaAuthorise, 14, 1, Qt::AlignLeft | Qt::AlignVCenter);
if (appsettings->cvalue(mainWindow->cyclist, GC_STRAVA_TOKEN, "")!="")
grid->addWidget(stravaAuthorised, 14, 1, Qt::AlignLeft | Qt::AlignVCenter);
else
stravaAuthorised->hide(); // if no token no show
grid->addWidget(rideWithGPSUser, 18, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(rideWithGPSPass, 19, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(cyclingAnalyticsAuthorise, 16, 1, Qt::AlignLeft | Qt::AlignVCenter);
if (appsettings->cvalue(mainWindow->cyclist, GC_CYCLINGANALYTICS_TOKEN, "")!="")
grid->addWidget(cyclingAnalyticsAuthorised, 16, 1, Qt::AlignLeft | Qt::AlignVCenter);
else
cyclingAnalyticsAuthorised->hide();
grid->addWidget(wiURL, 21, 1, 0);
grid->addWidget(wiUser, 22, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(wiPass, 23, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(rideWithGPSUser, 19, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(rideWithGPSPass, 20, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(zeoURL, 25, 1, 0);
grid->addWidget(zeoUser, 26, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(zeoPass, 27, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(wiURL, 22, 1, 0);
grid->addWidget(wiUser, 23, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(wiPass, 24, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(webcalURL, 29, 1, 0);
grid->addWidget(zeoURL, 26, 1, 0);
grid->addWidget(zeoUser, 27, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(zeoPass, 28, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(dvURL, 31, 1, 0);
grid->addWidget(dvUser, 32, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(dvPass, 33, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(webcalURL, 30, 1, 0);
grid->addWidget(ttbUser, 35, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(ttbPass, 36, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(dvURL, 32, 1, 0);
grid->addWidget(dvUser, 33, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(dvPass, 34, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(ttbUser, 36, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(ttbPass, 37, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(selUser, 39, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(selPass, 40, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->setColumnStretch(0,0);
grid->setColumnStretch(1,3);
@@ -525,6 +569,7 @@ CredentialsPage::CredentialsPage(QWidget *parent, MainWindow *mainWindow) : QScr
connect(twitterAuthorise, SIGNAL(clicked()), this, SLOT(authoriseTwitter()));
connect(stravaAuthorise, SIGNAL(clicked()), this, SLOT(authoriseStrava()));
connect(cyclingAnalyticsAuthorise, SIGNAL(clicked()), this, SLOT(authoriseCyclingAnalytics()));
}
void CredentialsPage::authoriseTwitter()
@@ -600,6 +645,16 @@ void CredentialsPage::authoriseStrava()
#endif
}
void CredentialsPage::authoriseCyclingAnalytics()
{
#ifdef GC_HAVE_LIBOAUTH
OAuthDialog *oauthDialog = new OAuthDialog(mainWindow, OAuthDialog::CYCLING_ANALYTICS);
oauthDialog->setWindowModality(Qt::ApplicationModal);
oauthDialog->exec();
#endif
}
void
CredentialsPage::saveClicked()
{
@@ -615,6 +670,8 @@ CredentialsPage::saveClicked()
appsettings->setCValue(mainWindow->cyclist, GC_RWGPSPASS, rideWithGPSPass->text());
appsettings->setCValue(mainWindow->cyclist, GC_TTBUSER, ttbUser->text());
appsettings->setCValue(mainWindow->cyclist, GC_TTBPASS, ttbPass->text());
appsettings->setCValue(mainWindow->cyclist, GC_SELUSER, selUser->text());
appsettings->setCValue(mainWindow->cyclist, GC_SELPASS, selPass->text());
appsettings->setCValue(mainWindow->cyclist, GC_TPTYPE, tpType->currentIndex());
appsettings->setCValue(mainWindow->cyclist, GC_TWURL, twitterURL->text());
appsettings->setCValue(mainWindow->cyclist, GC_WIURL, wiURL->text());

View File

@@ -131,6 +131,7 @@ class CredentialsPage : public QScrollArea
public slots:
void authoriseTwitter();
void authoriseStrava();
void authoriseCyclingAnalytics();
private:
MainWindow *mainWindow;
@@ -158,12 +159,17 @@ class CredentialsPage : public QScrollArea
QLineEdit *stravaPIN;
char *s_id, *s_secret;
QPushButton *cyclingAnalyticsAuthorise, *cyclingAnalyticsAuthorised;
QLineEdit *rideWithGPSUser;
QLineEdit *rideWithGPSPass;
QLineEdit *ttbUser;
QLineEdit *ttbPass;
QLineEdit *selUser;
QLineEdit *selPass;
QLineEdit *wiURL; // url for withings
QLineEdit *wiUser;
QLineEdit *wiPass;

View File

@@ -104,6 +104,8 @@
#define GC_RWGPSPASS "rwgps/pass"
#define GC_TTBUSER "ttb/user"
#define GC_TTBPASS "ttb/pass"
#define GC_SELUSER "sel/user"
#define GC_SELPASS "sel/pass"
#define GC_WIURL "wi/url"
#define GC_WIUSER "wi/user"
#define GC_WIKEY "wi/key"
@@ -154,6 +156,10 @@
#define GC_STRAVA_CLIENT_ID "83" //< client id
#define GC_STRAVA_TOKEN "strava_token"
//Cycling Analytics
#define GC_CYCLINGANALYTICS_CLIENT_ID "1504958" // app id
#define GC_CYCLINGANALYTICS_TOKEN "cyclinganalytics_token"
// Tcx Smart recording
#define GC_GARMIN_SMARTRECORD "garminSmartRecord"
#define GC_GARMIN_HWMARK "garminHWMark"

View File

@@ -31,6 +31,43 @@
#include "DBAccess.h"
#include "TcxRideFile.h"
#include <zlib.h>
// 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);
}
ShareDialog::ShareDialog(MainWindow *mainWindow, RideItem *item) :
mainWindow(mainWindow)
{
@@ -45,6 +82,8 @@ ShareDialog::ShareDialog(MainWindow *mainWindow, RideItem *item) :
// uploaders
stravaUploader = new StravaUploader(mainWindow, ride, this);
rideWithGpsUploader = new RideWithGpsUploader(mainWindow, ride, this);
cyclingAnalyticsUploader = new CyclingAnalyticsUploader(mainWindow, ride, this);
selfLoopsUploader = new SelfLoopsUploader(mainWindow, ride, this);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QGroupBox *groupBox1 = new QGroupBox(tr("Choose which sites you wish to share on: "));
@@ -54,10 +93,17 @@ ShareDialog::ShareDialog(MainWindow *mainWindow, RideItem *item) :
stravaChk->setEnabled(false);
#endif
rideWithGPSChk = new QCheckBox(tr("Ride With GPS"));
cyclingAnalyticsChk = new QCheckBox(tr("Cycling Analytics"));
#ifndef GC_CYCLINGANALYTICS_CLIENT_SECRET
cyclingAnalyticsChk->setEnabled(false);
#endif
selfLoopsChk = new QCheckBox(tr("Selfloops"));
QGridLayout *vbox1 = new QGridLayout();
vbox1->addWidget(stravaChk,0,0);
vbox1->addWidget(rideWithGPSChk,0,2);
vbox1->addWidget(rideWithGPSChk,0,1);
vbox1->addWidget(cyclingAnalyticsChk,0,2);
vbox1->addWidget(selfLoopsChk,0,3);
groupBox1->setLayout(vbox1);
mainLayout->addWidget(groupBox1);
@@ -131,7 +177,8 @@ ShareDialog::upload()
{
show();
if (!stravaChk->isChecked() && !rideWithGPSChk->isChecked()) {
if (!stravaChk->isChecked() && !rideWithGPSChk->isChecked() &&
!cyclingAnalyticsChk->isChecked() && !selfLoopsChk->isChecked()) {
QMessageBox aMsgBox;
aMsgBox.setText(tr("No share site selected !"));
aMsgBox.exec();
@@ -146,18 +193,28 @@ ShareDialog::upload()
if (stravaChk->isChecked()) {
shareSiteCount ++;
}
if (rideWithGPSChk->isChecked()) {
shareSiteCount ++;
}
if (cyclingAnalyticsChk->isChecked()) {
shareSiteCount ++;
}
if (selfLoopsChk->isChecked()) {
shareSiteCount ++;
}
if (stravaChk->isChecked()) {
stravaUploader->uploadStrava();
}
if (rideWithGPSChk->isChecked()) {
rideWithGpsUploader->uploadRideWithGPS();
}
if (cyclingAnalyticsChk->isChecked()) {
cyclingAnalyticsUploader->upload();
}
if (selfLoopsChk->isChecked()) {
selfLoopsUploader->upload();
}
}
StravaUploader::StravaUploader(MainWindow *mainWindow, RideItem *ride, ShareDialog *parent) :
@@ -633,4 +690,347 @@ RideWithGpsUploader::closeClicked()
}
CyclingAnalyticsUploader::CyclingAnalyticsUploader(MainWindow *mainWindow, RideItem *ride, ShareDialog *parent) :
mainWindow(mainWindow), ride(ride), parent(parent)
{
cyclingAnalyticsUploadId = ride->ride()->getTag("CyclingAnalytics uploadId", "");
}
void
CyclingAnalyticsUploader::upload()
{
// OAuth no more login
token = appsettings->cvalue(mainWindow->cyclist, GC_CYCLINGANALYTICS_TOKEN, "").toString();
if (token=="")
{
QMessageBox aMsgBox;
aMsgBox.setText(tr("Cannot login to CyclingAnalytics. Check permission"));
aMsgBox.exec();
return;
}
// already shared ?
if(cyclingAnalyticsUploadId.length()>0)
{
overwrite = false;
dialog = new QDialog();
QVBoxLayout *layout = new QVBoxLayout;
QVBoxLayout *layoutLabel = new QVBoxLayout();
QLabel *label = new QLabel();
label->setText(tr("This Ride is marked as already on CyclingAnalytics. Are you sure you want to upload it?"));
layoutLabel->addWidget(label);
QPushButton *ok = new QPushButton(tr("OK"), dialog);
QPushButton *cancel = new QPushButton(tr("Cancel"), dialog);
QHBoxLayout *buttons = new QHBoxLayout();
buttons->addStretch();
buttons->addWidget(cancel);
buttons->addWidget(ok);
connect(ok, SIGNAL(clicked()), this, SLOT(okClicked()));
connect(cancel, SIGNAL(clicked()), this, SLOT(closeClicked()));
layout->addLayout(layoutLabel);
layout->addLayout(buttons);
dialog->setLayout(layout);
if (!dialog->exec()) return;
}
requestUploadCyclingAnalytics();
if(!uploadSuccessful)
{
parent->progressLabel->setText("Error uploading to CyclingAnalytics");
}
else
{
parent->progressLabel->setText(tr("Successfully uploaded to CyclingAnalytics"));
}
}
void
CyclingAnalyticsUploader::requestUploadCyclingAnalytics()
{
parent->progressLabel->setText(tr("Upload ride to CyclingAnalytics..."));
parent->progressBar->setValue(parent->progressBar->value()+10/parent->shareSiteCount);
QEventLoop eventLoop;
QNetworkAccessManager networkMgr;
connect(&networkMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestUploadCyclingAnalyticsFinished(QNetworkReply*)));
connect(&networkMgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
QUrl url = QUrl( "https://www.cyclinganalytics.com/api/me/upload" );
QNetworkRequest request = QNetworkRequest(url);
QString boundary = QVariant(qrand()).toString()+QVariant(qrand()).toString()+QVariant(qrand()).toString();
TcxFileReader reader;
QByteArray file = reader.toByteArray(mainWindow, ride->ride(), parent->altitudeChk->isChecked(), parent->powerChk->isChecked(), parent->heartrateChk->isChecked(), parent->cadenceChk->isChecked());
// MULTIPART *****************
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
multiPart->setBoundary(boundary.toAscii());
request.setRawHeader("Authorization", (QString("Bearer %1").arg(token)).toAscii());
QHttpPart activityNamePart;
activityNamePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\""));
activityNamePart.setBody(QString(parent->titleEdit->text()).toAscii());
QHttpPart dataTypePart;
dataTypePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"format\""));
dataTypePart.setBody("tcx");
QHttpPart filenamePart;
filenamePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"filename\""));
filenamePart.setBody("file.tcx");
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"data\"; filename=\"file.tcx\"; type=\"text/xml\""));
filePart.setBody(file);
multiPart->append(activityNamePart);
multiPart->append(filenamePart);
multiPart->append(dataTypePart);
multiPart->append(filePart);
QScopedPointer<QNetworkReply> reply( networkMgr.post(request, multiPart) );
multiPart->setParent(reply.data());
parent->progressBar->setValue(parent->progressBar->value()+30/parent->shareSiteCount);
parent->progressLabel->setText(tr("Upload ride... Sending to CyclingAnalytics"));
eventLoop.exec();
}
void
CyclingAnalyticsUploader::requestUploadCyclingAnalyticsFinished(QNetworkReply *reply)
{
parent->progressBar->setValue(parent->progressBar->value()+50/parent->shareSiteCount);
parent->progressLabel->setText(tr("Upload to CyclingAnalytics finished."));
uploadSuccessful = false;
QString response = reply->readAll();
//qDebug() << "response" << response;
QScriptValue sc;
QScriptEngine se;
sc = se.evaluate("("+response+")");
QString uploadError = sc.property("error").toString();
if (uploadError.toLower() == "none" || uploadError.toLower() == "null")
uploadError = "";
if (uploadError.length()>0 || reply->error() != QNetworkReply::NoError)
{
//qDebug() << "Error " << reply->error() ;
//qDebug() << "Error " << uploadError;
parent->errorLabel->setText(parent->errorLabel->text()+ tr(" Error from CyclingAnalytics: ") + uploadError + "\n" );
}
else
{
cyclingAnalyticsUploadId = sc.property("upload_id").toString();
ride->ride()->setTag("CyclingAnalytics uploadId", cyclingAnalyticsUploadId);
ride->setDirty(true);
//qDebug() << "uploadId: " << cyclingAnalyticsUploadId;
parent->progressBar->setValue(parent->progressBar->value()+10/parent->shareSiteCount);
uploadSuccessful = true;
}
}
void
CyclingAnalyticsUploader::okClicked()
{
dialog->accept();
return;
}
void
CyclingAnalyticsUploader::closeClicked()
{
dialog->reject();
return;
}
SelfLoopsUploader::SelfLoopsUploader(MainWindow *mainWindow, RideItem *ride, ShareDialog *parent) :
mainWindow(mainWindow), ride(ride), parent(parent)
{
selfloopsUploadId = ride->ride()->getTag("Selfloops uploadId", "");
selfloopsActivityId = ride->ride()->getTag("Selfloops activityId", "");
}
void
SelfLoopsUploader::upload()
{
// allready shared ?
if(selfloopsActivityId.length()>0)
{
overwrite = false;
dialog = new QDialog();
QVBoxLayout *layout = new QVBoxLayout;
QVBoxLayout *layoutLabel = new QVBoxLayout();
QLabel *label = new QLabel();
label->setText(tr("This Ride is marked as already on Selfloops. Are you sure you want to upload it?"));
layoutLabel->addWidget(label);
QPushButton *ok = new QPushButton(tr("OK"), dialog);
QPushButton *cancel = new QPushButton(tr("Cancel"), dialog);
QHBoxLayout *buttons = new QHBoxLayout();
buttons->addStretch();
buttons->addWidget(cancel);
buttons->addWidget(ok);
connect(ok, SIGNAL(clicked()), this, SLOT(okClicked()));
connect(cancel, SIGNAL(clicked()), this, SLOT(closeClicked()));
layout->addLayout(layoutLabel);
layout->addLayout(buttons);
dialog->setLayout(layout);
if (!dialog->exec()) return;
}
requestUploadSelfLoops();
if(!uploadSuccessful)
{
parent->progressLabel->setText("Error uploading to Selfloops");
}
else
{
parent->progressLabel->setText(tr("Successfully uploaded to Selfloops"));
}
}
/*
make a multipart HTTP POST at the following path "/restapi/public/activities/upload.json"
on selflloops web site using SSL.
The requested parameters are:
- "email" the email of a valid SelfLoops account
- "pw" the password
- "tcxfile" the zipped TCX file (example: test.tcx.gz).
On success, response message contains a JSON encoded data
with the new "activity_id" created.
On error, SelfLoops response contains a JSON encoded data with "error_code" and "message" key.
*/
void
SelfLoopsUploader::requestUploadSelfLoops()
{
parent->progressLabel->setText(tr("Upload ride to Selfloops..."));
parent->progressBar->setValue(parent->progressBar->value()+10/parent->shareSiteCount);
QEventLoop eventLoop;
QNetworkAccessManager networkMgr;
connect(&networkMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestUploadSelfLoopsFinished(QNetworkReply*)));
connect(&networkMgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
QUrl url = QUrl( "https://www.selfloops.com/restapi/public/activities/upload.json" );
QNetworkRequest request = QNetworkRequest(url);
QString boundary = QVariant(qrand()).toString()+QVariant(qrand()).toString()+QVariant(qrand()).toString();
// The TCX file have to be gzipped
TcxFileReader reader;
QByteArray file = zCompress(reader.toByteArray(mainWindow, ride->ride(), parent->altitudeChk->isChecked(), parent->powerChk->isChecked(), parent->heartrateChk->isChecked(), parent->cadenceChk->isChecked()));
QString username = appsettings->cvalue(mainWindow->cyclist, GC_SELUSER).toString();
QString password = appsettings->cvalue(mainWindow->cyclist, GC_SELPASS).toString();
// MULTIPART *****************
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::MixedType);
multiPart->setBoundary(boundary.toAscii());
QHttpPart emailPart;
emailPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"email\""));
emailPart.setBody(username.toAscii());
QHttpPart passwordPart;
passwordPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"pw\""));
passwordPart.setBody(password.toAscii());
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"tcxfile\"; filename=\"myfile.tcx.gz\"; type=\"application/x-gzip\""));
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-gzip");
filePart.setBody(file);
multiPart->append(emailPart);
multiPart->append(passwordPart);
multiPart->append(filePart);
QScopedPointer<QNetworkReply> reply( networkMgr.post(request, multiPart) );
multiPart->setParent(reply.data());
parent->progressBar->setValue(parent->progressBar->value()+30/parent->shareSiteCount);
parent->progressLabel->setText(tr("Upload ride... Sending to Selfloops"));
eventLoop.exec();
}
void
SelfLoopsUploader::requestUploadSelfLoopsFinished(QNetworkReply *reply)
{
parent->progressBar->setValue(parent->progressBar->value()+50/parent->shareSiteCount);
parent->progressLabel->setText(tr("Upload to Selfloops finished."));
uploadSuccessful = false;
QString response = reply->readAll();
qDebug() << "response" << response;
QScriptValue sc;
QScriptEngine se;
sc = se.evaluate("("+response+")");
QString error = sc.property("error_code").toString();
QString uploadError = sc.property("message").toString();
if (error.length()>0 || reply->error() != QNetworkReply::NoError)
{
qDebug() << "Error " << reply->error() ;
qDebug() << "Error " << uploadError;
parent->errorLabel->setText(parent->errorLabel->text()+ tr(" Error from Selfloops: ") + uploadError + "\n" );
}
else
{
selfloopsActivityId = sc.property("activity_id").toString();
ride->ride()->setTag("Selfloops activityId", selfloopsActivityId);
ride->setDirty(true);
qDebug() << "uploadId: " << selfloopsActivityId;
parent->progressBar->setValue(parent->progressBar->value()+10/parent->shareSiteCount);
uploadSuccessful = true;
}
}
void
SelfLoopsUploader::okClicked()
{
dialog->accept();
return;
}
void
SelfLoopsUploader::closeClicked()
{
dialog->reject();
return;
}

View File

@@ -107,6 +107,77 @@ private:
bool overwrite;
};
// uploader to cyclinganalytics.com
class CyclingAnalyticsUploader : public QObject
{
Q_OBJECT
G_OBJECT
public:
CyclingAnalyticsUploader(MainWindow *mainWindow, RideItem *item, ShareDialog *parent = 0);
void upload();
private slots:
void requestUploadCyclingAnalytics();
void requestUploadCyclingAnalyticsFinished(QNetworkReply *reply);
void okClicked();
void closeClicked();
private:
MainWindow *mainWindow;
ShareDialog *parent;
RideItem *ride;
QDialog *dialog;
QString token;
bool loggedIn, uploadSuccessful;
bool overwrite;
QString cyclingAnalyticsUploadId;
QString uploadStatus;
QString uploadProgress;
};
// uploader to selfloops.com
class SelfLoopsUploader : public QObject
{
Q_OBJECT
G_OBJECT
public:
SelfLoopsUploader(MainWindow *mainWindow, RideItem *item, ShareDialog *parent = 0);
void upload();
private slots:
void requestUploadSelfLoops();
void requestUploadSelfLoopsFinished(QNetworkReply *reply);
void okClicked();
void closeClicked();
private:
MainWindow *mainWindow;
ShareDialog *parent;
RideItem *ride;
QDialog *dialog;
QString token;
bool loggedIn, uploadSuccessful;
bool overwrite;
QString selfloopsUploadId, selfloopsActivityId;
QString uploadStatus;
QString uploadProgress;
};
class ShareDialog : public QDialog
{
Q_OBJECT
@@ -141,11 +212,15 @@ private:
QCheckBox *stravaChk;
QCheckBox *rideWithGPSChk;
QCheckBox *cyclingAnalyticsChk;
QCheckBox *selfLoopsChk;
RideItem *ride;
StravaUploader *stravaUploader;
RideWithGpsUploader *rideWithGpsUploader;
CyclingAnalyticsUploader *cyclingAnalyticsUploader;
SelfLoopsUploader *selfLoopsUploader;
QString athleteId;
};