Skip to content

Метод экспериментальных выборок, волны опроса и раунды Дельфи

Открывайте сессии опроса через часы или недели после регистрации, напоминайте участникам по электронной почте, SMS через Twilio или сообщением в Prolific и экспортируйте долю ответов и задержку.

Новое в v2.13.0

С блоком schedule: Potato открывает короткие сессии опроса после первого визита участника, а блок notifications: отправляет сообщение по электронной почте, SMS или через Prolific, когда открывается очередная сессия. Исследователи опросов используют повторные волны в лонгитюдных панелях, психологи и исследователи здоровья применяют метод экспериментальных выборок в дневниковых исследованиях и исследованиях настроения, а экспертные панели проводят раунды Дельфи, чтобы прийти к согласию о критериях или рекомендациях. Для каждой сессии Potato записывает ответы, время отправки сигнала, время, когда участник впервые коснулся формы, и итог сессии: ответ получен, срок истёк или сессия пропущена.

Метод экспериментальных выборок (ESM) несколько раз в день задаёт участникам одни и те же короткие вопросы о моменте, в который пришёл сигнал. В исследованиях здоровья тот же дизайн называют экологической моментальной оценкой (EMA). В методе Дельфи панель экспертов оценивает одни и те же пункты в течение нескольких раундов и перед каждой новой оценкой видит анонимные ответы панели за предыдущий раунд.

  • Часовые пояса. Каждый участник выбирает часовой пояс при регистрации, и страница регистрации подставляет пояс браузера. Время по часам, например "09:00", отсчитывается в поясе участника для каждой местной даты.
  • Каналы. Сообщения уходят по электронной почте через любой SMTP-сервер, по SMS через Twilio или во входящие участника на Prolific. Учётные данные читаются только из переменных окружения.
  • Контактные данные. Potato спрашивает адрес электронной почты или номер телефона только после ответа «да» на вопрос о согласии и хранит адреса вне выходного каталога и вне любых экспортов.

Регистрация и план сессий

Каждый участник проходит эти шаги один раз:

  1. Участник проходит обычный сценарий из согласия, инструкций, разметки и опросов.
  2. В конце сценария страница регистрации запрашивает часовой пояс, а адрес электронной почты или номер мобильного телефона запрашивает только после ответа «да» на вопрос о согласии. Участники, отсеянные на отборе, не прошедшие обучение или заблокированные проверками внимательности, не регистрируются.
  3. В этот момент Potato вычисляет все сессии и все напоминания для участника и сохраняет план.
  4. Фоновый цикл отправляет каждое сообщение в назначенное время со ссылкой для возврата, которая выполняет вход участника и открывает сессию.
  5. Участник, вернувшийся по ссылке исследования, попадает в открытую сессию или на страницу статуса, где видно, когда откроется следующая сессия, по времени его собственного часового пояса.

План и часовой пояс фиксируются при регистрации. Если позже изменить waves, план изменится только для участников, зарегистрировавшихся после этого. Участник, отказавшийся от связи, всё равно регистрируется, не получает сообщений и находит открытые сессии, возвращаясь по ссылке исследования.

Конфигурация

Конфигурация ниже запускает семидневное исследование по методу экспериментальных выборок с пятью сигналами в день в случайное время между 09:00 и 21:00. Обычный сценарий состоит из страницы согласия и одного вступительного вопроса, и ответ на этот вопрос регистрирует участника:

yaml
annotation_task_name: "Daily mood study"
task_dir: .
output_annotation_dir: annotation_output
data_files: [data/intake.jsonl]
item_properties: {id_key: id, text_key: text}
user_config:
  allow_all_users: true
 
phases:
  order: [consent, annotation]
  consent:
    type: consent
    file: surveys/consent.json
  annotation:
    type: annotation
 
annotation_schemes:            # the intake questions, answered once
  - annotation_type: likert
    name: usual_mood
    description: "In general, how would you describe your mood?"
    size: 7
    min_label: "Very low"
    max_label: "Very good"
 
