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

« back to all changes in this revision

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

  • Committer: William Grant
  • Date: 2009-03-26 05:33:03 UTC
  • mto: (1165.3.1 submissions)
  • mto: This revision was merged to the branch mainline in revision 1174.
  • Revision ID: grantw@unimelb.edu.au-20090326053303-t1wsjswhk2sl2gml
Start a submission UI in ivle.webapp.submit.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
# A sample / testing application for IVLE.
24
24
 
25
25
import os
26
 
import os.path
27
26
import urllib
28
 
import urlparse
29
27
import cgi
30
28
 
31
 
from storm.locals import Desc, Store
32
 
import genshi
 
29
from storm.locals import Desc
33
30
from genshi.filters import HTMLFormFiller
34
 
from genshi.template import Context, TemplateLoader
35
31
import formencode
36
32
 
37
33
from ivle.webapp.base.xhtml import XHTMLView
38
34
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
39
 
from ivle.webapp import ApplicationRoot
40
 
 
41
 
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
42
 
                          ProjectSet, Project, ProjectSubmission
 
35
from ivle.webapp.errors import NotFound
 
36
from ivle.database import Subject, Semester, Offering, Enrolment, User
43
37
from ivle import util
44
 
import ivle.date
45
38
 
46
 
from ivle.webapp.admin.projectservice import ProjectSetRESTView
47
 
from ivle.webapp.admin.offeringservice import OfferingRESTView
48
 
from ivle.webapp.admin.publishing import (root_to_subject,
49
 
            subject_to_offering, offering_to_projectset, offering_to_project,
50
 
            subject_url, offering_url, projectset_url, project_url)
51
 
from ivle.webapp.admin.breadcrumbs import (SubjectBreadcrumb,
52
 
            OfferingBreadcrumb, UserBreadcrumb, ProjectBreadcrumb)
53
 
from ivle.webapp.groups import GroupsView
54
 
from ivle.webapp.tutorial import Plugin as TutorialPlugin
55
39
 
56
40
class SubjectsView(XHTMLView):
57
41
    '''The view of the list of subjects.'''
58
 
    template = 'templates/subjects.html'
 
42
    template = 'subjects.html'
59
43
    tab = 'subjects'
60
44
 
61
45
    def authorize(self, req):
66
50
        ctx['semesters'] = []
67
51
        for semester in req.store.find(Semester).order_by(Desc(Semester.year),
68
52
                                                     Desc(Semester.semester)):
69
 
            if req.user.admin:
70
 
                # For admins, show all subjects in the system
71
 
                offerings = list(semester.offerings.find())
72
 
            else:
73
 
                offerings = [enrolment.offering for enrolment in
74
 
                                    semester.enrolments.find(user=req.user)]
75
 
            if len(offerings):
76
 
                ctx['semesters'].append((semester, offerings))
77
 
 
78
 
class OfferingView(XHTMLView):
79
 
    """The home page of an offering."""
80
 
    template = 'templates/offering.html'
81
 
    tab = 'subjects'
82
 
    permission = 'view'
83
 
 
84
 
    def populate(self, req, ctx):
85
 
        # Need the worksheet result styles.
86
 
        self.plugin_styles[TutorialPlugin] = ['tutorial.css']
87
 
        ctx['context'] = self.context
88
 
        ctx['req'] = req
89
 
        ctx['permissions'] = self.context.get_permissions(req.user)
90
 
        ctx['format_submission_principal'] = util.format_submission_principal
91
 
        ctx['format_datetime'] = ivle.date.make_date_nice
92
 
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
93
 
        ctx['OfferingEdit'] = OfferingEdit
94
 
 
95
 
        # As we go, calculate the total score for this subject
96
 
        # (Assessable worksheets only, mandatory problems only)
97
 
 
98
 
        ctx['worksheets'], problems_total, problems_done = (
99
 
            ivle.worksheet.utils.create_list_of_fake_worksheets_and_stats(
100
 
                req.store, req.user, self.context))
101
 
 
102
 
        ctx['exercises_total'] = problems_total
103
 
        ctx['exercises_done'] = problems_done
104
 
        if problems_total > 0:
105
 
            if problems_done >= problems_total:
106
 
                ctx['worksheets_complete_class'] = "complete"
107
 
            elif problems_done > 0:
108
 
                ctx['worksheets_complete_class'] = "semicomplete"
109
 
            else:
110
 
                ctx['worksheets_complete_class'] = "incomplete"
111
 
            # Calculate the final percentage and mark for the subject
112
 
            (ctx['exercises_pct'], ctx['worksheet_mark'],
113
 
             ctx['worksheet_max_mark']) = (
114
 
                ivle.worksheet.utils.calculate_mark(
115
 
                    problems_done, problems_total))
116
 
 
117
 
 
118
 
