Skip to content

Источник ICS: три пути и план на случай отказа

Главный организационный риск проекта: доступа к корпоративному календарю могут не дать. Здесь — как получить ICS и почему отказ не блокирует работу.

Почему именно ICS

.ics (iCalendar, RFC 5545) — открытый формат, который понимают все: Outlook/Exchange, Teams, Google Calendar, Яндекс, Apple. Он же выдаётся при экспорте и при публикации календаря по ссылке.

Работать через ICS-файл, а не через API конкретного вендора — сознательный выбор: не привязываемся к Microsoft Graph, а значит не нужны регистрация приложения в Azure AD, согласие администратора и OAuth-токены. Для стажёра это разница между «сделал за вечер» и «жду решения ИБ три месяца».

Три пути получить ICS

Путь 1 · Публикация личного календаря (пробовать первым)

Outlook Web → КалендарьПараметрыОбщий доступ и разрешенияОпубликовать календарь → выбрать уровень детализации → получить ссылку .ics.

  • Плюс: это ваш личный календарь, а не корпоративный ресурс. Часто разрешено.
  • Минус: только ваши встречи, не все брони переговорки.
  • Риск: функцию нередко отключают на уровне тенанта — тогда кнопки просто нет.

Проверяется за две минуты. Если кнопка есть — дальше всё просто.

Уровень детализации. Достаточно «Только занят/свободен» — тем и лучше: тем времени и организаторов в данных нет, а для метрики они и не нужны. Это же снимает часть вопросов о приватности (см. ниже).

Путь 2 · Календарь ресурса (правильный, но через админа)

В Exchange переговорка — это room mailbox со своим календарём, где лежат все её брони. Именно он нужен по-хорошему.

Доступ даёт администратор: делегирование прав на почтовый ящик ресурса либо публикация его календаря.

  • Плюс: полные данные — все брони помещения, а не одного человека.
  • Минус: для стажёра запрос почти наверняка уйдёт в ИБ.

Просить это стоит не сейчас, а после прототипа — см. ниже.

Путь 3 · Ручной экспорт (всегда доступен)

Outlook → ФайлСохранить календарь → формат iCalendar → диапазон дат.

Это снимок, а не синхронизация. Но для демонстрации метрики этого полностью достаточно: выгрузили за месяц, посчитали, показали результат.

Если не дадут вообще ничего

Проект всё равно делается — на генераторе правдоподобного расписания.

# gen_ics.py — синтетический календарь переговорки на месяц.
# Нужен, когда корпоративного доступа нет: парсер и метрика те же.
import random, datetime as dt

TZ = "Europe/Moscow"
random.seed(42)                     # воспроизводимость

def workdays(start, days):
    d = start
    while d < start + dt.timedelta(days=days):
        if d.weekday() < 5:         # пн–пт
            yield d
        d += dt.timedelta(days=1)

lines = ["BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//xSTEM//MeetingRoom//RU"]

# еженедельный статус — повторяющееся событие (RRULE): проверяет парсер
lines += [
    "BEGIN:VEVENT",
    "UID:weekly-standup@example.org",
    "DTSTART;TZID=%s:20260706T100000" % TZ,
    "DTEND;TZID=%s:20260706T103000" % TZ,
    "RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=12",
    "SUMMARY:Еженедельный статус",
    "END:VEVENT",
]

# разовые встречи
uid = 0
for day in workdays(dt.date(2026, 7, 6), 28):
    for _ in range(random.choice([0, 1, 1, 2, 3])):
        hour = random.choice([9, 11, 13, 14, 15, 16, 17])
        dur  = random.choice([30, 60, 60, 90])
        uid += 1
        s = dt.datetime.combine(day, dt.time(hour, 0))
        e = s + dt.timedelta(minutes=dur)
        lines += [
            "BEGIN:VEVENT",
            f"UID:evt-{uid}@example.org",
            f"DTSTART;TZID={TZ}:{s:%Y%m%dT%H%M%S}",
            f"DTEND;TZID={TZ}:{e:%Y%m%dT%H%M%S}",
            "SUMMARY:Встреча",
            "END:VEVENT",
        ]

lines.append("END:VCALENDAR")
open("meetingroom.ics", "w", encoding="utf-8").write("\r\n".join(lines))
print(f"событий: {uid + 1} (включая серию RRULE)")

Генератор намеренно включает и RRULE-серию, и разовые встречи — иначе парсер не будет проверен на главной сложности.

Честность в портфолио. Если данные синтетические — так и говорить. «Пайплайн работает на реальных данных датчика и синтетическом календаре, потому что корпоративный доступ требует согласования» — это нормальный инженерный ответ, который показывает и результат, и понимание ограничений. Выдавать сгенерированное за реальное нельзя.

Абстракция источника

Ключевое проектное решение: обработка не знает, откуда взялся ICS.

# .env проекта — меняется одна строка, код не трогается
ICS_SOURCE_TYPE=url        # url | file | generated
ICS_URL=https://outlook.office365.com/owa/calendar/.../reachcalendar.ics
ICS_FILE=/opt/meetingroom/calendar.ics
ICS_SYNC_INTERVAL=15m
ICS_WINDOW_DAYS=30         # окно синхронизации: см. «Синк и метрика»
ТипКогдаЧто делает
urlдали ссылку на публикациюскачивает по расписанию
fileручной экспортчитает локальный файл
generatedдоступа нетберёт вывод gen_ics.py

Все три отдают одну и ту же строку ICS дальше по конвейеру. Парсер, upsert и метрика — общие.

Стратегия запроса доступа

Порядок важнее формулировок.

Неправильно: просить доступ к календарю переговорки, не имея ничего. Это запрос «дайте данные, я что-нибудь придумаю» — уходит в ИБ и застревает.

Правильно: сначала прототип на экспорте или генераторе, потом разговор:

«Я собрал систему: датчик присутствия показывает фактическую занятость переговорной, календарь — плановую. За тестовый месяц получилось, что N% времени помещение числилось забронированным, но фактически пустовало. Для регулярного обновления нужна ссылка на публикацию календаря переговорной — данные только занят/свободен, без тем и участников.»

Разница принципиальная: во втором случае вы просите под готовый результат и сразу снимаете вопрос о приватности, ограничивая запрос до занят/свободен.

Приватность — сказать до того, как спросят

Вопрос возникнет обязательно, лучше ответить заранее.

Не нужноПочему
темы встречметрике безразлично
организатор и участникиперсональные данные
вложения, тело событияне используются

Нужно ровно три поля: начало, конец, факт брони. Уровень «занят/свободен» при публикации календаря даёт именно это.

В схеме bookings поля organizer и title есть — при работе с корпоративными данными их не заполнять. Так проект не превращается в слежку за сотрудниками, а остаётся аналитикой помещения.

Что дальше

Синк и метрика — разворачивание RRULE, идемпотентный upsert и SQL, который отвечает на главный вопрос.