Skip to content

Commit

Permalink
feat: move tracking of important events (#25152)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
raquelmsmith and github-actions[bot] authored Oct 2, 2024
1 parent 861d9bf commit ddede4d
Show file tree
Hide file tree
Showing 25 changed files with 1,615 additions and 819 deletions.
7 changes: 7 additions & 0 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import {
SessionRecordingSnapshotParams,
SessionRecordingSnapshotResponse,
SessionRecordingType,
SessionRecordingUpdateType,
SharingConfigurationType,
SlackChannelType,
SubscriptionType,
Expand Down Expand Up @@ -1805,6 +1806,12 @@ const api = {
): Promise<SessionRecordingType> {
return await new ApiRequest().recording(recordingId).withQueryString(toParams(params)).get()
},
async update(
recordingId: SessionRecordingType['id'],
data: Partial<SessionRecordingUpdateType>
): Promise<SessionRecordingType> {
return await new ApiRequest().recording(recordingId).update({ data })
},

async persist(recordingId: SessionRecordingType['id']): Promise<{ success: boolean }> {
return await new ApiRequest().recording(recordingId).withAction('persist').create()
Expand Down
55 changes: 0 additions & 55 deletions frontend/src/lib/utils/eventUsageLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,6 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
reportBookmarkletDragged: true,
reportProjectCreationSubmitted: (projectCount: number, nameLength: number) => ({ projectCount, nameLength }),
reportProjectNoticeDismissed: (key: string) => ({ key }),
reportBulkInviteAttempted: (inviteesCount: number, namesCount: number) => ({ inviteesCount, namesCount }),
reportInviteAttempted: (nameProvided: boolean, instanceEmailAvailable: boolean) => ({
nameProvided,
instanceEmailAvailable,
}),
reportPersonPropertyUpdated: (
action: 'added' | 'updated' | 'removed',
totalProperties: number,
Expand Down Expand Up @@ -572,9 +567,6 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
}),
reportSurveyCreated: (survey: Survey, isDuplicate?: boolean) => ({ survey, isDuplicate }),
reportSurveyEdited: (survey: Survey) => ({ survey }),
reportSurveyLaunched: (survey: Survey) => ({ survey }),
reportSurveyStopped: (survey: Survey) => ({ survey }),
reportSurveyResumed: (survey: Survey) => ({ survey }),
reportSurveyArchived: (survey: Survey) => ({ survey }),
reportSurveyTemplateClicked: (template: SurveyTemplateType) => ({ template }),
reportSurveyCycleDetected: (survey: Survey | NewSurvey) => ({ survey }),
Expand Down Expand Up @@ -741,26 +733,6 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
// ProjectNotice was previously called DemoWarning
posthog.capture('demo warning dismissed', { warning_key: key })
},
reportBulkInviteAttempted: async ({
inviteesCount,
namesCount,
}: {
inviteesCount: number
namesCount: number
}) => {
// namesCount -> Number of invitees for which a name was provided
posthog.capture('bulk invite attempted', { invitees_count: inviteesCount, name_count: namesCount })
for (let i = 0; i < inviteesCount; i++) {
posthog.capture('team member invited')
}
},
reportInviteAttempted: async ({ nameProvided, instanceEmailAvailable }) => {
posthog.capture('team member invited')
posthog.capture('team invite attempted', {
name_provided: nameProvided,
instance_email_available: instanceEmailAvailable,
})
},
reportFunnelCalculated: async ({ eventCount, actionCount, interval, funnelVizType, success, error }) => {
posthog.capture('funnel result calculated', {
event_count: eventCount,
Expand Down Expand Up @@ -1268,16 +1240,6 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
),
})
},
reportSurveyLaunched: ({ survey }) => {
posthog.capture('survey launched', {
name: survey.name,
id: survey.id,
survey_type: survey.type,
question_types: survey.questions.map((question) => question.type),
created_at: survey.created_at,
start_date: survey.start_date,
})
},
reportSurveyViewed: ({ survey }) => {
posthog.capture('survey viewed', {
name: survey.name,
Expand All @@ -1287,23 +1249,6 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
end_date: survey.end_date,
})
},
reportSurveyStopped: ({ survey }) => {
posthog.capture('survey stopped', {
name: survey.name,
id: survey.id,
created_at: survey.created_at,
start_date: survey.start_date,
end_date: survey.end_date,
})
},
reportSurveyResumed: ({ survey }) => {
posthog.capture('survey resumed', {
name: survey.name,
id: survey.id,
created_at: survey.created_at,
start_date: survey.start_date,
})
},
reportSurveyArchived: ({ survey }) => {
posthog.capture('survey archived', {
name: survey.name,
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/queries/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9386,6 +9386,9 @@
"type": "string"
},
"type": "array"
},
"user_modified_filters": {
"type": "object"
}
},
"required": ["kind", "order"],
Expand Down
1 change: 1 addition & 0 deletions frontend/src/queries/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ export interface RecordingsQuery extends DataNode<RecordingsQueryResponse> {
| 'mouse_activity_count'
limit?: integer
offset?: integer
user_modified_filters?: Record<string, any>
}

