Something went wrong on our end
-
borzechof99 authoredborzechof99 authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
models.py 5.26 KiB
""" Model definitions for the quiz """
from django.core.exceptions import ValidationError
from django.db import models
def validate_rating(value):
"""
This function acts as a validator for ratings.
Sadly, it isn't called automatically, so it needs to be used manually.
"""
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):
"""
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")
objects = SportManager()
def __str__(self):
return self.name
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
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.
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):
"""
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()
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
rating_sum = 0
for rating_obj in CriterionRating.objects.filter(criterion=self):
rating_sum += rating_obj.rating
if rating_obj.rating > 1:
num_active += 1
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)
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