This commit introduces a suite of tools for analyzing and optimizing imports and functions within the backend codebase. The following files have been updated: - backend/FRONTEND_ASSETS_ANALYSE.md - backend/REDUNDANZ_ANALYSE_FINAL.md - backend/SOFORT_L\303\226SCHBARE_FUN
325 lines
13 KiB
Python
325 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Automatische Bereinigung von ungenutzten Imports im MYP Backend
|
|
|
|
Dieser Script bereinigt sichere, ungenutzte Imports automatisch und
|
|
erstellt ein Backup vor den Änderungen.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import ast
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import List, Dict, Set, Tuple
|
|
|
|
class ImportCleaner:
|
|
def __init__(self, backend_path: str):
|
|
self.backend_path = Path(backend_path)
|
|
self.backup_dir = self.backend_path / f"backup_imports_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
self.changes = []
|
|
|
|
# Sichere Imports die entfernt werden können (niedrige Fehlerwahrscheinlichkeit)
|
|
self.safe_unused_imports = {
|
|
# Typing imports (fast immer sicher zu entfernen)
|
|
'typing.Set', 'typing.Tuple', 'typing.List', 'typing.Dict', 'typing.Any',
|
|
'typing.Optional', 'typing.Union', 'typing.Callable',
|
|
|
|
# Standard library (meist sicher wenn ungenutzt)
|
|
'pathlib.Path', 'enum.Enum', 'dataclasses.dataclass', 'dataclasses.asdict',
|
|
'collections.defaultdict', 'collections.Counter',
|
|
|
|
# Entwicklungs-spezifische imports
|
|
'rich.console.Console', 'rich.table.Table', 'rich.panel.Panel',
|
|
'rich.progress.Progress', 'rich.text.Text',
|
|
'faker.Faker', 'bs4.BeautifulSoup',
|
|
|
|
# Selenium (oft in Test-Dateien ungenutzt)
|
|
'selenium.common.exceptions.TimeoutException',
|
|
'selenium.common.exceptions.WebDriverException',
|
|
'selenium.common.exceptions.NoSuchElementException',
|
|
'selenium.webdriver.firefox.service.Service',
|
|
'selenium.webdriver.chrome.service.Service',
|
|
'selenium.webdriver.support.expected_conditions',
|
|
'selenium.webdriver.common.by.By',
|
|
'selenium.webdriver.support.ui.WebDriverWait',
|
|
'selenium.webdriver.chrome.options.Options',
|
|
'selenium.webdriver.firefox.options.Options',
|
|
|
|
# WTForms (sicher wenn nicht in Templates verwendet)
|
|
'wtforms.validators.NumberRange', 'wtforms.validators.Optional',
|
|
'wtforms.validators.DataRequired', 'wtforms.validators.Email',
|
|
'wtforms.TextAreaField', 'wtforms.IntegerField', 'wtforms.StringField',
|
|
'wtforms.SelectField',
|
|
}
|
|
|
|
# Dateien die NUR automatisch bereinigt werden (niedrige Kritikalität)
|
|
self.safe_files = {
|
|
'template_analysis_tool.py',
|
|
'template_validation_final.py',
|
|
'template_problem_analysis.py',
|
|
'import_analyzer.py',
|
|
'form_test_automator.py',
|
|
'simple_form_tester.py',
|
|
'test_flask_minimal.py',
|
|
'scripts/screenshot_tool.py',
|
|
'scripts/quick_unicode_fix.py',
|
|
'scripts/test_protocol_generator.py',
|
|
'ssl/ssl_fix.py',
|
|
'ssl/fix_ssl_browser.py',
|
|
'static/icons/generate_icons.py',
|
|
}
|
|
|
|
def create_backup(self):
|
|
"""Erstellt Backup aller Python-Dateien"""
|
|
print(f"Erstelle Backup in: {self.backup_dir}")
|
|
self.backup_dir.mkdir(exist_ok=True)
|
|
|
|
for py_file in self.backend_path.rglob("*.py"):
|
|
if self.should_process_file(py_file):
|
|
rel_path = py_file.relative_to(self.backend_path)
|
|
backup_file = self.backup_dir / rel_path
|
|
backup_file.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(py_file, backup_file)
|
|
|
|
print(f"✅ Backup erstellt: {len(list(self.backup_dir.rglob('*.py')))} Dateien")
|
|
|
|
def should_process_file(self, file_path: Path) -> bool:
|
|
"""Bestimmt ob eine Datei verarbeitet werden soll"""
|
|
# Überspringe bestimmte Verzeichnisse
|
|
exclude_dirs = {'__pycache__', '.git', 'node_modules', 'instance'}
|
|
if any(part in str(file_path) for part in exclude_dirs):
|
|
return False
|
|
|
|
# Überspringe Backup-Verzeichnisse
|
|
if 'backup_' in str(file_path):
|
|
return False
|
|
|
|
return True
|
|
|
|
def analyze_file_imports(self, file_path: Path) -> Tuple[List[str], Set[str]]:
|
|
"""Analysiert Imports und Verwendungen in einer Datei"""
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
tree = ast.parse(content)
|
|
|
|
# Sammle alle Imports
|
|
imports = []
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.Import):
|
|
for alias in node.names:
|
|
imports.append(alias.name)
|
|
elif isinstance(node, ast.ImportFrom):
|
|
module = node.module or ''
|
|
for alias in node.names:
|
|
if module:
|
|
imports.append(f"{module}.{alias.name}")
|
|
else:
|
|
imports.append(alias.name)
|
|
|
|
# Sammle verwendete Namen (vereinfacht)
|
|
used_names = set()
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.Name):
|
|
used_names.add(node.id)
|
|
elif isinstance(node, ast.Attribute):
|
|
if isinstance(node.value, ast.Name):
|
|
used_names.add(f"{node.value.id}.{node.attr}")
|
|
|
|
# Prüfe auch String-Literale
|
|
for imp in imports:
|
|
if imp in content:
|
|
used_names.add(imp)
|
|
|
|
return imports, used_names
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Fehler beim Analysieren von {file_path}: {e}")
|
|
return [], set()
|
|
|
|
def find_safe_unused_imports(self, file_path: Path) -> List[str]:
|
|
"""Findet sichere ungenutzte Imports in einer Datei"""
|
|
imports, used_names = self.analyze_file_imports(file_path)
|
|
|
|
unused_safe = []
|
|
for imp in imports:
|
|
# Nur sichere Imports berücksichtigen
|
|
if imp in self.safe_unused_imports:
|
|
# Prüfe verschiedene Nutzungsformen
|
|
base_name = imp.split('.')[0]
|
|
is_used = (
|
|
base_name in used_names or
|
|
imp in used_names or
|
|
any(imp in name for name in used_names)
|
|
)
|
|
|
|
if not is_used:
|
|
unused_safe.append(imp)
|
|
|
|
return unused_safe
|
|
|
|
def remove_unused_imports(self, file_path: Path, unused_imports: List[str]) -> bool:
|
|
"""Entfernt ungenutzte Imports aus einer Datei"""
|
|
if not unused_imports:
|
|
return False
|
|
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
lines = f.readlines()
|
|
|
|
modified = False
|
|
new_lines = []
|
|
|
|
for line in lines:
|
|
should_remove = False
|
|
|
|
# Prüfe ob die Zeile einen zu entfernenden Import enthält
|
|
for unused_imp in unused_imports:
|
|
# Verschiedene Import-Patterns prüfen
|
|
patterns = [
|
|
f"from {unused_imp.split('.')[0]} import {unused_imp.split('.')[-1]}",
|
|
f"import {unused_imp}",
|
|
f"from {'.'.join(unused_imp.split('.')[:-1])} import {unused_imp.split('.')[-1]}",
|
|
]
|
|
|
|
for pattern in patterns:
|
|
if pattern in line and line.strip().startswith(('from ', 'import ')):
|
|
# Prüfe ob es eine reine Import-Zeile ist (keine Kommentare etc.)
|
|
clean_line = line.split('#')[0].strip()
|
|
if clean_line.endswith(unused_imp.split('.')[-1]) or clean_line.endswith(unused_imp):
|
|
should_remove = True
|
|
break
|
|
|
|
if should_remove:
|
|
break
|
|
|
|
if not should_remove:
|
|
new_lines.append(line)
|
|
else:
|
|
modified = True
|
|
print(f" Entferne: {line.strip()}")
|
|
|
|
if modified:
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
f.writelines(new_lines)
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"❌ Fehler beim Bereinigen von {file_path}: {e}")
|
|
return False
|
|
|
|
return False
|
|
|
|
def clean_file(self, file_path: Path) -> bool:
|
|
"""Bereinigt eine einzelne Datei"""
|
|
rel_path = file_path.relative_to(self.backend_path)
|
|
|
|
# Nur sichere Dateien automatisch bereinigen
|
|
if str(rel_path) not in self.safe_files:
|
|
print(f"⚠️ Überspringe {rel_path} (nicht in sicherer Liste)")
|
|
return False
|
|
|
|
print(f"\n🔍 Analysiere: {rel_path}")
|
|
|
|
unused_imports = self.find_safe_unused_imports(file_path)
|
|
|
|
if unused_imports:
|
|
print(f" Gefunden: {len(unused_imports)} sichere ungenutzte Imports")
|
|
modified = self.remove_unused_imports(file_path, unused_imports)
|
|
|
|
if modified:
|
|
self.changes.append({
|
|
'file': str(rel_path),
|
|
'removed_imports': unused_imports,
|
|
'count': len(unused_imports)
|
|
})
|
|
print(f" ✅ {len(unused_imports)} Imports entfernt")
|
|
return True
|
|
else:
|
|
print(f" ⚠️ Imports gefunden aber nicht entfernt")
|
|
else:
|
|
print(f" ✅ Keine sicheren ungenutzten Imports gefunden")
|
|
|
|
return False
|
|
|
|
def run_cleanup(self):
|
|
"""Führt die komplette Bereinigung durch"""
|
|
print("🧹 Starte automatische Import-Bereinigung...")
|
|
print(f"📁 Backend-Pfad: {self.backend_path}")
|
|
|
|
# Backup erstellen
|
|
self.create_backup()
|
|
|
|
# Alle Python-Dateien durchgehen
|
|
modified_files = 0
|
|
total_removed = 0
|
|
|
|
for py_file in self.backend_path.rglob("*.py"):
|
|
if self.should_process_file(py_file):
|
|
if self.clean_file(py_file):
|
|
modified_files += 1
|
|
|
|
# Statistiken
|
|
total_removed = sum(change['count'] for change in self.changes)
|
|
|
|
print(f"\n" + "="*60)
|
|
print(f"📊 BEREINIGUNG ABGESCHLOSSEN")
|
|
print(f"="*60)
|
|
print(f"Bearbeitete Dateien: {modified_files}")
|
|
print(f"Entfernte Imports gesamt: {total_removed}")
|
|
print(f"Backup erstellt in: {self.backup_dir}")
|
|
|
|
if self.changes:
|
|
print(f"\n📝 GEÄNDERTE DATEIEN:")
|
|
for change in self.changes:
|
|
print(f" 📄 {change['file']}: {change['count']} Imports entfernt")
|
|
|
|
print(f"\n💡 NÄCHSTE SCHRITTE:")
|
|
print(f"1. Tests ausführen: python -m pytest")
|
|
print(f"2. App starten und prüfen: python app.py --debug")
|
|
print(f"3. Bei Problemen Backup wiederherstellen")
|
|
print(f"4. Manuelle Bereinigung von app.py und models.py")
|
|
|
|
return modified_files, total_removed
|
|
|
|
def restore_backup(self):
|
|
"""Stellt das Backup wieder her"""
|
|
if not self.backup_dir.exists():
|
|
print("❌ Kein Backup gefunden!")
|
|
return False
|
|
|
|
print(f"🔄 Stelle Backup wieder her aus: {self.backup_dir}")
|
|
|
|
restored = 0
|
|
for backup_file in self.backup_dir.rglob("*.py"):
|
|
rel_path = backup_file.relative_to(self.backup_dir)
|
|
original_file = self.backend_path / rel_path
|
|
|
|
if original_file.exists():
|
|
shutil.copy2(backup_file, original_file)
|
|
restored += 1
|
|
|
|
print(f"✅ {restored} Dateien wiederhergestellt")
|
|
return True
|
|
|
|
def main():
|
|
backend_path = Path(__file__).parent
|
|
cleaner = ImportCleaner(str(backend_path))
|
|
|
|
import sys
|
|
if len(sys.argv) > 1 and sys.argv[1] == '--restore':
|
|
# Backup wiederherstellen
|
|
cleaner.restore_backup()
|
|
else:
|
|
# Bereinigung durchführen
|
|
modified, removed = cleaner.run_cleanup()
|
|
|
|
if modified > 0:
|
|
print(f"\n⚠️ WICHTIG: Führe Tests aus um sicherzustellen dass alles funktioniert!")
|
|
print(f"Bei Problemen: python cleanup_imports.py --restore")
|
|
|
|
if __name__ == "__main__":
|
|
main() |