class Lesson(models.Orderable): """ Intermediate model between a LessonPage and a Calendar. """ calendar = models.ParentalKey( 'CalendarPage', on_delete=models.CASCADE, null=True, blank=True, related_name='lessons', ) page = models.OneToOneField('LessonPage', null=True, blank=True, related_name='lesson') title = models.TextField( _('title'), help_text=_('A brief description for the lesson.'), ) date = models.DateField( _('date'), null=True, blank=True, help_text=_('Date scheduled for this lesson.'), ) panels = [ panels.FieldPanel('title'), panels.FieldPanel('date'), ]
class SingleUserGroup(models.Group): """Represent a group with a single user. Theses groups are useful to be plugged to models that could accept either a group or a single user reference.""" owner = models.OneToOneField(models.User, related_name='owned_group') def save(self, *args, **kwds): self.name = 'single-group-%s' % self.owner.username if not self.pk: super().save() self.user_set.add(self.owner) super().save(*args, **kwds) @classmethod def from_user(cls, user): """Return an existing single-user group for user or create a new one if it does not exist""" try: return cls.objects.get(owner=user) except cls.DoesNotExist: group = cls(owner=user) group.save() return group
class LessonInfo(models.Orderable): """ Intermediate model between a LessonPage and a Calendar to make it orderable. """ class Meta: verbose_name = _('Lesson') verbose_name_plural = _('Lessons') calendar = models.ParentalKey( Calendar, on_delete=models.CASCADE, null=True, blank=True, related_name='info', ) page = models.OneToOneField( Lesson, null=True, blank=True, related_name='info', ) title = models.TextField( _('title'), help_text=_('A brief description for the lesson.'), ) date = models.DateField( _('date'), null=True, blank=True, help_text=_('Date scheduled for this lesson.'), ) def save(self, *args, **kwargs): if self.pk is None and self.page is None: self.page = lesson_page = Lesson( title=self.title, slug=slugify(self.title), ) lesson_page._created_for_lesson = self self.calendar.add_child(instance=lesson_page) super().save(*args, **kwargs) panels = [ panels.FieldPanel('title'), panels.FieldPanel('date'), ]
class SyncCodeEditItem(models.Model): """ A simple state of the code in a SyncCodeActivity. """ activity = models.ForeignKey(SyncCodeActivity, related_name='data') text = models.TextField() next = models.OneToOneField('self', blank=True, null=True, related_name='previous') timestamp = models.DateTimeField(auto_now=True) @property def prev(self): try: return self.previous except ObjectDoesNotExist: return None
class Score(models.Model): """ A cache to keep track of the total number of points issued to each user. This can be recreated at any type from the GivenPoints table. """ user = models.OneToOneField( models.User, primary_key=True, related_name='score', ) points = models.PositiveIntegerField() objects = ScoreQuerySet.as_manager() class Meta: ordering = '-points', 'user' def __int__(self): return self.points
class Profile(UserenaBaseProfile): """ Userena profile. Social information about our users. """ user = models.OneToOneField( models.User, unique=True, verbose_name=_('user'), related_name='profile', ) nickname = models.CharField(max_length=50, blank=True, null=True) phone = models.CharField(max_length=20, blank=True, null=True) gender = models.SmallIntegerField(_('gender'), choices=[(0, _('male')), (1, _('female'))], blank=True, null=True) date_of_birth = models.DateField(_('date of birth'), blank=True, null=True) website = models.URLField(blank=True, null=True) about_me = models.TextField(blank=True, null=True) @property def age(self): if self.date_of_birth is None: return None today = timezone.now().date() return int(round((today - self.date_of_birth).days / 365.25)) @property def contact_classes(self): user = self.user try: friends = user.friends colleagues = user.staff_contacts staff = user.colleagues except AttributeError as ex: raise RuntimeError(ex) return [friends, colleagues, staff] def custom_fields(self, flat=False): """Return a dictionary with all custom fields""" D = {} for value in self.custom_field_values.all(): category = value.category_name field_name = value.field_name data = value.data if flat: D[(category, field_name)] = data else: category_dict = D.setdefault(category, {}) category_dict[field_name] = data return D class Meta: permissions = ( ('student', _('Can access/modify data visible to student\'s')), ('teacher', _('Can access/modify data visible only to Teacher\'s')), ) def __str__(self): if self.user is None: return __('unbound profile') return __('%(name)s\'s profile') % {'name': self.user.get_full_name()} def __repr__(self): return str(self.contact_classes)
class Profile(UserenaBaseProfile, models.CodeschoolPage): """ Social information about users. """ class Meta: permissions = ( ('student', _('Can access/modify data visible to student\'s')), ('teacher', _('Can access/modify data visible only to Teacher\'s')), ) username = delegate_to('user', True) first_name = delegate_to('user') last_name = delegate_to('user') email = delegate_to('user') @property def short_description(self): return '%s (id: %s)' % (self.get_full_name_or_username(), self.school_id) @property def age(self): if self.date_of_birth is None: return None today = timezone.now().date() return int(round((today - self.date_of_birth).years)) user = models.OneToOneField( models.User, unique=True, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('user'), related_name='_profile', ) school_id = models.CharField( _('school id'), help_text=_('Identification number in your school issued id card.'), max_length=50, blank=True, null=True) nickname = models.CharField(max_length=50, blank=True, null=True) phone = models.CharField(max_length=20, blank=True, null=True) gender = models.SmallIntegerField(_('gender'), choices=[(0, _('male')), (1, _('female'))], blank=True, null=True) date_of_birth = models.DateField(_('date of birth'), blank=True, null=True) website = models.URLField(blank=True, null=True) about_me = models.RichTextField(blank=True, null=True) objects = ProfileManager.from_queryset(models.PageManager)() def __init__(self, *args, **kwargs): if 'user' in kwargs and 'id' not in kwargs: kwargs.setdefault('parent_page', profile_root()) super().__init__(*args, **kwargs) if self.pk is None and self.user is not None: user = self.user self.title = self.title or __("%(name)s's profile") % { 'name': user.get_full_name() or user.username } self.slug = self.slug or user.username.replace('.', '-') def __str__(self): if self.user is None: return __('Unbound profile') full_name = self.user.get_full_name() or self.user.username return __('%(name)s\'s profile') % {'name': full_name} def get_full_name_or_username(self): name = self.user.get_full_name() if name: return name else: return self.user.username # Wagtail admin parent_page_types = ['cs_core.ProfileRoot'] content_panels = models.CodeschoolPage.content_panels + [ panels.MultiFieldPanel([ panels.FieldPanel('school_id'), ], heading='Required information'), panels.MultiFieldPanel([ panels.FieldPanel('nickname'), panels.FieldPanel('phone'), panels.FieldPanel('gender'), panels.FieldPanel('date_of_birth'), ], heading=_('Personal Info')), panels.MultiFieldPanel([ panels.FieldPanel('website'), ], heading=_('Web presence')), panels.RichTextFieldPanel('about_me'), ]
class Profile(UserenaBaseProfile, models.Page): """ Social information about users. """ class Meta: permissions = ( ('student', _('Can access/modify data visible to student\'s')), ('teacher', _('Can access/modify data visible only to Teacher\'s')), ) user = models.OneToOneField( User, unique=True, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('user'), related_name='profile', ) school_id = models.CharField( _('school id'), help_text=_('Identification number in your school issued id card.'), max_length=50, blank=True, null=True) nickname = models.CharField(max_length=50, blank=True, null=True) phone = models.CharField(max_length=20, blank=True, null=True) gender = models.SmallIntegerField(_('gender'), choices=[(0, _('male')), (1, _('female'))], blank=True, null=True) date_of_birth = models.DateField(_('date of birth'), blank=True, null=True) website = models.URLField(blank=True, null=True) about_me = models.RichTextField(blank=True, null=True) objects = ProfileManager() # Delegates and properties username = delegate_to('user', True) first_name = delegate_to('user') last_name = delegate_to('user') email = delegate_to('user') @property def short_description(self): return '%s (id: %s)' % (self.get_full_name_or_username(), self.school_id) @property def age(self): if self.date_of_birth is None: return None today = timezone.now().date() birthday = self.date_of_birth years = today.year - birthday.year birthday = datetime.date(today.year, birthday.month, birthday.day) if birthday > today: return years - 1 else: return years def __str__(self): if self.user is None: return __('Unbound profile') full_name = self.user.get_full_name() or self.user.username return __('%(name)s\'s profile') % {'name': full_name} def save(self, *args, **kwargs): user = self.user if not self.title: self.title = self.title or __("%(name)s's profile") % { 'name': user.get_full_name() or user.username } if not self.slug: self.slug = user.username.replace('.', '-') # Set parent page, if necessary if not self.path: root = ProfileList.objects.instance() root.add_child(instance=self) else: super().save(*args, **kwargs) def get_full_name_or_username(self): name = self.user.get_full_name() if name: return name else: return self.user.username # Serving pages template = 'cs_auth/profile-detail.jinja2' def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context['profile'] = self return context # Wagtail admin parent_page_types = ['ProfileList'] content_panels = models.Page.content_panels + [ panels.MultiFieldPanel([ panels.FieldPanel('school_id'), ], heading='Required information'), panels.MultiFieldPanel([ panels.FieldPanel('nickname'), panels.FieldPanel('phone'), panels.FieldPanel('gender'), panels.FieldPanel('date_of_birth'), ], heading=_('Personal Info')), panels.MultiFieldPanel([ panels.FieldPanel('website'), ], heading=_('Web presence')), panels.RichTextFieldPanel('about_me'), ]
class Profile(UserenaBaseProfile): """ Social information about users. """ class Meta: permissions = ( ('student', _('Can access/modify data visible to student\'s')), ('teacher', _('Can access/modify data visible only to Teacher\'s')), ) GENDER_MALE, GENDER_FEMALE = 0, 1 user = models.OneToOneField( User, verbose_name=_('user'), unique=True, blank=True, null=True, on_delete=models.SET_NULL, related_name='profile', ) school_id = models.CharField( _('school id'), max_length=50, blank=True, null=True, help_text=_('Identification number in your school issued id card.'), ) is_teacher = models.BooleanField(default=False) nickname = models.CharField(max_length=50, blank=True, null=True) phone = models.CharField(max_length=20, blank=True, null=True) gender = models.SmallIntegerField(_('gender'), choices=[(GENDER_MALE, _('Male')), (GENDER_FEMALE, _('Female'))], blank=True, null=True) date_of_birth = models.DateField(_('date of birth'), blank=True, null=True) website = models.URLField(blank=True, null=True) about_me = models.RichTextField(blank=True, null=True) # Delegates and properties username = delegate_to('user', True) first_name = delegate_to('user') last_name = delegate_to('user') email = delegate_to('user') @property def age(self): if self.date_of_birth is None: return None today = timezone.now().date() birthday = self.date_of_birth years = today.year - birthday.year birthday = datetime.date(today.year, birthday.month, birthday.day) if birthday > today: return years - 1 else: return years def __str__(self): if self.user is None: return __('Unbound profile') full_name = self.user.get_full_name() or self.user.username return __('%(name)s\'s profile') % {'name': full_name} def get_full_name_or_username(self): name = self.user.get_full_name() if name: return name else: return self.user.username def get_absolute_url(self): return reverse('auth:profile-detail', kwargs={'username': self.user.username}) # Serving pages template = 'cs_auth/profile-detail.jinja2' def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context['profile'] = self return context # Wagtail admin panels = [ panels.MultiFieldPanel([ panels.FieldPanel('school_id'), ], heading='Required information'), panels.MultiFieldPanel([ panels.FieldPanel('nickname'), panels.FieldPanel('phone'), panels.FieldPanel('gender'), panels.FieldPanel('date_of_birth'), ], heading=_('Personal Info')), panels.MultiFieldPanel([ panels.FieldPanel('website'), ], heading=_('Web presence')), panels.RichTextFieldPanel('about_me'), ]
class Feedback(HasProgressMixin, models.TimeStampedModel, models.PolymorphicModel): """ Feedback for user. Usually there will be one feedback per submission, but this figure may vary from case to case. """ TITLE_OK = _('Correct answer!') TITLE_PARTIAL = _('Partially correct.') TITLE_WRONG = _('Wrong answer.') TITLE_NOT_GRADED = _('Not graded.') MESSAGE_OK = _('*Congratulations!* Your response is correct!') MESSAGE_OK_WITH_PENALTIES = _( 'Your response is correct, but you did not achieved the maximum grade.' ) MESSAGE_WRONG = _('I\'m sorry. Wrong response response!') MESSAGE_PARTIAL = _( 'Your answer is partially correct: you achieved %(grade)d%% of ' 'the total grade.') MESSAGE_NOT_GRADED = _('Your response has not been graded yet!') submission = models.OneToOneField('Submission', related_name='feedback') manual_grading = models.BooleanField( default=True, help_text=_('True if feedback was created manually by a human.')) grader_user = models.ForeignKey( models.User, blank=True, null=True, help_text=_('User that performed the manual grading.')) given_grade_pc = models.DecimalField( _('percentage of maximum grade'), help_text=_( 'This grade is given by the auto-grader and represents the grade ' 'for the response before accounting for any bonuses or penalties.' ), max_digits=6, decimal_places=3, validators=[grade_validator], blank=True, null=True, ) final_grade_pc = models.DecimalField( _('final grade'), help_text=_( 'Similar to given_grade, but can account for additional factors ' 'such as delay penalties or for any other reason the teacher may ' 'want to override the student\'s grade.'), max_digits=6, decimal_places=3, validators=[grade_validator], blank=True, null=True, ) is_correct = models.BooleanField(default=False) progress = lazy(lambda x: x.submission.progress) def get_feedback_title(self): """ Return a title summarizing the feedback result. The default set of titles come from the list: * Correct answer! * Partially correct. * Wrong answer. * Not graded. Different question types may define additional values to this list. """ grade = self.given_grade_pc if grade == 100: return self.TITLE_OK elif grade is not None and grade > 0: return self.TITLE_PARTIAL elif grade == 0: return self.TITLE_WRONG else: return self.TITLE_NOT_GRADED def update_autograde(self): """ Compute and set self.given_grade. This function may change other states in the feedback object, depending on the activity. """ activity = self.activity submission = self.submission self.given_grade_pc = self.get_given_autograde(submission, activity) def get_given_autograde(self, submission, activity): """ Atomic and testable version of autograde_update(). Subclasses should overide this method. Args: submission: a submission object activity: the activity the submission refers to Returns: A numeric value between 0 and 100 with the assigned grade. """ name = self.__class__.__name__ raise ImproperlyConfigured( 'Class %s must implement the .autograde() method.' % name) def update_final_grade(self): """ Compute final grade applying all possible penalties and bonuses. """ self.final_grade_pc = self.given_grade_pc if self.given_grade_pc == 100: self.is_correct = True def render_message(self, **kwargs): """ Renders feedback message. """ if self.is_correct and self.final_grade_pc >= self.given_grade_pc: msg = self.MESSAGE_OK elif self.is_correct and self.final_grade_pc < self.given_grade_pc: msg = self.MESSAGE_OK_WITH_PENALTIES elif not self.is_correct and self.given_grade_pc > 0: msg = self.MESSAGE_PARTIAL else: msg = self.MESSAGE_WRONG return p(msg, cls='cs-feedback-message').render(**kwargs)
class Profile(models.TimeStampedModel): """ Social information about users. """ GENDER_MALE, GENDER_FEMALE, GENDER_OTHER = 0, 1, 2 GENDER_CHOICES = [ (GENDER_MALE, _('Male')), (GENDER_FEMALE, _('Female')), (GENDER_OTHER, _('Other')), ] VISIBILITY_PUBLIC, VISIBILITY_FRIENDS, VISIBILITY_HIDDEN = range(3) VISIBILITY_CHOICES = enumerate( [_('Any Codeschool user'), _('Only friends'), _('Private')]) visibility = models.IntegerField( _('Visibility'), choices=VISIBILITY_CHOICES, default=VISIBILITY_FRIENDS, help_text=_('Who do you want to share information in your profile?')) user = models.OneToOneField( User, verbose_name=_('user'), related_name='profile_ref', ) phone = models.CharField( _('Phone'), max_length=20, blank=True, null=True, ) gender = models.SmallIntegerField( _('gender'), choices=GENDER_CHOICES, blank=True, null=True, ) date_of_birth = models.DateField( _('date of birth'), blank=True, null=True, ) website = models.URLField( _('Website'), blank=True, null=True, help_text=_('A website that is shown publicly in your profile.')) about_me = models.RichTextField( _('About me'), blank=True, help_text=_('A small description about yourself.')) # Delegates and properties username = delegate_to('user', True) name = delegate_to('user') email = delegate_to('user') class Meta: permissions = ( ('student', _('Can access/modify data visible to student\'s')), ('teacher', _('Can access/modify data visible only to Teacher\'s')), ) @property def age(self): if self.date_of_birth is None: return None today = timezone.now().date() birthday = self.date_of_birth years = today.year - birthday.year birthday = datetime.date(today.year, birthday.month, birthday.day) if birthday > today: return years - 1 else: return years def __str__(self): if self.user is None: return __('Unbound profile') full_name = self.user.get_full_name() or self.user.username return __('%(name)s\'s profile') % {'name': full_name} def get_absolute_url(self): self.user.get_absolute_url()