exchange.py 5.13 KB
Newer Older
1
import logging
2
import re
3

4
from django.db import models, transaction
5

6
7
8
9
10
11
from backend_app.fields import JSONField
from backend_app.models.abstract.base import (
    BaseModel,
    BaseModelSerializer,
    BaseModelViewSet,
)
12
from backend_app.models.partner import Partner
13
from backend_app.models.shared import SEMESTER_OPTIONS
14
from backend_app.models.university import University
15
from backend_app.permissions.app_permissions import ReadOnly
16
from base_app.models import User
17

18
19
logger = logging.getLogger("django")

20
21
22

class Exchange(BaseModel):
    # This model should be filled with data from the ENT
23
24
25
26
27
    # 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(
28
        max_length=1, choices=SEMESTER_OPTIONS, default="A", null=True
29
30
31
32
    )
    duration = models.PositiveIntegerField(null=False)
    double_degree = models.BooleanField(null=False)
    master_obtained = models.BooleanField(null=False)
33
    student_major_and_semester = models.CharField(max_length=20, null=False, blank=True)
34
35
    student_minor = models.CharField(max_length=47, null=True, blank=True)
    student_option = models.CharField(max_length=7, null=True, blank=True)
36
37
38
39
40

    utc_allow_courses = models.BooleanField(null=False)
    utc_allow_login = models.BooleanField(null=False)

    # a bit of denormalization
41
42
    student_major = models.CharField(max_length=20, null=True, blank=True)
    student_semester = models.IntegerField(null=True)
43
44
45
46
47
    partner = models.ForeignKey(Partner, on_delete=models.PROTECT, null=True)
    university = models.ForeignKey(University, on_delete=models.PROTECT, null=True)
    # (managned by signals on course save)
    student = models.ForeignKey(User, on_delete=models.CASCADE, null=True)

48
49
50
    # Field to tell that for some reason there is no corresponding exchange in the UTC DB
    unlinked = models.BooleanField(default=False, null=False)

51
52
    def save(self, *args, **kwargs):
        """
53
        Custom handling of denormalization and character
54
        """
55
56
57
        if self.semester is not None:
            self.semester = self.semester.upper()

58
59
60
61
62
63
64
        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
65
                logger.error(
66
67
68
69
70
71
72
73
74
                    "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

75
76
77
78
79
80
81
82
83
84
        # 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

85
        super().save(*args, **kwargs)
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149


#####
#####
#####
#####
#####
#####


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():
        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")