示例#1
0
class Paragraph(AbstractDocumentBlock):
    text = models.TextField()

    style = models.ForeignKey(ParagraphStyle, on_delete=models.CASCADE)

    bulletText = models.TextField(default=None, null=True, blank=True)

    def __str__(self):
        return self.text[:80]

    def get_reportlab_block(self, visitor=None):
        self._process_by_visitor(visitor)
        return reportlab.platypus.Paragraph(self.text,
                                            self.style.get_reportlab_style(),
                                            self.bulletText)
示例#2
0
class QuestionnaireAnswer(models.Model):
    questionnaire = models.ForeignKey(
        Questionnaire,
        on_delete=models.CASCADE,
        related_name='answers',
    )

    user = models.ForeignKey(
        users.models.User,
        on_delete=models.CASCADE,
        related_name='questionnaire_answers',
    )

    # TODO: may be ForeignKey is better?
    question_short_name = models.CharField(max_length=100)

    answer = models.TextField(blank=True)

    def __str__(self):
        return 'Ответ «%s» на вопрос %s анкеты %s' % (
            self.answer.replace('\n', '\\n'),
            self.question_short_name,
            self.questionnaire,
        )

    @property
    def question(self):
        return AbstractQuestionnaireQuestion.objects.filter(
            questionnaire=self.questionnaire,
            short_name=self.question_short_name).first()

    class Meta:
        index_together = ('questionnaire', 'user', 'question_short_name')
示例#3
0
class EntranceExamTaskSolution(polymorphic.models.PolymorphicModel):
    task = models.ForeignKey(
        'EntranceExamTask',
        on_delete=models.CASCADE,
        related_name='solutions',
    )

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='entrance_exam_solutions',
    )

    solution = models.TextField()

    ip = models.CharField(
        max_length=50,
        help_text='IP-адрес, с которого было отправлено решение',
        default='')

    created_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
    )

    def __str__(self):
        return 'Решение %s по задаче %s' % (self.user, self.task)

    class Meta:
        ordering = ['-created_at']
        index_together = ('task', 'user')
示例#4
0
class ContestRegion(models.Model):
    contest = models.ForeignKey(Contest, related_name='regions')

    name = models.TextField(help_text='Region name')

    start_time = models.DateTimeField(
        help_text='Contest start time for this region')

    finish_time = models.DateTimeField(
        help_text='Contest finish time for this region')

    timezone = models.TextField(default='UTC',
                                help_text='Timezone for the region')

    def __str__(self):
        return self.name
示例#5
0
class InlineQuestionnaireBlock(AbstractQuestionnaireBlock):
    block_name = 'inline'

    text = models.TextField(help_text='Общий текст для вопросов в блоке',
                            blank=True)

    help_text = models.CharField(
        max_length=400,
        blank=True,
        help_text='Подсказка, помогающая ответить на вопросы в блоке',
    )

    def __str__(self):
        return '%s: %s' % (self.text, ', '.join(
            [c.block.short_name for c in self.children.all()]))

    def copy_dependencies_to_instance(self, other_block):
        super().copy_dependencies_to_instance(other_block)
        for child in self.children.all():
            child.pk = None
            child.parent = other_block
            child.block = other_block.questionnaire.blocks.get(
                short_name=child.block.short_name)
            child.save()

    def ordered_children(self):
        return self.children.order_by('block__order')
示例#6
0
class AbstractQuestionnaireQuestion(AbstractQuestionnaireBlock):
    is_question = True

    text = models.TextField(help_text='Вопрос')

    is_required = models.BooleanField(
        help_text='Является ли вопрос обязательным', )

    help_text = models.CharField(
        max_length=400,
        blank=True,
        help_text='Подсказка, помогающая ответить на вопрос',
    )

    is_disabled = models.BooleanField(
        default=False,
        help_text='Выключена ли возможность ответить на вопрос. Не может быть '
        'отмечено одновременно с is_required',
    )

    def get_form_field(self, attrs=None):
        raise NotImplementedError(
            'Child should implement its own method get_form_field()')

    def save(self, *args, **kwargs):
        if self.is_disabled and self.is_required:
            raise IntegrityError(
                'questionnaire.AbstractQuestionnaireBlock: is_disabled can not '
                'be set with is_required')
        super().save(*args, **kwargs)

    def __str__(self):
        return '%s: %s' % (self.questionnaire, self.text)
