Celery usages

This section briefly describes currently used applications of Celery across the infrastructure.

Getting current time

The main source of the current time in the request processing should be the request.timestamp variable. This variable contains the time when the request was initiated and, when used consistently, allows the admins to time travel.

Usage of timezone.now() is highly discouraged.

Current contest mechanism

oioioi.contests.current_contest.reverse(target, *args, **kwargs)[source]

A modified URL reverser that takes into account the current contest and generates URLs that are appropriately prefixed. With it we substitute the original urlresolvers.reverse function.

The choice of prefixing the URL with a particular contest ID (or not prefixing at all) by the function is made as follows:

  • If a contest_id kwarg is given which is not None then the URL, if succesfully reversed, is prefixed with it.
  • If a contest_id kwarg equal to None is given then the URL, if succesfully reversed, will not be prefixed.
  • If the kwarg isn’t given but a contest is active when calling the function then that contest is used for the generated URL.
  • If the above fails or there is no active contest then no contest will be used.

Our reverser uses the special structure of each app’s urls.py file:

  • Urls pointing to views that require a contest are defined in the contest_patterns pattern list. Those only have a contest-prefixed version.
  • Urls pointing to views that require no contest being active are defined in the noncontest_patterns pattern list. Those only have a non contest-prefixed version.
  • Urls pointing to views that can run both with and without current contest are defined in the urlpatterns pattern list. Those have both versions.

These files are preprocessed to be used by the reverser. Urls defined in oioioi.urls are not preprocessed, so they only have a non-prefixed version, even though they could exist within a contest.

Note that there is no point defining patterns that receive a contest_id kwarg. That particular kwarg is interpreted differently and will never be actually matched in the url pattern when reversing.

You need to take into account the behavior of reverse when defining your own custom urlconf (that means patterns lying outside an app’s urls.py file, e.g. for testing purposes), because it won’t be preprocessed. For that we created the make_patterns() function.

Exclusive contests

Checking for instance-level permissions in templates

To check for model-level permissions, one may use the standard Django mechanism. To check for instance-level permissions, use {% check_perm %} template tag.

oioioi.base.templatetags.check_perm.check_perm(parser, token)[source]

A template tag to look up object permissions.

The current user is tested agains the given permission on the given object. Current user is taken from the template context, so the django.contrib.auth.context_processors.auth template context processor must be present in settings.TEMPLATE_CONTEXT_PROCESSORS.


{% load check_perm %}

{% check_perm "some_permission" for some_object as variable %}
{% if variable %}
<p>This is shown if the user has some_permission on some_object.</p>
{% endif %}


To assign a condition to a view use the enforce_condition decorator:

Additionally, the enforce_condition decorator adds a condition attribute to the view, which can be later used by oioioi.base.menu.MenuRegistry.register_decorator().

Mixing it all together in a simple example:

def is_superuser(request):
    return request.user.is_superuser

@enforce_condition(is_superuser & ~is_superuser)
def not_accessible_view(request):

Switching users (su)

The SU app is used to change the current logged in user on-the-fly.

In order to achieve this goal, the module introduces concept of effective and real user privileges known from Unix-like systems. The effective user is stored in request.user field, while the real in request.real_user.

On-the-fly means that current session variables are preserved while changing effective user, which may be also a pitfall if some code stores there data directly connected with current user scope.

Zeus integration (zeus)

The zeus app is used for integration between oioioi and zeus, a system for grading distributed programing problems.

Zeus instances are configured in settings.ZEUS_INSTANCES, which is a dict mapping zeus_id - unique identifier of a zeus instance - to (zeus_url, zeus_login, zeus_secret) - base URL for zeus api (ZBU) and credentials. It is also possible to use a mock instance (ZeusTestServer) which allows manual testing for development purposes.

API specification

Communication with zeus is done over HTTPS protocol, in a REST-like style. Data is encoded using JSON. OIOIOI authorizes itself to zeus using HTTP Basic Authentication, with login and secret fixed for a zeus instance.

Prefix ? means optional attribute.

Sending submissions

POST ZBU/dcj_problem/zeus_problem_id/submissions
Data sent:
“submission_type”: submission_type :: Base64String(SMALL|LARGE),
“return_url”: return_url :: Base64String,
“username”: username :: Base64String,
“metadata”: metadata :: Base64String,
“source_code”: source_code :: Base64String,
“language”: source_language :: Base64String(CPP|…),

Code 200 and data:

{ “submission_id”: unique_job_id :: Uint }

or code 4xx|5xx and data:

{ ? “error”: error_description :: Base64String }

username and metadata fields are not used by Zeus and sent for debugging purposes only.

Receiving results

Zeus hits the “return_url” from submission data once it is graded.

