← Reports

SafairBru Structural Health Report

FastAPI + HTMX Rewrite -- Architecture Trace & Gap Analysis
Generated 2026-04-09 09:12 | Source: Graphify graph.json + gap-report.json

Executive Summary

Feature Mismatch Alert

The original ticket asked to trace File Search, Chat, and Timelines features. None of these exist in SafairBru. SafairBru is a forecasting gamification platform for FlySafair. Its actual features are: leaderboard, forecast submission, profile/achievements, admin panel, AI content generation, API endpoints, auth, email notifications, and Dropbox data integration.

372
Old Functions (app.py)
359
New Functions (staging)
79
Ported (21.2%)
268
Critical Gaps
25
Streamlit-Only Gaps
0
Working Features
9
Partial Features
0
Broken Features

Key finding: Graphify detected zero cross-file call edges between router and domain layers. All 1,247 call edges are intra-file. This means either (a) the graph extraction missed Python import-based calls, or (b) routers use dependency injection / request-time resolution that static analysis cannot trace. Every feature with a multi-layer chain is marked PARTIAL because cross-file connectivity cannot be confirmed from the graph alone.

Leaderboard

Monthly/annual/all-time leaderboards with scoring and ranking
⚠ PARTIAL
Router
routers/pages.py (15 functions)
leaderboard_page(), save_fyi_preferences()
|
No graph edges from routers/pages.py to domain/leaderboard.py (cross-file calls not captured)
v
Domain
domain/leaderboard.py (10 functions)
get_monthly_leaderboard(), get_annual_leaderboard(), get_alltime_leaderboard(), get_leaderboard_data()
|
No graph edges from domain/leaderboard.py to domain/scoring.py (cross-file calls not captured)
v
Scoring
domain/scoring.py (10 functions)
assign_ranking_points(), calculate_monthly_scores(), calculate_annual(), calculate_alltime()
|
No graph edges from domain/scoring.py to domain/firebase.py (cross-file calls not captured)
v
Data
domain/firebase.py (6 functions)
get_db(), _init_firebase()

Dead-End Nodes (no outgoing calls)

  • domain/leaderboard.py :: get_rank_movements()
  • domain/leaderboard.py :: get_alltime_leaderboard()
  • domain/leaderboard.py :: get_alltime_leaderboard_by_metric()
  • domain/leaderboard.py :: get_live_mtd_leaderboard()
  • domain/leaderboard.py :: get_medal_table()
  • domain/scoring.py :: bonus_rules_list_to_dict()
  • domain/scoring.py :: extract_monthly_actuals()
  • domain/scoring.py :: compute_mtd_data()
  • ... and 7 more

Unreachable from Routers (no incoming from router layer)

  • domain/leaderboard.py :: get_locked_months()
  • domain/leaderboard.py :: get_monthly_leaderboard()
  • domain/leaderboard.py :: get_rank_movements()
  • domain/leaderboard.py :: get_annual_leaderboard()
  • domain/leaderboard.py :: get_alltime_leaderboard()
  • domain/leaderboard.py :: get_annual_leaderboard_by_metric()
  • domain/leaderboard.py :: get_alltime_leaderboard_by_metric()
  • domain/leaderboard.py :: get_live_mtd_leaderboard()
  • ... and 14 more

Unmigrated Functions (117 critical gaps)

  • main() 25 deps (Leaderboard Calculation)
  • get_cached_data() 19 deps (Leaderboard Calculation)
  • set_cached_data() 19 deps (Leaderboard Calculation)
  • build_results_published_email_content() 15 deps (Leaderboard Calculation)
  • get_user_profile_stats() 14 deps (Leaderboard Calculation)
  • is_ai_error_response() 13 deps (Leaderboard Display & Export)
  • get_user_leaderboard_rank_summary() 12 deps (Leaderboard Calculation)
  • get_competition_ai_guardrails() 11 deps (Leaderboard Display & Export)
  • warm_runtime_leaderboard_caches() 11 deps (Leaderboard Calculation)
  • get_user_current_rank() 9 deps (Leaderboard Calculation)
  • ... and 107 more