示例#7
0
class UserListQuestionnaireQuestion(AbstractQuestionnaireQuestion):
    block_name = 'user_list_question'

    group = models.ForeignKey(
        'groups.AbstractGroup',
        null=True,
        on_delete=models.CASCADE,
        related_name='+',
        help_text="Группа, пользователей которой можно выбирать",
    )

    placeholder = models.TextField(
        blank=True,
        help_text='Подсказка, показываемая в поле для ввода; пример',
    )

    def get_form_field(self, attrs=None):
        return forms.ChooseUsersFromGroupField(
            group=self.group,
            required=self.is_required,
            disabled=self.is_disabled,
            label=self.text,
            help_text=self.help_text,
            placeholder=self.placeholder,
        )
示例#8
0
class MarkdownQuestionnaireBlock(AbstractQuestionnaireBlock):
    block_name = 'markdown'

    markdown = models.TextField()

    def __str__(self):
        return self.markdown[:40]
示例#9
0
class AbstractAbsenceReason(polymorphic.models.PolymorphicModel):
    school = models.ForeignKey('schools.School',
                               on_delete=models.CASCADE,
                               related_name='absence_reasons')

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='absence_reasons',
    )

    private_comment = models.TextField(blank=True,
                                       help_text='Не показывается школьнику')

    public_comment = models.TextField(blank=True,
                                      help_text='Показывается школьнику')

    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='+',
        null=True,
        default=None,
        blank=True,
    )

    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'Absence reason'

    @classmethod
    def for_user_in_school(cls, user, school):
        """
        Returns absence reason for specified user
        or None if user has not declined.
        """
        return cls.objects.filter(user=user, school=school).first()

    def default_public_comment(self):
        raise NotImplementedError()
示例#10
0
class ProgramEntranceExamTask(EjudgeEntranceExamTask):
    template_file = 'program.html'
    solutions_template_file = '_program_solutions.html'

    input_file_name = models.CharField(max_length=100, blank=True)

    output_file_name = models.CharField(max_length=100, blank=True)

    time_limit = models.PositiveIntegerField(help_text='В миллисекундах')

    # Use FileSizeField to be able to define memory limit with units (i.e. 256M)
    memory_limit = sizefield.models.FileSizeField()

    input_format = models.TextField(blank=True)

    output_format = models.TextField(blank=True)

    def get_form_for_user(self, user, *args, **kwargs):
        return forms.ProgramEntranceTaskForm(self, *args, **kwargs)

    @property
    def solution_class(self):
        return ProgramEntranceExamTaskSolution
示例#11
0
class TextQuestionnaireQuestion(AbstractQuestionnaireQuestion):
    block_name = 'text_question'

    is_multiline = models.BooleanField()

    placeholder = models.TextField(
        blank=True,
        help_text='Подсказка, показываемая в поле для ввода; пример',
    )

    fa = models.CharField(
        max_length=20,
        blank=True,
        help_text='Имя иконки FontAwesome, которую нужно показать в поле',
    )

    def get_form_field(self, attrs=None):
        if attrs is None:
            attrs = {}

        if 'placeholder' not in attrs:
            attrs['placeholder'] = self.placeholder

        if self.is_multiline:
            attrs['class'] = 'gui-textarea ' + attrs.pop('class', '')
        else:
            attrs['class'] = 'gui-input ' + attrs.pop('class', '')

        if self.fa != '':
            if 'fa' not in attrs:
                attrs['fa'] = self.fa
            if self.is_multiline:
                widget = frontend.forms.TextareaWithFaIcon(attrs)
            else:
                widget = frontend.forms.TextInputWithFaIcon(attrs)
        else:
            if self.is_multiline:
                widget = django.forms.Textarea(attrs)
            else:
                widget = django.forms.TextInput(attrs)

        return django.forms.CharField(
            required=self.is_required,
            disabled=self.is_disabled,
            help_text=self.help_text,
            label=self.text,
            widget=widget,
        )
