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.
98 lines
4.1 KiB
Scala
98 lines
4.1 KiB
Scala
package com.twitter.home_mixer.functional_component.feature_hydrator
|
|
|
|
import com.twitter.gizmoduck.{thriftscala => gt}
|
|
import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.FavoritedByUserIdsFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.FollowedByUserIdsFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.RealNamesFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.ScreenNamesFeature
|
|
import com.twitter.home_mixer.model.HomeFeatures.SourceUserIdFeature
|
|
import com.twitter.home_mixer.model.request.FollowingProduct
|
|
import com.twitter.home_mixer.param.HomeGlobalParams.EnableNahFeedbackInfoParam
|
|
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
|
|
import com.twitter.product_mixer.core.feature.Feature
|
|
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.feature_hydrator.BulkCandidateFeatureHydrator
|
|
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures
|
|
import com.twitter.product_mixer.core.model.common.Conditionally
|
|
import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier
|
|
import com.twitter.product_mixer.core.pipeline.PipelineQuery
|
|
import com.twitter.stitch.Stitch
|
|
import com.twitter.stitch.gizmoduck.Gizmoduck
|
|
import com.twitter.util.Return
|
|
import javax.inject.Inject
|
|
import javax.inject.Singleton
|
|
|
|
protected case class ProfileNames(screenName: String, realName: String)
|
|
|
|
@Singleton
|
|
class NamesFeatureHydrator @Inject() (gizmoduck: Gizmoduck)
|
|
extends BulkCandidateFeatureHydrator[PipelineQuery, TweetCandidate]
|
|
with Conditionally[PipelineQuery] {
|
|
|
|
override val identifier: FeatureHydratorIdentifier = FeatureHydratorIdentifier("Names")
|
|
|
|
override val features: Set[Feature[_, _]] = Set(ScreenNamesFeature, RealNamesFeature)
|
|
|
|
override def onlyIf(query: PipelineQuery): Boolean = query.product match {
|
|
case FollowingProduct => query.params(EnableNahFeedbackInfoParam)
|
|
case _ => true
|
|
}
|
|
|
|
private val queryFields: Set[gt.QueryFields] = Set(gt.QueryFields.Profile)
|
|
|
|
/**
|
|
* The UI currently only ever displays the first 2 names in social context lines
|
|
* E.g. "User and 3 others like" or "UserA and UserB liked"
|
|
*/
|
|
private val MaxCountUsers = 2
|
|
|
|
override def apply(
|
|
query: PipelineQuery,
|
|
candidates: Seq[CandidateWithFeatures[TweetCandidate]]
|
|
): Stitch[Seq[FeatureMap]] = {
|
|
|
|
val candidateUserIdsMap = candidates.map { candidate =>
|
|
candidate.candidate.id ->
|
|
(candidate.features.getOrElse(FavoritedByUserIdsFeature, Nil).take(MaxCountUsers) ++
|
|
candidate.features.getOrElse(FollowedByUserIdsFeature, Nil).take(MaxCountUsers) ++
|
|
candidate.features.getOrElse(AuthorIdFeature, None) ++
|
|
candidate.features.getOrElse(SourceUserIdFeature, None)).distinct
|
|
}.toMap
|
|
|
|
val distinctUserIds = candidateUserIdsMap.values.flatten.toSeq.distinct
|
|
|
|
Stitch
|
|
.collectToTry(distinctUserIds.map(userId => gizmoduck.getUserById(userId, queryFields)))
|
|
.map { allUsers =>
|
|
val idToProfileNamesMap = allUsers.flatMap {
|
|
case Return(allUser) =>
|
|
allUser.profile
|
|
.map(profile => allUser.id -> ProfileNames(profile.screenName, profile.name))
|
|
case _ => None
|
|
}.toMap
|
|
|
|
val validUserIds = idToProfileNamesMap.keySet
|
|
|
|
candidates.map { candidate =>
|
|
val combinedMap = candidateUserIdsMap
|
|
.getOrElse(candidate.candidate.id, Nil)
|
|
.flatMap {
|
|
case userId if validUserIds.contains(userId) =>
|
|
idToProfileNamesMap.get(userId).map(profileNames => userId -> profileNames)
|
|
case _ => None
|
|
}
|
|
|
|
val perCandidateRealNameMap = combinedMap.map { case (k, v) => k -> v.realName }.toMap
|
|
val perCandidateScreenNameMap = combinedMap.map { case (k, v) => k -> v.screenName }.toMap
|
|
|
|
FeatureMapBuilder()
|
|
.add(ScreenNamesFeature, perCandidateScreenNameMap)
|
|
.add(RealNamesFeature, perCandidateRealNameMap)
|
|
.build()
|
|
}
|
|
}
|
|
}
|
|
}
|