import shutil import xml.etree.ElementTree as ET from datetime import datetime, timedelta from pathlib import Path def find_xml_files(directory: Path) -> list[Path]: archive_directory = directory / "Archiv" return sorted( path for path in directory.rglob("*") if path.is_file() and path.suffix.lower() == ".xml" and not path.name.lower().startswith("sessions") and archive_directory not in path.parents ) def get_archive_year(xml_path: Path, now: datetime | None = None) -> int | None: try: root = ET.parse(xml_path).getroot() except ET.ParseError: return None session = root.find("Session") if session is None: return None if session.get("Duration", "").strip(): start_time = session.get("StartTime", "") try: return datetime.fromisoformat(start_time.replace("Z", "+00:00")).year except ValueError: return None created_at = datetime.fromtimestamp(xml_path.stat().st_birthtime) now = now or datetime.now() if now - created_at >= timedelta(days=30): return created_at.year return None def remove_empty_directories(directory: Path) -> int: archive_directory = directory / "Archiv" removed_count = 0 subdirectories = sorted( (path for path in directory.rglob("*") if path.is_dir()), key=lambda path: len(path.parts), reverse=True, ) for subdirectory in subdirectories: if subdirectory == archive_directory or archive_directory in subdirectory.parents: continue try: subdirectory.rmdir() except OSError: continue removed_count += 1 return removed_count def run(directory: Path) -> tuple[int, int, int]: if not directory.is_dir(): raise ValueError(f"Das Verzeichnis existiert nicht: {directory}") checked_count = 0 archived_count = 0 skipped_count = 0 for xml_file in find_xml_files(directory): checked_count += 1 year = get_archive_year(xml_file) if year is None: skipped_count += 1 continue target_directory = directory / "Archiv" / str(year) target_directory.mkdir(parents=True, exist_ok=True) target = target_directory / xml_file.name if target.exists(): skipped_count += 1 continue shutil.move(str(xml_file), str(target)) archived_count += 1 remove_empty_directories(directory) return checked_count, archived_count, skipped_count