class OfferingSchema(formencode.Schema):
119
 
    description = formencode.validators.UnicodeString(
120
 
        if_missing=None, not_empty=False)
121
 
    url = formencode.validators.URL(if_missing=None, not_empty=False)
122
 
 
123
 
 
124
 
class OfferingEdit(XHTMLView):
125
 
    """A form to edit an offering's details."""
126
 
    template = 'templates/offering-edit.html'
127
 
    permission = 'edit'
128
 
 
129
 
    def filter(self, stream, ctx):
130
 
        return stream | HTMLFormFiller(data=ctx['data'])
131
 
 
132
 
    def populate(self, req, ctx):
133
 
        if req.method == 'POST':
134
 
            data = dict(req.get_fieldstorage())
135
 
            try:
136
 
                validator = OfferingSchema()
137
 
                data = validator.to_python(data, state=req)
138
 
 
139
 
                self.context.url = unicode(data['url']) if data['url'] else None
140
 
                self.context.description = data['description']
141
 
                req.store.commit()
142
 
                req.throw_redirect(req.publisher.generate(self.context))
143
 
            except formencode.Invalid, e:
144
 
                errors = e.unpack_errors()
145
 
        else:
146
 
            data = {
147
 
                'url': self.context.url,
148
 
                'description': self.context.description,
149
 
            }
150
 
            errors = {}
151
 
 
152
 
        ctx['data'] = data or {}
153
 
        ctx['context'] = self.context
154
 
        ctx['errors'] = errors
 
53
            enrolments = semester.enrolments.find(user=req.user)
 
54
            if enrolments.count():
 
55
                ctx['semesters'].append((semester, enrolments))
155
56
 
156
57
 
157
58
class UserValidator(formencode.FancyValidator):
178
79
        return value
179
80
 
180
81
 
181
 
class RoleEnrolmentValidator(formencode.FancyValidator):
182
 
    """A FormEncode validator that checks permission to enrol users with a
183
 
    particular role.
184
 
 
185
 
    The state must have an 'offering' attribute.
186
 
    """
187
 
    def _to_python(self, value, state):
188
 
        if ("enrol_" + value) not in state.offering.get_permissions(state.user):
189
 
            raise formencode.Invalid('Not allowed to assign users that role',
190
 
                                     value, state)
191
 
        return value
192
 
 
193
 
 
194
82
class EnrolSchema(formencode.Schema):
195
83
    user = formencode.All(NoEnrolmentValidator(), UserValidator())
196
 
    role = formencode.All(formencode.validators.OneOf(
197
 
                                ["lecturer", "tutor", "student"]),
198
 
                          RoleEnrolmentValidator(),
199
 
                          formencode.validators.UnicodeString())
200
 
 
201
 
 
202
 
class EnrolmentsView(XHTMLView):
203
 
    """A page which displays all users enrolled in an offering."""
204
 
    template = 'templates/enrolments.html'
205
 
    permission = 'edit'
206
 
 
207
 
    def populate(self, req, ctx):
208
 
        ctx['offering'] = self.context
 
84
 
209
85
 
210
86
class EnrolView(XHTMLView):
211
87
    """A form to enrol a user in an offering."""
212
 
    template = 'templates/enrol.html'
 
88
    template = 'enrol.html'
213
89
    tab = 'subjects'
214
 
    permission = 'enrol'
 
90
    permission = 'edit'
 
91
 
 
92
    def __init__(self, req, subject, year, semester):
 
93
        """Find the given offering by subject, year and semester."""
 
94
        self.context = req.store.find(Offering,
 
95
            Offering.subject_id == Subject.id,
 
96
            Subject.short_name == subject,
 
97
            Offering.semester_id == Semester.id,
 
98
            Semester.year == year,
 
99
            Semester.semester == semester).one()
 
100
 
 
101
        if not self.context:
 
102
            raise NotFound()
215
103
 
216
104
    def filter(self, stream, ctx):
217
105
        return stream | HTMLFormFiller(data=ctx['data'])
223
111
                validator = EnrolSchema()
224
112
                req.offering = self.context # XXX: Getting into state.
225
113
                data = validator.to_python(data, state=req)
226
 
                self.context.enrol(data['user'], data['role'])
 
114
                self.context.enrol(data['user'])
227
115
                req.store.commit()
228
116
                req.throw_redirect(req.uri)
229
117
            except formencode.Invalid, e:
234
122
 
235
123
        ctx['data'] = data or {}
236
124
        ctx['offering'] = self.context
237
 
        ctx['roles_auth'] = self.context.get_permissions(req.user)
238
125
        ctx['errors'] = errors
239
126
 
240
 
class OfferingProjectsView(XHTMLView):
241
 
    """View the projects for an offering."""
