debug mode added. Fixed gitignore.

This commit is contained in:
Eduardo Figueroa 2025-09-09 16:14:53 -07:00
parent 7275c42af5
commit 735e41de84
8 changed files with 138 additions and 16 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
# Python # Python
__pycache__/ __pycache__/
*.py[cod] *.py[cod]*
*$py.class *$py.class
*.so *.so
.Python .Python

52
app.py
View file

@ -24,9 +24,19 @@ espn_api = ESPNAPI()
api_cache = {} api_cache = {}
CACHE_DURATION = 300 # 5 minutes in seconds CACHE_DURATION = 300 # 5 minutes in seconds
# Debug time override
debug_time_override = None
def get_debug_time():
"""Get the current time, or debug override time if set"""
global debug_time_override
if debug_time_override:
return debug_time_override
return datetime.now()
def get_cached_or_fetch(cache_key, fetch_function, *args): def get_cached_or_fetch(cache_key, fetch_function, *args):
"""Get data from cache or fetch it and cache the result""" """Get data from cache or fetch it and cache the result"""
now = datetime.now() now = get_debug_time()
if cache_key in api_cache: if cache_key in api_cache:
cached_data, cached_time = api_cache[cache_key] cached_data, cached_time = api_cache[cache_key]
@ -56,10 +66,14 @@ def get_league_color(league_index):
@app.context_processor @app.context_processor
def inject_apis(): def inject_apis():
"""Make API and version available to all templates""" """Make API and version available to all templates"""
global debug_time_override
is_debug_mode = request.args.get('debug') == 'true'
return dict( return dict(
sleeper_api=sleeper_api, sleeper_api=sleeper_api,
app_version=app.config['VERSION'], app_version=app.config['VERSION'],
current_theme=session.get('theme', 'dark') current_theme=session.get('theme', 'dark'),
is_debug_mode=is_debug_mode,
debug_time_override=debug_time_override
) )
@app.route('/') @app.route('/')
@ -102,6 +116,38 @@ def set_timezone():
return_url = request.form.get('return_url', url_for('index')) return_url = request.form.get('return_url', url_for('index'))
return redirect(return_url) return redirect(return_url)
# Add debug time override route
@app.route('/set_debug_time', methods=['POST'])
def set_debug_time():
"""Set debug time override (only in debug mode)"""
global debug_time_override
# Check if debug mode is enabled
if not request.args.get('debug') == 'true':
return jsonify({'error': 'Debug mode not enabled'}), 403
debug_datetime_str = request.form.get('debug_time')
if debug_datetime_str:
try:
# Parse the datetime string from the picker (format: YYYY-MM-DDTHH:MM)
debug_time_override = datetime.strptime(debug_datetime_str, '%Y-%m-%dT%H:%M')
print(f"DEBUG: Set debug time override to: {debug_time_override}", flush=True)
# Clear cache when time changes to get fresh data
api_cache.clear()
print("DEBUG: Cleared API cache due to time override", flush=True)
except ValueError as e:
print(f"DEBUG: Invalid debug time format: {debug_datetime_str}, error: {e}", flush=True)
return jsonify({'error': 'Invalid datetime format'}), 400
else:
# Clear the override
debug_time_override = None
api_cache.clear()
print("DEBUG: Cleared debug time override", flush=True)
# Get return URL from form or default to index
return_url = request.form.get('return_url', url_for('index'))
return redirect(return_url)
@app.route('/<username>') @app.route('/<username>')
def dashboard_current(username): def dashboard_current(username):
"""Dashboard for current NFL week""" """Dashboard for current NFL week"""
@ -166,7 +212,7 @@ def dashboard(username, week):
print(f"DEBUG: Using timezone: {user_timezone}", flush=True) print(f"DEBUG: Using timezone: {user_timezone}", flush=True)
print(f"DEBUG: Calling get_week_schedule() for week {week}, season {season}", flush=True) print(f"DEBUG: Calling get_week_schedule() for week {week}, season {season}", flush=True)
try: try:
schedule = espn_api.get_week_schedule(week, season, user_timezone) schedule = espn_api.get_week_schedule(week, season, user_timezone, debug_time_override)
print(f"DEBUG: Schedule retrieved successfully", flush=True) print(f"DEBUG: Schedule retrieved successfully", flush=True)
except Exception as e: except Exception as e:
print(f"ERROR: ESPN schedule fetch failed: {str(e)}", flush=True) print(f"ERROR: ESPN schedule fetch failed: {str(e)}", flush=True)

View file

