747 lines
21 KiB
HTML
747 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Team Tryouts — Architecture Documentation</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
<style>
|
|
:root {
|
|
--bg: #1a1a2e;
|
|
--surface: #16213e;
|
|
--primary: #0f3460;
|
|
--accent: #e94560;
|
|
--text: #eee;
|
|
--text-muted: #aaa;
|
|
--border: #2a2a4a;
|
|
--card-bg: #1e2a3a;
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
body {
|
|
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
header {
|
|
background: var(--surface);
|
|
border-bottom: 2px solid var(--accent);
|
|
padding: 1.5rem 2rem;
|
|
text-align: center;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
box-shadow: 0 2px 20px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
header h1 {
|
|
font-size: 1.8rem;
|
|
color: var(--accent);
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
header p {
|
|
color: var(--text-muted);
|
|
margin-top: 0.25rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
nav {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
margin-top: 1rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
nav a {
|
|
background: var(--primary);
|
|
color: var(--text);
|
|
padding: 0.5rem 1.5rem;
|
|
border-radius: 6px;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
transition: background 0.2s, transform 0.2s;
|
|
}
|
|
|
|
nav a:hover {
|
|
background: var(--accent);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
main {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
section {
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
section h2 {
|
|
color: var(--accent);
|
|
font-size: 1.5rem;
|
|
margin-bottom: 1rem;
|
|
padding-bottom: 0.5rem;
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
section h2 .icon {
|
|
font-size: 1.4rem;
|
|
}
|
|
|
|
.mermaid-wrapper {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 12px;
|
|
padding: 2rem;
|
|
overflow-x: auto;
|
|
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.mermaid-wrapper.full-width {
|
|
/* full bleed for wide diagrams */
|
|
}
|
|
|
|
.legend {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 1rem 1.5rem;
|
|
margin-top: 1rem;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.legend-dot {
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 3px;
|
|
display: inline-block;
|
|
}
|
|
|
|
.legend-dot.model { background: #4fc3f7; }
|
|
.legend-dot.blueprint { background: #ffb74d; }
|
|
.legend-dot.extension { background: #81c784; }
|
|
.legend-dot.utility { background: #ba68c8; }
|
|
.legend-dot.external { background: #e57373; }
|
|
.legend-dot.middleware { background: #4dd0e1; }
|
|
|
|
footer {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
color: var(--text-muted);
|
|
font-size: 0.8rem;
|
|
border-top: 1px solid var(--border);
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.stat-card {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-card .stat-num {
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.stat-card .stat-label {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
main { padding: 1rem; }
|
|
header h1 { font-size: 1.3rem; }
|
|
.mermaid-wrapper { padding: 1rem; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header>
|
|
<h1>🏆 Team Tryouts — Architecture Documentation</h1>
|
|
<p>UML Class Diagram & Functional System Architecture</p>
|
|
<nav>
|
|
<a href="#stats">Overview</a>
|
|
<a href="#uml">UML Class Diagram</a>
|
|
<a href="#functional">Functional Graph</a>
|
|
<a href="#legend">Legend</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
|
|
<!-- ============ STATS ============ -->
|
|
<section id="stats">
|
|
<h2><span class="icon">📊</span> Project Statistics</h2>
|
|
<div class="stats">
|
|
<div class="stat-card">
|
|
<div class="stat-num">19</div>
|
|
<div class="stat-label">SQLAlchemy Model Classes</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-num">8</div>
|
|
<div class="stat-label">Route Blueprints</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-num">4</div>
|
|
<div class="stat-label">Flask Extensions</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-num">2</div>
|
|
<div class="stat-label">Association Tables (M2M)</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-num">50+</div>
|
|
<div class="stat-label">Route Endpoints</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-num">8</div>
|
|
<div class="stat-label">Utility Modules</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ============ UML CLASS DIAGRAM ============ -->
|
|
<section id="uml">
|
|
<h2><span class="icon">📐</span> UML Class Diagram — All 19 Model Classes</h2>
|
|
<div class="mermaid-wrapper">
|
|
|
|
<div class="mermaid">
|
|
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
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ============ FUNCTIONAL ARCHITECTURE GRAPH ============ -->
|
|
<section id="functional">
|
|
<h2><span class="icon">🔀</span> Functional Architecture — Request Flow & Component Graph</h2>
|
|
<div class="mermaid-wrapper">
|
|
|
|
<div class="mermaid">
|
|
graph TD
|
|
subgraph CLIENT["🌐 Client Layer"]
|
|
BROWSER["Browser / User"]
|
|
DISCORD_APP["Discord App"]
|
|
MONITOR["Monitoring / LB"]
|
|
end
|
|
|
|
subgraph PROXY["🔄 Reverse Proxy"]
|
|
NGINX["Nginx<br/>(TLS termination,<br/>static files,<br/>rate limiting)"]
|
|
end
|
|
|
|
subgraph APP["🐍 Flask Application (create_app)"]
|
|
direction TB
|
|
|
|
subgraph INGRESS["Incoming"]
|
|
BEFORE_REQ["@before_request<br/>force_https()"]
|
|
CSRF_CHECK["CSRF Validate"]
|
|
LIMITER_CHECK["Rate Limiter<br/>200/day · 50/hr"]
|
|
LOGIN_CHECK["@login_required"]
|
|
end
|
|
|
|
subgraph BLUEPRINTS["📦 Route Blueprints (8 total)"]
|
|
AUTH_BP["auth_bp<br/>━━━━━━━<br/>⁕ /auth/login<br/>⁕ /auth/register<br/>⁕ /auth/logout<br/>⁕ /auth/profile<br/>⁕ /auth/change-password<br/>⁕ /auth/delete-account"]
|
|
MAIN_BP["main_bp<br/>━━━━━━━<br/>⁕ /<br/>⁕ /dashboard<br/>⁕ /privacy<br/>⁕ /terms"]
|
|
TRYOUTS_BP["tryouts_bp<br/>━━━━━━━<br/>⁕ /tryouts/<br/>⁕ /tryouts/create<br/>⁕ /tryouts/{id}<br/>⁕ /tryouts/{id}/register<br/>⁕ /tryouts/{id}/edit<br/>⁕ /tryouts/{id}/delete"]
|
|
TEAMS_BP["teams_bp<br/>━━━━━━━<br/>⁕ /teams/<br/>⁕ /teams/create<br/>⁕ /teams/{id}<br/>⁕ /teams/{id}/edit<br/>⁕ /teams/{id}/delete<br/>⁕ /teams/{id}/add-player"]
|
|
MATCHES_BP["matches_bp<br/>━━━━━━━<br/>⁕ /matches/<br/>⁕ /matches/create<br/>⁕ /matches/{id}<br/>⁕ /matches/{id}/edit"]
|
|
TEAM_MATCHES_BP["team_matches_bp<br/>━━━━━━━<br/>⁕ /team-matches/<br/>⁕ /team-matches/create<br/>⁕ /team-matches/{id}<br/>⁕ /team-matches/{id}/edit<br/>⁕ /team-matches/{id}/delete"]
|
|
USERS_BP["users_bp<br/>━━━━━━━<br/>⁕ /users/<br/>⁕ /users/create<br/>⁕ /users/{id}<br/>⁕ /users/{id}/delete<br/>⁕ /users/disponibilities/<br/>⁕ /users/coach-availability/<br/>⁕ /users/api/gamertags"]
|
|
EVALS_BP["evaluations_bp<br/>━━━━━━━<br/>⁕ /evaluations/<br/>⁕ /evaluations/create<br/>⁕ /evaluations/{id}<br/>⁕ /evaluations/{id}/edit<br/>⁕ /evaluations/{id}/delete"]
|
|
end
|
|
|
|
subgraph EGRESS["Outgoing"]
|
|
AFTER_REQ["@after_request<br/>add_security_headers()"]
|
|
ERROR_HANDLERS["Error Handlers<br/>400 · 401 · 403 · 404<br/>429 · 500 · HTTPException"]
|
|
HEALTH["GET /health"]
|
|
end
|
|
end
|
|
|
|
subgraph EXTENSIONS["🔌 Flask Extensions"]
|
|
DB_EXT["SQLAlchemy<br/>(db)"]
|
|
LOGIN_EXT["Flask-Login<br/>(login_manager)"]
|
|
CSRF_EXT["CSRFProtect<br/>(csrf)"]
|
|
LIMITER_EXT["Flask-Limiter<br/>(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<br/>(SQLALCHEMY_DATABASE_URI)"]
|
|
end
|
|
|
|
subgraph UTILS["🛠️ Utility Modules"]
|
|
VALIDATORS["validators.py<br/>Password/username validation"]
|
|
LOGGING["logging_config.py<br/>Structured logging"]
|
|
DISCORD_BOT["discord_bot.py<br/>Discord notifications<br/>+ reaction handling"]
|
|
SECURITY["security_scan.py<br/>Security audit tool"]
|
|
SEED["seed.py<br/>Database seeding"]
|
|
BACKUP["backup.py<br/>Database backup"]
|
|
MIGRATE["migrate_usernames.py<br/>Username migration"]
|
|
end
|
|
|
|
subgraph TEMPLATES["🖼️ Jinja2 Templates"]
|
|
PAGES["templates/pages/<br/>(~30 HTML pages)"]
|
|
LAYOUTS["templates/layouts/<br/>(base, nav)"]
|
|
ERRORS_TPL["templates/errors/<br/>(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<br/>(gamertag profiles)"]
|
|
DISCORD_API["Discord API<br/>(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
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ============ LEGEND ============ -->
|
|
<section id="legend">
|
|
<h2><span class="icon">🗺️</span> Legend</h2>
|
|
<div class="legend">
|
|
<div class="legend-item">
|
|
<span class="legend-dot model"></span>
|
|
<span><strong>SQLAlchemy Models</strong> — Database entity classes</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span class="legend-dot blueprint"></span>
|
|
<span><strong>Route Blueprints</strong> — Flask route groups</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span class="legend-dot extension"></span>
|
|
<span><strong>Flask Extensions</strong> — SQLAlchemy, Login, CSRF, Limiter</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span class="legend-dot utility"></span>
|
|
<span><strong>Utility Modules</strong> — Logging, validators, seeding, backups</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span class="legend-dot external"></span>
|
|
<span><strong>External APIs</strong> — Discord, TRN/Tracker.gg</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span class="legend-dot middleware"></span>
|
|
<span><strong>Middleware</strong> — Security headers, HTTPS redirect, error handlers</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span style="font-weight:bold;">─── Solid arrow</span>
|
|
<span>= HTTP request flow</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<span style="font-weight:bold; color:#999;">- - - Dashed arrow</span>
|
|
<span>= Data access / logical dependency</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
</main>
|
|
|
|
<footer>
|
|
<p>Team Tryouts © 2026 — Generated from codebase analysis</p>
|
|
<p>19 SQLAlchemy models · 8 blueprints · 4 Flask extensions · 8 utility modules</p>
|
|
</footer>
|
|
|
|
<script>
|
|
mermaid.initialize({
|
|
startOnLoad: true,
|
|
theme: 'dark',
|
|
themeVariables: {
|
|
primaryColor: '#0f3460',
|
|
primaryTextColor: '#eee',
|
|
primaryBorderColor: '#4fc3f7',
|
|
lineColor: '#4fc3f7',
|
|
secondaryColor: '#1e2a3a',
|
|
tertiaryColor: '#16213e',
|
|
noteBkgColor: '#1a1a2e',
|
|
noteTextColor: '#aaa',
|
|
fontFamily: 'Segoe UI, system-ui, -apple-system, sans-serif',
|
|
fontSize: '13px',
|
|
},
|
|
class: {
|
|
useMaxWidth: false,
|
|
},
|
|
flowchart: {
|
|
useMaxWidth: false,
|
|
htmlLabels: true,
|
|
curve: 'basis',
|
|
padding: 20,
|
|
nodeSpacing: 30,
|
|
rankSpacing: 60,
|
|
defaultRenderer: 'dagre-wrapper',
|
|
},
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html> |