728 lines
29 KiB
Python
728 lines
29 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Automatisches Screenshot-Tool für Mitarbeiterschulungen
|
|
======================================================
|
|
|
|
Dieses Tool erstellt automatisch Screenshots von allen verfügbaren Seiten
|
|
der Webanwendung für Schulungszwecke und Präsentationsmaterial.
|
|
|
|
Funktionen:
|
|
- Automatische Erkennung aller verfügbaren Routen
|
|
- Screenshots in verschiedenen Auflösungen (Desktop, Tablet, Mobile)
|
|
- Strukturierte Ordnerorganisation für Schulungen
|
|
- Automatischer Login als Admin
|
|
- Detaillierter Bericht über erstellte Screenshots
|
|
- Behandlung von geschützten und öffentlichen Bereichen
|
|
|
|
Autor: KI-Assistant
|
|
Datum: 2025-01-16
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import json
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Dict, Tuple, Optional, Set
|
|
from urllib.parse import urljoin, urlparse
|
|
import traceback
|
|
import subprocess
|
|
|
|
# Selenium Imports
|
|
try:
|
|
from selenium import webdriver
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
from selenium.webdriver.support import expected_conditions as EC
|
|
from selenium.webdriver.chrome.options import Options as ChromeOptions
|
|
from selenium.webdriver.firefox.options import Options as FirefoxOptions
|
|
from selenium.webdriver.chrome.service import Service as ChromeService
|
|
from selenium.webdriver.firefox.service import Service as FirefoxService
|
|
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
|
|
SELENIUM_AVAILABLE = True
|
|
except ImportError as e:
|
|
print(f"⚠️ Selenium nicht verfügbar: {e}")
|
|
print("💡 Installieren Sie Selenium: pip install selenium")
|
|
SELENIUM_AVAILABLE = False
|
|
|
|
# Flask App Import
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
try:
|
|
from app import app, User
|
|
from models import get_db_session
|
|
FLASK_APP_AVAILABLE = True
|
|
except ImportError as e:
|
|
print(f"⚠️ Flask-App nicht verfügbar: {e}")
|
|
FLASK_APP_AVAILABLE = False
|
|
|
|
# Logging-Konfiguration
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
logging.FileHandler('screenshot_tool.log'),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class ScreenshotConfiguration:
|
|
"""Konfiguration für das Screenshot-Tool"""
|
|
|
|
# Basis-Konfiguration
|
|
BASE_URL = "http://localhost:5000"
|
|
ADMIN_EMAIL = "admin@example.com" # Anpassen falls nötig
|
|
ADMIN_PASSWORD = "admin123" # Anpassen falls nötig
|
|
|
|
# Screenshot-Ordner
|
|
SCREENSHOT_BASE_DIR = os.path.join("docs", "schulung", "screenshots")
|
|
|
|
# Browser-Konfiguration
|
|
BROWSER_TYPE = "chrome" # "chrome" oder "firefox"
|
|
HEADLESS = True # Für Server ohne GUI
|
|
|
|
# Auflösungen für verschiedene Geräte
|
|
RESOLUTIONS = {
|
|
"desktop": (1920, 1080),
|
|
"tablet": (1024, 768),
|
|
"mobile": (375, 667)
|
|
}
|
|
|
|
# Wartezeiten
|
|
PAGE_LOAD_TIMEOUT = 15
|
|
ELEMENT_WAIT_TIMEOUT = 10
|
|
SCREENSHOT_DELAY = 2
|
|
|
|
# Zu ignorierende Routen (z.B. API-Endpunkte)
|
|
IGNORED_ROUTES = {
|
|
"/api/", "/auth/api/", "/static/", "/favicon.ico",
|
|
"/robots.txt", "/sitemap.xml"
|
|
}
|
|
|
|
# Routen die spezielle Behandlung benötigen
|
|
SPECIAL_ROUTES = {
|
|
"/admin": "Benötigt Admin-Rechte",
|
|
"/user/": "Benötigt User-Login",
|
|
"/guest": "Öffentlich zugänglich"
|
|
}
|
|
|
|
class RouteDiscovery:
|
|
"""Klasse zur automatischen Erkennung aller verfügbaren Routen"""
|
|
|
|
def __init__(self):
|
|
self.routes = set()
|
|
self.protected_routes = set()
|
|
self.public_routes = set()
|
|
|
|
def discover_routes_from_app(self) -> List[str]:
|
|
"""Extrahiert alle Routen aus der Flask-App"""
|
|
if not FLASK_APP_AVAILABLE:
|
|
logger.warning("Flask-App nicht verfügbar - verwende Standard-Routen")
|
|
return self._get_default_routes()
|
|
|
|
discovered_routes = []
|
|
|
|
try:
|
|
with app.app_context():
|
|
# Alle URL-Regeln der App durchgehen
|
|
for rule in app.url_map.iter_rules():
|
|
# Nur GET-Routen für Screenshots
|
|
if 'GET' in rule.methods:
|
|
route = str(rule.rule)
|
|
|
|
# Dynamische Parameter ersetzen
|
|
if '<' in route:
|
|
resolved_routes = self._resolve_dynamic_route(route)
|
|
if resolved_routes:
|
|
for resolved_route in resolved_routes:
|
|
discovered_routes.append(resolved_route)
|
|
self._categorize_route(resolved_route)
|
|
else:
|
|
discovered_routes.append(route)
|
|
self._categorize_route(route)
|
|
|
|
logger.info(f"✅ {len(discovered_routes)} Routen aus Flask-App extrahiert")
|
|
return discovered_routes
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Fehler beim Extrahieren der Routen: {e}")
|
|
return self._get_default_routes()
|
|
|
|
def _resolve_dynamic_route(self, route: str) -> Optional[List[str]]:
|
|
"""Löst dynamische Routen mit Beispieldaten auf"""
|
|
resolved_routes = []
|
|
|
|
try:
|
|
if not FLASK_APP_AVAILABLE:
|
|
return None
|
|
|
|
with app.app_context():
|
|
db_session = get_db_session()
|
|
|
|
# User-ID Parameter
|
|
if '<int:user_id>' in route:
|
|
users = db_session.query(User).limit(3).all()
|
|
for user in users:
|
|
resolved_routes.append(route.replace('<int:user_id>', str(user.id)))
|
|
|
|
# Printer-ID Parameter
|
|
elif '<int:printer_id>' in route:
|
|
# Beispiel-Printer-IDs (1, 2, 3)
|
|
for printer_id in [1, 2, 3]:
|
|
resolved_routes.append(route.replace('<int:printer_id>', str(printer_id)))
|
|
|
|
# Job-ID Parameter
|
|
elif '<int:job_id>' in route:
|
|
# Beispiel-Job-IDs (1, 2, 3)
|
|
for job_id in [1, 2, 3]:
|
|
resolved_routes.append(route.replace('<int:job_id>', str(job_id)))
|
|
|
|
# Andere Parameter mit Standard-Werten
|
|
elif '<' in route:
|
|
# Generische Behandlung
|
|
import re
|
|
pattern = r'<[^>]+>'
|
|
resolved_route = re.sub(pattern, '1', route)
|
|
resolved_routes.append(resolved_route)
|
|
|
|
db_session.close()
|
|
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ Fehler beim Auflösen der dynamischen Route {route}: {e}")
|
|
|
|
return resolved_routes if resolved_routes else None
|
|
|
|
def _categorize_route(self, route: str):
|
|
"""Kategorisiert Routen als öffentlich oder geschützt"""
|
|
if any(protected in route for protected in ['/admin', '/user/', '/dashboard']):
|
|
self.protected_routes.add(route)
|
|
elif any(public in route for public in ['/guest', '/privacy', '/terms', '/imprint']):
|
|
self.public_routes.add(route)
|
|
else:
|
|
# Standardmäßig als geschützt behandeln
|
|
self.protected_routes.add(route)
|
|
|
|
def _get_default_routes(self) -> List[str]:
|
|
"""Fallback-Routen falls automatische Erkennung fehlschlägt"""
|
|
return [
|
|
"/",
|
|
"/dashboard",
|
|
"/admin",
|
|
"/admin-dashboard",
|
|
"/printers",
|
|
"/jobs",
|
|
"/jobs/new",
|
|
"/stats",
|
|
"/user/profile",
|
|
"/user/settings",
|
|
"/admin/users/add",
|
|
"/admin/printers/add",
|
|
"/reports",
|
|
"/maintenance",
|
|
"/guest",
|
|
"/guest-status",
|
|
"/privacy",
|
|
"/terms",
|
|
"/imprint",
|
|
"/demo"
|
|
]
|
|
|
|
class BrowserManager:
|
|
"""Verwaltet den Webbrowser für Screenshots"""
|
|
|
|
def __init__(self, config: ScreenshotConfiguration):
|
|
self.config = config
|
|
self.driver = None
|
|
|
|
def initialize_browser(self) -> bool:
|
|
"""Initialisiert den Webbrowser"""
|
|
try:
|
|
if self.config.BROWSER_TYPE.lower() == "chrome":
|
|
self.driver = self._setup_chrome()
|
|
elif self.config.BROWSER_TYPE.lower() == "firefox":
|
|
self.driver = self._setup_firefox()
|
|
else:
|
|
raise ValueError(f"Unbekannter Browser-Typ: {self.config.BROWSER_TYPE}")
|
|
|
|
# Browser-Konfiguration
|
|
self.driver.set_page_load_timeout(self.config.PAGE_LOAD_TIMEOUT)
|
|
self.driver.implicitly_wait(self.config.ELEMENT_WAIT_TIMEOUT)
|
|
|
|
logger.info(f"✅ Browser {self.config.BROWSER_TYPE} erfolgreich initialisiert")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Fehler beim Initialisieren des Browsers: {e}")
|
|
return False
|
|
|
|
def _setup_chrome(self):
|
|
"""Konfiguriert Chrome-Browser"""
|
|
options = ChromeOptions()
|
|
|
|
if self.config.HEADLESS:
|
|
options.add_argument('--headless')
|
|
|
|
# Weitere Chrome-Optionen für Stabilität
|
|
options.add_argument('--no-sandbox')
|
|
options.add_argument('--disable-dev-shm-usage')
|
|
options.add_argument('--disable-gpu')
|
|
options.add_argument('--window-size=1920,1080')
|
|
options.add_argument('--disable-notifications')
|
|
options.add_argument('--disable-popup-blocking')
|
|
|
|
try:
|
|
# Automatische Erkennung des ChromeDriver
|
|
return webdriver.Chrome(options=options)
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ ChromeDriver automatisch nicht gefunden: {e}")
|
|
logger.info("💡 Installieren Sie ChromeDriver oder verwenden Sie webdriver-manager")
|
|
raise
|
|
|
|
def _setup_firefox(self):
|
|
"""Konfiguriert Firefox-Browser"""
|
|
options = FirefoxOptions()
|
|
|
|
if self.config.HEADLESS:
|
|
options.add_argument('--headless')
|
|
|
|
try:
|
|
return webdriver.Firefox(options=options)
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ GeckoDriver nicht gefunden: {e}")
|
|
logger.info("💡 Installieren Sie GeckoDriver für Firefox")
|
|
raise
|
|
|
|
def set_resolution(self, resolution_name: str):
|
|
"""Setzt die Browser-Auflösung"""
|
|
if resolution_name in self.config.RESOLUTIONS:
|
|
width, height = self.config.RESOLUTIONS[resolution_name]
|
|
self.driver.set_window_size(width, height)
|
|
logger.debug(f"📱 Auflösung gesetzt: {resolution_name} ({width}x{height})")
|
|
|
|
def close(self):
|
|
"""Schließt den Browser"""
|
|
if self.driver:
|
|
try:
|
|
self.driver.quit()
|
|
logger.info("✅ Browser geschlossen")
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ Fehler beim Schließen des Browsers: {e}")
|
|
|
|
class ScreenshotTool:
|
|
"""Hauptklasse für das Screenshot-Tool"""
|
|
|
|
def __init__(self, config: ScreenshotConfiguration = None):
|
|
self.config = config or ScreenshotConfiguration()
|
|
self.browser = BrowserManager(self.config)
|
|
self.route_discovery = RouteDiscovery()
|
|
self.results = {
|
|
"success": [],
|
|
"failed": [],
|
|
"skipped": [],
|
|
"total_screenshots": 0,
|
|
"start_time": None,
|
|
"end_time": None
|
|
}
|
|
|
|
def run_screenshot_session(self) -> Dict:
|
|
"""Führt eine komplette Screenshot-Session durch"""
|
|
logger.info("🚀 Starte automatische Screenshot-Erstellung für Schulungen")
|
|
self.results["start_time"] = datetime.now()
|
|
|
|
try:
|
|
# 1. Vorbereitung
|
|
if not self._prepare_environment():
|
|
return self.results
|
|
|
|
# 2. Browser initialisieren
|
|
if not self.browser.initialize_browser():
|
|
return self.results
|
|
|
|
# 3. Routen entdecken
|
|
routes = self.route_discovery.discover_routes_from_app()
|
|
logger.info(f"📋 {len(routes)} Routen gefunden")
|
|
|
|
# 4. Login durchführen
|
|
if not self._perform_admin_login():
|
|
logger.error("❌ Admin-Login fehlgeschlagen")
|
|
return self.results
|
|
|
|
# 5. Screenshots erstellen
|
|
self._create_screenshots_for_routes(routes)
|
|
|
|
# 6. Bericht generieren
|
|
self._generate_report()
|
|
|
|
except KeyboardInterrupt:
|
|
logger.info("⏹️ Screenshot-Session vom Benutzer abgebrochen")
|
|
except Exception as e:
|
|
logger.error(f"❌ Unerwarteter Fehler: {e}")
|
|
logger.debug(traceback.format_exc())
|
|
finally:
|
|
self.browser.close()
|
|
self.results["end_time"] = datetime.now()
|
|
|
|
return self.results
|
|
|
|
def _prepare_environment(self) -> bool:
|
|
"""Bereitet die Umgebung für Screenshots vor"""
|
|
try:
|
|
# Screenshot-Ordner erstellen
|
|
os.makedirs(self.config.SCREENSHOT_BASE_DIR, exist_ok=True)
|
|
|
|
# Unterordner für verschiedene Kategorien
|
|
categories = ["admin", "benutzer", "oeffentlich", "alle_auflösungen"]
|
|
for category in categories:
|
|
category_dir = os.path.join(self.config.SCREENSHOT_BASE_DIR, category)
|
|
os.makedirs(category_dir, exist_ok=True)
|
|
|
|
# Auflösungs-Unterordner
|
|
for resolution in self.config.RESOLUTIONS.keys():
|
|
resolution_dir = os.path.join(category_dir, resolution)
|
|
os.makedirs(resolution_dir, exist_ok=True)
|
|
|
|
logger.info(f"✅ Screenshot-Ordner vorbereitet: {self.config.SCREENSHOT_BASE_DIR}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Fehler bei der Umgebungsvorbereitung: {e}")
|
|
return False
|
|
|
|
def _perform_admin_login(self) -> bool:
|
|
"""Führt den Admin-Login durch"""
|
|
try:
|
|
login_url = urljoin(self.config.BASE_URL, "/auth/login")
|
|
self.browser.driver.get(login_url)
|
|
|
|
# Warten bis Login-Formular geladen ist
|
|
wait = WebDriverWait(self.browser.driver, self.config.ELEMENT_WAIT_TIMEOUT)
|
|
|
|
# Email eingeben
|
|
email_field = wait.until(EC.presence_of_element_located((By.NAME, "email")))
|
|
email_field.clear()
|
|
email_field.send_keys(self.config.ADMIN_EMAIL)
|
|
|
|
# Passwort eingeben
|
|
password_field = self.browser.driver.find_element(By.NAME, "password")
|
|
password_field.clear()
|
|
password_field.send_keys(self.config.ADMIN_PASSWORD)
|
|
|
|
# Login-Button klicken
|
|
login_button = self.browser.driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
|
|
login_button.click()
|
|
|
|
# Warten auf Weiterleitung zum Dashboard
|
|
wait.until(lambda driver: "/dashboard" in driver.current_url or "/admin" in driver.current_url)
|
|
|
|
logger.info("✅ Admin-Login erfolgreich")
|
|
return True
|
|
|
|
except TimeoutException:
|
|
logger.error("❌ Timeout beim Admin-Login")
|
|
return False
|
|
except NoSuchElementException as e:
|
|
logger.error(f"❌ Login-Element nicht gefunden: {e}")
|
|
return False
|
|
except Exception as e:
|
|
logger.error(f"❌ Fehler beim Admin-Login: {e}")
|
|
return False
|
|
|
|
def _create_screenshots_for_routes(self, routes: List[str]):
|
|
"""Erstellt Screenshots für alle angegebenen Routen"""
|
|
total_routes = len(routes)
|
|
|
|
for i, route in enumerate(routes, 1):
|
|
logger.info(f"📸 Screenshot {i}/{total_routes}: {route}")
|
|
|
|
# Route ignorieren falls in Ignore-Liste
|
|
if any(ignored in route for ignored in self.config.IGNORED_ROUTES):
|
|
logger.debug(f"⏭️ Route ignoriert: {route}")
|
|
self.results["skipped"].append({"route": route, "reason": "In Ignore-Liste"})
|
|
continue
|
|
|
|
# Screenshots für alle Auflösungen erstellen
|
|
for resolution_name in self.config.RESOLUTIONS.keys():
|
|
try:
|
|
self._take_screenshot_for_route(route, resolution_name)
|
|
self.results["total_screenshots"] += 1
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Fehler bei Screenshot für {route} ({resolution_name}): {e}")
|
|
self.results["failed"].append({
|
|
"route": route,
|
|
"resolution": resolution_name,
|
|
"error": str(e)
|
|
})
|
|
|
|
def _take_screenshot_for_route(self, route: str, resolution_name: str):
|
|
"""Erstellt einen Screenshot für eine spezifische Route und Auflösung"""
|
|
# Browser-Auflösung setzen
|
|
self.browser.set_resolution(resolution_name)
|
|
|
|
# Zur Seite navigieren
|
|
full_url = urljoin(self.config.BASE_URL, route)
|
|
self.browser.driver.get(full_url)
|
|
|
|
# Warten bis Seite geladen ist
|
|
time.sleep(self.config.SCREENSHOT_DELAY)
|
|
|
|
# Kategorie bestimmen
|
|
category = self._determine_route_category(route)
|
|
|
|
# Dateiname generieren
|
|
safe_route_name = self._sanitize_filename(route)
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
filename = f"{safe_route_name}_{resolution_name}_{timestamp}.png"
|
|
|
|
# Screenshot-Pfad
|
|
screenshot_path = os.path.join(
|
|
self.config.SCREENSHOT_BASE_DIR,
|
|
category,
|
|
resolution_name,
|
|
filename
|
|
)
|
|
|
|
# Screenshot erstellen
|
|
self.browser.driver.save_screenshot(screenshot_path)
|
|
|
|
# Erfolg protokollieren
|
|
self.results["success"].append({
|
|
"route": route,
|
|
"resolution": resolution_name,
|
|
"file_path": screenshot_path,
|
|
"file_size": os.path.getsize(screenshot_path),
|
|
"timestamp": datetime.now().isoformat()
|
|
})
|
|
|
|
logger.debug(f"✅ Screenshot gespeichert: {screenshot_path}")
|
|
|
|
def _determine_route_category(self, route: str) -> str:
|
|
"""Bestimmt die Kategorie einer Route für die Ordnerorganisation"""
|
|
if any(admin_route in route for admin_route in ['/admin', '/admin-dashboard']):
|
|
return "admin"
|
|
elif any(user_route in route for user_route in ['/user/', '/dashboard', '/printers', '/jobs']):
|
|
return "benutzer"
|
|
elif any(public_route in route for public_route in ['/guest', '/privacy', '/terms', '/imprint', '/']):
|
|
return "oeffentlich"
|
|
else:
|
|
return "benutzer" # Standard-Kategorie
|
|
|
|
def _sanitize_filename(self, route: str) -> str:
|
|
"""Bereinigt Routennamen für Dateinamen"""
|
|
import re
|
|
# Entferne gefährliche Zeichen
|
|
safe_name = re.sub(r'[<>:"/\\|?*]', '_', route)
|
|
# Ersetze Slashes mit Unterstrichen
|
|
safe_name = safe_name.replace('/', '_')
|
|
# Entferne führende/nachfolgende Unterstriche
|
|
safe_name = safe_name.strip('_')
|
|
# Leere Namen behandeln
|
|
if not safe_name or safe_name == '_':
|
|
safe_name = "home"
|
|
return safe_name
|
|
|
|
def _generate_report(self):
|
|
"""Generiert einen detaillierten Bericht über die Screenshot-Session"""
|
|
duration = self.results["end_time"] - self.results["start_time"]
|
|
|
|
report = {
|
|
"session_info": {
|
|
"start_time": self.results["start_time"].isoformat(),
|
|
"end_time": self.results["end_time"].isoformat(),
|
|
"duration_seconds": duration.total_seconds(),
|
|
"duration_formatted": str(duration)
|
|
},
|
|
"statistics": {
|
|
"total_screenshots": self.results["total_screenshots"],
|
|
"successful_screenshots": len(self.results["success"]),
|
|
"failed_screenshots": len(self.results["failed"]),
|
|
"skipped_routes": len(self.results["skipped"])
|
|
},
|
|
"results": self.results
|
|
}
|
|
|
|
# Bericht als JSON speichern
|
|
report_path = os.path.join(self.config.SCREENSHOT_BASE_DIR, "screenshot_report.json")
|
|
with open(report_path, 'w', encoding='utf-8') as f:
|
|
json.dump(report, f, indent=2, ensure_ascii=False, default=str)
|
|
|
|
# Menschenlesbaren Bericht erstellen
|
|
self._generate_human_readable_report(report)
|
|
|
|
logger.info(f"📊 Bericht gespeichert: {report_path}")
|
|
|
|
def _generate_human_readable_report(self, report: Dict):
|
|
"""Erstellt einen menschenlesbaren Bericht"""
|
|
report_path = os.path.join(self.config.SCREENSHOT_BASE_DIR, "screenshot_bericht.md")
|
|
|
|
with open(report_path, 'w', encoding='utf-8') as f:
|
|
f.write("# Screenshot-Bericht für Mitarbeiterschulungen\n\n")
|
|
f.write(f"**Erstellt am:** {datetime.now().strftime('%d.%m.%Y um %H:%M:%S')}\n\n")
|
|
|
|
# Zusammenfassung
|
|
f.write("## Zusammenfassung\n\n")
|
|
f.write(f"- **Gesamte Screenshots:** {report['statistics']['total_screenshots']}\n")
|
|
f.write(f"- **Erfolgreich:** {report['statistics']['successful_screenshots']}\n")
|
|
f.write(f"- **Fehlgeschlagen:** {report['statistics']['failed_screenshots']}\n")
|
|
f.write(f"- **Übersprungen:** {report['statistics']['skipped_routes']}\n")
|
|
f.write(f"- **Dauer:** {report['session_info']['duration_formatted']}\n\n")
|
|
|
|
# Erfolgreiche Screenshots
|
|
if self.results["success"]:
|
|
f.write("## Erfolgreich erstellte Screenshots\n\n")
|
|
for item in self.results["success"]:
|
|
f.write(f"- **{item['route']}** ({item['resolution']})\n")
|
|
f.write(f" - Datei: `{item['file_path']}`\n")
|
|
f.write(f" - Größe: {item['file_size']:,} Bytes\n\n")
|
|
|
|
# Fehlgeschlagene Screenshots
|
|
if self.results["failed"]:
|
|
f.write("## Fehlgeschlagene Screenshots\n\n")
|
|
for item in self.results["failed"]:
|
|
f.write(f"- **{item['route']}** ({item['resolution']})\n")
|
|
f.write(f" - Fehler: {item['error']}\n\n")
|
|
|
|
# Verwendungshinweise
|
|
f.write("## Verwendung für Schulungen\n\n")
|
|
f.write("### Ordnerstruktur\n\n")
|
|
f.write("```\n")
|
|
f.write("docs/schulung/screenshots/\n")
|
|
f.write("├── admin/ # Administrator-Bereich\n")
|
|
f.write("│ ├── desktop/ # Desktop-Auflösung (1920x1080)\n")
|
|
f.write("│ ├── tablet/ # Tablet-Auflösung (1024x768)\n")
|
|
f.write("│ └── mobile/ # Mobile-Auflösung (375x667)\n")
|
|
f.write("├── benutzer/ # Benutzer-Bereich\n")
|
|
f.write("│ ├── desktop/\n")
|
|
f.write("│ ├── tablet/\n")
|
|
f.write("│ └── mobile/\n")
|
|
f.write("└── oeffentlich/ # Öffentlicher Bereich\n")
|
|
f.write(" ├── desktop/\n")
|
|
f.write(" ├── tablet/\n")
|
|
f.write(" └── mobile/\n")
|
|
f.write("```\n\n")
|
|
|
|
f.write("### Empfehlungen für Präsentationen\n\n")
|
|
f.write("1. **Desktop-Screenshots** für Hauptpräsentationen verwenden\n")
|
|
f.write("2. **Mobile-Screenshots** für Responsive-Design-Demonstrationen\n")
|
|
f.write("3. **Admin-Ordner** für Administratoren-Schulungen\n")
|
|
f.write("4. **Benutzer-Ordner** für allgemeine Mitarbeiterschulungen\n")
|
|
f.write("5. **Öffentlich-Ordner** für Gäste-/Kunden-Präsentationen\n\n")
|
|
|
|
logger.info(f"📝 Menschenlesbarer Bericht gespeichert: {report_path}")
|
|
|
|
def load_config_from_file(config_file: str = "screenshot_config.json") -> ScreenshotConfiguration:
|
|
"""Lädt Konfiguration aus JSON-Datei"""
|
|
config = ScreenshotConfiguration()
|
|
|
|
if os.path.exists(config_file):
|
|
try:
|
|
with open(config_file, 'r', encoding='utf-8') as f:
|
|
config_data = json.load(f)
|
|
|
|
# Server-Konfiguration laden
|
|
if 'server' in config_data:
|
|
server_config = config_data['server']
|
|
config.BASE_URL = server_config.get('base_url', config.BASE_URL)
|
|
config.ADMIN_EMAIL = server_config.get('admin_email', config.ADMIN_EMAIL)
|
|
config.ADMIN_PASSWORD = server_config.get('admin_password', config.ADMIN_PASSWORD)
|
|
|
|
# Browser-Konfiguration laden
|
|
if 'browser' in config_data:
|
|
browser_config = config_data['browser']
|
|
config.BROWSER_TYPE = browser_config.get('type', config.BROWSER_TYPE)
|
|
config.HEADLESS = browser_config.get('headless', config.HEADLESS)
|
|
config.PAGE_LOAD_TIMEOUT = browser_config.get('page_load_timeout', config.PAGE_LOAD_TIMEOUT)
|
|
config.ELEMENT_WAIT_TIMEOUT = browser_config.get('element_wait_timeout', config.ELEMENT_WAIT_TIMEOUT)
|
|
config.SCREENSHOT_DELAY = browser_config.get('screenshot_delay', config.SCREENSHOT_DELAY)
|
|
|
|
# Output-Konfiguration laden
|
|
if 'output' in config_data:
|
|
output_config = config_data['output']
|
|
config.SCREENSHOT_BASE_DIR = output_config.get('base_directory', config.SCREENSHOT_BASE_DIR)
|
|
|
|
# Auflösungen laden
|
|
if 'resolutions' in config_data:
|
|
resolutions_config = config_data['resolutions']
|
|
new_resolutions = {}
|
|
for name, res_data in resolutions_config.items():
|
|
if isinstance(res_data, dict) and 'width' in res_data and 'height' in res_data:
|
|
new_resolutions[name] = (res_data['width'], res_data['height'])
|
|
if new_resolutions:
|
|
config.RESOLUTIONS = new_resolutions
|
|
|
|
logger.info(f"✅ Konfiguration aus {config_file} geladen")
|
|
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ Fehler beim Laden der Konfiguration aus {config_file}: {e}")
|
|
logger.info("💡 Verwende Standard-Konfiguration")
|
|
else:
|
|
logger.info(f"📋 Keine Konfigurationsdatei {config_file} gefunden - verwende Standard-Werte")
|
|
|
|
return config
|
|
|
|
def main():
|
|
"""Hauptfunktion des Screenshot-Tools"""
|
|
print("=" * 60)
|
|
print("🎯 AUTOMATISCHES SCREENSHOT-TOOL FÜR SCHULUNGEN")
|
|
print("=" * 60)
|
|
|
|
# Abhängigkeiten prüfen
|
|
if not SELENIUM_AVAILABLE:
|
|
print("❌ Selenium ist nicht installiert!")
|
|
print("💡 Führen Sie aus: pip install selenium")
|
|
return 1
|
|
|
|
# Konfiguration aus Datei laden
|
|
config = load_config_from_file()
|
|
|
|
# Benutzer-Eingaben für Konfiguration (optional)
|
|
print("\n📋 KONFIGURATION")
|
|
print("-" * 30)
|
|
|
|
base_url = input(f"Server-URL [{config.BASE_URL}]: ").strip()
|
|
if base_url:
|
|
config.BASE_URL = base_url
|
|
|
|
admin_email = input(f"Admin-Email [{config.ADMIN_EMAIL}]: ").strip()
|
|
if admin_email:
|
|
config.ADMIN_EMAIL = admin_email
|
|
|
|
admin_password = input(f"Admin-Passwort [{config.ADMIN_PASSWORD}]: ").strip()
|
|
if admin_password:
|
|
config.ADMIN_PASSWORD = admin_password
|
|
|
|
headless_input = input(f"Headless-Modus (ohne GUI) [{'Ja' if config.HEADLESS else 'Nein'}]: ").strip().lower()
|
|
if headless_input in ['nein', 'n', 'no', 'false']:
|
|
config.HEADLESS = False
|
|
elif headless_input in ['ja', 'j', 'yes', 'true']:
|
|
config.HEADLESS = True
|
|
|
|
print(f"\n✅ Konfiguration abgeschlossen")
|
|
print(f"📂 Screenshots werden gespeichert in: {config.SCREENSHOT_BASE_DIR}")
|
|
|
|
# Screenshot-Tool starten
|
|
tool = ScreenshotTool(config)
|
|
results = tool.run_screenshot_session()
|
|
|
|
# Ergebnisse anzeigen
|
|
print("\n" + "=" * 60)
|
|
print("📊 ERGEBNISSE")
|
|
print("=" * 60)
|
|
print(f"✅ Erfolgreich: {len(results['success'])} Screenshots")
|
|
print(f"❌ Fehlgeschlagen: {len(results['failed'])} Screenshots")
|
|
print(f"⏭️ Übersprungen: {len(results['skipped'])} Routen")
|
|
print(f"📸 Gesamt: {results['total_screenshots']} Screenshots")
|
|
|
|
if results["start_time"] and results["end_time"]:
|
|
duration = results["end_time"] - results["start_time"]
|
|
print(f"⏱️ Dauer: {duration}")
|
|
|
|
print(f"\n📁 Alle Screenshots verfügbar in:")
|
|
print(f" {os.path.abspath(config.SCREENSHOT_BASE_DIR)}")
|
|
|
|
return 0 if results["total_screenshots"] > 0 else 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |