Пример #1
0
class SourceItem(models.ListItemModel):
    """A file item for the FileDownloadActivity."""
    class Meta:
        root_field = 'activity'

    activity = models.ForeignKey('SourceCodeActivity')
    format = models.ForeignKey(
        'cs_core.models.fileformat.FileFormat',
        verbose_name=_('format'),
        default='txt',
        help_text=_('The file format for the source code.'),
    )
    name = models.CharField(
        _('name'),
        max_length=140,
        help_text='A short description of the given source code fragment')
    description = models.TextField(
        _('description'),
        blank=True,
        help_text=_(
            'A detailed description of the source code fragment. This field '
            'accepts Markdown.'))
    source = models.TextField(_('source'),
                              help_text=_('The source code fragment.'))
    visible = models.BooleanField(
        _('is visible'),
        default=True,
        help_text=_(
            'Non-visible source items are available for download, but are not '
            'included in the main page'),
    )

    def __str__(self):
        return self.name
Пример #2
0
class Badge(models.TimeStampedModel, models.PolymorphicModel):
    """
    An abstract badge that marks an accomplishment in a given badge track.
    """

    track = models.ForeignKey(BadgeTrack, related_name='badges')
    name = models.CharField(
        _('name'),
        max_length=200,
    )
    slug = models.CharField(unique=True)
    description = models.TextField(
        _('description'),
        help_text=_(
            'A detailed description of the accomplishment required to receive '
            'the badge.'),
    )
    message = models.TextField(
        _('message'),
        help_text=_(
            'The message displayed when users receive the given badge'))
    image = models.ImageField(
        upload_to='gamification/badges/',
        blank=True,
        null=True,
    )
    required_achievement = models.PositiveIntegerField(
        default=0,
        help_text=_(
            'Abstract quantity that associated with linear badge tracks.'),
    )
    level = models.PositiveIntegerField(
        _('Badge level'),
        help_text=_(
            'The badge level: for linear badge tracks, it defines the ordering'
            'between different badges.'),
    )
    extra = models.JSONField(default=dict, )
    users = models.ManyToManyField(
        models.User,
        through='GivenBadge',
        related_name='badges',
    )

    def issue_badge(self, user):
        """
        Issue badge for the given user.
        """

        self.users.add(user)
Пример #3
0
class CodePost(Post):
    """
    Post some code.
    """

    language = models.ForeignKey('core.ProgrammingLanguage', related_name='+')
    source = models.TextField()
Пример #4
0
class ProgrammingLanguage(models.Model):
    """Represents a programming language."""

    ref = models.CharField(max_length=10, primary_key=True)
    name = models.CharField(max_length=140)
    comments = models.TextField(blank=True)
    __populated = False

    @classmethod
    def from_ref(cls, ref):
        """Return the programming language object from the given ref."""

        return cls.objects.get(ref=ref)

    @classmethod
    def populate(cls):
        if not cls.__populated:
            for line in supported_languages.strip().splitlines():
                ref, _, name = line.partition(': ')
                cls.setdefault(ref, name)
            cls.__populated = True

    @classmethod
    def setdefault(cls, ref, *args, **kwds):
        """Create object if it does not exists."""

        try:
            return cls.objects.get(ref=ref)
        except cls.DoesNotExist:
            new = cls(ref, *args, **kwds)
            new.save()
            return new

    def __str__(self):
        return '%s (%s)' % (self.ref, self.name)
Пример #5
0
class ProgrammingLanguage(Base, models.Model):
    """Represents a programming language."""

    ref = models.CharField(max_length=10, primary_key=True)
    name = models.CharField(max_length=140)
    comments = models.TextField(blank=True)

    @classmethod
    def get_language(cls, ref):
        """Return the programming language object from the given ref."""

        try:
            ref = default_aliases.get(ref, ref)
            return cls.objects.get(ref=ref)
        except cls.DoesNotExist:
            name = default_languages.get(ref)
            if ref is None:
                raise
            else:
                return cls.objects.create(ref=ref, name=name)

    def save(self, *args, **kwargs):
        SourceFormat.setdefault(self.ref,
                                name=self.name,
                                comments=self.comments)
        super().save(*args, **kwargs)
Пример #6
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'),
    ]
