feat(metrics): add onboarding screen metrics and section navigation (#16112) (#16145)

* feat(metrics): add onboarding screen metrics and section navigation

Fixes #16100

Adds metrics for navigating to the different screens of the onboarding and when navigating to a new section while logged in; only when enabled of course.

I refactored the code a little to make it simpler. I moved the check to see if the metrics collection is enabled in the async task itself, so we don't have to check it each time we add a new metric

* fix metric sent from module

* review comments

* add flowtype
This commit is contained in:
Jonathan Rainville 2024-08-19 12:52:17 -04:00 committed by GitHub
parent afc129ee5b
commit 42a9530452
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 69 additions and 35 deletions

View File

@ -57,4 +57,4 @@ QtObject:
proc showCommunityMemberUnbannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} proc showCommunityMemberUnbannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.}
proc addCentralizedMetric*(self: GlobalEvents, eventName: string, eventValueJson: string) {.signal.} proc addCentralizedMetricIfEnabled*(self: GlobalEvents, eventName: string, eventValueJson: string) {.signal.}

View File

@ -1,4 +1,4 @@
import NimQml, tables, json, sugar, sequtils, stew/shims/strformat, marshal, times, chronicles, stint, browsers import NimQml, tables, json, sugar, sequtils, stew/shims/strformat, marshal, times, chronicles, stint, browsers, strutils
import io_interface, view, controller, chat_search_item, chat_search_model import io_interface, view, controller, chat_search_item, chat_search_model
import ephemeral_notification_item, ephemeral_notification_model import ephemeral_notification_item, ephemeral_notification_model
@ -884,6 +884,15 @@ method activeSectionSet*[T](self: Module[T], sectionId: string, skipSavingInSett
of conf.COMMUNITIESPORTAL_SECTION_ID: of conf.COMMUNITIESPORTAL_SECTION_ID:
self.communitiesModule.onActivated() self.communitiesModule.onActivated()
# If metrics are enabled, send a navigation event
var sectionIdToSend = sectionId
if sectionId == singletonInstance.userProfile.getPubKey():
sectionIdToSend = conf.CHAT_SECTION_NAME
elif sectionId.startsWith("0x"):
# This is a community
sectionIdToSend = "community"
singletonInstance.globalEvents.addCentralizedMetricIfEnabled("navigation", $(%*{"viewId": sectionIdToSend}))
self.view.model().setActiveSection(sectionId) self.view.model().setActiveSection(sectionId)
self.view.activeSectionSet(item) self.view.activeSectionSet(item)

View File

@ -1,5 +1,6 @@
import NimQml, chronicles import NimQml, chronicles, json
import io_interface import io_interface
import ../../global/global_singleton
import selected_login_account import selected_login_account
import internal/[state, state_wrapper] import internal/[state, state_wrapper]
import models/login_account_model as login_acc_model import models/login_account_model as login_acc_model
@ -74,6 +75,9 @@ QtObject:
return self.currentStartupState.getStateObj() return self.currentStartupState.getStateObj()
proc setCurrentStartupState*(self: View, state: State) = proc setCurrentStartupState*(self: View, state: State) =
# If metrics is enabled, we send a metric of the screen visited
singletonInstance.globalEvents.addCentralizedMetricIfEnabled("navigation", $(%*{"viewId": state.stateType, "flowType": state.flowType}))
self.currentStartupState.setStateObj(state) self.currentStartupState.setStateObj(state)
proc getCurrentStartupState(self: View): QVariant {.slot.} = proc getCurrentStartupState(self: View): QVariant {.slot.} =
return self.currentStartupStateVariant return self.currentStartupStateVariant

View File

@ -2,13 +2,23 @@ include ../../common/json_utils
include ../../../app/core/tasks/common include ../../../app/core/tasks/common
type type
AsyncAddCentralizedMetricTaskArg = ref object of QObjectTaskArg AsyncAddCentralizedMetricIfEnabledTaskArg = ref object of QObjectTaskArg
eventName: string eventName: string
eventValueJson: string eventValueJson: string
proc asyncAddCentralizedMetricTask(argEncoded: string) {.gcsafe, nimcall.} = proc asyncAddCentralizedMetricIfEnabledTask(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncAddCentralizedMetricTaskArg](argEncoded) let arg = decode[AsyncAddCentralizedMetricIfEnabledTaskArg](argEncoded)
try: try:
let metricsEnabled = getIsCentralizedMetricsEnabled()
if not metricsEnabled:
arg.finish(%* {
"metricId": "",
"metricsDisabled": true,
"error": "",
})
return
debug "Add metric for ", eventName = arg.eventName, eventValueJson = arg.eventValueJson
var metric = CentralizedMetricDto() var metric = CentralizedMetricDto()
metric.eventName = arg.eventName metric.eventName = arg.eventName
metric.eventValue = if arg.eventValueJson.len > 0: parseJson(arg.eventValueJson) else: JsonNode() metric.eventValue = if arg.eventValueJson.len > 0: parseJson(arg.eventValueJson) else: JsonNode()
@ -21,6 +31,7 @@ proc asyncAddCentralizedMetricTask(argEncoded: string) {.gcsafe, nimcall.} =
if jsonObj.hasKey("error"): if jsonObj.hasKey("error"):
arg.finish(%* { arg.finish(%* {
"metricId": "", "metricId": "",
"metricsDisabled": false,
"error": jsonObj{"error"}.getStr, "error": jsonObj{"error"}.getStr,
}) })
return return
@ -29,6 +40,7 @@ proc asyncAddCentralizedMetricTask(argEncoded: string) {.gcsafe, nimcall.} =
arg.finish(%* { arg.finish(%* {
"metricId": response, "metricId": response,
"metricsDisabled": false,
"error": "", "error": "",
}) })

View File

@ -8,6 +8,18 @@ import status_go
import constants import constants
import ./dto import ./dto
proc getIsCentralizedMetricsEnabled*(): bool =
try:
let response = status_go.centralizedMetricsInfo()
let jsonObj = response.parseJson
if jsonObj.hasKey("error"):
error "isCentralizedMetricsEnabled", errorMsg=jsonObj["error"].getStr
return false
let metricsInfo = toCentralizedMetricsInfoDto(jsonObj)
return metricsInfo.enabled
except Exception:
return false
include async_tasks include async_tasks
logScope: logScope:
@ -16,6 +28,7 @@ logScope:
QtObject: QtObject:
type MetricsService* = ref object of QObject type MetricsService* = ref object of QObject
threadpool: ThreadPool threadpool: ThreadPool
metricsEnabled: bool
proc delete*(self: MetricsService) = proc delete*(self: MetricsService) =
self.QObject.delete self.QObject.delete
@ -25,48 +38,42 @@ QtObject:
result.QObject.setup result.QObject.setup
result.threadpool = threadpool result.threadpool = threadpool
signalConnect(singletonInstance.globalEvents, "addCentralizedMetric(QString, QString)", signalConnect(singletonInstance.globalEvents, "addCentralizedMetricIfEnabled(QString, QString)",
result, "addCentralizedMetric(QString, QString)", 2) result, "addCentralizedMetricIfEnabled(QString, QString)", 2)
# eventValueJson is a json string # eventValueJson is a json string
proc addCentralizedMetric*(self: MetricsService, eventName: string, eventValueJson: string) {.slot.} = proc addCentralizedMetricIfEnabled*(self: MetricsService, eventName: string, eventValueJson: string) {.slot.} =
let arg = AsyncAddCentralizedMetricTaskArg( let arg = AsyncAddCentralizedMetricIfEnabledTaskArg(
tptr: asyncAddCentralizedMetricTask, tptr: asyncAddCentralizedMetricIfEnabledTask,
vptr: cast[ByteAddress](self.vptr), vptr: cast[ByteAddress](self.vptr),
slot: "onCentralizedMetricAdded", slot: "onCentralizedMetricAddedIdEnabled",
eventName: eventName, eventName: eventName,
eventValueJson: eventValueJson, eventValueJson: eventValueJson,
) )
self.threadpool.start(arg) self.threadpool.start(arg)
proc onCentralizedMetricAdded*(self: MetricsService, response: string) {.slot.} = proc onCentralizedMetricAddedIdEnabled*(self: MetricsService, response: string) {.slot.} =
try: try:
let responseObj = response.parseJson let responseObj = response.parseJson
let errorString = responseObj{"error"}.getStr() let errorString = responseObj{"error"}.getStr()
if errorString != "": if errorString != "":
error "onCentralizedMetricAdded", error=errorString error "onCentralizedMetricAddedIdEnabled", error=errorString
return
if responseObj{"metricsDisabled"}.getBool:
return return
debug "onCentralizedMetricAdded", metricId=responseObj{"metricId"}.getStr() debug "onCentralizedMetricAddedIdEnabled", metricId=responseObj{"metricId"}.getStr()
except Exception as e: except Exception as e:
error "onCentralizedMetricAdded", exceptionMsg = e.msg error "onCentralizedMetricAddedIdEnabled", exceptionMsg = e.msg
proc centralizedMetricsEnabledChaned*(self: MetricsService) {.signal.} proc centralizedMetricsEnabledChanged*(self: MetricsService) {.signal.}
proc isCentralizedMetricsEnabled*(self: MetricsService): bool {.slot.} = proc isCentralizedMetricsEnabled*(self: MetricsService): bool {.slot.} =
try: return getIsCentralizedMetricsEnabled()
let response = status_go.centralizedMetricsInfo()
let jsonObj = response.parseJson
if jsonObj.hasKey("error"):
error "isCentralizedMetricsEnabled", errorMsg=jsonObj["error"].getStr
return false
let metricsInfo = toCentralizedMetricsInfoDto(jsonObj)
return metricsInfo.enabled
except Exception:
return false
QtProperty[bool] isCentralizedMetricsEnabled: QtProperty[bool] isCentralizedMetricsEnabled:
read = isCentralizedMetricsEnabled read = isCentralizedMetricsEnabled
notify = centralizedMetricsEnabledChaned notify = centralizedMetricsEnabledChanged
proc toggleCentralizedMetrics*(self: MetricsService, enabled: bool) {.slot.} = proc toggleCentralizedMetrics*(self: MetricsService, enabled: bool) {.slot.} =
try: try:
@ -79,6 +86,6 @@ QtObject:
if jsonObj{"error"}.getStr.len > 0: if jsonObj{"error"}.getStr.len > 0:
error "toggleCentralizedMetrics", errorMsg=jsonObj["error"].getStr error "toggleCentralizedMetrics", errorMsg=jsonObj["error"].getStr
else: else:
self.centralizedMetricsEnabledChaned() self.centralizedMetricsEnabledChanged()
except Exception as e: except Exception as e:
error "toggleCentralizedMetrics", exceptionMsg = e.msg error "toggleCentralizedMetrics", exceptionMsg = e.msg

View File

@ -7,9 +7,9 @@ QtObject {
metrics.toggleCentralizedMetrics(enabled) metrics.toggleCentralizedMetrics(enabled)
} }
function addCentralizedMetric(eventName, eventValue = null) { function addCentralizedMetricIfEnabled(eventName, eventValue = null) {
let eventValueJsonStr = !!eventValue ? JSON.stringify(eventValue) : "" let eventValueJsonStr = !!eventValue ? JSON.stringify(eventValue) : ""
metrics.addCentralizedMetric(eventName, eventValueJsonStr) metrics.addCentralizedMetricIfEnabled(eventName, eventValueJsonStr)
} }
readonly property bool isCentralizedMetricsEnabled : metrics.isCentralizedMetricsEnabled readonly property bool isCentralizedMetricsEnabled : metrics.isCentralizedMetricsEnabled

