Repository navigation
Improved telemetry by adding anonymous usage reports - #4687
Open
gabriel-bolbotina wants to merge 27 commits into
Open
gabriel-bolbotina wants to merge 27 commits into
gabriel-bolbotina wants to merge 27 commits into
Conversation
Coverage Report for CI Build 42239994214Coverage decreased (-1.0%) to 59.135%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions764 previously-covered lines in 6 files lost coverage.
Coverage Stats
💛 - Coveralls |
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
PeterPetrik
reviewed
Aug 31, 2026
gabriel-bolbotina
force-pushed
the
prototype/telemetry
branch
from
September 23, 2026 14:05
f3dce9f to
8567d83
Compare
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
gabriel-bolbotina
marked this pull request as ready for review
October 12, 2026 12:47
gabriel-bolbotina
requested review from
xkello
and removed request for
Withalion
October 12, 2026 12:48
📦 Build Artifacts Ready
|
xkello
reviewed
Oct 20, 2026
xkello
left a comment
Contributor
There was a problem hiding this comment.
@gabriel-bolbotina looks good. Just some small things here and there.
Comment on lines
+648
to
+749
| // Temporary: immediate snapshot trigger from QML test button | ||
| QObject::connect( as, &AppSettings::immediateUsageSnapshotRequested, &usageReportController, &UsageReportController::forceSubmitSnapshot ); | ||
|
|
||
| // Gather dynamic usage data via signal connections | ||
| UsageReportController &urc = usageReportController; | ||
|
|
||
| // Auto-sync | ||
| QObject::connect( &activeProject, &ActiveProject::autosyncControllerChanged, &lambdaContext, [&urc]( AutosyncController * ac ) | ||
| { | ||
| if ( !ac ) return; | ||
| QObject::connect( ac, &AutosyncController::projectSyncRequested, ac, [&urc]( SyncOptions::RequestOrigin origin ) | ||
| { | ||
| if ( origin == SyncOptions::AutomaticRequest ) | ||
| urc.trackFeature( QStringLiteral( "autosync" ) ); | ||
| } ); | ||
| } ); | ||
|
|
||
| // Created project | ||
| QObject::connect( &pw, &ProjectWizard::projectCreated, &lambdaContext, [&urc]( const QString &, const QString & ) | ||
| { | ||
| urc.trackFeature( QStringLiteral( "created_project" ) ); | ||
| } ); | ||
|
|
||
| // Photo captured vs attached | ||
| QObject::connect( &androidUtils, &AndroidUtils::photoCaptured, &lambdaContext, [&urc]() | ||
| { | ||
| urc.incrementCounter( QStringLiteral( "captured_images" ) ); | ||
| } ); | ||
| QObject::connect( &androidUtils, &AndroidUtils::photoFromGallery, &lambdaContext, [&urc]() | ||
| { | ||
| urc.incrementCounter( QStringLiteral( "attached_images" ) ); | ||
| } ); | ||
| QObject::connect( &iosUtils, &IosUtils::photoCaptured, &lambdaContext, [&urc]() | ||
| { | ||
| urc.incrementCounter( QStringLiteral( "captured_images" ) ); | ||
| } ); | ||
| QObject::connect( &iosUtils, &IosUtils::photoFromGallery, &lambdaContext, [&urc]() | ||
| { | ||
| urc.incrementCounter( QStringLiteral( "attached_images" ) ); | ||
| } ); | ||
|
|
||
| // Workspace switches | ||
| QObject::connect( ma->userInfo(), &MerginUserInfo::activeWorkspaceChanged, &lambdaContext, [&urc]() | ||
| { | ||
| urc.incrementCounter( QStringLiteral( "workspace_switches" ) ); | ||
| } ); | ||
|
|
||
| // External GPS provider | ||
| QObject::connect( pk, &PositionKit::positionProviderChanged, &lambdaContext, [&urc]( AbstractPositionProvider * provider ) | ||
| { | ||
| if ( !provider ) return; | ||
|
|
||
| // simulated provider has type "internal", so check it by id first | ||
| const QString type = provider->type(); | ||
| QString connectionType; | ||
| if ( provider->id() == QLatin1String( "simulated" ) ) | ||
| connectionType = QStringLiteral( "mock" ); | ||
| else if ( type == QLatin1String( "external_bt" ) ) | ||
| connectionType = QStringLiteral( "bluetooth" ); | ||
| else if ( type == QLatin1String( "external_ip" ) ) | ||
| connectionType = QStringLiteral( "network" ); | ||
| else if ( type == QLatin1String( "external_trimble" ) ) | ||
| connectionType = QStringLiteral( "trimble" ); | ||
| else | ||
| return; // internal | ||
|
|
||
| urc.trackFeature( QStringLiteral( "external_gps" ) ); | ||
| urc.setData( QStringLiteral( "external_connection_type" ), connectionType ); | ||
| urc.setData( QStringLiteral( "external_brand" ), UsageReportController::externalProviderBrand( type, provider->name() ) ); | ||
| } ); | ||
|
|
||
| // Highest project role | ||
| QObject::connect( &activeProject, &ActiveProject::projectRoleChanged, &lambdaContext, [&urc, as, &activeProject]() | ||
| { | ||
| if ( !as->usageReportEnabled() ) return; | ||
|
|
||
| const QString role = activeProject.projectRole(); | ||
| auto roleRank = []( const QString & r ) -> int | ||
| { | ||
| if ( r == QLatin1String( "owner" ) ) return 5; | ||
| if ( r == QLatin1String( "admin" ) ) return 4; | ||
| if ( r == QLatin1String( "writer" ) ) return 3; | ||
| if ( r == QLatin1String( "reader" ) ) return 2; | ||
| if ( r == QLatin1String( "guest" ) ) return 1; | ||
| return 0; | ||
| }; | ||
|
|
||
| QSettings s; | ||
| const QString current = s.value( QStringLiteral( "usage_report/data/highest_role" ) ).toString(); | ||
| if ( roleRank( role ) > roleRank( current ) ) | ||
| urc.setData( QStringLiteral( "highest_role" ), role ); | ||
| } ); | ||
|
|
||
| // Project load time | ||
| QObject::connect( &activeProject, &ActiveProject::loadingStarted, &lambdaContext, [&urc]() | ||
| { | ||
| urc.startLoadTimer(); | ||
| } ); | ||
|
|
||
| urc.startPingTimer(); | ||
|
|
||
| QObject::connect( &activeProject, &ActiveProject::projectReloaded, &lambdaContext, [merginApi = ma.get(), &activeProject, &urc]() |
Contributor
There was a problem hiding this comment.
Can't this be moved to the usagereportcontroller.cpp?
Contributor
Author
There was a problem hiding this comment.
I moved the logic, role ranking, provider mapping, snapshot, timers, to the usage report controller. I suggest that we keep only the connections in main, as the class already has ActiveProject, PositionKit, platform utils and so on, and the controller shouldn't depend on all of them.
Contributor
Author
There was a problem hiding this comment.
Updated the PR description to contain this separation
📦 Build Artifacts Ready
|
Add usageReportEnabled property to AppSettings with QSettings persistence under usage_report/enabled. On opt-out, all accumulated usage_report/* data is cleared but the enabled flag is preserved. Wire the toggle to the settings page UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add USAGE_REPORT_KEY CMake variable and mmconfig.h.in define. Add trySubmitUsageSnapshot() function in main.cpp that: - checks if a weekly snapshot is due - collects static data (device, app, server info) - merges accumulated dynamic data from QSettings - sends a single HTTP POST to the EU endpoint - resets dynamic data on success, silently ignores errors Called once after app initialization. No signal connections yet — dynamic data accumulation will be wired in the next commit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire feature class signals in main.cpp to lambdas that write usage data to QSettings under usage_report/data/*. Signals connected: - FilterController::hasFiltersActivatedChanged → filtering - AutosyncController::projectSyncRequested → autosync - ProjectWizard::projectCreated → created_project - AndroidUtils/IosUtils::photoCaptured/photoFromGallery → image counts - MerginUserInfo::activeWorkspaceChanged → workspace_switches - PositionKit::positionProviderChanged → external GPS tracking - ActiveProject::projectRoleChanged → highest_role - ActiveProject::loadingStarted/projectReloaded → project load time New signals added: - MapSketchingController::sketched() for map sketching detection - AndroidUtils/IosUtils::photoCaptured()/photoFromGallery() to distinguish camera capture from gallery attachment Connectivity ping timer: HEAD request every 5 minutes, counts success/failure in QSettings. Note: map measuring, map sketching, photo sketching, reuse last value, and bulk editing are QML-instantiated objects — these will be tracked via AppSettings helpers in the next commit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add trackUsageFeature() and incrementUsageCounter() to AppSettings for features whose C++ controllers are QML-instantiated per-use objects with no persistent instance to connect to in main.cpp. Tracked from QML: - map_measuring (MMMapController) - map_sketching (MMMapController) - photo_sketching (MMFormPhotoSketchingPageDialog) - reuse_last_value (MMFormStackController, on form save) - bulk_editing (main.qml, on multi-edit with >1 feature) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added a temporary endpoint Remaned distinct_id with device_id
Added layer and feature search analytics Updated filtering analytics
Updated the dialog image
Moved the logic out of main
Small changes in the usage reporter controller
Added total ping count
Updated connectivity ping endpoint
Added workspace info Added retry timer when the snapshot can't be sent Refactored the usage reporter controller
gabriel-bolbotina
force-pushed
the
prototype/telemetry
branch
from
November 3, 2026 08:55
af16ffb to
9d33939
Compare
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This enhancement has been done to better understand our app and which features are valuable to our users, as well as continuing on improving the experience overall. It also helps prioritise development and simplifies UX/ UI design. It is an OPT-OUT enhancement that stores no personal data.
Summary of changes:
UsageReportController(counters, role ranking, GNSS provider mapping, weekly snapshot, retry timer).main.cpponly wires feature signals to it.Temporary parts of code for testing:
Resolves: https://github.com/MerginMaps/product/issues/127