Spaces:
Sleeping
Sleeping
from flask import Flask, render_template_string, jsonify | |
from apscheduler.schedulers.background import BackgroundScheduler | |
import subprocess | |
import threading | |
from datetime import datetime | |
app = Flask(__name__) | |
execution_logs = [] | |
MAX_LOG_ENTRIES = 20 | |
def run_cli_script(): | |
"""Runs cli.py and streams logs in real-time to both UI and terminal.""" | |
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") | |
log_entry = {'time': timestamp, 'output': '', 'error': ''} | |
try: | |
process = subprocess.Popen( | |
["python", "cli.py"], | |
stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE, | |
text=True, | |
bufsize=1 | |
) | |
# Stream logs to UI and print to terminal | |
for line in process.stdout: | |
log_entry['output'] += line | |
execution_logs.append({'time': timestamp, 'output': line, 'error': ''}) | |
print(line, end="") # β Print logs to terminal | |
if len(execution_logs) > MAX_LOG_ENTRIES: | |
execution_logs.pop(0) | |
for line in process.stderr: | |
log_entry['error'] += line | |
execution_logs.append({'time': timestamp, 'output': '', 'error': line}) | |
print(line, end="") # β Print errors to terminal | |
if len(execution_logs) > MAX_LOG_ENTRIES: | |
execution_logs.pop(0) | |
except Exception as e: | |
log_entry['error'] = str(e) | |
execution_logs.append({'time': timestamp, 'output': '', 'error': str(e)}) | |
print(f"Error: {str(e)}") # β Print error to terminal | |
def start_initial_run(): | |
threading.Thread(target=run_cli_script, daemon=True).start() | |
scheduler = BackgroundScheduler(daemon=True) | |
scheduler.add_job( | |
run_cli_script, | |
'interval', | |
hours=3, | |
id='main_job', | |
next_run_time=datetime.now() | |
) | |
scheduler.start() | |
start_initial_run() | |
def home(): | |
"""Main UI displaying logs and next run time.""" | |
job = scheduler.get_job('main_job') | |
next_run = job.next_run_time.strftime('%Y-%m-%d %H:%M:%S UTC') if job else 'N/A' | |
return render_template_string(''' | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Script Scheduler</title> | |
<script> | |
function fetchLogs() { | |
fetch('/logs') | |
.then(response => response.json()) | |
.then(data => { | |
let logBox = document.getElementById("log-box"); | |
logBox.innerHTML = ""; | |
data.logs.forEach(log => { | |
let logEntry = "<div class='timestamp'>" + log.time + "</div>"; | |
if (log.output) logEntry += "<div class='output'>" + log.output + "</div>"; | |
if (log.error) logEntry += "<div class='error'>" + log.error + "</div>"; | |
logEntry += "<hr>"; | |
logBox.innerHTML += logEntry; | |
}); | |
logBox.scrollTop = logBox.scrollHeight; | |
}); | |
} | |
setInterval(fetchLogs, 2000); | |
window.onload = fetchLogs; | |
</script> | |
<style> | |
body { font-family: Arial, sans-serif; padding: 20px; } | |
.log-box { | |
background: #000; | |
color: #0f0; | |
padding: 15px; | |
border-radius: 5px; | |
margin-top: 20px; | |
white-space: pre-wrap; | |
max-height: 400px; | |
overflow-y: auto; | |
} | |
.timestamp { color: #888; margin-bottom: 10px; } | |
.error { color: #ff4444; } | |
</style> | |
</head> | |
<body> | |
<h1>Script Scheduler</h1> | |
<p>Next run: {{ next_run }}</p> | |
<h2>Latest Execution Logs</h2> | |
<div id="log-box" class="log-box"></div> | |
<p><a href="/force-run">Trigger Manual Run</a></p> | |
<p><a href="/run-check">Check Scheduler Status</a></p> | |
</body> | |
</html> | |
''', next_run=next_run) | |
def logs(): | |
"""Returns logs as JSON for AJAX polling.""" | |
return jsonify({'logs': execution_logs}) | |
def force_run(): | |
"""Manually trigger the script execution.""" | |
threading.Thread(target=run_cli_script, daemon=True).start() | |
return "Script executed manually", 200 | |
def run_check(): | |
"""Check if the scheduler is still running.""" | |
if not scheduler.running: | |
print("Scheduler was stopped! Restarting...") | |
scheduler.start() | |
return "Scheduler is running", 200 | |
if __name__ == '__main__': | |
app.run(host='0.0.0.0', port=7860) | |