Forecast

KPI forecast submission and management
⚠ PARTIAL
Router
routers/pages.py (15 functions)
forecast_page(), forecast_submit()
|
No graph edges from routers/pages.py to domain/forecasts.py (cross-file calls not captured)
v
Domain
domain/forecasts.py (7 functions)
save_forecast(), get_user_forecast(), get_all_forecasts_for_month()
|
No graph edges from domain/forecasts.py to domain/firebase.py (cross-file calls not captured)
v
Data
domain/firebase.py (6 functions)
get_db()

Dead-End Nodes (no outgoing calls)

  • domain/forecasts.py :: get_user_forecast()
  • domain/forecasts.py :: save_forecast()
  • domain/forecasts.py :: is_month_locked()
  • domain/forecasts.py :: get_current_month()
  • domain/forecasts.py :: get_all_forecasts_for_month()
  • domain/forecasts.py :: delete_forecast()
  • domain/firebase.py :: get_db()
  • domain/firebase.py :: async_get()
  • ... and 2 more

Unreachable from Routers (no incoming from router layer)

  • domain/forecasts.py :: get_user_forecast()
  • domain/forecasts.py :: save_forecast()
  • domain/forecasts.py :: is_month_locked()
  • domain/forecasts.py :: get_current_month()
  • domain/forecasts.py :: get_all_forecasts_for_month()
  • domain/forecasts.py :: delete_forecast()
  • domain/firebase.py :: get_db()
  • domain/firebase.py :: async_get()
  • ... and 2 more

Unmigrated Functions (32 critical gaps)

  • build_forecast_lock_email_content() 12 deps (Admin & Cache Management)
  • get_forecast_submission_commentary() 6 deps (Leaderboard Display & Export)
  • clear_forecast_related_caches() 6 deps (Admin & Cache Management)
  • calculate_forecast_twins() 6 deps (Settings Module)
  • .clear() 5 deps (Forecasts Module)
  • .get_entries() 3 deps (Forecasts Module)
  • _build_forecast_rows_for_metric() 3 deps (Core App Logic & Scoring)
  • .__new__() 1 deps (Forecasts Module)
  • .__init__() 1 deps (Forecasts Module)
  • .emit() 1 deps (Forecasts Module)
  • ... and 22 more

Profile

User profile with achievements and ranking summary
⚠ PARTIAL
Router
routers/pages.py (15 functions)
profile_page()
|
No graph edges from routers/pages.py to domain/achievements.py (cross-file calls not captured)
v
Domain-Achievements
domain/achievements.py (2 functions)
get_user_achievements(), check_achievements()
|
No graph edges from domain/achievements.py to domain/leaderboard.py (cross-file calls not captured)
v
Domain-Leaderboard
domain/leaderboard.py (10 functions)
get_monthly_leaderboard()

Dead-End Nodes (no outgoing calls)

  • domain/achievements.py :: get_user_achievements()
  • domain/leaderboard.py :: get_rank_movements()
  • domain/leaderboard.py :: get_alltime_leaderboard()
  • domain/leaderboard.py :: get_alltime_leaderboard_by_metric()
  • domain/leaderboard.py :: get_live_mtd_leaderboard()
  • domain/leaderboard.py :: get_medal_table()

Unreachable from Routers (no incoming from router layer)

  • domain/achievements.py :: get_user_achievements()
  • domain/leaderboard.py :: get_locked_months()
  • domain/leaderboard.py :: get_monthly_leaderboard()
  • domain/leaderboard.py :: get_rank_movements()
  • domain/leaderboard.py :: get_annual_leaderboard()
  • domain/leaderboard.py :: get_alltime_leaderboard()
  • domain/leaderboard.py :: get_annual_leaderboard_by_metric()
  • domain/leaderboard.py :: get_alltime_leaderboard_by_metric()
  • ... and 2 more

