"""Base User model — shared fields and polymorphic configuration.""" from datetime import datetime from flask_login import UserMixin from app.extensions import db class User(UserMixin, db.Model): """Base user model — shared fields for every role. Do not instantiate this class directly; use Admin, Manager, Coach, Player, or Scout so that `polymorphic_identity` is set correctly. """ __tablename__ = 'users' # --- columns ----------------------------------------------------------- id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password_hash = db.Column(db.String(256), nullable=False) role = db.Column(db.String(20), nullable=False, default='player') # polymorphic discriminator full_name = db.Column(db.String(100), nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) phone = db.Column(db.String(20), nullable=True) is_active_account = db.Column(db.Boolean, default=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) failed_login_attempts = db.Column(db.Integer, default=0) locked_until = db.Column(db.DateTime, nullable=True) # E-Sports fields games = db.Column(db.Text, nullable=True) # comma-separated (only meaningful for Player) discord_username = db.Column(db.String(128), nullable=True) discord_user_id = db.Column(db.String(64), nullable=True) league_os_profile = db.Column(db.String(256), nullable=True) # --- polymorphic configuration ----------------------------------------- __mapper_args__ = { 'polymorphic_identity': 'user', 'polymorphic_on': role, } # --- relationships (defined once on the base) -------------------------- evaluations_given = db.relationship( 'Evaluation', foreign_keys='Evaluation.evaluator_id', backref='evaluator', lazy='dynamic' ) evaluations_received = db.relationship( 'Evaluation', foreign_keys='Evaluation.player_id', backref='player', lazy='dynamic' ) tryout_registrations = db.relationship('TryoutRegistration', backref='player', lazy='dynamic') team_assignments = db.relationship( 'TeamMember', foreign_keys='TeamMember.player_id', backref='player_ref', lazy='dynamic' ) # --- Flask-Login integration ------------------------------------------- @property def is_active(self): """Whether Flask-Login should accept this account. UserMixin returns True unconditionally, which meant a deactivated account kept any session it already held. Binding this to is_active_account makes deactivation take effect on the next request. """ return bool(self.is_active_account) # --- shared helper methods --------------------------------------------- def get_games_list(self): """Return the user's games as a list.""" if self.games: return [g.strip() for g in self.games.split(',') if g.strip()] return [] def get_gamertags(self): """Return gamertags as a dict keyed by game.""" return { gt.game: {'gamertag': gt.gamertag, 'platform': gt.platform} for gt in self.gamertags } def get_org_teams(self): """Return all OrgTeams this player belongs to.""" return [tp.org_team for tp in self.team_placements] # --- stubs (overridden in subclasses) ---------------------------------- def can_evaluate(self): return False def can_manage_users(self): return False def can_manage_teams(self): return False def can_manage_tryouts(self): return False def can_schedule_matches(self): return False def can_manage_this_tryout(self, tryout): return False def can_manage_this_org_team(self, org_team): return False def get_visible_tryouts(self): return []