Пример #1
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)
Пример #2
0
class ExpectedUsername(models.Model):
    """A string of an allowed value for e.g., white listing user names that
    can enroll in a specific course/activity/event etc.

    This class is used to create white lists of users that might not exist yet
    in the database. If you are sure that your users exist, maybe it is more
    convenient to create a regular Group."""

    username = models.CharField(max_length=100, )
    listener_id = models.IntegerField(
        null=True,
        blank=True,
    )
    listener_type = models.ForeignKey(
        models.ContentType,
        null=True,
        blank=True,
    )
    listener_action = models.CharField(
        max_length=30,
        blank=True,
    )

    @property
    def exists(self):
        return models.User.objects.filter(username=self.username).size() == 1

    @property
    def is_active(self):
        try:
            return models.User.objects.get(username=self.username).is_active
        except models.User.DoesNotExist:
            return False

    @property
    def listener(self):
        ctype = models.ContentType.objects.get(pk=self.listener_type)
        cls = ctype.model_class()
        try:
            return cls.objects.get(pk=self.listener_id)
        except cls.DoesNotExist:
            return None

    @property
    def user(self):
        return models.User.objects.get(username=self.username)

    def notify(self, user=None):
        """
        Notify that user with the given username was created.
        """

        if self.action:
            listener = self.listener
            if listener is not None:
                callback = getattr(listener, action)
                callback(user or self.user)

    def __str__(self):
        return self.username
Пример #3
0
class CustomFieldDefinition(models.Model):
    """
    Define a custom site-specific field for the user profile.
    """
    name = models.CharField(max_length=40)
    description = models.CharField(max_length=140)
    category = models.ForeignKey(CustomFieldCategory)
    enabled = models.BooleanField(
        _('enabled'),
        help_text=_('Enable or disable a custom field'),
        default=True,
    )
    type = models.CharField(default='text',
                            blank=True,
                            max_length=10,
                            choices=[('text', _('text')), ('int', _('int')),
                                     ('float', _('float')),
                                     ('date', _('date')),
                                     ('datetime', _('datetime'))])

    from_db_conversions = {
        'text': lambda x: x,
        'int': int,
        'float': float,
        'date': lambda x: datetime.date(*map(int, x.split('-'))),
        'datetime': lambda x: strptime("%Y-%m-%dT%H:%M:%S.%f%z", x),
    }

    to_db_conversions = {
        'date': lambda x: x.isoformat(),
        'datetime': lambda x: x.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
    }
Пример #4
0
class Personalization(models.Model):
    """
    Personalize a few cosmetic aspects of the activity.
    """

    name = models.CharField(max_length=140)
    icon_src = models.CharField(
        _('activity icon'),
        max_length=50,
        blank=True,
        help_text=_(
            'Optional icon name that can be used to personalize the activity. '
            'Material icons are available by using the "material:" namespace '
            'as in "material:menu".'),
    )

    @property
    def material_icon(self):
        """
        The material icon used in conjunction with the activity.
        """

        if self.icon_src.startswith('material:'):
            return self.icon_src[9:]
        return self.default_material_icon

    @property
    def icon_html(self):
        """
        A string of HTML source that points to the icon element fo the activity.
        """

        return '<i class="material-icon">%s</i>' % self.material_icon
Пример #5
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)
Пример #6
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)
Пример #7
0
class UrlItem(models.ListItemModel):
    """
    An URL item for the UrlActivity.
    """
    class Meta:
        root_field = 'activity'

    activity = models.ForeignKey('UrlActivity')
    url = models.URLField()
    name = models.CharField(max_length=50, blank=True)
    alt = models.CharField(max_length=50, blank=True)
