File size: 5,449 Bytes
14b7018
e96a62a
 
88e9f1a
aa456fd
2a6e962
5c6037b
e96a62a
2a6e962
e96a62a
2a6e962
 
 
 
 
3a2ce2a
2a6e962
 
5c6037b
aa456fd
2a6e962
 
 
 
 
 
 
 
 
 
aa456fd
 
2a6e962
 
 
 
 
 
87175db
e96a62a
87175db
5c6037b
87175db
 
2a6e962
5c6037b
87175db
2a6e962
 
 
 
 
 
 
 
 
87175db
5c6037b
2a6e962
 
 
 
e96a62a
88e9f1a
2a6e962
88e9f1a
 
2a6e962
 
cd03aa3
 
 
 
 
 
88e9f1a
cd03aa3
 
88e9f1a
2a6e962
88e9f1a
e96a62a
2a6e962
e96a62a
 
88e9f1a
cd03aa3
2a6e962
de50ad7
5c6037b
 
 
 
 
de50ad7
14b7018
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de50ad7
5c6037b
3a2ce2a
 
 
 
5c6037b
 
3a2ce2a
5c6037b
88e9f1a
 
5c6037b
3a2ce2a
 
5c6037b
 
 
3a2ce2a
cd03aa3
3a2ce2a
de50ad7
cd03aa3
88e9f1a
5c6037b
 
de50ad7
5c6037b
2a6e962
14b7018
 
 
 
 
2a6e962
3a2ce2a
 
88e9f1a
 
2a6e962
3a2ce2a
e96a62a
2a6e962
88e9f1a
 
2a6e962
88e9f1a
2a6e962
88e9f1a
2a6e962
88e9f1a
 
2a6e962
e96a62a
14b7018
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
from flask import Flask, render_template_string, jsonify
from apscheduler.schedulers.background import BackgroundScheduler
import subprocess
import threading
import pytz
import logging
from datetime import datetime

# Initialize Flask app
app = Flask(__name__)

# Setup logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

# Execution logs
execution_logs = []
MAX_LOG_ENTRIES = 50
log_lock = threading.Lock()  # Prevents race conditions when modifying logs


def add_log_entry(entry):
    """Safely add log entries while maintaining MAX_LOG_ENTRIES limit."""
    with log_lock:
        execution_logs.append(entry)
        if len(execution_logs) > MAX_LOG_ENTRIES:
            execution_logs.pop(0)


def get_ist_time():
    """Get the current time in IST (Indian Standard Time)."""
    utc_now = datetime.utcnow()
    ist_timezone = pytz.timezone("Asia/Kolkata")
    return utc_now.replace(tzinfo=pytz.utc).astimezone(ist_timezone)


def run_cli_script():
    """Runs cli.py and streams logs in real-time to both UI and terminal."""
    timestamp = get_ist_time().strftime("%Y-%m-%d %H:%M:%S IST")

    try:
        process = subprocess.Popen(
            ["python", "cli.py"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )

        stdout, stderr = process.communicate()  # Wait for process to complete

        if stdout:
            add_log_entry({'time': timestamp, 'output': stdout, 'error': ''})
            logging.info(stdout.strip())

        if stderr:
            add_log_entry({'time': timestamp, 'output': '', 'error': stderr})
            logging.error(stderr.strip())

    except Exception as e:
        error_msg = str(e)
        add_log_entry({'time': timestamp, 'output': '', 'error': error_msg})
        logging.error(f"Error: {error_msg}")


def start_initial_run():
    """Runs the CLI script immediately upon startup in a separate thread."""
    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()

# Ensure script runs once on startup
start_initial_run()


@app.route('/')
def home():
    """Main UI displaying logs and next run time."""
    job = scheduler.get_job('main_job')
    next_run = get_ist_time().strftime('%Y-%m-%d %H:%M:%S IST') 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)


@app.route('/logs')
def logs():
    """Returns logs as JSON for AJAX polling."""
    return jsonify({'logs': execution_logs})


@app.route('/force-run')
def force_run():
    """Manually trigger the script execution."""
    threading.Thread(target=run_cli_script, daemon=True).start()
    logging.info("Manual script execution triggered")
    return "Script executed manually", 200


@app.route('/run-check')
def run_check():
    """Check if the scheduler is still running and restart if necessary."""
    if not scheduler.running:
        logging.warning("Scheduler was stopped! Restarting...")
        scheduler.start()
        return "Scheduler restarted", 200
    return "Scheduler is running", 200


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=7860)