From db386139fc63572a0ed6f9f40a0ad988d0f79fa2 Mon Sep 17 00:00:00 2001 From: Admin9705 Date: Sun, 24 Aug 2025 00:09:41 -0400 Subject: [PATCH] update --- frontend/static/js/new-main.js | 6 +- src/primary/apps/prowlarr_routes.py | 141 +++++++++++++++++++++++----- 2 files changed, 124 insertions(+), 23 deletions(-) diff --git a/frontend/static/js/new-main.js b/frontend/static/js/new-main.js index 74f7f878..aad835ec 100644 --- a/frontend/static/js/new-main.js +++ b/frontend/static/js/new-main.js @@ -2746,6 +2746,7 @@ let huntarrUI = {
${successRate}%
`); + console.log('Added success rate card:', successRate, successClass); // Average response time if (stats.avg_response_time !== undefined) { @@ -2785,7 +2786,10 @@ let huntarrUI = { if (statisticsCards.length === 0) { statisticsContent.innerHTML = '
No recent activity
'; } else { - statisticsContent.innerHTML = statisticsCards.join(''); + const finalHTML = statisticsCards.join(''); + console.log('Final Prowlarr statistics HTML:', finalHTML); + console.log('Number of stat cards:', statisticsCards.length); + statisticsContent.innerHTML = finalHTML; } }, diff --git a/src/primary/apps/prowlarr_routes.py b/src/primary/apps/prowlarr_routes.py index 43909c7e..3ec677a3 100644 --- a/src/primary/apps/prowlarr_routes.py +++ b/src/primary/apps/prowlarr_routes.py @@ -319,21 +319,52 @@ def _fetch_detailed_stats(): if status_response.status_code == 200: stats['connected'] = True - # Get API history/usage statistics + # Get API history/usage statistics using the optimized /since endpoint try: - history_url = f"{api_url.rstrip('/')}/api/v1/history" - history_response = requests.get(history_url, headers=headers, timeout=15, params={'pageSize': 50}) + # Calculate date range for today and yesterday + from datetime import datetime, timedelta + now = datetime.utcnow() + today = now.date() + yesterday = today - timedelta(days=1) + # Use the /history/since endpoint for efficient date-based filtering + # This gets ALL records since yesterday without pagination limits + yesterday_start = datetime.combine(yesterday, datetime.min.time()) + since_date = yesterday_start.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + + history_since_url = f"{api_url.rstrip('/')}/api/v1/history/since" + params = {'date': since_date} + + prowlarr_logger.debug(f"Fetching history since {since_date} using /history/since endpoint") + history_response = requests.get(history_since_url, headers=headers, timeout=15, params=params) + + all_records = [] if history_response.status_code == 200: - history_data = history_response.json() + # /history/since returns an array directly, not a paged response + all_records = history_response.json() + prowlarr_logger.debug(f"Retrieved {len(all_records)} history records from /history/since endpoint") + else: + # Fallback to regular history endpoint if /since is not available + prowlarr_logger.debug(f"/history/since failed with status {history_response.status_code}, falling back to regular history endpoint") + + history_url = f"{api_url.rstrip('/')}/api/v1/history" + params = { + 'pageSize': 500, # Larger page size for fallback + 'sortKey': 'date', + 'sortDirection': 'descending' + } + + fallback_response = requests.get(history_url, headers=headers, timeout=15, params=params) + if fallback_response.status_code == 200: + fallback_data = fallback_response.json() + all_records = fallback_data.get('records', []) + prowlarr_logger.debug(f"Fallback: Retrieved {len(all_records)} history records from regular endpoint") + + if all_records: # Total records gives us approximate API call count - stats['total_api_calls'] = history_data.get('totalRecords', 0) + stats['total_api_calls'] = len(all_records) # Analyze recent activity (overall and per indexer) - from datetime import datetime, timedelta - now = datetime.utcnow() - today = now.date() - yesterday = today - timedelta(days=1) searches_today = 0 searches_yesterday = 0 @@ -343,8 +374,9 @@ def _fetch_detailed_stats(): # Track per-indexer statistics indexer_daily_stats = {} - records = history_data.get('records', []) - for record in records: + prowlarr_logger.debug(f"Processing {len(all_records)} history records for date analysis") + + for record in all_records: try: # Parse the date from the record record_date = datetime.fromisoformat(record.get('date', '').replace('Z', '+00:00')).date() @@ -389,19 +421,79 @@ def _fetch_detailed_stats(): stats['recent_failed_searches'] = failed_searches stats['indexer_daily_stats'] = indexer_daily_stats + prowlarr_logger.debug(f"Calculated stats - Today: {searches_today}, Yesterday: {searches_yesterday}, Success rate: {stats['recent_success_rate']}%") + except Exception as e: prowlarr_logger.debug(f"History endpoint failed: {str(e)}") - # Get indexer performance statistics + # Get indexer performance statistics with date filtering for today's data try: + # Use indexerstats endpoint with date filtering for accurate daily statistics + today_start = datetime.combine(today, datetime.min.time()) + today_end = datetime.combine(today + timedelta(days=1), datetime.min.time()) + indexerstats_url = f"{api_url.rstrip('/')}/api/v1/indexerstats" - indexerstats_response = requests.get(indexerstats_url, headers=headers, timeout=15) + indexerstats_params = { + 'startDate': today_start.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), + 'endDate': today_end.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + } + + prowlarr_logger.debug(f"Fetching indexer stats for today: {today_start} to {today_end}") + indexerstats_response = requests.get(indexerstats_url, headers=headers, timeout=15, params=indexerstats_params) if indexerstats_response.status_code == 200: indexerstats_data = indexerstats_response.json() indexer_stats = indexerstats_data.get('indexers', []) if indexer_stats: + # Update individual indexer stats with indexerstats data for consistency + for indexer_stat in indexer_stats: + indexer_id = indexer_stat.get('indexerId') + queries_today = indexer_stat.get('numberOfQueries', 0) + + if indexer_id and queries_today > 0: + # Update the indexer_daily_stats with indexerstats data for consistency + if indexer_id not in stats.get('indexer_daily_stats', {}): + stats.setdefault('indexer_daily_stats', {})[indexer_id] = { + 'searches_today': 0, + 'searches_yesterday': 0, + 'successful_today': 0, + 'failed_today': 0 + } + + # Use indexerstats data as the authoritative source for today's count + stats['indexer_daily_stats'][indexer_id]['searches_today'] = queries_today + + # Calculate main total from updated indexer stats for consistency + total_searches_today = sum( + indexer_data.get('searches_today', 0) + for indexer_data in stats.get('indexer_daily_stats', {}).values() + ) + + if total_searches_today > 0: + stats['searches_today'] = total_searches_today + prowlarr_logger.debug(f"Using consistent indexerstats total: {total_searches_today} searches (sum of all indexers)") + + # Calculate main statistics from indexerstats data for consistency + total_grabs = sum(stat.get('numberOfGrabs', 0) for stat in indexer_stats) + total_failed = sum(stat.get('numberOfFailedQueries', 0) for stat in indexer_stats) + + # Update main success rate to match indexer calculations + if total_searches_today > 0: + stats['recent_success_rate'] = round((total_grabs / total_searches_today) * 100, 1) + else: + stats['recent_success_rate'] = 0 + + # Update total API calls to match indexerstats + stats['total_api_calls'] = sum(stat.get('numberOfQueries', 0) for stat in indexer_stats) + stats['recent_failed_searches'] = total_failed + + prowlarr_logger.debug(f"Main stats updated - Success rate: {stats['recent_success_rate']}%, Total API calls: {stats['total_api_calls']}, Failed: {total_failed}") + + # Debug logging to track individual indexer contributions + for idx_id, idx_data in stats.get('indexer_daily_stats', {}).items(): + prowlarr_logger.debug(f"Indexer {idx_id}: {idx_data.get('searches_today', 0)} searches today") + # Calculate average response time total_response_time = 0 total_queries = 0 @@ -415,7 +507,7 @@ def _fetch_detailed_stats(): indexer_id = indexer_stat.get('indexerId') indexer_name = indexer_stat.get('indexerName', 'Unknown') - # Get daily stats for this indexer + # Get daily stats for this indexer (now updated with consistent indexerstats data) daily_stats = stats.get('indexer_daily_stats', {}).get(indexer_id, { 'searches_today': 0, 'searches_yesterday': 0, @@ -423,10 +515,13 @@ def _fetch_detailed_stats(): 'failed_today': 0 }) - # Calculate success rate for today + # Use indexerstats numberOfQueries as the authoritative source for today's searches + indexer_searches_today = queries # This comes from indexerstats API + + # Calculate success rate for today using indexerstats data (consistent with main calculation) today_success_rate = 0 - if daily_stats['searches_today'] > 0: - today_success_rate = round((daily_stats['successful_today'] / daily_stats['searches_today']) * 100, 1) + if indexer_searches_today > 0: + today_success_rate = round((grabs / indexer_searches_today) * 100, 1) indexer_data = { 'id': indexer_id, @@ -434,10 +529,10 @@ def _fetch_detailed_stats(): 'response_time': response_time, 'queries': queries, 'grabs': grabs, - 'success_rate': round((grabs / queries) * 100, 1) if queries > 0 else 0, - 'searches_today': daily_stats['searches_today'], - 'searches_yesterday': daily_stats['searches_yesterday'], - 'successful_today': daily_stats['successful_today'], + 'success_rate': today_success_rate, # Use consistent calculation method + 'searches_today': indexer_searches_today, # Use consistent indexerstats data + 'searches_yesterday': daily_stats['searches_yesterday'], # Keep history data for yesterday + 'successful_today': grabs, # Use grabs from indexerstats as successful searches 'failed_today': daily_stats['failed_today'], 'today_success_rate': today_success_rate } @@ -450,9 +545,11 @@ def _fetch_detailed_stats(): # Store individual stats by name for easy lookup individual_indexer_stats[indexer_name] = indexer_data - # Calculate overall average response time + # Calculate overall average response time (weighted average across all indexers) avg_response_time = round(total_response_time / max(total_queries, 1), 0) stats['avg_response_time'] = avg_response_time + + prowlarr_logger.debug(f"Calculated weighted average response time: {avg_response_time}ms from {len(indexer_stats)} indexers") stats['indexer_performance'] = sorted(indexer_performance, key=lambda x: x['queries'], reverse=True) stats['individual_indexer_stats'] = individual_indexer_stats