Data received:
“compilation_output”: output :: Base64String,

in case of compilation failure or

“tests_info”: list_of_results :: [TestInfo],

in case of compilation success, where

TestInfo = {
“time_limit_ms”: execution_time_limit :: Int,
“memory_limit_byte”: memory_limit :: Int,
“verdict”: test_status :: Base64String(OK|WA|TLE|RE|RV|OLE|MSE|MCE),
“runtime”: max_of_times_at_all_machines :: Int,
“metadata”: more_data_about_test :: Base64String,
? “nof_nodes”: number_of_nodes :: Int,
Our response:

Code 200 and HttpResponse("Recorded!") or code 4xx|5xx and a lot of HTML (for example the one which normally displays a message Internal Server Error in a browser). When server received invalid JSON or strings are not encoded with Base64, then it will response with code 400 and nice error message.

MSE and MCE are statuses meaning that size or count of outgoing messages sent by submitted program has exceeded the limit.

Metadata is subject to coordination between judges and contest admin. It may be passed through zeus, but in the most recent workflow we sent meaningless metadata to zeus and received meaningful metadata (zeus admins were provided with a file containing metadata for each test). It is designed to contain additional data about grouping, scoring etc. Currently we expect it to be in format:

test name,group name,max score

Test name will be shown to users. All tests with the same, non-empty group name will be grouped together. All tests in group shall have the same max score. Example tests are expected to be in the group 0.

Selenium tests

This module allows us to run selenium tests for OIOIOI. As opposed to regular django tests these are integration tests run under production-like environment, with external filetracker and database.


  1. Do not run tests straight by invoking test_selenium.sh script. Instead build docker images without the -d flag in another terminal (just copy and paste the command from the script and remove -d) [docker-compose -f docker-compose-selenium.yml up]. Why? You can easily navigate through the logs there real-time (instance is visible at 8001 port).
  2. Run the docker images and test from a different terminal (using pytest). There is one problem with this, though. When you need to reload the database, just connect to the docker and manually restart it.
  3. Use firefox to take correct XPATHs to some elements (just inspect and copy). The alternatives are third-party extensions to Chrome, they can even export test to python2 code - but watch out here, sometimes the IDs in OIOIOI are broken (e.g. the ID of a date is a date itself).
  4. When in trouble you can connect your own webdriver (f.e. chrome webdriver) and write tests to see the results right at your screen.
  5. Firefox and selenium are broken. Clicking the element once is not enough for most cases, just click twice with an exception catching phrases or send some KEY presses to the element.

Test creation process

Tests may be created in two ways:
  • Simply write test in python, basing on existing one,
  • Use Selenium IDE plugin for Firefox.

Writing tests with Selenium IDE

With Selenium IDE it’s possible to record mouse and keyboard events in order to create a test.

Before you start look at Predefined selenium actions, there are some helpful methods that simplify common tasks like login or form submit.

Let’s assume you have recorded a test. What’s next?

  1. Export the test to proper python file.
    File -> Export Test Case As… -> Python 2 / unittest / WebDriver
  2. Remove setUp and tearDown functions and set OIOIOISeleniumTestCase as a superclass.

  3. Important! Even if test is doing what it’s supposed to do, there’s still some work left. Selenium recorder will recognize elements in the easiest possible way. That’s not what we want, things may change and test will broke because of insufficient description. Simplified html access section may be helpful now. The goal is to find elements that are poorly accessed by Selenium (for ex. by a class that is not guaranteed to be unique). Usually it applies to tables, buttons. The best solution is to access elements by ID’s, if there’s no corresponding ID, just do the best you can. It’s recommended to find elements by xpath, that way you can nicely access nested elements with specific classes, ids or tags.

Running Selenium tests

Selenium tests are excluded from default Django tests because they need different environment. In OIOIOI’s root directory there’s script test_selenium.sh which runs all Selenium tests. Any arguments passed to it are forwarded to pytest. This script is responsible for launching docker containers with fresh OIOIOI and stuff needed by Selenium. By default it take some time to perform whole process so you may customize script to just wipe data between launches etc.

What to do when tests are not working

Selenium tests easily get broken after frontend updates – for example adding new buttons. If there were errors in testing the first place to go are the logs. Unfortunately, you can’t rely on screenshots because stylesheets can’t be parsed by Selenium (maybe that will be fixed soon…). The best alternative we came up with is printing the whole page source and url after each important step of a test.

Sometimes the tests themselves may be working fine and the real culprit is Docker or Selenium. Sometimes new Firefox version may become incompatible with Selenium, so update each of them with care.

Predefined selenium actions

Methods to perform common actions.

If you write some code that fits this section, don’t hesitate to add it there!

Simplified html access

If you write some code that fits this section, don’t hesitate to add it there!