Skip to content

Commit

Permalink
Target flask app instead of GA4, add ability to queue events if serve…
Browse files Browse the repository at this point in the history
…r is down.
  • Loading branch information
akenmorris committed Feb 1, 2023
1 parent 5b540e7 commit ff8c5af
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 15 deletions.
12 changes: 11 additions & 1 deletion Studio/Data/Preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,19 @@ QDateTime Preferences::get_update_snooze_until() {
void Preferences::set_update_snooze_until(QDateTime date) { settings_.setValue("General/update_snooze_until", date); }

QString Preferences::get_device_id() {
return settings_.value("General/device_id", QUuid::createUuid().toString()).toString();
QString id = settings_.value("General/device_id", QUuid::createUuid().toString()).toString();
settings_.setValue("General/device_id", id);
return id;
}

bool Preferences::get_telemetry_enabled() { return settings_.value("General/telemetry_enabled", true).toBool(); }
void Preferences::set_telemetry_enabled(bool enabled) { settings_.setValue("General/telemetry_enabled", enabled); }
bool Preferences::get_telemetry_asked() { return settings_.value("General/telemetry_asked", false).toBool(); }
void Preferences::set_telemetry_asked(bool asked) { settings_.setValue("General/telemetry_asked", asked); }

QStringList Preferences::get_pending_telemetry_events() {
return settings_.value("Telemetry/pending_events").toStringList();
}
void Preferences::set_pending_telemetry_events(QStringList events) {
settings_.setValue("Telemetry/pending_events", events);
}
3 changes: 3 additions & 0 deletions Studio/Data/Preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ class Preferences : public QObject {
bool get_telemetry_asked();
void set_telemetry_asked(bool asked);

QStringList get_pending_telemetry_events();
void set_pending_telemetry_events(QStringList events);

Q_SIGNALS:

void color_scheme_changed(int newIndex);
Expand Down
96 changes: 82 additions & 14 deletions Studio/Data/Telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <QJsonObject>
#include <QMessageBox>
#include <QNetworkReply>
#include <QOperatingSystemVersion>
#include <QUrlQuery>

namespace shapeworks {
Expand All @@ -35,40 +36,107 @@ void Telemetry::record_event(const QString& name, const QVariantMap& params) {
prefs_.set_telemetry_asked(true);
}

auto device_id = prefs_.get_device_id();
auto params_json = QJsonObject{
{"device_id", device_id}, {"platform", StudioUtils::get_platform_string()}, {"version", SHAPEWORKS_VERSION}};

QString measurement_id{GA_MEASUREMENT_ID};
QString api_secret{GA_API_SECRET};

if (measurement_id.isEmpty() || api_secret.isEmpty()) {
SW_LOG("Telemetry disabled, no measurement id or api secret");
enabled_ = false;
return;
}

if (!prefs_.get_telemetry_enabled()) {
SW_LOG("Telemetry disabled by preferences");
enabled_ = false;
return;
}

active_event_ = create_event(name, params);
send_event(active_event_);
}

void Telemetry::handle_network_reply(QNetworkReply* reply) {
try {
std::string response = QString(reply->readAll()).toStdString();
// parse reply as json
QJsonDocument json = QJsonDocument::fromJson(response.c_str());
// get response from json
QJsonObject json_obj = json.object();
if (json_obj["response"].toString() == "ok") {
SW_DEBUG("Telemetry::handleNetworkReply: ok");

auto events = prefs_.get_pending_telemetry_events();
if (!events.empty()) {
SW_DEBUG("Telemetry::handleNetworkReply: sending pending events");
auto event = events.front();
events.pop_front();
prefs_.set_pending_telemetry_events(events);
send_event(event);
}

return;
}
} catch (std::exception& e) {
SW_DEBUG("Telemetry::handleNetworkReply: exception: {}", e.what());
return;
}

enabled_ = false;
// store event for later retry
SW_DEBUG("Storing event for later retry");
store_event(active_event_);
active_event_ = "";
}

QString Telemetry::create_event(const QString& name, const QVariantMap& params) {
auto device_id = prefs_.get_device_id();
auto params_json = QJsonObject{
{"device_id", device_id}, {"platform", StudioUtils::get_platform_string()}, {"version", SHAPEWORKS_VERSION}};

const auto os = QOperatingSystemVersion::current();
params_json["operating_system"] =
QString{"%1 %2.%3"}.arg(os.name(), QString::number(os.majorVersion()), QString::number(os.minorVersion()));
params_json["operating_system_language"] = QLocale::system().nativeLanguageName();

for (auto it = params.constKeyValueBegin(); it != params.constKeyValueEnd(); it++) {
params_json.insert(it->first, QJsonValue::fromVariant(it->second));
}

auto payload = QJsonObject{{"client_id", device_id},
{"timestamp_micros", QDateTime::currentMSecsSinceEpoch() * 1000},
{"events", QJsonArray{QJsonObject{{"name", name}, {"params", params_json}}}}};
auto event = QJsonObject{{"client_id", device_id},
{"timestamp_micros", QDateTime::currentMSecsSinceEpoch() * 1000},
{"events", QJsonArray{QJsonObject{{"name", name}, {"params", params_json}}}}};

QNetworkRequest request(QUrl("https://www.google-analytics.com/mp/collect?&measurement_id=" + measurement_id +
"&api_secret=" + api_secret));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_.post(request, QJsonDocument(payload).toJson());
return QJsonDocument(event).toJson();
}

void Telemetry::handle_network_reply(QNetworkReply* reply) {
std::string response = QString(reply->readAll()).toStdString();
// SW_LOG("Telemetry::handleNetworkReply: {}", response);
void Telemetry::send_event(const QString& event) {
if (!enabled_) {
store_event(event);
return;
}

QString api_secret{GA_API_SECRET};

QUrlQuery query;
query.addQueryItem("secret", api_secret);

QString server = "http://127.0.0.1:5001";
QUrl url(server + "/post_json");
url.setQuery(query);
QNetworkRequest network_request(url);

active_event_ = event;
network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
network_.post(network_request, event.toUtf8());
}

void Telemetry::store_event(const QString& event) {
auto events = prefs_.get_pending_telemetry_events();
events.push_back(event);
while (events.size() > 100) { // only keep the last 100 events
events.pop_front();
}
prefs_.set_pending_telemetry_events(events);
}

} // namespace shapeworks
10 changes: 10 additions & 0 deletions Studio/Data/Telemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,18 @@ class Telemetry : public QObject {
void handle_network_reply(QNetworkReply* reply);

private:
QString create_event(const QString& name, const QVariantMap& params);

void send_event(const QString& event);

void store_event(const QString& event);

bool enabled_ = true;

QNetworkAccessManager network_;

QString active_event_;

Preferences& prefs_;
};

Expand Down

0 comments on commit ff8c5af

Please sign in to comment.