app.py aktualisiert

This commit is contained in:
2025-08-18 11:25:50 +00:00
parent 49e4c68e1e
commit ce9b458cef

73
app.py
View File

@@ -1,9 +1,11 @@
# app.py (v2.1 - Final für Docker) # app.py (v2.1 - mit Job-Manager)
from flask import Flask, jsonify, request from flask import Flask, jsonify, request
import subprocess import subprocess
import sys import sys
import os import os
import logging import logging
import uuid
import json
from datetime import datetime from datetime import datetime
from pyngrok import ngrok, conf from pyngrok import ngrok, conf
from config import Config from config import Config
@@ -11,32 +13,28 @@ from config import Config
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
app = Flask(__name__) app = Flask(__name__)
# Der Pfad, in dem sich der Code im Container befindet STATUS_DIR = "job_status"
# (muss mit WORKDIR im Dockerfile übereinstimmen) os.makedirs(STATUS_DIR, exist_ok=True)
APP_DIR = "/code"
SCRIPT_MAP = { SCRIPT_MAP = {
"run_duplicate_check": {"script": "duplicate_checker.py", "args": []}, "run_duplicate_check": {"script": "duplicate_checker.py", "args": []},
"run_reclassify_branches": {"script": "brancheneinstufung2.py", "args": ["--mode", "reclassify_branches"]}, "run_reclassify_branches": {"script": "brancheneinstufung2.py", "args": ["--mode", "reclassify_branches"]},
# ... fügen Sie hier bei Bedarf brancheneinstufung2.py hinzu "run_predict_technicians": {"script": "brancheneinstufung2.py", "args": ["--mode", "predict_technicians"]},
} }
def setup_ngrok(): def setup_ngrok():
"""Konfiguriert und startet den ngrok-Tunnel.""" # ... (Ihre ngrok-Setup-Funktion bleibt unverändert)
try: try:
authtoken = os.environ.get("NGROK_AUTHTOKEN") authtoken = os.environ.get("NGROK_AUTHTOKEN")
if not authtoken: if not authtoken:
if os.path.exists("ngrok_authtoken.txt"): if os.path.exists("ngrok_authtoken.txt"):
with open("ngrok_authtoken.txt", "r") as f: authtoken = f.read().strip() with open("ngrok_authtoken.txt", "r") as f: authtoken = f.read().strip()
if not authtoken: if not authtoken:
logging.error("NGROK_AUTHTOKEN nicht gefunden. Tunnel kann nicht gestartet werden.") logging.error("NGROK_AUTHTOKEN nicht gefunden.")
return None return None
conf.get_default().auth_token = authtoken conf.get_default().auth_token = authtoken
public_url = ngrok.connect(8080, "http") public_url = ngrok.connect(8080, "http")
logging.info(f"!!! Ngrok-Tunnel gestartet: {public_url} !!!") logging.info(f"!!! Ngrok-Tunnel gestartet: {public_url} !!!")
logging.info("!!! Bitte diese URL im Google Apps Script eintragen. !!!")
return public_url return public_url
except Exception as e: except Exception as e:
logging.error(f"Fehler beim Starten von ngrok: {e}") logging.error(f"Fehler beim Starten von ngrok: {e}")
@@ -44,60 +42,51 @@ def setup_ngrok():
@app.route('/run-script', methods=['POST']) @app.route('/run-script', methods=['POST'])
def run_script(): def run_script():
""" """Startet ein Skript als Hintergrundprozess und verfolgt es über eine Job-ID."""
Startet ein Skript als Hintergrundprozess.
Finale, robuste Version mit absoluten Pfaden.
"""
action = "unknown"
try: try:
data = request.get_json() data = request.get_json()
action = data.get('action') action = data.get('action')
if not action or action not in SCRIPT_MAP: if not action or action not in SCRIPT_MAP:
return jsonify({"status": "error", "message": "Ungültige Aktion."}), 400 return jsonify({"status": "error", "message": "Ungültige Aktion."}), 400
script_config = SCRIPT_MAP[action] script_config = SCRIPT_MAP[action]
script_name = script_config["script"] script_name = script_config["script"]
# ENTSCHEIDENDER FIX: Finde den Pfad relativ zu DIESER app.py Datei # Job-ID generieren und als Argument übergeben
# __file__ ist der Pfad zur aktuell laufenden Datei (app.py) job_id = str(uuid.uuid4())
# os.path.dirname(__file__) ist der Ordner, in dem app.py liegt (/app im Container) command = [sys.executable, script_name] + script_config["args"] + ["--job-id", job_id]
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
script_path = os.path.join(script_dir, script_name)
if not os.path.exists(script_path):
logging.error(f"Skript unter dem Pfad '{script_path}' NICHT GEFUNDEN.")
return jsonify({"status": "error", "message": f"Server-Fehler: Skript '{script_name}' nicht gefunden."}), 500
logging.info(f"Aktion '{action}' empfangen. Starte Skript: '{script_path}'...")
python_executable = sys.executable
command = [python_executable, script_path] + script_config["args"]
log_dir = os.path.join(script_dir, "Log") log_dir = os.path.join(script_dir, "Log")
os.makedirs(log_dir, exist_ok=True) os.makedirs(log_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
process_log_path = os.path.join(log_dir, f"{timestamp}_{action}.log") process_log_path = os.path.join(log_dir, f"{timestamp}_{action}_{job_id[:8]}.log")
with open(process_log_path, 'w') as log_file: with open(process_log_path, 'w') as log_file:
subprocess.Popen( subprocess.Popen(command, cwd=script_dir, stdout=log_file, stderr=log_file)
command,
# Wir müssen das Arbeitsverzeichnis nicht mehr setzen, da wir absolute Pfade verwenden
stdout=log_file,
stderr=log_file
)
return jsonify({"status": "success", "message": f"Aktion '{action}' gestartet. Log wird in '{process_log_path}' geschrieben."}), 200 # Erstelle eine initiale Statusdatei
status_file = os.path.join(STATUS_DIR, f"{job_id}.json")
with open(status_file, 'w') as f:
json.dump({
"action": action, "status": "Gestartet",
"start_time": datetime.now().isoformat(),
"progress": "Prozess wird initialisiert..."
}, f)
return jsonify({"status": "success", "message": f"Aktion '{action}' gestartet."}), 200
except Exception as e: except Exception as e:
logging.error(f"Kritischer Fehler in der run_script-Route bei Aktion '{action}': {e}", exc_info=True) logging.error(f"Fehler in run_script: {e}", exc_info=True)
return jsonify({"status": "error", "message": f"Server-Fehler: {str(e)}"}), 500 return jsonify({"status": "error", "message": str(e)}), 500
# --- NEUE FUNKTION ---
@app.route('/get-status', methods=['GET']) @app.route('/get-status', methods=['GET'])
def get_status(): def get_status():
"""Liest alle Job-Status-Dateien und gibt den aktuellen Stand zurück.""" """Liest alle Job-Status-Dateien und gibt den aktuellen Stand zurück."""
all_statuses = [] all_statuses = []
for filename in os.listdir(STATUS_DIR): for filename in sorted(os.listdir(STATUS_DIR), reverse=True): # Neueste zuerst
if filename.endswith(".json"): if filename.endswith(".json"):
try: try:
with open(os.path.join(STATUS_DIR, filename), 'r') as f: with open(os.path.join(STATUS_DIR, filename), 'r') as f:
@@ -107,8 +96,6 @@ def get_status():
except Exception as e: except Exception as e:
logging.warning(f"Konnte Statusdatei {filename} nicht lesen: {e}") logging.warning(f"Konnte Statusdatei {filename} nicht lesen: {e}")
# Sortiere nach Startzeit, neuester Job zuerst
all_statuses.sort(key=lambda x: x.get('start_time', ''), reverse=True)
return jsonify(all_statuses) return jsonify(all_statuses)
if __name__ == '__main__': if __name__ == '__main__':