All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.iheart.thomas.abtest.AssignGroups.scala Maven / Gradle / Ivy

There is a newer version: 0.46-RC4
Show newest version
/*
 * Copyright [2018] [iHeartMedia Inc]
 * All rights reserved
 */

package com.iheart.thomas
package abtest

import java.time.OffsetDateTime

import cats.Monad
import cats.implicits._
import com.iheart.thomas.abtest.model.Abtest.Status.InProgress
import com.iheart.thomas.abtest.model._
import lihua.Entity

trait AssignGroups[F[_]] {
  def assign(query: UserGroupQuery)
    : F[(OffsetDateTime, Map[FeatureName, (GroupName, Entity[Abtest])])]
}

object AssignGroups {

  def fromTestsFeatures[F[_]: Monad](
      data: Vector[(Entity[Abtest], Feature)]
  )(implicit eligibilityControl: EligibilityControl[F]): AssignGroups[F] =
    new DefaultAssignGroups[F](
      ofTime =>
        data
          .collect {
            case (et @ Entity(_, test), _) if test.statusAsOf(ofTime) == InProgress => et
          }
          .pure[F],
      fn =>
        data
          .collectFirst {
            case (_, f) if f.name == fn => f
          }
          .pure[F]
    )

  class DefaultAssignGroups[F[_]: Monad](
      testsRetriever: OffsetDateTime => F[Vector[Entity[Abtest]]],
      featureRetriever: FeatureName => F[Option[Feature]]
  )(implicit eligibilityControl: EligibilityControl[F])
      extends AssignGroups[F] {

    def assign(query: UserGroupQuery)
      : F[(OffsetDateTime, Map[FeatureName, (GroupName, Entity[Abtest])])] = {
      val ofTime = query.at.getOrElse(TimeUtil.currentMinute)
      val allTests = testsRetriever(ofTime)
      val targetTests =
        if (query.features.isEmpty) allTests
        else allTests.map(_.filter(t => query.features.contains(t.data.feature)))
      targetTests
        .flatMap(_.traverseFilter { test ⇒
          eligibilityControl.eligible(query, test.data).flatMap {
            eligible =>
              featureRetriever(test.data.feature).map {
                feature =>
                  val idToUse = test.data.idToUse(query)
                  def overriddenGroup = {
                    (feature, idToUse).mapN((f, uid) => f.overrides.get(uid)).flatten
                  }
                  {
                    if (eligible)
                      overriddenGroup orElse {
                        idToUse.flatMap(uid => Bucketing.getGroup(uid, test.data))
                      } else if (feature.fold(false)(_.overrideEligibility))
                      overriddenGroup
                    else
                      None
                  }.map(gn => (test.data.feature, (gn, test)))
              }
          }
        }.map(_.toMap))
        .map((ofTime, _))
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy