Spaces:
Build error
Build error
File size: 7,924 Bytes
0821095 3bfd040 0821095 3bfd040 0821095 3bfd040 0821095 3bfd040 0821095 3bfd040 7e0c218 3bfd040 7e0c218 3bfd040 7e0c218 3bfd040 7e0c218 3bfd040 7e0c218 3bfd040 7e0c218 3bfd040 0821095 5b8ed2c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
"""
Module de serveur API pour le leaderboard parser.
Ce module contient la configuration FastAPI et les endpoints pour le mode serveur.
"""
import datetime
import threading
import logging
import os
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse, HTMLResponse
from src.file_utils import format_datetime
# Initialiser le logger
logger = logging.getLogger("leaderboard-parser")
# Variables globales pour suivre l'état du serveur
processing_status = "idle"
processing_error = None
last_run_time = None
# Initialiser l'application FastAPI
app = FastAPI(title="Leaderboard Parser API")
# Cette fonction sera importée depuis main.py
process_leaderboards = None
def initialize_server(process_function):
"""
Initialise le serveur avec la fonction de traitement des leaderboards.
Cette fonction doit être appelée avant de démarrer le serveur.
Args:
process_function: Fonction qui traite les leaderboards
"""
global process_leaderboards, last_run_time
process_leaderboards = process_function
# Initialiser last_run_time à la date actuelle
last_run_time = datetime.datetime.now()
logger.info(f"Serveur initialisé avec la fonction de traitement. last_run_time initialisé à {last_run_time.isoformat()}")
# Endpoints API
@app.get("/", response_class=HTMLResponse)
async def root():
"""Root endpoint serving a simple HTML page with status info"""
global processing_status, last_run_time, processing_error
next_run = format_datetime(last_run_time + datetime.timedelta(hours=int(os.environ.get("LEADERBOARD_REPROCESS_INTERVAL_HOURS", 24)))) if last_run_time else "Not yet scheduled"
last_run = format_datetime(last_run_time) if last_run_time else "Never"
# Calculate timestamp for next run to use in JavaScript
next_run_timestamp = ""
if last_run_time:
next_run_time = last_run_time + datetime.timedelta(hours=int(os.environ.get("LEADERBOARD_REPROCESS_INTERVAL_HOURS", 24)))
next_run_timestamp = next_run_time.timestamp() * 1000 # Convert to milliseconds for JavaScript
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Leaderboard Parser Status</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f7f7f7;
}}
.status-box {{
padding: 30px;
text-align: center;
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
max-width: 500px;
}}
h1 {{
margin-top: 0;
color: #333;
font-weight: 600;
}}
p {{
margin: 12px 0;
font-size: 16px;
line-height: 1.5;
}}
.status-label {{
font-weight: bold;
display: inline-block;
min-width: 80px;
}}
.status-running {{ color: #1a73e8; font-weight: bold; }}
.status-idle {{ color: #188038; font-weight: bold; }}
.status-failed {{ color: #d93025; font-weight: bold; }}
.countdown {{
margin-top: 10px;
padding: 8px;
background-color: #f5f5f5;
border-radius: 6px;
display: inline-block;
min-width: 250px;
}}
</style>
</head>
<body>
<div class="status-box">
<h1>Leaderboard Parser</h1>
<p><span class="status-label">Status</span> <span class="status-{processing_status}">{processing_status}</span></p>
<p><span class="status-label">Last run</span> {last_run}</p>
{f'<p style="color: #d93025; margin-top: 20px;"><span class="status-label">Error</span> {processing_error}</p>' if processing_error else ''}
<div class="countdown" id="countdown">
Calculating time remaining...
</div>
</div>
<script>
// Set the date we're counting down to (from server)
const nextRunTimestamp = {next_run_timestamp};
// Function to format time remaining in a human-readable way
function formatTimeRemaining(distance) {{
const hours = Math.floor(distance / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
return `Next run in <b>${{hours}}h ${{minutes}}m ${{seconds}}s</b>`;
}}
// Update the countdown every 1 second
function updateCountdown() {{
// Get current timestamp
const now = new Date().getTime();
// If no next run is scheduled
if (!nextRunTimestamp) {{
document.getElementById("countdown").innerHTML = "No next run scheduled";
return;
}}
// Find the time remaining
const distance = nextRunTimestamp - now;
// If the countdown is over
if (distance < 0) {{
document.getElementById("countdown").innerHTML = "Scheduled run in progress or overdue";
}} else {{
// Display the result
document.getElementById("countdown").innerHTML = formatTimeRemaining(distance);
}}
}}
// Initial call to display right away
updateCountdown();
// Set up interval to update every second
setInterval(updateCountdown, 1000);
</script>
</body>
</html>
"""
return HTMLResponse(content=html_content)
@app.get("/status")
async def get_status():
"""Get the current status of the parser"""
global processing_status, last_run_time, processing_error
return {
"status": processing_status,
"last_run": format_datetime(last_run_time) if last_run_time else None,
"next_run": format_datetime(last_run_time + datetime.timedelta(hours=int(os.environ.get("LEADERBOARD_REPROCESS_INTERVAL_HOURS", 24)))) if last_run_time else None,
"error": processing_error
}
@app.post("/run")
async def trigger_run():
"""Manually trigger a leaderboard processing run"""
global processing_status, process_leaderboards
if not process_leaderboards:
raise HTTPException(status_code=500, detail="Server not properly initialized")
if processing_status == "running":
raise HTTPException(status_code=409, detail="Processing is already running")
# Start processing in a separate thread
threading.Thread(target=lambda: process_leaderboards()).start()
return {"status": "started", "message": "Processing started"}
@app.get("/health")
async def health_check():
"""Health check endpoint for HF Spaces to verify the app is running"""
return {"status": "ok"} |