import logging
import re
from django.db import models, transaction
from backend_app.fields import JSONField
from backend_app.models.abstract.base import (
BaseModel,
BaseModelSerializer,
BaseModelViewSet,
)
from backend_app.models.partner import Partner
from backend_app.models.shared import SEMESTER_OPTIONS
from backend_app.models.university import University
from backend_app.permissions.app_permissions import ReadOnly
from base_app.models import User
logger = logging.getLogger("django")
class Exchange(BaseModel):
# This model should be filled with data from the ENT
# Not using utc_id as Primary Key here for our model to be more resilient
utc_id = models.IntegerField(null=False, unique=True)
utc_partner_id = models.IntegerField(null=True)
year = models.PositiveIntegerField(default=2018, null=True)
semester = models.CharField(
max_length=1, choices=SEMESTER_OPTIONS, default="A", null=True
)
duration = models.PositiveIntegerField(null=False)
double_degree = models.BooleanField(null=False)
master_obtained = models.BooleanField(null=False)
student_major_and_semester = models.CharField(max_length=20, null=False, blank=True)
student_minor = models.CharField(max_length=47, null=True, blank=True)
student_option = models.CharField(max_length=7, null=True, blank=True)
utc_allow_courses = models.BooleanField(null=False)
utc_allow_login = models.BooleanField(null=False)
# a bit of denormalization
student_major = models.CharField(max_length=20, null=True, blank=True)
student_semester = models.IntegerField(null=True)
partner = models.ForeignKey(Partner, on_delete=models.PROTECT, null=True)
university = models.ForeignKey(
University, on_delete=models.PROTECT, null=True, related_name="exchanges"
)
# (managned by signals on course save)
student = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, related_name="exchanges"
)
# Field to tell that for some reason there is no corresponding exchange in the UTC DB
unlinked = models.BooleanField(default=False, null=False)
def save(self, *args, **kwargs):
"""
Custom handling of denormalization and character
"""
if self.semester is not None:
self.semester = self.semester.upper()
if self.utc_partner_id is not None:
try:
self.partner = Partner.objects.get(utc_id=self.utc_partner_id)
self.university = self.partner.university
except Partner.DoesNotExist:
self.partner = None
self.university = None
logger.error(
"Trying to find partner {}"
"when updating exchange {} but it doesn't exist".format(
self.utc_partner_id, self.utc_id
)
)
else:
self.partner = None
self.university = None
# Updating student major and semester
regex = r"^(\w+)(\d+)$"
search = re.search(regex, self.student_major_and_semester)
if search is not None:
self.student_major = search.group(1)
self.student_semester = search.group(2)
else:
self.student_semester = None
self.student_minor = None
super().save(*args, **kwargs)
#####
#####
#####
#####
#####
#####
class UnivMajorMinors(BaseModel):
"""
Model to store denormalize data about all the exchanges
"""
university = models.ForeignKey(University, on_delete=models.CASCADE, null=False)
major = models.CharField(max_length=20, null=True, blank=True)
minors = JSONField(default=list)
class Meta:
unique_together = ("university", "major")
class UnivMajorMinorsSerializer(BaseModelSerializer):
class Meta:
model = UnivMajorMinors
fields = BaseModelSerializer.Meta.fields + ("university", "major", "minors")
class UnivMajorMinorsViewSet(BaseModelViewSet):
queryset = UnivMajorMinors.objects.all() # pylint: disable=E1101
serializer_class = UnivMajorMinorsSerializer
permission_classes = (ReadOnly,)
end_point_route = "univMajorMinors"
filterset_fields = ("university",)
required_filterset_fields = ("university",)
@transaction.atomic
def update_denormalized_univ_major_minor():
logger.info("Computing the denormalized univ, major and minor")
data = {}
for exchange in Exchange.objects.all().prefetch_related("university"):
university = exchange.university
if university is None:
continue
student_major = exchange.student_major
student_minor = exchange.student_minor
if university not in data.keys():
data[university] = {}
if student_major not in data[university].keys():
data[university][student_major] = set()
data[university][student_major].add(student_minor)
UnivMajorMinors.objects.all().delete()
for university, majors_and_minors in data.items():
for major, minors in majors_and_minors.items():
UnivMajorMinors.objects.update_or_create(
university=university, major=major, defaults=dict(minors=list(minors))
)
logger.info("Done computing the denormarlize major and minor")
|