Unmigrated Functions (9 critical gaps)

  • get_user_profile_stats() 14 deps (Leaderboard Calculation)
  • get_user_leaderboard_rank_summary() 12 deps (Leaderboard Calculation)
  • get_user_current_rank() 9 deps (Leaderboard Calculation)
  • Generate a short rivalry quip for the Profile > Rivals section. 1 deps (Leaderboard Display & Export)
  • Generate a short profile rankings summary line with mechanics-aware humor. 1 deps (Leaderboard Display & Export)
  • Generate one playful line about achievements progression. 1 deps (Leaderboard Display & Export)
  • Get comprehensive profile stats for a user (cached for 30 min) 1 deps (Leaderboard Calculation)
  • Calculate achievements/badges for a user with expanded criteria (cached) 1 deps (Settings Module)
  • Render profile dashboard with rankings and achievements - Modernized with tabs 1 deps (Leaderboard Display & Export)

Admin

Admin panel: user management, scoring rules, calendar, data, AI config
⚠ PARTIAL
Router
routers/admin.py (33 functions)
admin_index(), admin_users(), admin_bonus_rules(), admin_calendar(), admin_stats(), admin_logs(), admin_data_preview(), admin_leaderboard_config(), admin_ai_config(), admin_email()
|
No graph edges from routers/admin.py to domain/settings.py (cross-file calls not captured)
v
Domain-Settings
domain/settings.py (24 functions)
get_settings(), get_leaderboard_flags(), save_settings()
|
No graph edges from domain/settings.py to domain/users.py (cross-file calls not captured)
v
Domain-Users
domain/users.py (5 functions)
create_user(), get_all_users(), update_user_status()
|
No graph edges from domain/users.py to domain/scheduler.py (cross-file calls not captured)
v
Domain-Scheduler
domain/scheduler.py (14 functions)
carry_over_forecasts(), auto_lock_previous_month()

Dead-End Nodes (no outgoing calls)

  • domain/settings.py :: get_setting_compat_sync()
  • domain/settings.py :: is_forecast_open_sync()
  • domain/settings.py :: get_forecast_month_sync()
  • domain/settings.py :: set_setting()
  • domain/settings.py :: get_bonus_rules()
  • domain/settings.py :: save_bonus_rules()
  • domain/settings.py :: is_forecast_open()
  • domain/settings.py :: get_forecast_month()
  • ... and 18 more

Unreachable from Routers (no incoming from router layer)

  • domain/settings.py :: get_settings()
  • domain/settings.py :: get_setting()
  • domain/settings.py :: get_legacy_setting()
  • domain/settings.py :: get_setting_compat()
  • domain/settings.py :: get_settings_sync()
  • domain/settings.py :: get_setting_sync()
  • domain/settings.py :: get_legacy_setting_sync()
  • domain/settings.py :: get_setting_compat_sync()
  • ... and 27 more

Unmigrated Functions (75 critical gaps)

  • clear_cache() 18 deps (Admin & Cache Management)
  • build_forecast_lock_email_content() 12 deps (Admin & Cache Management)
  • request_password_reset_for_user() 12 deps (Admin & Cache Management)
  • save_local_data() 9 deps (Leaderboard Router)
  • get_user() 9 deps (Admin & Cache Management)
  • ensure_firebase_auth_user() 8 deps (Admin & Cache Management)
  • sync_password_to_firebase_auth() 8 deps (Admin & Cache Management)
  • get_previous_leaderboard_state() 7 deps (Admin Router)
  • save_leaderboard_state() 7 deps (Admin Router)
  • send_firebase_password_reset_email() 7 deps (Admin & Cache Management)
  • ... and 65 more

AI Content

AI-generated commentary, tips, roasts, and analysis
⚠ PARTIAL
Router
routers/fragments.py (25 functions)
ai_leaderboard_commentary(), ai_profile_commentary(), ai_forecast_banter()
|
No graph edges from routers/fragments.py to domain/ai.py (cross-file calls not captured)
v
Domain
domain/ai.py (16 functions)
generate_ai_content(), generate_leaderboard_commentary(), generate_profile_commentary(), generate_forecast_banter(), build_tone_directive()