Пример #8
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)
Пример #9
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)}
Пример #10
0
class BadgeTrack(models.Model):
    """
    A badge track represents a single type of action that can give an increasing
    number of badges for different levels of accomplishment.
    """

    name = models.CharField(_('name'), max_length=200)
    entry_point = models.ForeignKey(models.Page, related_name='badge_tracks')

    @lazy
    def badges_list(self):
        """
        A list of all badges in the track sorted by difficulty.
        """
        badges = list(self.badges.all())
        badges.sort(key=lambda x: x.value)
        return badges

    def issue_badges(self, user, **kwargs):
        """
        Issue all badges for the given
        """

        for badge in self.badges_list:
            badge.update_for_user(user, **kwargs)
Пример #11
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):
        """
Пример #12
0
class Group(models.Model):
    """
    A group of students.
    """

    name = models.CharField(max_length=100)
    users = models.ManyToManyField(models.User, related_name='+')
Пример #13
0
class FreeFormQuestion(Question):
    """
    A free form question is *not* automatically graded.

    The student can submit a resource that can be a text, code, file, image,
    etc and a human has to analyse and grade it manually.
    """

    Type = Type
    type = models.IntegerField(
        _('Text type'),
        choices=[
            (Type.CODE.value, _('Code')),
            (Type.RICHTEXT.value, _('Rich text')),
            (Type.FILE.value, _('File')),
            (Type.PHYSICAL.value, _('Physical delivery')),
        ],
        default=Type.CODE,
    )
    filter = models.CharField(
        _('filter'),
        max_length=30,
        blank=True,
        help_text=_(
            'Filters the response by some criteria.'
        ),
    )

    class Meta:
        autograde = False
Пример #14
0
class Event(models.Model):
    """
    Represents an event that we want to confirm attendance.
    """

    sheet = models.ForeignKey(AttendanceSheet, related_name='events')
    date = models.DateField()
    created = models.DateTimeField()
    expires = models.DateTimeField()
    passphrase = models.CharField(
        _('Passphrase'),
        max_length=200,
        help_text=_(
            'The passphrase is case-insensitive. We tolerate small typing '
            'errors.'
        ),
    )

    def update(self, commit=True):
        """
        Regenerate passphrase and increases expiration time.
        """

        new = self.passphrase
        while new == self.passphrase:
            new = phrase()
        self.passphrase = new
        self.expires += self.sheet.expiration_interval
        if commit:
            self.save()
Пример #15
0
class FreeFormSubmission(QuestionSubmission):
    """
    Submission object for free form questions.
    """

    data = models.TextField(blank=True)
    metadata = models.CharField(blank=True, max_length=200)
Пример #16
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
Пример #17
0
class Faculty(models.DescribablePage):
    """
    Describes a faculty/department or any institution that is responsible for
    managing disciplines.
    """

    location_coords = models.CharField(
        _('coordinates'),
        max_length=255,
        blank=True,
        null=True,
        help_text=_(
            'Latitude and longitude coordinates for the faculty building. The '
            'coordinates are selected from a Google Maps widget.'),
    )

    @property
    def courses(self):
        return apps.get_model(
            'cs_core', 'Course').objects.filter(path__startswith=self.path)

    # Wagtail admin
    parent_page_types = ['wagtailcore.Page']
    subpage_types = None
    subpage_types = ['Discipline']
    content_panels = models.DescribablePage.content_panels + [
        panels.MultiFieldPanel([
            panels.FieldPanel('location_coords', classname="gmap"),
        ],
                               heading=_('Location')),
    ]
Пример #18
0
class TimeSlot(models.Model):
    """
    Represents the weekly time slot that can be assigned to lessons for a
    given course.
    """

    class Meta:
        ordering = ('weekday', 'start')

    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY = range(7)
    WEEKDAY_CHOICES = [
        (MONDAY, _('Monday')),
        (TUESDAY, _('Tuesday')),
        (WEDNESDAY, _('Wednesday')),
        (THURSDAY, _('Thursday')),
        (FRIDAY, _('Friday')),
        (SATURDAY, _('Saturday')),
        (SUNDAY, _('Sunday'))
    ]
    course = models.ParentalKey(
        'Classroom',
        related_name='time_slots'
    )
    weekday = models.IntegerField(
        _('weekday'),
        choices=WEEKDAY_CHOICES,
        help_text=_('Day of the week in which this class takes place.')
    )
    start = models.TimeField(
        _('start'),
        blank=True,
        null=True,
        help_text=_('The time in which the class starts.'),
    )
    end = models.TimeField(
        _('ends'),
        blank=True,
        null=True,
        help_text=_('The time in which the class ends.'),
    )
    room = models.CharField(
        _('classroom'),
        max_length=100,
        blank=True,
        help_text=_('Name for the room in which this class takes place.'),
    )

    # Wagtail admin
    panels = [
        panels.FieldRowPanel([
            panels.FieldPanel('weekday', classname='col6'),
            panels.FieldPanel('room', classname='col6'),
        ]),
        panels.FieldRowPanel([
            panels.FieldPanel('start', classname='col6'),
            panels.FieldPanel('end', classname='col6'),
        ]),
    ]
Пример #19
0
class Discipline(models.TimeStampedModel):
    """
    Represents an academic discipline.
    """

    name = models.CharField(max_length=100)
    slug = models.SlugField(_('short name'))
    description = models.RichTextField(blank=True)
    syllabus = models.RichTextField(blank=True)
Пример #20
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
Пример #21
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)
Пример #22
0
class SocialPresenceSettings(BaseSetting):
    facebook = models.URLField(help_text='Your Facebook page URL')
    instagram = models.CharField(
        max_length=255, help_text='Your Instagram username, without the @')
    trip_advisor = models.URLField(help_text='Your Trip Advisor page URL')
    youtube = models.URLField(
        help_text='Your YouTube channel or user account URL')

    panels = [
        panels.FieldPanel('facebook'),
        panels.FieldPanel('instagram'),
        panels.FieldPanel('trip_advisor'),
    ]
Пример #23
0
class Feature(models.Model):
    """
    A Feature in a sentiment board.
    """

    board = models.ParentalKey(SentimentBoard, related_name='features')
    name = models.CodeschoolNameField()
    description = models.CodeschoolDescriptionField(blank=True)
    image = models.ImageField(blank=True, null=True)
    icon = models.CharField(max_length=50)

    class Meta:
        unique_together = [('board', 'name')]
Пример #24
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'),
    ]
Пример #25
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)}
Пример #26
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
Пример #27
0
class TimeSlot(models.Model):
    """Represents the weekly time slot that can be assigned to classes for a
    given course."""

    class Meta:
        unique_together = ('course', 'weekday')

    weekday = models.IntegerField(
        choices=[(0, 'Monday'), (1, 'Tuesday'), (2, 'Wednesday'),
                 (3, 'Thursday'), (4, 'Friday'), (5, 'Saturday'),
                 (6, 'Sunday')]
    )
    start = models.TimeField()
    end = models.TimeField()
    course = models.ForeignKey(Course)
    room = models.CharField(max_length=100, blank=True)
Пример #28
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)
Пример #29
0
class ResponseDataMixin(models.Model):
    """
    Mixin that adds response data for several fields stored in a JSON
    dictionary.
    """

    class Meta:
        abstract = True

    response_data = models.JSONField(
        null=True,
        blank=True,
    )
    response_hash = models.CharField(
        max_length=32,
        blank=True,
    )
Пример #30
0
class FreeTextQuestion(Question):
    TYPE_CODE = 0
    TYPE_RICHTEXT = 1

    text_type = models.IntegerField(
        _('Text type'),
        choices=[
            (TYPE_CODE, _('Code')),
            (TYPE_RICHTEXT, _('Rich text')),
        ],
        default=TYPE_CODE,
    )
    syntax_highlight = models.CharField(
        choices=[x.split(':') for x in formats],
        default='python',
        help_text=_('Syntax highlight for code based questions.'),
    )