Project structure¶
An uproot project is a Python package with a specific layout. Here's what a typical project looks like.
Directory layout¶
my_project/
├── main.py # Entry point and configuration
├── pyproject.toml # Python dependencies
├── Procfile # For cloud deployment (Heroku, Railway)
├── uproot_license.txt # uproot's LGPL license
├── my_app/
│ ├── __init__.py # App logic: pages, fields, callbacks
│ ├── Welcome.html # Template for Welcome page
│ ├── Decision.html # Template for Decision page
│ ├── Results.html # Template for Results page
│ └── static/ # App-specific static files (optional)
│ └── diagram.png
└── another_app/
├── __init__.py
└── ...
main.py¶
The entry point configures your experiment and starts the server:
import uproot.deployment as upd
from uproot.cli import cli
from uproot.server import load_config, uproot_server
upd.project_metadata(created="1970-01-01", uproot="*.*.*")
load_config(uproot_server, config="my_experiment", apps=["my_app"])
upd.ADMINS["admin"] = ... # Ellipsis = auto-login on localhost
upd.LANGUAGE = "en" # Available: "de", "en", "es"
if __name__ == "__main__":
cli()
Configs¶
Each load_config call registers a config—a named experiment configuration that specifies which apps to run and in what order:
# A single-app config
load_config(uproot_server, config="survey", apps=["survey"])
# A multi-app config: participants go through both apps in sequence
load_config(uproot_server, config="full_experiment", apps=["instructions", "game", "survey"])
When you create a session in the admin, you select a config. The session runs all listed apps in order.
You can optionally pass a settings dictionary to load_config that provides default session settings:
load_config(
uproot_server,
config="my_experiment",
apps=["my_app"],
settings={"n_rounds": 5, "show_feedback": True},
)
Admin accounts¶
# Auto-login on localhost (development only)
upd.ADMINS["admin"] = ...
# Password-protected (required for production)
upd.ADMINS["admin"] = "your-secure-password"
API keys¶
Default rooms¶
from uproot.rooms import room
upd.DEFAULT_ROOMS.append(
room("my_room", config="my_experiment", labels=["A", "B", "C"])
)
Rooms defined this way are created automatically when the server starts.
App module¶
Each app is a Python package (a directory with __init__.py). The __init__.py defines the experiment logic.
Required¶
page_order— List of page classes (and SmoothOperators) that define the participant flow:
Optional module-level attributes¶
| Attribute | Purpose |
|---|---|
DESCRIPTION |
Human-readable description shown in admin |
SUGGESTED_MULTIPLE |
Hint for session creation (e.g., 2 for pair experiments) |
LANDING_PAGE |
If True, shows a landing page before the app starts |
C |
Constants class, available in templates as C |
Optional callbacks¶
| Callback | When it runs |
|---|---|
new_session(session) |
Once when session initializes |
new_player(player) |
Once per player when they join |
restart() |
On server restart (can be async) |
digest(session) |
Returns data for admin digest view |
language(player) |
Returns ISO 639 language code for the player |
See Storing and accessing data for details on new_session and new_player.
Page classes¶
Pages are defined as classes that inherit from Page, NoshowPage, GroupCreatingWait, or SynchronizingWait:
class Welcome(Page):
pass
class Calculate(NoshowPage):
@classmethod
def after_always_once(page, player):
player.score = player.correct * 10
See Pages and templates for details.
Templates¶
Each page needs a corresponding HTML template in the same directory. By default, uproot looks for a file matching the class name:
Templates extend a base layout:
{% extends "Base.html" %}
{% block title %}Welcome{% endblock title %}
{% block main %}
<h1>Welcome to the experiment</h1>
{% endblock main %}
PlayerContext¶
Define a Context class for computed values accessible across all templates:
class Context(PlayerContext):
@property
def total_earnings(self):
return self.player.payoff * C.EXCHANGE_RATE
Available in templates as player.context.total_earnings.
Database¶
uproot uses SQLite by default. The database file uproot.sqlite3 is created automatically in your project directory when the server starts. No configuration needed.
SQLite works well in production too—uproot is optimized for it. PostgreSQL is available as an alternative but is never required. See Deployment for details.
Environment variables¶
| Variable | Default | Purpose |
|---|---|---|
UPROOT_DATABASE |
sqlite3 |
Database driver (sqlite3, memory, postgresql) |
UPROOT_SQLITE3 |
uproot.sqlite3 |
SQLite file path |
UPROOT_POSTGRESQL |
— | PostgreSQL connection URL |
UPROOT_ORIGIN |
— | Public server URL |
UPROOT_SUBDIRECTORY |
— | Subdirectory prefix for all routes |
UPROOT_API_KEY |
— | API key for the REST API |
Run uproot deployment to see the current values.
What's next?¶
- Pages and templates — How pages work
- Collecting data with forms — Available field types
- The admin interface — Managing your experiments