Dead-End Nodes (no outgoing calls)

  • domain/ai.py :: _openai_generate()
  • domain/ai.py :: _gemini_generate()
  • domain/ai.py :: generate_leaderboard_commentary()
  • domain/ai.py :: generate_profile_commentary()
  • domain/ai.py :: generate_forecast_banter()
  • domain/ai.py :: generate_user_roast()
  • domain/ai.py :: generate_month_end_commentary()
  • domain/ai.py :: generate_annual_commentary()
  • ... and 5 more

Unreachable from Routers (no incoming from router layer)

  • domain/ai.py :: build_tone_directive()
  • domain/ai.py :: generate_ai_content()
  • domain/ai.py :: generate_leaderboard_commentary()
  • domain/ai.py :: generate_profile_commentary()
  • domain/ai.py :: generate_forecast_banter()
  • domain/ai.py :: generate_user_roast()
  • domain/ai.py :: generate_month_end_commentary()
  • domain/ai.py :: generate_annual_commentary()
  • ... and 5 more

Unmigrated Functions (4 critical gaps)

  • is_ai_error_response() 13 deps (Leaderboard Display & Export)
  • get_competition_ai_guardrails() 11 deps (Leaderboard Display & Export)
  • get_ai_roast() 7 deps (Leaderboard Display & Export)
  • get_ai_performance_analysis() 7 deps (Leaderboard Display & Export)

Dropbox Data

External data fetch from Dropbox (actuals/sales)
⚠ PARTIAL
Domain
domain/dropbox.py (5 functions)
get_dropbox_data(), _fetch_from_dropbox(), get_latest_month_data()

Dead-End Nodes (no outgoing calls)

  • domain/dropbox.py :: _fetch_from_dropbox()
  • domain/dropbox.py :: get_latest_month_data()
  • domain/dropbox.py :: get_data_freshness()

Unreachable from Routers (no incoming from router layer)

  • domain/dropbox.py :: get_dropbox_data()
  • domain/dropbox.py :: get_latest_month_data()
  • domain/dropbox.py :: get_data_freshness()

Unmigrated Functions (10 critical gaps)

  • force_refresh_dropbox_data() 8 deps (Leaderboard Calculation)
  • get_sales_daily_preview() 8 deps (Leaderboard Calculation)
  • get_latest_sales_date() 7 deps (Leaderboard Calculation)
  • _cached_dropbox_fetch() 5 deps (Settings Module)
  • _fetch_dropbox_data_internal() 4 deps (Settings Module)
  • extract_latest_sales_date_from_df() 4 deps (Leaderboard Calculation)
  • Automatically lock the previous month's actuals from Dropbox on the 1st of each 1 deps (Leaderboard Calculation)
  • Internal function to actually fetch data from Dropbox 1 deps (Settings Module)
  • Fetch Dropbox data (thread-safe, shared across sessions). Adds a session_st 1 deps (Leaderboard Calculation)
  • Clear cache and force a fresh fetch from Dropbox 1 deps (Leaderboard Calculation)

Auth

Login, session management, password reset
⚠ PARTIAL
Router
routers/pages.py (15 functions)
login_page(), login_submit(), logout(), password_change_page(), password_change_submit()
|
No graph edges from routers/pages.py to domain/auth.py (cross-file calls not captured)
v
Domain
domain/auth.py (13 functions)
verify_password(), create_session_token(), validate_session_token(), set_user_password(), create_login_session()
|
No graph edges from domain/auth.py to domain/firebase.py (cross-file calls not captured)
v
Data
domain/firebase.py (6 functions)
get_db()

Dead-End Nodes (no outgoing calls)

  • domain/auth.py :: verify_password()
  • domain/auth.py :: set_user_password()
  • domain/auth.py :: needs_password_change()
  • domain/auth.py :: get_user_by_username()
  • domain/auth.py :: get_user_by_email()
  • domain/auth.py :: is_user_approved()
  • domain/auth.py :: create_login_session()
  • domain/auth.py :: validate_session_token()
  • ... and 6 more

