Coverage for bc/kwai-bc-training/src/kwai_bc_training/trainings/training.py: 89%
70 statements
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
1"""Module for defining a training entity."""
3from dataclasses import dataclass, field, replace
4from typing import ClassVar, Self, Type
6from kwai_core.domain.entity import DataclassEntity
7from kwai_core.domain.value_objects.identifier import IntIdentifier
8from kwai_core.domain.value_objects.owner import Owner
9from kwai_core.domain.value_objects.period import Period
10from kwai_core.domain.value_objects.text import LocaleText
11from kwai_core.domain.value_objects.unique_id import UniqueId
13from kwai_bc_training.coaches.coach import CoachEntity
14from kwai_bc_training.teams.team import TeamEntity
15from kwai_bc_training.trainings.training_schedule import (
16 TrainingScheduleEntity,
17)
20class TrainingCoachIdentifier(IntIdentifier):
21 """Identifier for a coach appointed to a training."""
24@dataclass(kw_only=True, frozen=True, slots=True)
25class TrainingCoachEntity(DataclassEntity):
26 """A coach attached to a training."""
28 ID: ClassVar[Type] = TrainingCoachIdentifier
30 coach: CoachEntity
31 owner: Owner
32 present: bool = False
33 type: int = 0
34 payed: bool = False
35 remark: str = ""
38class TrainingIdentifier(IntIdentifier):
39 """Identifier for a training."""
42@dataclass(frozen=True, slots=True, kw_only=True)
43class TrainingSession:
44 """A training session.
46 Attributes:
47 period: the period of the training
48 hours: duration in hours
49 minutes: duration in minutes
50 coach_uuid: the id of the coach
51 head: was the coach the head coach of the training?
52 training_id: the id of the training
53 """
55 period: Period
56 hours: int = field(init=False, default=0)
57 minutes: int = field(init=False, default=0)
58 coach_uuid: UniqueId
59 head: bool
60 training_id: TrainingIdentifier
62 def __post_init__(self):
63 """Initialize computed fields hours / minutes."""
64 delta = self.period.delta
65 if delta:
66 self.__setattr__("hours", delta.seconds // 3600)
67 self.__setattr__("minutes", (delta.seconds % 3600) // 60)
70@dataclass(kw_only=True, eq=False, slots=True, frozen=True)
71class TrainingEntity(DataclassEntity):
72 """A training entity.
74 Attributes:
75 texts: A tuple with text content
76 schedule: The related training schedule, when the training was created from a schedule.
77 coaches: A frozenset of assigned coaches.
78 teams: A frozenset of assigned teams.
79 period: The period of the training.
80 season: The season that the training belongs to (not supported yet).
81 active: Is this training active?
82 cancelled: Is this training cancelled?
83 location: The location of this training
84 remark: A remark about this training
85 """
87 ID: ClassVar[Type] = TrainingIdentifier
89 texts: tuple[LocaleText, ...] = field(default_factory=tuple)
90 schedule: TrainingScheduleEntity | None = None
91 coaches: frozenset[TrainingCoachEntity] = field(default_factory=frozenset)
92 teams: frozenset[TeamEntity] = field(default_factory=frozenset)
93 season: None = None
94 period: Period
95 active: bool = True
96 cancelled: bool = False
97 location: str = ""
98 remark: str = ""
100 def add_team(self, team: TeamEntity) -> Self:
101 """Add a team to this training."""
102 teams = self.teams | {team}
103 return replace(self, teams=teams)
105 def delete_team(self, team: TeamEntity) -> Self:
106 """Remove a team from this training."""
107 return replace(self, teams=self.teams - {team})
109 def add_coach(self, coach: TrainingCoachEntity) -> Self:
110 """Add a coach to this training."""
111 coaches = self.coaches | {coach}
112 return replace(self, coaches=coaches)
114 def delete_coach(self, coach: TrainingCoachEntity) -> Self:
115 """Remove a coach from this training."""
116 if coach.present:
117 raise ValueError(
118 "It's not allowed to remove a coach who was present on a training."
119 )
120 return replace(self, coaches=self.coaches - {coach})
122 def mark_coach_as_present(self, coach_uuid: UniqueId, present: bool = True) -> Self:
123 """Mark a coach as present or not for this training.
125 Args:
126 coach_uuid: The uuid of the coach to mark as present or not
127 present: Whether the coach was present or not
128 """
129 training_coaches = list(
130 filter(lambda c: c.coach.uuid == coach_uuid, self.coaches)
131 )
132 if len(training_coaches) == 0:
133 raise ValueError(
134 f"Coach with {coach_uuid} is not attached to this training."
135 )
136 training_coach = replace(training_coaches[0], present=present)
137 return replace(
138 self, coaches=(self.coaches - {training_coaches[0]} | {training_coach})
139 )
141 def appoint_head_coach(self, coach_uuid: UniqueId, appoint: bool = True) -> Self:
142 """Appoint a coach as head of this training.
144 Args:
145 coach_uuid: The uuid of the coach to appoint or not
146 appoint: Whether the coach is appointed or not
147 """
148 training_coaches = list(
149 filter(lambda c: c.coach.uuid == coach_uuid, self.coaches)
150 )
151 if len(training_coaches) == 0:
152 raise ValueError(
153 f"Coach with {coach_uuid} is not attached to this training."
154 )
155 training_coach = replace(training_coaches[0], type=1 if appoint else 0)
156 return replace(
157 self, coaches=(self.coaches - {training_coaches[0]} | {training_coach})
158 )
160 def get_training_session(self, coach_uuid: UniqueId) -> TrainingSession:
161 """Returns the training session for the coach wit the given unique id.
163 Raises:
164 ValueError: If coach is not found.
165 """
166 training_coach = next(
167 filter(lambda c: c.coach.uuid == coach_uuid, self.coaches), None
168 )
169 if training_coach is None:
170 raise ValueError(
171 f"Coach with uuid {coach_uuid} is not attached to this training."
172 )
174 return TrainingSession(
175 period=self.period,
176 coach_uuid=coach_uuid,
177 head=training_coach.type == 1,
178 training_id=self.id,
179 )