# app.py (v2.0 - Autark & Transparent) from flask import Flask, jsonify, request, render_template_string import subprocess import sys import os import logging import uuid import json from datetime import datetime from pyngrok import ngrok, conf from config import Config # --- Konfiguration --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__) # Verzeichnis für Statusdateien der laufenden Jobs STATUS_DIR = "job_status" os.makedirs(STATUS_DIR, exist_ok=True) # SCRIPT MAP (Passen Sie hier bei Bedarf die Skriptnamen an, z.B. auf brancheneinstufung2.py) SCRIPT_MAP = { "run_duplicate_check": {"script": "duplicate_checker.py", "args": []}, "run_reclassify_branches": {"script": "brancheneinstufung.py", "args": ["--mode", "reclassify_branches"]}, "run_predict_technicians": {"script": "brancheneinstufung.py", "args": ["--mode", "predict_technicians"]}, } def setup_ngrok(): """Konfiguriert und startet den ngrok-Tunnel.""" try: authtoken = os.environ.get("NGROK_AUTHTOKEN") if not authtoken: if os.path.exists("ngrok_authtoken.txt"): with open("ngrok_authtoken.txt", "r") as f: authtoken = f.read().strip() if not authtoken: logging.error("NGROK_AUTHTOKEN nicht gefunden. Tunnel kann nicht gestartet werden.") return None conf.get_default().auth_token = authtoken public_url = ngrok.connect(8080, "http") logging.info(f"!!! Ngrok-Tunnel gestartet: {public_url} !!!") logging.info("!!! Bitte diese URL im Google Apps Script eintragen. !!!") return public_url except Exception as e: logging.error(f"Fehler beim Starten von ngrok: {e}") sys.exit(1) @app.route('/run-script', methods=['POST']) def run_script(): """Startet ein Skript als Hintergrundprozess und verfolgt es über eine Job-ID.""" try: data = request.get_json() action = data.get('action') if not action or action not in SCRIPT_MAP: return jsonify({"status": "error", "message": "Ungültige Aktion."}), 400 script_config = SCRIPT_MAP[action] script_name = script_config["script"] # NEU: Job-ID generieren und als Argument übergeben job_id = str(uuid.uuid4()) command = [sys.executable, script_name] + script_config["args"] + ["--job-id", job_id] # ENTSCHEIDENDER FIX: Setze das Arbeitsverzeichnis explizit script_dir = os.path.dirname(os.path.abspath(__file__)) logging.info(f"Starte Job {job_id} für Aktion '{action}' im Verzeichnis '{script_dir}'...") subprocess.Popen(command, cwd=script_dir) # 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}' mit Job-ID {job_id} gestartet."}), 200 except Exception as e: logging.error(f"Fehler in run_script: {e}") return jsonify({"status": "error", "message": str(e)}), 500 @app.route('/get-status', methods=['GET']) def get_status(): """Liest alle Job-Status-Dateien und gibt den aktuellen Stand zurück.""" all_statuses = [] for filename in os.listdir(STATUS_DIR): if filename.endswith(".json"): try: with open(os.path.join(STATUS_DIR, filename), 'r') as f: status_data = json.load(f) status_data['job_id'] = filename.replace('.json', '') all_statuses.append(status_data) except Exception as 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) if __name__ == '__main__': setup_ngrok() app.run(host='0.0.0.0', port=8080)