schedule:
  timezone: "America/Detroit"  # used when the browser reports no time zone
  seed: "daily-mood-2026"
  experience_sampling:
    title: "How are you right now?"
    days: 7
    start_day: 1               # first prompts on the day after enrollment
    daily_window: "09:00-21:00"
    signal: {per_day: 5, min_gap: 90m}
    expires_after: 30m
    questions:
      - {name: mood, annotation_type: likert, size: 7, required: true,
         description: "How do you feel right now?",
         min_label: "Very bad", max_label: "Very good"}
      - {name: company, annotation_type: radio, required: true,
         description: "Who are you with?",
         labels: ["Alone", "Family", "Friends", "Colleagues", "Strangers"]}
    reminders:
      - at: open
      - after_open: 15m
 
notifications:
  dry_run: true                # pilot: render and log every message, send none
  base_url: "https://mood-study.example.org"
  channels:
    - type: log
    - type: twilio
      account_sid_env: TWILIO_ACCOUNT_SID
      auth_token_env: TWILIO_AUTH_TOKEN
      from_number: "+12025550143"
  contact:
    consent: {question: contact_consent, value: "Yes"}

Два напоминания отправляют сигнал в момент его открытия и второе сообщение через 15 минут тем участникам, кто ещё не ответил. С dry_run: true Potato формирует и адресует каждое сообщение Twilio и показывает каждое сообщение на странице администратора, ничего не доставляя. Установите dry_run: false, когда пилот завершён. Случайное время сигналов выводится из seed, идентификатора участника и даты. Для каждого участника выборка времени независима, и её можно воспроизвести позже.

Страница согласия в surveys/consent.json задаёт вопрос, который указан в notifications.contact.consent:

json
[
  {"name": "study_consent", "annotation_type": "radio",
   "description": "I agree to answer short surveys five times a day for seven days.",
   "labels": ["I agree", "I do not agree"],
   "label_requirement": {"required_label": ["I agree"]}},
  {"name": "contact_consent", "annotation_type": "radio",
   "description": "May we text you when each survey opens? Your number is kept apart from your answers and deleted when the study ends.",
   "labels": ["Yes", "No"],
   "label_requirement": {"required": true}}
]

При запуске Potato проверяет, что contact_consent задаётся на странице одной из фаз и что Yes входит в варианты ответа на этот вопрос. Заключайте "Yes" в кавычки в YAML, потому что YAML 1.1 читает голое Yes как логическое значение, а значение согласия true или false не проходит проверку. В value можно также указать список ответов, разрешающих связь.

Параметры метода экспериментальных выборок

Ключ experience_samplingПо умолчаниюЗначение
daysобязателенЧисло дней выборки, от 1 до 366
start_day1Первый день выборки, считая от дня регистрации; 0 означает сам день регистрации
daily_window"09:00-21:00"Местное окно, в которое попадают сигналы
signal.per_dayобязателен вместе с signalЧисло случайных сигналов в день, от 1 до 48
signal.min_gap60mНаименьший промежуток между двумя сигналами, не меньше expires_after
interval.everyнетСигнал в начале окна и затем через каждый интервал после начала
interval.timesнетФиксированное местное время по часам, например ["10:00", "14:00", "20:00"]
expires_after30mСколько каждый сигнал остаётся открытым, не меньше одной минуты
questions, questions_fileобязателен один из нихВопросы сигнала (см. раздел о вопросах сессий ниже)
reminders[{at: open}]at: open, after_open и before_close. before_open отклоняется
title"Short survey"Показывается на странице сигнала и в сообщениях
introнетТекст над вопросами

Исследование использует ровно один из блоков: signal (по случайным сигналам, в случайное время) или interval (по интервальному протоколу, в фиксированное время). Блок interval принимает ровно один из ключей every или times. Длительности записываются как 30m, 48h, 7d или 2w либо как число секунд.

