AssignmentAdmin   A
last analyzed

Size/Duplication

Total Lines 35
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A get_queryset() 0 6 2
A formfield_for_foreignkey() 0 4 2
1
# Assignment admin interface
2
3
from collections import defaultdict
4
from django.contrib.admin import ModelAdmin
5
from django.utils.translation import ugettext_lazy as _
6
from django.db.models import Q
7
from opensubmit.models import Course
8
from django import forms
9
from django.core.exceptions import ValidationError
10
from django.utils.safestring import mark_safe
11
from django.core.urlresolvers import reverse
12
from django.utils.html import format_html
13
14
class AssignmentAdminForm(forms.ModelForm):
15
    def __init__(self, *args, **kwargs):
16
        '''
17
            The intention here is to correct the shown download URL for already existing test script uploads,
18
            which is suprisingly hard.
19
            The URL comes as read-only field from the underlying storage system implementation, which
20
            generates it from the relative file path and MEDIA_URL. Since we want to control all media file downloads,
21
            MEDIA_URL is not given in OpenSubmit, so the resulting URL does not exist. Since test scripts are not
22
            a separate model as submission files (*sick*), we cannot use the elegant get_absolute_url() override to
23
            fix the download URL for a test script. Instead, we hack the widget rendering here.
24
        '''
25
        super(AssignmentAdminForm, self).__init__(*args, **kwargs)
26
        if self.instance.pk:        # makes only sense if this is not a new assignment to be created
27
            if self.instance.validity_test_url():
28
                self.fields['attachment_test_validity'].widget.template_with_initial = (
29
                    '%(initial_text)s: <a href="'+self.instance.validity_test_url()+'">%(initial)s</a> '
30
                    '%(clear_template)s<br />%(input_text)s: %(input)s'
31
                )
32
            else:
33
                self.fields['attachment_test_validity'].widget.template_with_initial = (
34
                    '%(initial_text)s: %(clear_template)s<br />%(input_text)s: %(input)s'
35
                )
36
            if self.instance.full_test_url():
37
                self.fields['attachment_test_full'].widget.template_with_initial = (
38
                    '%(initial_text)s: <a href="'+self.instance.full_test_url()+'">%(initial)s</a> '
39
                    '%(clear_template)s<br />%(input_text)s: %(input)s'
40
                )
41
            else:
42
                self.fields['attachment_test_full'].widget.template_with_initial = (
43
                    '%(initial_text)s: %(clear_template)s<br />%(input_text)s: %(input)s'
44
                )
45
            if self.instance.url():
46
                self.fields['description'].widget.template_with_initial = (
47
                    '%(initial_text)s: <a href="'+self.instance.url()+'">%(initial)s</a> '
48
                    '%(clear_template)s<br />%(input_text)s: %(input)s'
49
                )
50
            else:
51
                self.fields['description'].widget.template_with_initial = (
52
                    '%(initial_text)s: %(clear_template)s<br />%(input_text)s: %(input)s'
53
                )
54
55
    def clean(self):
56
        '''
57
            Check if such an assignment configuration makes sense, and reject it otherwise.
58
            This mainly relates to interdependencies between the different fields, since
59
            single field constraints are already clatified by the Django model configuration.
60
        '''
61
        super(AssignmentAdminForm, self).clean()
62
        d = defaultdict(lambda: False)
63
        d.update(self.cleaned_data)
64
        # Having validation or full test enabled demands file upload
65
        if d['attachment_test_validity'] and not d['has_attachment']:
66
            raise ValidationError('You cannot have a validation script without allowing file upload.')
67
        if d['attachment_test_full'] and not d['has_attachment']:
68
            raise ValidationError('You cannot have a full test script without allowing file upload.')
69
        # Having validation or full test enabled demands a test machine
70
        if d['attachment_test_validity'] and 'test_machines' in d and not len(d['test_machines'])>0:
71
            raise ValidationError('You cannot have a validation script without specifying test machines.')
72
        if d['attachment_test_full'] and 'test_machines' in d and not len(d['test_machines'])>0:
73
            raise ValidationError('You cannot have a full test script without specifying test machines.')
74
        if d['download'] and d['description']:
75
            raise ValidationError('You can only have a description link OR a description file.')
76
        if not d['download'] and not d['description']:
77
            raise ValidationError('You need a description link OR a description file.')
78
        # Having test machines demands compilation or validation scripts
79
        if 'test_machines' in d and len(d['test_machines'])>0               \
80
            and not 'attachment_test_validity' in d  \
81
            and not 'attachment_test_full' in d:
82
            raise ValidationError('For using test machines, you need to enable validation or full test.')
83
84
def course(obj):
85
    ''' Course name as string.'''
86
    return str(obj.course)
87
88
def num_subm(obj):
89
    return obj.valid_submissions().count()
90
num_subm.short_description = "Submissions"
91
92
def num_authors(obj):
93
    return obj.authors().count()
94
num_authors.short_description = "Authors"
95
96
def num_finished(obj):
97
    return obj.graded_submissions().count()
98
num_finished.short_description = "Grading finished"
99
100
def num_unfinished(obj):
101
    unfinished=obj.grading_unfinished_submissions().count()
102
    gradable  =obj.gradable_submissions().count()
103
    return "%u (%u)"%(gradable, unfinished)
104
num_unfinished.short_description = "To be graded (unfinished)"
105
106
def view_links(obj):
107
    ''' Link to performance data and duplicate overview.'''
108
    result=format_html('')
109
    result+=format_html('<a href="%s" style="white-space: nowrap">Show duplicates</a><br/>'%reverse('duplicates', args=(obj.pk,)))
110
    result+=format_html('<a href="%s" style="white-space: nowrap">Show submissions</a><br/>'%obj.grading_url())
111
    result+=format_html('<a href="%s" style="white-space: nowrap">Download submissions</a>'%reverse('assarchive', args=(obj.pk,)))
112
    return result
113
view_links.short_description = ""
114
115
class AssignmentAdmin(ModelAdmin):
116
    list_display = ['title', course, 'soft_deadline', 'hard_deadline', 'gradingScheme', num_authors, num_subm, num_finished, num_unfinished, view_links]
117
    change_list_template = "admin/change_list_filter_sidebar.html"
118
119
    class Media:
120
        css = {'all': ('css/teacher.css',)}
121
        js = ('js/opensubmit.js',)
122
123
    form = AssignmentAdminForm
124
125
    fieldsets = (
126
            ('',
127
                {'fields': (('title','course'), 'gradingScheme', 'max_authors', 'has_attachment')}),
128
            ('Description',
129
                {   'fields': ('download', 'description')}),
130
            ('Time',
131
                {   'fields': ('publish_at', ('soft_deadline', 'hard_deadline'))}),
132
            ('File Upload Validation',
133
                {   'fields': (('attachment_test_validity', 'validity_script_download'), \
134
                               'attachment_test_full', \
135
                               ('test_machines', 'attachment_test_timeout') )},
136
            )
137
    )
138
139
    def get_queryset(self, request):
140
        ''' Restrict the listed assignments for the current user.'''
141
        qs = super(AssignmentAdmin, self).get_queryset(request)
142
        if not request.user.is_superuser:
143
            qs = qs.filter(course__active=True).filter(Q(course__tutors__pk=request.user.pk) | Q(course__owner=request.user)).distinct()
144
        return qs.order_by('title')
145
146
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
147
        if db_field.name == "course":
148
            kwargs["queryset"] = Course.valid_ones
149
        return super(AssignmentAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)