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

1"""Module for defining a training entity.""" 

2 

3from dataclasses import dataclass, field, replace 

4from typing import ClassVar, Self, Type 

5 

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 

12 

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) 

18 

19 

20class TrainingCoachIdentifier(IntIdentifier): 

21 """Identifier for a coach appointed to a training.""" 

22 

23 

24@dataclass(kw_only=True, frozen=True, slots=True) 

25class TrainingCoachEntity(DataclassEntity): 

26 """A coach attached to a training.""" 

27 

28 ID: ClassVar[Type] = TrainingCoachIdentifier 

29 

30 coach: CoachEntity 

31 owner: Owner 

32 present: bool = False 

33 type: int = 0 

34 payed: bool = False 

35 remark: str = "" 

36 

37 

38class TrainingIdentifier(IntIdentifier): 

39 """Identifier for a training.""" 

40 

41 

42@dataclass(frozen=True, slots=True, kw_only=True) 

43class TrainingSession: 

44 """A training session. 

45 

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 """ 

54 

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 

61 

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) 

68 

69 

70@dataclass(kw_only=True, eq=False, slots=True, frozen=True) 

71class TrainingEntity(DataclassEntity): 

72 """A training entity. 

73 

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 """ 

86 

87 ID: ClassVar[Type] = TrainingIdentifier 

88 

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 = "" 

99 

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) 

104 

105 def delete_team(self, team: TeamEntity) -> Self: 

106 """Remove a team from this training.""" 

107 return replace(self, teams=self.teams - {team}) 

108 

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) 

113 

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}) 

121 

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. 

124 

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 ) 

140 

141 def appoint_head_coach(self, coach_uuid: UniqueId, appoint: bool = True) -> Self: 

142 """Appoint a coach as head of this training. 

143 

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 ) 

159 

160 def get_training_session(self, coach_uuid: UniqueId) -> TrainingSession: 

161 """Returns the training session for the coach wit the given unique id. 

162 

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 ) 

173 

174 return TrainingSession( 

175 period=self.period, 

176 coach_uuid=coach_uuid, 

177 head=training_coach.type == 1, 

178 training_id=self.id, 

179 )