π 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