Пример #1
0
 def has_add_permission(self, request):
     return is_contest_basicadmin(request)
Пример #2
0
 def has_change_permission(self, request, obj=None):
     if obj:
         return False
     return is_contest_basicadmin(request)
Пример #3
0
 def has_add_permission(self, request):
     # Correct object contest ensured by form.
     return is_contest_basicadmin(request)
Пример #4
0
 def has_change_permission(self, request, obj=None):
     if obj:
         return is_superuser(request) or \
                (is_contest_basicadmin(request) and
                 obj.contest == request.contest)
     return self.has_add_permission(request)
Пример #5
0
    def __init__(self, request, *args, **kwargs):
        problem_instance = kwargs.pop('problem_instance', None)
        if problem_instance is None:
            # if problem_instance does not exist any from the current
            # contest is chosen. To change in future.
            # ALSO in mailsubmit.forms
            contest = request.contest
            assert contest is not None
            problem_instances = ProblemInstance.objects \
                    .filter(contest=contest)
            problem_instance = problem_instances[0]
        else:
            problem_instances = [problem_instance]
            contest = None
        self.all_problem_instances = problem_instances

        # Default kind is selected based on
        # the first problem_instance assigned to this form.
        # This is an arbitrary choice.
        self.kind = kwargs.pop(
            'kind',
            problem_instance.controller.get_default_submission_kind(
                request, problem_instance=problem_instance))
        problem_filter = kwargs.pop('problem_filter', None)
        self.request = request

        # taking the available problems
        pis = self.get_problem_instances()
        if problem_filter:
            pis = problem_filter(pis)
        pi_choices = [(pi.id, six.text_type(pi)) for pi in pis]

        # pylint: disable=non-parent-init-called
        # init form with previously sent data
        forms.Form.__init__(self, *args, **kwargs)

        # prepare problem instance selector
        pi_field = self.fields['problem_instance_id']
        pi_field.widget.attrs['class'] = 'input-xlarge'
        self._set_field_show_always('problem_instance_id')

        if len(pi_choices) > 1:
            pi_field.choices = [('', '')] + pi_choices
        else:
            pi_field.choices = pi_choices

        narrow_input_field(pi_field)

        # if contest admin, add kind and 'as other user' field
        if contest and is_contest_basicadmin(request):
            self.fields['user'] = UserSelectionField(
                label=_("User"),
                hints_url=reverse('contest_user_hints',
                                  kwargs={'contest_id': request.contest.id}),
                initial=request.user)
            self._set_field_show_always('user')

            def clean_user():
                try:
                    user = self.cleaned_data['user']
                    if user == request.user:
                        return user
                    if not request.user.is_superuser:
                        contest.controller.registration_controller() \
                            .filter_participants(
                                User.objects.filter(pk=user.pk)).get()
                    return user
                except User.DoesNotExist:
                    raise forms.ValidationError(
                        _("User does not exist or "
                          "you do not have enough privileges"))

            self.clean_user = clean_user
            self.fields['kind'] = forms.ChoiceField(choices=[
                ('NORMAL', _("Normal")), ('IGNORED', _("Ignored"))
            ],
                                                    initial=self.kind,
                                                    label=_("Kind"))
            self._set_field_show_always('kind')
            narrow_input_fields([self.fields['kind'], self.fields['user']])

        # adding additional fields, etc
        for pi in pis:
            pi.controller.adjust_submission_form(request, self, pi)

        self._set_default_fields_attributes()

        # fix field order (put kind and user at the end)
        self._move_field_to_end('user')
        self._move_field_to_end('kind')
Пример #6
0
 def has_delete_permission(self, request, obj=None):
     return is_contest_basicadmin(request)
Пример #7
0
def get_contest_permissions(request, response):
    response['is_contest_admin'] = is_contest_admin(request)
    response['is_contest_basicadmin'] = is_contest_basicadmin(request)
    return response
Пример #8
0
 def filter_my_visible_submissions(self, request, queryset):
     if not is_contest_basicadmin(request):
         queryset = queryset.exclude(kind='USER_OUTS')
     return super(ProgrammingContestController, self). \
             filter_my_visible_submissions(request, queryset)
