the-algorithm/home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/gate/DismissFatigueGate.scala

49 lines
1.9 KiB
Scala

package com.twitter.home_mixer.functional_component.gate
import com.twitter.conversions.DurationOps._
import com.twitter.product_mixer.core.feature.Feature
import com.twitter.product_mixer.core.functional_component.gate.Gate
import com.twitter.product_mixer.core.model.common.identifier.GateIdentifier
import com.twitter.product_mixer.core.pipeline.PipelineQuery
import com.twitter.stitch.Stitch
import com.twitter.timelinemixer.clients.manhattan.DismissInfo
import com.twitter.timelineservice.suggests.thriftscala.SuggestType
import com.twitter.util.Duration
object DismissFatigueGate {
// how long a dismiss action from user needs to be respected
val DefaultBaseDismissDuration = 7.days
val MaximumDismissalCountMultiplier = 4
}
case class DismissFatigueGate(
suggestType: SuggestType,
dismissInfoFeature: Feature[PipelineQuery, Map[SuggestType, Option[DismissInfo]]],
baseDismissDuration: Duration = DismissFatigueGate.DefaultBaseDismissDuration,
) extends Gate[PipelineQuery] {
override val identifier: GateIdentifier = GateIdentifier("DismissFatigue")
override def shouldContinue(query: PipelineQuery): Stitch[Boolean] = {
val dismissInfoMap = query.features.map(
_.getOrElse(dismissInfoFeature, Map.empty[SuggestType, Option[DismissInfo]]))
val isVisible = dismissInfoMap
.flatMap(_.get(suggestType))
.flatMap(_.map { info =>
val currentDismissalDuration = query.queryTime.since(info.lastDismissed)
val targetDismissalDuration = dismissDurationForCount(info.count, baseDismissDuration)
currentDismissalDuration > targetDismissalDuration
}).getOrElse(true)
Stitch.value(isVisible)
}
private def dismissDurationForCount(
dismissCount: Int,
dismissDuration: Duration
): Duration =
// limit to maximum dismissal duration
dismissDuration * Math.min(dismissCount, DismissFatigueGate.MaximumDismissalCountMultiplier)
}