Spaces:
Paused
Paused
| 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 = 1000 | |
| def run_cli_script(): | |
| """Runs cli.py and streams logs in real-time to both UI and terminal.""" | |
| global execution_logs # Declare the global variable | |
| timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") | |
| try: | |
| process = subprocess.Popen( | |
| ["python3", "cli.py"], | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, # Merge stdout and stderr | |
| text=True, | |
| bufsize=1 | |
| ) | |
| for line in process.stdout: | |
| execution_logs.append({'time': timestamp, 'output': line.strip(), 'error': ''}) | |
| print(line, end="") | |
| # Trim logs efficiently | |
| if len(execution_logs) > MAX_LOG_ENTRIES: | |
| execution_logs = execution_logs[-MAX_LOG_ENTRIES:] | |
| except Exception as e: | |
| execution_logs.append({'time': timestamp, 'output': '', 'error': str(e)}) | |
| print(f"Error: {str(e)}") | |
| 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) | |