示例#12
0
class ChoiceQuestionnaireQuestionVariant(models.Model):
    text = models.TextField()

    question = models.ForeignKey('ChoiceQuestionnaireQuestion',
                                 on_delete=models.CASCADE,
                                 related_name='variants')

    order = models.PositiveIntegerField(default=0)

    # If variant is disabled it shows as gray and can't be selected
    is_disabled = models.BooleanField(default=False)

    # If this one is selected all options are disabled
    disable_question_if_chosen = models.BooleanField(default=False)

    def __str__(self):
        return '{}: {} {}'.format(self.question, self.id, self.text)
示例#13
0
class News(ModelWithTimestamps):
    contest = models.ForeignKey(Contest, related_name='news')

    author = models.ForeignKey(users.models.User, related_name='+')

    title = models.CharField(max_length=1000, help_text='Title')

    text = models.TextField(help_text='Supports markdown')

    is_published = models.BooleanField(default=False)

    publish_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = 'News'

    def get_absolute_url(self):
        return urlresolvers.reverse('contests:news',
                                    args=[self.contest_id, self.id])
示例#14
0
class FileEntranceExamTask(EntranceExamTask):
    template_file = 'file.html'
    type_title = 'Теоретические задачи'

    checking_criteria = models.TextField(
        default='',
        blank=True,
        help_text='Критерии выставления баллов для проверяющих. '
        'Поддерживается Markdown',
    )

    def is_accepted_for_user(self, user):
        return self.solutions.filter(user=user).exists()

    def get_form_for_user(self, user, *args, **kwargs):
        return forms.FileEntranceTaskForm(self, *args, **kwargs)

    @property
    def solution_class(self):
        return FileEntranceExamTaskSolution
示例#15
0
class FileEntranceExamTaskSolution(EntranceExamTaskSolution):
    original_filename = models.TextField()
示例#16
0
class AbstractGroup(polymorphic.models.PolymorphicModel):
    school = models.ForeignKey(
        schools.models.School,
        null=True,
        blank=True,
        related_name='groups',
        on_delete=models.CASCADE,
    )

    created_by = models.ForeignKey(
        users.models.User,
        null=True,
        blank=True,
        related_name='created_groups',
        on_delete=models.CASCADE,
        help_text='Создатель группы. Не может никогда измениться и '
        'всегда имеет полные права на группу.'
        'None, если владелец группы — система')

    short_name = models.CharField(
        max_length=100,
        help_text='Используется в урлах. '
        'Лучше обойтись латинскими буквами, цифрами и подчёркиванием',
        db_index=True,
    )

    name = models.CharField(max_length=60,
                            help_text='Покороче, используется на метках')

    description = models.TextField(help_text='Подлинее, подробное описание')

    can_be_deleted = models.BooleanField(
        default=True,
        help_text='Системные группы не могут быть удалены',
    )

    list_members_to_everyone = models.BooleanField(
        default=False,
        help_text='Видно ли всем участие других в этой группе',
    )

    class Meta:
        unique_together = ('short_name', 'school')
        verbose_name = 'group'

    def is_user_in_group(self, user):
        """
        You can override this method in subclass.
        By default it calls overridden self.user_ids.
        Be careful: this approach can be slow on large groups.
        :return: True if user is in group and False otherwise.
        """
        return user.id in self.user_ids

    @property
    def users(self):
        """
        You can override this method in subclass. By default it calls overridden self.user_ids
        :return: QuerySet for users.models.User model with users from this group
        """
        return users.models.User.objects.filter(id__in=self.user_ids)

    @property
    def user_ids(self):
        """
        :return: QuerySet or list of ids of users which are members of this group
        """
        raise NotImplementedError(
            'Each group should implement user_ids(), but %s doesn\'t' %
            self.__class__.__name__)

    @property
    def default_access_type(self):
        if self.list_members_to_everyone:
            return GroupAccess.Type.LIST_MEMBERS
        return GroupAccess.Type.NONE

    def get_access_type_for_user(self, user):
        user_access = GroupAccessForUser.objects.filter(to_group=self,
                                                        user=user).first()
        if user_access is None:
            user_access = self.default_access_type
        else:
            user_access = user_access.access_type

        # We need to cast queryset to list because following call
        # group_access.group.is_user_in_group() produces another query to database
        # and this query should be finished at this time
        group_accesses = list(
            GroupAccessForGroup.objects.filter(to_group=self).select_related(
                'group').order_by('-access_type'))
        for group_access in group_accesses:
            # Access levels are sorted in decreasing order,
            # so we use the first one granted to the user
            if user_access > group_access.access_type:
                break

            if group_access.group.is_user_in_group(user):
                return group_access.access_type

        return user_access

    def __str__(self):
        result = 'Группа «%s»' % self.name
        if self.school is not None:
            result += ' для ' + str(self.school)
        return result
示例#17
0
class EntranceExamTaskCategory(models.Model):
    """
    Tasks are displayed in these categories on the exam page.
    """
    exam = models.ForeignKey(
        'EntranceExam',
        on_delete=models.CASCADE,
        verbose_name="экзамен",
        related_name='task_categories',
    )

    short_name = models.SlugField(
        help_text="Может состоять только из букв, цифр, знака подчеркивания и "
        "дефиса.", )

    title = models.CharField(
        verbose_name="заголовок",
        max_length=100,
        help_text="Заголовок категории, например «Практические задачи:»",
    )

    order = models.IntegerField(
        verbose_name="порядок",
        help_text="Категории задач отображаются в заданном порядке",
    )

    is_mandatory = models.BooleanField(
        verbose_name="обязательная категория",
        default=True,
        help_text="Обязательно ли решать задачи в этой категории, чтобы "
        "вступительная считалась выполненной?",
    )

    available_from_time = models.ForeignKey(
        'dates.KeyDate',
        on_delete=models.CASCADE,
        verbose_name="доступна c",
        related_name='+',
        null=True,
        blank=True,
        default=None,
        help_text="Момент времени, начиная с которого задачи этой категории "
        "показываются пользователям. Оставьте пустым, если задачи "
        "должны быть доступны с начала вступительной работы.",
    )

    available_to_time = models.ForeignKey(
        'dates.KeyDate',
        on_delete=models.CASCADE,
        verbose_name="доступна до",
        related_name='+',
        null=True,
        blank=True,
        default=None,
        help_text="Момент времени, после которого возможность послать решения "
        "по задачам этой категории будет закрыта. Оставьте пустым, "
        "если задачи должны быть доступны до конца вступительной "
        "работы.",
    )

    text_after_closing = models.TextField(
        blank=True,
        verbose_name="текст после закрытия",
        help_text="Текст, который показывается на странице задачи после "
        "закрытия задач этой категории, но до конца вступительной "
        "работы.\n"
        "Поддерживается Markdown.",
    )

    class Meta:
        unique_together = [('exam', 'short_name'), ('exam', 'order')]
        verbose_name = _('task category')
        verbose_name_plural = _('task categories')

    def __str__(self):
        return 'Категория задач «{}» для «{}»'.format(self.title, self.exam)

    def is_started_for_user(self, user):
        if self.available_from_time is None:
            return True
        return self.available_from_time.passed_for_user(user)

    def is_finished_for_user(self, user):
        if self.available_to_time is None:
            return False
        return self.available_to_time.passed_for_user(user)
