ajout de match régulier pour les équipes et de pratiques
Ajout d'un profil public cliquable pour les utilisateurs déplacement du profil
This commit is contained in:
@@ -175,9 +175,18 @@ class User(UserMixin, db.Model):
|
||||
if self.role == 'manager' and (tryout.created_by == self.id or tryout.manager_id == self.id):
|
||||
return True
|
||||
if self.role == 'coach':
|
||||
org_team = OrgTeam.query.filter_by(coach_id=self.id).first()
|
||||
if org_team and tryout.target_org_team_id == org_team.id:
|
||||
return True
|
||||
# Check if coach belongs to the target org team (many-to-many)
|
||||
if tryout.target_org_team_id:
|
||||
is_coach_of_target = OrgTeam.query.filter(
|
||||
OrgTeam.id == tryout.target_org_team_id,
|
||||
OrgTeam.coaches.any(id=self.id)
|
||||
).first() is not None
|
||||
if is_coach_of_target:
|
||||
return True
|
||||
# Fallback to legacy coach_id
|
||||
org_team = OrgTeam.query.filter_by(coach_id=self.id).first()
|
||||
if org_team and tryout.target_org_team_id == org_team.id:
|
||||
return True
|
||||
if tryout.coach_id == self.id:
|
||||
return True
|
||||
return False
|
||||
@@ -186,7 +195,7 @@ class User(UserMixin, db.Model):
|
||||
"""Check if user can manage a specific org team.
|
||||
|
||||
Presidents can manage all org teams. Managers can manage all org teams.
|
||||
Coaches can only manage their own coached team.
|
||||
Coaches can only manage their own coached team (via many-to-many or legacy).
|
||||
|
||||
Args:
|
||||
org_team: The OrgTeam object to check permissions for.
|
||||
@@ -198,8 +207,13 @@ class User(UserMixin, db.Model):
|
||||
return True
|
||||
if self.role == 'manager':
|
||||
return True # Managers can manage all org teams (create/edit/delete)
|
||||
if self.role == 'coach' and org_team.coach_id == self.id:
|
||||
return True
|
||||
if self.role == 'coach':
|
||||
# Check many-to-many coaches
|
||||
if org_team.coaches.filter_by(id=self.id).first():
|
||||
return True
|
||||
# Fallback to legacy coach_id
|
||||
if org_team.coach_id == self.id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_gamertags(self):
|
||||
@@ -338,31 +352,87 @@ class TeamPlayer(db.Model):
|
||||
)
|
||||
|
||||
|
||||
# Many-to-many association tables for multiple coaches and managers per team
|
||||
org_team_coaches = db.Table('org_team_coaches',
|
||||
db.Column('org_team_id', db.Integer, db.ForeignKey('org_teams.id', ondelete='CASCADE'), primary_key=True),
|
||||
db.Column('coach_id', db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True)
|
||||
)
|
||||
|
||||
org_team_managers = db.Table('org_team_managers',
|
||||
db.Column('org_team_id', db.Integer, db.ForeignKey('org_teams.id', ondelete='CASCADE'), primary_key=True),
|
||||
db.Column('manager_id', db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True)
|
||||
)
|
||||
|
||||
|
||||
class OrgTeam(db.Model):
|
||||
"""Persistent organization teams (e.g., Varsity, JV) that exist across tryouts.
|
||||
|
||||
These teams are long-term organizational structures that persist beyond
|
||||
individual tryouts, unlike tryout-specific Team entities.
|
||||
|
||||
Supports multiple coaches and managers per team through many-to-many
|
||||
junction tables (org_team_coaches, org_team_managers).
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier.
|
||||
name: Team name (e.g., "Varsity", "Junior Varsity").
|
||||
coach_id: Foreign key to the assigned coach.
|
||||
manager_id: Foreign key to the assigned manager.
|
||||
created_by: Foreign key to the user who created the team.
|
||||
created_at: Timestamp of team creation.
|
||||
coaches: Many-to-many relationship to User (coaches).
|
||||
managers: Many-to-many relationship to User (managers).
|
||||
"""
|
||||
__tablename__ = 'org_teams'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), nullable=False, unique=True)
|
||||
coach_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
manager_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# Legacy single coach/manager columns kept for backward compatibility during migration
|
||||
# These will be removed in a future migration after existing data is migrated
|
||||
coach_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
manager_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
|
||||
coach = db.relationship('User', foreign_keys=[coach_id], backref='coached_org_team', uselist=False)
|
||||
manager = db.relationship('User', foreign_keys=[manager_id], backref='managed_org_team', uselist=False)
|
||||
creator = db.relationship('User', foreign_keys=[created_by])
|
||||
|
||||
# New many-to-many relationships for multiple coaches/managers
|
||||
coaches = db.relationship('User', secondary=org_team_coaches, lazy='dynamic',
|
||||
backref=db.backref('coached_org_teams', lazy='dynamic'))
|
||||
managers = db.relationship('User', secondary=org_team_managers, lazy='dynamic',
|
||||
backref=db.backref('managed_org_teams', lazy='dynamic'))
|
||||
|
||||
# Legacy single relationships — now properties that use the many-to-many lists
|
||||
coach = db.relationship('User', foreign_keys=[coach_id],
|
||||
backref=db.backref('coached_org_team_legacy', uselist=False), viewonly=True)
|
||||
manager = db.relationship('User', foreign_keys=[manager_id],
|
||||
backref=db.backref('managed_org_team_legacy', uselist=False), viewonly=True)
|
||||
|
||||
def get_coaches(self):
|
||||
"""Get the list of coaches for display/handling.
|
||||
|
||||
Returns coaches from the many-to-many relationship, falling back to
|
||||
the legacy single coach for backward compatibility.
|
||||
|
||||
Returns:
|
||||
list: List of User objects who are coaches for this team.
|
||||
"""
|
||||
coach_list = self.coaches.all()
|
||||
if not coach_list and self.coach:
|
||||
return [self.coach]
|
||||
return coach_list
|
||||
|
||||
def get_managers(self):
|
||||
"""Get the list of managers for display/handling.
|
||||
|
||||
Returns managers from the many-to-many relationship, falling back to
|
||||
the legacy single manager for backward compatibility.
|
||||
|
||||
Returns:
|
||||
list: List of User objects who are managers for this team.
|
||||
"""
|
||||
manager_list = self.managers.all()
|
||||
if not manager_list and self.manager:
|
||||
return [self.manager]
|
||||
return manager_list
|
||||
|
||||
@property
|
||||
def players(self):
|
||||
@@ -862,4 +932,80 @@ class OneOnOneRequest(db.Model):
|
||||
|
||||
player = db.relationship('User', foreign_keys=[player_id], backref='one_on_one_requests')
|
||||
coach = db.relationship('User', foreign_keys=[coach_id])
|
||||
team = db.relationship('OrgTeam', foreign_keys=[org_team_id])
|
||||
team = db.relationship('OrgTeam', foreign_keys=[org_team_id])
|
||||
|
||||
|
||||
class TeamMatch(db.Model):
|
||||
"""Regular season match for an organization team (not tied to a tryout).
|
||||
|
||||
These matches are team-specific, not tryout-specific. The team's roster is
|
||||
automatically pre-filled as participants. The creator (coach, manager, president)
|
||||
only needs to select date-time and an optional opponent.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier.
|
||||
org_team_id: Foreign key to the organization team.
|
||||
title: Match title/name.
|
||||
description: Optional description.
|
||||
opponent: Optional opponent name.
|
||||
date: Match date.
|
||||
start_time: Match start time.
|
||||
end_time: Match end time.
|
||||
location: Match location.
|
||||
status: Match status (scheduled, completed, cancelled).
|
||||
created_by: Foreign key to the creator.
|
||||
created_at: Timestamp of creation.
|
||||
"""
|
||||
__tablename__ = 'team_matches'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
org_team_id = db.Column(db.Integer, db.ForeignKey('org_teams.id'), nullable=False)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text, nullable=True)
|
||||
opponent = db.Column(db.String(200), nullable=True)
|
||||
date = db.Column(db.Date, nullable=False)
|
||||
start_time = db.Column(db.Time, nullable=True)
|
||||
end_time = db.Column(db.Time, nullable=True)
|
||||
location = db.Column(db.String(200), nullable=True)
|
||||
status = db.Column(db.String(20), default='scheduled') # scheduled, completed, cancelled
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
org_team = db.relationship('OrgTeam', backref='team_matches')
|
||||
creator = db.relationship('User', backref='created_team_matches')
|
||||
participants = db.relationship('TeamMatchParticipant', backref='team_match', lazy='dynamic', cascade='all, delete-orphan')
|
||||
|
||||
def get_confirmed_count(self):
|
||||
"""Get count of confirmed participants.
|
||||
|
||||
Returns:
|
||||
tuple: (confirmed_count, total_count)
|
||||
"""
|
||||
all_p = self.participants.all()
|
||||
confirmed = sum(1 for p in all_p if p.is_confirmed)
|
||||
return confirmed, len(all_p)
|
||||
|
||||
|
||||
class TeamMatchParticipant(db.Model):
|
||||
"""Participant in a team match (regular season).
|
||||
|
||||
Automatically created for all team players when a TeamMatch is created.
|
||||
Tracks attendance confirmation per player.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier.
|
||||
team_match_id: Foreign key to the team match.
|
||||
player_id: Foreign key to the player.
|
||||
is_confirmed: Whether attendance is confirmed (manual toggle or Discord reaction).
|
||||
added_at: Timestamp when added.
|
||||
"""
|
||||
__tablename__ = 'team_match_participants'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
team_match_id = db.Column(db.Integer, db.ForeignKey('team_matches.id'), nullable=False)
|
||||
player_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
is_confirmed = db.Column(db.Boolean, default=False)
|
||||
added_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
player = db.relationship('User')
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint('team_match_id', 'player_id', name='unique_team_match_player'),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user