clients.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. fieldnames = ["ParticipantName", "ClientName", "StartTime", "Kunde"]
  43. with csv_path.open("w", newline="", encoding="latin-1", errors="replace") as csv_file:
  44. writer = csv.DictWriter(csv_file, fieldnames=fieldnames, delimiter=";")
  45. writer.writeheader()
  46. writer.writerows(rows)
  47. def read_clients_csv(csv_path: Path) -> list[dict[str, str]]:
  48. if not csv_path.exists():
  49. return []
  50. with csv_path.open("r", newline="", encoding="latin-1") as csv_file:
  51. reader = csv.DictReader(csv_file, delimiter=";")
  52. return [
  53. {
  54. "ParticipantName": row.get("ParticipantName", "").strip(),
  55. "ClientName": row.get("ClientName", ""),
  56. "StartTime": row.get("StartTime", ""),
  57. }
  58. for row in reader
  59. if row.get("ParticipantName", "").strip() and row.get("ClientName", "").strip()
  60. ]
  61. def parse_start_time(start_time: str) -> datetime:
  62. if not start_time:
  63. return datetime.min
  64. try:
  65. return datetime.fromisoformat(start_time.replace("Z", "+00:00")).replace(tzinfo=None)
  66. except ValueError:
  67. return datetime.min
  68. def merge_clients(existing_rows: list[dict[str, str]], new_rows: list[dict[str, str]]) -> list[dict[str, str]]:
  69. newest_rows: dict[str, dict[str, str]] = {}
  70. for row in [*existing_rows, *new_rows]:
  71. participant_name = row["ParticipantName"].strip()
  72. client_name = row["ClientName"].strip()
  73. if not participant_name or not client_name:
  74. continue
  75. normalized_row = {
  76. "ParticipantName": participant_name,
  77. "ClientName": client_name,
  78. "StartTime": row.get("StartTime", ""),
  79. }
  80. current = newest_rows.get(participant_name)
  81. if current is None or parse_start_time(normalized_row["StartTime"]) >= parse_start_time(current["StartTime"]):
  82. newest_rows[participant_name] = normalized_row
  83. return [
  84. {**row, "Kunde": calculate_customer(row["ClientName"])}
  85. for row in sorted(newest_rows.values(), key=lambda row: row["ParticipantName"].casefold())
  86. ]
  87. def find_xml_files(directory: Path) -> list[Path]:
  88. return sorted(
  89. path
  90. for path in directory.rglob("*")
  91. if path.is_file() and path.suffix.lower() == ".xml" and not path.name.lower().startswith("sessions")
  92. )
  93. def run(directory: Path, output: Path) -> tuple[int, int]:
  94. if not directory.is_dir():
  95. raise ValueError(f"Das Verzeichnis existiert nicht: {directory}")
  96. xml_files = find_xml_files(directory)
  97. new_rows = [row for xml_file in xml_files for row in extract_clients(xml_file)]
  98. rows = merge_clients(read_clients_csv(output), new_rows)
  99. write_clients_csv(rows, output)
  100. return len(xml_files), len(rows)