示例#18
0
class EntranceExamTask(polymorphic.models.PolymorphicModel):
    title = models.CharField(max_length=100, help_text='Название')

    text = models.TextField(help_text='Формулировка задания')

    exam = models.ForeignKey(
        'EntranceExam',
        on_delete=models.CASCADE,
        related_name='%(class)s',
    )

    category = models.ForeignKey(
        'EntranceExamTaskCategory',
        on_delete=models.CASCADE,
        verbose_name='категория',
        related_name='tasks',
    )

    help_text = models.CharField(
        max_length=100,
        help_text='Дополнительная информация, например, сведения о формате '
        'ответа',
        blank=True)

    order = models.IntegerField(
        help_text='Задачи выстраиваются по возрастанию порядка', default=0)

    max_score = models.PositiveIntegerField()

    custom_description = models.TextField(
        help_text='Текст с описанием типа задачи. Оставьте пустым, тогда будет '
        'использован текст по умолчанию для данного вида задач. '
        'В этом тексте можно указать, например, '
        'для кого эта задача предназначена.\n'
        'Поддерживается Markdown',
        blank=True,
    )

    def __str__(self):
        return "{}: {}".format(self.exam.school.name, self.title)

    def save(self, *args, **kwargs):
        if self.category.exam_id != self.exam_id:
            raise IntegrityError(
                "{}.{}: task's category should belong to the same exam as the "
                "task itself".format(self.__module__, self.__class__.__name__))
        super().save(*args, **kwargs)

    def is_accepted_for_user(self, user):
        # Always not accepted by default. Override when subclassing.
        return False

    def is_solved_by_user(self, user):
        # Always not solved by default. Override when subclassing.
        return False

    @property
    def template_file(self):
        """
        Return template file name in folder templates/entrance/exam/
        """
        raise NotImplementedError('Child should define property template_file')

    @property
    def type_title(self):
        """
        Return title of blocks with these tasks
        """
        raise NotImplementedError('Child should define property type_title')

    def get_form_for_user(self, user, *args, **kwargs):
        """
        Return form for this task for the specified user
        """
        raise NotImplementedError('Child should define get_form_for_user()')

    @property
    def solution_class(self):
        raise NotImplementedError(
            'Child should define property solution_class')
示例#19
0
class EntranceStatus(models.Model):
    class Status(djchoices.DjangoChoices):
        NOT_PARTICIPATED = djchoices.ChoiceItem(1, 'Не участвовал в конкурсе')
        AUTO_REJECTED = djchoices.ChoiceItem(2, 'Автоматический отказ')
        NOT_ENROLLED = djchoices.ChoiceItem(3, 'Не прошёл по конкурсу')
        ENROLLED = djchoices.ChoiceItem(4, 'Поступил')
        PARTICIPATING = djchoices.ChoiceItem(5, 'Подал заявку')
        IN_RESERVE_LIST = djchoices.ChoiceItem(6, 'В резервном списке')

    school = models.ForeignKey(
        'schools.School',
        on_delete=models.CASCADE,
        related_name='entrance_statuses',
    )

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='entrance_statuses',
    )

    # created_by=None means system's auto creating
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='+',
        blank=True,
        null=True,
        default=None,
    )

    public_comment = models.TextField(
        help_text='Публичный комментарий. Может быть виден поступающему',
        blank=True,
    )

    private_comment = models.TextField(
        help_text='Приватный комментарий. Виден только админам вступительной',
        blank=True,
    )

    is_status_visible = models.BooleanField(default=False)

    status = models.IntegerField(choices=Status.choices,
                                 validators=[Status.validator])

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

    is_approved = models.BooleanField(
        help_text=
        'Подтверждено ли участие пользователем. Имеет смысл, только если статус = «Поступил»',
        default=False)

    approved_at = models.DateTimeField(null=True, blank=True, default=None)

    @property
    def is_enrolled(self):
        return self.status == self.Status.ENROLLED

    @property
    def is_in_reserve_list(self):
        return self.status == self.Status.IN_RESERVE_LIST

    def approve(self):
        self.is_approved = True
        self.approved_at = django.utils.timezone.now()
        self.save()

    def remove_approving(self):
        self.is_approved = False
        self.approved_at = None
        self.save()

    @classmethod
    def create_or_update(cls, school, user, status, **kwargs):
        with transaction.atomic():
            current = cls.objects.filter(school=school, user=user).first()
            if current is None:
                current = cls(school=school,
                              user=user,
                              status=status,
                              **kwargs)
            else:
                current.status = status
                for key, value in current:
                    setattr(current, key, value)
            current.save()

    @classmethod
    def get_visible_status(cls, school, user):
        return cls.objects.filter(school=school,
                                  user=user,
                                  is_status_visible=True).first()

    def __str__(self):
        return '%s %s' % (self.user, self.Status.values[self.status])

    class Meta:
        verbose_name_plural = 'User entrance statuses'
        unique_together = ('school', 'user')
