Skip to content

Management Commands

A lookup of every LCOJ-specific admin command (create users, register judges, import problems, export contest data, generate editorials...) with the exact arguments each one takes.

👤 Operators · 🔑 SSH access to the server and permission to run docker compose in the dmoj/ directory

LCOJ ships a set of Django management commands for administrative work: creating users and judges, importing problems, exporting contest data, generating editorials, and more. This page lists every one of these custom commands, with the exact arguments each one accepts.

When you need this page

Use this page when you need an admin task that the web interface can't do, or does slowly (for example, creating many accounts at once). A management command is a Django command run from the command line inside the site container; see also the Glossary.

I want to...Command
Create one accountadduser
Create accounts for a whole class from a CSV filebatchadduser
Merge two accounts belonging to the same personmove_user_content
Give a user an API tokengenerate_api_token
Register a new judgeaddjudge
Import a problem from Codeforces Polygonimport_polygon_package
Download contestants' source code after a contestexport_contest_submissions
Check a contest for plagiarismrunmoss
Generate editorials automaticallygenerate_editorials
See the arguments of any command./scripts/manage.py help <command> (details)

How to run a command

On a Docker install, run commands from the dmoj/ directory through the ./scripts/manage.py wrapper:

sh
cd dmoj/
./scripts/manage.py <command> [arguments] [options]

The wrapper runs docker compose exec $COMPOSE_EXEC_FLAGS site python3 manage.py <command>, so the command executes inside the site container.

File paths are inside the container

The site container's working directory is /site/, which is dmoj/repo/ on the host. Any relative path you pass (input CSV, output file, output directory) resolves under dmoj/repo/ on the host. Other shared mounts: /problems/ = dmoj/problems/, /media/ = dmoj/media/.

Arguments with spaces

The wrapper passes arguments unquoted ($@), so an argument containing spaces, such as a problem title, is split into several arguments. For such commands, open a shell in the container with ./scripts/enter_site and run python3 manage.py <command> ... there.

Passing extra environment variables

The wrapper forwards COMPOSE_EXEC_FLAGS to docker compose exec. For example, to set a variable for a single run:

sh
COMPOSE_EXEC_FLAGS="-e OPENAI_API_KEY=sk-..." ./scripts/manage.py generate_editorials --dry-run

Summary

GroupCommandWhat it does
UsersadduserCreate a single user
UsersbatchadduserCreate many users from a CSV file, with generated passwords
Usersmove_user_contentMove submissions and comments from one user to another
Usersgenerate_api_tokenGenerate (or regenerate) a user's API token
JudgingaddjudgeRegister a judge with its authentication key
JudgingrunbridgedRun the judge bridge (used by the bridged service)
JudgingrunbalancerRun the judge load balancer
Problemscreate_problemCreate an empty problem
Problemsimport_polygon_packageImport a Codeforces Polygon package
Problemssubmit_polygon_solutionsSubmit every solution in a Polygon package
Problemscopy_languageAllow language B wherever language A is allowed
Problemsrender_pdfRender a problem statement to PDF
Problemsbackfill_problem_data_sizeRecompute the stored size of each problem's data files
Editorialsgenerate_editorialsGenerate editorials with an OpenAI-compatible API
Contestsexport_contest_submissionsExport contestants' source code into a directory tree
Contestsexport_contest_submissions_detailsExport per-test-case results to CSV
Contestsexport_event_feedExport a CLICS XML event feed (for the ICPC Resolver)
ContestsrunmossCheck a contest for plagiarism with MOSS
Contestsmerge_replay_dataAdd another contest's participants as "ghosts" to a replay
Organizationsbackfill_current_creditRecompute this month's credit usage of every organization
Organizationsbackfill_monthly_creditRecompute monthly credit history of every organization
Siteadd_blog_navigationAdd a "Blog" item to the navigation bar
Sitegenerate_sitemapWrite static sitemap files to a directory
Siteupdate_permissionsCreate/rename permissions after model changes
SitemakedmojmessagesBuild translation files for database strings
SitecamoPrint the Camo proxy URL for an image URL