Unreachable from Routers (no incoming from router layer)

  • domain/auth.py :: verify_password()
  • domain/auth.py :: set_user_password()
  • domain/auth.py :: create_session_token()
  • domain/auth.py :: needs_password_change()
  • domain/auth.py :: get_user_by_username()
  • domain/auth.py :: get_user_by_email()
  • domain/auth.py :: is_user_approved()
  • domain/auth.py :: create_login_session()
  • ... and 7 more

Unmigrated Functions (11 critical gaps)

  • request_password_reset_for_user() 12 deps (Admin & Cache Management)
  • ensure_firebase_auth_user() 8 deps (Admin & Cache Management)
  • send_firebase_password_reset_email() 7 deps (Admin & Cache Management)
  • firebase_auth_user_exists() 4 deps (Settings Module)
  • save_login_token() 4 deps (Data Import (Actuals))
  • delete_login_token() 4 deps (Data Import (Actuals))
  • generate_login_token() 3 deps (Core App Logic & Scoring)
  • Generate a secure random login token 1 deps (Core App Logic & Scoring)
  • Save a login token for persistent login 1 deps (Data Import (Actuals))
  • Validate a login token and return username if valid 1 deps (Settings Module)
  • ... and 1 more

Email

Event emails, winner announcements, SMTP/Resend delivery
⚠ PARTIAL
Domain
domain/email.py (20 functions)
send_event_email(), send_winners_announcement(), get_email_config(), get_email_recipients(), _send_via_resend(), _send_via_smtp()

Dead-End Nodes (no outgoing calls)

  • domain/email.py :: save_email_config()
  • domain/email.py :: send_winners_announcement()
  • domain/email.py :: _send_app_event_email()

Unreachable from Routers (no incoming from router layer)

  • domain/email.py :: get_email_config()
  • domain/email.py :: save_email_config()
  • domain/email.py :: get_email_recipients()
  • domain/email.py :: send_event_email()
  • domain/email.py :: log_email()
  • domain/email.py :: get_recent_emails()
  • domain/email.py :: build_winners_html()
  • domain/email.py :: send_winners_announcement()

Unmigrated Functions (43 critical gaps)

  • build_results_published_email_content() 15 deps (Leaderboard Calculation)
  • build_forecast_lock_email_content() 12 deps (Admin & Cache Management)
  • send_event_notification_email() 7 deps (Core App Logic & Scoring)
  • reserve_event_email_idempotency() 7 deps (Leaderboard Router)
  • send_firebase_password_reset_email() 7 deps (Admin & Cache Management)
  • set_user_email() 6 deps (Admin & Cache Management)
  • get_metric_results_email_commentary() 6 deps (Leaderboard Display & Export)
  • create_event_email_log() 6 deps (Admin & Cache Management)
  • get_recent_event_email_logs() 6 deps (Core App Logic & Scoring)
  • mark_event_email_log_retried() 6 deps (Leaderboard Router)
  • ... and 33 more

API

External leaderboard API (FastAPI sidecar)
⚠ PARTIAL
Router
routers/api.py (6 functions)
api_monthly(), api_annual(), api_alltime(), _verify_api_key()
|
No graph edges from routers/api.py to domain/leaderboard.py (cross-file calls not captured)
v
Domain-Leaderboard
domain/leaderboard.py (10 functions)
get_monthly_leaderboard()

Dead-End Nodes (no outgoing calls)

  • domain/leaderboard.py :: get_rank_movements()
  • domain/leaderboard.py :: get_alltime_leaderboard()
  • domain/leaderboard.py :: get_alltime_leaderboard_by_metric()
  • domain/leaderboard.py :: get_live_mtd_leaderboard()
  • domain/leaderboard.py :: get_medal_table()

Unreachable from Routers (no incoming from router layer)

  • domain/leaderboard.py :: get_locked_months()
  • domain/leaderboard.py :: get_monthly_leaderboard()
  • domain/leaderboard.py :: get_rank_movements()
  • domain/leaderboard.py :: get_annual_leaderboard()
  • domain/leaderboard.py :: get_alltime_leaderboard()
  • domain/leaderboard.py :: get_annual_leaderboard_by_metric()
  • domain/leaderboard.py :: get_alltime_leaderboard_by_metric()
  • domain/leaderboard.py :: get_live_mtd_leaderboard()
  • ... and 1 more

