diff --git a/.gitignore b/.gitignore index d3168d3..7b29c09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Python __pycache__/ -*.py[cod] +*.py[cod]* *$py.class *.so .Python @@ -198,4 +198,4 @@ Thumbs.db .env.development .env.test .env.production -config.local.py \ No newline at end of file +config.local.py diff --git a/app.py b/app.py index 9069e13..e3f253b 100644 --- a/app.py +++ b/app.py @@ -24,9 +24,19 @@ espn_api = ESPNAPI() api_cache = {} 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): """Get data from cache or fetch it and cache the result""" - now = datetime.now() + now = get_debug_time() if cache_key in api_cache: cached_data, cached_time = api_cache[cache_key] @@ -56,10 +66,14 @@ def get_league_color(league_index): @app.context_processor def inject_apis(): """Make API and version available to all templates""" + global debug_time_override + is_debug_mode = request.args.get('debug') == 'true' return dict( sleeper_api=sleeper_api, 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('/') @@ -102,6 +116,38 @@ def set_timezone(): return_url = request.form.get('return_url', url_for('index')) 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('/') def dashboard_current(username): """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: Calling get_week_schedule() for week {week}, season {season}", flush=True) 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) except Exception as e: print(f"ERROR: ESPN schedule fetch failed: {str(e)}", flush=True) diff --git a/services/__pycache__/__init__.cpython-313.pyc b/services/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 04319b7..0000000 Binary files a/services/__pycache__/__init__.cpython-313.pyc and /dev/null differ diff --git a/services/__pycache__/espn_api.cpython-313.pyc b/services/__pycache__/espn_api.cpython-313.pyc deleted file mode 100644 index cb8be8c..0000000 Binary files a/services/__pycache__/espn_api.cpython-313.pyc and /dev/null differ diff --git a/services/__pycache__/sleeper_api.cpython-313.pyc b/services/__pycache__/sleeper_api.cpython-313.pyc deleted file mode 100644 index 3c7f316..0000000 Binary files a/services/__pycache__/sleeper_api.cpython-313.pyc and /dev/null differ diff --git a/services/espn_api.py b/services/espn_api.py index 4ce9f62..da549ce 100644 --- a/services/espn_api.py +++ b/services/espn_api.py @@ -35,7 +35,7 @@ class ESPNAPI: print(f"ESPN API: Error fetching injury status for {player_name}: {e}", flush=True) 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""" try: print(f"ESPN API: Fetching schedule for week {week}, season {season}", flush=True) @@ -114,7 +114,7 @@ class ESPNAPI: 'home_team': home_team, 'away_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, 'game_state': game_state, 'status_display': status_display @@ -125,7 +125,7 @@ class ESPNAPI: print("ESPN API: No 'events' key found in response", 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 for day, day_games in schedule.items(): @@ -137,9 +137,10 @@ class ESPNAPI: print(f"ESPN API: Error fetching schedule: {e}", flush=True) 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""" schedule = {} + current_time_for_comparison = debug_current_time or datetime.now() # Sort games by date first games_sorted = sorted(games, key=lambda x: x['date_local']) @@ -152,13 +153,27 @@ class ESPNAPI: 'day_name': game['day_of_week'], 'date': game['date_formatted'], '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': [] } 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(): - 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 diff --git a/static/style.css b/static/style.css index 5c812b7..79fe901 100644 --- a/static/style.css +++ b/static/style.css @@ -199,6 +199,32 @@ 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 { display: flex; align-items: center; diff --git a/templates/dashboard.html b/templates/dashboard.html index 1b2ec15..d0de849 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -8,16 +8,38 @@

RosterHash for {{ user.display_name }}

-
+
+ + + {% if is_debug_mode %} +
+
+ + + + + +
+ {% if debug_time_override %} +
+ Current override: {{ debug_time_override.strftime('%Y-%m-%d %H:%M') }} +
+ {% endif %} +
+ {% endif %}
@@ -71,8 +93,21 @@ {% set ns = namespace(has_games=false) %} - + + {% set live_future_days = [] %} + {% set completed_days = [] %} {% 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') %} + + + {% for day_key, day_info in sorted_live_future + sorted_completed %} {% set ns.has_games = true %}