~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/admin/subject.py

MergedĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import urllib
27
27
import cgi
28
28
 
29
 
from storm.locals import Desc
30
 
import genshi
31
 
from genshi.filters import HTMLFormFiller
32
 
from genshi.template import Context, TemplateLoader
33
 
import formencode
34
 
 
35
29
from ivle.webapp.base.xhtml import XHTMLView
36
30
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
37
31
from ivle.webapp.errors import NotFound
38
 
 
39
 
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
40
 
                          ProjectSet, Project, ProjectSubmission
 
32
from ivle.database import Subject
41
33
from ivle import util
42
34
 
43
 
from ivle.webapp.admin.projectservice import ProjectSetRESTView,\
44
 
                                             ProjectRESTView
45
 
from ivle.webapp.admin.offeringservice import OfferingRESTView
46
 
 
47
35
 
48
36
class SubjectsView(XHTMLView):
49
37
    '''The view of the list of subjects.'''
50
 
    template = 'templates/subjects.html'
 
38
    template = 'subjects.html'
51
39
    tab = 'subjects'
52
40
 
53
41
    def authorize(self, req):
54
42
        return req.user is not None
55
43
 
56
44
    def populate(self, req, ctx):
57
 
        ctx['user'] = req.user
58
 
        ctx['semesters'] = []
59
 
        for semester in req.store.find(Semester).order_by(Desc(Semester.year),
60
 
                                                     Desc(Semester.semester)):
61
 
            enrolments = semester.enrolments.find(user=req.user)
62
 
            if enrolments.count():
63
 
                ctx['semesters'].append((semester, enrolments))
64
 
 
65
 
 
66
 
class UserValidator(formencode.FancyValidator):
67
 
    """A FormEncode validator that turns a username into a user.
68
 
 
69
 
    The state must have a 'store' attribute, which is the Storm store
70
 
    to use."""
71
 
    def _to_python(self, value, state):
72
 
        user = User.get_by_login(state.store, value)
73
 
        if user:
74
 
            return user
75
 
        else:
76
 
            raise formencode.Invalid('User does not exist', value, state)
77
 
 
78
 
 
79
 
class NoEnrolmentValidator(formencode.FancyValidator):
80
 
    """A FormEncode validator that ensures absence of an enrolment.
81
 
 
82
 
    The state must have an 'offering' attribute.
83
 
    """
84
 
    def _to_python(self, value, state):
85
 
        if state.offering.get_enrolment(value):
86
 
            raise formencode.Invalid('User already enrolled', value, state)
87
 
        return value
88
 
 
89
 
 
90
 
class EnrolSchema(formencode.Schema):
91
 
    user = formencode.All(NoEnrolmentValidator(), UserValidator())
92
 
 
93
 
 
94
 
class EnrolView(XHTMLView):
95
 
    """A form to enrol a user in an offering."""
96
 
    template = 'templates/enrol.html'
97
 
    tab = 'subjects'
98
 
    permission = 'edit'
99
 
 
100
 
    def __init__(self, req, subject, year, semester):
101
 
        """Find the given offering by subject, year and semester."""
102
 
        self.context = req.store.find(Offering,
103
 
            Offering.subject_id == Subject.id,
104
 
            Subject.short_name == subject,
105
 
            Offering.semester_id == Semester.id,
106
 
            Semester.year == year,
107
 
            Semester.semester == semester).one()
108
 
 
109
 
        if not self.context:
110
 
            raise NotFound()
111
 
 
112
 
    def filter(self, stream, ctx):
113
 
        return stream | HTMLFormFiller(data=ctx['data'])
114
 
 
115
 
    def populate(self, req, ctx):
116
 
        if req.method == 'POST':
117
 
            data = dict(req.get_fieldstorage())
118
 
            try:
119
 
                validator = EnrolSchema()
120
 
                req.offering = self.context # XXX: Getting into state.
121
 
                data = validator.to_python(data, state=req)
122
 
                self.context.enrol(data['user'])
123
 
                req.store.commit()
124
 
                req.throw_redirect(req.uri)
125
 
            except formencode.Invalid, e:
126
 
                errors = e.unpack_errors()
127
 
        else:
128
 
            data = {}
129
 
            errors = {}
130
 
 
131
 
        ctx['data'] = data or {}
132
 
        ctx['offering'] = self.context
133
 
        ctx['errors'] = errors
134
 
 
135
 
class SubjectProjectSetView(XHTMLView):
136
 
    """View the ProjectSets for a subject."""
137
 
    template = 'templates/subject_projects.html'
138
 
    permission = 'edit'
139
 
    
140
 
    def __init__(self, req, subject, year, semester):
141
 
        self.context = req.store.find(Offering,
142
 
            Offering.subject_id == Subject.id,
143
 
            Subject.short_name == subject,
144
 
            Offering.semester_id == Semester.id,
145
 
            Semester.year == year,
146
 
            Semester.semester == semester).one()