@ -35,7 +35,7 @@ class ESPNAPI:
print(f"ESPN API: Error fetching injury status for {player_name}: {e}", flush=True) print(f"ESPN API: Error fetching injury status for {player_name}: {e}", flush=True)
return None return None
def get_week_schedule(self, week, season, user_timezone='America/Los_Angeles'): def get_week_schedule(self, week, season, user_timezone='America/Los_Angeles', debug_current_time=None):
"""Get NFL schedule for a specific week""" """Get NFL schedule for a specific week"""
try: try:
print(f"ESPN API: Fetching schedule for week {week}, season {season}", flush=True) print(f"ESPN API: Fetching schedule for week {week}, season {season}", flush=True)
@ -114,7 +114,7 @@ class ESPNAPI:
'home_team': home_team, 'home_team': home_team,
'away_team': away_team, 'away_team': away_team,
'teams': [home_team, away_team], 'teams': [home_team, away_team],
'is_past': game_date_local < datetime.now(game_date_local.tzinfo), 'is_past': game_date_local < (debug_current_time.astimezone(game_date_local.tzinfo) if debug_current_time else datetime.now(game_date_local.tzinfo)),
'is_live': is_live, 'is_live': is_live,
'game_state': game_state, 'game_state': game_state,
'status_display': status_display 'status_display': status_display
@ -125,7 +125,7 @@ class ESPNAPI:
print("ESPN API: No 'events' key found in response", flush=True) print("ESPN API: No 'events' key found in response", flush=True)
print(f"ESPN API: Processed {len(games)} games total", flush=True) print(f"ESPN API: Processed {len(games)} games total", flush=True)
schedule = self._organize_by_day(games) schedule = self._organize_by_day(games, debug_current_time)
# Log games by day # Log games by day
for day, day_games in schedule.items(): for day, day_games in schedule.items():
@ -137,9 +137,10 @@ class ESPNAPI:
print(f"ESPN API: Error fetching schedule: {e}", flush=True) print(f"ESPN API: Error fetching schedule: {e}", flush=True)
return {} return {}
def _organize_by_day(self, games): def _organize_by_day(self, games, debug_current_time=None):
"""Organize games by day, sorted chronologically with date info""" """Organize games by day, sorted chronologically with date info"""
schedule = {} schedule = {}
current_time_for_comparison = debug_current_time or datetime.now()
# Sort games by date first # Sort games by date first
games_sorted = sorted(games, key=lambda x: x['date_local']) games_sorted = sorted(games, key=lambda x: x['date_local'])
@ -152,13 +153,27 @@ class ESPNAPI:
'day_name': game['day_of_week'], 'day_name': game['day_of_week'],
'date': game['date_formatted'], 'date': game['date_formatted'],
'date_obj': game['date_local'].date(), 'date_obj': game['date_local'].date(),
'is_past': game['date_local'].date() < datetime.now(game['date_local'].tzinfo).date(), 'is_past': game['date_local'].date() < current_time_for_comparison.astimezone(game['date_local'].tzinfo).date(),
'games': [] 'games': []
} }
schedule[day_key]['games'].append(game) schedule[day_key]['games'].append(game)
# Sort games within each day by time # Sort games within each day: live/future first, then completed, then by time within each group
for day_info in schedule.values(): for day_info in schedule.values():
day_info['games'].sort(key=lambda x: x['date_local']) def game_sort_key(game):
# Priority: live games first (0), future games (1), completed games (2)
if game['is_live']:
priority = 0
elif not game['is_past']:
priority = 1 # future games
else:
priority = 2 # completed games
return (priority, game['date_local'])
day_info['games'].sort(key=game_sort_key)
# Add day-level status info for day sorting
day_info['has_live_games'] = any(game['is_live'] for game in day_info['games'])
day_info['has_future_games'] = any(not game['is_past'] and not game['is_live'] for game in day_info['games'])
return schedule return schedule

View file

