A Django web application used in a user study to determine the human baseline performance of classifying suzipu 俗字谱 notation (samples from the Shanghai MS edition of Baishidaoren Gequ 白石道人歌曲).
Participants log in, annotate image samples, and their actions are timestamped. The collected data can later be evaluated to derive annotation accuracy and timing statistics.
See also the user instruction (German), the informed consent sheet (German), and the Chapter 6.7 in my dissertation for more information about the user study.
- What this is
- Requirements
- Quick start (Docker)
- Environment configuration
- Running the app
- Create a superuser (admin)
- Import sample data
- Using the app (annotation flow)
- Admin dashboard and statistics export
- Project layout
- Domain model
- Troubleshooting
- Framework: Django 5
- Database: SQLite by default (via DATABASE_URL)
- Containerization: Docker and docker compose
- Media and static directories mounted in the container at
/app/mediaand/app/static
- Docker and Docker Compose (v2)
- Optional: Python 3.12+ if you want to run locally without Docker (not covered here)
-
Copy the example environment to a local file:
- Create a file named
.env.developmentin the repository root with the following content (adjust as needed):
SECRET_KEY=dev-secret-key-change-me DEBUG=True ALLOWED_HOSTS=["*"] DATABASE_URL=sqlite:////app/media/db.sqlite3 - Create a file named
-
Start the development server:
docker compose up --build
This will:
- Build the Django image (development)
- Mount the repository at
/appinside the container - Run
python manage.py runserver 0.0.0.0:8000from/app/src
-
Access the app at:
- The app uses
django-environto read variables from the environment and.env.development(viadocker-compose.yml). - Key variables:
SECRET_KEY: Django secret keyDEBUG: True/FalseALLOWED_HOSTS: JSON-like list of hosts, e.g. ["*"] or ["localhost","127.0.0.1"]DATABASE_URL: e.g.sqlite:////app/media/db.sqlite3(default path under the mounted volume)
-
Start:
docker compose up -d -
Stop:
docker compose stop -
Run one-off Django commands:
docker compose run --rm django python manage.py migrate docker compose run --rm django python manage.py createsuperuser
Run:
docker compose run --rm django python manage.py createsuperuserThen log in at /admin/ to view data and statistics.
Two bundled datasets exist:
- Full demo with grouped samples:
/app/sample_data/dataset_grouped.json(expects images next to it insample_data/images) - Smaller demo images:
sample_data/images_demo(if you prepare your own JSON against these images)
Run the import command (from the repository root):
- Full dataset (recommended):
docker compose run --rm django python manage.py import_samples --data-file /app/sample_data/dataset_grouped.jsonNotes:
- The importer resolves image paths relative to the JSON file’s directory.
- If a Sample with the same
real_nameexists, it will be updated.
- Navigate to the root
/and log in (non-authenticated users are redirected to login). - You will be directed to annotate samples assigned to your group.
- Choose:
- Pitch component
- Secondary component (or none)
- Navigation:
- Use the on-screen Previous/Next buttons
- Keyboard shortcuts: Left/right arrows to move between samples
- The progress indicator shows percent completed (where
pitch != None).
- Admin site:
/admin/(requires superuser) - The dashboard displays per-user and aggregate statistics (computed in
core.build_statistics). - Export raw statistics JSON: GET
/download_statistics/(superuser only). The response is served as a file attachmentstatistics.json.
src/manage.py– Django entrypointsrc/suziai_human_annotation/settings/base.py– base settings (uses environment variables)src/suziai_human_annotation/core/– app with models, forms, views, admin, templatessrc/suziai_human_annotation/core/management/commands/import_samples.py– sample data importerdocker/– Dockerfiles and entrypointrequirements/– pip requirement sets for dev/prodsample_data/– JSON and images for import
classDiagram
direction LR
class User {
+id
+username
+is_superuser
...
}
class Sample {
+id
+image : ImageField
+real_name : str (unique)
+real_pitch : PitchChoices
+real_secondary : SecondaryChoices
+group : smallint
+get_annotation_state(annotations) : str
}
class Annotation {
+id
+user : FK(User)
+sample : FK(Sample)
+pitch : PitchChoices
+secondary : SecondaryChoices
__unique_together__(user, sample)
}
class Action {
+id
+annotation : FK(Annotation)
+timestamp : datetime (auto_now_add)
}
User "1" --> "*" Annotation : author
Sample "1" --> "*" Annotation : annotated by
Annotation "1" --> "*" Action : produces
- App doesn’t start / 500 on boot:
- Ensure
.env.developmentis present andSECRET_KEY/DATABASE_URLare set.
- Ensure
- Import complains about image files:
- The importer expects image paths relative to the JSON file. Make sure
sample_data/imagesexists and matches the JSON entries.
- The importer expects image paths relative to the JSON file. Make sure
- Static or media not accessible in development:
- In dev,
django.contrib.staticfilesserves static. Media is exposed byurls.pyusingstatic(settings.MEDIA_URL, ...)when DEBUG is true.
- In dev,

