debug mode added. Fixed gitignore.
This commit is contained in:
parent
7275c42af5
commit
735e41de84
8 changed files with 138 additions and 16 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]*
|
||||||
*$py.class
|
*$py.class
|
||||||
*.so
|
*.so
|
||||||
.Python
|
.Python
|
||||||
|
|
@ -198,4 +198,4 @@ Thumbs.db
|
||||||
.env.development
|
.env.development
|
||||||
.env.test
|
.env.test
|
||||||
.env.production
|
.env.production
|
||||||
config.local.py
|
config.local.py
|
||||||
|
|
|
||||||
52
app.py
52
app.py
|
|
@ -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)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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">← Week {{ week - 1 }}</a>
|
<a href="/{{ user.username }}/{{ week - 1 }}{% if is_debug_mode %}?debug=true{% endif %}" class="week-btn">← 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 }} →</a>
|
<a href="/{{ user.username }}/{{ week + 1 }}{% if is_debug_mode %}?debug=true{% endif %}" class="week-btn">Week {{ week + 1 }} →</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 }}')">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue