Miscellanea¶
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¶
- class oioioi.contests.middleware.CurrentContestMiddleware(get_response)[source]¶
Middleware which tracks the currently visited contest and stores it to be used in other parts of the current contest mechanism.
It is assumed that all contest-specific url patterns are defined in the
contest_patternsvariable in each module’s urlconf. These patterns are extended with non contest-specific patterns defined in theurlpatternsvariable and then used to generate URLs prefixed with a contest ID (thus the non contest-specific URLs come in two versions, with and without a contest ID prefix). If a request matches a contest ID-prefixed URL and the ID is valid, the contest becomes the current contest. If the ID is not valid, a 404 Not Found is generated.After a contest becomes the current contest, the corresponding
Contestinstance is available inrequest.contest. In addition to that, our customreverse()function automatically prefixes generated URLs with the contest’s ID if appropriate.Using
settings.CONTEST_MODE, the administrator may decide that users should, if possible, be forcibly put into a contest. Then, if there is no contest ID in a request’s URL, but the URL also comes with a contest-specific version and a contest exists, a redirection is performed to one of the existing contests. Which one it is is determined by the following algorithm:If last contest is saved in session, this value is used.
If the session value is not available or invalid,
settings.DEFAULT_CONTESTis used.If not set, the most recently created contest will be chosen.
URL patterns may be explicitly defined as requiring that no contest is given using the
noncontest_patternsvariable in each module’s urlconf. Again, usingsettings.CONTEST_MODE, the administrator may decide that if a contest is available, users cannot access those URLs. Trying to access them then generates a 403 Permission Denied unless one is a superuser.
- 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
urls.reversefunction.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_idkwarg is given which is not None then the URL, if successfully reversed, is prefixed with it.If a
contest_idkwarg equal to None is given then the URL, if successfully 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_patternspattern list. Those only have a contest-prefixed version.Urls pointing to views that require no contest being active are defined in the
noncontest_patternspattern 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
urlpatternspattern list. Those have both versions.
These files are preprocessed to be used by the reverser. Urls defined in
oioioi.urlsare 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_idkwarg. 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.
- oioioi.contests.urls.make_patterns(neutrals=None, contests=None, noncontests=None, globs=None)[source]¶
Creates url patterns to be used in a custom urlconf.
Use this function when you create a custom urlconf, for example when writing tests. It will allow our
reverse()function to run correctly when using this urlconf.DON’T use this function when defining patterns in your app’s urls.py file. Instead just define the following variables (though all of them are optional), and the file will be preprocessed by us:
contest_patterns- these patterns will generate urls with prefix/c/<contest_id>/and a request whose path matches such an url will have an attribute calledcontest. For more information readCurrentContestMiddleware’s documentation. Use this variable if your view needs needs a contest.urlpatterns- these patterns will generate urls both with and without the prefix. If your view doesn’t depend on the contest or its behavior is conditional on the existence of a contest, you should use this variable (this should be the default choice).noncontest_patterns- these patterns will generate urls without the prefix. Use this variable if you think that users accessing your views should not currently participate in any contest.
When creating a custom urlconf, you can use this function and each parameter (with one exception) represents one of the mentioned variables:
- Parameters:
neutrals – represents
urlpatternscontests – represents
contest_patternsnoncontests – represents
noncontest_patternsglobs – represents global url patterns - those defined in
oioioi.urls. These urls won’t be prefixed by us with/c/<contest_id>/, but they could already contain urls in this form. When you create your custom urlconf and you want to use all of the existing OIOIOI urls, you can use this param to pass them (e.g.:from oioioi import urls; make_patterns(..., globs=urls.urlpatterns))
Typically the function’s return value will be assigned to
urlpatterns.
- oioioi.contests.admin.contest_site = ContestProxyAdminSite(name='oioioiadmin')¶
Every contest-dependent model admin should be registered in this site using the
contest_registermethod. You can also register non-dependent model admins like you would normally do using theregistermethod. Model admins registered using thecontest_registermethod “don’t exist” when there is no active contest, that is, they can only be accessed by a contest-prefixed URL and they don’t show up in/admin/(but they do in/c/<contest_id>/admin/).
- oioioi.contests.processors.register_current_contest(request)[source]¶
A template context processor which makes the current contest available to the templates.
The current
Contestinstance is added to the template context as acontestvariable.Must be used together with
CurrentContestMiddleware.
Exclusive contests¶
- class oioioi.contestexcl.middleware.ExclusiveContestsMiddleware(*args, **kwargs)[source]¶
Middleware which checks whether the user participate in an exclusive contest, which is a contest that blocks other contests, and sets the current contest to that contest.
It works as follows:
If ONLY_DEFAULT_CONTEST is set, only the default contest is taken into account.
All contests with active
ExclusivenessConfiginstance are acquired from the database.They are filtered with a special selector function, which by default checks if the user is not a contest admin. In addition,
process_viewaccepts another selector function as an argument. If it is present, the contest list is filtered with a logical conjunction of the default selector and the selector passed as an argument (it may be useful with mixins).If there is only one contest left, the
request.contestvariable is set to this contest or a redirect is made if necessary.If there is more than one contest left, the user is logged out, an error message is displayed and an e-mail describing the situation is sent to the administrators.
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.authtemplate context processor must be present insettings.TEMPLATE_CONTEXT_PROCESSORS.Usage:
{% 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 %}
Conditions¶
- class oioioi.base.permissions.Condition(condition, *args, **kwargs)[source]¶
Class representing a condition (a function which returns a boolean based on its arguments) intended for use with views and menu items.
Technically, an instance of this class is a callable object wrapping a function.
Additionally, it implements basic logical operators: AND (&), OR (|), and (~) – a logical negation.
- Parameters:
condition (fun: *args, **kwargs → bool) – the function to be wrapped
- class oioioi.base.permissions.RequestBasedCondition(condition, *args, **kwargs)[source]¶
Subclass of the
Conditionclass.It is a special condition class representing a condition which takes request as its only argument.
It allows the usage of
oioioi.base.utils.request_cached().
- oioioi.base.permissions.make_condition(condition_class=Condition)[source]¶
Decorator which transforms a function into an instance of a given
condition_class(subclass ofCondition).
- oioioi.base.permissions.make_request_condition(func)¶
Shortcut for
make_condition(RequestBasedCondition). See example usage below.
To assign a condition to a view use the enforce_condition decorator:
- oioioi.base.permissions.enforce_condition(condition, template=None, login_redirect=True)[source]¶
Decorator for views that checks that the request passes the given
condition.conditionmust be an instance ofCondition.If the condition returns
Falseandtemplateis notNone, a suitableTemplateResponseis returned.If
templateisNoneand the user is not authenticated and thelogin_redirectflag is set toTrue, a redirect to the login page is issued, otherwisePermissionDeniedis raised.If the condition returns an instance of
AccessDeniedwith a specific response to use, this response is used instead of calling the decorated view.- Parameters:
condition (
Condition) – condition to checktemplate (basestring) – template name to return when
conditionfails
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:
@make_request_condition
def is_superuser(request):
return request.user.is_superuser
@enforce_condition(is_superuser & ~is_superuser)
def not_accessible_view(request):
pass
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.
- oioioi.su.utils.su_to_user(request, user, backend_path=None)[source]¶
Changes current effective user to
user.After changing to
user, originalrequest.useris saved inrequest.real_user. If given,backend_pathshould be dotted name of authentication backend, otherwise it’s inherited from current user.
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¶
- Request:
- Data sent:
- Result:
Code 200 and data:
or code 4xx|5xx and data:
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:
in case of compilation failure or
in case of compilation success, where
- 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 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.