Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

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

150

151

152

153

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