# 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)
}
}
```
---