mirror of
https://github.com/twitter/the-algorithm.git
synced 2024-06-27 13:36:03 +02:00
ef4c5eb65e
Please note we have force-pushed a new initial commit in order to remove some publicly-available Twitter user information. Note that this process may be required in the future.
138 lines
6.5 KiB
Scala
138 lines
6.5 KiB
Scala
package com.twitter.home_mixer.functional_component.selector
|
|
|
|
import com.twitter.home_mixer.functional_component.decorator.HomeClientEventDetailsBuilder
|
|
import com.twitter.home_mixer.model.HomeFeatures.AncestorsFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.ConversationModule2DisplayedTweetsFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.ConversationModuleHasGapFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.HasRandomTweetFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.IsRandomTweetAboveFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.IsRandomTweetFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.PositionFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.ServedInConversationModuleFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.ServedSizeFeature
|
|
import com.twitter.product_mixer.component_library.model.presentation.urt.UrtItemPresentation
|
|
import com.twitter.product_mixer.component_library.model.presentation.urt.UrtModulePresentation
|
|
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
|
|
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
|
|
import com.twitter.product_mixer.core.functional_component.common.CandidateScope
|
|
import com.twitter.product_mixer.core.functional_component.common.SpecificPipelines
|
|
import com.twitter.product_mixer.core.functional_component.selector.Selector
|
|
import com.twitter.product_mixer.core.functional_component.selector.SelectorResult
|
|
import com.twitter.product_mixer.core.model.common.identifier.CandidatePipelineIdentifier
|
|
import com.twitter.product_mixer.core.model.common.presentation.CandidateWithDetails
|
|
import com.twitter.product_mixer.core.model.common.presentation.ItemCandidateWithDetails
|
|
import com.twitter.product_mixer.core.model.common.presentation.ModuleCandidateWithDetails
|
|
import com.twitter.product_mixer.core.model.marshalling.response.urt.item.tweet.TweetItem
|
|
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
|
|
/**
|
|
* Builds serialized tweet type metrics controller data and updates Client Event Details
|
|
* and Candidate Presentations with this info.
|
|
*
|
|
* Currently only updates presentation of Item Candidates. This needs to be updated
|
|
* when modules are added.
|
|
*
|
|
* This is implemented as a Selector instead of a Decorator in the Candidate Pipeline
|
|
* because we need to add controller data that looks at the final timeline as a whole
|
|
* (e.g. served size, final candidate positions).
|
|
*
|
|
* @param candidatePipelines - only candidates from the specified pipeline will be updated
|
|
*/
|
|
case class UpdateHomeClientEventDetails(candidatePipelines: Set[CandidatePipelineIdentifier])
|
|
extends Selector[PipelineQuery] {
|
|
|
|
override val pipelineScope: CandidateScope = SpecificPipelines(candidatePipelines)
|
|
|
|
private val detailsBuilder = HomeClientEventDetailsBuilder()
|
|
|
|
override def apply(
|
|
query: PipelineQuery,
|
|
remainingCandidates: Seq[CandidateWithDetails],
|
|
result: Seq[CandidateWithDetails]
|
|
): SelectorResult = {
|
|
val selectedCandidates = result.filter(pipelineScope.contains)
|
|
|
|
val randomTweetsByPosition = result
|
|
.map(_.features.getOrElse(IsRandomTweetFeature, false))
|
|
.zipWithIndex.map(_.swap).toMap
|
|
|
|
val resultFeatures = FeatureMapBuilder()
|
|
.add(ServedSizeFeature, Some(selectedCandidates.size))
|
|
.add(HasRandomTweetFeature, randomTweetsByPosition.valuesIterator.contains(true))
|
|
.build()
|
|
|
|
val updatedResult = result.zipWithIndex.map {
|
|
case (item @ ItemCandidateWithDetails(candidate, _, _), position)
|
|
if pipelineScope.contains(item) =>
|
|
val resultCandidateFeatures = FeatureMapBuilder()
|
|
.add(PositionFeature, Some(position))
|
|
.add(IsRandomTweetAboveFeature, randomTweetsByPosition.getOrElse(position - 1, false))
|
|
.build()
|
|
|
|
updateItemPresentation(query, item, resultFeatures, resultCandidateFeatures)
|
|
|
|
case (module @ ModuleCandidateWithDetails(candidates, presentation, features), position)
|
|
if pipelineScope.contains(module) =>
|
|
val resultCandidateFeatures = FeatureMapBuilder()
|
|
.add(PositionFeature, Some(position))
|
|
.add(IsRandomTweetAboveFeature, randomTweetsByPosition.getOrElse(position - 1, false))
|
|
.add(ServedInConversationModuleFeature, true)
|
|
.add(ConversationModule2DisplayedTweetsFeature, module.candidates.size == 2)
|
|
.add(
|
|
ConversationModuleHasGapFeature,
|
|
module.candidates.last.features.getOrElse(AncestorsFeature, Seq.empty).size > 2)
|
|
.build()
|
|
|
|
val updatedItemCandidates =
|
|
candidates.map(updateItemPresentation(query, _, resultFeatures, resultCandidateFeatures))
|
|
|
|
val updatedCandidateFeatures = features ++ resultFeatures ++ resultCandidateFeatures
|
|
|
|
val updatedPresentation = presentation.map {
|
|
case urtModule @ UrtModulePresentation(timelineModule) =>
|
|
val clientEventDetails =
|
|
detailsBuilder(
|
|
query,
|
|
candidates.last.candidate,
|
|
query.features.get ++ updatedCandidateFeatures)
|
|
val updatedClientEventInfo =
|
|
timelineModule.clientEventInfo.map(_.copy(details = clientEventDetails))
|
|
val updatedTimelineModule =
|
|
timelineModule.copy(clientEventInfo = updatedClientEventInfo)
|
|
urtModule.copy(timelineModule = updatedTimelineModule)
|
|
}
|
|
|
|
module.copy(
|
|
candidates = updatedItemCandidates,
|
|
presentation = updatedPresentation,
|
|
features = updatedCandidateFeatures
|
|
)
|
|
|
|
case (any, position) => any
|
|
}
|
|
|
|
SelectorResult(remainingCandidates = remainingCandidates, result = updatedResult)
|
|
}
|
|
|
|
private def updateItemPresentation(
|
|
query: PipelineQuery,
|
|
item: ItemCandidateWithDetails,
|
|
resultCandidateFeatures: FeatureMap,
|
|
resultFeatures: FeatureMap,
|
|
): ItemCandidateWithDetails = {
|
|
val updatedItemCandidateFeatures = item.features ++ resultFeatures ++ resultCandidateFeatures
|
|
|
|
val updatedPresentation = item.presentation.map {
|
|
case urtItem @ UrtItemPresentation(timelineItem: TweetItem, _) =>
|
|
val clientEventDetails =
|
|
detailsBuilder(query, item.candidate, query.features.get ++ updatedItemCandidateFeatures)
|
|
val updatedClientEventInfo =
|
|
timelineItem.clientEventInfo.map(_.copy(details = clientEventDetails))
|
|
val updatedTimelineItem = timelineItem.copy(clientEventInfo = updatedClientEventInfo)
|
|
urtItem.copy(timelineItem = updatedTimelineItem)
|
|
case any => any
|
|
}
|
|
item.copy(presentation = updatedPresentation, features = updatedItemCandidateFeatures)
|
|
}
|
|
}
|