План, который не помещается в дневное окно, не проходит проверку. Пяти сигналам с промежутком 90 минут нужны четыре промежутка по 90 минут плюс 30 минут, в течение которых последний сигнал остаётся открытым, то есть 6 часов 30 минут окна. Девяти сигналам с тем же промежутком нужно 12 часов 30 минут, и в 12-часовом окне они не проходят проверку. Так же не проходят проверку interval.every длиннее окна и фиксированные моменты времени, стоящие ближе друг к другу, чем expires_after.

Для каждого дня Potato размещает дневное окно на этой местной дате в часовом поясе участника. Поэтому в день перевода часов окно всё равно идёт с 09:00 до 21:00 по местному времени. Случайное время выбирается равномерно среди всех расстановок, при которых сигналы отстоят друг от друга не меньше чем на min_gap, а последний сигнал закрывается внутри окна. Страница статуса показывает окно дня, но никогда не показывает время следующего случайного сигнала. Сигналы, время которых уже прошло на момент регистрации, не включаются в план.

Волны опроса

Лонгитюдная панель использует waves. Фрагмент ниже заменяет schedule в конфигурации выше и добавляет повторные волны через неделю и через месяц:

yaml
schedule:
  timezone: "America/Detroit"
  waves:
    - name: week1
      title: "One-week follow-up"
      offset: 7d
      at: "09:00"              # local clock time on day 7
      window: 48h
      questions:
        - {name: changed_view, annotation_type: radio, required: true,
           description: "Since last week, has your view changed?",
           labels: ["No", "A little", "A lot"]}
      reminders:
        - before_open: 12h
        - at: open
        - before_close: 12h
      completion_code: "FOLLOWUP-DONE"
    - name: month1
      offset: 30d
      at: "09:00"
      window: 72h
      questions_file: surveys/followup.json

С at каждая волна открывается в 09:00 по местному времени в заданный день, даже если между регистрацией и этим днём переводили часы, и тогда offset должен быть целым числом дней. Без at значение offset: 7d означает семь суток прошедшего времени, и переход на летнее или зимнее время сдвигает местное время открытия на час. Заключайте время по часам, например "09:00", в кавычки. YAML 1.1 читает 10:30 без кавычек как число 630, а Potato читает 630 как минуты после полуночи.

Ключ wavesПо умолчаниюЗначение
nameобязателенКороткий идентификатор длиной до 40 символов из букв, цифр, _ или -, используется в экспорте
titleзначение nameПоказывается участникам и в сообщениях
offset0Время после регистрации
atнетМестное время по часам в день, наступающий через offset дней после регистрации
windowобязателенСколько волна остаётся открытой, не меньше одной минуты
questions, questions_fileобязателен один из нихВопросы волны
reminders[{at: open}]Когда уходят сообщения
completion_codeнетКод, который показывается после того, как участник отправил ответы волны, например чтобы получить оплату во втором исследовании на Prolific
introнетТекст над вопросами

Раунды Дельфи

Исследование по методу Дельфи использует delphi. Фрагмент ниже заменяет schedule в конфигурации для метода экспериментальных выборок, а data/criteria.json содержит список пунктов {"id", "text"}:

yaml
schedule:
  timezone: "UTC"
  delphi:
    title: "Guideline criteria"
    items_file: data/criteria.json
    scale: {min: 1, max: 9, min_label: "Not important", max_label: "Essential"}
    comments: true
    max_rounds: 3
    round_length: 72h
    gap: 12h
    start: "2026-11-16T09:00:00-05:00"
    min_panel: 3
    stop:
      iqr_at_most: 1
      changed_share_below: 0.15
    reminders:
      - at: open
      - before_close: 24h

