🎉 Added new documentation files and scripts for Screenshot Tool 📚 in backend/

This commit is contained in:
Till Tomczak 2025-06-03 13:10:23 +02:00
parent cad2f4f7cc
commit 915a5d7ffe
5 changed files with 962 additions and 0 deletions

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,169 @@
# PowerShell-Skript für automatische Screenshots
# =============================================
param(
[string]$ConfigFile = "screenshot_config.json",
[switch]$Interactive = $false,
[switch]$QuickRun = $false,
[string]$ServerUrl = "",
[string]$OutputDir = ""
)
Write-Host "===============================================" -ForegroundColor Cyan
Write-Host "🎯 AUTOMATISCHES SCREENSHOT-TOOL FÜR SCHULUNGEN" -ForegroundColor Yellow
Write-Host "===============================================" -ForegroundColor Cyan
Write-Host ""
# Arbeitsverzeichnis zum Skript-Ordner ändern
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location $ScriptDir
# Prüfe ob Python verfügbar ist
try {
$pythonVersion = python --version 2>&1
Write-Host "✅ Python gefunden: $pythonVersion" -ForegroundColor Green
} catch {
Write-Host "❌ Python nicht gefunden!" -ForegroundColor Red
Write-Host "💡 Installieren Sie Python von https://python.org" -ForegroundColor Yellow
exit 1
}
# Prüfe ob Selenium installiert ist
$seleniumCheck = python -c "import selenium; print('✅ Selenium verfügbar')" 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "⚠️ Selenium nicht installiert. Installiere jetzt..." -ForegroundColor Yellow
pip install selenium webdriver-manager
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Selenium-Installation fehlgeschlagen!" -ForegroundColor Red
exit 1
}
Write-Host "✅ Selenium erfolgreich installiert" -ForegroundColor Green
} else {
Write-Host $seleniumCheck -ForegroundColor Green
}
# Prüfe ChromeDriver
$chromeCheck = python -c "from selenium import webdriver; from selenium.webdriver.chrome.service import Service; print('✅ Chrome WebDriver verfügbar')" 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "⚠️ ChromeDriver nicht gefunden. Installiere webdriver-manager..." -ForegroundColor Yellow
pip install webdriver-manager
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ WebDriver-Manager-Installation fehlgeschlagen!" -ForegroundColor Red
Write-Host "💡 Manuell ChromeDriver von https://chromedriver.chromium.org/ herunterladen" -ForegroundColor Yellow
} else {
Write-Host "✅ WebDriver-Manager installiert" -ForegroundColor Green
}
}
# Flask-App verfügbarkeit prüfen
Write-Host ""
Write-Host "🔍 Prüfe Flask-App..." -ForegroundColor Blue
$flaskCheck = python -c "import sys; sys.path.append('..'); from app import app; print('✅ Flask-App verfügbar')" 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "⚠️ Flask-App nicht direkt verfügbar" -ForegroundColor Yellow
Write-Host "📋 Das Tool wird mit Standard-Routen arbeiten" -ForegroundColor Blue
} else {
Write-Host $flaskCheck -ForegroundColor Green
}
Write-Host ""
Write-Host "🚀 STARTE SCREENSHOT-TOOL" -ForegroundColor Green
Write-Host "=" * 30 -ForegroundColor Green
# Parameter für das Python-Skript vorbereiten
$pythonArgs = @("screenshot_tool.py")
if ($QuickRun) {
Write-Host "⚡ Quick-Run-Modus aktiviert" -ForegroundColor Yellow
# Setze Umgebungsvariablen für automatische Konfiguration
$env:SCREENSHOT_AUTO_MODE = "true"
$env:SCREENSHOT_HEADLESS = "true"
if ($ServerUrl) {
$env:SCREENSHOT_SERVER_URL = $ServerUrl
}
if ($OutputDir) {
$env:SCREENSHOT_OUTPUT_DIR = $OutputDir
}
}
# Tool ausführen
try {
if ($Interactive) {
Write-Host "🎛️ Interaktiver Modus - Folgen Sie den Anweisungen" -ForegroundColor Blue
python @pythonArgs
} else {
Write-Host "🤖 Automatischer Modus" -ForegroundColor Blue
python @pythonArgs
}
$exitCode = $LASTEXITCODE
if ($exitCode -eq 0) {
Write-Host ""
Write-Host "🎉 SCREENSHOT-ERSTELLUNG ERFOLGREICH ABGESCHLOSSEN!" -ForegroundColor Green
Write-Host ""
# Zeige Ausgabe-Ordner an
$outputPath = "docs\schulung\screenshots"
if (Test-Path $outputPath) {
Write-Host "📁 Screenshots verfügbar in:" -ForegroundColor Blue
Write-Host " $(Resolve-Path $outputPath)" -ForegroundColor White
# Frage ob Ordner geöffnet werden soll
$openFolder = Read-Host "📂 Möchten Sie den Screenshot-Ordner öffnen? (j/n)"
if ($openFolder -eq "j" -or $openFolder -eq "ja" -or $openFolder -eq "y" -or $openFolder -eq "yes") {
Start-Process "explorer.exe" -ArgumentList (Resolve-Path $outputPath)
}
}
# Zeige Bericht an
$reportPath = "$outputPath\screenshot_bericht.md"
if (Test-Path $reportPath) {
Write-Host ""
Write-Host "📊 Detaillierter Bericht verfügbar:" -ForegroundColor Blue
Write-Host " $(Resolve-Path $reportPath)" -ForegroundColor White
$openReport = Read-Host "📖 Möchten Sie den Bericht öffnen? (j/n)"
if ($openReport -eq "j" -or $openReport -eq "ja" -or $openReport -eq "y" -or $openReport -eq "yes") {
Start-Process "notepad.exe" -ArgumentList (Resolve-Path $reportPath)
}
}
} else {
Write-Host ""
Write-Host "❌ FEHLER BEI DER SCREENSHOT-ERSTELLUNG" -ForegroundColor Red
Write-Host "📋 Überprüfen Sie die Logs für Details" -ForegroundColor Yellow
}
} catch {
Write-Host ""
Write-Host "❌ UNERWARTETER FEHLER: $($_.Exception.Message)" -ForegroundColor Red
$exitCode = 1
}
Write-Host ""
Write-Host "💡 VERWENDUNGSHINWEISE FÜR SCHULUNGEN:" -ForegroundColor Cyan
Write-Host "=" * 40 -ForegroundColor Cyan
Write-Host "• Admin-Screenshots: docs\schulung\screenshots\admin\" -ForegroundColor White
Write-Host "• Benutzer-Screenshots: docs\schulung\screenshots\benutzer\" -ForegroundColor White
Write-Host "• Öffentliche Screenshots: docs\schulung\screenshots\oeffentlich\" -ForegroundColor White
Write-Host "• Verschiedene Auflösungen in Unterordnern verfügbar" -ForegroundColor White
Write-Host "• Perfekt für PowerPoint-Präsentationen geeignet" -ForegroundColor White
Write-Host ""
Write-Host "🎓 TIPPS FÜR PRÄSENTATIONEN:" -ForegroundColor Yellow
Write-Host "• Desktop-Screenshots für Hauptpräsentationen verwenden" -ForegroundColor White
Write-Host "• Mobile-Screenshots für Responsive-Design zeigen" -ForegroundColor White
Write-Host "• Admin-Ordner für Administrator-Schulungen" -ForegroundColor White
Write-Host "• Benutzer-Ordner für allgemeine Mitarbeiterschulungen" -ForegroundColor White
Write-Host ""
Write-Host "📞 Bei Problemen:" -ForegroundColor Magenta
Write-Host "• Log-Datei prüfen: screenshot_tool.log" -ForegroundColor White
Write-Host "• Server läuft auf korrekter URL?" -ForegroundColor White
Write-Host "• Admin-Zugangsdaten korrekt?" -ForegroundColor White
Write-Host "• ChromeDriver installiert?" -ForegroundColor White
exit $exitCode

