Skip to content
Snippets Groups Projects
models.py 5.26 KiB
Newer Older
""" Model definitions for the quiz """

from django.core.exceptions import ValidationError
dominip89's avatar
dominip89 committed
from django.db import models

borzechof99's avatar
borzechof99 committed
    This function acts as a validator for ratings.
    Sadly, it isn't called automatically, so it needs to be used manually.
borzechof99's avatar
borzechof99 committed
    if not ((10 >= value >= 1) or value == -1):
        raise ValidationError(u"%s is not a valid rating!" % value)


class CriterionRating(models.Model):
    """
    This is the relation between Sport and Criterion.
    You can use it to add a rating for a specific criterion to a sport.
    To see it's usage check Sport.rate() and Sport.get_rating()
    """

    rating = models.IntegerField(validators=[validate_rating])
    criterion = models.ForeignKey("Criterion", on_delete=models.CASCADE)
    sport = models.ForeignKey("Sport", on_delete=models.CASCADE)
    def __str__(self):
        return str(self.sport) + " - " + str(self.criterion) + ": " + str(self.rating)

class SportManager(models.Manager):
    """
    Manages Creation of Sport Objects
    Since every Criterion Connection needs to be present in the DB at all times,
    the connections are made at creation of the Sport object. For this, Sport objects need to be
    created through this Manager Class.

    Docs: https://docs.djangoproject.com/en/3.2/ref/models/instances/#creating-objects
    """

    def create_sport(self, **kwargs):
borzechof99's avatar
borzechof99 committed
        """
        Creates new Sport Object and every CriterionRating for it
        """
        sport = self.create(**kwargs)

        for crit in Criterion.objects.iterator():
            sport.rate(crit, -1)

        return sport


class Sport(models.Model):
    """
    Defines a Sport with name, url that leads to the booking page.
    A sport includes ratings for all criterions.
    (e.g. How much it corresponds to the criterion "Martial Arts")
    """

    name = models.TextField()
    url = models.URLField()
    criteria_ratings = models.ManyToManyField("Criterion", through="CriterionRating")
    def __str__(self):
    def rate(self, criterion, rating):
        """Defines how much (rating) the sport meets the given criterion"""
        rating_obj, _ = CriterionRating.objects.get_or_create(
            sport=self, criterion=criterion, defaults={"rating": rating}
        )
        validate_rating(rating)
        rating_obj.rating = rating
        rating_obj.save()
        return rating_obj
    def get_rating(self, criterion):
        """Returns how much the sport meets the given criterion"""
        criterion_rating = CriterionRating.objects.get(sport=self, criterion=criterion)
        return criterion_rating.rating
    def is_filled(self):
        """
        Returns a Boolean whether all Criterions are given a valid rating (unequal to -1)
        """

        for crit in self.criteria_ratings.iterator():
            if self.get_rating(crit) == -1:
                return False

        return True

class CriterionManager(models.Manager):
    """
    Manages Creation of Criterion Objects
borzechof99's avatar
borzechof99 committed
    Since every Sport Object needs to be rated in every Criterion,
    when a new Criterion is created, every Sport object needs to be rated for that Criterion.
borzechof99's avatar
borzechof99 committed
    As a default value, -1 is entered so that it can be recognized that no true value is given.

    Docs: https://docs.djangoproject.com/en/3.2/ref/models/instances/#creating-objects
    """

    def create_criterion(self, **kwargs):
borzechof99's avatar
borzechof99 committed
        """
        Creates a Criterion Object and Rates every existing Sport with -1
        """
        crit = Criterion(**kwargs)

        # Criterion needs to be saved before it can be connected to a sport
        crit.save()

        for sport in Sport.objects.iterator():
            sport.rate(crit, -1)

        return crit


class Criterion(models.Model):
    """
    Defines a Sport property that is used a a criterion for our quiz.
    (e.g. Individual or Team sport)
    """

    name = models.TextField()
    def __str__(self):
        return self.name

borzechof99's avatar
borzechof99 committed
        """
        Get Number of Sports with Rating larger than 1 and the cumulated sum of all ratings
        TODO: Think about Usefulness of Rating Sums with 1 as Min Value
borzechof99's avatar
borzechof99 committed
        """
borzechof99's avatar
borzechof99 committed
        rating_sum = 0

        for rating_obj in CriterionRating.objects.filter(criterion=self):
borzechof99's avatar
borzechof99 committed
            rating_sum += rating_obj.rating
            if rating_obj.rating > 1:
borzechof99's avatar
borzechof99 committed
        return num_active, rating_sum

class CallToMove(models.Model):
    """Defines text and image that are used to show a call to move between questions"""

    text = models.TextField()
    image = models.ImageField(null=True, max_length=200)

    def __str__(self):
        return self.text


class KnowledgeSnack(models.Model):
    """Defines text and image that are used to show a KnowledgeSnack between questions"""

    text = models.TextField()
    image = models.ImageField(null=True, max_length=200)
fu2662cw's avatar
fu2662cw committed

    def __str__(self):
        return self.text

fu2662cw's avatar
fu2662cw committed

fu2662cw's avatar
fu2662cw committed
class Question(models.Model):
    """Defines a Question that is assigned to exactly one Criterion"""

    text = models.TextField()
fu2662cw's avatar
fu2662cw committed
    criterion = models.OneToOneField(
        Criterion, on_delete=models.CASCADE, primary_key=True
    )

    def __str__(self):
        return self.text