djbrown/hbscorez

View on GitHub
src/games/models.py

Summary

Maintainability
A
2 hrs
Test Coverage
F
58%
from enum import Enum, auto
 
from django.conf import settings
from django.db import models
from returns.maybe import Maybe
 
from leagues.models import League
from sports_halls.models import SportsHall
from teams.models import Team
 
 
class GameOutcome(Enum):
HOME_WIN = auto()
AWAY_WIN = auto()
TIE = auto()
OPEN = auto()
 
 
class TeamOutcome(Enum):
WIN = auto()
LOSS = auto()
TIE = auto()
OPEN = auto()
 
 
class Leg(Enum):
FIRST = auto()
BEWTEEN = auto()
SECOND = auto()
UNKNOWN = auto()
 
 
class Game(models.Model):
number = models.IntegerField()
league = models.ForeignKey(League, on_delete=models.CASCADE)
opening_whistle = models.DateTimeField(blank=True, null=True)
sports_hall = models.ForeignKey(SportsHall, on_delete=models.SET_NULL, blank=True, null=True)
home_team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="home_team")
guest_team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="guest_team")
home_goals = models.IntegerField(blank=True, null=True)
guest_goals = models.IntegerField(blank=True, null=True)
report_number = models.IntegerField(blank=True, null=True, unique=True)
remark = models.TextField(blank=True)
spectators = models.IntegerField(blank=True, null=True)
 
class Meta:
unique_together = ("number", "league")
 
def __str__(self):
return f"{self.number} {self.league} {self.home_team.short_name} vs. {self.guest_team.short_name}"
 
@staticmethod
def build_report_source_url(report_number):
return settings.ROOT_SOURCE_URL + f"misc/sboPublicReports.php?sGID={report_number}"
 
def report_source_url(self):
if self.report_number is None:
return None
return self.build_report_source_url(self.report_number)
 
def opponent_of(self, team):
if team == self.home_team:
return self.guest_team
if team == self.guest_team:
return self.home_team
raise ValueError(f"neither home or guest is team: {team}")
 
def other_games(self):
return Game.objects.filter(
~models.Q(number=self.number),
home_team__in=(self.home_team, self.guest_team),
guest_team__in=(self.home_team, self.guest_team),
)
 
Cyclomatic complexity is too high in method leg. (11)
Function `leg` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.
def leg(self) -> Leg:
other_games = self.other_games()
if (
not other_games
or len(other_games) > 2
or self.opening_whistle is None
or list(filter(lambda g: g.opening_whistle is None, other_games))
):
return Leg.UNKNOWN
 
if len(other_games) == 1:
first_leg = self.opening_whistle < other_games[0].opening_whistle
return Leg.FIRST if first_leg else Leg.SECOND
 
# len(other_games) == 2
if (
self.opening_whistle < other_games[0].opening_whistle
and self.opening_whistle < other_games[1].opening_whistle
):
return Leg.FIRST
if (
self.opening_whistle > other_games[0].opening_whistle
and self.opening_whistle > other_games[1].opening_whistle
):
return Leg.SECOND
 
Avoid too many `return` statements within this function.
return Leg.BEWTEEN
 
def leg_title(self) -> str:
mapping = {
Leg.FIRST: "Hinspiel",
Leg.BEWTEEN: "Zwischenspiel",
Leg.SECOND: "Rückspiel",
Leg.UNKNOWN: "Spiel",
}
return mapping[self.leg()]
 
def forfeiting_team(self) -> Team | None:
if "2:0" in self.remark:
return self.guest_team
if "0:2" in self.remark:
return self.home_team
return None
 
Cyclomatic complexity is too high in method outcome. (8)
def outcome(self) -> GameOutcome:
if self.home_goals is None and self.guest_goals is None:
return GameOutcome.OPEN
home_goals: int = Maybe.from_optional(self.home_goals).unwrap()
guest_goals: int = Maybe.from_optional(self.guest_goals).unwrap()
if home_goals > guest_goals or self.forfeiting_team() == self.guest_team:
return GameOutcome.HOME_WIN
if home_goals < guest_goals or self.forfeiting_team() == self.home_team:
return GameOutcome.AWAY_WIN
if home_goals == guest_goals:
return GameOutcome.TIE
raise ValueError("no matching outcome")
 
Cyclomatic complexity is too high in method outcome_for. (11)
Function `outcome_for` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.
def outcome_for(self, team) -> TeamOutcome:
if self.outcome() == GameOutcome.OPEN:
return TeamOutcome.OPEN
if self.outcome() == GameOutcome.TIE:
return TeamOutcome.TIE
if (
team == self.home_team
and self.outcome() == GameOutcome.HOME_WIN
or team == self.guest_team
and self.outcome() == GameOutcome.AWAY_WIN
):
return TeamOutcome.WIN
if (
team == self.home_team
and self.outcome() == GameOutcome.AWAY_WIN
or team == self.guest_team
and self.outcome() == GameOutcome.HOME_WIN
):
return TeamOutcome.LOSS
raise ValueError(f"no matching outcome for team: {team}")
 
def goals_of(self, team):
if team == self.home_team:
return self.home_goals
if team == self.guest_team:
return self.guest_goals
return 0