示例#20
0
class Contest(polymorphic.models.PolymorphicModel):
    name = models.TextField(help_text='Contest name')

    is_visible_in_list = models.BooleanField(default=False)

    registration_type = models.CharField(
        max_length=20,
        choices=ContestRegistrationType.choices,
        validators=[ContestRegistrationType.validator])

    participation_mode = models.CharField(
        max_length=20,
        choices=ContestParticipationMode.choices,
        validators=[ContestParticipationMode.validator])

    start_time = models.DateTimeField(help_text='Contest start time')

    finish_time = models.DateTimeField(help_text='Contest finish time')

    registration_start_time = models.DateTimeField(
        help_text=
        'Contest registration start time, only for open and moderated registrations',
        blank=True,
        null=True)

    registration_finish_time = models.DateTimeField(
        help_text=
        'Contest registration finish time, only for open and moderated registration',
        blank=True,
        null=True)

    short_description = models.TextField(help_text='Shows on main page')

    description = models.TextField(
        help_text='Full description. Supports MarkDown')

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return urls.reverse('contests:contest', args=[self.id])

    def is_user_participating(self, user):
        if self.participation_mode == ContestParticipationMode.Individual:
            return self.participants.filter(
                individualparticipant__user=user).exists()
        elif self.participation_mode == ContestParticipationMode.Team:
            return self.participants.filter(
                teamparticipant__team__members=user).exists()
        else:
            raise ValueError('Unknown participation mode: %s' %
                             (self.participation_mode, ))

    def get_user_team(self, user):
        """
        Return Team object for user if contest is team-based and user is a participant
        Otherwise return None
        """
        if self.participation_mode != ContestParticipationMode.Team:
            return None

        team_participant = self.get_participant_for_user(user)
        if team_participant is None:
            return None

        return team_participant.team

    def get_participant_for_user(self, user):
        """ Returns IndividualParticipant or TeamParticipant """
        participant = None

        if user.is_anonymous:
            return None

        if self.participation_mode == ContestParticipationMode.Team:
            participant = self.participants.filter(
                teamparticipant__team__members=user).first()
        if self.participation_mode == ContestParticipationMode.Individual:
            participant = self.participants.filter(
                individualparticipant__user=user).first()

        return participant

    def can_register_now(self):
        return (self.registration_type in [
            ContestRegistrationType.Open, ContestRegistrationType.Moderated
        ] and self.registration_start_time <= timezone.now() <
                self.registration_finish_time)

    def can_register_in_future(self):
        return (self.registration_type in [
            ContestRegistrationType.Open, ContestRegistrationType.Moderated
        ] and timezone.now() < self.registration_start_time)

    def is_running(self):
        return self.start_time <= timezone.now() < self.finish_time

    def is_finished(self):
        return self.finish_time <= timezone.now()

    def is_started(self):
        return self.start_time <= timezone.now()

    def show_menu_on_top(self):
        return self.is_started()

    def is_team(self):
        return self.participation_mode == ContestParticipationMode.Team

    def is_individual(self):
        return self.participation_mode == ContestParticipationMode.Individual