π Project Statistics
+
+
+
+
+
+
+
+
+31
Python files in models/
7
Subpackages
24
Classes (incl. abstract)
8
Route Blueprints
4
Flask Extensions
8
Utility Modules
π UML Class Diagram β All 24 Classes, Relationships & Inheritance
+
+
+
+
+classDiagram
+ class BaseAvailability {
+ +int day_of_week
+ +time start_time
+ +time end_time
+ +datetime created_at
+ +datetime updated_at
+ }
+ class PlayerDisponibility {
+ +int id
+ +int player_id
+ }
+ class CoachAvailability {
+ +int id
+ +int coach_id
+ }
+ BaseAvailability <|-- PlayerDisponibility : extends
+ BaseAvailability <|-- CoachAvailability : extends
+
+ class BaseMatch {
+ +str title
+ +str description
+ +date date
+ +time start_time
+ +time end_time
+ +str location
+ +str status
+ +int created_by
+ +datetime created_at
+ }
+ class Match {
+ +int id
+ +int tryout_id
+ +str match_type
+ +int team1_id
+ +int team2_id
+ +get_participating_players()
+ }
+ class TeamMatch {
+ +int id
+ +int org_team_id
+ +str opponent
+ +get_confirmed_count()
+ }
+ BaseMatch <|-- Match : extends
+ BaseMatch <|-- TeamMatch : extends
+
+ class BaseParticipant {
+ +int player_id
+ +datetime added_at
+ }
+ class MatchParticipant {
+ +int id
+ +int match_id
+ +int team_side
+ +str position
+ +bool attendance_confirmed
+ }
+ class TeamMatchParticipant {
+ +int id
+ +int team_match_id
+ +bool is_confirmed
+ }
+ BaseParticipant <|-- MatchParticipant : extends
+ BaseParticipant <|-- TeamMatchParticipant : extends
+
+ class User {
+ +int id
+ +str username
+ +str password_hash
+ +str role
+ +str full_name
+ +str email
+ +str phone
+ +bool is_active_account
+ +datetime created_at
+ +int failed_login_attempts
+ +datetime locked_until
+ +str games
+ +str discord_username
+ +str discord_user_id
+ +str league_os_profile
+ +get_games_list()
+ +get_gamertags()
+ +get_org_teams()
+ +can_evaluate()
+ +can_manage_users()
+ +can_manage_teams()
+ +can_manage_tryouts()
+ +can_schedule_matches()
+ +can_manage_this_tryout()
+ +can_manage_this_org_team()
+ +get_visible_tryouts()
+ }
+ class Admin {
+ +all permissions = True
+ }
+ class Manager {
+ +can_manage_teams()
+ +can_manage_tryouts()
+ }
+ class Coach {
+ +can_evaluate()
+ +can_schedule_matches()
+ +can_manage_this_tryout()
+ +can_manage_this_org_team()
+ }
+ class Player {
+ +get_visible_tryouts()
+ }
+ class Scout {
+ +can_evaluate()
+ +get_visible_tryouts()
+ }
+ User <|-- Admin : polymorphic
+ User <|-- Manager : polymorphic
+ User <|-- Coach : polymorphic
+ User <|-- Player : polymorphic
+ User <|-- Scout : polymorphic
+
+ class UserGamertag {
+ +int id
+ +int user_id
+ +str game
+ +str gamertag
+ +str platform
+ +get_trn_url()
+ }
+ class OrgTeam {
+ +int id
+ +str name
+ +int created_by
+ +datetime created_at
+ +int coach_id
+ +int manager_id
+ +get_coaches()
+ +get_managers()
+ +players()
+ +get_players_with_status()
+ }
+ class TeamPlayer {
+ +int id
+ +int player_id
+ +int org_team_id
+ +str status
+ +str position
+ +datetime added_at
+ }
+ class Tryout {
+ +int id
+ +str title
+ +str description
+ +str game
+ +date date
+ +str location
+ +str status
+ +int max_players
+ +int created_by
+ +int target_org_team_id
+ +int manager_id
+ +int coach_id
+ +datetime created_at
+ }
+ class TryoutRegistration {
+ +int id
+ +int tryout_id
+ +int player_id
+ +datetime registered_at
+ +str status
+ +str notes
+ }
+ class Evaluation {
+ +int id
+ +int tryout_id
+ +int player_id
+ +int evaluator_id
+ +int mecanics_score
+ +int cohesion_score
+ +int communication_score
+ +int gamesense_score
+ +int versatility_score
+ +int discipline_score
+ +int analysis_score
+ +int sport_ethics_score
+ +int mental_score
+ +float overall_score
+ +str comments
+ +str position_recommendation
+ +datetime created_at
+ +datetime updated_at
+ }
+ class Team {
+ +int id
+ +int tryout_id
+ +str name
+ +int created_by
+ +datetime created_at
+ }
+ class TeamMember {
+ +int id
+ +int team_id
+ +int player_id
+ +str position
+ +datetime added_at
+ }
+ class Contract {
+ +int id
+ +int player_id
+ +int team_id
+ +int uploaded_by_id
+ +str original_filename
+ +str stored_filename
+ +str file_path
+ +str signed_filename
+ +str signed_file_path
+ +str status
+ +str notes
+ +datetime uploaded_at
+ +datetime signed_at
+ +can_view()
+ +can_upload_signed()
+ }
+ class TeamNote {
+ +int id
+ +int org_team_id
+ +int coach_id
+ +str content
+ +datetime created_at
+ +datetime updated_at
+ }
+ class PersonalNote {
+ +int id
+ +int player_id
+ +int coach_id
+ +str content
+ +datetime created_at
+ +datetime updated_at
+ +int match_id
+ +int team_id
+ +int tryout_id
+ }
+ class OneOnOneRequest {
+ +int id
+ +int player_id
+ +int coach_id
+ +int org_team_id
+ +date date
+ +time start_time
+ +time end_time
+ +str points
+ +str status
+ +datetime created_at
+ +datetime responded_at
+ +bigint discord_message_id
+ +str coach_rejection_message
+ }
+ class load_user {
+ +load_user(user_id)
+ }
+
+ User "1" --> "*" UserGamertag : gamertags
+ User "1" --> "*" Tryout : created_tryouts
+ User "1" --> "*" Tryout : managed_tryouts
+ User "1" --> "*" Tryout : coached_tryouts
+ User "1" --> "*" Evaluation : evaluations_given
+ User "1" --> "*" Evaluation : evaluations_received
+ User "1" --> "*" TryoutRegistration : registrations
+ User "1" --> "*" TeamMember : team_assignments
+ User "1" --> "*" TeamPlayer : team_placements
+ User "1" --> "*" Match : created_matches
+ User "1" --> "*" TeamMatch : created_team_matches
+ User "1" --> "*" PlayerDisponibility : disponibilities
+ User "1" --> "*" CoachAvailability : availabilities
+ User "1" --> "*" Team : created_teams
+ User "1" --> "*" Contract : contracts
+ User "1" --> "*" PersonalNote : personal_notes
+ User "1" --> "*" OneOnOneRequest : one_on_one_requests
+
+ OrgTeam "1" --> "*" TeamPlayer : team_players
+ OrgTeam "1" --> "*" Tryout : tryouts
+ OrgTeam "1" --> "*" TeamNote : team_notes
+ OrgTeam "1" --> "*" TeamMatch : team_matches
+ OrgTeam "1" --> "*" OneOnOneRequest : requests
+ OrgTeam "1" --> "*" Contract : contracts
+
+ Tryout "1" --> "*" TryoutRegistration : registrations
+ Tryout "1" --> "*" Evaluation : evaluations
+ Tryout "1" --> "*" Team : teams
+ Tryout "1" --> "*" Match : matches
+ Tryout "1" --> "*" PersonalNote : notes
+
+ Team "1" --> "*" TeamMember : members
+ Team "1" --> "*" Match : as_team1
+ Team "1" --> "*" Match : as_team2
+ Match "1" --> "*" MatchParticipant : participants
+ Match "1" --> "*" PersonalNote : notes
+
+ TeamMatch "1" --> "*" TeamMatchParticipant : participants
+
+ User .. load_user : loads
+
+
+ π¦ Model Package Structure β 7 Subpackages
+
+
+
+
+graph TD
+ subgraph MODELS["app/models/ β 31 files"]
+ direction TB
+ INIT["__init__.py β master re-exporter"]
+ CONST["_constants.py β USER_TYPES, ESPORT_GAMES, GAME_POSITIONS, etc."]
+ LOADERS["_loaders.py β Flask-Login load_user()"]
+ ASSOC["_associations.py β M2M association tables"]
+
+ subgraph USER_PKG["user_model/ (7 files)"]
+ USER["user.py β User base (polymorphic)"]
+ ADMIN["admin.py β Admin(User)"]
+ MGR["manager.py β Manager(User)"]
+ COACH["coach.py β Coach(User)"]
+ PLAYER["player.py β Player(User)"]
+ SCOUT["scout.py β Scout(User)"]
+ end
+
+ subgraph AVAIL_PKG["availability/ (4 files)"]
+ BASE_AVAIL["base.py β BaseAvailability (abstract)"]
+ PD["player_disponibility.py β PlayerDisponibility"]
+ CA["coach_availability.py β CoachAvailability"]
+ end
+
+ subgraph MATCH_PKG["match_model/ (4 files)"]
+ BASE_M["base.py β BaseMatch (abstract)"]
+ MATCH["match.py β Match (tryout-scoped)"]
+ TM["team_match.py β TeamMatch (regular season)"]
+ end
+
+ subgraph PARTIC_PKG["participant/ (4 files)"]
+ BASE_P["base.py β BaseParticipant (abstract)"]
+ MP["match_participant.py β MatchParticipant"]
+ TMP["team_match_participant.py β TeamMatchParticipant"]
+ end
+
+ subgraph ORG_PKG["org_team/ (3 files)"]
+ ORG["org_team.py β OrgTeam"]
+ TP["team_player.py β TeamPlayer"]
+ end
+
+ subgraph TRYOUT_PKG["tryout/ (3 files)"]
+ TRY["tryout.py β Tryout"]
+ TR["tryout_registration.py β TryoutRegistration"]
+ end
+
+ subgraph TEAM_PKG["team/ (3 files)"]
+ TEAM["team.py β Team (tryout-specific)"]
+ TMEMBER["team_member.py β TeamMember"]
+ end
+
+ subgraph STANDALONE["6 Standalone Files"]
+ EVAL["evaluation.py"]
+ CONTRACT["contract.py"]
+ GT["user_gamertag.py"]
+ TN["team_note.py"]
+ PN["personal_note.py"]
+ OOO["one_on_one_request.py"]
+ end
+ end
+
+ INIT --> USER_PKG
+ INIT --> AVAIL_PKG
+ INIT --> MATCH_PKG
+ INIT --> PARTIC_PKG
+ INIT --> ORG_PKG
+ INIT --> TRYOUT_PKG
+ INIT --> TEAM_PKG
+ INIT --> STANDALONE
+ INIT --> CONST
+ INIT --> LOADERS
+ INIT --> ASSOC
+
+
+ π Functional Architecture β Request Flow & Component Graph
+
+
+
+
+graph TD
+ subgraph CLIENT["Client Layer"]
+ BROWSER["Browser / User"]
+ DISCORD_APP["Discord App"]
+ MONITOR["Monitoring / LB"]
+ end
+
+ subgraph PROXY["Reverse Proxy"]
+ NGINX["Nginx
TLS termination
Static files
Rate limiting"] + end + + subgraph APP["Flask Application β create_app()"] + subgraph INGRESS["Incoming Middleware"] + BEFORE_REQ["before_request
force_https()"] + CSRF_CHECK["CSRF Validation"] + LIMITER_CHECK["Rate Limiter
200/day | 50/hr"] + LOGIN_CHECK["login_required
Permission checks"] + end + + subgraph BLUEPRINTS["Route Blueprints (8)"] + AUTH_BP["auth_bp /auth
login, register, logout"] + MAIN_BP["main_bp /
dashboard, privacy, terms"] + TRYOUTS_BP["tryouts_bp /tryouts
CRUD + register"] + TEAMS_BP["teams_bp /teams
OrgTeam CRUD"] + MATCHES_BP["matches_bp /matches
Tryout match CRUD"] + TEAM_MATCHES_BP["team_matches_bp /team-matches
Season match CRUD"] + USERS_BP["users_bp /users
User CRUD, contracts
disponibilities, one-on-one"] + EVALS_BP["evaluations_bp /evaluations
9-score evaluations"] + end + + subgraph EGRESS["Outgoing Middleware"] + AFTER_REQ["after_request
Security headers"] + ERRORS["Error Handlers
400 401 403 404 429 500"] + HEALTH["GET /health"] + end + end + + subgraph EXTENSIONS["Flask Extensions"] + DB_EXT["SQLAlchemy db"] + LOGIN_EXT["LoginManager"] + CSRF_EXT["CSRFProtect"] + LIMITER_EXT["Flask-Limiter"] + end + + subgraph MODELS["SQLAlchemy Models (7 subpackages)"] + USER_M["user_model/ β User hierarchy
User, Admin, Manager, Coach, Player, Scout"] + AVAIL_M["availability/ β BaseAvailability
PlayerDisponibility, CoachAvailability"] + MATCH_M["match_model/ β BaseMatch
Match, TeamMatch"] + PARTIC_M["participant/ β BaseParticipant
MatchParticipant, TeamMatchParticipant"] + ORG_M["org_team/ β OrgTeam, TeamPlayer"] + TRYOUT_M["tryout/ β Tryout, TryoutRegistration"] + TEAM_M["team/ β Team, TeamMember"] + STANDALONE_M["Standalone: Evaluation, Contract
UserGamertag, TeamNote, PersonalNote
OneOnOneRequest"] + end + + subgraph DB["Database"] + SQLITE["SQLite / PostgreSQL
DATABASE_URL"] + end + + subgraph UTILS["Utility Modules"] + VALIDATORS["validators.py
Input validation schemas"] + LOGGING["logging_config.py
Structured logging"] + DISCORD_BOT["discord_bot.py
Discord notifications
Reaction handling"] + SECURITY["security_scan.py
Security audit"] + SEED["seed.py
Database seeding"] + BACKUP["backup.py
Database backup"] + end + + subgraph TEMPLATES["Jinja2 Templates"] + PAGES["templates/pages/
~30 HTML pages"] + LAYOUTS["templates/layouts/
base, nav"] + ERRORS_TPL["templates/errors/
400-500 errors"] + end + + subgraph STATIC["Static Assets"] + CSS_F["static/css/"] + JS_F["static/js/"] + end + + subgraph EXTERNAL["External APIs"] + TRN_API["TRN / Tracker.gg
Gamertag profiles"] + DISCORD_API["Discord API
Bot notifications"] + end + + BROWSER --> NGINX + NGINX --> BEFORE_REQ + BEFORE_REQ --> CSRF_CHECK + CSRF_CHECK --> LIMITER_CHECK + LIMITER_CHECK --> LOGIN_CHECK + LOGIN_CHECK --> BLUEPRINTS + BLUEPRINTS --> AFTER_REQ + AFTER_REQ --> BROWSER + + AUTH_BP -.-> USER_M + TRYOUTS_BP -.-> TRYOUT_M + EVALS_BP -.-> STANDALONE_M + TEAMS_BP -.-> ORG_M + MATCHES_BP -.-> MATCH_M + TEAM_MATCHES_BP -.-> MATCH_M + USERS_BP -.-> USER_M + + MODELS --> DB + + DB_EXT --> MODELS + LOGIN_EXT --> USER_M + CSRF_EXT --> BLUEPRINTS + LIMITER_EXT --> BLUEPRINTS + + VALIDATORS -.-> AUTH_BP + VALIDATORS -.-> USERS_BP + LOGGING -.-> APP + DISCORD_BOT --> DISCORD_API + DISCORD_BOT -.-> MATCHES_BP + DISCORD_BOT -.-> TEAM_MATCHES_BP + DISCORD_BOT -.-> USERS_BP + SEED -.-> DB + BACKUP -.-> DB + + BLUEPRINTS --> TEMPLATES + TEMPLATES --> STATIC + + USER_M -.-> TRN_API + MONITOR --> HEALTH + DISCORD_APP --> DISCORD_BOT +
+
+ TLS termination
Static files
Rate limiting"] + end + + subgraph APP["Flask Application β create_app()"] + subgraph INGRESS["Incoming Middleware"] + BEFORE_REQ["before_request
force_https()"] + CSRF_CHECK["CSRF Validation"] + LIMITER_CHECK["Rate Limiter
200/day | 50/hr"] + LOGIN_CHECK["login_required
Permission checks"] + end + + subgraph BLUEPRINTS["Route Blueprints (8)"] + AUTH_BP["auth_bp /auth
login, register, logout"] + MAIN_BP["main_bp /
dashboard, privacy, terms"] + TRYOUTS_BP["tryouts_bp /tryouts
CRUD + register"] + TEAMS_BP["teams_bp /teams
OrgTeam CRUD"] + MATCHES_BP["matches_bp /matches
Tryout match CRUD"] + TEAM_MATCHES_BP["team_matches_bp /team-matches
Season match CRUD"] + USERS_BP["users_bp /users
User CRUD, contracts
disponibilities, one-on-one"] + EVALS_BP["evaluations_bp /evaluations
9-score evaluations"] + end + + subgraph EGRESS["Outgoing Middleware"] + AFTER_REQ["after_request
Security headers"] + ERRORS["Error Handlers
400 401 403 404 429 500"] + HEALTH["GET /health"] + end + end + + subgraph EXTENSIONS["Flask Extensions"] + DB_EXT["SQLAlchemy db"] + LOGIN_EXT["LoginManager"] + CSRF_EXT["CSRFProtect"] + LIMITER_EXT["Flask-Limiter"] + end + + subgraph MODELS["SQLAlchemy Models (7 subpackages)"] + USER_M["user_model/ β User hierarchy
User, Admin, Manager, Coach, Player, Scout"] + AVAIL_M["availability/ β BaseAvailability
PlayerDisponibility, CoachAvailability"] + MATCH_M["match_model/ β BaseMatch
Match, TeamMatch"] + PARTIC_M["participant/ β BaseParticipant
MatchParticipant, TeamMatchParticipant"] + ORG_M["org_team/ β OrgTeam, TeamPlayer"] + TRYOUT_M["tryout/ β Tryout, TryoutRegistration"] + TEAM_M["team/ β Team, TeamMember"] + STANDALONE_M["Standalone: Evaluation, Contract
UserGamertag, TeamNote, PersonalNote
OneOnOneRequest"] + end + + subgraph DB["Database"] + SQLITE["SQLite / PostgreSQL
DATABASE_URL"] + end + + subgraph UTILS["Utility Modules"] + VALIDATORS["validators.py
Input validation schemas"] + LOGGING["logging_config.py
Structured logging"] + DISCORD_BOT["discord_bot.py
Discord notifications
Reaction handling"] + SECURITY["security_scan.py
Security audit"] + SEED["seed.py
Database seeding"] + BACKUP["backup.py
Database backup"] + end + + subgraph TEMPLATES["Jinja2 Templates"] + PAGES["templates/pages/
~30 HTML pages"] + LAYOUTS["templates/layouts/
base, nav"] + ERRORS_TPL["templates/errors/
400-500 errors"] + end + + subgraph STATIC["Static Assets"] + CSS_F["static/css/"] + JS_F["static/js/"] + end + + subgraph EXTERNAL["External APIs"] + TRN_API["TRN / Tracker.gg
Gamertag profiles"] + DISCORD_API["Discord API
Bot notifications"] + end + + BROWSER --> NGINX + NGINX --> BEFORE_REQ + BEFORE_REQ --> CSRF_CHECK + CSRF_CHECK --> LIMITER_CHECK + LIMITER_CHECK --> LOGIN_CHECK + LOGIN_CHECK --> BLUEPRINTS + BLUEPRINTS --> AFTER_REQ + AFTER_REQ --> BROWSER + + AUTH_BP -.-> USER_M + TRYOUTS_BP -.-> TRYOUT_M + EVALS_BP -.-> STANDALONE_M + TEAMS_BP -.-> ORG_M + MATCHES_BP -.-> MATCH_M + TEAM_MATCHES_BP -.-> MATCH_M + USERS_BP -.-> USER_M + + MODELS --> DB + + DB_EXT --> MODELS + LOGIN_EXT --> USER_M + CSRF_EXT --> BLUEPRINTS + LIMITER_EXT --> BLUEPRINTS + + VALIDATORS -.-> AUTH_BP + VALIDATORS -.-> USERS_BP + LOGGING -.-> APP + DISCORD_BOT --> DISCORD_API + DISCORD_BOT -.-> MATCHES_BP + DISCORD_BOT -.-> TEAM_MATCHES_BP + DISCORD_BOT -.-> USERS_BP + SEED -.-> DB + BACKUP -.-> DB + + BLUEPRINTS --> TEMPLATES + TEMPLATES --> STATIC + + USER_M -.-> TRN_API + MONITOR --> HEALTH + DISCORD_APP --> DISCORD_BOT +
πΊοΈ Legend
+
+
+
+
+ Models (abstract) β BaseAvailability, BaseMatch, BaseParticipant
+
+
+
+ Subpackages β user_model, availability, match_model, participant, org_team, tryout, team
+
+
+
+ Blueprints β Flask route groups
+
+
+
+ Extensions β SQLAlchemy, Login, CSRF, Limiter
+
+
+
+ Utilities β Logging, validators, seed, backup, bot
+
+
+
+ External APIs β Discord API, TRN/Tracker.gg
+
+
+
+ Middleware β HTTPS redirect, security headers, errors
+
+
+ ββ Solid
+ = HTTP / data flow
+
+
+ β
β
Dashed
+ = Logical dependency
+
+