UserProfile.user_courses()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
1
from django.db import models, transaction
2
from django.contrib.auth.models import User
3
from django.utils import timezone
4
from django.shortcuts import get_object_or_404
5
6
from .assignment import Assignment
7
from .course import Course
8
from .submission import Submission
9
from .studyprogram import StudyProgram
10
11
12
class UserProfile(models.Model):
13
    user = models.OneToOneField(User, related_name='profile')
14
    student_id = models.CharField(max_length=30, blank=True, null=True)
15
    courses = models.ManyToManyField(
16
        Course, blank=True, related_name='participants', limit_choices_to={'active__exact': True})
17
    study_program = models.ForeignKey(
18
        StudyProgram, blank=True, null=True, related_name='students')
19
20
    class Meta:
21
        app_label = 'opensubmit'
22
23
    def add_course_safe(self, id):
24
        '''
25
            Adds a course for the user after conducting a set of sanity checks.
26
            Return the title of the course or an exception.
27
        '''
28
        course = get_object_or_404(Course, pk=int(id), active=True)
29
        if course not in self.courses.all():
30
            self.courses.add(course)
31
        return course.title
32
33
    def can_see_future(self):
34
        '''
35
        Decides if this user is allowed to work with assignments that
36
        have their starting date in the future.
37
        '''
38
        return self.user.is_staff
39
40
    def tutor_courses(self):
41
        '''
42
            Returns the list of courses this user is tutor or owner for.
43
        '''
44
        tutoring = self.user.courses_tutoring.all().filter(active__exact=True)
45
        owning = self.user.courses.all().filter(active__exact=True)
46
        result = (tutoring | owning).distinct()
47
        return result
48
49
    def user_courses(self):
50
        '''
51
            Returns the list of courses this user is subscribed for,
52
            or owning, or tutoring.
53
            This leads to the fact that tutors and owners don't need
54
            course membership.
55
        '''
56
        registered = self.courses.filter(active__exact=True).distinct()
57
        return (self.tutor_courses() | registered).distinct()
58
59
    def open_assignments(self):
60
        '''
61
            Returns the list of open assignments from the
62
            viewpoint of this user.
63
        '''
64
        # Include only assignments with future, or no, hard deadline
65
        qs = Assignment.objects.filter(hard_deadline__gt=timezone.now(
66
        )) | Assignment.objects.filter(hard_deadline__isnull=True)
67
        # Include only assignments that are already published,
68
        # as long as you are not a tutor / course owner
69
        if not self.can_see_future():
70
            qs = qs.filter(publish_at__lt=timezone.now())
71
        # Include only assignments from courses that you are registered for
72
        qs = qs.filter(course__in=self.user_courses())
73
        # Ordering of resulting list
74
        qs = qs.order_by('soft_deadline', '-gradingScheme', 'title')
75
        waiting_for_action = [subm.assignment for subm in self.user.authored.all(
76
        ).exclude(state=Submission.WITHDRAWN)]
77
        # Emulate is_null sorting for soft_deadline
78
        qs_without_soft_deadline = qs.filter(soft_deadline__isnull=True)
79
        qs_with_soft_deadline = qs.filter(soft_deadline__isnull=False)
80
        ass_list = [
81
            ass for ass in qs_without_soft_deadline if ass not in waiting_for_action]
82
        ass_list += [
83
            ass for ass in qs_with_soft_deadline if ass not in waiting_for_action]
84
        return ass_list
85
86
    def gone_assignments(self):
87
        '''
88
            Returns the list of past assignments the user did not submit for
89
            before the hard deadline.
90
        '''
91
        # Include only assignments with past hard deadline
92
        qs = Assignment.objects.filter(hard_deadline__lt=timezone.now())
93
        # Include only assignments from courses this user is registered for
94
        qs = qs.filter(course__in=self.user_courses())
95
        # Include only assignments this user has no submission for
96
        return qs.order_by('-hard_deadline')
97
98
99
def db_fixes(user):
100
    '''
101
        Fix users that already exist and never got a user profile attached.
102
        This may be user accounts that were created by the Django Social or manually by the admin.
103
104
        TODO: This belongs into a User post_save handler.
105
    '''
106
    profile, created = UserProfile.objects.get_or_create(user=user)
107
108
109
def user_unicode(self):
110
    '''
111
        Monkey patch for getting better user name stringification,
112
        user proxies did not make the job.
113
        Django's custom user model feature would have needed to be introduced
114
        before the first syncdb, which does not work for existing installations.
115
'''
116
    if self.email:
117
        shortened = self.email.split('@')[0]
118
        return '%s %s (%s@...)' % (self.first_name, self.last_name, shortened)
119
    elif self.first_name or self.last_name:
120
        return '%s %s' % (self.first_name, self.last_name)
121
    elif self.username:
122
        return '%s' % (self.username)
123
    else:
124
        return 'User %u' % (self.pk)
125
126
127
User.__str__ = user_unicode
128
129
130
@transaction.atomic
131
def move_user_data(primary, secondary):
132
    '''
133
        Moves all submissions and other data linked to the secondary user into the primary user.
134
        Nothing is deleted here, we just modify foreign user keys.
135
    '''
136
    # Update all submission authorships of the secondary to the primary
137
    submissions = Submission.objects.filter(authors__id=secondary.pk)
138
    for subm in submissions:
139
        if subm.submitter == secondary:
140
            subm.submitter = primary
141
        subm.authors.remove(secondary)
142
        subm.authors.add(primary)
143
        subm.save()
144
    # Transfer course registrations
145
    try:
146
        for course in secondary.profile.courses.all():
147
            primary.profile.courses.add(course)
148
            primary.profile.save()
149
    except UserProfile.DoesNotExist:
150
        # That's a database consistency problem, but he will go away anyway
151
        pass
152