Dependency Graph -- New Architecture

    ROUTERS (HTTP handlers)                    DOMAIN (business logic)                DATA LAYER
    =============================               ========================               ==========

    routers/pages.py ------?-----> domain/leaderboard.py ------?-----> domain/firebase.py
      login_page()                                get_monthly_leaderboard()              get_db()
      leaderboard_page()                          get_annual_leaderboard()               _init_firebase()
      forecast_page()           ------?-----> domain/scoring.py
      profile_page()                              calculate_monthly_scores()
      scoring_rules_page()      ------?-----> domain/forecasts.py
      password_change_page()                      save_forecast()
      forecast_submit()                           get_user_forecast()
                                ------?-----> domain/achievements.py
                                                  get_user_achievements()
                                ------?-----> domain/auth.py ----------?-----> domain/firebase.py
                                                  verify_password()
                                                  create_session_token()
                                                  validate_session_token()

    routers/admin.py -----?-----> domain/settings.py
      admin_index() (31 routes)                   get_settings()
                                ------?-----> domain/users.py ----------?-----> domain/firebase.py
                                                  create_user()
                                ------?-----> domain/scheduler.py
                                                  carry_over_forecasts()

    routers/fragments.py -?-----> domain/ai.py ----------?-----> OpenAI / Gemini API
      (25 HTMX fragments)                        generate_ai_content()
                                                  generate_leaderboard_commentary()
                                                  build_tone_directive()

    routers/api.py -------?-----> domain/leaderboard.py
      api_monthly()
      api_annual()
      api_alltime()

    domain/dropbox.py ------?-----> Dropbox API
      get_dropbox_data()
      _fetch_from_dropbox()

    domain/email.py -------?-----> SMTP / Resend
      send_event_email()
      send_winners_announcement()

    ----?----> = cross-file call NOT confirmed in graph (static analysis gap)
    ---------> = intra-file call confirmed in graph

Critical Unmigrated Functions (Top 30 by Dependency Count)

Functions from the old app.py that have NOT been ported to the new architecture. Sorted by connection count (how many other functions depend on them). Streamlit-specific functions excluded.

Function Community Deps Location
main() Leaderboard Calculation 25 app.py:L14679
get_cached_data() Leaderboard Calculation 19 app.py:L3693
set_cached_data() Leaderboard Calculation 19 app.py:L3707
clear_cache() Admin & Cache Management 18 app.py:L3716
build_results_published_email_content() Leaderboard Calculation 15 app.py:L4431
get_user_profile_stats() Leaderboard Calculation 14 app.py:L7167
is_ai_error_response() Leaderboard Display & Export 13 app.py:L554
build_forecast_lock_email_content() Admin & Cache Management 12 app.py:L4228
request_password_reset_for_user() Admin & Cache Management 12 app.py:L5425
get_user_leaderboard_rank_summary() Leaderboard Calculation 12 app.py:L6746
get_competition_ai_guardrails() Leaderboard Display & Export 11 app.py:L541
warm_runtime_leaderboard_caches() Leaderboard Calculation 11 app.py:L7146
get_user_current_rank() Leaderboard Calculation 9 app.py:L3544
save_local_data() Leaderboard Router 9 app.py:L3669
get_user() Admin & Cache Management 9 app.py:L3759
calculate_head_to_head() Leaderboard Calculation 9 app.py:L7717
get_sidebar_ranks() Leaderboard Calculation 8 app.py:L3570
ensure_firebase_auth_user() Admin & Cache Management 8 app.py:L5300
sync_password_to_firebase_auth() Admin & Cache Management 8 app.py:L5383
force_refresh_dropbox_data() Leaderboard Calculation 8 app.py:L6599
get_sales_daily_preview() Leaderboard Calculation 8 app.py:L6620
get_ai_roast() Leaderboard Display & Export 7 app.py:L1472
get_ai_performance_analysis() Leaderboard Display & Export 7 app.py:L1572
get_previous_leaderboard_state() Admin Router 7 app.py:L2198
save_leaderboard_state() Admin Router 7 app.py:L2213
send_event_notification_email() Core App Logic & Scoring 7 app.py:L4031
reserve_event_email_idempotency() Leaderboard Router 7 app.py:L4732
send_firebase_password_reset_email() Admin & Cache Management 7 app.py:L5258
update_settings() Admin & Cache Management 7 app.py:L5767
get_latest_sales_date() Leaderboard Calculation 7 app.py:L6661

