Импорт расписания событий через CSV
Когда событий нужно завести много разом — сезон еженедельных заездов, план на квартал — они загружаются файлом из ACP: События → Импорт расписания. Панель разбирает файл на глазах у оператора, показывает построчный разбор до создания и отчёт после.
Формат файла
Первая строка — заголовок с названиями колонок. Порядок любой, колонки, которых импорт не знает, просто игнорируются: рабочий файл планирования со своими пометками загружается как есть.
type,start_time,finish_time,group_size,data:target_score,attr:season
weekly_race,2026-10-01T00:00:00Z,2026-10-08T00:00:00Z,50,1500,autumn
weekly_race,2026-10-08T00:00:00Z,2026-10-15T00:00:00Z,50,1600,autumn| Колонка | Обязательна | Что значит |
|---|---|---|
type | да | тип события, из типов проекта |
start_time | да | начало |
processing_time | по типу | фаза подсчёта |
rewarding_time | по типу | фаза выдачи наград |
finish_time | по типу | конец; без него событие идёт бесконечно |
group_size | по типу | размер группы |
data:<параметр> | по типу | параметр этого типа события |
attr:<ключ> | нет | произвольный атрибут |
«По типу» значит, что колонка обязательна ровно тогда, когда тип события требует это поле: конфигурация проекта перечисляет для каждого типа его фазы (require / optional) и его параметры (data). Строка проверяется по схеме своего типа, поэтому в одном файле можно планировать разные типы: колонка data:, которой у типа строки нет, для неё просто не читается.
Значение в data: приводится к тому виду, который объявил проект: число — к числу, переключатель — к true/false (понимаются true/1/yes/да, false/0/no/нет), параметр с несколькими значениями — к списку через ;. Слово в числовой колонке останавливает строку, а не уезжает в базу строкой. У attr: схемы нет, поэтому значение читается как число или true/false, когда ячейка явно ими является, иначе как текст.
Колонка data:, называющая параметр, которого нет ни у одного типа событий проекта, пропускается, и панель говорит об этом один раз по заголовку, а не тысячей одинаковых отказов по строкам.
Время
Принимаются 2026-10-01T00:00:00Z, 2026-10-01T03:00:00+03:00, 2026-10-01 10:30 и 2026-10-01. Ячейка без указания зоны читается как UTC — так же, как панель хранит время. В таблице предпросмотра показан уже разобранный момент, поэтому час, на который событие реально встанет, видно до создания.
Разделитель определяется сам (,, ;, табуляция, |) по строке заголовка.
Что не создастся
Строка не выбрасывается молча — она остаётся в таблице с причиной: нет типа, неизвестный тип, нет начала, время не читается, не заполнена обязательная фаза, фазы идут не по порядку, не указан или некорректен размер группы, не заполнен или некорректен обязательный параметр, дубликат. Дубликатом считается повтор пары тип+начало; побеждает первая строка.
Порядок фаз — start_time ≤ processing_time ≤ rewarding_time ≤ finish_time — проверяется и в панели, и на сервере: событие, которое заканчивается раньше, чем началось, не создаётся ни одним из путей.
Пересечения
Если событие идёт одновременно с другим событием того же типа — уже запланированным или соседней строкой этого же файла — панель говорит об этом и требует явного подтверждения, как и при создании одного события. Это предупреждение, а не запрет: иногда так и задумано. Событие без finish_time идёт бесконечно и поэтому пересекается со всем, что стартует после него.
Ограничения
За один импорт — не больше 10 000 событий. Панель режет файл на запросы по 500 строк и шлёт их последовательно, показывая прогресс.
Повторная загрузка ничего не создаёт дважды
Каждый импорт получает batch_key — имя одного решения оператора. Все запросы одного файла несут его, а база отказывается создать второе событие того же типа с тем же началом под тем же ключом.
Отсюда следует главное: повторить импорт всегда безопасно. Оборвалось соединение на середине, оператор нажал дважды, перезагрузил страницу и положил тот же файл снова — создастся только то, что не дошло. Разные импорты — разные ключи, поэтому запланировать такой же сезон ещё раз можно обычным способом. События, созданные по одному, ключа не несут, и на них это не влияет.
Отчёт
После импорта показываются счётчики — создано, уже было создано, отклонено сервером, пропущено в файле — и отчёт выгружается в CSV: каждая строка исходного файла с тем, что с ней стало.
API
POST /acp/events/bulk-create — один чанк импорта. Требует права AccessEvent.
{
"batch_key": "events-20260923T120000-a1b2c3",
"events": [
{
"type": "weekly_race",
"start_time": "2026-10-01T00:00:00Z",
"finish_time": "2026-10-08T00:00:00Z",
"group_size": 50,
"data": { "target_score": 1500 },
"attributes": { "season": "autumn" }
}
]
}batch_key обязателен, в одном запросе не больше 1000 событий. Ответ — отчёт по этому чанку:
{
"report": {
"requested": 1,
"created": 1,
"already_exists": 0,
"invalid": 0,
"rows": [
{ "type": "weekly_race", "start_time": "2026-10-01T00:00:00Z", "outcome": "created" }
]
}
}outcome принимает значения created, already_exists (событие этого типа с этим началом уже создано под этим batch_key) и invalid — у последнего в reason написано, что именно не так. Отклонённая строка не останавливает остальной чанк.
