sport/app/football/model/matches.scala (181 lines of code) (raw):

package football.model import com.madgag.scala.collection.decorators.MapDecorator import common.{Edition, editions} import football.collections.RichList import football.datetime.DateHelpers import implicits.Football import model.Competition import pa.{FootballMatch, Round} import play.api.mvc.RequestHeader import java.time.format.DateTimeFormatter import java.time.{LocalDate, ZonedDateTime} trait MatchesList extends Football with RichList { val competitions: Seq[Competition] val baseUrl: String val pageType: String val date: LocalDate val daysToDisplay: Int lazy val isEmpty = allRelevantMatches.isEmpty def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean // ordering for the displayed matches def timeComesFirstInList(d: ZonedDateTime, other: ZonedDateTime): Boolean def dateComesFirstInList(d: LocalDate, other: LocalDate): Boolean = timeComesFirstInList( d.atStartOfDay(DateHelpers.defaultFootballZoneId), other.atStartOfDay(DateHelpers.defaultFootballZoneId), ) private lazy val allRelevantMatches: List[(FootballMatch, Competition)] = { val matchesWithCompetition = for { competition <- competitions matches = competition.matches .filter(fMatch => filterMatches(fMatch, competition)) fMatch <- matches } yield (fMatch, competition) matchesWithCompetition.sortWith { case ((fMatch1, _), (fMatch2, _)) => timeComesFirstInList(fMatch1.date, fMatch2.date) } } lazy val matchDates = allRelevantMatches.map { case (fMatch, _) => fMatch.date.toLocalDate }.distinct // the subset of football matches to display for the given date lazy val relevantMatches: List[(FootballMatch, Competition)] = { val startDate = date val matchDates = allRelevantMatches.map { case (fMatch, _) => fMatch.date.toLocalDate }.distinct val eligibleDates = matchDates.dropWhile(dateComesFirstInList(_, startDate)).take(daysToDisplay) allRelevantMatches.filter { case (fMatch, _) => eligibleDates.contains(fMatch.date.toLocalDate) } } def matchesGroupedByDate(implicit request: RequestHeader) = { relevantMatches.segmentBy(key = _._1.date.withZoneSameInstant(Edition(request).timezoneId).toLocalDate) } def matchesGroupedByDateAndCompetition(implicit request: RequestHeader, ): Seq[(LocalDate, List[(Competition, List[FootballMatch])])] = matchesGroupedByDate.map { case (d, ms) => val competitionsWithMatches = ms .groupBy(_._2) .mapV(_.map { case (matches, _) => matches }) .toList .sortWith { case ((comp1, matches1), (comp2, matches2)) => val competitionOrder = competitions.map(_.id).toList competitionOrder.indexOfOpt(comp1.id).getOrElse(competitionOrder.size) < competitionOrder .indexOfOpt(comp2.id) .getOrElse(competitionOrder.size) } (d, competitionsWithMatches) } lazy val nextPage: Option[String] = { val nextMatchDate = matchDates.dropWhile(dateComesFirstInList(_, date)).drop(daysToDisplay).headOption nextMatchDate.map(s"$baseUrl/more/" + _.format(DateTimeFormatter.ofPattern("yyyy/MMM/dd"))) } lazy val previousPage: Option[String] = { val nextMatchDate = matchDates.takeWhile(dateComesFirstInList(_, date)).lastOption nextMatchDate.map(s"$baseUrl/" + _.format(DateTimeFormatter.ofPattern("yyyy/MMM/dd"))) } def getPageTitle(edition: Edition): String = { pageType match { case "live" => "Live football scores" case "matches" => "Football scores" case "fixtures" if (edition == editions.Us) => "Soccer schedules" case "fixtures" => "Football fixtures" case "results" => "Football results" case _ => pageType } } } trait Fixtures extends MatchesList { override val baseUrl: String = "/football/fixtures" override val pageType = "fixtures" // ordering for the displayed matches override def timeComesFirstInList(d: ZonedDateTime, other: ZonedDateTime): Boolean = d.isBefore(other) } trait Results extends MatchesList { override val baseUrl: String = "/football/results" override val pageType = "results" override def timeComesFirstInList(d: ZonedDateTime, other: ZonedDateTime): Boolean = d.isAfter(other) } trait MatchDays extends MatchesList { override val baseUrl: String = "/football/live" override val pageType = if (LocalDate.now == date) "live" else "matches" override val daysToDisplay = 1 override lazy val nextPage: Option[String] = None override lazy val previousPage: Option[String] = None override def timeComesFirstInList(d: ZonedDateTime, other: ZonedDateTime): Boolean = d.isBefore(other) } trait TeamList { val teamId: String } trait CompetitionList { val competitions: Seq[Competition] val competitionId: String lazy val competition = competitions.find(_.id == competitionId) } case class FixturesList(date: LocalDate, competitions: Seq[Competition]) extends Fixtures { override val daysToDisplay = 3 override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = fMatch.isFixture } case class CompetitionFixturesList( date: LocalDate, competitions: Seq[Competition], competitionId: String, competitionTag: String, ) extends Fixtures with CompetitionList { override val daysToDisplay = 20 override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = competition.id == competitionId && fMatch.isFixture override val baseUrl: String = s"/football/$competitionTag/fixtures" } case class TeamFixturesList( date: LocalDate, competitions: Seq[Competition], teamId: String, teamTag: String, daysToDisplay: Int = 20, ) extends Fixtures with TeamList { override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = fMatch.isFixture && fMatch.hasTeam(teamId) override val baseUrl: String = s"/football/$teamTag/fixtures" } case class ResultsList(date: LocalDate, competitions: Seq[Competition]) extends Results { override val daysToDisplay = 3 override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = fMatch.isResult } case class CompetitionResultsList(date: LocalDate, competitions: Seq[Competition], competitionId: String) extends Results with CompetitionList { override val baseUrl: String = competition.fold("/football/results")(_.url + "/results") override val daysToDisplay = 20 override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = competition.id == competitionId && fMatch.isResult } case class TeamResultsList( date: LocalDate, competitions: Seq[Competition], teamId: String, teamUrl: Option[String] = None, ) extends Results with TeamList { override val baseUrl: String = teamUrl.fold("/football/results")(url => s"$url/results") override val daysToDisplay = 20 override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = fMatch.isResult && fMatch.hasTeam(teamId) } case class MatchDayList(competitions: Seq[Competition], date: LocalDate) extends MatchDays { override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = fMatch.date.toLocalDate == date } case class CompetitionMatchDayList(competitions: Seq[Competition], competitionId: String, date: LocalDate) extends MatchDays with CompetitionList { override def filterMatches(fMatch: FootballMatch, competition: Competition): Boolean = fMatch.date.toLocalDate == date && competition.id == competitionId } case class CompetitionRoundMatchesList(competitions: Seq[Competition], competition: Competition, round: Round) extends MatchDays { override val daysToDisplay = 1000 override lazy val date = competition.startDate.getOrElse(LocalDate.now) override def filterMatches(fMatch: FootballMatch, matchCompetition: Competition): Boolean = competition.id == matchCompetition.id && fMatch.round == round }