Newer
Older
""" Model definitions for the quiz """

borzechof99
committed
from django.core.exceptions import ValidationError

borzechof99
committed
def validate_rating(value):
"""
Sadly, it isn't called automatically, so it needs to be used manually.

borzechof99
committed
"""

borzechof99
committed
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()
"""

borzechof99
committed
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)

borzechof99
committed
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):
"""
Creates new Sport Object and every CriterionRating for it
"""

borzechof99
committed
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")

borzechof99
committed
objects = SportManager()
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

borzechof99
committed
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

borzechof99
committed
class CriterionManager(models.Manager):
"""
Manages Creation of Criterion Objects
Since every Sport Object needs to be rated in every Criterion,

borzechof99
committed
when a new Criterion is created, every Sport object needs to be rated for that Criterion.
As a default value, -1 is entered so that it can be recognized that no true value is given.

borzechof99
committed
Docs: https://docs.djangoproject.com/en/3.2/ref/models/instances/#creating-objects
"""
def create_criterion(self, **kwargs):
"""
Creates a Criterion Object and Rates every existing Sport with -1
"""

borzechof99
committed
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()

borzechof99
committed
objects = CriterionManager()
def __str__(self):
return self.name
def get_active_sum(self):
"""
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
num_active = 0
for rating_obj in CriterionRating.objects.filter(criterion=self):
rating_sum += rating_obj.rating
if rating_obj.rating > 1:
num_active += 1
"""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)
def __str__(self):
return self.text
class Question(models.Model):
"""Defines a Question that is assigned to exactly one Criterion"""
text = models.TextField()
criterion = models.OneToOneField(
Criterion, on_delete=models.CASCADE, primary_key=True
)
def __str__(self):
return self.text