export interface HogQLNotice {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ describe('sessionRecordingDataLogic', () => {
post: {
'/api/projects/:team/query': recordingEventsJson,
},
patch: {
'/api/projects/:team/session_recordings/:id': { success: true },
},
})
initKeaTests()
logic = sessionRecordingDataLogic({
Expand Down Expand Up @@ -364,7 +367,7 @@ describe('sessionRecordingDataLogic', () => {
action.payload.source?.source === 'blob',
'loadSnapshotsForSourceSuccess',
// and then we report having viewed the recording
'reportViewed',
'markViewed',
// the response to the success action triggers loading of the second item which is the realtime source
(action) =>
action.type === logic.actionTypes.loadSnapshotsForSource &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,14 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([
loadSnapshotsForSource: (source: Pick<SessionRecordingSnapshotSource, 'source' | 'blob_key'>) => ({ source }),
loadEvents: true,
loadFullEventData: (event: RecordingEventType) => ({ event }),
reportViewed: true,
markViewed: (delay?: number) => ({ delay }),
reportUsageIfFullyLoaded: true,
persistRecording: true,
maybePersistRecording: true,
pollRealtimeSnapshots: true,
stopRealtimePolling: true,
setTrackedWindow: (windowId: string | null) => ({ windowId }),
setWasMarkedViewed: (wasMarkedViewed: boolean) => ({ wasMarkedViewed }),
}),
reducers(() => ({
trackedWindow: [
Expand Down Expand Up @@ -466,6 +467,12 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([
},
},
],
wasMarkedViewed: [
false as boolean,
{
setWasMarkedViewed: (_, { wasMarkedViewed }) => wasMarkedViewed,
},
],
})),
loaders(({ values, props, cache }) => ({
sessionPlayerMetaData: {
Expand All @@ -476,9 +483,7 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([

cache.metaStartTime = performance.now()

const response = await api.recordings.get(props.sessionRecordingId, {
save_view: true,
})
const response = await api.recordings.get(props.sessionRecordingId)
breakpoint()

return response
Expand Down Expand Up @@ -750,7 +755,9 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([
})
} else if (!cache.firstPaintDuration) {
cache.firstPaintDuration = Math.round(performance.now() - cache.snapshotsStartTime)
actions.reportViewed()
}
if (!values.wasMarkedViewed) {
actions.markViewed()
}

actions.loadNextSnapshotSource()
Expand Down Expand Up @@ -815,25 +822,27 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([
resetTimingsCache(cache)
}
},
reportViewed: async (_, breakpoint) => {
markViewed: async ({ delay }, breakpoint) => {
const durations = generateRecordingReportDurations(cache)
breakpoint()
// Triggered on first paint
eventUsageLogic.actions.reportRecording(
values.sessionPlayerData,
breakpoint()
if (values.wasMarkedViewed) {
return
}
actions.setWasMarkedViewed(true) // this prevents us from calling the function multiple times

await breakpoint(IS_TEST_MODE ? 1 : delay ?? 3000)
await api.recordings.update(props.sessionRecordingId, {
viewed: true,
player_metadata: values.sessionPlayerMetaData,
durations,
SessionRecordingUsageType.VIEWED,
values.sessionPlayerMetaData,
0
)
})
await breakpoint(IS_TEST_MODE ? 1 : 10000)
eventUsageLogic.actions.reportRecording(
values.sessionPlayerData,
await api.recordings.update(props.sessionRecordingId, {
analyzed: true,
player_metadata: values.sessionPlayerMetaData,
durations,
SessionRecordingUsageType.ANALYZED,
values.sessionPlayerMetaData,
10
)
})
},

maybePersistRecording: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const sessionRecordingPlayerLogic = kea<sessionRecordingPlayerLogicType>(
'createExportJSON',
'customRRWebEvents',
'fullyLoaded',
'wasMarkedViewed',
],
playerSettingsLogic,
['speed', 'skipInactivitySetting', 'showMouseTail'],
Expand All @@ -131,6 +132,8 @@ export const sessionRecordingPlayerLogic = kea<sessionRecordingPlayerLogicType>(
'loadSnapshotSourcesFailure',
'loadRecordingMetaSuccess',
'maybePersistRecording',
'setWasMarkedViewed',
'markViewed',
],
playerSettingsLogic,
['setSpeed', 'setSkipInactivitySetting'],
Expand Down Expand Up @@ -812,6 +815,10 @@ export const sessionRecordingPlayerLogic = kea<sessionRecordingPlayerLogicType>(
setEndReached: ({ reached }) => {
if (reached) {
actions.setPause()
// TODO: this will be time-gated so won't happen immediately, but we need it to
if (!values.wasMarkedViewed) {
actions.markViewed(0)
}
}
},
startBuffer: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { objectClean, objectsEqual } from 'lib/utils'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import posthog from 'posthog-js'

import { NodeKind, RecordingsQuery, RecordingsQueryResponse } from '~/queries/schema'
import {
Expand Down Expand Up @@ -95,19 +94,6 @@ export const getDefaultFilters = (personUUID?: PersonUUID): RecordingUniversalFi
return personUUID ? DEFAULT_PERSON_RECORDING_FILTERS : DEFAULT_RECORDING_FILTERS
}

const capturePartialFilters = (filters: Partial<RecordingUniversalFilters>): void => {
// capture only the partial filters applied (not the full filters object)
// take each key from the filter and change it to `partial_filter_chosen_${key}`
const partialFilters = Object.keys(filters).reduce((acc, key) => {
acc[`partial_filter_chosen_${key}`] = filters[key]
return acc
}, {})

posthog.capture('recording list filters changed', {
...partialFilters,
})
}

export function convertUniversalFiltersToRecordingsQuery(universalFilters: RecordingUniversalFilters): RecordingsQuery {
const filters = filtersFromUniversalFilterGroups(universalFilters)

Expand Down Expand Up @@ -298,7 +284,10 @@ export const sessionRecordingsPlaylistLogic = kea<sessionRecordingsPlaylistLogic
}),
loadAllRecordings: true,
loadPinnedRecordings: true,
loadSessionRecordings: (direction?: 'newer' | 'older') => ({ direction }),
loadSessionRecordings: (direction?: 'newer' | 'older', userModifiedFilters?: Record<string, any>) => ({
direction,
userModifiedFilters,
}),
maybeLoadSessionRecordings: (direction?: 'newer' | 'older') => ({ direction }),
loadNext: true,
loadPrev: true,
Expand Down Expand Up @@ -340,14 +329,18 @@ export const sessionRecordingsPlaylistLogic = kea<sessionRecordingsPlaylistLogic
order: 'start_time',
} as RecordingsQueryResponse & { order: RecordingsQuery['order'] },
{
loadSessionRecordings: async ({ direction }, breakpoint) => {
loadSessionRecordings: async ({ direction, userModifiedFilters }, breakpoint) => {
const params: RecordingsQuery = {
...convertUniversalFiltersToRecordingsQuery(values.filters),
person_uuid: props.personUUID ?? '',
order: values.orderBy,
limit: RECORDINGS_LIMIT,
}

if (userModifiedFilters) {
params.user_modified_filters = userModifiedFilters
}

if (direction === 'older') {
params.offset = values.sessionRecordings.length
}
Expand Down Expand Up @@ -524,9 +517,8 @@ export const sessionRecordingsPlaylistLogic = kea<sessionRecordingsPlaylistLogic
actions.loadPinnedRecordings()
},
setFilters: ({ filters }) => {
actions.loadSessionRecordings()
actions.loadSessionRecordings(undefined, filters)
props.onFiltersChange?.(values.filters)
capturePartialFilters(filters)
actions.loadEventsHaveSessionId()
},

Expand Down
5 changes: 0 additions & 5 deletions frontend/src/scenes/settings/organization/inviteLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { router, urlToAction } from 'kea-router'
import api from 'lib/api'
import { OrganizationMembershipLevel } from 'lib/constants'
import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { organizationLogic } from 'scenes/organizationLogic'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'

Expand Down Expand Up @@ -55,10 +54,6 @@ export const inviteLogic = kea<inviteLogicType>([

const payload: Pick<OrganizationInviteType, 'target_email' | 'first_name' | 'level' | 'message'>[] =
values.invitesToSend.filter((invite) => invite.target_email)
eventUsageLogic.actions.reportBulkInviteAttempted(
payload.length,
payload.filter((invite) => !!invite.first_name).length
)
if (values.message) {
payload.forEach((payload) => (payload.message = values.message))
}
Expand Down
10 changes: 2 additions & 8 deletions frontend/src/scenes/surveys/surveyLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,8 @@ export const surveyLogic = kea<surveyLogicType>([
eventUsageLogic,
[
'reportSurveyCreated',
'reportSurveyLaunched',
'reportSurveyEdited',
'reportSurveyArchived',
'reportSurveyStopped',
'reportSurveyResumed',
'reportSurveyViewed',
'reportSurveyCycleDetected',
],
Expand Down Expand Up @@ -578,15 +575,12 @@ export const surveyLogic = kea<surveyLogicType>([
launchSurveySuccess: ({ survey }) => {
lemonToast.success(<>Survey {survey.name} launched</>)
actions.loadSurveys()
actions.reportSurveyLaunched(survey)
},
stopSurveySuccess: ({ survey }) => {
stopSurveySuccess: () => {
actions.loadSurveys()
actions.reportSurveyStopped(survey)
},
resumeSurveySuccess: ({ survey }) => {
resumeSurveySuccess: () => {
actions.loadSurveys()
actions.reportSurveyResumed(survey)
},
archiveSurvey: () => {
actions.updateSurvey({ archived: true })
Expand Down
Loading

0 comments on commit ddede4d

Please sign in to comment.