Django's built-in commands (migrate, createsuperuser, shell, loaddata, compilemessages, ...) also work through the same wrapper.

Users

adduser

Create one user and their profile.

sh
./scripts/manage.py adduser <name> <email> <password> [language] [--superuser] [--staff]
ArgumentDescription
nameUsername
emailEmail address (does not have to be real)
passwordPassword
languageOptional. Key of the user's default language; defaults to DEFAULT_USER_LANGUAGE (CPP20)
--superuserGive the user superuser privileges
--staffGive the user staff privileges (Django admin access)
sh
./scripts/manage.py adduser alice [email protected] 'S3cret!' PY3

WARNING

The password appears in your shell history. Prefer createsuperuser for admin accounts, or change the password after creation.

batchadduser

Create many users from a CSV file. Passwords are generated (8 random characters) and written to an output CSV.

sh
./scripts/manage.py batchadduser <input> <output>
ArgumentDescription
inputCSV with header columns username and fullname
outputWhere to write the result CSV (username,fullname,password)

Input file (dmoj/repo/students.csv on the host):

csv
username,fullname
lc_student01,Nguyen Van A
lc_student02,Tran Thi B
sh
./scripts/manage.py batchadduser students.csv students_out.csv

fullname is stored as the user's first name; every user gets the default language DEFAULT_USER_LANGUAGE. The command stops at the first duplicate username, so check the input first.

WARNING

The output CSV contains plaintext passwords. Hand it out securely and delete it afterwards.

move_user_content

Reassign all submissions, comments and comment votes from source to target. Useful when a person has two accounts.

sh
./scripts/manage.py move_user_content <source> <target>

Irreversible

The change runs in a single transaction but cannot be undone automatically: afterwards you can no longer tell which submissions came from source. Back up the database first. The command refuses to run if source has any contest participation. The source account itself is not deleted.

generate_api_token

Generate or regenerate a user's API token and print it. The token is a 48-character URL-safe string used as Authorization: Bearer <token> (see API).

sh
./scripts/manage.py generate_api_token <name>

WARNING

Regenerating invalidates the user's previous token. The token bypasses two-factor authentication (it cannot access admin pages), so treat it like a password.

Judging

addjudge

Register a judge in the database so it can connect to the bridge.

