Пример #1
0
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'),
    ]
Пример #2
0
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
Пример #3
0
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'),
    ]
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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)
Пример #7
0
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'),
    ]
Пример #8
0
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'),
    ]
Пример #9
0
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'),
    ]
Пример #10
0
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)
Пример #11
0
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()