Пример #9
0
    def get_contest_participant_info_list(self, request, user):
        """Returns a list of tuples (priority, info).
        Each entry represents a fragment of HTML with information about the
        user's participation in the contest. This information will be
        visible for contest admins. It can be any information an application
        wants to add.

        The fragments are sorted by priority (descending) and rendered in
        that order.

        The default implementation returns basic info about the contestant:
        his/her full name, e-mail, the user id, his/her submissions and
        round time extensions.

        To add additional info from another application, override this
        method. For integrity, include the result of the parent
        implementation in your output.
        """
        res = [(
            100,
            render_to_string(
                'contests/basic_user_info.html',
                {
                    'request': request,
                    'target_user_name': self.get_user_public_name(
                        request, user),
                    'target_user': user,
                    'user': request.user,
                },
            ),
        )]

        exts = RoundTimeExtension.objects.filter(
            user=user, round__contest=request.contest)
        if exts.exists():
            res.append((
                99,
                render_to_string(
                    'contests/roundtimeextension_info.html',
                    {
                        'request': request,
                        'extensions': exts,
                        'user': request.user
                    },
                ),
            ))

        if is_contest_basicadmin(request) or is_contest_observer(request):
            submissions = (Submission.objects.filter(
                problem_instance__contest=request.contest,
                user=user).order_by('-date').select_related())

            if submissions.exists():
                submission_records = [
                    submission_template_context(request, s)
                    for s in submissions
                ]
                context = {
                    'submissions': submission_records,
                    'show_scores': True
                }
                rendered_submissions = render_to_string(
                    'contests/user_submissions_table.html',
                    context=context,
                    request=request,
                )
                res.append((50, rendered_submissions))

        return res
Пример #10
0
def can_add_problems(request):
    return request.user.has_perm('problems.problems_db_admin') \
           or is_contest_basicadmin(request)