View File

@ -0,0 +1,113 @@
{
"server": {
"base_url": "http://localhost:5000",
"admin_email": "admin@example.com",
"admin_password": "admin123"
},
"browser": {
"type": "chrome",
"headless": true,
"page_load_timeout": 15,
"element_wait_timeout": 10,
"screenshot_delay": 2
},
"output": {
"base_directory": "docs/schulung/screenshots",
"include_timestamp": true,
"create_subdirectories": true
},
"resolutions": {
"desktop": {
"width": 1920,
"height": 1080,
"description": "Standard Desktop-Auflösung"
},
"tablet": {
"width": 1024,
"height": 768,
"description": "Tablet-Auflösung"
},
"mobile": {
"width": 375,
"height": 667,
"description": "Mobile-Auflösung (iPhone)"
},
"large_desktop": {
"width": 2560,
"height": 1440,
"description": "Große Desktop-Auflösung für 4K-Displays"
}
},
"routes": {
"include_patterns": [
"/",
"/dashboard",
"/admin*",
"/user/*",
"/printers*",
"/jobs*",
"/stats*",
"/reports*",
"/maintenance*",
"/guest*"
],
"exclude_patterns": [
"/api/*",
"/auth/api/*",
"/static/*",
"*/favicon.ico",
"*/robots.txt",
"*/sitemap.xml"
],
"custom_routes": [
{
"url": "/demo",
"category": "benutzer",
"description": "Komponenten-Demo-Seite"
},
{
"url": "/socket-test",
"category": "admin",
"description": "WebSocket-Test-Seite"
}
]
},
"categories": {
"admin": {
"patterns": ["/admin", "/admin-dashboard", "/admin/*"],
"description": "Administrator-Bereich"
},
"benutzer": {
"patterns": ["/user/*", "/dashboard", "/printers", "/jobs", "/stats"],
"description": "Allgemeiner Benutzerbereich"
},
"oeffentlich": {
"patterns": ["/", "/guest", "/privacy", "/terms", "/imprint", "/legal"],
"description": "Öffentlich zugängliche Seiten"
},
"berichte": {
"patterns": ["/reports", "/stats"],
"description": "Berichte und Statistiken"
},
"wartung": {
"patterns": ["/maintenance"],
"description": "Wartung und System-Tools"
}
},
"advanced": {
"take_full_page_screenshots": true,
"wait_for_dynamic_content": true,
"capture_hover_states": false,
"include_browser_ui": false,
"compress_images": false,
"generate_thumbnails": true,
"create_comparison_report": false
},
"schulung": {
"create_presentation_slides": true,
"generate_step_by_step_guide": true,
"create_feature_overview": true,
"include_annotations": false,
"language": "de"
}
}

View File

@ -0,0 +1,678 @@
#!/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:
route = self._resolve_dynamic_route(route)
if route:
discovered_routes.extend(route if isinstance(route, list) else [route])
else:
discovered_routes.append(route)
# Kategorisierung der Routen
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 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 erstellen
config = ScreenshotConfiguration()
# Benutzer-Eingaben für Konfiguration
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())

View File

@ -0,0 +1 @@