Пример #7
0
class Badge(models.Model):
    """
    Represents an abstract badge.

    Instances of these class are not associated to specific users. GivenBadge
    makes the association between badges and users.
    """

    track = models.ForeignKey(BadgeTrack, related_name='badges')
    name = models.CharField(max_length=200)
    image = models.ImageField(
        upload_to='gamification/badges/',
        blank=True,
        null=True,
    )
    required_points = models.PositiveIntegerField(default=0)
    required_score = models.PositiveIntegerField(default=0)
    required_stars = models.PositiveIntegerField(default=0)
    description = models.TextField()
    details = models.RichTextField(blank=True)

    @property
    def value(self):
        """
        A sortable element that describes the overall badge difficulty.
        """
        return self.required_stars, self.required_points, self.required_score

    @classmethod
    def update_for_user(cls, user, **kwargs):
        """
Пример #8
0
class CodingIoResponse(QuestionResponse):
    source = models.TextField(blank=True)
    language = models.ForeignKey(ProgrammingLanguage)

    # Feedback properties
    feedback = property(lambda x: x.feedback_data)
    feedback_title = property(lambda x: x.feedback_data.title)
    testcase = property(lambda x: x.feedback_data.testcase)
    answer_key = property(lambda x: x.feedback_data.answer_key)
    is_correct = property(lambda x: x.feedback_data.is_correct)

    def autograde_compute(self):
        self.feedback_data = self.question.grade(self)
        return self.feedback_data.grade * 100

    def html_feedback(self):
        if self.is_done:
            return render_html(
                self.feedback,
                template_name='cs_questions/render/feedback.jinja2')
        else:
            return super().html_feedback()

    @classmethod
    def _recompute_all_responses(cls):
        for r in cls.objects.all():
            r.grade = r.get_grade_from_feedback()
            r.save()
Пример #9
0
class FreeFormSubmission(QuestionSubmission):
    """
    Submission object for free form questions.
    """

    data = models.TextField(blank=True)
    metadata = models.CharField(blank=True, max_length=200)
Пример #10
0
class PageActivity(Activity):
    """
    Students complete this activity by seeing the content of a webpage defined
    in markdown.

    This activity allows teachers to share arbitrary content with the students.
    """

    body = models.TextField()
Пример #11
0
class StringMatchQuestion(Question):
    answer = models.TextField()
    is_regex = models.BooleanField(default=True)

    def grade(self, response):
        if self.is_regex:
            value = response.value

        else:
            return super().grade(response)
Пример #12
0
class SourceFormat(Base, models.Model):
    """
    Generalizes a programming language to other non-programming based file
    formats.
    """
    # TODO: make it a base class for ProgrammingLanguage

    ref = models.CharField(max_length=10, primary_key=True)
    name = models.CharField(max_length=140)
    comments = models.TextField(blank=True)
Пример #13
0
class CodingIoSubmission(QuestionSubmission):
    """
    A response proxy class specialized in CodingIoQuestion responses.
    """

    source = models.TextField(blank=True)
    language = models.ForeignKey(ProgrammingLanguage)

    objects = manager_instance(QuestionSubmission,
                               CodingIoSubmissionQuerySet,
                               use_for_related_fields=True)
Пример #14
0
class Discipline(models.Model):
    """A discipline represents one abstract academic discipline.

    Each discipline can be associated with many courses."""

    name = models.CharField(_('name'), max_length=200)
    abbreviation = AutoSlugField(populate_from='name')
    short_description = models.TextField(_('short description'))
    long_description = RichTextField(_('long description'))

    def __str__(self):
        return '%s (%s)' % (self.name, self.abbreviation)
Пример #15
0
class FileItem(models.ListItemModel):
    """A file item for the FileDownloadActivity."""
    class Meta:
        root_field = 'activity'

    activity = models.ForeignKey('FileDownloadActivity')
    file = models.FileField(upload_to='file-activities/')
    name = models.TextField(blank=True)
    description = models.TextField(blank=True)

    # Derived properties
    size = property(lambda x: x.file.size)
    url = property(lambda x: x.file.url)
    open = property(lambda x: x.file.open)
    close = property(lambda x: x.file.close)
    save_file = property(lambda x: x.file.save)
    delete_file = property(lambda x: x.file.delete)

    def save(self, *args, **kwargs):
        if not self.name:
            self.name = os.path.basename(self.file.name)
        super().save(*args, **kwargs)
Пример #16
0
class TestState(models.TimeStampedModel):
    """
    Register iospec expansions for a given question.
    """
    class Meta:
        unique_together = [('question', 'hash')]

    question = models.ForeignKey('CodingIoQuestion')
    hash = models.models.CharField(max_length=32)
    uuid = models.UUIDField(default=uuid.uuid4, editable=False)
    pre_tests_source = models.TextField(blank=True)
    post_tests_source = models.TextField(blank=True)
    pre_tests_source_expansion = models.TextField(blank=True)
    post_tests_source_expansion = models.TextField(blank=True)

    @property
    def is_current(self):
        return self.hash == self.question.test_state_hash

    def __str__(self):
        status = 'current' if self.is_current else 'outdated'
        return 'TestState for %s (%s)' % (self.question, status)
Пример #17
0
class ExhibitEntry(models.ClusterableModel):
    """
    Each user submission
    """
    class Meta:
        unique_together = [('user', 'exhibit')]

    exhibit = models.ParentalKey(CodeExhibit, related_name='entries')
    user = models.ForeignKey(models.User,
                             related_name='+',
                             on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    source = models.TextField()
    image = models.ImageField(upload_to='images/code_exhibit/')
    # image = models.FileField(upload_to='images/code_exhibit/')
    votes_from = models.ManyToManyField(models.User, related_name='+')
    num_votes = models.IntegerField(default=int)
    objects = ExhibitEntryQuerySet.as_manager()

    def vote(self, user):
        """
        Register a vote from user.
        """

        if not self.votes_from.filter(id=user.id).count():
            self.votes_from.add(user)
            self.num_votes += 1
            self.save(update_fields=['num_votes'])

    def unvote(self, user):
        """
        Remove a vote from user.
        """

        if self.votes_from.filter(id=user.id).count():
            self.votes_from.remove(user)
            self.num_votes -= 1
            self.save(update_fields=['num_votes'])

    def icon_for_user(self, user):
        if user in self.votes_from.all():
            return 'star'
        return 'start_border'

    # Wagtail admin
    panels = [
        panels.FieldPanel('user'),
        panels.FieldPanel('source'),
        panels.FieldPanel('image'),
        panels.FieldPanel('num_votes'),
    ]
Пример #18
0
class NumericQuestion(Question):
    """
    A very simple question with a simple numeric answer.
    """

    correct_answer = models.FloatField(
        _('Correct answer'),
        help_text=_('The expected numeric answer for question.'))

    tolerance = models.FloatField(
        _('Tolerance'),
        default=0,
        help_text=_('If tolerance is zero, the responses must be exact.'),
    )
    label = models.CharField(
        _('Label'),
        max_length=100,
        default=_('Answer'),
        help_text=_(
            'The label text that is displayed in the submission form.'),
    )
    help_text = models.TextField(
        _('Help text'),
        blank=True,
        help_text=_(
            'Additional explanation that is displayed under the input form.'))

    class Meta:
        verbose_name = _('Numeric question')
        verbose_name_plural = _('Numeric questions')

    def get_form_class(self):
        class NumericForm(forms.Form):
            value = forms.FloatField(label=self.label, required=True)

        return NumericForm

    def get_form(self, *args, **kwargs):
        return self.get_form_class()(*args, **kwargs)

    # Serving Pages
    template = 'questions/numeric/detail.jinja2'

    def get_context(self, request, **kwargs):
        ctx = super().get_context(request, **kwargs)
        ctx['form'] = self.get_form(request.POST)
        return ctx

    def get_submission_kwargs(self, request, kwargs):
        return {'value': float(kwargs.get('value', None) or 0)}
Пример #19
0
class CodeCarouselItem(models.Orderable):
    """
    A simple state of the code in a SyncCodeActivity.
    """

    activity = models.ParentalKey('cs_core.CodeCarouselActivity',
                                  related_name='items')
    text = models.TextField()
    timestamp = models.DateTimeField(auto_now=True)

    # Wagtail admin
    panels = [
        panels.FieldPanel('text', widget=blocks.AceWidget()),
    ]
Пример #20
0
class CodingIoSubmission(QuestionSubmission):
    """
    A response proxy class specialized in CodingIoQuestion responses.
    """

    source = models.TextField(blank=True)
    language = models.ForeignKey('core.ProgrammingLanguage')

    objects = manager_instance(QuestionSubmission,
                               CodingIoSubmissionQuerySet,
                               use_for_related_fields=True)

    def compute_hash(self):
        return md5hash(self.source + self.language.ref)
Пример #21
0
class TextQuestion(Question):
    """
    A very simple question with a simple Text answer.
    """
    class Meta:
        verbose_name = _('Text question')
        verbose_name_plural = _('Text questions')

    correct_answer = models.CharField(
        _('Correct answer'),
        help_text=_('The expected Text answer for question.'),
        max_length=100,
    )

    label = models.CharField(
        _('Label'),
        max_length=100,
        default=_('Answer'),
        help_text=_(
            'The label text that is displayed in the submission form.'),
    )
    help_text = models.TextField(
        _('Help text'),
        blank=True,
        help_text=_(
            'Additional explanation that is displayed under the input form.'))

    instant_autograde = True

    def get_form_class(self):
        class TextForm(forms.Form):
            value = forms.CharField(label=self.label,
                                    required=True,
                                    max_length=250)

        return TextForm

    def get_form(self, *args, **kwargs):
        return self.get_form_class()(*args, **kwargs)

    # Serving Pages
    template = 'questions/text/detail.jinja2'

    def get_context(self, request, **kwargs):
        ctx = super().get_context(request, **kwargs)
        ctx['form'] = self.get_form(request.POST)
        return ctx

    def get_submission_kwargs(self, request, kwargs):
        return {'value': str(kwargs.get('value', None) or 0)}
Пример #22
0
class Lesson(models.ListItemModel):
    """A single lesson in a doubly linked list."""
    class Meta:
        verbose_name = _('Lesson')
        verbose_name_plural = _('Lessons')
        root_field = 'course'

    title = models.CharField(max_length=140, blank=True)
    description = models.TextField(blank=True)
    date = models.DateField(null=True, blank=True)
    course = models.ForeignKey(Course)

    def __str__(self):
        return self.title
Пример #23
0
class StringMatchQuestion(Question):
    """
    The student response is compared with an answer_key string either by
    simple string comparison or using a regular expression.
    """

    answer_key = models.TextField()
    is_regex = models.BooleanField(default=True)

    def grade(self, response):
        if self.is_regex:
            value = response.value

        else:
            return super().grade(response)
Пример #24
0
class FreeAnswerQuestion(Question):
    DATA_FILE = 'file'
    DATA_IMAGE = 'image'
    DATA_PDF = 'pdf'
    DATA_PLAIN = 'plain'
    DATA_RICHTEXT = 'richtext'
    DATA_CHOICES = (
        (DATA_FILE, _('Arbitary file')),
        (DATA_IMAGE, _('Image file')),
        (DATA_PDF, _('PDF file')),
        (DATA_RICHTEXT, _('Rich text input')),
        (DATA_RICHTEXT, _('Plain text input')),
    )
    metadata = models.TextField()
    data_type = models.CharField(choices=DATA_CHOICES, max_length=10)
    data_file = models.FileField(blank=True, null=True)
Пример #25
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'),
    ]
Пример #26
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
Пример #27
0
class FileFormat(models.Model):
    """
    Represents a source file format.

    These can be programming languages or some specific data format.
    """

    ref = models.CharField(max_length=10, unique=True)
    name = models.CharField(max_length=140)
    comments = models.TextField(blank=True)
    is_binary = models.BooleanField(default=False)
    is_language = models.BooleanField(default=False)
    is_supported = models.BooleanField(default=False)
    objects = models.Manager.from_queryset(SourceFormatQuerySet)()

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('name', kwargs.get('ref', 'unnamed format').title())
        super().__init__(*args, **kwargs)

    def ace_mode(self):
        """
        Return the ace mode associated with the language.
        """

        return ACE_ALIASES.get(self.ref)

    def pygments_mode(self):
        """
        Returns the Pygments mode associated with the language.
        """

        return self.ref

    def ejudge_ref(self):
        """
        A valid reference for the language in the ejudge framework.
        """

        return self.ref

    def __str__(self):
        return self.name
Пример #28
0
class LoginSettings(BaseSetting):
    username_as_school_id = models.BooleanField(
        default=False,
        help_text=_(
            'If true, force the username be equal to the school id for all '
            'student accounts.'))
    school_id_regex = models.TextField(
        default='',
        blank=True,
        help_text=_(
            'A regular expression for matching valid school ids. If blank, no'
            'check will be performed on the validity of the given school ids'),
    )
    panels = [
        panels.MultiFieldPanel([
            panels.FieldPanel('username_as_school_id'),
            panels.FieldPanel('school_id_regex'),
        ],
                               heading=_('School id configuration'))
    ]
Пример #29
0
class CustomFieldValue(models.Model):
    """
    Represents a value of a custom field.

    Since custom fields can have many different types, we store all of them
    as a TextField and provide serializers for each type in order to convert
    each field to the correct type. The ``value`` attribute is always converted
    to the correct type.
    """

    definition = models.ForeignKey(CustomFieldDefinition)
    profile = models.ForeignKey(Profile, related_name='custom_field_values')
    db_value = models.TextField()

    @property
    def value(self):
        tt = self.definition.type
        if tt == 'text':
            return self.db_value
        elif tt == 'float':
            return float(self.db_value)
Пример #30
0
class BadgeTrack(models.PolymorphicModel):
    """
    A badge track represents a single type of action that can give several
    badges for different levels of accomplishment.
    """

    name = models.CharField(
        _('name'),
        max_length=200,
    )
    slug = models.SlugField(unique=True)
    description = models.TextField(
        _('description'),
        help_text=_('A detailed description of the badge track.'),
    )
    extra = models.JSONField(default=dict)
    badge_class = None

    @classmethod
    def instance(cls):
        """
        Returns the default instance for the given track.
        """

        return cls()

    @classmethod
    def badge(cls, slug, name, description, message, **kwargs):
        """
        Construct a badge instance in the current track.
        """

        return cls.badge_class(track=cls.instance(),
                               name=name,
                               slug=slug,
                               description=description,
                               message=message,
                               **kwargs)