Skip to content

Commit

Permalink
Bug 1866992 - Add product recommendations exposure telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
rahulsainani authored and mergify[bot] committed Nov 28, 2023
1 parent ca18c9b commit daf7931
Show file tree
Hide file tree
Showing 16 changed files with 409 additions and 25 deletions.
42 changes: 42 additions & 0 deletions fenix/app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10942,6 +10942,48 @@ shopping:
metadata:
tags:
- Shopping
ads_exposure:
type: event
description: |
On a supported product page, the review checker showed analysis,
and the ads exposure pref was enabled, or review checker ads were enabled,
and when we tried to fetch an ad from the ad server, an ad was available.
Does not indicate whether the ad was actually shown.
send_in_pings:
- events
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1866992
data_reviews:
- https://github.com/mozilla-mobile/firefox-android/pull/4622#issuecomment-1829905076
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: never
metadata:
tags:
- Shopping
surface_no_ads_available:
type: event
description: |
On a supported product page, the review checker
showed analysis, and review checker ads were enabled,
but when we tried to fetch an ad from the ad server,
no ad was available.
send_in_pings:
- events
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1866992
data_reviews:
- https://github.com/mozilla-mobile/firefox-android/pull/4622#issuecomment-1829905076
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: never
metadata:
tags:
- Shopping

shopping.settings:
component_opted_out:
Expand Down
5 changes: 5 additions & 0 deletions fenix/app/nimbus.fml.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,16 @@ features:
description: if true, recommended products feature is enabled to be shown to the user based on their preference.
type: Boolean
default: false
product-recommendations-exposure:
description: if true, we want to record recommended products inventory for opted-in users, even if product recommendations are disabled.
type: Boolean
default: false
defaults:
- channel: developer
value:
enabled: true
product-recommendations: true
product-recommendations-exposure: true

print:
description: A feature for printing from the share or browser menu.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ interface ShoppingExperienceFeature {
* Returns true if the shopping experience feature is enabled.
*/
val isEnabled: Boolean

/**
* Returns true if product recommendations exposure nimbus flag is enabled.
*/
val isProductRecommendationsExposureEnabled: Boolean
}

