import re from operator import attrgetter import arrow import bleach from django.conf import settings from ics import Calendar EVENT_KEYS = ("begin", "end", "all_day", "name", "description", "location") def remove_alarms(source): """Removes VALARM blocks from iCal source.""" return re.sub(r"(BEGIN:VALARM.*?END:VALARM\r?\n)", "", source, flags=re.S) def parse_ical(source): """Parses iCalendar source and returns events as list of dicts.""" # there is a bug in parsing alarms, but we don't need them, so we remove them source = remove_alarms(source) cal = Calendar(source) events = [] for event in sorted(cal.events, key=attrgetter("begin"), reverse=True): events.append({key: getattr(event, key) for key in EVENT_KEYS}) return events def split_events(events): """Splits events and returns list of past events and future events.""" singularity = arrow.utcnow().shift(hours=-2) past = [ev for ev in events if ev["end"] < singularity] future = [ev for ev in events if ev["end"] > singularity] return past, future def set_event_description(event): """Clears even description from unwanted tags.""" description: str = event.get("description", "") or "" event["description"] = bleach.clean(description, tags=["a", "br"], strip=True) return event def set_event_duration(event): """Sets duration for event.""" if event["all_day"]: event["duration"] = "celĂ˝ den" return event delta = event["end"] - event["begin"] if delta.days < 1: begin = event["begin"].to(settings.TIME_ZONE).format("H:mm") end = event["end"].to(settings.TIME_ZONE).format("H:mm") event["duration"] = f"{begin} - {end}" else: begin = event["begin"].to(settings.TIME_ZONE).format("H:mm") end = event["end"].to(settings.TIME_ZONE).format("H:mm (D.M.)") event["duration"] = f"{begin} - {end}" return event def process_event(event): """Processes single event for use in Majak""" event = set_event_duration(event) event = set_event_description(event) return event def process_ical(source): """Parses iCalendar source and returns events as list of dicts. Returns tuple of past and future events. """ events = parse_ical(source) events = list(map(process_event, events)) return split_events(events)