Spaces:
Running
Running
from flask import Flask, render_template_string | |
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 the output in real-time.""" | |
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 # Ensures line buffering (real-time output) | |
) | |
# Stream stdout in real-time | |
for line in process.stdout: | |
log_entry['output'] += line | |
print(line, end="") # Also print to terminal for debugging | |
# Capture errors in real-time | |
for line in process.stderr: | |
log_entry['error'] += line | |
print(line, end="") # Also print to terminal for debugging | |
except Exception as e: | |
log_entry['error'] = str(e) | |
finally: | |
execution_logs.append(log_entry) | |
if len(execution_logs) > MAX_LOG_ENTRIES: | |
execution_logs.pop(0) | |
# Start script in a separate thread to avoid blocking Flask | |
def start_initial_run(): | |
threading.Thread(target=run_cli_script, daemon=True).start() | |
# Initialize scheduler | |
scheduler = BackgroundScheduler(daemon=True) | |
scheduler.add_job( | |
run_cli_script, | |
'interval', | |
hours=3, | |
id='main_job', | |
next_run_time=datetime.now() | |
) | |
scheduler.start() | |
# Run the script asynchronously to prevent blocking | |
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> | |
<meta http-equiv="refresh" content="10"> | |
<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 class="log-box"> | |
{% for log in logs|reverse %} | |
<div class="timestamp">{{ log.time }}</div> | |
{% if log.output %} | |
<div class="output">{{ log.output }}</div> | |
{% endif %} | |
{% if log.error %} | |
<div class="error">{{ log.error }}</div> | |
{% endif %} | |
<hr> | |
{% else %} | |
<div>No logs available yet</div> | |
{% endfor %} | |
</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, 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) | |