Skip to content

Commit

Permalink
CloudDB - Version & Telemetry
Browse files Browse the repository at this point in the history
... improve Telemetry Dialog
... add more data to Telemetry Storage / including user counter
... do not show any network errors / lack of connection
... call all CloudDB functions asynchronously without blocking UI
... use an installer specific UUID as identifier - not IP address (as this is to volatile)
  • Loading branch information
Joern-R committed Jan 21, 2017
1 parent 37ec727 commit 6330703
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 84 deletions.
92 changes: 47 additions & 45 deletions src/Cloud/CloudDBTelemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "CloudDBTelemetry.h"
#include "CloudDBCommon.h"
#include "GcUpgrade.h"

#include <QJsonParseError>
#include <QJsonObject>
Expand All @@ -40,24 +41,39 @@ CloudDBTelemetryClient::~CloudDBTelemetryClient() {
}

void
CloudDBTelemetryClient::storeTelemetry() {
CloudDBTelemetryClient::upsertTelemetry() {

QScopedPointer<QNetworkAccessManager> l_nam(new QNetworkAccessManager());
QNetworkReply *l_reply;
QNetworkAccessManager *l_nam = new QNetworkAccessManager(this);

QString l_telemetry_url_base = CloudDBCommon::cloudDBBaseURL + QString("telemetry");

QNetworkRequest request;
CloudDBCommon::prepareRequest(request, l_telemetry_url_base);
// empty createDate is filled with time.now() in CloudDB
l_reply = l_nam->post(request, "{ \"createDate\": \"\" }");

// blocking request (need to wait otherwise NetworkAccessManager is destroyed for call finished)
QEventLoop loop;
connect(l_reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QString os;
#ifdef Q_OS_LINUX
os = "Linux";
#endif

#ifdef WIN32
os = "Windows";
#endif

#ifdef Q_OS_MAC
os = "macOS";
#endif


// make sure we have a unique ID - if not created yet, create one and store for the future
QString id = appsettings->value(NULL, GC_TELEMETRY_ID, QUuid::createUuid().toString()).toString();
appsettings->setValue(GC_TELEMETRY_ID, id);

// empty lastChange is filled with time.now() in CloudDB
QString payload = QString("{ \"key\" : \"%1\", \"lastChange\": \"\", \"operatingSystem\" : \"%2\", \"version\" : \"%3\"}").arg(id).arg( os ).arg( VERSION_LATEST );
l_nam->put(request, payload.toUtf8());

// the request is asynchronously - the user does not care about any response - so we just skip it

// ignore any errors or reply - user does not need to be informed in case of problems
}


Expand All @@ -69,54 +85,40 @@ CloudDBAcceptTelemetryDialog::CloudDBAcceptTelemetryDialog()

QVBoxLayout *layout = new QVBoxLayout(this);

QPushButton *important = new QPushButton(style()->standardIcon(QStyle::SP_MessageBoxInformation), "", this);
important->setFixedSize(80,80);
important->setFlat(true);
important->setIconSize(QSize(80,80));
important->setAutoFillBackground(false);
important->setFocusPolicy(Qt::NoFocus);

QLabel *header = new QLabel(this);
header->setWordWrap(true);
header->setTextFormat(Qt::RichText);
header->setText(QString(tr("<b><big>Please read carefully before accepting !</big></b>")));

QHBoxLayout *toprow = new QHBoxLayout;
toprow->addWidget(important);
toprow->addWidget(header);
layout->addLayout(toprow);

QLabel *text = new QLabel(this);
text->setWordWrap(true);
text->setTextFormat(Qt::RichText);
text->setText(tr("<center><b>GoldenCheetah User Location Storage</b><br>"
"GoldenCheetah would like to store your location, retrieved from your IP address "
"to get an overview, where GoldenCheetah users are located to create location aware "
"statics of GoldenCheetah users.<br>"
"The location will be derived and stored once from your current IP address.<br>"
"GoldenCheetah will NOT store your IP address, but only derived location information "
"which are Country, City, Region and City Lattitude/Longitude.<br>"
text->setText(tr("<center><b>GoldenCheetah User Location Data</b><br>"
"We want to start collecting data about where our active users are located and which operating "
"system and GoldenCheetah version they are using. The collection is done via our Cloud DB. "
"We would like to use your IP address to determine your location and count how often you "
"are using GoldenCheetah. Besides the data mentioned we do not store any personal "
"information in our Cloud DB. <br><br>"
"<b>Can we please record your location, OS and GoldenCheetah version ?"
));

layout->addWidget(text);

proceedButton = new QPushButton(tr("Yes, a agree that my current location is stored in GoldenCheetah's CloudDB"), this);
proceedButton->setEnabled(true);
connect(proceedButton, SIGNAL(clicked()), this, SLOT(acceptConditions()));
abortButton = new QPushButton(tr("No, I do not want my location to be stored in GoldenCheetah's CloudDB"), this);
abortButton->setDefault(true);
connect(abortButton, SIGNAL(clicked()), this, SLOT(rejectConditions()));
QHBoxLayout *lastRow = new QHBoxLayout;

noButton = new QPushButton(tr("No"), this);
connect(noButton, SIGNAL(clicked()), this, SLOT(rejectConditions()));
yesButton = new QPushButton(tr("Yes"), this);
yesButton->setDefault(true);
connect(yesButton, SIGNAL(clicked()), this, SLOT(acceptConditions()));

lastRow->addStretch();
lastRow->addWidget(noButton);
lastRow->addWidget(yesButton);

layout->addStretch();
layout->addWidget(abortButton);
layout->addWidget(proceedButton);
layout->addLayout(lastRow);

}

void CloudDBAcceptTelemetryDialog::acceptConditions() {

// document the decision
appsettings->setValue(GC_ALLOW_TELEMETRY, "userAccepted");
appsettings->setValue(GC_ALLOW_TELEMETRY, true);
appsettings->setValue(GC_ALLOW_TELEMETRY_DATE, QDateTime::currentDateTime().toString(Qt::ISODate));

accept();
Expand All @@ -125,7 +127,7 @@ void CloudDBAcceptTelemetryDialog::acceptConditions() {
void CloudDBAcceptTelemetryDialog::rejectConditions() {

// document the decision
appsettings->setValue(GC_ALLOW_TELEMETRY, "userRejected");
appsettings->setValue(GC_ALLOW_TELEMETRY, false);
appsettings->setValue(GC_ALLOW_TELEMETRY_DATE, QDateTime::currentDateTime().toString(Qt::ISODate));

reject();
Expand Down
7 changes: 3 additions & 4 deletions src/Cloud/CloudDBTelemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ class CloudDBTelemetryClient : public QObject
CloudDBTelemetryClient();
~CloudDBTelemetryClient();

static void storeTelemetry();
void upsertTelemetry();

private:


};
Expand All @@ -55,8 +54,8 @@ private slots:

private:

QPushButton *proceedButton;
QPushButton *abortButton;
QPushButton *yesButton;
QPushButton *noButton;

};

Expand Down
46 changes: 25 additions & 21 deletions src/Cloud/CloudDBVersion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,13 @@ int CloudDBVersionClient::CloudDBVersion_DevelopmentBuild = 30;


CloudDBVersionClient::CloudDBVersionClient() {
// only static members

}
CloudDBVersionClient::~CloudDBVersionClient() {
// only static members

}


QList<VersionAPIGetV1>
CloudDBVersionClient::getLatestVersions() {
void
CloudDBVersionClient::informUserAboutLatestVersions() {

// consider any updates done since the last start and consider only newer versions
int lastVersion = appsettings->value(NULL, GC_LAST_VERSION_CHECKED, 0).toInt();
Expand All @@ -56,32 +53,37 @@ CloudDBVersionClient::getLatestVersions() {
lastVersion = VERSION_LATEST;
}

QList<VersionAPIGetV1> retrieved;

QScopedPointer<QNetworkAccessManager> l_nam(new QNetworkAccessManager());
QNetworkReply *l_reply;
QNetworkAccessManager *l_nam = new QNetworkAccessManager(this);

QNetworkRequest request;
QUrlQuery query;
query.addQueryItem("version", QString::number(lastVersion));
CloudDBCommon::prepareRequest(request, CloudDBCommon::cloudDBBaseURL+"version", &query);
l_reply = l_nam->get(request);
l_nam->get(request);

connect(l_nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(showVersionPopup(QNetworkReply*)));

}

void
CloudDBVersionClient::showVersionPopup(QNetworkReply * l_reply) {

// blocking request
QEventLoop loop;
connect(l_reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QList<VersionAPIGetV1> retrieved;

if (l_reply->error() == QNetworkReply::NoError) {
QByteArray result = l_reply->readAll();
unmarshallAPIGetV1(result, &retrieved);
}; // silently ignore network errors, since the user can't do anything about it anyway
}

return retrieved;
if (retrieved.count() > 0) {
CloudDBUpdateAvailableDialog updateAvailableDialog(retrieved);
updateAvailableDialog.setModal(true);
// we are not interested in the result - update check status is updated as part of the dialog box
updateAvailableDialog.exec();
}
}



bool
CloudDBVersionClient::unmarshallAPIGetV1(QByteArray json, QList<VersionAPIGetV1> *versionList) {

Expand Down Expand Up @@ -127,6 +129,8 @@ CloudDBVersionClient::unmarshallAPIGetV1(QByteArray json, QList<VersionAPIGetV1>
}




CloudDBUpdateAvailableDialog::CloudDBUpdateAvailableDialog(QList<VersionAPIGetV1> versions) : versions(versions)
{

Expand Down Expand Up @@ -182,15 +186,15 @@ CloudDBUpdateAvailableDialog::CloudDBUpdateAvailableDialog(QList<VersionAPIGetV1
QHBoxLayout *lastRow = new QHBoxLayout;

doNotAskAgainButton = new QPushButton(tr("Do not ask again for these versions"), this);
doNotAskAgainButton->setEnabled(true);
connect(doNotAskAgainButton, SIGNAL(clicked()), this, SLOT(doNotAskAgain()));
askAgainNextStartButton = new QPushButton(tr("Show available versions again on next start"), this);
askAgainNextStartButton = new QPushButton(tr("Show new versions on next start"), this);
askAgainNextStartButton->setDefault(true);
connect(askAgainNextStartButton, SIGNAL(clicked()), this, SLOT(askAgainOnNextStart()));

lastRow->addWidget(askAgainNextStartButton);

lastRow->addStretch();
lastRow->addWidget(doNotAskAgainButton);
lastRow->addWidget(askAgainNextStartButton);
layout->addLayout(lastRow);

}
Expand Down
8 changes: 6 additions & 2 deletions src/Cloud/CloudDBVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,19 @@ class CloudDBVersionClient : public QObject
CloudDBVersionClient();
~CloudDBVersionClient();

static QList<VersionAPIGetV1> getLatestVersions();
void informUserAboutLatestVersions();

static int CloudDBVersion_Release;
static int CloudDBVersion_ReleaseCandidate;
static int CloudDBVersion_DevelopmentBuild;

private slots:

void showVersionPopup(QNetworkReply*);

private:

static bool unmarshallAPIGetV1(QByteArray , QList<VersionAPIGetV1> *versionList );
bool unmarshallAPIGetV1(QByteArray , QList<VersionAPIGetV1> *versionList );

};

Expand Down
5 changes: 3 additions & 2 deletions src/Core/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,9 @@
#define GC_SWIMPACE "<global-general>swimpace"
#define GC_ELEVATION_HYSTERESIS "<global-general>elevationHysteresis"
#define GC_UNIT "<global-general>unit"
#define GC_ALLOW_TELEMETRY "<global-general>telemetry"
#define GC_ALLOW_TELEMETRY_DATE "<global-general>telemetryDate"
#define GC_ALLOW_TELEMETRY "<global-general>telemetryAllowed"
#define GC_ALLOW_TELEMETRY_DATE "<global-general>telemetryDecisionDate"
#define GC_TELEMETRY_ID "<global-general>telemetryId"
#define GC_LAST_VERSION_CHECKED "<global-general>lastVersionChecked"


Expand Down
18 changes: 8 additions & 10 deletions src/Gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,26 +630,24 @@ MainWindow::MainWindow(const QDir &home)

#ifdef GC_HAS_CLOUD_DB

#if 0 // temporarily de-activated
telemetryClient = new CloudDBTelemetryClient();
if (appsettings->value(NULL, GC_ALLOW_TELEMETRY, "undefined").toString() == "undefined" ) {
// ask user if storing is allowed

// check for Telemetry Storage acceptance
CloudDBAcceptTelemetryDialog acceptDialog;
acceptDialog.setModal(true);
if (acceptDialog.exec() == QDialog::Accepted) {
CloudDBTelemetryClient::storeTelemetry();
telemetryClient->upsertTelemetry();
};
} else if (appsettings->value(NULL, GC_ALLOW_TELEMETRY, false).toBool()) {
telemetryClient->upsertTelemetry();
}
#endif

QList<VersionAPIGetV1> versions = CloudDBVersionClient::getLatestVersions();
if (versions.count() > 0) {
CloudDBUpdateAvailableDialog updateAvailableDialog(versions);
updateAvailableDialog.setModal(true);
// we are not interested in the result - update check status is updated as part of the dialog box
updateAvailableDialog.exec();
}
versionClient = new CloudDBVersionClient();
versionClient->informUserAboutLatestVersions();



#endif

Expand Down
6 changes: 6 additions & 0 deletions src/Gui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "DragBar.h"
#ifdef GC_HAS_CLOUD_DB
#include "CloudDBChart.h"
#include "CloudDBVersion.h"
#include "CloudDBTelemetry.h"
#endif

#ifdef Q_OS_MAC
Expand Down Expand Up @@ -294,6 +296,10 @@ class MainWindow : public QMainWindow
// Miscellany
QSignalMapper *toolMapper;


CloudDBVersionClient *versionClient;
CloudDBTelemetryClient *telemetryClient;

};

#endif // _GC_MainWindow_h

0 comments on commit 6330703

Please sign in to comment.