Skip to content

Commit

Permalink
Update the analytics events for relevancy (#5108)
Browse files Browse the repository at this point in the history
* Make analytics tests stricter; replace collectionValue with query

* Update the analytics events for relevancy

* Update tests

* Update frontend/shared/types/analytics.ts

Co-authored-by: Dhruv Bhanushali <[email protected]>

---------

Co-authored-by: Dhruv Bhanushali <[email protected]>
  • Loading branch information
obulat and dhruvkb authored Dec 17, 2024
1 parent 4555dcd commit 1d69f30
Show file tree
Hide file tree
Showing 20 changed files with 107 additions and 37 deletions.
9 changes: 6 additions & 3 deletions frontend/shared/types/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export type SearchParamsForEvent = {
export type SearchResultParams = {
/** The unique ID of the media */
id: string
/** The search term */
query: string
/** The position, not index, of the result in the search results */
position: number
}

/**
Expand Down Expand Up @@ -78,11 +82,9 @@ export type Events = {
* - How often are searches returning fewer than one page of results?
* - How many results do most searches yield?
*/
GET_SEARCH_RESULTS: {
GET_SEARCH_RESULTS: SearchParamsForEvent & {
/** the media type of the results. Is different from `searchType` when searchType is "all media" */
mediaType: SupportedMediaType
/** The search term */
query: string
/** The number of results found for this search */
resultsCount: number
}
Expand Down Expand Up @@ -293,6 +295,7 @@ export type Events = {
* - Which results are most popular for given searches?
* - How often do searches lead to clicking a result?
* - Are there popular searches that do not result in result selection?
* - How often do users select results from the "All content" search?
*/
SELECT_SEARCH_RESULT: SearchResultParams &
Pick<SearchParamsForEvent, "collectionType"> & {
Expand Down
1 change: 1 addition & 0 deletions frontend/shared/types/collection-component-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type SingleResultProps = {
kind: ResultKind
searchTerm: string
relatedTo?: string
position?: number
}
export type CommonCollectionProps = SingleResultProps & {
collectionLabel: string
Expand Down
13 changes: 13 additions & 0 deletions frontend/shared/utils/query-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ export const firstParam = (
return params ?? null
}

export function getNumber(
params: LocationQueryValue | LocationQueryValue[] | undefined
): number {
const value = firstParam(params)
return value ? parseInt(value, 10) : -1
}

export function getString(
params: LocationQueryValue | LocationQueryValue[] | undefined
): string {
return firstParam(params) ?? ""
}

export const validateUUID = (id: string | undefined | null) => {
if (!id) {
return false
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/components/VImageResult/VImageResult.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ const props = withDefaults(
* uses the image's intrinsic size.
*/
aspectRatio?: AspectRatio
position?: number
}
>(),
{
aspectRatio: "square",
kind: "search",
relatedTo: "null",
position: -1,
}
)
Expand Down Expand Up @@ -61,7 +63,7 @@ const imageUrl = computed(() => {
})
const imageLink = computed(() => {
return `/image/${props.image.id}/${singleResultQuery(props.searchTerm)}`
return `/image/${props.image.id}/${singleResultQuery(props.searchTerm, props.position)}`
})
/**
Expand Down Expand Up @@ -110,14 +112,13 @@ const sendSelectSearchResultEvent = (event: MouseEvent) => {
return
}
const { searchType, collectionType } = searchStore.searchParamsForEvent
$sendCustomEvent("SELECT_SEARCH_RESULT", {
searchType,
collectionType,
...searchStore.searchParamsForEvent,
id: props.image.id,
kind: props.kind,
mediaType: IMAGE,
provider: props.image.provider,
position: props.position,
relatedTo: props.relatedTo ?? "null",
sensitivities: props.image.sensitivity?.join(",") ?? "",
isBlurred: shouldBlur.value ?? "null",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/VMediaInfo/VGetMediaButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import { useNuxtApp } from "#imports"
import type { SupportedMediaType } from "#shared/constants/media"
import type { Media } from "#shared/types/media"
import { useRouteResultParams } from "~/composables/use-route-result-params"
import VButton from "~/components/VButton.vue"
const props = defineProps<{
media: Media
mediaType: SupportedMediaType
}>()
const { resultParams } = useRouteResultParams()
const { $sendCustomEvent } = useNuxtApp()
const sendGetMediaEvent = () => {
$sendCustomEvent("GET_MEDIA", {
id: props.media.id,
...resultParams.value,
provider: props.media.provider,
mediaType: props.mediaType,
})
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/VMediaInfo/VLicenseTabPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { useNuxtApp } from "#imports"
import type { SupportedMediaType } from "#shared/constants/media"
import { useRouteResultParams } from "~/composables/use-route-result-params"
import VCopyButton from "~/components/VCopyButton.vue"
import VTabPanel from "~/components/VTabs/VTabPanel.vue"
Expand All @@ -23,10 +24,12 @@ const props = defineProps<{
mediaType: SupportedMediaType
}>()
const { resultParams } = useRouteResultParams()
const { $sendCustomEvent } = useNuxtApp()
const handleCopy = () => {
$sendCustomEvent("COPY_ATTRIBUTION", {
id: props.mediaId,
...resultParams.value,
format: props.tab,
mediaType: props.mediaType,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ const isSm = computed(() => uiStore.isBreakpoint("sm"))
"
:aria-label="collectionLabel"
>
<template v-for="item in results">
<template v-for="(item, idx) in results">
<VImageResult
v-if="isDetail.image(item)"
:key="item.id"
:image="item"
:position="idx + 1"
:search-term="searchTerm"
kind="search"
aspect-ratio="square"
Expand All @@ -51,6 +52,7 @@ const isSm = computed(() => uiStore.isBreakpoint("sm"))
v-if="isDetail.audio(item)"
:key="item.id"
:audio="item"
:position="idx + 1"
:search-term="searchTerm"
layout="box"
:size="isSm ? 'l' : 's'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ const audioTrackSize = computed(() => {
:class="kind === 'related' ? 'gap-4' : 'gap-2 md:gap-1'"
>
<VAudioResult
v-for="audio in results"
v-for="(audio, idx) in results"
:key="audio.id"
:search-term="searchTerm"
:related-to="relatedTo"
:audio="audio"
layout="row"
:size="audioTrackSize"
:kind="kind"
:position="idx + 1"
/>
</ol>
</div>
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/components/VSearchResultsGrid/VAudioResult.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ const props = withDefaults(
layout: Extract<AudioLayout, "box" | "row">
size?: AudioSize
audio: AudioDetail
position?: number
}
>(),
{ relatedTo: "null" }
{ relatedTo: "null", position: -1 }
)
const { $sendCustomEvent } = useNuxtApp()
Expand All @@ -34,7 +35,7 @@ const searchStore = useSearchStore()
const { isHidden: shouldBlur } = useSensitiveMedia(ref(props.audio))
const href = computed(() => {
return `/audio/${props.audio.id}/${singleResultQuery(props.searchTerm)}`
return `/audio/${props.audio.id}/${singleResultQuery(props.searchTerm, props.position)}`
})
const sendSelectSearchResultEvent = (
Expand All @@ -47,13 +48,12 @@ const sendSelectSearchResultEvent = (
return
}
useAudioSnackbar().hide()
const { searchType, collectionType } = searchStore.searchParamsForEvent
$sendCustomEvent("SELECT_SEARCH_RESULT", {
searchType,
collectionType,
...searchStore.searchParamsForEvent,
id: audio.id,
kind: props.kind,
mediaType: AUDIO,
position: props.position,
provider: audio.provider,
relatedTo: props.relatedTo ?? "null",
sensitivities: audio.sensitivity?.join(",") ?? "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ withDefaults(defineProps<CollectionComponentProps<"image">>(), {
:aria-label="collectionLabel"
>
<VImageResult
v-for="image in results"
v-for="(image, idx) in results"
:key="image.id"
:image="image"
:position="idx + 1"
:search-term="searchTerm"
aspect-ratio="intrinsic"
:kind="kind"
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/composables/use-route-result-params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useRoute } from "#imports"
import { computed } from "vue"

import { getNumber, getString } from "#shared/utils/query-utils"

/**
* Extracts the media ID, search term, and result position from the single result page route.
*/
export const useRouteResultParams = () => {
const route = useRoute()

const resultParams = computed(() => {
const id = getString(route.params.id)
const query = getString(route.query.q)
const position = getNumber(route.query.p)
return { id, query, position }
})

return {
resultParams,
}
}
11 changes: 6 additions & 5 deletions frontend/src/pages/image/[id]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useAnalytics } from "~/composables/use-analytics"
import { useSensitiveMedia } from "~/composables/use-sensitive-media"
import { useSingleResultPageMeta } from "~/composables/use-single-result-page-meta"
import { usePageRobotsRule } from "~/composables/use-page-robots-rule"
import { useRouteResultParams } from "~/composables/use-route-result-params"
import VBone from "~/components/VSkeleton/VBone.vue"
import VMediaReuse from "~/components/VMediaInfo/VMediaReuse.vue"
Expand Down Expand Up @@ -99,10 +100,10 @@ const showLoadingState = computed(() => {
const { sendCustomEvent } = useAnalytics()
const handleRightClick = (id: string) => {
sendCustomEvent("RIGHT_CLICK_IMAGE", {
id,
})
const { resultParams } = useRouteResultParams()
const handleRightClick = () => {
sendCustomEvent("RIGHT_CLICK_IMAGE", resultParams.value)
}
const { reveal, isHidden } = useSensitiveMedia(image.value)
Expand Down Expand Up @@ -262,7 +263,7 @@ watch(error, (err) => {
:height="image.height ?? 0"
@load="onImageLoaded"
@error="onImageError"
@contextmenu="handleRightClick(image.id)"
@contextmenu="handleRightClick"
/>
<div
v-if="sketchFabUid"
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/stores/media/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,8 @@ export const useMediaStore = defineStore("media", {

if (page == 1) {
$sendCustomEvent("GET_SEARCH_RESULTS", {
mediaType: mediaType,
query: queryParams.q,
...searchStore.searchParamsForEvent,
mediaType,
resultsCount: mediaCount,
})
}
Expand Down
3 changes: 3 additions & 0 deletions frontend/test/playwright/e2e/all-results-analytics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { Events } from "#shared/types/analytics"
test.describe.configure({ mode: "parallel" })

const searchResultPayload = {
query: "birds",
kind: "search",
collectionType: "null",
searchType: ALL_MEDIA,
Expand Down Expand Up @@ -55,6 +56,7 @@ test.describe("all results grid analytics test", () => {
const expectedPayload: Events["SELECT_SEARCH_RESULT"] = {
...defaultPayload,
...audioResult,
position: 2,
}
expectEventPayloadToMatch(selectSearchResultEvent, expectedPayload)
})
Expand All @@ -74,6 +76,7 @@ test.describe("all results grid analytics test", () => {
id: "da5cb478-c093-4d62-b721-cda18797e3fb",
mediaType: IMAGE,
provider: "flickr",
position: 1,
}

expectEventPayloadToMatch(selectSearchResultEvent, expectedPayload)
Expand Down
18 changes: 13 additions & 5 deletions frontend/test/playwright/e2e/all-results-keyboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ test.describe.configure({ mode: "parallel" })

const singleResultRegex = (
mediaType: SupportedMediaType,
searchTerm?: string
searchTerm?: string,
position?: number
) => {
const query = searchTerm ? `\\?q=${searchTerm}` : ""
const query = new URLSearchParams()
if (searchTerm) {
query.set("q", searchTerm)
}
if (position) {
query.set("p", position.toString())
}
const queryStr = query.toString() ? `\\?${query}` : ""
return new RegExp(
`/${mediaType}/[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}${query}$`,
`/${mediaType}/[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}${queryStr}$`,
"i"
)
}
Expand All @@ -43,7 +51,7 @@ test.describe(
const mediaType = "image"
await walkToType(mediaType, page)
await page.keyboard.press("Enter")
const urlRegex = singleResultRegex(mediaType, searchTerm)
const urlRegex = singleResultRegex(mediaType, searchTerm, 1)

await page.waitForURL(urlRegex)
expect(page.url()).toMatch(urlRegex)
Expand All @@ -53,7 +61,7 @@ test.describe(
const mediaType = "audio"
await walkToType(mediaType, page)
await page.keyboard.press("Enter")
const urlRegex = singleResultRegex(mediaType, searchTerm)
const urlRegex = singleResultRegex(mediaType, searchTerm, 2)
await page.waitForURL(urlRegex)
expect(page.url()).toMatch(urlRegex)
})
Expand Down
2 changes: 2 additions & 0 deletions frontend/test/playwright/e2e/attribution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ test.describe("attribution", () => {
id: imageId,
format,
mediaType: "image",
query: "",
position: -1,
})
})
})
4 changes: 4 additions & 0 deletions frontend/test/playwright/e2e/audio-detail.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ test("sends GET_MEDIA event on CTA button click", async ({ context, page }) => {
const expectedPayload: Events["GET_MEDIA"] = {
...audioObject,
mediaType: "audio",
query: "",
position: -1,
}
expectEventPayloadToMatch(getMediaEvent, expectedPayload)
})
Expand Down Expand Up @@ -138,6 +140,8 @@ test("sends SELECT_SEARCH_RESULT event on related audio click", async ({
mediaType: "audio",
provider: "wikimedia_audio",
searchType: "all",
query: "",
position: 1,
sensitivities: "",
isBlurred: false,
collectionType: "null",
Expand Down
Loading

0 comments on commit 1d69f30

Please sign in to comment.