clients.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import csv
  2. import re
  3. import xml.etree.ElementTree as ET
  4. from datetime import datetime
  5. from pathlib import Path
  6. def extract_clients(xml_path: Path) -> list[dict[str, str]]:
  7. root = ET.parse(xml_path).getroot()
  8. session = root.find("Session")
  9. if session is None:
  10. return []
  11. client_description = session.find("SessionDescription[@Name='ClientName']")
  12. client_name = "" if client_description is None else client_description.get("Description", "")
  13. start_time = session.get("StartTime", "")
  14. return [
  15. {
  16. "ParticipantName": participant.get("Name", ""),
  17. "ClientName": client_name,
  18. "StartTime": start_time,
  19. }
  20. for participant in session.findall("Participant")
  21. if participant.get("Role") not in {"ROLE_SUPPORTER", "ROLE_UNKNOWN"}
  22. and participant.get("Name", "").strip()
  23. and client_name.strip()
  24. ]
  25. def calculate_customer(client_name: str) -> str:
  26. match = re.search(r"\bC(?:7|11)\b", client_name, re.IGNORECASE)
  27. customer = client_name[: match.end()] if match else client_name
  28. customer = (
  29. customer.replace("&", "u.")
  30. .replace("ä", "ae")
  31. .replace("ö", "oe")
  32. .replace("ü", "ue")
  33. .replace("Ä", "Ae")
  34. .replace("Ö", "Oe")
  35. .replace("Ü", "Ue")
  36. .replace("ß", "ss")
  37. )
  38. customer = re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", customer)
  39. customer = customer.rstrip(" .")
  40. return customer or "_"
  41. def write_clients_csv(rows: list[dict[str, str]], csv_path: Path) -> None:
  42. csv_path.parent.mkdir(parents=True, exist_ok=True)
  43. fieldnames = ["ParticipantName", "ClientName", "StartTime", "Kunde"]
  44. with csv_path.open("w", newline="", encoding="latin-1", errors="replace") as csv_file:
  45. writer = csv.DictWriter(csv_file, fieldnames=fieldnames, delimiter=";")
  46. writer.writeheader()
  47. writer.writerows(rows)
  48. def read_clients_csv(csv_path: Path) -> list[dict[str, str]]:
  49. if not csv_path.exists():
  50. return []
  51. with csv_path.open("r", newline="", encoding="latin-1") as csv_file:
  52. reader = csv.DictReader(csv_file, delimiter=";")
  53. return [
  54. {
  55. "ParticipantName": row.get("ParticipantName", "").strip(),
  56. "ClientName": row.get("ClientName", ""),
  57. "StartTime": row.get("StartTime", ""),
  58. }
  59. for row in reader
  60. if row.get("ParticipantName", "").strip() and row.get("ClientName", "").strip()
  61. ]
  62. def parse_start_time(start_time: str) -> datetime:
  63. if not start_time:
  64. return datetime.min
  65. try:
  66. return datetime.fromisoformat(start_time.replace("Z", "+00:00")).replace(tzinfo=None)
  67. except ValueError:
  68. return datetime.min
  69. def merge_clients(existing_rows: list[dict[str, str]], new_rows: list[dict[str, str]]) -> list[dict[str, str]]:
  70. newest_rows: dict[str, dict[str, str]] = {}
  71. for row in [*existing_rows, *new_rows]:
  72. participant_name = row["ParticipantName"].strip()
  73. client_name = row["ClientName"].strip()
  74. if not participant_name or not client_name:
  75. continue
  76. normalized_row = {
  77. "ParticipantName": participant_name,
  78. "ClientName": client_name,
  79. "StartTime": row.get("StartTime", ""),
  80. }
  81. current = newest_rows.get(participant_name)
  82. if current is None or parse_start_time(normalized_row["StartTime"]) >= parse_start_time(current["StartTime"]):
  83. newest_rows[participant_name] = normalized_row
  84. return [
  85. {**row, "Kunde": calculate_customer(row["ClientName"])}
  86. for row in sorted(newest_rows.values(), key=lambda row: row["ParticipantName"].casefold())
  87. ]
  88. def find_xml_files(directory: Path) -> list[Path]:
  89. return sorted(
  90. path
  91. for path in directory.rglob("*")
  92. if path.is_file() and path.suffix.lower() == ".xml" and not path.name.lower().startswith("sessions")
  93. )
  94. def run(directory: Path, output: Path) -> tuple[int, int]:
  95. if not directory.is_dir():
  96. raise ValueError(f"Das Verzeichnis existiert nicht: {directory}")
  97. xml_files = find_xml_files(directory)
  98. new_rows = [row for xml_file in xml_files for row in extract_clients(xml_file)]
  99. rows = merge_clients(read_clients_csv(output), new_rows)
  100. write_clients_csv(rows, output)
  101. return len(xml_files), len(rows)