Skip to content

Commit

Permalink
#480 web,front: League info is not calculated when any team was repla…
Browse files Browse the repository at this point in the history
…ced in the middle of the season
  • Loading branch information
Blackmorse committed Sep 18, 2024
1 parent 311fd53 commit 40b7cd5
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 38 deletions.
2 changes: 1 addition & 1 deletion front/src/leagueunit/TeamPositionsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const TeamPositionsChart = (leagueUnitPropsWrapper: {leagueUnitProps: LeagueUnit

const content = (stateAndRequest: StateAndRequest<Request, Array<LeagueUnitTeamStat> | undefined>) => {
let positionsHistory = stateAndRequest.currentState
if (positionsHistory === undefined) {
if (positionsHistory === undefined || positionsHistory.length === 0) {
return <></>
}
let currentRound = positionsHistory.reduce((a, b) => a.round > b.round ? a : b).round
Expand Down
85 changes: 54 additions & 31 deletions web/app/controllers/RestLeagueUnitController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import databases.requests.{ClickhouseStatisticsRequest, OrderingKeyPath}
import hattid.CommonData
import models.web._
import models.web.leagueUnit.RestLeagueUnitData
import play.api.libs.json.{Json, OWrites, Writes}
import play.api.libs.json.{JsValue, Json, OWrites, Writes}
import play.api.mvc.{Action, AnyContent, ControllerComponents, Result}
import service.leagueinfo.LeagueInfoService
import service.leagueunit.LeagueUnitCalculatorService
import service.leagueunit.{LeagueUnitCalculatorService, LeagueUnitTeamStat, LeagueUnitTeamStatHistoryInfo, LeagueUnitTeamStatsWithPositionDiff}
import utils.{LeagueNameParser, Romans}
import webclients.ChppClient

Expand All @@ -32,7 +32,7 @@ import scala.concurrent.Future


class RestLeagueUnitController @Inject() (val chppClient: ChppClient,
val controllerComponents: ControllerComponents,
val controllerComponents: ControllerComponents,
val leagueInfoService: LeagueInfoService,
implicit val restClickhouseDAO: RestClickhouseDAO,
val leagueUnitCalculatorService: LeagueUnitCalculatorService) extends RestController {
Expand Down Expand Up @@ -196,39 +196,62 @@ class RestLeagueUnitController @Inject() (val chppClient: ChppClient,
def oldestTeams(leagueUnitId: Int, restStatisticsParameters: RestStatisticsParameters): Action[AnyContent] =
stats(OldestTeamsRequest, leagueUnitId, restStatisticsParameters)

def teamPositions(leagueUnitId: Int, restStatisticsParameters: RestStatisticsParameters): Action[AnyContent] = Action.async {
chppClient.executeUnsafe[LeagueDetails, LeagueDetailsRequest](LeagueDetailsRequest(leagueUnitId = Some(leagueUnitId)))
.flatMap(leagueDetails => {
val offsettedSeason = leagueInfoService.getRelativeSeasonFromAbsolute(restStatisticsParameters.season, leagueDetails.leagueId)

chppClient.executeUnsafe[LeagueFixtures, LeagueFixturesRequest](LeagueFixturesRequest(leagueLevelUnitId = Some(leagueUnitId), season = Some(offsettedSeason)))
.map(leagueFixture => {
val round = restStatisticsParameters.statsType.asInstanceOf[Round].round

val teamsWithPositionDiff = leagueUnitCalculatorService.calculate(leagueFixture, Some(round),
restStatisticsParameters.sortBy, restStatisticsParameters.sortingDirection).teamsLastRoundWithPositionsDiff
private def lastRoundPositionsDiffFromLeagueDetails(leagueDetails: LeagueDetails): Seq[LeagueUnitTeamStatsWithPositionDiff] = {
leagueDetails.teams.map { team =>
LeagueUnitTeamStatsWithPositionDiff(
positionDiff = team.positionChange,
leagueUnitTeamStat = LeagueUnitTeamStat(
round = leagueDetails.currentMatchRound,
position = team.position,
teamId = team.teamId,
teamName = team.teamName,
games = team.matches,
scored = team.goalsFor,
missed = team.goalsAgainst,
win = team.won,
draw = team.draws,
lost = team.lost,
points = team.points
)
)
}
}

Ok(Json.toJson(RestTableData(teamsWithPositionDiff, true)))
})
})
def teamPositions(leagueUnitId: Int, restStatisticsParameters: RestStatisticsParameters): Action[AnyContent] = Action.async {
val season = restStatisticsParameters.season
teamPositionsInternal(leagueUnitId, season, _.teamsLastRoundWithPositionsDiff, lastRoundPositionsDiffFromLeagueDetails)
.map { positionsWithDiff => Ok(Json.toJson(RestTableData(positionsWithDiff, isLastPage = true))) }
}

def teamPositionsHistory(leagueUnitId: Int, season: Int): Action[AnyContent] = Action.async {
chppClient.executeUnsafe[LeagueDetails, LeagueDetailsRequest](LeagueDetailsRequest(leagueUnitId = Some(leagueUnitId)))
.flatMap(leagueDetails => {
val offsettedSeason = leagueInfoService.getRelativeSeasonFromAbsolute(season, leagueDetails.leagueId)
chppClient.executeUnsafe[LeagueFixtures, LeagueFixturesRequest](LeagueFixturesRequest(leagueLevelUnitId = Some(leagueUnitId), season = Some(offsettedSeason)))
.map(leagueFixture => {

val round = if(leagueInfoService.leagueInfo(leagueDetails.leagueId).currentSeason() == season) leagueInfoService.leagueInfo(leagueDetails.leagueId).currentRound() else 14

val positionsHistory = leagueUnitCalculatorService.calculate(leagueFixture, Some(round),
/* doesn't matter what parameters are there */"points", Desc).positionsHistory

Ok(Json.toJson(positionsHistory))
})

})
teamPositionsInternal(leagueUnitId, season, _.positionsHistory, _ => Seq())
.map { positionsHistory => Ok(Json.toJson(positionsHistory)) }
}

private def teamPositionsInternal[T](leagueUnitId: Int,
season: Int,
resultFunc: LeagueUnitTeamStatHistoryInfo => Seq[T],
fallbackResultFunc: LeagueDetails => Seq[T]): Future[Seq[T]] = {
chppClient.execute[LeagueDetails, LeagueDetailsRequest](LeagueDetailsRequest(leagueUnitId = Some(leagueUnitId)))
.flatMap {
case Right(leagueDetails) =>
val offsettedSeason = leagueInfoService.getRelativeSeasonFromAbsolute(season, leagueDetails.leagueId)
chppClient.execute[LeagueFixtures, LeagueFixturesRequest](
LeagueFixturesRequest(leagueLevelUnitId = Some(leagueUnitId), season = Some(offsettedSeason)))
.map {
case Right(leagueFixture) =>
val round = if(leagueInfoService.leagueInfo(leagueDetails.leagueId).currentSeason() == season) leagueInfoService.leagueInfo(leagueDetails.leagueId).currentRound() else 14
leagueUnitCalculatorService.calculateSafe(leagueFixture, Some(round),
/* doesn't matter what parameters are there */"points", Desc) match {
case Right(leagueUnitTeamHistoryInfo) => resultFunc(leagueUnitTeamHistoryInfo)
case Left(_) => fallbackResultFunc(leagueDetails)
}

case Left(_) => fallbackResultFunc(leagueDetails)
}
case Left(_) => Future(Seq())
}
}

def promotions(leagueUnitId: Int): Action[AnyContent] = Action.async {
Expand Down
16 changes: 15 additions & 1 deletion web/app/service/leagueunit/LeagueUnitCalculatorService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ class LeagueUnitCalculatorService {
}
}

def calculate(leagueFixture: LeagueFixtures, tillRound: Option[Int] = None, sortingField: String = "points", sortingDirection: SortingDirection = Desc): LeagueUnitTeamStatHistoryInfo = {
/**
* In case of any errors (e.g. https://github.com/Blackmorse/hat-all-stats/issues/480) will return Left
*/
def calculateSafe(leagueFixture: LeagueFixtures,
tillRound: Option[Int] = None,
sortingField: String = "points",
sortingDirection: SortingDirection = Desc): Either[Unit, LeagueUnitTeamStatHistoryInfo] = {
try {
Right(calculate(leagueFixture, tillRound, sortingField, sortingDirection))
} catch {
case _: Throwable => Left(())
}
}

private def calculate(leagueFixture: LeagueFixtures, tillRound: Option[Int] = None, sortingField: String = "points", sortingDirection: SortingDirection = Desc): LeagueUnitTeamStatHistoryInfo = {
val ordering = sortingField match {
case "points" => orderingFactory(_.points)
case "teamName" => orderingFactory(_.teamName)
Expand Down
11 changes: 6 additions & 5 deletions web/app/service/leagueunit/LeagueUnitTeamStat.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package service.leagueunit

import play.api.libs.json.Json
import play.api.libs.json.{Json, OWrites}

case class LeagueUnitTeamStat(round: Int,
position: Int,
Expand All @@ -15,18 +15,19 @@ case class LeagueUnitTeamStat(round: Int,
points: Int)

object LeagueUnitTeamStat {
implicit val writes = Json.writes[LeagueUnitTeamStat]
implicit val writes: OWrites[LeagueUnitTeamStat] = Json.writes[LeagueUnitTeamStat]
}

case class LeagueUnitTeamStatsWithPositionDiff(positionDiff: Int, leagueUnitTeamStat: LeagueUnitTeamStat)
case class LeagueUnitTeamStatsWithPositionDiff(positionDiff: Int,
leagueUnitTeamStat: LeagueUnitTeamStat)

object LeagueUnitTeamStatsWithPositionDiff {
implicit val writes = Json.writes[LeagueUnitTeamStatsWithPositionDiff]
implicit val writes: OWrites[LeagueUnitTeamStatsWithPositionDiff] = Json.writes[LeagueUnitTeamStatsWithPositionDiff]
}

case class LeagueUnitTeamStatHistoryInfo(teamsLastRoundWithPositionsDiff: Seq[LeagueUnitTeamStatsWithPositionDiff],
positionsHistory: Seq[LeagueUnitTeamStat])

object LeagueUnitTeamStatHistoryInfo {
implicit val writes = Json.writes[LeagueUnitTeamStatHistoryInfo]
implicit val writes: OWrites[LeagueUnitTeamStatHistoryInfo] = Json.writes[LeagueUnitTeamStatHistoryInfo]
}

0 comments on commit 40b7cd5

Please sign in to comment.