Раунды общие для всей панели. Раунд 1 открывается в момент start, а если start не задан, то при первом запуске сервера с этой конфигурацией, и Potato сохраняет это время между перезапусками. Каждый следующий раунд открывается через gap после закрытия предыдущего. Эксперт, зарегистрировавшийся во время раунда 2, присоединяется к раунду 2 и видит обратную связь по раунду 1 без собственной оценки. Эксперт, отказавшийся от сообщений, по-прежнему получает каждый раунд, но без напоминаний.

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

  • Медиана и средняя половина оценок (от Q1 до Q3) в виде затенённой полосы на шкале, с отметкой собственной оценки эксперта.
  • Число оценок.
  • Комментарии других экспертов без имён, в порядке, фиксированном для каждого эксперта. Собственный комментарий эксперту не показывается.

Пункт, который оценили меньше min_panel экспертов, не показывает ни сводки, ни комментариев. Этот порог не позволяет связать оценки небольшой панели с конкретным человеком. Оценка текущего раунда никогда не заполняется заранее.

Когда раунд закрывается по окончании своего окна или когда администратор досрочно закрывает раунд на /admin/schedule, Potato вычисляет для каждого пункта n, медиану, среднее, Q1, Q3 и межквартильный размах (IQR). Квартили вычисляются линейной интерполяцией между порядковыми статистиками; в R этот метод называется type 7, в NumPy он используется по умолчанию. Начиная с раунда 2, Potato также вычисляет median_change и changed_share. Значение median_change равно абсолютному изменению медианы, а changed_share равно доле экспертов, которые оценили пункт в обоих раундах и изменили оценку. Пункт считается решённым, когда он удовлетворяет хотя бы одному заданному критерию:

Ключ stopВидПункт решён, когда
iqr_at_mostсогласиеIQR не превышает значения
median_change_at_mostстабильность, начиная с раунда 2Медиана сдвинулась с предыдущего раунда не больше чем на значение
changed_share_belowстабильность, начиная с раунда 2Доля экспертов, изменивших оценку, ниже значения, от 0 до 1

Исследование останавливается после первого раунда, в котором решены все пункты, или после max_rounds. Без блока stop исследование проходит все max_rounds раундов. Причина остановки показывается на странице статуса экспертов и на странице администратора.

Ключ delphiПо умолчаниюЗначение
items, items_fileобязателен один из них[{id, text}] прямо в конфигурации или файл JSON, JSONL или YAML
scale{min: 1, max: 9}Целочисленная шкала оценок не больше чем из 21 деления, с необязательными min_label и max_label
commentstrueНеобязательный комментарий к каждому пункту длиной до 2 000 символов, показывается панели без имён
max_rounds3Верхний предел числа раундов, от 1 до 10
round_lengthобязателенСколько открыт каждый раунд
gap0Время между закрытием одного раунда и открытием следующего
startпервый запуск сервераКогда открывается раунд 1, в формате ISO 8601 со смещением от UTC
min_panel3Сколько оценок нужно, чтобы показать сводку и комментарии по пункту
stopнетКритерии остановки
reminders[{at: open}]Когда уходят сообщения
title"Delphi round"Показывается на странице раунда, за ним следует номер раунда
introнетТекст над пунктами

Вопросы сессий

Вопросы волн и сигналов записываются так же, как схемы разметки: прямо в questions или в JSON- или YAML-файле questions_file, путь к которому отсчитывается от файла конфигурации. Каждый вопрос принимает name, annotation_type, description и либо required: true, либо label_requirement: {required: true}. Шесть типов вопросов принимают такие ключи:

ТипКлючи и значения по умолчанию
radiolabels (не меньше двух), horizontal
multiselectlabels (не меньше двух)
likertsize (от 2 до 11), min_label, max_label
slidermin_value (0), max_value (100), starting_value (значение min_value), step (1), min_label, max_label
numbermin_value, max_value, integer
textmultiline (true), max_length (1000, не больше 4 000)

Ключ вне списка для данного типа не проходит проверку. В отличие от схемы разметки slider, slider в сессии принимает min_label и max_label. Ползунок считается отвеченным только после того, как участник его сдвинул, а сервер отклоняет ответы вне вариантов или диапазона вопроса. Potato хранит ответы сессий по сессиям в <output_annotation_dir>/scheduling/schedule.sqlite, а не в user_state.json. В конфигурации выше один участник может ответить на каждый вопрос сигнала 35 раз, по разу на сигнал.