/**
Expand All @@ -24,4 +29,7 @@ class DefaultShoppingExperienceFeature : ShoppingExperienceFeature {

override val isEnabled
get() = FxNimbus.features.shoppingExperience.value().enabled

override val isProductRecommendationsExposureEnabled: Boolean
get() = FxNimbus.features.shoppingExperience.value().productRecommendationsExposure
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import kotlinx.coroutines.CoroutineScope
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.tabs.TabsUseCases
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.shopping.DefaultShoppingExperienceFeature
import org.mozilla.fenix.shopping.middleware.DefaultNetworkChecker
import org.mozilla.fenix.shopping.middleware.DefaultReviewQualityCheckPreferences
import org.mozilla.fenix.shopping.middleware.DefaultReviewQualityCheckService
Expand Down Expand Up @@ -58,6 +59,7 @@ object ReviewQualityCheckMiddlewareProvider {
reviewQualityCheckPreferences = DefaultReviewQualityCheckPreferences(settings),
reviewQualityCheckVendorsService = DefaultReviewQualityCheckVendorsService(browserStore),
appStore = appStore,
shoppingExperienceFeature = DefaultShoppingExperienceFeature(),
scope = scope,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,15 @@ class ReviewQualityCheckNetworkMiddleware(
private suspend fun Store<ReviewQualityCheckState, ReviewQualityCheckAction>.updateRecommendedProductState() {
val currentState = state
if (currentState is ReviewQualityCheckState.OptedIn &&
currentState.productRecommendationsPreference == true
(currentState.productRecommendationsExposure || (currentState.productRecommendationsPreference == true))
) {
reviewQualityCheckService.productRecommendation().toRecommendedProductState().also {
dispatch(UpdateRecommendedProduct(it))
val productRecommendation = reviewQualityCheckService.productRecommendation(
currentState.productRecommendationsPreference ?: false,
)
if (currentState.productRecommendationsPreference == true) {
productRecommendation.toRecommendedProductState().also {
dispatch(UpdateRecommendedProduct(it))
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.mozilla.fenix.components.appstate.AppAction.ShoppingAction.InfoCardEx
import org.mozilla.fenix.components.appstate.AppAction.ShoppingAction.SettingsCardExpanded
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.components.appstate.shopping.ShoppingState.CardState
import org.mozilla.fenix.shopping.ShoppingExperienceFeature
import org.mozilla.fenix.shopping.store.ReviewQualityCheckAction
import org.mozilla.fenix.shopping.store.ReviewQualityCheckMiddleware
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState
Expand All @@ -29,12 +30,14 @@ import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.OptedIn
* @param reviewQualityCheckVendorsService The [ReviewQualityCheckVendorsService] instance for
* getting the list of product vendors.
* @param appStore The [AppStore] instance for dispatching [ShoppingAction]s.
* @param shoppingExperienceFeature The [ShoppingExperienceFeature] instance to get feature flags.
* @param scope The [CoroutineScope] to use for launching coroutines.
*/
class ReviewQualityCheckPreferencesMiddleware(
private val reviewQualityCheckPreferences: ReviewQualityCheckPreferences,
private val reviewQualityCheckVendorsService: ReviewQualityCheckVendorsService,
private val appStore: AppStore,
private val shoppingExperienceFeature: ShoppingExperienceFeature,
private val scope: CoroutineScope,
) : ReviewQualityCheckMiddleware {

Expand Down Expand Up @@ -75,6 +78,8 @@ class ReviewQualityCheckPreferencesMiddleware(

ReviewQualityCheckAction.OptInCompleted(
isProductRecommendationsEnabled = isProductRecommendationsEnabled,
productRecommendationsExposure =
shoppingExperienceFeature.isProductRecommendationsExposureEnabled,
productVendor = reviewQualityCheckVendorsService.productVendor(),
isHighlightsExpanded = savedCardState.isHighlightsExpanded,
isInfoExpanded = savedCardState.isInfoExpanded,
Expand All @@ -95,6 +100,8 @@ class ReviewQualityCheckPreferencesMiddleware(
store.dispatch(
ReviewQualityCheckAction.OptInCompleted(
isProductRecommendationsEnabled = isProductRecommendationsEnabled,
productRecommendationsExposure =
shoppingExperienceFeature.isProductRecommendationsExposureEnabled,
productVendor = reviewQualityCheckVendorsService.productVendor(),
isHighlightsExpanded = false,
isInfoExpanded = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.shopping.ProductAnalysis
import mozilla.components.concept.engine.shopping.ProductRecommendation
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.GleanMetrics.Shopping
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

Expand Down Expand Up @@ -50,7 +51,7 @@ interface ReviewQualityCheckService {
*
* @return [ProductRecommendation] if request succeeds, null otherwise.
*/
suspend fun productRecommendation(): ProductRecommendation?
suspend fun productRecommendation(shouldRecordAvailableTelemetry: Boolean): ProductRecommendation?

/**
* Sends a click attribution event for a given product aid.
Expand Down Expand Up @@ -128,13 +129,20 @@ class DefaultReviewQualityCheckService(
override fun selectedTabUrl(): String? =
browserStore.state.selectedTab?.content?.url

override suspend fun productRecommendation(): ProductRecommendation? =
override suspend fun productRecommendation(shouldRecordAvailableTelemetry: Boolean): ProductRecommendation? =
withContext(Dispatchers.Main) {
suspendCoroutine { continuation ->
browserStore.state.selectedTab?.let { tab ->
tab.engineState.engineSession?.requestProductRecommendations(
url = tab.content.url,
onResult = {
if (it.isEmpty()) {
if (shouldRecordAvailableTelemetry) {
Shopping.surfaceNoAdsAvailable.record()
}
} else {
Shopping.adsExposure.record()
}
// Return the first available recommendation since ui requires only
// one recommendation.
continuation.resume(it.firstOrNull())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ sealed interface ReviewQualityCheckAction : Action {
*
* @property isProductRecommendationsEnabled Reflects the user preference update to display
* recommended product. Null when product recommendations feature is disabled.
* @property productRecommendationsExposure Whether product recommendations exposure is enabled.
* @property productVendor The vendor of the product.
* @property isHighlightsExpanded Whether the highlights card should be expanded.
* @property isInfoExpanded Whether the info card should be expanded.
* @property isSettingsExpanded Whether the settings card should be expanded.
*/
data class OptInCompleted(
val isProductRecommendationsEnabled: Boolean?,
val productRecommendationsExposure: Boolean,
val productVendor: ReviewQualityCheckState.ProductVendor,
val isHighlightsExpanded: Boolean,
val isInfoExpanded: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ sealed interface ReviewQualityCheckState : State {
* @property productRecommendationsPreference User preference whether to show product
* recommendations. True if product recommendations should be shown. Null indicates that product
* recommendations are disabled.
* @property productRecommendationsExposure Whether product recommendations exposure is enabled.
* @property productVendor The vendor of the product.
* @property isSettingsExpanded Whether or not the settings card is expanded.
* @property isInfoExpanded Whether or not the info card is expanded.
Expand All @@ -49,6 +50,7 @@ sealed interface ReviewQualityCheckState : State {
data class OptedIn(
val productReviewState: ProductReviewState = ProductReviewState.Loading,
val productRecommendationsPreference: Boolean?,
val productRecommendationsExposure: Boolean,
val productVendor: ProductVendor,
val isSettingsExpanded: Boolean = false,
val isInfoExpanded: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ private fun mapStateForUpdateAction(
if (state is ReviewQualityCheckState.OptedIn) {
state.copy(
productRecommendationsPreference = action.isProductRecommendationsEnabled,
productRecommendationsExposure = action.productRecommendationsExposure,
isHighlightsExpanded = action.isHighlightsExpanded,
isInfoExpanded = action.isInfoExpanded,
isSettingsExpanded = action.isSettingsExpanded,
)
} else {
ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = action.isProductRecommendationsEnabled,
productRecommendationsExposure = action.productRecommendationsExposure,
productVendor = action.productVendor,
isHighlightsExpanded = action.isHighlightsExpanded,
isInfoExpanded = action.isInfoExpanded,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ReviewQualityCheckBottomSheetStateFeatureTest {
store.dispatch(
ReviewQualityCheckAction.OptInCompleted(
isProductRecommendationsEnabled = true,
productRecommendationsExposure = true,
productVendor = ReviewQualityCheckState.ProductVendor.WALMART,
isHighlightsExpanded = false,
isInfoExpanded = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class FakeReviewQualityCheckService(
private val reanalysis: AnalysisStatusDto? = null,
private val status: AnalysisStatusDto? = null,
private val selectedTabUrl: String? = null,
private val productRecommendation: ProductRecommendation? = null,
private val productRecommendation: () -> ProductRecommendation? = { null },
private val recordClick: (String) -> Unit = {},
private val recordImpression: (String) -> Unit = {},
) : ReviewQualityCheckService {
Expand All @@ -33,7 +33,9 @@ class FakeReviewQualityCheckService(

override fun selectedTabUrl(): String? = selectedTabUrl

override suspend fun productRecommendation(): ProductRecommendation? = productRecommendation
override suspend fun productRecommendation(shouldRecordAvailableTelemetry: Boolean): ProductRecommendation? {
return productRecommendation.invoke()
}

override suspend fun recordRecommendedProductClick(productAid: String) {
recordClick(productAid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import org.mozilla.fenix.shopping.ShoppingExperienceFeature

class FakeShoppingExperienceFeature(
private val enabled: Boolean = true,
private val productRecommendationsExposureEnabled: Boolean = true,
) : ShoppingExperienceFeature {

override val isEnabled: Boolean
get() = enabled

override val isProductRecommendationsExposureEnabled: Boolean
get() = productRecommendationsExposureEnabled
}

0 comments on commit daf7931

Please sign in to comment.