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_patterns
variable in each module’s urlconf. These patterns are extended with non contest-specific patterns defined in theurlpatterns
variable 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
Contest
instance 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_CONTEST
is 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_patterns
variable 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.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 successfully reversed, is prefixed with it.If a
contest_id
kwarg 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_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.
- 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
urlpatterns
contests – represents
contest_patterns
noncontests – represents
noncontest_patterns
globs – 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_register
method. You can also register non-dependent model admins like you would normally do using theregister
method. Model admins registered using thecontest_register
method “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
Contest
instance is added to the template context as acontest
variable.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
ExclusivenessConfig
instance 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_view
accepts 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.contest
variable 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.auth
template 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
Condition
class.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
.condition
must be an instance ofCondition
.If the condition returns
False
andtemplate
is notNone
, a suitableTemplateResponse
is returned.If
template
isNone
and the user is not authenticated and thelogin_redirect
flag is set toTrue
, a redirect to the login page is issued, otherwisePermissionDenied
is raised.If the condition returns an instance of
AccessDenied
with 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
condition
fails
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.user
is saved inrequest.real_user
. If given,backend_path
should 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
.