#!/usr/bin/env python3 """ MYP Platform - Requirements Update Script Aktualisiert die Requirements basierend auf tatsächlich verwendeten Imports """ import os import sys import subprocess import ast import importlib.util from pathlib import Path from typing import Set, List, Dict def get_imports_from_file(file_path: Path) -> Set[str]: """Extrahiert alle Import-Statements aus einer Python-Datei.""" imports = set() try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() tree = ast.parse(content) for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imports.add(alias.name.split('.')[0]) elif isinstance(node, ast.ImportFrom): if node.module: imports.add(node.module.split('.')[0]) except Exception as e: print(f"Fehler beim Parsen von {file_path}: {e}") return imports def get_all_imports(project_root: Path) -> Set[str]: """Sammelt alle Imports aus dem Projekt.""" all_imports = set() # Wichtige Dateien analysieren important_files = [ 'app.py', 'models.py', 'utils/rate_limiter.py', 'utils/job_scheduler.py', 'utils/queue_manager.py', 'utils/ssl_manager.py', 'utils/security.py', 'utils/permissions.py', 'utils/analytics.py', 'utils/template_helpers.py', 'utils/logging_config.py' ] for file_path in important_files: full_path = project_root / file_path if full_path.exists(): imports = get_imports_from_file(full_path) all_imports.update(imports) print(f"✓ Analysiert: {file_path} ({len(imports)} Imports)") return all_imports def get_package_mapping() -> Dict[str, str]: """Mapping von Import-Namen zu PyPI-Paketnamen.""" return { 'flask': 'Flask', 'flask_login': 'Flask-Login', 'flask_wtf': 'Flask-WTF', 'sqlalchemy': 'SQLAlchemy', 'werkzeug': 'Werkzeug', 'bcrypt': 'bcrypt', 'cryptography': 'cryptography', 'PyP100': 'PyP100', 'redis': 'redis', 'requests': 'requests', 'jinja2': 'Jinja2', 'markupsafe': 'MarkupSafe', 'itsdangerous': 'itsdangerous', 'psutil': 'psutil', 'click': 'click', 'blinker': 'blinker', 'pywin32': 'pywin32', 'pytest': 'pytest', 'gunicorn': 'gunicorn' } def get_current_versions() -> Dict[str, str]: """Holt die aktuell installierten Versionen.""" versions = {} try: result = subprocess.run(['pip', 'list', '--format=freeze'], capture_output=True, text=True, encoding='utf-8', errors='replace') for line in result.stdout.strip().split('\n'): if '==' in line: package, version = line.split('==', 1) versions[package.lower()] = version except Exception as e: print(f"Fehler beim Abrufen der Versionen: {e}") return versions def check_package_availability(package: str, version: str = None) -> bool: """Prüft, ob ein Paket in der angegebenen Version verfügbar ist.""" try: if version: cmd = ['pip', 'index', 'versions', package] else: cmd = ['pip', 'show', package] result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8', errors='replace') return result.returncode == 0 except Exception: return False def generate_requirements(imports: Set[str], versions: Dict[str, str]) -> List[str]: """Generiert die Requirements-Liste.""" package_mapping = get_package_mapping() requirements = [] # Standard-Bibliotheken, die nicht installiert werden müssen stdlib_modules = { 'os', 'sys', 'logging', 'atexit', 'datetime', 'time', 'subprocess', 'json', 'signal', 'threading', 'functools', 'typing', 'contextlib', 'secrets', 'hashlib', 'calendar', 'random', 'socket', 'ipaddress', 'enum', 'dataclasses', 'concurrent', 'collections' } # Lokale Module ausschließen local_modules = { 'models', 'utils', 'config', 'blueprints' } # Nur externe Pakete berücksichtigen external_imports = imports - stdlib_modules - local_modules for import_name in sorted(external_imports): package_name = package_mapping.get(import_name, import_name) # Version aus installierten Paketen holen version = versions.get(package_name.lower()) if version and check_package_availability(package_name, version): if package_name == 'pywin32': requirements.append(f"{package_name}=={version}; sys_platform == \"win32\"") else: requirements.append(f"{package_name}=={version}") else: print(f"⚠️ Paket {package_name} nicht gefunden oder Version unbekannt") return requirements def write_requirements_file(requirements: List[str], output_file: Path): """Schreibt die Requirements in eine Datei.""" header = """# MYP Platform - Python Dependencies # Basierend auf tatsächlich verwendeten Imports in app.py # Automatisch generiert am: {date} # Installiere mit: pip install -r requirements.txt # ===== CORE FLASK FRAMEWORK ===== # Direkt in app.py verwendet {flask_requirements} # ===== DATENBANK ===== # SQLAlchemy für Datenbankoperationen (models.py, app.py) {db_requirements} # ===== SICHERHEIT UND AUTHENTIFIZIERUNG ===== # Werkzeug für Passwort-Hashing und Utilities (app.py) {security_requirements} # ===== SMART PLUG STEUERUNG ===== # PyP100 für TP-Link Tapo Smart Plugs (utils/job_scheduler.py) {smartplug_requirements} # ===== RATE LIMITING UND CACHING ===== # Redis für Rate Limiting (utils/rate_limiter.py) - optional {cache_requirements} # ===== HTTP REQUESTS ===== # Requests für HTTP-Anfragen (utils/queue_manager.py, utils/debug_drucker_erkennung.py) {http_requirements} # ===== TEMPLATE ENGINE ===== # Jinja2 und MarkupSafe (automatisch mit Flask installiert, aber explizit für utils/template_helpers.py) {template_requirements} # ===== SYSTEM MONITORING ===== # psutil für System-Monitoring (utils/debug_utils.py, utils/debug_cli.py) {monitoring_requirements} # ===== ZUSÄTZLICHE CORE ABHÄNGIGKEITEN ===== # Click für CLI-Kommandos (automatisch mit Flask) {core_requirements} # ===== WINDOWS-SPEZIFISCHE ABHÄNGIGKEITEN ===== # Nur für Windows-Systeme erforderlich {windows_requirements} # ===== OPTIONAL: ENTWICKLUNG UND TESTING ===== # Nur für Entwicklungsumgebung {dev_requirements} # ===== OPTIONAL: PRODUKTIONS-SERVER ===== # Gunicorn für Produktionsumgebung {prod_requirements} """ # Requirements kategorisieren flask_reqs = [r for r in requirements if any(x in r.lower() for x in ['flask'])] db_reqs = [r for r in requirements if 'SQLAlchemy' in r] security_reqs = [r for r in requirements if any(x in r for x in ['Werkzeug', 'bcrypt', 'cryptography'])] smartplug_reqs = [r for r in requirements if 'PyP100' in r] cache_reqs = [r for r in requirements if 'redis' in r] http_reqs = [r for r in requirements if 'requests' in r] template_reqs = [r for r in requirements if any(x in r for x in ['Jinja2', 'MarkupSafe', 'itsdangerous'])] monitoring_reqs = [r for r in requirements if 'psutil' in r] core_reqs = [r for r in requirements if any(x in r for x in ['click', 'blinker'])] windows_reqs = [r for r in requirements if 'sys_platform' in r] from datetime import datetime content = header.format( date=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), flask_requirements='\n'.join(flask_reqs) or '# Keine Flask-spezifischen Requirements', db_requirements='\n'.join(db_reqs) or '# Keine Datenbank-Requirements', security_requirements='\n'.join(security_reqs) or '# Keine Sicherheits-Requirements', smartplug_requirements='\n'.join(smartplug_reqs) or '# Keine Smart Plug Requirements', cache_requirements='\n'.join(cache_reqs) or '# Keine Cache-Requirements', http_requirements='\n'.join(http_reqs) or '# Keine HTTP-Requirements', template_requirements='\n'.join(template_reqs) or '# Keine Template-Requirements', monitoring_requirements='\n'.join(monitoring_reqs) or '# Keine Monitoring-Requirements', core_requirements='\n'.join(core_reqs) or '# Keine Core-Requirements', windows_requirements='\n'.join(windows_reqs) or '# Keine Windows-Requirements', dev_requirements='pytest==8.3.4; extra == "dev"\npytest-cov==6.0.0; extra == "dev"', prod_requirements='gunicorn==23.0.0; extra == "prod"' ) with open(output_file, 'w', encoding='utf-8') as f: f.write(content) def main(): """Hauptfunktion.""" print("🔄 MYP Platform Requirements Update") print("=" * 50) # Projekt-Root ermitteln project_root = Path(__file__).parent print(f"📁 Projekt-Verzeichnis: {project_root}") # Imports sammeln print("\n📋 Sammle Imports aus wichtigen Dateien...") imports = get_all_imports(project_root) print(f"\n📦 Gefundene externe Imports: {len(imports)}") for imp in sorted(imports): print(f" - {imp}") # Aktuelle Versionen abrufen print("\n🔍 Prüfe installierte Versionen...") versions = get_current_versions() # Requirements generieren print("\n⚙️ Generiere Requirements...") requirements = generate_requirements(imports, versions) # Requirements-Datei schreiben output_file = project_root / 'requirements.txt' write_requirements_file(requirements, output_file) print(f"\n✅ Requirements aktualisiert: {output_file}") print(f"📊 {len(requirements)} Pakete in requirements.txt") # Zusammenfassung print("\n📋 Generierte Requirements:") for req in requirements: print(f" - {req}") print("\n🎉 Requirements-Update abgeschlossen!") print("\nNächste Schritte:") print("1. pip install -r requirements.txt") print("2. Anwendung testen") print("3. requirements-dev.txt und requirements-prod.txt bei Bedarf anpassen") if __name__ == "__main__": main()