πŸ† Team Tryouts β€” Architecture v3 (Refactored)

UML Class Diagram & Functional Graph β€” 7 Subpackages + 6 Standalone Models

πŸ“Š 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

πŸ—ΊοΈ 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