@ -199,6 +199,32 @@
font-size: 1.8rem; font-size: 1.8rem;
} }
/* Debug time picker styles */
.debug-time-picker {
background: var(--bg-secondary);
border: 2px solid #ff6b6b;
border-radius: 8px;
padding: 12px;
margin-top: 12px;
box-shadow: 0 2px 8px rgba(255, 107, 107, 0.2);
width: 100%;
}
.debug-time-picker input[type="datetime-local"] {
background: var(--bg-color);
color: var(--text-color);
border: 2px solid #ff6b6b;
border-radius: 4px;
padding: 6px 10px;
font-family: inherit;
}
.debug-time-picker input[type="datetime-local"]:focus {
outline: none;
border-color: #ff4757;
box-shadow: 0 0 0 2px rgba(255, 107, 107, 0.2);
}
.week-nav { .week-nav {
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -8,16 +8,38 @@
<header class="dashboard-header"> <header class="dashboard-header">
<h1>RosterHash for {{ user.display_name }}</h1> <h1>RosterHash for {{ user.display_name }}</h1>
<div class="week-nav"> <div class="week-nav">
<a href="/{{ user.username }}/{{ week - 1 }}" class="week-btn">&larr; Week {{ week - 1 }}</a> <a href="/{{ user.username }}/{{ week - 1 }}{% if is_debug_mode %}?debug=true{% endif %}" class="week-btn">&larr; Week {{ week - 1 }}</a>
<span class="current-week">Week {{ week }}</span> <span class="current-week">Week {{ week }}</span>
<a href="/{{ user.username }}/{{ week + 1 }}" class="week-btn">Week {{ week + 1 }} &rarr;</a> <a href="/{{ user.username }}/{{ week + 1 }}{% if is_debug_mode %}?debug=true{% endif %}" class="week-btn">Week {{ week + 1 }} &rarr;</a>
</div> </div>
<!-- Server-side refresh --> <!-- Server-side refresh -->
<div class="refresh-nav"> <div class="refresh-nav">
<form method="post" action="/{{ user.username }}/{{ week }}/refresh" style="display: inline;"> <form method="post" action="/{{ user.username }}/{{ week }}/refresh{% if is_debug_mode %}?debug=true{% endif %}" style="display: inline;">
<button type="submit" class="refresh-btn">🔄 Refresh Scores</button> <button type="submit" class="refresh-btn">🔄 Refresh Scores</button>
</form> </form>
</div> </div>
<!-- Debug Time Picker (only shown when ?debug=true) -->
{% if is_debug_mode %}
<div class="debug-time-picker">
<form method="post" action="/set_debug_time?debug=true" style="display: inline-block;">
<label for="debug_time" style="color: #ff6b6b; font-size: 14px; font-weight: bold;">🐛 Debug Time:</label>
<input type="datetime-local"
id="debug_time"
name="debug_time"
value="{% if debug_time_override %}{{ debug_time_override.strftime('%Y-%m-%dT%H:%M') }}{% endif %}"
style="margin: 0 8px; padding: 4px 8px; border: 2px solid #ff6b6b; border-radius: 4px; background: var(--bg-color); color: var(--text-color);">
<input type="hidden" name="return_url" value="{{ request.url }}">
<button type="submit" style="padding: 4px 12px; background: #ff6b6b; color: white; border: none; border-radius: 4px; cursor: pointer;">Set</button>
<button type="submit" onclick="document.getElementById('debug_time').value=''; return true;" style="padding: 4px 12px; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer; margin-left: 4px;">Clear</button>
</form>
{% if debug_time_override %}
<div style="color: #ff6b6b; font-size: 12px; margin-top: 4px;">
Current override: {{ debug_time_override.strftime('%Y-%m-%d %H:%M') }}
</div>
{% endif %}
</div>
{% endif %}
</header> </header>
<!-- Compact league scores at top --> <!-- Compact league scores at top -->
@ -71,8 +93,21 @@
<!-- Track if we have any games using namespace --> <!-- Track if we have any games using namespace -->
{% set ns = namespace(has_games=false) %} {% set ns = namespace(has_games=false) %}
<!-- Loop through days that have games --> <!-- Sort days: live/future days first, then completed days, then by date -->
{% set live_future_days = [] %}
{% set completed_days = [] %}
{% for day_key, day_info in schedule.items() if day_info.games %} {% for day_key, day_info in schedule.items() if day_info.games %}
{% if day_info.has_live_games or day_info.has_future_games %}
{% set _ = live_future_days.append((day_key, day_info)) %}
{% else %}
{% set _ = completed_days.append((day_key, day_info)) %}
{% endif %}
{% endfor %}
{% set sorted_live_future = live_future_days|sort(attribute='1.date_obj') %}
{% set sorted_completed = completed_days|sort(attribute='1.date_obj') %}
<!-- Loop through days: live/future first, then completed -->
{% for day_key, day_info in sorted_live_future + sorted_completed %}
{% set ns.has_games = true %} {% set ns.has_games = true %}
<div class="day-row {% if day_info.is_past %}collapsed{% endif %}" data-day="{{ day_key }}"> <div class="day-row {% if day_info.is_past %}collapsed{% endif %}" data-day="{{ day_key }}">
<div class="day-header" onclick="toggleDay('{{ day_key }}')"> <div class="day-header" onclick="toggleDay('{{ day_key }}')">