Code reference › domain › schedule

module

pretalx.schedule.domain.ical

Functions 7

build_slot_vevent(slot, calendar, *, creation_time=None, netloc=None)sourceAppend a VEVENT for *slot* to *calendar*.

No-op if the slot is incomplete.

def build_slot_vevent(slot, calendar, *, creation_time=None, netloc=None):
    """Append a VEVENT for *slot* to *calendar*. No-op if the slot is incomplete."""
    if not slot.start or not slot.local_end or not slot.room or not slot.submission:
        return
    import icalendar  # noqa: PLC0415 -- slow import

    creation_time = creation_time or dt.datetime.now(ZoneInfo("UTC"))
    netloc = netloc or get_netloc(slot.event)

    vevent = icalendar.Event()
    vevent.add(
        "summary",
        strip_control_characters(
            f"{slot.submission.title} - {slot.submission.display_speaker_names}"
        ),
    )
    vevent.add("dtstamp", creation_time)
    vevent.add("location", strip_control_characters(slot.room.name))
    vevent.add(
        "uid",
        f"pretalx-{slot.submission.event.slug}-{slot.submission.code}{slot.id_suffix}@{netloc}",
    )
    vevent.add("dtstart", slot.local_start)
    vevent.add("dtend", slot.local_end)
    vevent.add("description", strip_control_characters(slot.submission.abstract))
    vevent.add("url", slot.submission.urls.public.full())
    calendar.add_component(vevent)
get_calendar(event, prodid)sourceBuild an empty iCalendar tagged with -//pretalx//{netloc}//{prodid}.

prodid is used by calendar clients for de-duplication, so it should be stable for the same logical export and distinct between exports.

def get_calendar(event, prodid):
    """Build an empty iCalendar tagged with `-//pretalx//{netloc}//{prodid}`.

    `prodid` is used by calendar clients for de-duplication, so it should be
    stable for the same logical export and distinct between exports.
    """
    import icalendar  # noqa: PLC0415 -- slow import

    cal = icalendar.Calendar()
    cal.add("version", "2.0")
    cal.add("prodid", f"-//pretalx//{get_netloc(event)}//{prodid}")
    return cal
get_slot_ical(slot)source
def get_slot_ical(slot):
    cal = get_calendar(slot.event, slot.submission.code or slot.pk)
    build_slot_vevent(slot, cal)
    return cal
get_slots_ical(event, slots, prodid_suffix=None)source
def get_slots_ical(event, slots, prodid_suffix=None):
    prodid = event.slug
    if prodid_suffix:
        prodid = f"{prodid}//{prodid_suffix}"
    cal = get_calendar(event, prodid)
    creation_time = dt.datetime.now(ZoneInfo("UTC"))
    netloc = get_netloc(event)
    for slot in slots:
        build_slot_vevent(slot, cal, creation_time=creation_time, netloc=netloc)
    return cal
get_speaker_ical(event, speaker)source
def get_speaker_ical(event, speaker):
    return get_slots_ical(
        event, speaker.current_talk_slots, prodid_suffix=f"speaker//{speaker.code}"
    )
get_submission_ical(submission, slots)source
def get_submission_ical(submission, slots):
    return get_slots_ical(
        submission.event, slots, prodid_suffix=f"talk//{submission.code}"
    )
serialize_calendar(calendar)source
def serialize_calendar(calendar):
    if events := calendar.events:
        # icalendar includes 1970-2038 tz data by default, which is
        # *slightly* overkill for non-repeating event data
        timezone_range_padding = dt.timedelta(days=365)
        calendar.add_missing_timezones(
            first_date=(
                min(event.start for event in events) - timezone_range_padding
            ).date(),
            last_date=(
                max(event.end for event in events) + timezone_range_padding
            ).date(),
        )
        for timezone in calendar.timezones:
            # Drop useless comment
            timezone.pop("comment", None)
    return calendar.to_ical().decode()