# Prudsys View Tracking ## Product view event endpoint https://{{host}}/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/productview?pid={{pid}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}} **The parameters used are :** * **`tracking`**: a flag indicating if tracking is allowed by the user * **`sid`**: session id * **`userid`**: user id * **`pid`**: id of viewed product **The product view event should be sent every time the user opens a product detail page.** ### Steps: 1. Modify the `RecommendationService` class * Add `trackProductViewEvent` function ```kotlin= interface RecommendationService { @GET("/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/productview?pid={{pid}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}}") suspend fun trackProductViewEvent( @Query(IS_TRACKING_ENABLED) tracking: Boolean, @Query(SESSION_ID) sessionId: String, @Query(USER_ID) userId: String?, @Query(PRODUCT_ID) productId: Long, ): Response companion object { const val IS_TRACKING_ENABLED = "tracking" const val SESSION_ID = "sid" const val USER_ID = "userid" const val PRODUCT_ID = "pid" } } ``` 2. We need to expose a new function to be able to track product view event * Add `recommendationServiceCreator` to the `TrackingRepositoryImpl` and declare it in the `RepositoryModule` * Override `trackProductViewEvent` function ```kotlin= private suspend fun trackProductViewEvent( sku: Long, ) { recommendationsServiceCreator().run { trackProductViewEvent( tracking = isTrackingEnabled(), sessionId = getSessionIdOrThrow(), userid = sessionDataSource.getCurrentSession()?.customerId, productId = sku, ) } ``` 3. We need to expose a new UseCase to be able to track product view events * Create `TrackRecommendationProductViewEventUseCase` ```kotlin= class TrackProductViewEventsforRecommendationsUseCase( private val trackingRepository: TrackingRepository ) : SuspendingUseCase<Long, Unit> { override suspend fun execute(pdpSku: Long) { trackingRepository.trackProductViewEvent(pdpSku) } } ``` 4. Create a`SideEffect` in `ProductAction` to track the product view for recommendations: ```kotlin= object TrackProductViewForRecommendations : ProductDetailsSideEffect() { override suspend fun performSideEffect(getCurrentState: () -> ProductDetailState) { val completeState = getCurrentState().asProductLoadCompleteStateOrNull() ?: return injectValue<TrackProductViewEventsforRecommendationsUseCase>().execute(completeState.product.sku.base) } } ``` --- ## Category view event endpoint https://{{host}}/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/categoryview?cid={{cid}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}} **The parameters used are :** * **`tracking`**: a flag indicating if tracking is allowed by the user * **`sid`**: session id * **`userid`**: user id * **`cid`**: category id **The category view event should be sent if the user opens a category overview page** ### Steps: 1. Add `trackCategoryViewEvent` function in `ReccomendationService` class ```kotlin= interface ReccomendationService { @GET("/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/categoryview?cid={{cid}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}}") suspend fun trackCategoryViewEvent( @Query(IS_TRACKING_ENABLED) tracking: Boolean, @Query(SESSION_ID) sessionId: String, @Query(USER_ID) userId: String?, @Query(CATEGORY_ID) categoryId: Long, ): Response companion object { const val IS_TRACKING_ENABLED = "tracking" const val SESSION_ID = "sid" const val USER_ID = "userid" const val CATEGORY_ID = "cid" } } ``` 2. We need to expose a new function to be able to track product view event * Override `trackCategoryViewEvent` function in `TrackingRepositoryImpl` ```kotlin= private suspend fun trackCategoryViewEvent( categoryId: Long, ) { productViewEventServiceCreator().runCatching { trackCategoryViewEvent( tracking = isTrackingEnabled(), sessionId = getSessionIdOrThrow(), userId = sessionDataSource.getCurrentSession()?.customerId, categoryId = categoryId, ) }.getOrThrow() } ``` 4. We need to expose a new UseCase to be able to track product view events * Create `TrackCategoryViewForRecommendationsUseCase` ```kotlin= class TrackCategoryViewForRecommendationsUseCase( private val trackingRepository: TrackingRepository ) : SuspendingUseCase<Long, Unit> { override suspend fun execute(categoryId: Long) { trackingRepository.trackCategoryViewEvent(categoryId) } } ``` 5. We should call a side-effect when navigating to search results ```kotlin= object TrackCategoryViewForRecommendations : CategoriesSideEffect() { override suspend fun performSideEffect(getCurrentState: () -> OldCategoriesState) { val navigationTarget = getCurrentState().let { it as? OldCategoriesState.Complete }?.navigationTarget?.let { it as? OldCategoriesState.Navigation.SearchResults } ?: return injectValue<TrackCategoryViewForRecommendationsUseCase>().execute(navigationTarget.categoryId) } } ``` --- ## Basket event endpoint https://{{host}}/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/basket?pids={{pids}}&quantities={{quantities}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}} **The parameters used are :** * **`tracking`**: a flag indicating if tracking is allowed by the user * **`sid`**: session id * **`userid`**: user id * **`pids`**: IDs of products added to cart * **`quantities`**: quantities of products added to cart **The basket event should be sent after a product is added to the basket** ### Steps: 1. Add `trackBasketEvent` function in `RecommendationService` class ```kotlin= interface RecommendationService { @GET("/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/evet/basket?pids={{pids}}&quantities={{quantities}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}}") suspend fun trackBasketEvent( @Query(IS_TRACKING_ENABLED) tracking: Boolean, @Query(SESSION_ID) sessionId: String, @Query(USER_ID) userId: String?, @Query(PRODUCT_ID) productIDs: String, @Query(QUANTITIES) quantities: Int, ): Response companion object { const val IS_TRACKING_ENABLED = "tracking" const val SESSION_ID = "sid" const val USER_ID = "userid" const val PRODUCT_ID = "pids" const val QUANTITIES = "quantities" } } ``` 2. We need to expose a new function to be able to track product view event * Create `RecommendationTrackingInformation` sealed class ```kotlin= sealed class RecommendationTrackingInformation { abstract val productIds: List<Long>, abstract val quantities: List<Int>, data class Basket( override val productIds: List<Long>, override val quantities: List<Int>, ) } ``` * Override `trackBasketEvent` function in `TrackingRepositoryImpl` ```kotlin= private suspend fun trackBasketEvent( basketRecommendationTrackingInformation: RecommendationTrackingInformation.Bakset, ) { basketEventServiceCreator().runCatching { trackBasketEvent( tracking = isTrackingEnabled(), sessionId = getSessionIdOrThrow(), userId = sessionDataSource.getCurrentSession()?.customerId, productIDs = basketRecommendationTrackingInformation.productIDs, quantites = basketRecommendationTrackingInformation.productQuantities, ) }.getOrThrow() } ``` 3. We need to expose a new UseCase to be able to track product view events * Create `TrackBasketEventForRecommendationsUseCase` ```kotlin= class TrackBasketEventForRecommendationsUseCase( private val trackingRepository: TrackingRepository ) : SuspendingUseCase<RecommendationTrackingInformation.Basket, Unit> { override suspend fun execute(basketTrackingInfo: RecommendationTrackingInformation.Basket) { trackingRepository.trackBasketEvent(basketTrackingInfo) } } ``` 4. Track `addToBasket` event from **PDP** and **Wishlist** * For **PDP**: * In the `ProdutDetailAction` in `TrackEecEvent.AddToBasket` SideEffect call the use case: `TrackBasketEventForRecommendationsUseCase` * For the **wishlist**: * Create a SideEffect in `SearchSuggestionAction` ```kotlin= sealed class WishListSideEffect: SideEffect<WishListState> { data class TrackAddToBasketForRecommendations(private val sku: Long) : WishListSideEffect() { override suspend fun performSideEffect(getCurrentState: () -> WishListState) { injectValue<TrackBasketEventForRecommendationsUseCase>().execute(BasketProductsDetails(sku = skutoString(), quantity = 1 //This should be a constant)) } } } ``` --- ## Order event endpoint https://{{host}}/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/order?pids={{pids}}&quantities={{quantities}}&totals={{totals}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}} **The parameters used are :** * **`tracking`**: a flag indicating if tracking is allowed by the user * **`sid`**: session id * **`userid`**: user id * **`pids`**: IDs of products added to cart, separated by comma if multiple * **`quantities`**: quantities of products added to cart, separated by comma if * **`totals`** : prices of products included in order, separated by comma if multiple **The order event should be sent after the order confirmation.** ### Steps: 1. Add `trackOrderEvent` function in `RecommendationService` class ```kotlin= interface RecommendationService { @GET("/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/order?pids={{pids}}&quantities={{quantities}}&totals={{totals}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}}") suspend fun trackOrderEvent( @Query(IS_TRACKING_ENABLED) tracking: Boolean, @Query(SESSION_ID) sessionId: String, @Query(USER_ID) userId: String?, @Query(PRODUCT_ID) productIDs: String, @Query(QUANTITIES) quantities: String, @Query(TOTALS) totals: String ): Response companion object { const val IS_TRACKING_ENABLED = "tracking" const val SESSION_ID = "sid" const val USER_ID = "userid" const val PRODUCT_ID = "pids" const val QUANTITIES = "quantities" const val TOTALS = "totals" } } ``` 2. We need to expose a new function to be able to track product view event * Modify sealed class `RecommendationTrackingInformation`, add `Order` data class ```kotlin= sealed class RecommendationTrackingInformation { ... data class Order( override val productIds: List<Long>, override val quantities: List<Int>, val totals: List<String>, ) } ``` * Override `trackOrderEvent` function ```kotlin= private suspend fun trackBasketEvent( orderRecommendationTrackingInformation: RecommendationTrackingInformation.Order, ) { orderEventServiceCreator().runCatching { trackOrederEvent( tracking = isTrackingEnabled(), sessionId = getSessionIdOrThrow(), userId = sessionDataSource.getCurrentSession()?.customerId, productIDs = orderRecommendationTrackingInformation.productIds.joinToString(), quantites = orderRecommendationTrackingInformation.productQuantities.joinToString(), totals = orderRecommendationTrackingInformation.totals.joinToString() ) }.getOrThrow() } ``` 4. We need to expose a new UseCase to be able to track product view events * Create `TrackOrederEventForRecommendationsUseCase` ```kotlin= class TrackOrederEventForRecommendationsUseCase( private val trackingRepository: TrackingRepository ) : SuspendingUseCase<RecommendationTrackingInformation.Order, Unit> { override suspend fun execute(orderRecommendationTrackingInformation: RecommendationTrackingInformation.Order) { trackingRepository.trackOrderEvent(orderRecommendationTrackingInformation) } } ``` 5. Event should be dispatched when `onViewCreated` is called from the `CheckoutConfirmationFragment` * Create a `trackOrderEvent()` function in the `CheckoutConfirmationViewModel` * Add a mapper to convert from `BasketItemUiModel` to `RecommendationTrackingInformation.Order` * Parameters can be found in the `CheckoutConfirmationFragment` args * Check the existing `trackScreen()` function --- ## Search event endpoint https://{{host}}/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/search?searchterm={{searchterm}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}} **The parameters used are :** * **`tracking`**: a flag indicating if tracking is allowed by the user * **`sid`**: session id * **`userid`**: user id * **`searchterm`** : keyword searched by user Before sending search term it should be normalize as described in these steps: * cast all characters into lower-case * replace all blanks with minus "-" * the normalized string should be truncated after 30 characters * drop any pipe-characater (|) in case it's part of the searchstring * drop the following characters: ▪ "%" ▪ "?" ▪ "&" ▪ "\" ▪ "/" ▪ ": iOS example: ```swift= var normalizedPrudsysSearch: String { var normalizedString = self normalizedString = normalizedString.lowercased().replacingOccurrences(of: " ", with: "-") let excludedCharacters: Set<Character> = ["|", "%", "?", "&", "\\", "/", ":"] normalizedString.removeAll(where: { excludedCharacters.contains($0) }) return String(normalizedString.prefix(30)) } ``` **The search event should be sent every time the user did a search by keyword** ### Steps: 1. Add `trackSearchEvent` function in `RecommendationService` class ```kotlin= interface RecommendationService { @GET("/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/search?searchterm={{searchterm}}&sid={{sid}}&userid={{userid}}&tracking={{tracking}}") suspend fun trackSearchEvent( @Query(IS_TRACKING_ENABLED) tracking: Boolean, @Query(SESSION_ID) sessionId: String, @Query(USER_ID) userId: String?, @Query(SEARCH_TERM) searchterm: String, ): Response companion object { const val IS_TRACKING_ENABLED = "tracking" const val SESSION_ID = "sid" const val USER_ID = "userid" const val SEARCH_TERM = "searchterm" } } ``` 2. We need to expose a new function to be able to track product view event * Override `trackSearchEvent` function in `TrackingRepositoryImpl` ```kotlin= private suspend fun trackSearchEvent( searchTerm: String ) { searchEventServiceCreator().runCatching { trackSearchEvent( tracking = isTrackingEnabled(), sessionId = getSessionIdOrThrow(), userId = sessionDataSource.getCurrentSession()?.customerId, searchTerm = searchTerm ) }.getOrThrow() } ``` 3. Create `SearchTermToRecommendationsNormalizedStringMapper` class and tests: * The normalized string should be truncated after 30 characters * Drop any pipe characater | in case it's part of the searchstring * Drop the following characters: ▪ "%" ▪ "?" ▪ "&" ▪ "\" ▪ "/" ▪ ":" ```kotlin= val invalidCharacterRegex = Regex("""%?&\/:|""") ``` 4. We need to expose a new UseCase to be able to track product view events * Create `TrackSearchEventForRecommendationsUseCase` ```kotlin= class TrackSearchEventForRecommendationsUseCase( private val trackingRepository: TrackingRepository ) : SuspendingUseCase<String, Unit> { override suspend fun execute(searchTerm: String) { trackingRepository.trackSearchEvent(searchTerm) } } ``` 5. Create a new SideEffect to track the search term when navigating to the search results screen: ```kotlin= data class TrackSearchTermForRecommendations(private val navigationTarget: SearchSuggestionState.SearchSuggestionNavigationTarget) : SearchSuggestionSideEffect() { override suspend fun performSideEffect(getCurrentState: () -> SearchSuggestionState) { val searchTermNavigationTarget = navigationTarget.let { it as? SearchSuggestionState.SearchSuggestionNavigationTarget.SearchResults.SearchTerm } ?: return injectValue<TrackSearchEventForRecommendationsUseCase>().execute(searchResultsNavigationTarget.text) } } ``` ---