Fix List -- Required Migrations per Feature

Leaderboard PARTIAL

  1. [HIGH] Migrate get_user_profile_stats() to domain/leaderboard.py -- 14 deps, used by profile and leaderboard pages
  2. [HIGH] Migrate get_user_leaderboard_rank_summary() to domain/leaderboard.py -- 12 deps, powers profile ranking display
  3. [HIGH] Migrate warm_runtime_leaderboard_caches() to domain/cache.py -- 11 deps, critical for page load performance
  4. [HIGH] Migrate calculate_head_to_head() to domain/leaderboard.py -- 9 deps, rivalry feature
  5. [MED] Migrate get_sidebar_ranks() to domain/leaderboard.py -- 8 deps, sidebar display
  6. [MED] Migrate get_user_current_rank() to domain/leaderboard.py -- 9 deps
  7. [MED] Migrate get_previous_leaderboard_state() / save_leaderboard_state() to domain/leaderboard.py -- movement arrows

Forecast PARTIAL

  1. [MED] Migrate force_refresh_dropbox_data() to domain/dropbox.py -- 8 deps, manual data refresh
  2. [MED] Migrate get_sales_daily_preview() to domain/dropbox.py -- 8 deps, admin data preview
  3. [LOW] Migrate get_latest_sales_date() to domain/dropbox.py -- 7 deps, status display

Profile PARTIAL

  1. [HIGH] Migrate get_user_profile_stats() -- 14 deps, profile page data assembly
  2. [HIGH] Migrate get_user_leaderboard_rank_summary() -- 12 deps, profile rankings
  3. [MED] Migrate get_user_current_rank() -- 9 deps, rank badge on profile

Admin PARTIAL

  1. [HIGH] Migrate update_settings() to domain/settings.py -- 7 deps, admin save operations
  2. [HIGH] Migrate request_password_reset_for_user() to domain/users.py -- 12 deps
  3. [HIGH] Migrate ensure_firebase_auth_user() / sync_password_to_firebase_auth() -- 8 deps each, user provisioning
  4. [MED] Migrate send_firebase_password_reset_email() to domain/auth.py -- 7 deps
  5. [MED] Migrate get_user() to domain/users.py -- 9 deps, individual user lookup

AI Content PARTIAL

  1. [HIGH] Migrate is_ai_error_response() to domain/ai.py -- 13 deps, error handling for all AI features
  2. [HIGH] Migrate get_competition_ai_guardrails() to domain/ai.py -- 11 deps, safety guardrails
  3. [MED] Migrate get_ai_roast() / get_ai_performance_analysis() to domain/ai.py -- 7 deps each

Email PARTIAL

  1. [HIGH] Migrate build_results_published_email_content() to domain/email.py -- 15 deps, month-end email
  2. [HIGH] Migrate build_forecast_lock_email_content() to domain/email.py -- 12 deps, forecast lock notification
  3. [MED] Migrate send_event_notification_email() to domain/email.py -- 7 deps
  4. [MED] Migrate reserve_event_email_idempotency() to domain/email.py -- 7 deps

Auth PARTIAL

  1. [HIGH] Migrate ensure_firebase_auth_user() to domain/auth.py -- 8 deps, Firebase Auth user creation
  2. [MED] Migrate sync_password_to_firebase_auth() to domain/auth.py -- 8 deps
  3. [MED] Migrate send_firebase_password_reset_email() to domain/auth.py -- 7 deps