import logging from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any logger = logging.getLogger(__name__) def _extract_uid(ical_data) -> str: raw = ical_data if isinstance(raw, bytes): raw = raw.decode("utf-8", errors="replace") for line in str(raw).split("\r\n"): if line.upper().startswith("UID:"): return line[4:].strip() return "" def _find_event_by_uid(calendar: Any, uid: str) -> Any | None: try: for event in calendar.events(): raw = event.data if isinstance(raw, bytes): content = raw.decode("utf-8", errors="replace") else: content = str(raw) for line in content.split("\r\n"): if line.upper().startswith("UID:"): if line[4:].strip() == uid: return event except Exception as exc: logger.error("Error scanning events for UID %s: %s", uid, exc) return None def _add_event(calendar: Any, uid: str, ical_data: bytes) -> bool: try: calendar.add_event(ical_data) return True except Exception as exc: logger.error("Failed to add event %s: %s", uid, exc) return False def _delete_event(calendar: Any, uid: str) -> bool: try: event = _find_event_by_uid(calendar, uid) if event: event.delete() return True logger.error("Event with UID %s not found", uid) return False except Exception as exc: logger.error("Failed to delete event %s: %s", uid, exc) return False def _update_event(calendar: Any, uid: str, ical_data: bytes) -> bool: if not _delete_event(calendar, uid): return False return _add_event(calendar, uid, ical_data) def apply_adds(calendar: Any, events: dict[str, bytes], max_workers: int = 10) -> tuple[int, int]: success = 0 errors = 0 with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = { executor.submit(_add_event, calendar, uid, data): uid for uid, data in events.items() } for future in as_completed(futures): if future.result(): success += 1 else: errors += 1 return success, errors def apply_updates(calendar: Any, events: dict[str, bytes], max_workers: int = 10) -> tuple[int, int]: success = 0 errors = 0 with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = { executor.submit(_update_event, calendar, uid, data): uid for uid, data in events.items() } for future in as_completed(futures): if future.result(): success += 1 else: errors += 1 return success, errors def apply_deletes(calendar: Any, uids: list[str], max_workers: int = 10) -> tuple[int, int]: success = 0 errors = 0 with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = { executor.submit(_delete_event, calendar, uid): uid for uid in uids } for future in as_completed(futures): if future.result(): success += 1 else: errors += 1 return success, errors