Метод экспериментальных выборок, волны опроса и раунды Дельфи
Открывайте сессии опроса через часы или недели после регистрации, напоминайте участникам по электронной почте, SMS через Twilio или сообщением в Prolific и экспортируйте долю ответов и задержку.
Новое в v2.13.0
С блоком schedule: Potato открывает короткие сессии опроса после первого визита участника, а блок notifications: отправляет сообщение по электронной почте, SMS или через Prolific, когда открывается очередная сессия. Исследователи опросов используют повторные волны в лонгитюдных панелях, психологи и исследователи здоровья применяют метод экспериментальных выборок в дневниковых исследованиях и исследованиях настроения, а экспертные панели проводят раунды Дельфи, чтобы прийти к согласию о критериях или рекомендациях. Для каждой сессии Potato записывает ответы, время отправки сигнала, время, когда участник впервые коснулся формы, и итог сессии: ответ получен, срок истёк или сессия пропущена.
Метод экспериментальных выборок (ESM) несколько раз в день задаёт участникам одни и те же короткие вопросы о моменте, в который пришёл сигнал. В исследованиях здоровья тот же дизайн называют экологической моментальной оценкой (EMA). В методе Дельфи панель экспертов оценивает одни и те же пункты в течение нескольких раундов и перед каждой новой оценкой видит анонимные ответы панели за предыдущий раунд.
- Часовые пояса. Каждый участник выбирает часовой пояс при регистрации, и страница регистрации подставляет пояс браузера. Время по часам, например
"09:00", отсчитывается в поясе участника для каждой местной даты. - Каналы. Сообщения уходят по электронной почте через любой SMTP-сервер, по SMS через Twilio или во входящие участника на Prolific. Учётные данные читаются только из переменных окружения.
- Контактные данные. Potato спрашивает адрес электронной почты или номер телефона только после ответа «да» на вопрос о согласии и хранит адреса вне выходного каталога и вне любых экспортов.
Регистрация и план сессий
Каждый участник проходит эти шаги один раз:
- Участник проходит обычный сценарий из согласия, инструкций, разметки и опросов.
- В конце сценария страница регистрации запрашивает часовой пояс, а адрес электронной почты или номер мобильного телефона запрашивает только после ответа «да» на вопрос о согласии. Участники, отсеянные на отборе, не прошедшие обучение или заблокированные проверками внимательности, не регистрируются.
- В этот момент Potato вычисляет все сессии и все напоминания для участника и сохраняет план.
- Фоновый цикл отправляет каждое сообщение в назначенное время со ссылкой для возврата, которая выполняет вход участника и открывает сессию.
- Участник, вернувшийся по ссылке исследования, попадает в открытую сессию или на страницу статуса, где видно, когда откроется следующая сессия, по времени его собственного часового пояса.
План и часовой пояс фиксируются при регистрации. Если позже изменить waves, план изменится только для участников, зарегистрировавшихся после этого. Участник, отказавшийся от связи, всё равно регистрируется, не получает сообщений и находит открытые сессии, возвращаясь по ссылке исследования.
Конфигурация
Конфигурация ниже запускает семидневное исследование по методу экспериментальных выборок с пятью сигналами в день в случайное время между 09:00 и 21:00. Обычный сценарий состоит из страницы согласия и одного вступительного вопроса, и ответ на этот вопрос регистрирует участника:
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:
[
{"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_day | 1 | Первый день выборки, считая от дня регистрации; 0 означает сам день регистрации |
daily_window | "09:00-21:00" | Местное окно, в которое попадают сигналы |
signal.per_day | обязателен вместе с signal | Число случайных сигналов в день, от 1 до 48 |
signal.min_gap | 60m | Наименьший промежуток между двумя сигналами, не меньше expires_after |
interval.every | нет | Сигнал в начале окна и затем через каждый интервал после начала |
interval.times | нет | Фиксированное местное время по часам, например ["10:00", "14:00", "20:00"] |
expires_after | 30m | Сколько каждый сигнал остаётся открытым, не меньше одной минуты |
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 в конфигурации выше и добавляет повторные волны через неделю и через месяц:
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 | Показывается участникам и в сообщениях |
offset | 0 | Время после регистрации |
at | нет | Местное время по часам в день, наступающий через offset дней после регистрации |
window | обязателен | Сколько волна остаётся открытой, не меньше одной минуты |
questions, questions_file | обязателен один из них | Вопросы волны |
reminders | [{at: open}] | Когда уходят сообщения |
completion_code | нет | Код, который показывается после того, как участник отправил ответы волны, например чтобы получить оплату во втором исследовании на Prolific |
intro | нет | Текст над вопросами |
Раунды Дельфи
Исследование по методу Дельфи использует delphi. Фрагмент ниже заменяет schedule в конфигурации для метода экспериментальных выборок, а data/criteria.json содержит список пунктов {"id", "text"}:
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 |
comments | true | Необязательный комментарий к каждому пункту длиной до 2 000 символов, показывается панели без имён |
max_rounds | 3 | Верхний предел числа раундов, от 1 до 10 |
round_length | обязателен | Сколько открыт каждый раунд |
gap | 0 | Время между закрытием одного раунда и открытием следующего |
start | первый запуск сервера | Когда открывается раунд 1, в формате ISO 8601 со смещением от UTC |
min_panel | 3 | Сколько оценок нужно, чтобы показать сводку и комментарии по пункту |
stop | нет | Критерии остановки |
reminders | [{at: open}] | Когда уходят сообщения |
title | "Delphi round" | Показывается на странице раунда, за ним следует номер раунда |
intro | нет | Текст над пунктами |
Вопросы сессий
Вопросы волн и сигналов записываются так же, как схемы разметки: прямо в questions или в JSON- или YAML-файле questions_file, путь к которому отсчитывается от файла конфигурации. Каждый вопрос принимает name, annotation_type, description и либо required: true, либо label_requirement: {required: true}. Шесть типов вопросов принимают такие ключи:
| Тип | Ключи и значения по умолчанию |
|---|---|
radio | labels (не меньше двух), horizontal |
multiselect | labels (не меньше двух) |
likert | size (от 2 до 11), min_label, max_label |
slider | min_value (0), max_value (100), starting_value (значение min_value), step (1), min_label, max_label |
number | min_value, max_value, integer |
text | multiline (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, со своим текстом для сигналов:
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 | На номер мобильного телефона, указанный при регистрации, по SMS | account_sid_env (TWILIO_ACCOUNT_SID), auth_token_env (TWILIO_AUTH_TOKEN), ровно один из from_number (номер в формате E.164) или messaging_service_sid_env, timeout (20) |
prolific | Во входящие участника на Prolific, по идентификатору участника Prolific | token_env (PROLIFIC_API_TOKEN), study_id (STUDY_ID участника, если не задан), timeout (30) |
Канал Prolific доходит только до участников, пришедших из Prolific. Potato читает каждую переменную, названную в ключе *_env, в момент отправки сообщения. Potato останавливается при запуске, если YAML содержит учётные данные под ключом вроде password, token или account_sid. Отсутствующая переменная записывается в журнал при запуске, и затем каждое сообщение в этом канале завершается ошибкой с именем переменной. Задайте переменные в окружении сервера до запуска Potato:
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_run | false | Формирует и адресует каждое сообщение в каждом канале и записывает каждое сообщение как dry_run, не доставляя его |
base_url | http://localhost:<port> | Публичный адрес, используемый в ссылках. Обязателен для любого канала, кроме log, если не включён dry_run |
channels | нет | Каналы, описанные выше |
contact.consent | обязателен при наличии каналов | {question, value}: вопрос о согласии на странице фазы и ответ, разрешающий связь |
contact.delete_after_study | true | Удаляет контактные данные участника, когда у участника не остаётся сессий |
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 | По умолчанию | Значение |
|---|---|---|
timezone | UTC | Часовой пояс IANA, заранее выбранный на странице регистрации, если браузер не сообщает свой |
seed | пусто | Текст, подмешиваемый в случайное время сигналов и в порядок комментариев Дельфи |
poll_seconds | 30 | Как часто выполняется фоновый цикл, от 1 до 3 600 секунд |
scheduler | true | Запускает фоновый цикл вместе с сервером |
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/schedule | GET | Данные страницы в формате JSON, без адресов |
/admin/api/schedule/tick | POST | Сразу отправляет сообщения, срок которых наступил |
/admin/api/schedule/delphi/close_round | POST | Закрывает открытый раунд Дельфи |
/admin/api/schedule/contact/<participant>/delete | POST | Удаляет контактные данные одного участника |
Экспорт
Ответы сессий не являются разметкой объектов, и основной экспорт их не включает. Вместо этого экспорт schedule записывает отдельные таблицы, и в каждой таблице есть столбец participant с тем же идентификатором, что и user_id в основном экспорте:
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:
| Столбец | Значение |
|---|---|
kind | wave, 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 и по местному времени |
status | answered, 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 объединяются через ; |
Пример
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/ лежат панель с повторной волной через неделю и исследование Дельфи из трёх раундов.
Дополнительные материалы
- Многофазные сценарии - Страницы согласия, инструкций и опросов в обычном сценарии
- Опросные инструменты - Валидированные опросники для страниц до и после исследования
- Краудсорсинговое исследование на Prolific и SONA - Набор участников и проведение последующих волн на Prolific
- Психология - Potato для онлайн-экспериментов в поведенческих науках
- Исследования опросов - Potato для экспериментов с опросами на собственном сервере
Подробности реализации смотрите в исходной документации.