π Project Statistics
+
+
+
+
+ 19
+ SQLAlchemy Model Classes
+
+
+ 8
+ Route Blueprints
+
+
+ 4
+ Flask Extensions
+
+
+ 2
+ Association Tables (M2M)
+
+
+ 50+
+ Route Endpoints
+
+
+ 8
+ Utility Modules
+ π UML Class Diagram β All 19 Model Classes
+
+
+
+
+classDiagram
+ 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()
+ +has_role()
+ +can_evaluate()
+ +can_manage_users()
+ +can_manage_tryouts()
+ +can_manage_teams()
+ +can_schedule_matches()
+ +can_manage_this_tryout()
+ +can_manage_this_org_team()
+ +get_gamertags()
+ +get_org_teams()
+ }
+ 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 Match {
+ +int id
+ +int tryout_id
+ +str title
+ +str description
+ +date date
+ +time start_time
+ +time end_time
+ +str location
+ +str status
+ +str match_type
+ +int created_by
+ +datetime created_at
+ +int team1_id
+ +int team2_id
+ +get_participating_players()
+ }
+ class PlayerDisponibility {
+ +int id
+ +int player_id
+ +int day_of_week
+ +time start_time
+ +time end_time
+ +datetime created_at
+ +datetime updated_at
+ }
+ class MatchParticipant {
+ +int id
+ +int match_id
+ +int player_id
+ +int team_side
+ +str position
+ +bool attendance_confirmed
+ +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 CoachAvailability {
+ +int id
+ +int coach_id
+ +int day_of_week
+ +time start_time
+ +time end_time
+ +datetime created_at
+ +datetime updated_at
+ }
+ 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 TeamMatch {
+ +int id
+ +int org_team_id
+ +str title
+ +str description
+ +str opponent
+ +date date
+ +time start_time
+ +time end_time
+ +str location
+ +str status
+ +int created_by
+ +datetime created_at
+ +get_confirmed_count()
+ }
+ class TeamMatchParticipant {
+ +int id
+ +int team_match_id
+ +int player_id
+ +bool is_confirmed
+ +datetime added_at
+ }
+ class load_user {
+ +load_user(user_id)
+ }
+
+ User "1" --> "*" UserGamertag : gamertags
+ User "1" --> "*" Tryout : created_by
+ User "1" --> "*" Tryout : managed_tryouts
+ User "1" --> "*" Tryout : coached_tryouts
+ User "1" --> "*" Evaluation : evaluations_given
+ User "1" --> "*" Evaluation : evaluations_received
+ User "1" --> "*" TryoutRegistration : tryout_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 : coach_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 : matches_as_team1
+ Team "1" --> "*" Match : matches_as_team2
+ Match "1" --> "*" MatchParticipant : participants
+ Match "1" --> "*" PersonalNote : notes
+
+ TeamMatch "1" --> "*" TeamMatchParticipant : participants
+
+ User .. load_user : loads
+
+
+ π 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)"] + direction TB + + subgraph INGRESS["Incoming"] + BEFORE_REQ["@before_request
force_https()"] + CSRF_CHECK["CSRF Validate"] + LIMITER_CHECK["Rate Limiter
200/day Β· 50/hr"] + LOGIN_CHECK["@login_required"] + end + + subgraph BLUEPRINTS["π¦ Route Blueprints (8 total)"] + AUTH_BP["auth_bp
βββββββ
β /auth/login
β /auth/register
β /auth/logout
β /auth/profile
β /auth/change-password
β /auth/delete-account"] + MAIN_BP["main_bp
βββββββ
β /
β /dashboard
β /privacy
β /terms"] + TRYOUTS_BP["tryouts_bp
βββββββ
β /tryouts/
β /tryouts/create
β /tryouts/{id}
β /tryouts/{id}/register
β /tryouts/{id}/edit
β /tryouts/{id}/delete"] + TEAMS_BP["teams_bp
βββββββ
β /teams/
β /teams/create
β /teams/{id}
β /teams/{id}/edit
β /teams/{id}/delete
β /teams/{id}/add-player"] + MATCHES_BP["matches_bp
βββββββ
β /matches/
β /matches/create
β /matches/{id}
β /matches/{id}/edit"] + TEAM_MATCHES_BP["team_matches_bp
βββββββ
β /team-matches/
β /team-matches/create
β /team-matches/{id}
β /team-matches/{id}/edit
β /team-matches/{id}/delete"] + USERS_BP["users_bp
βββββββ
β /users/
β /users/create
β /users/{id}
β /users/{id}/delete
β /users/disponibilities/
β /users/coach-availability/
β /users/api/gamertags"] + EVALS_BP["evaluations_bp
βββββββ
β /evaluations/
β /evaluations/create
β /evaluations/{id}
β /evaluations/{id}/edit
β /evaluations/{id}/delete"] + end + + subgraph EGRESS["Outgoing"] + AFTER_REQ["@after_request
add_security_headers()"] + ERROR_HANDLERS["Error Handlers
400 Β· 401 Β· 403 Β· 404
429 Β· 500 Β· HTTPException"] + HEALTH["GET /health"] + end + end + + subgraph EXTENSIONS["π Flask Extensions"] + DB_EXT["SQLAlchemy
(db)"] + LOGIN_EXT["Flask-Login
(login_manager)"] + CSRF_EXT["CSRFProtect
(csrf)"] + LIMITER_EXT["Flask-Limiter
(limiter)"] + end + + subgraph MODELS["ποΈ SQLAlchemy Models (models.py)"] + direction LR + USER_M["User"] + TRYOUT_M["Tryout"] + EVAL_M["Evaluation"] + ORGTEAM_M["OrgTeam"] + TEAM_M["Team"] + MATCH_M["Match"] + TEAMMATCH_M["TeamMatch"] + OTHERS_M["...15 more classes"] + end + + subgraph DB["πΎ Database"] + SQLITE["SQLite / PostgreSQL
(SQLALCHEMY_DATABASE_URI)"] + end + + subgraph UTILS["π οΈ Utility Modules"] + VALIDATORS["validators.py
Password/username validation"] + LOGGING["logging_config.py
Structured logging"] + DISCORD_BOT["discord_bot.py
Discord notifications
+ reaction handling"] + SECURITY["security_scan.py
Security audit tool"] + SEED["seed.py
Database seeding"] + BACKUP["backup.py
Database backup"] + MIGRATE["migrate_usernames.py
Username migration"] + end + + subgraph TEMPLATES["πΌοΈ Jinja2 Templates"] + PAGES["templates/pages/
(~30 HTML pages)"] + LAYOUTS["templates/layouts/
(base, nav)"] + ERRORS_TPL["templates/errors/
(400, 401, 403, 404, 429, 500)"] + 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 + + %% ========== FLOW ========== + 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 + + %% Blueprint to models + AUTH_BP -.-> USER_M + TRYOUTS_BP -.-> TRYOUT_M + EVALS_BP -.-> EVAL_M + TEAMS_BP -.-> ORGTEAM_M + MATCHES_BP -.-> MATCH_M + TEAM_MATCHES_BP -.-> TEAMMATCH_M + USERS_BP -.-> USER_M + + %% Models to DB + MODELS --> DB + + %% Extensions + DB_EXT --> MODELS + LOGIN_EXT --> USER_M + CSRF_EXT --> BLUEPRINTS + LIMITER_EXT --> BLUEPRINTS + + %% Utilities + 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 + + %% Templates & Static + BLUEPRINTS --> TEMPLATES + TEMPLATES --> STATIC + + %% External + USER_M -.-> TRN_API + MONITOR --> HEALTH + + %% Discord + DISCORD_APP --> DISCORD_BOT +
+
+ (TLS termination,
static files,
rate limiting)"] + end + + subgraph APP["π Flask Application (create_app)"] + direction TB + + subgraph INGRESS["Incoming"] + BEFORE_REQ["@before_request
force_https()"] + CSRF_CHECK["CSRF Validate"] + LIMITER_CHECK["Rate Limiter
200/day Β· 50/hr"] + LOGIN_CHECK["@login_required"] + end + + subgraph BLUEPRINTS["π¦ Route Blueprints (8 total)"] + AUTH_BP["auth_bp
βββββββ
β /auth/login
β /auth/register
β /auth/logout
β /auth/profile
β /auth/change-password
β /auth/delete-account"] + MAIN_BP["main_bp
βββββββ
β /
β /dashboard
β /privacy
β /terms"] + TRYOUTS_BP["tryouts_bp
βββββββ
β /tryouts/
β /tryouts/create
β /tryouts/{id}
β /tryouts/{id}/register
β /tryouts/{id}/edit
β /tryouts/{id}/delete"] + TEAMS_BP["teams_bp
βββββββ
β /teams/
β /teams/create
β /teams/{id}
β /teams/{id}/edit
β /teams/{id}/delete
β /teams/{id}/add-player"] + MATCHES_BP["matches_bp
βββββββ
β /matches/
β /matches/create
β /matches/{id}
β /matches/{id}/edit"] + TEAM_MATCHES_BP["team_matches_bp
βββββββ
β /team-matches/
β /team-matches/create
β /team-matches/{id}
β /team-matches/{id}/edit
β /team-matches/{id}/delete"] + USERS_BP["users_bp
βββββββ
β /users/
β /users/create
β /users/{id}
β /users/{id}/delete
β /users/disponibilities/
β /users/coach-availability/
β /users/api/gamertags"] + EVALS_BP["evaluations_bp
βββββββ
β /evaluations/
β /evaluations/create
β /evaluations/{id}
β /evaluations/{id}/edit
β /evaluations/{id}/delete"] + end + + subgraph EGRESS["Outgoing"] + AFTER_REQ["@after_request
add_security_headers()"] + ERROR_HANDLERS["Error Handlers
400 Β· 401 Β· 403 Β· 404
429 Β· 500 Β· HTTPException"] + HEALTH["GET /health"] + end + end + + subgraph EXTENSIONS["π Flask Extensions"] + DB_EXT["SQLAlchemy
(db)"] + LOGIN_EXT["Flask-Login
(login_manager)"] + CSRF_EXT["CSRFProtect
(csrf)"] + LIMITER_EXT["Flask-Limiter
(limiter)"] + end + + subgraph MODELS["ποΈ SQLAlchemy Models (models.py)"] + direction LR + USER_M["User"] + TRYOUT_M["Tryout"] + EVAL_M["Evaluation"] + ORGTEAM_M["OrgTeam"] + TEAM_M["Team"] + MATCH_M["Match"] + TEAMMATCH_M["TeamMatch"] + OTHERS_M["...15 more classes"] + end + + subgraph DB["πΎ Database"] + SQLITE["SQLite / PostgreSQL
(SQLALCHEMY_DATABASE_URI)"] + end + + subgraph UTILS["π οΈ Utility Modules"] + VALIDATORS["validators.py
Password/username validation"] + LOGGING["logging_config.py
Structured logging"] + DISCORD_BOT["discord_bot.py
Discord notifications
+ reaction handling"] + SECURITY["security_scan.py
Security audit tool"] + SEED["seed.py
Database seeding"] + BACKUP["backup.py
Database backup"] + MIGRATE["migrate_usernames.py
Username migration"] + end + + subgraph TEMPLATES["πΌοΈ Jinja2 Templates"] + PAGES["templates/pages/
(~30 HTML pages)"] + LAYOUTS["templates/layouts/
(base, nav)"] + ERRORS_TPL["templates/errors/
(400, 401, 403, 404, 429, 500)"] + 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 + + %% ========== FLOW ========== + 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 + + %% Blueprint to models + AUTH_BP -.-> USER_M + TRYOUTS_BP -.-> TRYOUT_M + EVALS_BP -.-> EVAL_M + TEAMS_BP -.-> ORGTEAM_M + MATCHES_BP -.-> MATCH_M + TEAM_MATCHES_BP -.-> TEAMMATCH_M + USERS_BP -.-> USER_M + + %% Models to DB + MODELS --> DB + + %% Extensions + DB_EXT --> MODELS + LOGIN_EXT --> USER_M + CSRF_EXT --> BLUEPRINTS + LIMITER_EXT --> BLUEPRINTS + + %% Utilities + 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 + + %% Templates & Static + BLUEPRINTS --> TEMPLATES + TEMPLATES --> STATIC + + %% External + USER_M -.-> TRN_API + MONITOR --> HEALTH + + %% Discord + DISCORD_APP --> DISCORD_BOT +
πΊοΈ Legend
+
+
+
+
+ SQLAlchemy Models β Database entity classes
+
+
+
+ Route Blueprints β Flask route groups
+
+
+
+ Flask Extensions β SQLAlchemy, Login, CSRF, Limiter
+
+
+
+ Utility Modules β Logging, validators, seeding, backups
+
+
+
+ External APIs β Discord, TRN/Tracker.gg
+
+
+
+ Middleware β Security headers, HTTPS redirect, error handlers
+
+
+ βββ Solid arrow
+ = HTTP request flow
+
+
+ - - - Dashed arrow
+ = Data access / logical dependency
+
+