242
 
    template = 'templates/offering_projects.html'
243
 
    permission = 'edit'
244
 
    tab = 'subjects'
245
 
 
246
 
    def populate(self, req, ctx):
247
 
        self.plugin_styles[Plugin] = ["project.css"]
248
 
        self.plugin_scripts[Plugin] = ["project.js"]
249
 
        ctx['req'] = req
250
 
        ctx['offering'] = self.context
251
 
        ctx['projectsets'] = []
252
 
        ctx['OfferingRESTView'] = OfferingRESTView
253
 
 
254
 
        #Open the projectset Fragment, and render it for inclusion
255
 
        #into the ProjectSets page
256
 
        #XXX: This could be a lot cleaner
257
 
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
258
 
 
259
 
        set_fragment = os.path.join(os.path.dirname(__file__),
260
 
                "templates/projectset_fragment.html")
261
 
        project_fragment = os.path.join(os.path.dirname(__file__),
262
 
                "templates/project_fragment.html")
263
 
 
264
 
        for projectset in self.context.project_sets:
265
 
            settmpl = loader.load(set_fragment)
266
 
            setCtx = Context()
267
 
            setCtx['req'] = req
268
 
            setCtx['projectset'] = projectset
269
 
            setCtx['projects'] = []
270
 
            setCtx['GroupsView'] = GroupsView
271
 
            setCtx['ProjectSetRESTView'] = ProjectSetRESTView
272
 
 
273
 
            for project in projectset.projects:
274
 
                projecttmpl = loader.load(project_fragment)
275
 
                projectCtx = Context()
276
 
                projectCtx['req'] = req
277
 
                projectCtx['project'] = project
278
 
 
279
 
                setCtx['projects'].append(
280
 
                        projecttmpl.generate(projectCtx))
281
 
 
282
 
            ctx['projectsets'].append(settmpl.generate(setCtx))
283
 
 
284
 
 
285
 
class ProjectView(XHTMLView):
286
 
    """View the submissions for a ProjectSet"""
287
 
    template = "templates/project.html"
288
 
    permission = "edit"
289
 
    tab = 'subjects'
290
 
 
291
 
    def build_subversion_url(self, svnroot, submission):
292
 
        princ = submission.assessed.principal
293
 
 
294
 
        if isinstance(princ, User):
295
 
            path = 'users/%s' % princ.login
296
 
        else:
297
 
            path = 'groups/%s_%s_%s_%s' % (
298
 
                    princ.project_set.offering.subject.short_name,
299
 
                    princ.project_set.offering.semester.year,
300
 
                    princ.project_set.offering.semester.semester,
301
 
                    princ.name
302
 
                    )
303
 
        return urlparse.urljoin(
304
 
                    svnroot,
305
 
                    os.path.join(path, submission.path[1:] if
306
 
                                       submission.path.startswith(os.sep) else
307
 
                                       submission.path))
308
 
 
309
 
    def populate(self, req, ctx):
310
 
        self.plugin_styles[Plugin] = ["project.css"]
311
 
 
312
 
        ctx['req'] = req
313
 
        ctx['GroupsView'] = GroupsView
314
 
        ctx['EnrolView'] = EnrolView
315
 
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
316
 
        ctx['build_subversion_url'] = self.build_subversion_url
317
 
        ctx['svn_addr'] = req.config['urls']['svn_addr']
318
 
        ctx['project'] = self.context
319
 
        ctx['user'] = req.user
320
127
 
321
128
class Plugin(ViewPlugin, MediaPlugin):
322
 
    forward_routes = (root_to_subject, subject_to_offering,
323
 
                      offering_to_project, offering_to_projectset)
324
 
    reverse_routes = (subject_url, offering_url, projectset_url, project_url)
325
 
 
326
 
    views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
327
 
             (Offering, '+index', OfferingView),
328
 
             (Offering, '+edit', OfferingEdit),
329
 
             (Offering, ('+enrolments', '+index'), EnrolmentsView),
330
 
             (Offering, ('+enrolments', '+new'), EnrolView),
331
 
             (Offering, ('+projects', '+index'), OfferingProjectsView),
332
 
             (Project, '+index', ProjectView),
333
 
 
334
 
             (Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
335
 
             (ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
336
 
             ]
337
 
 
338
 
    breadcrumbs = {Subject: SubjectBreadcrumb,
339
 
                   Offering: OfferingBreadcrumb,
340
 
                   User: UserBreadcrumb,
341
 
                   Project: ProjectBreadcrumb,
342
 
                   }
 
129
    urls = [
 
130
        ('subjects/', SubjectsView),
 
131
        ('subjects/:subject/:year/:semester/+enrolments/+new', EnrolView),
 
132
    ]
343
133
 
344
134
    tabs = [
345
135
        ('subjects', 'Subjects',