View File

@ -1460,4 +1460,6 @@ QtObject {
LessThanFiveMins, LessThanFiveMins,
MoreThanFiveMins MoreThanFiveMins
} }
readonly property string navigationMetric: "navigation"
} }

View File

@ -106,7 +106,7 @@ QtObject {
// Metrics // Metrics
signal openMetricsEnablePopupRequested(string placement, var cb) signal openMetricsEnablePopupRequested(string placement, var cb)
signal addCentralizedMetric(string eventName, var eventValue) signal addCentralizedMetricIfEnabled(string eventName, var eventValue)
signal openAddEditSavedAddressesPopup(var params) signal openAddEditSavedAddressesPopup(var params)
signal openDeleteSavedAddressesPopup(var params) signal openDeleteSavedAddressesPopup(var params)

View File

@ -289,7 +289,7 @@ StatusWindow {
restoreAppState(); restoreAppState();
Global.openMetricsEnablePopupRequested.connect(openMetricsEnablePopup) Global.openMetricsEnablePopupRequested.connect(openMetricsEnablePopup)
Global.addCentralizedMetric.connect(metricsStore.addCentralizedMetric) Global.addCentralizedMetricIfEnabled.connect(metricsStore.addCentralizedMetricIfEnabled)
} }
signal navigateTo(string path) signal navigateTo(string path)
@ -381,8 +381,8 @@ StatusWindow {
onClosed: metricsPopupLoader.active = false onClosed: metricsPopupLoader.active = false
onToggleMetrics: { onToggleMetrics: {
applicationWindow.metricsStore.toggleCentralizedMetrics(enabled) applicationWindow.metricsStore.toggleCentralizedMetrics(enabled)
if(enabled) { if (enabled) {
Global.addCentralizedMetric("usage_data_shared", {placement: metricsPopupLoader.item.placement}) Global.addCentralizedMetricIfEnabled("usage_data_shared", {placement: metricsPopupLoader.item.placement})
} }
} }
} }