🎉 3 Änderungen an Tools-Modul vorgenommen:
- backend/form_test_automator.py -> backend/tools/analysis/form_test_automator.py - backend/simple_form_tester.py -> backend/tools/analysis/simple_form_tester.py - backend/template_analysis_tool.py -> backend/tools/analysis/template_analysis_tool.py - backend
This commit is contained in:
2493
backend/tools/analysis/form_test_automator.py
Normal file
2493
backend/tools/analysis/form_test_automator.py
Normal file
File diff suppressed because it is too large
Load Diff
635
backend/tools/analysis/simple_form_tester.py
Normal file
635
backend/tools/analysis/simple_form_tester.py
Normal file
@ -0,0 +1,635 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Vereinfachter HTML-Formular Tester für MYP System
|
||||
===============================================
|
||||
|
||||
Ein schlanker Formular-Tester ohne externe Dependencies,
|
||||
der grundlegende Formular-Validierung mit Standard-Python-Libraries testet.
|
||||
|
||||
Autor: Till Tomczak
|
||||
Mercedes-Benz Projektarbeit MYP
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
from urllib.parse import urljoin, urlparse
|
||||
from html.parser import HTMLParser
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List, Optional
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
@dataclass
|
||||
class FormField:
|
||||
"""Repräsentiert ein Formular-Feld"""
|
||||
name: str
|
||||
field_type: str
|
||||
required: bool = False
|
||||
pattern: str = ""
|
||||
placeholder: str = ""
|
||||
value: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Form:
|
||||
"""Repräsentiert ein HTML-Formular"""
|
||||
action: str
|
||||
method: str
|
||||
fields: List[FormField]
|
||||
name: str = ""
|
||||
|
||||
|
||||
class FormParser(HTMLParser):
|
||||
"""Parst HTML und extrahiert Formular-Informationen"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.forms = []
|
||||
self.current_form = None
|
||||
self.in_form = False
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
attrs_dict = dict(attrs)
|
||||
|
||||
if tag == 'form':
|
||||
self.in_form = True
|
||||
self.current_form = Form(
|
||||
action=attrs_dict.get('action', ''),
|
||||
method=attrs_dict.get('method', 'GET').upper(),
|
||||
fields=[],
|
||||
name=attrs_dict.get('name', attrs_dict.get('id', f'form_{len(self.forms)}'))
|
||||
)
|
||||
|
||||
elif self.in_form and tag in ['input', 'textarea', 'select']:
|
||||
field = FormField(
|
||||
name=attrs_dict.get('name', attrs_dict.get('id', f'field_{len(self.current_form.fields)}')),
|
||||
field_type=attrs_dict.get('type', tag),
|
||||
required='required' in attrs_dict,
|
||||
pattern=attrs_dict.get('pattern', ''),
|
||||
placeholder=attrs_dict.get('placeholder', ''),
|
||||
value=attrs_dict.get('value', '')
|
||||
)
|
||||
self.current_form.fields.append(field)
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag == 'form' and self.in_form:
|
||||
self.in_form = False
|
||||
if self.current_form:
|
||||
self.forms.append(self.current_form)
|
||||
self.current_form = None
|
||||
|
||||
|
||||
class SimpleFormTester:
|
||||
"""
|
||||
Vereinfachter Formular-Tester ohne Browser-Dependencies.
|
||||
Führt HTTP-basierte Tests durch.
|
||||
"""
|
||||
|
||||
def __init__(self, base_url: str):
|
||||
self.base_url = base_url.rstrip('/')
|
||||
self.session_cookies = {}
|
||||
|
||||
def fetch_page(self, path: str) -> str:
|
||||
"""Lädt eine Seite und gibt den HTML-Inhalt zurück"""
|
||||
url = urljoin(self.base_url, path)
|
||||
|
||||
try:
|
||||
request = urllib.request.Request(url)
|
||||
|
||||
# Cookies hinzufügen
|
||||
if self.session_cookies:
|
||||
cookie_header = '; '.join([f'{k}={v}' for k, v in self.session_cookies.items()])
|
||||
request.add_header('Cookie', cookie_header)
|
||||
|
||||
with urllib.request.urlopen(request) as response:
|
||||
html = response.read().decode('utf-8')
|
||||
|
||||
# Cookies aus Response extrahieren
|
||||
cookie_header = response.getheader('Set-Cookie')
|
||||
if cookie_header:
|
||||
self._parse_cookies(cookie_header)
|
||||
|
||||
return html
|
||||
|
||||
except urllib.error.URLError as e:
|
||||
print(f"❌ Fehler beim Laden von {url}: {e}")
|
||||
return ""
|
||||
|
||||
def _parse_cookies(self, cookie_header: str):
|
||||
"""Parst Set-Cookie Header"""
|
||||
for cookie in cookie_header.split(','):
|
||||
if '=' in cookie:
|
||||
name, value = cookie.split('=', 1)
|
||||
self.session_cookies[name.strip()] = value.split(';')[0].strip()
|
||||
|
||||
def find_forms(self, html: str) -> List[Form]:
|
||||
"""Findet alle Formulare in HTML"""
|
||||
parser = FormParser()
|
||||
parser.feed(html)
|
||||
return parser.forms
|
||||
|
||||
def generate_test_data(self, field: FormField) -> str:
|
||||
"""Generiert Test-Daten für ein Feld"""
|
||||
|
||||
if field.field_type == 'email':
|
||||
return "test@mercedes-benz.com"
|
||||
elif field.field_type == 'password':
|
||||
return "TestPassword123!"
|
||||
elif field.field_type == 'tel':
|
||||
return "+49 711 17-0"
|
||||
elif field.field_type == 'url':
|
||||
return "https://www.mercedes-benz.com"
|
||||
elif field.field_type == 'number':
|
||||
return "42"
|
||||
elif field.field_type == 'date':
|
||||
return "2024-06-18"
|
||||
elif field.field_type == 'checkbox':
|
||||
return "on"
|
||||
elif field.field_type == 'radio':
|
||||
return field.value or "option1"
|
||||
elif 'name' in field.name.lower():
|
||||
return "Test Benutzer"
|
||||
elif 'username' in field.name.lower():
|
||||
return "admin"
|
||||
elif 'ip' in field.name.lower():
|
||||
return "192.168.1.100"
|
||||
elif 'port' in field.name.lower():
|
||||
return "80"
|
||||
else:
|
||||
return f"Test_{field.name}"
|
||||
|
||||
def submit_form(self, form: Form, test_data: Dict[str, str], base_url: str) -> Dict:
|
||||
"""Sendet Formular-Daten und gibt Response zurück"""
|
||||
|
||||
# Action URL bestimmen
|
||||
if form.action.startswith('http'):
|
||||
url = form.action
|
||||
else:
|
||||
url = urljoin(base_url, form.action)
|
||||
|
||||
# Daten vorbereiten
|
||||
form_data = {}
|
||||
for field in form.fields:
|
||||
if field.name in test_data:
|
||||
form_data[field.name] = test_data[field.name]
|
||||
elif field.field_type not in ['submit', 'button']:
|
||||
form_data[field.name] = self.generate_test_data(field)
|
||||
|
||||
try:
|
||||
if form.method == 'GET':
|
||||
# GET-Request mit Query-Parametern
|
||||
query_string = urllib.parse.urlencode(form_data)
|
||||
full_url = f"{url}?{query_string}"
|
||||
request = urllib.request.Request(full_url)
|
||||
else:
|
||||
# POST-Request
|
||||
data = urllib.parse.urlencode(form_data).encode('utf-8')
|
||||
request = urllib.request.Request(url, data=data)
|
||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||
|
||||
# Cookies hinzufügen
|
||||
if self.session_cookies:
|
||||
cookie_header = '; '.join([f'{k}={v}' for k, v in self.session_cookies.items()])
|
||||
request.add_header('Cookie', cookie_header)
|
||||
|
||||
with urllib.request.urlopen(request) as response:
|
||||
response_html = response.read().decode('utf-8')
|
||||
status_code = response.getcode()
|
||||
|
||||
return {
|
||||
'success': 200 <= status_code < 400,
|
||||
'status_code': status_code,
|
||||
'html': response_html,
|
||||
'form_data': form_data
|
||||
}
|
||||
|
||||
except urllib.error.HTTPError as e:
|
||||
return {
|
||||
'success': False,
|
||||
'status_code': e.code,
|
||||
'error': str(e),
|
||||
'form_data': form_data
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'form_data': form_data
|
||||
}
|
||||
|
||||
def validate_field(self, field: FormField, value: str) -> Dict:
|
||||
"""Validiert ein Feld mit gegebenem Wert"""
|
||||
|
||||
errors = []
|
||||
|
||||
# Required-Validierung
|
||||
if field.required and not value.strip():
|
||||
errors.append(f"Feld '{field.name}' ist erforderlich")
|
||||
|
||||
# Pattern-Validierung
|
||||
if field.pattern and value:
|
||||
try:
|
||||
if not re.match(field.pattern, value):
|
||||
errors.append(f"Feld '{field.name}' entspricht nicht dem Pattern '{field.pattern}'")
|
||||
except re.error:
|
||||
errors.append(f"Ungültiges Pattern für Feld '{field.name}': {field.pattern}")
|
||||
|
||||
# Type-spezifische Validierung
|
||||
if value and field.field_type == 'email':
|
||||
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
if not re.match(email_pattern, value):
|
||||
errors.append(f"Ungültige Email-Adresse: {value}")
|
||||
|
||||
elif value and field.field_type == 'url':
|
||||
try:
|
||||
parsed = urlparse(value)
|
||||
if not parsed.scheme or not parsed.netloc:
|
||||
errors.append(f"Ungültige URL: {value}")
|
||||
except:
|
||||
errors.append(f"Ungültige URL: {value}")
|
||||
|
||||
elif value and field.field_type == 'number':
|
||||
try:
|
||||
float(value)
|
||||
except ValueError:
|
||||
errors.append(f"'{value}' ist keine gültige Zahl")
|
||||
|
||||
return {
|
||||
'valid': len(errors) == 0,
|
||||
'errors': errors,
|
||||
'field': field.name,
|
||||
'value': value
|
||||
}
|
||||
|
||||
def test_form_validation(self, form: Form) -> List[Dict]:
|
||||
"""Testet Formular-Validierung mit verschiedenen Daten"""
|
||||
|
||||
validation_results = []
|
||||
|
||||
# 1. Test mit leeren Required-Feldern
|
||||
for field in form.fields:
|
||||
if field.required:
|
||||
result = self.validate_field(field, "")
|
||||
validation_results.append({
|
||||
'test_type': 'required_field_empty',
|
||||
'field': field.name,
|
||||
'expected_invalid': True,
|
||||
'actual_valid': result['valid'],
|
||||
'passed': not result['valid'], # Sollte ungültig sein
|
||||
'errors': result['errors']
|
||||
})
|
||||
|
||||
# 2. Test mit ungültigen Email-Adressen
|
||||
for field in form.fields:
|
||||
if field.field_type == 'email':
|
||||
invalid_emails = ['invalid-email', 'test@', '@domain.com', 'test..test@domain.com']
|
||||
for invalid_email in invalid_emails:
|
||||
result = self.validate_field(field, invalid_email)
|
||||
validation_results.append({
|
||||
'test_type': 'invalid_email',
|
||||
'field': field.name,
|
||||
'value': invalid_email,
|
||||
'expected_invalid': True,
|
||||
'actual_valid': result['valid'],
|
||||
'passed': not result['valid'],
|
||||
'errors': result['errors']
|
||||
})
|
||||
|
||||
# 3. Test mit gültigen Daten
|
||||
valid_test_data = {}
|
||||
for field in form.fields:
|
||||
if field.field_type not in ['submit', 'button']:
|
||||
valid_test_data[field.name] = self.generate_test_data(field)
|
||||
|
||||
for field in form.fields:
|
||||
if field.name in valid_test_data:
|
||||
result = self.validate_field(field, valid_test_data[field.name])
|
||||
validation_results.append({
|
||||
'test_type': 'valid_data',
|
||||
'field': field.name,
|
||||
'value': valid_test_data[field.name],
|
||||
'expected_invalid': False,
|
||||
'actual_valid': result['valid'],
|
||||
'passed': result['valid'],
|
||||
'errors': result['errors']
|
||||
})
|
||||
|
||||
return validation_results
|
||||
|
||||
def test_form_submission(self, form: Form, base_url: str) -> Dict:
|
||||
"""Testet Formular-Submission"""
|
||||
|
||||
print(f"🧪 Teste Formular: {form.name} ({form.method} → {form.action})")
|
||||
|
||||
# Test-Daten generieren
|
||||
test_data = {}
|
||||
for field in form.fields:
|
||||
if field.field_type not in ['submit', 'button']:
|
||||
test_data[field.name] = self.generate_test_data(field)
|
||||
|
||||
print(f" 📝 Test-Daten: {list(test_data.keys())}")
|
||||
|
||||
# Formular senden
|
||||
start_time = time.time()
|
||||
result = self.submit_form(form, test_data, base_url)
|
||||
execution_time = time.time() - start_time
|
||||
|
||||
# Validierungs-Tests
|
||||
validation_results = self.test_form_validation(form)
|
||||
|
||||
return {
|
||||
'form_name': form.name,
|
||||
'method': form.method,
|
||||
'action': form.action,
|
||||
'fields_count': len(form.fields),
|
||||
'test_data': test_data,
|
||||
'submission_result': result,
|
||||
'validation_results': validation_results,
|
||||
'execution_time': execution_time,
|
||||
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
def test_page_forms(self, path: str) -> List[Dict]:
|
||||
"""Testet alle Formulare auf einer Seite"""
|
||||
|
||||
print(f"🔍 Scanne Formulare auf: {self.base_url}{path}")
|
||||
|
||||
# Seite laden
|
||||
html = self.fetch_page(path)
|
||||
if not html:
|
||||
return []
|
||||
|
||||
# Formulare finden
|
||||
forms = self.find_forms(html)
|
||||
print(f"✅ {len(forms)} Formulare gefunden")
|
||||
|
||||
# Jedes Formular testen
|
||||
results = []
|
||||
for form in forms:
|
||||
result = self.test_form_submission(form, self.base_url + path)
|
||||
results.append(result)
|
||||
|
||||
return results
|
||||
|
||||
def generate_report(self, test_results: List[Dict], output_file: str = "simple_form_test_report.html"):
|
||||
"""Generiert einen einfachen HTML-Report"""
|
||||
|
||||
total_forms = len(test_results)
|
||||
successful_submissions = len([r for r in test_results if r['submission_result'].get('success', False)])
|
||||
|
||||
# Validierungs-Statistiken
|
||||
total_validations = sum(len(r['validation_results']) for r in test_results)
|
||||
passed_validations = sum(len([v for v in r['validation_results'] if v['passed']]) for r in test_results)
|
||||
|
||||
html_report = f"""<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MYP Formular Test Report</title>
|
||||
<style>
|
||||
body {{
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}}
|
||||
.container {{
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}}
|
||||
.header {{
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
margin: -20px -20px 20px -20px;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}}
|
||||
.stats {{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
}}
|
||||
.stat-card {{
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
border: 1px solid #dee2e6;
|
||||
}}
|
||||
.stat-number {{
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}}
|
||||
.form-result {{
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}}
|
||||
.form-header {{
|
||||
background: #e9ecef;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}}
|
||||
.form-content {{
|
||||
padding: 15px;
|
||||
}}
|
||||
.success {{ color: #28a745; }}
|
||||
.failure {{ color: #dc3545; }}
|
||||
.warning {{ color: #ffc107; }}
|
||||
.test-data {{
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
}}
|
||||
.validation-result {{
|
||||
margin: 5px 0;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #dee2e6;
|
||||
}}
|
||||
.validation-passed {{
|
||||
background: #d4edda;
|
||||
border-left-color: #28a745;
|
||||
}}
|
||||
.validation-failed {{
|
||||
background: #f8d7da;
|
||||
border-left-color: #dc3545;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🧪 MYP Formular Test Report</h1>
|
||||
<p>Generiert am {time.strftime('%d.%m.%Y um %H:%M:%S')}</p>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{total_forms}</div>
|
||||
<div>Formulare getestet</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number {'success' if successful_submissions == total_forms else 'warning' if successful_submissions > 0 else 'failure'}">{successful_submissions}</div>
|
||||
<div>Erfolgreiche Submissions</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{total_validations}</div>
|
||||
<div>Validierungs-Tests</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number {'success' if passed_validations == total_validations else 'warning' if passed_validations > 0 else 'failure'}">{passed_validations}</div>
|
||||
<div>Validierungen bestanden</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>📋 Detaillierte Ergebnisse</h2>
|
||||
"""
|
||||
|
||||
# Formular-spezifische Ergebnisse
|
||||
for result in test_results:
|
||||
submission = result['submission_result']
|
||||
success_class = 'success' if submission.get('success', False) else 'failure'
|
||||
|
||||
html_report += f"""
|
||||
<div class="form-result">
|
||||
<div class="form-header">
|
||||
<h3>📝 {result['form_name']}</h3>
|
||||
<p><strong>Method:</strong> {result['method']} | <strong>Action:</strong> {result['action']} | <strong>Felder:</strong> {result['fields_count']}</p>
|
||||
</div>
|
||||
<div class="form-content">
|
||||
<h4>Submission-Ergebnis: <span class="{success_class}">{'✅ Erfolgreich' if submission.get('success', False) else '❌ Fehlgeschlagen'}</span></h4>
|
||||
|
||||
{f'<p><strong>Status Code:</strong> {submission.get("status_code", "N/A")}</p>' if 'status_code' in submission else ''}
|
||||
{f'<p><strong>Fehler:</strong> {submission.get("error", "N/A")}</p>' if 'error' in submission else ''}
|
||||
|
||||
<h4>Test-Daten:</h4>
|
||||
<div class="test-data">{json.dumps(result['test_data'], indent=2, ensure_ascii=False)}</div>
|
||||
|
||||
<h4>Validierungs-Ergebnisse:</h4>
|
||||
"""
|
||||
|
||||
# Validierungs-Ergebnisse
|
||||
for validation in result['validation_results']:
|
||||
validation_class = 'validation-passed' if validation['passed'] else 'validation-failed'
|
||||
status_icon = '✅' if validation['passed'] else '❌'
|
||||
|
||||
html_report += f"""
|
||||
<div class="validation-result {validation_class}">
|
||||
<strong>{status_icon} {validation['test_type']}</strong> - Feld: {validation['field']}
|
||||
{f"<br>Wert: <code>{validation.get('value', 'N/A')}</code>" if 'value' in validation else ''}
|
||||
{f"<br>Fehler: {', '.join(validation['errors'])}" if validation['errors'] else ''}
|
||||
</div>
|
||||
"""
|
||||
|
||||
html_report += f"""
|
||||
<p><small>⏱️ Ausführungszeit: {result['execution_time']:.2f}s</small></p>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
html_report += """
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
try:
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(html_report)
|
||||
print(f"📊 Report erstellt: {output_file}")
|
||||
return output_file
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Report-Erstellen: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
"""Haupt-CLI-Interface"""
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("""
|
||||
🧪 Vereinfachter MYP Formular Tester
|
||||
|
||||
Verwendung:
|
||||
python simple_form_tester.py <base_url> [path]
|
||||
|
||||
Beispiele:
|
||||
python simple_form_tester.py http://localhost:5000
|
||||
python simple_form_tester.py http://localhost:5000 /login
|
||||
python simple_form_tester.py http://localhost:5000 /admin/add_printer
|
||||
|
||||
Testet alle Formulare auf der angegebenen Seite und generiert einen HTML-Report.
|
||||
""")
|
||||
return
|
||||
|
||||
base_url = sys.argv[1]
|
||||
path = sys.argv[2] if len(sys.argv) > 2 else '/'
|
||||
|
||||
print(f"""
|
||||
{'='*60}
|
||||
🧪 MYP FORMULAR TESTER (Vereinfacht)
|
||||
{'='*60}
|
||||
|
||||
🎯 Basis-URL: {base_url}
|
||||
📍 Pfad: {path}
|
||||
🕒 Gestartet: {time.strftime('%d.%m.%Y um %H:%M:%S')}
|
||||
|
||||
{'='*60}
|
||||
""")
|
||||
|
||||
# Tester initialisieren
|
||||
tester = SimpleFormTester(base_url)
|
||||
|
||||
# Tests ausführen
|
||||
try:
|
||||
results = tester.test_page_forms(path)
|
||||
|
||||
if results:
|
||||
# Report generieren
|
||||
report_file = tester.generate_report(results)
|
||||
|
||||
# Zusammenfassung
|
||||
total_forms = len(results)
|
||||
successful = len([r for r in results if r['submission_result'].get('success', False)])
|
||||
|
||||
print(f"""
|
||||
{'='*60}
|
||||
🎉 TESTS ABGESCHLOSSEN
|
||||
{'='*60}
|
||||
|
||||
📊 ZUSAMMENFASSUNG:
|
||||
• Formulare getestet: {total_forms}
|
||||
• Erfolgreiche Submissions: {successful}
|
||||
• Erfolgsrate: {successful/total_forms*100:.1f}%
|
||||
|
||||
📋 Report: {report_file}
|
||||
|
||||
{'='*60}
|
||||
""")
|
||||
else:
|
||||
print("⚠️ Keine Formulare gefunden oder Fehler beim Laden der Seite")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Testen: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
245
backend/tools/analysis/template_analysis_tool.py
Normal file
245
backend/tools/analysis/template_analysis_tool.py
Normal file
@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Intelligentes Template-Analyse-Tool für MYP Admin Panel
|
||||
Systematische Identifikation und Behebung aller Template-Probleme
|
||||
|
||||
Autor: MYP Team - Till Tomczak
|
||||
Datum: 2025-06-19
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
import json
|
||||
from typing import Dict, List, Set, Tuple
|
||||
from pathlib import Path
|
||||
|
||||
class TemplateAnalyzer:
|
||||
"""
|
||||
Intelligenter Template-Analyzer für systematische Problemerkennung
|
||||
"""
|
||||
|
||||
def __init__(self, template_path: str, blueprint_path: str):
|
||||
self.template_path = template_path
|
||||
self.blueprint_path = blueprint_path
|
||||
self.template_endpoints: Set[str] = set()
|
||||
self.available_endpoints: Set[str] = set()
|
||||
self.endpoint_mapping: Dict[str, str] = {}
|
||||
self.problems: List[Dict] = []
|
||||
self.corrections: List[Dict] = []
|
||||
|
||||
def analyze_template_endpoints(self) -> Set[str]:
|
||||
"""
|
||||
1. Jinja2 Syntax-Analyse: Extrahiere alle url_for() Aufrufe
|
||||
"""
|
||||
print("🔍 PHASE 1: Template Endpoint-Analyse")
|
||||
|
||||
with open(self.template_path, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
# Regex für url_for() Aufrufe in Jinja2-Templates
|
||||
url_for_pattern = r"{{\s*url_for\(['\"]([^'\"]+)['\"][^}]*\)\s*}}"
|
||||
matches = re.findall(url_for_pattern, template_content)
|
||||
|
||||
self.template_endpoints = set(matches)
|
||||
|
||||
print(f" ✓ Gefundene Template-Endpoints: {len(self.template_endpoints)}")
|
||||
for endpoint in sorted(self.template_endpoints):
|
||||
print(f" - {endpoint}")
|
||||
|
||||
return self.template_endpoints
|
||||
|
||||
def analyze_blueprint_routes(self) -> Set[str]:
|
||||
"""
|
||||
2. Backend Blueprint-Analyse: Scanne alle registrierten Routen
|
||||
"""
|
||||
print("\n🔍 PHASE 2: Blueprint Route-Analyse")
|
||||
|
||||
with open(self.blueprint_path, 'r', encoding='utf-8') as f:
|
||||
blueprint_content = f.read()
|
||||
|
||||
# Regex für Blueprint-Routen
|
||||
route_pattern = r'@admin_blueprint\.route\(["\']([^"\']+)["\'][^)]*\)\s*@admin_required\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)'
|
||||
matches = re.findall(route_pattern, blueprint_content, re.MULTILINE | re.DOTALL)
|
||||
|
||||
# Erstelle verfügbare Endpoints mit admin.-Prefix
|
||||
for route_path, function_name in matches:
|
||||
endpoint = f"admin.{function_name}"
|
||||
self.available_endpoints.add(endpoint)
|
||||
self.endpoint_mapping[function_name] = endpoint
|
||||
|
||||
# Zusätzlich: API-Blueprint-Routen
|
||||
api_route_pattern = r'@admin_api_blueprint\.route\(["\']([^"\']+)["\'][^)]*\)\s*@admin_required\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)'
|
||||
api_matches = re.findall(api_route_pattern, blueprint_content, re.MULTILINE | re.DOTALL)
|
||||
|
||||
for route_path, function_name in api_matches:
|
||||
endpoint = f"admin_api.{function_name}"
|
||||
self.available_endpoints.add(endpoint)
|
||||
self.endpoint_mapping[function_name] = endpoint
|
||||
|
||||
print(f" ✓ Verfügbare Blueprint-Endpoints: {len(self.available_endpoints)}")
|
||||
for endpoint in sorted(self.available_endpoints):
|
||||
print(f" - {endpoint}")
|
||||
|
||||
return self.available_endpoints
|
||||
|
||||
def cross_reference_validation(self) -> List[Dict]:
|
||||
"""
|
||||
3. Cross-Reference Validierung: Vergleiche Template vs. Blueprint
|
||||
"""
|
||||
print("\n🔍 PHASE 3: Cross-Reference Validierung")
|
||||
|
||||
# Finde fehlende oder falsche Referenzen
|
||||
missing_endpoints = self.template_endpoints - self.available_endpoints
|
||||
unused_endpoints = self.available_endpoints - self.template_endpoints
|
||||
|
||||
for endpoint in missing_endpoints:
|
||||
problem = {
|
||||
'type': 'missing_endpoint',
|
||||
'endpoint': endpoint,
|
||||
'description': f'Template referenziert nicht existierenden Endpoint: {endpoint}',
|
||||
'severity': 'critical'
|
||||
}
|
||||
|
||||
# Versuche ähnliche Endpoints zu finden
|
||||
suggestions = self._find_similar_endpoints(endpoint)
|
||||
if suggestions:
|
||||
problem['suggestions'] = suggestions
|
||||
|
||||
self.problems.append(problem)
|
||||
|
||||
print(f" ❌ Problematische Endpoints: {len(missing_endpoints)}")
|
||||
for endpoint in missing_endpoints:
|
||||
print(f" - {endpoint}")
|
||||
|
||||
print(f" ⚠️ Ungenutzte Endpoints: {len(unused_endpoints)}")
|
||||
for endpoint in unused_endpoints:
|
||||
print(f" - {endpoint}")
|
||||
|
||||
return self.problems
|
||||
|
||||
def _find_similar_endpoints(self, target: str) -> List[str]:
|
||||
"""
|
||||
Finde ähnliche Endpoints basierend auf String-Ähnlichkeit
|
||||
"""
|
||||
suggestions = []
|
||||
target_parts = target.split('.')
|
||||
|
||||
for available in self.available_endpoints:
|
||||
available_parts = available.split('.')
|
||||
|
||||
# Ähnlichkeits-Heuristiken
|
||||
if len(target_parts) == len(available_parts):
|
||||
# Gleiche Struktur
|
||||
if target_parts[-1] in available_parts[-1] or available_parts[-1] in target_parts[-1]:
|
||||
suggestions.append(available)
|
||||
|
||||
# Ähnliche Funktionsnamen
|
||||
if target_parts[-1] in available_parts[-1]:
|
||||
suggestions.append(available)
|
||||
|
||||
return suggestions[:3] # Maximal 3 Vorschläge
|
||||
|
||||
def generate_corrections(self) -> List[Dict]:
|
||||
"""
|
||||
4. Auto-Fix Generator: Generiere MultiEdit-Korrekturen
|
||||
"""
|
||||
print("\n🔧 PHASE 4: Korrektur-Generierung")
|
||||
|
||||
with open(self.template_path, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
for problem in self.problems:
|
||||
if problem['type'] == 'missing_endpoint' and 'suggestions' in problem:
|
||||
old_endpoint = problem['endpoint']
|
||||
suggested_endpoint = problem['suggestions'][0] # Beste Suggestion
|
||||
|
||||
# Erstelle Korrektur
|
||||
old_pattern = f"url_for('{old_endpoint}'"
|
||||
new_pattern = f"url_for('{suggested_endpoint}'"
|
||||
|
||||
# Prüfe ob Pattern im Template vorkommt
|
||||
if old_pattern in template_content:
|
||||
correction = {
|
||||
'old_string': old_pattern,
|
||||
'new_string': new_pattern,
|
||||
'description': f'Korrigiere {old_endpoint} → {suggested_endpoint}',
|
||||
'confidence': 'high'
|
||||
}
|
||||
self.corrections.append(correction)
|
||||
|
||||
print(f" ✓ Generierte Korrekturen: {len(self.corrections)}")
|
||||
for correction in self.corrections:
|
||||
print(f" - {correction['description']}")
|
||||
|
||||
return self.corrections
|
||||
|
||||
def generate_report(self) -> Dict:
|
||||
"""
|
||||
Generiere strukturierten Analysebericht
|
||||
"""
|
||||
return {
|
||||
'summary': {
|
||||
'template_endpoints': len(self.template_endpoints),
|
||||
'available_endpoints': len(self.available_endpoints),
|
||||
'problems_found': len(self.problems),
|
||||
'corrections_generated': len(self.corrections)
|
||||
},
|
||||
'template_endpoints': list(self.template_endpoints),
|
||||
'available_endpoints': list(self.available_endpoints),
|
||||
'problems': self.problems,
|
||||
'corrections': self.corrections,
|
||||
'endpoint_mapping': self.endpoint_mapping
|
||||
}
|
||||
|
||||
def main():
|
||||
"""
|
||||
Hauptfunktion für Template-Analyse
|
||||
"""
|
||||
print("🚀 MYP Template-Analyse-Tool")
|
||||
print("=" * 50)
|
||||
|
||||
template_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/templates/admin.html"
|
||||
blueprint_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/blueprints/admin_unified.py"
|
||||
|
||||
analyzer = TemplateAnalyzer(template_path, blueprint_path)
|
||||
|
||||
# Durchführung der Analyse
|
||||
analyzer.analyze_template_endpoints()
|
||||
analyzer.analyze_blueprint_routes()
|
||||
analyzer.cross_reference_validation()
|
||||
analyzer.generate_corrections()
|
||||
|
||||
# Generiere Bericht
|
||||
report = analyzer.generate_report()
|
||||
|
||||
print("\n📊 ANALYSEBERICHT")
|
||||
print("=" * 50)
|
||||
print(f"Template-Endpoints: {report['summary']['template_endpoints']}")
|
||||
print(f"Verfügbare Endpoints: {report['summary']['available_endpoints']}")
|
||||
print(f"Gefundene Probleme: {report['summary']['problems_found']}")
|
||||
print(f"Generierte Korrekturen: {report['summary']['corrections_generated']}")
|
||||
|
||||
if report['problems']:
|
||||
print("\n❌ GEFUNDENE PROBLEME:")
|
||||
for i, problem in enumerate(report['problems'], 1):
|
||||
print(f" {i}. {problem['description']}")
|
||||
if 'suggestions' in problem:
|
||||
print(f" Vorschläge: {', '.join(problem['suggestions'])}")
|
||||
|
||||
if report['corrections']:
|
||||
print("\n🔧 EMPFOHLENE KORREKTUREN:")
|
||||
for i, correction in enumerate(report['corrections'], 1):
|
||||
print(f" {i}. {correction['description']}")
|
||||
print(f" Alt: {correction['old_string']}")
|
||||
print(f" Neu: {correction['new_string']}")
|
||||
|
||||
# Speichere Bericht als JSON
|
||||
with open('/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/template_analysis_report.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n💾 Bericht gespeichert: template_analysis_report.json")
|
||||
|
||||
return report
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
212
backend/tools/analysis/template_problem_analysis.py
Normal file
212
backend/tools/analysis/template_problem_analysis.py
Normal file
@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Erweiterte Template-Problemanalyse für MYP Admin Panel
|
||||
Vollständige Identifikation aller Template-Probleme mit Kontextanalyse
|
||||
|
||||
Autor: MYP Team - Till Tomczak
|
||||
Datum: 2025-06-19
|
||||
"""
|
||||
|
||||
import re
|
||||
import json
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
class AdvancedTemplateAnalyzer:
|
||||
"""
|
||||
Erweiterte Template-Problemanalyse mit detaillierter Kontextverarbeitung
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.template_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/templates/admin.html"
|
||||
self.blueprint_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/blueprints/admin_unified.py"
|
||||
self.app_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/app.py"
|
||||
|
||||
self.problems = []
|
||||
self.corrections = []
|
||||
self.all_endpoints = set()
|
||||
|
||||
def analyze_all_endpoints(self):
|
||||
"""
|
||||
Analysiere alle verfügbaren Endpoints aus allen Quellen
|
||||
"""
|
||||
print("🔍 Vollständige Endpoint-Analyse")
|
||||
|
||||
# 1. Admin Blueprint Endpoints
|
||||
with open(self.blueprint_path, 'r', encoding='utf-8') as f:
|
||||
blueprint_content = f.read()
|
||||
|
||||
# Admin Blueprint Routen
|
||||
admin_routes = re.findall(r'@admin_blueprint\.route\(["\']([^"\']+)["\'][^)]*\)\s*@admin_required\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)', blueprint_content, re.MULTILINE | re.DOTALL)
|
||||
for route_path, function_name in admin_routes:
|
||||
self.all_endpoints.add(f"admin.{function_name}")
|
||||
|
||||
# Admin API Blueprint Routen
|
||||
api_routes = re.findall(r'@admin_api_blueprint\.route\(["\']([^"\']+)["\'][^)]*\)\s*@admin_required\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)', blueprint_content, re.MULTILINE | re.DOTALL)
|
||||
for route_path, function_name in api_routes:
|
||||
self.all_endpoints.add(f"admin_api.{function_name}")
|
||||
|
||||
# 2. App.py Endpoints (Haupt-Routen)
|
||||
with open(self.app_path, 'r', encoding='utf-8') as f:
|
||||
app_content = f.read()
|
||||
|
||||
# Haupt-App Routen ohne Blueprint
|
||||
app_routes = re.findall(r'@app\.route\(["\']([^"\']+)["\'][^)]*\)\s*(?:@[a-zA-Z_][a-zA-Z0-9_]*\s*)*def\s+([a-zA-Z_][a-zA-Z0-9_]*)', app_content, re.MULTILINE | re.DOTALL)
|
||||
for route_path, function_name in app_routes:
|
||||
self.all_endpoints.add(function_name)
|
||||
|
||||
print(f" ✓ Insgesamt {len(self.all_endpoints)} Endpoints gefunden")
|
||||
|
||||
def analyze_template_problems(self):
|
||||
"""
|
||||
Analysiere spezifische Template-Probleme
|
||||
"""
|
||||
print("\n🔍 Template-Problem-Analyse")
|
||||
|
||||
with open(self.template_path, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
# Problem 1: Fehlende Blueprint-Referenz für jobs_page
|
||||
if "jobs.jobs_page" in template_content:
|
||||
self.problems.append({
|
||||
'type': 'incorrect_blueprint_reference',
|
||||
'line': 179,
|
||||
'issue': "jobs.jobs_page existiert nicht als Blueprint",
|
||||
'current': "url_for('jobs.jobs_page')",
|
||||
'correct': "url_for('jobs_page')",
|
||||
'description': "Referenz auf jobs_page ohne Blueprint-Prefix"
|
||||
})
|
||||
|
||||
# Problem 2: Überprüfe komplexe url_for Konstrukte
|
||||
complex_url_pattern = r"url_for\(['\"]([^'\"]+)['\"].*if.*else.*\)"
|
||||
complex_matches = re.findall(complex_url_pattern, template_content)
|
||||
|
||||
for match in complex_matches:
|
||||
if match not in self.all_endpoints:
|
||||
self.problems.append({
|
||||
'type': 'complex_url_for_construct',
|
||||
'issue': f"Komplexes url_for Konstrukt mit möglicherweise nicht existierendem Endpoint: {match}",
|
||||
'current': match,
|
||||
'description': "Komplexe bedingte url_for Referenz"
|
||||
})
|
||||
|
||||
# Problem 3: Überprüfe Parameter-basierte url_for Aufrufe
|
||||
param_url_pattern = r"url_for\(['\"]([^'\"]+)['\"][^)]+\)"
|
||||
param_matches = re.findall(param_url_pattern, template_content)
|
||||
|
||||
for match in param_matches:
|
||||
if match not in self.all_endpoints:
|
||||
self.problems.append({
|
||||
'type': 'parametrized_url_for',
|
||||
'issue': f"Parametrisierter url_for Aufruf mit möglicherweise nicht existierendem Endpoint: {match}",
|
||||
'current': match,
|
||||
'description': "URL-Parameter-basierte Referenz"
|
||||
})
|
||||
|
||||
print(f" ❌ {len(self.problems)} Template-Probleme identifiziert")
|
||||
|
||||
def generate_fixes(self):
|
||||
"""
|
||||
Generiere konkrete Fixes für alle identifizierten Probleme
|
||||
"""
|
||||
print("\n🔧 Fix-Generierung")
|
||||
|
||||
with open(self.template_path, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
for problem in self.problems:
|
||||
if problem['type'] == 'incorrect_blueprint_reference':
|
||||
# Fix für jobs.jobs_page → jobs_page
|
||||
old_string = "{{ url_for('jobs.jobs_page') if 'jobs' in url_for.__globals__ else '#' }}"
|
||||
new_string = "{{ url_for('jobs_page') }}"
|
||||
|
||||
if old_string in template_content:
|
||||
self.corrections.append({
|
||||
'old_string': old_string,
|
||||
'new_string': new_string,
|
||||
'description': 'Korrigiere jobs.jobs_page → jobs_page',
|
||||
'line': problem.get('line', 'unknown'),
|
||||
'confidence': 'high'
|
||||
})
|
||||
|
||||
print(f" ✓ {len(self.corrections)} Korrekturen generiert")
|
||||
|
||||
def generate_detailed_report(self):
|
||||
"""
|
||||
Generiere detaillierten Analysebericht
|
||||
"""
|
||||
return {
|
||||
'analysis_summary': {
|
||||
'total_endpoints': len(self.all_endpoints),
|
||||
'problems_found': len(self.problems),
|
||||
'corrections_available': len(self.corrections),
|
||||
'analysis_scope': ['admin.html', 'admin_unified.py', 'app.py']
|
||||
},
|
||||
'endpoints_discovered': sorted(list(self.all_endpoints)),
|
||||
'problems_identified': self.problems,
|
||||
'corrections_generated': self.corrections,
|
||||
'recommendations': [
|
||||
"Verwende konsistente Blueprint-Referenzen",
|
||||
"Vermeide komplexe bedingte url_for Konstrukte",
|
||||
"Überprüfe alle Parameter-basierten URL-Generierungen",
|
||||
"Implementiere einheitliche Namenskonventionen"
|
||||
]
|
||||
}
|
||||
|
||||
def main():
|
||||
"""
|
||||
Hauptfunktion für erweiterte Template-Analyse
|
||||
"""
|
||||
print("🚀 MYP Template-Problemanalyse Tool")
|
||||
print("=" * 60)
|
||||
|
||||
analyzer = AdvancedTemplateAnalyzer()
|
||||
|
||||
# Vollständige Analyse
|
||||
analyzer.analyze_all_endpoints()
|
||||
analyzer.analyze_template_problems()
|
||||
analyzer.generate_fixes()
|
||||
|
||||
# Generiere detaillierten Bericht
|
||||
report = analyzer.generate_detailed_report()
|
||||
|
||||
print("\n📊 DETAILLIERTER ANALYSEBERICHT")
|
||||
print("=" * 60)
|
||||
print(f"Gesamte Endpoints: {report['analysis_summary']['total_endpoints']}")
|
||||
print(f"Identifizierte Probleme: {report['analysis_summary']['problems_found']}")
|
||||
print(f"Verfügbare Korrekturen: {report['analysis_summary']['corrections_available']}")
|
||||
|
||||
if report['problems_identified']:
|
||||
print("\n❌ IDENTIFIZIERTE PROBLEME:")
|
||||
for i, problem in enumerate(report['problems_identified'], 1):
|
||||
print(f" {i}. {problem['issue']}")
|
||||
print(f" Typ: {problem['type']}")
|
||||
if 'line' in problem:
|
||||
print(f" Zeile: {problem['line']}")
|
||||
print(f" Aktuell: {problem.get('current', 'N/A')}")
|
||||
print(f" Korrekt: {problem.get('correct', 'N/A')}")
|
||||
print()
|
||||
|
||||
if report['corrections_generated']:
|
||||
print("🔧 GENERIERTE KORREKTUREN:")
|
||||
for i, correction in enumerate(report['corrections_generated'], 1):
|
||||
print(f" {i}. {correction['description']}")
|
||||
print(f" Vertrauen: {correction['confidence']}")
|
||||
print(f" Zeile: {correction['line']}")
|
||||
print(f" Alt: {correction['old_string']}")
|
||||
print(f" Neu: {correction['new_string']}")
|
||||
print()
|
||||
|
||||
print("💡 EMPFEHLUNGEN:")
|
||||
for rec in report['recommendations']:
|
||||
print(f" • {rec}")
|
||||
|
||||
# Speichere erweiterten Bericht
|
||||
with open('/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/template_problem_report.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n💾 Detaillierter Bericht gespeichert: template_problem_report.json")
|
||||
|
||||
return report
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
226
backend/tools/analysis/template_validation_final.py
Normal file
226
backend/tools/analysis/template_validation_final.py
Normal file
@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Finale Template-Validierung für MYP Admin Panel
|
||||
Abschließende Verifikation aller Template-Endpoints nach Problembehebeung
|
||||
|
||||
Autor: MYP Team - Till Tomczak
|
||||
Datum: 2025-06-19
|
||||
"""
|
||||
|
||||
import re
|
||||
import json
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
class FinalTemplateValidator:
|
||||
"""
|
||||
Finale Template-Validierung mit vollständiger Endpoint-Verifikation
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.template_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/templates/admin.html"
|
||||
self.blueprint_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/blueprints/admin_unified.py"
|
||||
self.app_path = "/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/app.py"
|
||||
|
||||
self.all_available_endpoints = set()
|
||||
self.template_references = []
|
||||
self.validation_results = []
|
||||
|
||||
def collect_all_endpoints(self):
|
||||
"""
|
||||
Sammle alle verfügbaren Endpoints aus allen Quellen
|
||||
"""
|
||||
print("🔍 Vollständige Endpoint-Sammlung")
|
||||
|
||||
# 1. Admin Blueprint Endpoints
|
||||
with open(self.blueprint_path, 'r', encoding='utf-8') as f:
|
||||
blueprint_content = f.read()
|
||||
|
||||
admin_routes = re.findall(
|
||||
r'@admin_blueprint\.route\(["\']([^"\']+)["\'][^)]*\)\s*@admin_required\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)',
|
||||
blueprint_content,
|
||||
re.MULTILINE | re.DOTALL
|
||||
)
|
||||
for route_path, function_name in admin_routes:
|
||||
self.all_available_endpoints.add(f"admin.{function_name}")
|
||||
|
||||
# 2. Admin API Blueprint Endpoints
|
||||
api_routes = re.findall(
|
||||
r'@admin_api_blueprint\.route\(["\']([^"\']+)["\'][^)]*\)\s*@admin_required\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)',
|
||||
blueprint_content,
|
||||
re.MULTILINE | re.DOTALL
|
||||
)
|
||||
for route_path, function_name in api_routes:
|
||||
self.all_available_endpoints.add(f"admin_api.{function_name}")
|
||||
|
||||
# 3. Haupt-App Endpoints
|
||||
with open(self.app_path, 'r', encoding='utf-8') as f:
|
||||
app_content = f.read()
|
||||
|
||||
app_routes = re.findall(
|
||||
r'@app\.route\(["\']([^"\']+)["\'][^)]*\)\s*(?:@[a-zA-Z_][a-zA-Z0-9_]*\s*)*def\s+([a-zA-Z_][a-zA-Z0-9_]*)',
|
||||
app_content,
|
||||
re.MULTILINE | re.DOTALL
|
||||
)
|
||||
for route_path, function_name in app_routes:
|
||||
self.all_available_endpoints.add(function_name)
|
||||
|
||||
print(f" ✓ {len(self.all_available_endpoints)} Endpoints verfügbar")
|
||||
|
||||
def extract_template_references(self):
|
||||
"""
|
||||
Extrahiere alle url_for Referenzen aus dem Template
|
||||
"""
|
||||
print("\n🔍 Template-Referenz-Extraktion")
|
||||
|
||||
with open(self.template_path, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
# Finde alle url_for Aufrufe mit Zeilennummern
|
||||
lines = template_content.split('\n')
|
||||
for line_num, line in enumerate(lines, 1):
|
||||
url_for_matches = re.findall(r"url_for\(['\"]([^'\"]+)['\"][^)]*\)", line)
|
||||
for match in url_for_matches:
|
||||
self.template_references.append({
|
||||
'line': line_num,
|
||||
'endpoint': match,
|
||||
'context': line.strip()
|
||||
})
|
||||
|
||||
print(f" ✓ {len(self.template_references)} Template-Referenzen gefunden")
|
||||
|
||||
def validate_all_references(self):
|
||||
"""
|
||||
Validiere alle Template-Referenzen gegen verfügbare Endpoints
|
||||
"""
|
||||
print("\n🔍 Referenz-Validierung")
|
||||
|
||||
valid_count = 0
|
||||
invalid_count = 0
|
||||
|
||||
for ref in self.template_references:
|
||||
endpoint = ref['endpoint']
|
||||
is_valid = endpoint in self.all_available_endpoints
|
||||
|
||||
result = {
|
||||
'line': ref['line'],
|
||||
'endpoint': endpoint,
|
||||
'is_valid': is_valid,
|
||||
'context': ref['context']
|
||||
}
|
||||
|
||||
if not is_valid:
|
||||
# Versuche ähnliche Endpoints zu finden
|
||||
suggestions = self._find_similar_endpoints(endpoint)
|
||||
if suggestions:
|
||||
result['suggestions'] = suggestions
|
||||
invalid_count += 1
|
||||
else:
|
||||
valid_count += 1
|
||||
|
||||
self.validation_results.append(result)
|
||||
|
||||
print(f" ✅ Gültige Referenzen: {valid_count}")
|
||||
print(f" ❌ Ungültige Referenzen: {invalid_count}")
|
||||
|
||||
return invalid_count == 0
|
||||
|
||||
def _find_similar_endpoints(self, target: str) -> List[str]:
|
||||
"""
|
||||
Finde ähnliche Endpoints für Vorschläge
|
||||
"""
|
||||
suggestions = []
|
||||
target_parts = target.split('.')
|
||||
target_func = target_parts[-1] if '.' in target else target
|
||||
|
||||
for available in self.all_available_endpoints:
|
||||
available_parts = available.split('.')
|
||||
available_func = available_parts[-1] if '.' in available else available
|
||||
|
||||
# Exakte Funktionsnamen-Übereinstimmung
|
||||
if target_func == available_func:
|
||||
suggestions.append(available)
|
||||
# Ähnliche Namen
|
||||
elif target_func in available_func or available_func in target_func:
|
||||
suggestions.append(available)
|
||||
|
||||
return suggestions[:3]
|
||||
|
||||
def generate_final_report(self):
|
||||
"""
|
||||
Generiere finalen Validierungsbericht
|
||||
"""
|
||||
valid_refs = [r for r in self.validation_results if r['is_valid']]
|
||||
invalid_refs = [r for r in self.validation_results if not r['is_valid']]
|
||||
|
||||
return {
|
||||
'validation_summary': {
|
||||
'total_endpoints_available': len(self.all_available_endpoints),
|
||||
'total_template_references': len(self.template_references),
|
||||
'valid_references': len(valid_refs),
|
||||
'invalid_references': len(invalid_refs),
|
||||
'validation_success': len(invalid_refs) == 0
|
||||
},
|
||||
'available_endpoints': sorted(list(self.all_available_endpoints)),
|
||||
'template_references': self.template_references,
|
||||
'validation_results': self.validation_results,
|
||||
'invalid_references': invalid_refs,
|
||||
'validation_status': 'PASSED' if len(invalid_refs) == 0 else 'FAILED'
|
||||
}
|
||||
|
||||
def main():
|
||||
"""
|
||||
Finale Template-Validierung
|
||||
"""
|
||||
print("🎯 MYP Template-Validierung - Finale Überprüfung")
|
||||
print("=" * 70)
|
||||
|
||||
validator = FinalTemplateValidator()
|
||||
|
||||
# Vollständige Validierung
|
||||
validator.collect_all_endpoints()
|
||||
validator.extract_template_references()
|
||||
is_valid = validator.validate_all_references()
|
||||
|
||||
# Generiere finalen Bericht
|
||||
report = validator.generate_final_report()
|
||||
|
||||
print("\n📊 FINALE VALIDIERUNGSERGEBNISSE")
|
||||
print("=" * 70)
|
||||
print(f"Status: {report['validation_status']}")
|
||||
print(f"Verfügbare Endpoints: {report['validation_summary']['total_endpoints_available']}")
|
||||
print(f"Template-Referenzen: {report['validation_summary']['total_template_references']}")
|
||||
print(f"Gültige Referenzen: {report['validation_summary']['valid_references']}")
|
||||
print(f"Ungültige Referenzen: {report['validation_summary']['invalid_references']}")
|
||||
|
||||
if report['invalid_references']:
|
||||
print("\n❌ UNGÜLTIGE REFERENZEN:")
|
||||
for ref in report['invalid_references']:
|
||||
print(f" • Zeile {ref['line']}: {ref['endpoint']}")
|
||||
print(f" Kontext: {ref['context']}")
|
||||
if 'suggestions' in ref:
|
||||
print(f" Vorschläge: {', '.join(ref['suggestions'])}")
|
||||
print()
|
||||
else:
|
||||
print("\n✅ ALLE TEMPLATE-REFERENZEN SIND GÜLTIG!")
|
||||
print(" Das Admin-Template ist vollständig korrekt konfiguriert.")
|
||||
|
||||
print("\n📋 TEMPLATE-REFERENZEN DETAILS:")
|
||||
for ref in report['template_references']:
|
||||
status = "✅" if ref['endpoint'] in report['available_endpoints'] else "❌"
|
||||
print(f" {status} Zeile {ref['line']:3d}: {ref['endpoint']}")
|
||||
|
||||
# Speichere finalen Bericht
|
||||
with open('/mnt/c/Users/TTOMCZA.EMEA/Dev/Projektarbeit-MYP/backend/template_validation_final_report.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n💾 Finaler Validierungsbericht gespeichert: template_validation_final_report.json")
|
||||
|
||||
if report['validation_status'] == 'PASSED':
|
||||
print("\n🎉 TEMPLATE-VALIDIERUNG ERFOLGREICH ABGESCHLOSSEN!")
|
||||
return True
|
||||
else:
|
||||
print("\n⚠️ TEMPLATE-VALIDIERUNG FEHLGESCHLAGEN - Korrekturen erforderlich")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user