147
 
 
148
 
        if not self.context:
149
 
            raise NotFound()
150
 
 
151
 
    def project_url(self, projectset, project):
152
 
        return "/subjects/" + self.context.subject.short_name + "/" +\
153
 
                self.context.semester.year + "/" + \
154
 
                self.context.semester.semester + "/+projectsets/" +\
155
 
                str(projectset.id) + "/+projects/" + project.short_name
156
 
 
157
 
    def new_project_url(self, projectset):
158
 
        return "/api/subjects/" + self.context.subject.short_name + "/" +\
159
 
                self.context.semester.year + "/" + \
160
 
                self.context.semester.semester + "/+projectsets/" +\
161
 
                str(projectset.id) + "/+projects/+new"
162
 
    
163
 
    def populate(self, req, ctx):
164
 
        self.plugin_styles[Plugin] = ["project.css"]
165
 
        self.plugin_scripts[Plugin] = ["project.js"]
166
 
        ctx['offering'] = self.context
167
 
        ctx['subject'] = self.context.subject.short_name
168
 
        ctx['year'] = self.context.semester.year
169
 
        ctx['semester'] = self.context.semester.semester
170
 
 
171
 
        ctx['projectsets'] = []
172
 
 
173
 
        #Open the projectset Fragment, and render it for inclusion
174
 
        #into the ProjectSets page
175
 
        #XXX: This could be a lot cleaner
176
 
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
177
 
 
178
 
        set_fragment = os.path.join(os.path.dirname(__file__),
179
 
                "templates/projectset_fragment.html")
180
 
        project_fragment = os.path.join(os.path.dirname(__file__),
181
 
                "templates/project_fragment.html")
182
 
 
183
 
        for projectset in self.context.project_sets:
184
 
            settmpl = loader.load(set_fragment)
185
 
            setCtx = Context()
186
 
            setCtx['group_size'] = projectset.max_students_per_group
187
 
            setCtx['projectset_id'] = projectset.id
188
 
            setCtx['new_project_url'] = self.new_project_url(projectset)
189
 
            setCtx['projects'] = []
190
 
 
191
 
            for project in projectset.projects:
192
 
                projecttmpl = loader.load(project_fragment)
193
 
                projectCtx = Context()
194
 
                projectCtx['project'] = project
195
 
                projectCtx['project_url'] = self.project_url(projectset, project)
196
 
 
197
 
                setCtx['projects'].append(
198
 
                        projecttmpl.generate(projectCtx))
199
 
 
200
 
            ctx['projectsets'].append(settmpl.generate(setCtx))
201
 
 
202
 
 
203
 
class ProjectView(XHTMLView):
204
 
    """View the submissions for a ProjectSet"""
205
 
    template = "templates/project.html"
206
 
    permission = "edit"
207
 
 
208
 
    def __init__(self, req, subject, year, semester, projectset, project):
209
 
        self.context = req.store.find(Project,
210
 
                Project.short_name == project,
211
 
                Project.project_set_id == ProjectSet.id,
212
 
                ProjectSet.offering_id == Offering.id,
213
 
                Offering.semester_id == Semester.id,
214
 
                Semester.year == year,
215
 
                Semester.semester == semester,
216
 
                Offering.subject_id == Subject.id,
217
 
                Subject.short_name == subject).one()
218
 
        if self.context is None:
219
 
            raise NotFound()
220
 
 
221
 
    def populate(self, req, ctx):
222
 
        ctx['project'] = self.context
223
 
        ctx['assesseds'] = self.context.assesseds
224
 
 
225
 
        ctx['submissions'] = []
226
 
        for assessed in self.context.assesseds:
227
 
            if assessed.submissions.count() > 0:
228
 
                ctx['submissions'].append(
229
 
                        assessed.submissions.order_by(ProjectSubmission.date_submitted)[:-1])
230
 
 
 
45
        ctx['enrolments'] = req.user.active_enrolments
231
46
 
232
47
class Plugin(ViewPlugin, MediaPlugin):
233
48
    urls = [
234
49
        ('subjects/', SubjectsView),
235
 
        ('subjects/:subject/:year/:semester/+enrolments/+new', EnrolView),
236
 
        ('subjects/:subject/:year/:semester/+projects', SubjectProjectSetView),
237
 
        ('subjects/:subject/:year/:semester/+projectsets/:projectset/+projects/:project', ProjectView),
238
 
        #API Views
239
 
        ('api/subjects/:subject/:year/:semester/+projectsets/+new',
240
 
            OfferingRESTView),
241
 
        ('api/subjects/:subject/:year/:semester/+projectsets/:projectset/+projects/+new',
242
 
            ProjectSetRESTView),
243
 
        ('api/subjects/:subject/:year/:semester/+projectsets/:projectset/+projects/:project', 
244
 
            ProjectRESTView),
245
 
 
246
50
    ]
247
51
 
248
52
    tabs = [