sh
./scripts/manage.py addjudge <name> <auth_key>
ArgumentDescription
nameJudge name (must match the id in the judge's configuration)
auth_keyAuthentication key (must match key in the judge's configuration)

The command does not generate a key; you choose one, for example with openssl rand -base64 48. You can also create judges in the Django admin. See Judge setup.

runbridged

Run the bridge that judges connect to. On a Docker install this is the entrypoint of the bridged service; you do not need to run it by hand.

sh
python3 manage.py runbridged [--monitor] [--problem-storage-globs GLOB ...]
OptionDescription
--monitorWatch problem storage and automatically update problems when data changes
--problem-storage-globsGlobs to watch for problem updates (default: none)

Listen addresses come from the BRIDGED_JUDGE_ADDRESS (default port 9999) and BRIDGED_DJANGO_ADDRESS (default port 9998) settings, not from command-line options.

runbalancer

Run the judge load balancer with a YAML configuration file.

sh
./scripts/manage.py runbalancer -c <config.yml>
OptionDescription
-c, --configYAML file containing the balancer configuration (required)

Problems

create_problem

Create an empty problem with a statement, one type and a group. The type and group must already exist.

sh
./scripts/manage.py create_problem <code> <name> <body> <type> <group>
ArgumentDescription
codeProblem code
nameProblem title
bodyStatement (Markdown)
typeName of an existing problem type
groupName of an existing problem group
sh
# inside the container (./scripts/enter_site), because the title and body contain spaces
python3 manage.py create_problem aplusb "A + B" "Compute a + b." <type_name> <group_name>

Limits, points, tests and authors are not set by this command; edit the problem afterwards (see Managing problems).

import_polygon_package

Import a full Codeforces Polygon package (zip).

sh
./scripts/manage.py import_polygon_package <package> <code> [--update] [--authors USER ...] [--curators USER ...]
Argument / optionDescription
packagePath to the package zip
codeProblem code to create
--updateUpdate the problem if it already exists
--authorsOne or more usernames to set as authors
--curatorsOne or more usernames to set as curators
sh
./scripts/manage.py import_polygon_package packages/aplusb.zip aplusb --authors admin

The import is interactive (it may ask questions), and it prints the problem URL when done.

submit_polygon_solutions

Submit every solution listed in a Polygon package's problem.xml to an existing problem, to check that verdicts match the expected tags. Each source gets a header comment with its file name and expected verdict.

sh
./scripts/manage.py submit_polygon_solutions <package> <code> <submitter>
ArgumentDescription
packagePath to the package zip
codeProblem code
submitterUsername to submit as

Supported languages map to the keys CPP20, JAVA, PAS, PY2, PY3, PYPY, PYPY3, KOTLIN, GO, RUST; other solutions are skipped. Those language keys must exist on your site.

copy_language

For every problem that allows language source, also allow language target, and copy source's per-language time/memory limits to target.

sh
./scripts/manage.py copy_language <source> <target>
sh
./scripts/manage.py copy_language CPP17 CPP20

Both arguments are language keys, not problem codes.

Overwrites the problem list of target

The allowed-problem list of target is replaced by that of source: any problem that allows target but not source loses target. Back up the database before running it.

render_pdf

Render a problem statement to <code>.pdf in the working directory (dmoj/repo/ on the host).

sh
./scripts/manage.py render_pdf <code> [-l LANGUAGE]
Argument / optionDescription
codeProblem code
-l, --languageStatement language; uses the translation if one exists. Default: LANGUAGE_CODE (vi in the default lcoj-docker config)

Requires Pdfoid (DMOJ_PDF_PDFOID_URL). See Pdfoid.

backfill_problem_data_size

Recompute the storage size of each problem's data files (test zip, generator, custom checker, custom grader, custom header) from DMOJ_PROBLEM_DATA_ROOT, and store the total in the problem data record.

sh
./scripts/manage.py backfill_problem_data_size [--dry-run]
OptionDescription
--dry-runShow what would change without saving

Run it once after upgrading to a version that tracks problem data size, or whenever the stored sizes look wrong.

Editorials

generate_editorials

Generate editorials for public problems that do not have one yet, using an OpenAI-compatible chat API with Pydantic structured output.

sh
./scripts/manage.py generate_editorials [options]

Requirements

  • The openai and pydantic packages. They are listed in additional_requirements.txt and installed in the Docker base image.
  • OPENAI_API_KEY must be set in the site container's environment (add it to environment/site.env and recreate the container, or pass it with COMPOSE_EXEC_FLAGS as shown above).
  • OPENAI_BASE_URL is optional; set it to use a different OpenAI-compatible endpoint.

Options

OptionDescriptionDefault
--problem CODE, -p CODEProcess one problem onlyAll public problems without an editorial
--limit N, -l NMaximum number of problems to process10
--offset NSkip the first N matching problems (ordered by ID)0
--dry-runGenerate and preview, but save nothingoff
--verboseDebug-level loggingoff
--model MODELModel name sent to the APImimo-v2-flash
--temperature TSampling temperature0.7
--max-retries NRetries on API errors3
--retry-delay SBase delay in seconds; doubles on each retry2
--log-file PATHAlso write logs to this filenone

How it works

  1. Selects problems with is_public=True that have no editorial yet (with --problem, the problem must be public and have no editorial).
  2. Picks up to 3 recent Accepted C/C++ submissions, preferring different users.
  3. Sends the statement and the solutions to the API and parses the answer into a fixed schema.
  4. Builds Markdown in the standard format below.
  5. Saves the editorial as public, published now. Authors: the user named admin (or the first superuser), followed by the authors of the sampled submissions.

Published immediately

Generated editorials are visible to users right away. Always review with --dry-run first, and check a few results on the site (https://luyencode.net/problem/<code>/editorial).

Editorial format (headings are in Vietnamese, as generated):

markdown
## Hiểu bài toán
[Explanation of the problem]

## Các cách tiếp cận

### Cách Brute Force

```cpp
[code]
```

* **Time Complexity**: O(n²)
* **Space Complexity**: O(1)

[Explanation]

### Cách Hash Map
[code + explanation]

## Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|--------------|------|-------|-----|
| 1 | O(n²) | O(1) | Brute Force |
| 2 | O(n) | O(n) | Hash Map |

## Bài học kinh nghiệm
- [Insight]

## Lỗi thường gặp
- [Pitfall]

Examples

sh
# 1. Preview one problem (nothing is saved)
./scripts/manage.py generate_editorials --problem aplusb --dry-run --verbose

# 2. Generate it for real
./scripts/manage.py generate_editorials --problem aplusb

# 3. Process 20 problems and keep a log (the path is inside the container)
./scripts/manage.py generate_editorials --limit 20 --log-file /tmp/editorials.log

# 4. Use another model
./scripts/manage.py generate_editorials --problem aplusb --model gpt-4o-mini --temperature 0.5

The log marks successes with and failures with . Each problem takes several seconds; lower --limit or raise --retry-delay if the API rate-limits you.

Common errors

MessageFix
OPENAI_API_KEY environment variable not setProvide the key to the site container
OpenAI package not installedRebuild the images: docker compose up -d --build base site celery
Insufficient AC C/C++ solutionsThe problem has no Accepted C/C++ submission; skip it or write the editorial by hand
Problem '<code>' not found or already has editorialThe code is wrong, the problem is not public, or an editorial already exists

Removing a generated editorial

Edit or delete it in the editorial section of the problem's page in the Django admin, or from the shell:

DANGER

This permanently deletes the editorial of the given problem.

sh
./scripts/manage.py shell
>>> from judge.models import Solution
>>> Solution.objects.filter(problem__code='aplusb').delete()

Contests

export_contest_submissions

Export the source code of all live (non-virtual) participants into a directory tree. Each user's last submission per problem goes to <output>/<username>/<problem>.<ext>; older ones go to <output>/<username>/$History/<problem>_<id>.<ext>.

sh
./scripts/manage.py export_contest_submissions <key> <output>
ArgumentDescription
keyContest key
outputOutput directory; must not exist yet
sh
./scripts/manage.py export_contest_submissions lcoj_round1 exports/lcoj_round1

export_contest_submissions_details

Export the per-test-case results of every submission in the contest to a CSV file with columns username, problem, submission, testcase, points, time, memory, feedback.

sh
./scripts/manage.py export_contest_submissions_details <key> <output>
sh
./scripts/manage.py export_contest_submissions_details lcoj_round1 exports/lcoj_round1_details.csv

export_event_feed

Export a CLICS XML event feed for tools such as the ICPC Resolver.

sh
./scripts/manage.py export_event_feed <key> <output> [--medal lastGold lastSilver lastBronze]
Argument / optionDescriptionDefault
keyContest key
outputOutput file; must end in .xml
--medalLast rank that gets gold, silver and bronze, respectively4 8 12
sh
./scripts/manage.py export_event_feed lcoj_icpc exports/lcoj_icpc.xml --medal 1 3 6

runmoss

Run MOSS on the Accepted submissions of a contest (live and spectating participations), per problem and per language (C++, C, Java, Python, Pascal), and print the MOSS result URLs.

sh
./scripts/manage.py runmoss <contest>

contest is the contest key. Requires MOSS_API_KEY (in lcoj-docker, set through the MOSS_API_KEY environment variable). Contest organizers can also run MOSS from the contest's /moss page.

merge_replay_data

Patch contest A's ranking replay with the participants of another contest B, shown as "ghosts". Useful for comparing a mirror contest on luyencode.net with the original.

sh
./scripts/manage.py merge_replay_data <contest> <b_json>
ArgumentDescription
contestKey of contest A on this server
b_jsonReplay data JSON of contest B (the format served at /contest/<key>/replay/<version>/)

The command rebuilds A's replay data from the database (so re-running it does not duplicate ghosts), matches problems by position, bumps the contest's replay_version, writes the new file under MEDIA_ROOT/contest_replay/, and turns on the ghost toggle on the ranking page. Both contests must have the same number of problems; a warning is printed if their durations differ. Replay is only available for contests that can be replayed (public, ended, not frozen, ranking visible).

Organizations

These commands recompute organization credit usage (judging time used by an organization's problems and contests). They take no arguments and write to the database.

backfill_current_credit

Recompute every organization's usage for the current month and reset its free credit to VNOJ_MONTHLY_FREE_CREDIT.

sh
./scripts/manage.py backfill_current_credit

backfill_monthly_credit

Recompute monthly usage records for every organization, from June 2023 up to the last complete month.

sh
./scripts/manage.py backfill_monthly_credit

Site maintenance

add_blog_navigation

Add a top-level navigation item Blog/blog/ (key blog) at the end of the navigation bar. Does nothing if an item with key blog already exists.

sh
./scripts/manage.py add_blog_navigation

generate_sitemap

Write static sitemap files: <directory>/sitemap.xml (index) plus one file per sitemap page in a subdirectory. The site also serves a dynamic /sitemap.xml, so this is only needed if you want to serve static files.

sh
./scripts/manage.py generate_sitemap <directory> [-s SITE] [-p PROTOCOL] [-d SUBDIR] [-P PREFIX]
Argument / optionDescriptionDefault
directoryOutput directory
-s, --siteSite IDCurrent site
-p, --protocolProtocol used in linkshttps
-d, --subdir, --subdirectorySubdirectory for individual sitemap filessitemaps
-P, --prefixURL prefix of individual sitemaps; must end with /<protocol>://<domain>/<subdir>/

Add -v 2 to see progress.

update_permissions

Create missing permissions and update the names of existing ones, for all apps or only the given ones. Run it after a model's Meta.permissions changes (see Permissions).

sh
./scripts/manage.py update_permissions [--apps APP1,APP2] [--create-only | --update-only]
OptionDescription
--appsComma-separated app labels (default: all apps)
--create-onlyOnly create missing permissions
--update-onlyOnly rename existing permissions

Use -v 2 to print each renamed permission.

makedmojmessages

Build the dmoj-user translation catalog from database strings (navigation bar labels and problem type names), instead of source code.

sh
./scripts/manage.py makedmojmessages (-l LOCALE ... | -a) [-x LOCALE] [--no-wrap] [--no-obsolete] [--keep-pot]
OptionDescription
-l, --localeLocale to create/update (repeatable), e.g. vi
-a, --allUpdate all existing locales
-x, --excludeLocale to skip (repeatable)
--no-wrapDo not wrap long lines
--no-obsoleteRemove obsolete strings
--keep-potKeep the .pot file (for debugging)

Then compile with ./scripts/manage.py compilemessages.

camo

Print the Camo proxy URL for an image URL. Fails with Camo not available if Camo is not configured (DMOJ_CAMO_URL, DMOJ_CAMO_KEY). See SSL content proxy.

sh
./scripts/manage.py camo <url>

Tips

Run a long command in the background

Use -T (no TTY) so the command keeps running when your SSH session ends:

sh
COMPOSE_EXEC_FLAGS="-T" nohup ./scripts/manage.py generate_editorials --limit 100 > editorials.out 2>&1 &
tail -f editorials.out

Schedule with cron

Run from the host's crontab; use the absolute path to dmoj/ and -T because cron has no TTY:

cron
# Recompute organization credit at 00:10 on the 1st of each month
10 0 1 * * cd /path/to/lcoj-docker/dmoj && COMPOSE_EXEC_FLAGS="-T" ./scripts/manage.py backfill_monthly_credit

Getting help for a command

List all commands (built-in and LCOJ-specific):

sh
./scripts/manage.py help

Show the arguments of one command:

sh
./scripts/manage.py help <command>
# for example
./scripts/manage.py help adduser

Next steps

  • Helper scripts: the other scripts in dmoj/scripts/, including manage.py and enter_site.
  • Judge setup: uses addjudge when adding a judge.
  • Managing users: working with accounts through the web interface.
  • API: uses the token created by generate_api_token.
  • Settings reference: settings mentioned on this page, such as BRIDGED_JUDGE_ADDRESS, MOSS_API_KEY, and VNOJ_MONTHLY_FREE_CREDIT.