files.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import csv
  2. import os
  3. import xml.etree.ElementTree as ET
  4. from dataclasses import dataclass
  5. from datetime import datetime
  6. from pathlib import Path
  7. @dataclass(frozen=True)
  8. class FilesResult:
  9. xml_count: int
  10. row_count: int
  11. def find_xml_files(directory: Path) -> list[Path]:
  12. return sorted(
  13. path
  14. for path in directory.rglob("*")
  15. if path.is_file() and path.suffix.lower() == ".xml" and not path.name.lower().startswith("sessions")
  16. )
  17. def is_file_from_session_day(file_path: Path, session_start_time: str) -> bool:
  18. try:
  19. session_date = datetime.fromisoformat(session_start_time.replace("Z", "+00:00")).date()
  20. except ValueError:
  21. return False
  22. return datetime.fromtimestamp(file_path.stat().st_birthtime).date() == session_date
  23. def is_in_pc_visit_directory(file_path: Path, desktop_directory: Path) -> bool:
  24. try:
  25. relative_parts = file_path.relative_to(desktop_directory).parts
  26. except ValueError:
  27. return False
  28. return bool(relative_parts) and relative_parts[0].casefold() == "pc-visit"
  29. def find_file_on_desktop(
  30. file_path: str,
  31. session_start_time: str,
  32. desktop_directory: Path | None = None,
  33. ignore_timestamp: bool = False,
  34. ) -> Path | None:
  35. desktop_directory = desktop_directory or Path(os.environ.get("USERPROFILE", str(Path.home()))) / "Desktop"
  36. path_parts = [part for part in file_path.replace("\\", "/").split("/") if part]
  37. desktop_index = next(
  38. (index for index, part in enumerate(path_parts) if part.casefold() == "desktop"),
  39. None,
  40. )
  41. if desktop_index is not None:
  42. relative_path = Path(*path_parts[desktop_index + 1 :])
  43. exact_match = desktop_directory / relative_path
  44. if (
  45. exact_match.is_file()
  46. and not is_in_pc_visit_directory(exact_match, desktop_directory)
  47. and (ignore_timestamp or is_file_from_session_day(exact_match, session_start_time))
  48. ):
  49. return exact_match
  50. file_name = Path(path_parts[-1]).name if path_parts else ""
  51. if not file_name or not desktop_directory.is_dir():
  52. return None
  53. return next(
  54. (
  55. path
  56. for path in desktop_directory.rglob(file_name)
  57. if path.is_file()
  58. and not is_in_pc_visit_directory(path, desktop_directory)
  59. and (ignore_timestamp or is_file_from_session_day(path, session_start_time))
  60. ),
  61. None,
  62. )
  63. def extract_received_files(xml_path: Path, ignore_timestamp: bool = False) -> list[dict[str, str]]:
  64. root = ET.parse(xml_path).getroot()
  65. session = root.find("Session")
  66. if session is None:
  67. return []
  68. participant_names = [
  69. participant.get("Name", "").strip()
  70. for participant in session.findall("Participant")
  71. if participant.get("Role") not in {"ROLE_SUPPORTER", "ROLE_UNKNOWN"} and participant.get("Name", "").strip()
  72. ]
  73. if not participant_names:
  74. return []
  75. rows = []
  76. for received_file in session.findall("ReceivedFile"):
  77. original_path = received_file.get("Name", "").strip()
  78. start_time = received_file.get("DateTime", "").strip()
  79. local_path = find_file_on_desktop(original_path, start_time, ignore_timestamp=ignore_timestamp)
  80. if local_path is not None:
  81. rows.append({"ParticipantName": participant_names[0], "FilePath": str(local_path)})
  82. return rows
  83. def write_files_csv(rows: list[dict[str, str]], csv_path: Path) -> None:
  84. csv_path.parent.mkdir(parents=True, exist_ok=True)
  85. with csv_path.open("w", newline="", encoding="latin-1", errors="replace") as csv_file:
  86. writer = csv.DictWriter(csv_file, fieldnames=["ParticipantName", "FilePath"], delimiter=";")
  87. writer.writeheader()
  88. writer.writerows(rows)
  89. def run(directory: Path, output: Path, ignore_timestamp: bool = False) -> FilesResult:
  90. if not directory.is_dir():
  91. raise ValueError(f"Das Verzeichnis existiert nicht: {directory}")
  92. xml_files = find_xml_files(directory)
  93. rows = [
  94. row for xml_file in xml_files for row in extract_received_files(xml_file, ignore_timestamp=ignore_timestamp)
  95. ]
  96. unique_rows = {(row["ParticipantName"], row["FilePath"]): row for row in rows}
  97. write_files_csv(
  98. sorted(unique_rows.values(), key=lambda row: (row["ParticipantName"].casefold(), row["FilePath"].casefold())),
  99. output,
  100. )
  101. return FilesResult(xml_count=len(xml_files), row_count=len(unique_rows))