108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
"""Base User model — shared fields and polymorphic configuration."""
|
|
|
|
from flask_login import UserMixin
|
|
|
|
from app.extensions import db
|
|
from app.time_utils import utc_now_naive
|
|
|
|
|
|
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=utc_now_naive)
|
|
|
|
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 []
|