Data Files and Admin Access¶
Input formats, side files, and reading results back out. It ships with the potato-tasks skill:
/plugin marketplace add davidjurgens/potato-skill
/plugin install potato-tasks@potato
Where items come from, and who is allowed to annotate them. Nothing else in this pack covers either.
Items¶
data_files:
- data/articles.json
item_properties:
id_key: id
text_key: body
data_files accepts JSON, JSONL, CSV and TSV. JSON may be an array of objects or
one object per line. Every object needs the fields named by id_key and
text_key.
| Key | Effect |
|---|---|
item_properties.id_key |
Field holding the unique identifier |
item_properties.text_key |
Field shown to the annotator when there is no instance_display |
item_properties.category_key |
Field holding a category, used by category_based assignment |
item_properties.kwargs |
Extra per-item fields carried through to the display layer |
text_key still matters when you define instance_display. It is what
plain-text rendering falls back to, what span schemes anchor to by default, and
what the training and search surfaces read. Point it at the field with the actual
content, not at a title.
Every other field in the row is available to the display layer by name. A row
with outlet, headline and body can render all three as separate
instance_display.fields without any of them being text_key. Fields you do not
display are still stored and still exported, which is how you keep a condition
label out of the annotator's view without dropping it from the data.
A directory instead of a list¶
data_directory: ./data/incoming
data_directory_encoding: utf-8
watch_data_directory: true # rescan while running and pick up new files
watch_poll_interval: 5.0
Remote and live sources¶
# type: url, s3, huggingface, google_sheets, database, google_drive,
# dropbox or file
data_sources:
- type: url
url: https://example.org/items.json
data_cache:
enabled: true
ttl_seconds: 3600
max_size_mb: 200
Sources can be polled for new rows, which is how a task keeps ingesting while
annotators work. Pair with automation (rules over incoming items) and triage
(rank what arrives). Both are in modes-and-subsystems.md.
Media¶
media_directory: media # default; served at /media/
Put images, audio and video under it and reference them by relative path from a
data field. Without an instance_display field of the right type, that path
renders as text — the classic "the item shows a file path instead of an
image". See building-the-ui.md for the display types.
Big corpora¶
partial_loading: {...} # load lazily rather than reading everything at boot
item_store:
backend: paged # memory (default) or paged
cache_size: 1000
path: .item_cache.sqlite # under the output directory when unset
An unknown item_store.backend warns and silently falls back to memory rather
than refusing to start, so check the boot log if paging was the point.
Output¶
output_annotation_dir: annotation_output/
export_annotation_format: [csv] # periodic auto-export; empty means none
auto_export_interval: 60 # seconds
export_include_phase_data: false # consent and survey answers; usually where the PII is
export_include_annotation_changes: false
There is one storage format and it is not configurable. Annotations live at
annotation_output/<user>/user_state.json, one file per annotator, whatever
else the config says. Any other shape is an export, and there are two ways to
ask for one: export_annotation_format, which writes it on a timer, and the
admin export route, which writes it on demand. output_annotation_format is
deprecated — the loader reads it as export_annotation_format and warns. Write
the live key in anything you author.
So what a researcher gets at the end of a study is a directory of per-annotator
JSON, not a table. If they want one row per item, produce it: either set
export_annotation_format: [csv], or read the user_state.json files and write
the table yourself. Decide which before handover and say so, because "where is
my CSV" is the first question after the annotators finish.
annotation_output/ is rewritten wholesale by the server. Never hand-edit
anything in it — potato repair-annotations exists for when something already
did.
Per-annotator state lives at annotation_output/<user>/user_state.json and is
the fastest way to check what the server actually stored. Keys worth knowing:
instance_id_ordering, instance_id_to_label_to_value,
instance_id_to_span_to_value, phase_to_page_to_label_to_value,
training_state, max_assignments.
Who may log in¶
login:
type: password # password | url_direct | none
user_config:
allow_all_users: true # anyone may register
| Key | Effect |
|---|---|
login.type |
password (register and sign in), url_direct (identity in a query parameter), none |
login.url_argument |
The query parameter carrying the user id under url_direct |
user_config.allow_all_users |
Default true. Anyone can register |
user_config.users |
Allowlist, used when allow_all_users: false |
require_password / require_no_password |
Force the choice regardless of login.type |
authentication |
SSO and database-backed accounts |
rbac |
Role assignments and SSO role mapping |
url_direct is what crowdsourcing platforms want: the platform appends the
worker id and the annotator never sees a login screen. Pair it with
completion_code and auto_redirect_on_completion.
The login page has two tabs, login and register, in one document — if you are
driving it in a browser, switch with switchTab('register') before filling the
register form.
Serving it¶
| Key | Effect | Default |
|---|---|---|
port |
Port; -p overrides |
8000 |
host |
Interface to bind; 0.0.0.0 exposes beyond localhost |
localhost |
secret_key |
Flask session signing key. Set it for anything that must keep sessions across a restart | — |
persist_sessions, session_lifetime_days |
Keep annotator sessions across a restart | false, 2 |
admin_api_key |
Admin API key, sent as X-API-Key. Generated into {task_dir}/admin_api_key.txt when unset |
— |
debug: true disables admin authentication and skips login entirely. It is
for developing the interface, never for anything an annotator can reach. The same
goes for --debug on the command line: it is what makes
--debug-phase annotation able to drop you straight into the task, and it is
also what makes any admin-auth check you run under it meaningless.
Binding 0.0.0.0 is how a researcher on the same network opens the task. Say so
explicitly in a handover, along with the port, rather than leaving them to guess
that localhost is not the only address.