Напоминания

Каждая волна, блок метода экспериментальных выборок и блок Дельфи принимают список reminders, составленный из четырёх правил:

ПравилоКогда уходит сообщение
{at: open}Когда открывается окно
{before_open: 12h}За 12 часов до открытия окна
{after_open: 2h}Через 2 часа после открытия окна, если на сессию ещё не ответили
{before_close: 12h}За 12 часов до закрытия окна, если на сессию ещё не ответили

Potato пропускает напоминание, если на сессию уже ответили или она закрыта, и пропускает напоминание before_open, если сессия уже открылась. Напоминания, срок которых наступил до регистрации участника, не отправляются. Смещение after_open или before_close должно быть короче окна. Метод экспериментальных выборок не принимает before_open, потому что такое сообщение заранее объявило бы о случайном сигнале.

Уведомления

Фрагмент ниже заменяет notifications в конфигурации выше и отправляет сообщения по электронной почте и во входящие Prolific, со своим текстом для сигналов:

yaml
notifications:
  base_url: "https://mood-study.example.org"
  channels:
    - type: log
    - type: smtp
      host: smtp.example.org
      port: 587
      security: starttls        # starttls, ssl, or none
      username_env: POTATO_SMTP_USER
      password_env: POTATO_SMTP_PASSWORD
      from: "Mood study <mood-study@example.org>"
    - type: prolific
      token_env: PROLIFIC_API_TOKEN
  templates:
    prompt:
      subject: "{study}: a short survey is open"
      body: "Please answer within {time_left}: {link}\n\nStop messages: {stop_link}"
  contact:
    consent: {question: contact_consent, value: "Yes"}
    delete_after_study: true

Каждый тип канала может встречаться один раз:

КаналКуда доставляетКлючи и значения по умолчанию
logВ журнал сервера и на страницу администратора, с полным текстом и ссылками. Участникам ничего не доставляетнет
smtpНа адрес электронной почты, указанный при регистрацииhost (обязателен), from (обязателен), security (starttls), port (587, или 465 при ssl), username_env и password_env (оба или ни одного), timeout (20 секунд)
twilioНа номер мобильного телефона, указанный при регистрации, по SMSaccount_sid_env (TWILIO_ACCOUNT_SID), auth_token_env (TWILIO_AUTH_TOKEN), ровно один из from_number (номер в формате E.164) или messaging_service_sid_env, timeout (20)
prolificВо входящие участника на Prolific, по идентификатору участника Prolifictoken_env (PROLIFIC_API_TOKEN), study_id (STUDY_ID участника, если не задан), timeout (30)

Канал Prolific доходит только до участников, пришедших из Prolific. Potato читает каждую переменную, названную в ключе *_env, в момент отправки сообщения. Potato останавливается при запуске, если YAML содержит учётные данные под ключом вроде password, token или account_sid. Отсутствующая переменная записывается в журнал при запуске, и затем каждое сообщение в этом канале завершается ошибкой с именем переменной. Задайте переменные в окружении сервера до запуска Potato:

bash
export POTATO_SMTP_USER="mood-study@example.org"
export POTATO_SMTP_PASSWORD="<your SMTP password>"
export PROLIFIC_API_TOKEN="<your Prolific API token>"
Ключ notificationsПо умолчаниюЗначение
dry_runfalseФормирует и адресует каждое сообщение в каждом канале и записывает каждое сообщение как dry_run, не доставляя его
base_urlhttp://localhost:<port>Публичный адрес, используемый в ссылках. Обязателен для любого канала, кроме log, если не включён dry_run
channelsнетКаналы, описанные выше
contact.consentобязателен при наличии каналов{question, value}: вопрос о согласии на странице фазы и ответ, разрешающий связь
contact.delete_after_studytrueУдаляет контактные данные участника, когда у участника не остаётся сессий
templatesвстроенныеТекст сообщения для каждого вида

