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')
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
class EntranceLevelUpgradeRequirement(polymorphic.models.PolymorphicModel): base_level = models.ForeignKey( 'EntranceLevel', on_delete=models.CASCADE, related_name='+', ) created_at = models.DateTimeField(auto_now_add=True) def is_met_by_user(self, user): # Always met by default. Override when subclassing. return True
class EntranceLevelUpgrade(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, ) upgraded_to = models.ForeignKey( 'EntranceLevel', on_delete=models.CASCADE, related_name='+', ) created_at = models.DateTimeField(auto_now_add=True)
class GroupAccess(polymorphic.models.PolymorphicModel): class Type(djchoices.DjangoChoices): NONE = djchoices.ChoiceItem( value=0, label='Нет доступа, группа не видна', ) LIST_MEMBERS = djchoices.ChoiceItem( value=10, label='Может просматривать участников', ) EDIT_MEMBERS = djchoices.ChoiceItem( value=20, label='Может добавлять и удалять участников', ) ADMIN = djchoices.ChoiceItem( value=30, label='Полный доступ', ) to_group = models.ForeignKey( AbstractGroup, related_name='accesses', on_delete=models.CASCADE, ) access_type = models.PositiveIntegerField( choices=Type.choices, validators=[Type.validator], db_index=True, ) created_by = models.ForeignKey( users.models.User, related_name='+', on_delete=models.CASCADE, null=True, blank=True, help_text='Кем выдан доступ. Если None, то системой') created_at = models.DateTimeField( auto_now_add=True, db_index=True, ) class Meta: verbose_name = 'group access' verbose_name_plural = 'group accesses'
class QuestionnaireTypingDynamics(models.Model): user = models.ForeignKey( users.models.User, on_delete=models.CASCADE, related_name='+', ) questionnaire = models.ForeignKey( Questionnaire, on_delete=models.CASCADE, related_name='+', ) typing_data = sistema.models.CompressedTextField( help_text='JSON с данными о нажатиях клавиш') created_at = models.DateTimeField(auto_now_add=True)
class EntranceExam(models.Model): school = models.OneToOneField( 'schools.School', on_delete=models.CASCADE, related_name='entrance_exam', ) close_time = models.DateTimeField(blank=True, default=None, null=True) def __str__(self): return 'Вступительная работа для %s' % self.school def is_closed(self): return (self.close_time is not None and django.utils.timezone.now() >= self.close_time) def get_absolute_url(self): return reverse('school:entrance:exam', kwargs={'school_name': self.school.short_name})
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])
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()
class GroupMembership(models.Model): group = models.ForeignKey( ManuallyFilledGroup, related_name='+', on_delete=models.CASCADE, ) added_by = models.ForeignKey( users.models.User, null=True, blank=True, related_name='+', on_delete=models.CASCADE, help_text='Кем добавлен участник группы. None, если добавлено системой.' ) created_at = models.DateTimeField( auto_now_add=True, db_index=True, ) class Meta: abstract = True
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
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')