Пример #11
0
def ranking_view(request, key=None):
    rcontroller = request.contest.controller.ranking_controller()
    choices = rcontroller.available_rankings(request)
    if key is None:
        key = choices[0][0]
    if key not in next(zip(*choices)):
        raise Http404

    context = dict()

    ranking = None

    if rcontroller.can_search_for_users():
        form = FilterUsersInRankingForm(request, request.GET)
        context['form'] = form

        if form.is_valid():
            user = form.cleaned_data.get('user')
            # Everybody can search for themselves.
            # Contest admins can search for anyone.
            if user and (is_contest_basicadmin(request) \
                         or user == request.user):
                found_pos = rcontroller.find_user_position(request, key, user)
                if found_pos:
                    users_per_page = getattr(settings, 'PARTICIPANTS_ON_PAGE',
                                             100)
                    found_page = ((found_pos - 1) // users_per_page) + 1
                    get_dict = request.GET.copy()
                    get_dict.pop('user')
                    get_dict['page'] = found_page
                    return redirect(request.path + '?' + get_dict.urlencode() +
                                    '#' + str(user.id))
                else:
                    msg = _("User is not in the ranking.")
                    # Admin should receive error in form,
                    # whereas user should see it as an error message,
                    # because there is no form then.
                    if is_contest_basicadmin(request):
                        form._errors['user'] = form.error_class([msg])
                    else:
                        messages.error(request, msg)

    if ranking is None:
        # Changing request.GET is necessary!
        # The pagination library not only truncates the list of objects,
        # but also generates the links to other pages.
        # It simply copies the current url and replaces 'page'
        # with another number. If there is a different GET parameter,
        # it is included (without any change) in the url to another page.

        # If a user requests a page, he can provide any number of useless
        # GET parameters, which will be shown in urls to other pages.
        # The ranking page could be cached and those urls could be
        # visible to other users, giving them links to the ranking
        # with strange arguments (e.g. arguments, which perform a search).

        # Below, only 'user' key is deleted, because that's the only argument
        # performing an action, and I'd like to change request.GET
        # as less as possible.
        # This solution does not prevent users from sending "messages"
        # between each other (using these GET parameters).
        request.GET = request.GET.copy()
        try:
            request.GET.pop('user')
        except KeyError:
            pass
        ranking = rcontroller.get_rendered_ranking(request, key)

    context['choices'] = choices
    context['ranking'] = ranking
    context['key'] = key

    return TemplateResponse(request, 'rankings/ranking_view.html', context)
Пример #12
0
class RankingController(RegisteredSubclassesBase, ObjectWithMixins):
    """Ranking system uses two types of keys: "partial key"s and "full key"s.
    Please note that full keys are abbreviated in the code as "key"s.

    A pair (request, partial_key) should allow to build a full key, while a
    partial_key can always be extracted from the full key.
    partial keys identify the rounds to display and are used everywhere
    outside controllers and rankingsd (e.g. in views and urls). However, the
    actual ranking contents can depend on many other factors, like user
    permissions. This was the reason for introduction of full keys, which
    are always sufficient to choose the right data for serialization and
    display.
    """

    modules_with_subclasses = ['controllers']
    abstract = True
    PERMISSION_CHECKERS = [
        lambda request: 'admin' if is_contest_basicadmin(request) else None,
        lambda request: 'observer' if is_contest_observer(request) else None,
        lambda request: 'regular',
    ]

    def get_partial_key(self, key):
        """Extracts partial key from a full key."""
        return key.split('#')[1]

    def replace_partial_key(self, key, new_partial):
        """Replaces partial key in a full key"""
        return key.split('#')[0] + '#' + new_partial

    def get_full_key(self, request, partial_key):
        """Returns a full key associated with request and partial_key"""
        for checker in self.PERMISSION_CHECKERS:
            res = checker(request)
            if res is not None:
                return res + '#' + partial_key

    def _key_permission(self, key):
        """Returns a permission level associated with given full key"""
        return key.split('#')[0]

    def is_admin_key(self, key):
        """Returns true if a given full key corresponds to users with
        administrative permissions.
        """
        return self._key_permission(key) == 'admin'

    def __init__(self, contest):
        self.contest = contest

    def available_rankings(self, request):
        """Returns a list of available rankings.

        Each ranking is a pair ``(key, description)``.
        """
        raise NotImplementedError

    def can_search_for_users(self):
        """Determines if in this ranking, searching for users is enabled."""
        return False

    def find_user_position(self, request, partial_key, user):
        """Returns user's position in the ranking.
        User should be an object of class User, not a string with username.

        If user is not in the ranking, None is returned.
        """
        raise NotImplementedError

    def get_rendered_ranking(self, request, partial_key):
        """Retrieves ranking generated by rankingsd.

        You should never override this function. It will be responsible for
        communication with rankingsd and use render_ranking for actual
        HTML generation. Feel free to override render_ranking to customize
        its logic.

        If the ranking is still being generated, or the user requested an
        invalid page, displays an appropriate message.
        """
        try:
            page_nr = int(request.GET.get('page', 1))
        except ValueError:
            return HttpResponseBadRequest("Page number must be integer")
        key = self.get_full_key(request, partial_key)
        # Let's pretend the ranking is always up-to-date during tests.
        if getattr(settings, 'MOCK_RANKINGSD', False):
            data = self.serialize_ranking(key)
            html = self._render_ranking_page(key, data, page_nr)
            print(data)
            return mark_safe(html)

        ranking = Ranking.objects.get_or_create(contest=self.contest,
                                                key=key)[0]
        try:
            page = ranking.pages.get(nr=page_nr)
        except RankingPage.DoesNotExist:
            # The ranking hasn't been yet generated
            if page_nr == 1:
                return mark_safe(
                    render_to_string("rankings/generating_ranking.html"))
            return mark_safe(render_to_string("rankings/no_page.html"))

        context = {
            'ranking_html': mark_safe(page.data),
            'is_up_to_date': ranking.is_up_to_date(),
        }
        return mark_safe(
            render_to_string("rankings/rendered_ranking.html", context))

    def get_serialized_ranking(self, key):
        return self.serialize_ranking(key)

    def build_ranking(self, key):
        """Serializes data and renders html for given key.

        Results are processed using serialize_ranking, and then as many
        pages as needed are rendered. Returns a tuple containing serialized
        data and a list of strings, that are html code of ranking pages.
        """
        data = self.serialize_ranking(key)
        pages = []
        num_participants = len(data['rows'])
        on_page = data['participants_on_page']
        num_pages = (num_participants + on_page - 1) / on_page
        num_pages = max(num_pages, 1)  # Render at least a single page
        for i in range(1, num_pages + 1):
            pages.append(self._render_ranking_page(key, data, i))
        return data, pages

    def _fake_request(self, page):
        """Creates a fake request used to render ranking.

        Pagination engine requires access to request object, so it can
        extract page number from GET parameters.
        """
        fake_req = RequestFactory().get('/?page=' + str(page))
        fake_req.user = AnonymousUser()
        fake_req.contest = self.contest
        # This is required by dj-pagination
        # Normally they monkey patch this function in their middleware
        fake_req.page = lambda _: page
        return fake_req

    def _render_ranking_page(self, key, data, page):
        raise NotImplementedError

    def render_ranking_to_csv(self, request, partial_key):
        raise NotImplementedError

    def serialize_ranking(self, key):
        """Returns some data (representing ranking).
        This data will be used by :meth:`render_ranking`
        to generate the html code.
        """
        raise NotImplementedError
Пример #13
0
 def _rounds_for_ranking(self, request, partial_key=CONTEST_RANKING_KEY):
     can_see_all = is_contest_basicadmin(request) or is_contest_observer(
         request)
     return self._iter_rounds(can_see_all, request.timestamp, partial_key,
                              request)
Пример #14
0
 def check_repeated_submission(self, request, problem_instance, form):
     return not is_contest_basicadmin(request) and \
         form.kind == 'NORMAL' and \
         getattr(settings, 'WARN_ABOUT_REPEATED_SUBMISSION', False)
Пример #15
0
 def make_context(self, request_or_context):
     if isinstance(request_or_context, ContestControllerContext):
         return request_or_context
     return ContestControllerContext(request_or_context.contest,
             request_or_context.timestamp,
             is_contest_basicadmin(request_or_context))
Пример #16
0
 def filter_visible_reports(self, request, submission, queryset):
     if is_contest_basicadmin(request) or is_contest_observer(request):
         return queryset
     return queryset.filter(status='ACTIVE',
                            kind__in=self.get_visible_reports_kinds(
                                request, submission))
Пример #17
0
 def get_submissions_limit(self, request, problem_instance, kind='NORMAL'):
     if is_contest_basicadmin(request):
         return None
     return problem_instance.problem.controller \
         .get_submissions_limit(request, problem_instance, kind)
Пример #18
0
 def is_admin(self, request, report):
     return is_contest_basicadmin(request)
Пример #19
0
def contest_files_view(request):
    additional_files = attachment_registry.to_list(request=request)
    contest_files = ContestAttachment.objects.filter(contest=request.contest) \
        .filter(Q(round__isnull=True) | Q(round__in=visible_rounds(request))) \
        .select_related('round')
    if not is_contest_basicadmin(request):
        contest_files = contest_files.filter(
            Q(pub_date__isnull=True)
            | Q(pub_date__lte=request.timestamp))

    round_file_exists = contest_files.filter(round__isnull=False).exists()
    problem_instances = visible_problem_instances(request)
    problem_ids = [pi.problem_id for pi in problem_instances]
    problem_files = ProblemAttachment.objects \
            .filter(problem_id__in=problem_ids) \
            .select_related('problem')
    add_category_field = round_file_exists or problem_files.exists()
    rows = [{
        'category':
        cf.round if cf.round else '',
        'name':
        cf.download_name,
        'description':
        cf.description,
        'link':
        reverse('contest_attachment',
                kwargs={
                    'contest_id': request.contest.id,
                    'attachment_id': cf.id
                }),
        'pub_date':
        cf.pub_date
    } for cf in contest_files]
    rows += [{
        'category':
        pf.problem,
        'name':
        pf.download_name,
        'description':
        pf.description,
        'link':
        reverse('problem_attachment',
                kwargs={
                    'contest_id': request.contest.id,
                    'attachment_id': pf.id
                }),
        'pub_date':
        None
    } for pf in problem_files]
    rows += [{
        'category': af.get('category'),
        'name': af.get('name'),
        'description': af.get('description'),
        'link': af.get('link'),
        'pub_date': af.get('pub_date')
    } for af in additional_files]
    rows.sort(key=itemgetter('name'))
    return TemplateResponse(
        request, 'contests/files.html', {
            'files': rows,
            'files_on_page': getattr(settings, 'FILES_ON_PAGE', 100),
            'add_category_field': add_category_field,
            'show_pub_dates': True
        })