Виды шаблонов: before_open, open, reminder (для after_open и before_close), prompt (первое сообщение сигнала в методе экспериментальных выборок) и delphi_round. Каждый вид принимает subject и body. Подстановки: {study}, {session}, {opens}, {closes}, {time_left}, {link} и {stop_link}. Potato показывает {opens} и {closes} в часовом поясе участника и останавливается при запуске, если шаблон называет неизвестную подстановку. SMS использует только тело сообщения, до предела Twilio в 1 600 символов, а сообщение в Prolific объединяет тему и тело.

Перед отправкой Potato закрепляет каждое сообщение в базе данных. Поэтому Potato не отправляет ни одно сообщение дважды, даже после перезапуска или из второго серверного процесса. Сообщение, прерванное во время отправки, помечается как неудачное и повторно не отправляется. После временной ошибки Potato пробует снова через 5, а затем через 10 минут, пока сессия ещё открыта, не больше трёх попыток в сумме. Временными ошибками считаются ошибка соединения SMTP и ответ 429 или 5xx от Twilio или Prolific. Остальные ошибки сразу приводят к неудаче.

Общие настройки расписания

Ключ scheduleПо умолчаниюЗначение
timezoneUTCЧасовой пояс IANA, заранее выбранный на странице регистрации, если браузер не сообщает свой
seedпустоТекст, подмешиваемый в случайное время сигналов и в порядок комментариев Дельфи
poll_seconds30Как часто выполняется фоновый цикл, от 1 до 3 600 секунд
schedulertrueЗапускает фоновый цикл вместе с сервером
waves, experience_sampling, delphiнетОбязателен хотя бы один

Фоновый цикл отправляет сообщения, срок которых наступил, открывает и закрывает раунды Дельфи и удаляет контактные данные участников, завершивших исследование. С scheduler: false ничего не отправляется, пока администратор не нажмёт Send due messages now на /admin/schedule или не отправит POST-запрос на /admin/api/schedule/tick. Блок notifications без блока schedule не проходит проверку.

Ссылки для возврата и контактные данные

Ссылка для возврата (/s/<token>) выполняет вход участника, открывает одну сессию и перестаёт действовать, когда сессия закрывается. Ссылки подписываются HMAC-SHA256 ключом из POTATO_SCHEDULE_LINK_KEY, а если переменная не задана, то случайным ключом, один раз созданным в <task_dir>/.potato/scheduling/link.key. Задайте переменную, если сервер может перезапуститься на чистом диске. Когда участник отказывается от сообщений, Potato аннулирует все ссылки, отправленные ему ранее.

Адреса электронной почты и номера телефонов хранятся в <task_dir>/.potato/scheduling/contacts.sqlite с правами доступа 0600, вне выходного каталога разметки. Ни один экспортер, архив данных или пакет развёртывания не читает хранилище контактов, а журнал уведомлений записывает канал и никогда не записывает адрес. Хостинг, который стирает диск при каждом перезапуске, теряет адреса, и участники на таком хостинге больше не получают сообщений.

Участники могут отказаться от сообщений на странице статуса или по ссылке для отказа, которая есть в каждом сообщении. Ссылка для отказа открывает страницу подтверждения, и удаляет данные только кнопка на этой странице. Поэтому почтовый сканер, открывший ссылку, никого не отписывает. С delete_after_study: true данные удаляются, когда у участника не остаётся открытых или предстоящих сессий, а в исследовании Дельфи данные хранятся, пока панель не завершит работу.

Пропущенные и истёкшие сессии

Сессия закрывается в конце своего окна, и сервер отклоняет ответы, отправленные после закрытия. Potato записывает закрытую сессию в одном из трёх состояний:

  • answered означает, что участник отправил ответы сессии до закрытия.
  • expired означает, что участник открыл сессию или начал отвечать, но не отправил ответы до закрытия.
  • missed означает, что участник ни разу не открыл сессию, пока она была открыта.

Potato не переносит закрытую сессию, а ожидающие напоминания для неё пропускаются. Участник, открывший старую ссылку, видит, когда сессия закрылась и когда откроется следующая, если только следующая сессия не случайный сигнал. Доля ответов (compliance) равна числу отвеченных сессий, делённому на число закрытых сессий, для каждого участника и каждого вида сессий.

Страница администратора

Для /admin/schedule нужен ключ API администратора или роль администратора в RBAC. Страница показывает для каждого участника часовой пояс, согласие, долю ответов, медианную задержку и следующую сессию, а также перечисляет все сообщения по статусу, с полным текстом для сообщений log и dry_run. Раунды Дельфи показываются со статистикой по пунктам. Кнопки на странице соответствуют POST-эндпоинтам ниже:

ЭндпоинтМетодДействие
/admin/api/scheduleGETДанные страницы в формате JSON, без адресов
/admin/api/schedule/tickPOSTСразу отправляет сообщения, срок которых наступил
/admin/api/schedule/delphi/close_roundPOSTЗакрывает открытый раунд Дельфи
/admin/api/schedule/contact/<participant>/deletePOSTУдаляет контактные данные одного участника

Экспорт

Ответы сессий не являются разметкой объектов, и основной экспорт их не включает. Вместо этого экспорт schedule записывает отдельные таблицы, и в каждой таблице есть столбец participant с тем же идентификатором, что и user_id в основном экспорте:

bash
python -m potato.export --config config.yaml --format schedule --output out/
ФайлСтроки
schedule_sessions.csvПо одной на участника и сессию, плюс одна строка enrolment на участника для обычного сценария
schedule_compliance.csvНа участника и вид сессии, со столбцами sessions, closed, answered, expired, missed, compliance, median_latency_seconds
schedule_notifications.csvКаждое запланированное сообщение, со столбцами session_id, rule, template, channel, due_at_utc, status, attempts, sent_at_utc, detail. Без адреса и без текста сообщения
delphi_ratings.csvТолько для Дельфи, со столбцами participant, round, item, rating, comment
delphi_rounds.csvТолько для Дельфи, на раунд и пункт, со столбцами n, median, q1, q3, iqr, mean, median_change, changed_share, settled, opens_at_utc, closed_at_utc, round_status, stop_reason

Оценки Дельфи попадают в delphi_ratings.csv, а сессии раундов появляются в schedule_sessions.csv со статусом и задержкой. Столбцы schedule_sessions.csv:

СтолбецЗначение
kindwave, prompt, round или enrolment
session_id, wave, day, prompt, round, titleКакой сессии соответствует строка. day — номер дня выборки, начиная с 1, а prompt — номер сигнала в этот день
timezoneЧасовой пояс участника
opens_at_utc, closes_at_utc, opens_at_local, closes_at_localОкно в UTC и по местному времени
statusanswered, expired, missed, open, started или upcoming (enrolled в строке регистрации)
prompt_at_utcКогда ушло первое сообщение или, если сообщение не отправлялось, начало окна
opened_at_utc, first_answer_at_utc, submitted_at_utcКогда участник открыл страницу, впервые коснулся формы и отправил ответы
latency_secondsОт prompt_at_utc до первого ответа
response_secondsОт первого ответа до отправки
reminder_channelsКаналы, по которым участник получает сообщения, в строке регистрации
answer.<name>По одному столбцу на вопрос; ответы multiselect объединяются через ;

Пример

bash
python potato/flask_server.py start examples/advanced/experience-sampling/config.yaml -p 8000

Пример запускает описанный выше план выборки с каналом log и каналом Twilio в режиме пробного прогона. Страница администратора /admin/schedule показывает ссылку для возврата для каждого сигнала. В папках examples/advanced/two-wave-panel/ и examples/advanced/delphi-study/ лежат панель с повторной волной через неделю и исследование Дельфи из трёх раундов.

Дополнительные материалы

Подробности реализации смотрите в исходной документации.