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

« back to all changes in this revision

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

  • Committer: Matt Giuca
  • Date: 2009-12-15 04:44:25 UTC
  • Revision ID: matt.giuca@gmail.com-20091215044425-uxzmdrd89s2d5ax6
doc: Added dev/faq, with some helpful tips on creating certain objects.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE
 
2
# Copyright (C) 2007-2008 The University of Melbourne
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 
 
18
# App: subjects
 
19
# Author: Matt Giuca
 
20
# Date: 29/2/2008
 
21
 
 
22
# This is an IVLE application.
 
23
# A sample / testing application for IVLE.
 
24
 
 
25
import os
 
26
import os.path
 
27
import urllib
 
28
import urlparse
 
29
import cgi
 
30
 
 
31
from storm.locals import Desc, Store
 
32
import genshi
 
33
from genshi.filters import HTMLFormFiller
 
34
from genshi.template import Context, TemplateLoader
 
35
import formencode
 
36
 
 
37
from ivle.webapp.base.xhtml import XHTMLView
 
38
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
 
43
from ivle import util
 
44
import ivle.date
 
45
 
 
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
 
 
55
class SubjectsView(XHTMLView):
 
56
    '''The view of the list of subjects.'''
 
57
    template = 'templates/subjects.html'
 
58
    tab = 'subjects'
 
59
 
 
60
    def authorize(self, req):
 
61
        return req.user is not None
 
62
 
 
63
    def populate(self, req, ctx):
 
64
        ctx['user'] = req.user
 
65
        ctx['semesters'] = []
 
66
        for semester in req.store.find(Semester).order_by(Desc(Semester.year),
 
67
                                                     Desc(Semester.semester)):
 
68
            if req.user.admin:
 
69
                # For admins, show all subjects in the system
 
70
                offerings = list(semester.offerings.find())
 
71
            else:
 
72
                offerings = [enrolment.offering for enrolment in
 
73
                                    semester.enrolments.find(user=req.user)]
 
74
            if len(offerings):
 
75
                ctx['semesters'].append((semester, offerings))
 
76
 
 
77
 
 
78
class UserValidator(formencode.FancyValidator):
 
79
    """A FormEncode validator that turns a username into a user.
 
80
 
 
81
    The state must have a 'store' attribute, which is the Storm store
 
82
    to use."""
 
83
    def _to_python(self, value, state):
 
84
        user = User.get_by_login(state.store, value)
 
85
        if user:
 
86
            return user
 
87
        else:
 
88
            raise formencode.Invalid('User does not exist', value, state)
 
89
 
 
90
 
 
91
class NoEnrolmentValidator(formencode.FancyValidator):
 
92
    """A FormEncode validator that ensures absence of an enrolment.
 
93
 
 
94
    The state must have an 'offering' attribute.
 
95
    """
 
96
    def _to_python(self, value, state):
 
97
        if state.offering.get_enrolment(value):
 
98
            raise formencode.Invalid('User already enrolled', value, state)
 
99
        return value
 
100
 
 
101
 
 
102
class RoleEnrolmentValidator(formencode.FancyValidator):
 
103
    """A FormEncode validator that checks permission to enrol users with a
 
104
    particular role.
 
105
 
 
106
    The state must have an 'offering' attribute.
 
107
    """
 
108
    def _to_python(self, value, state):
 
109
        if ("enrol_" + value) not in state.offering.get_permissions(state.user):
 
110
            raise formencode.Invalid('Not allowed to assign users that role',
 
111
                                     value, state)
 
112
        return value
 
113
 
 
114
 
 
115
class EnrolSchema(formencode.Schema):
 
116
    user = formencode.All(NoEnrolmentValidator(), UserValidator())
 
117
    role = formencode.All(formencode.validators.OneOf(
 
118
                                ["lecturer", "tutor", "student"]),
 
119
                          RoleEnrolmentValidator(),
 
120
                          formencode.validators.UnicodeString())
 
121
 
 
122
 
 
123
class EnrolmentsView(XHTMLView):
 
124
    """A page which displays all users enrolled in an offering."""
 
125
    template = 'templates/enrolments.html'
 
126
    permission = 'edit'
 
127
 
 
128
    def populate(self, req, ctx):
 
129
        ctx['offering'] = self.context
 
130
 
 
131
class EnrolView(XHTMLView):
 
132
    """A form to enrol a user in an offering."""
 
133
    template = 'templates/enrol.html'
 
134
    tab = 'subjects'
 
135
    permission = 'enrol'
 
136
 
 
137
    def filter(self, stream, ctx):
 
138
        return stream | HTMLFormFiller(data=ctx['data'])
 
139
 
 
140
    def populate(self, req, ctx):
 
141
        if req.method == 'POST':
 
142
            data = dict(req.get_fieldstorage())
 
143
            try:
 
144
                validator = EnrolSchema()
 
145
                req.offering = self.context # XXX: Getting into state.
 
146
                data = validator.to_python(data, state=req)
 
147
                self.context.enrol(data['user'], data['role'])
 
148
                req.store.commit()
 
149
                req.throw_redirect(req.uri)
 
150
            except formencode.Invalid, e:
 
151
                errors = e.unpack_errors()
 
152
        else:
 
153
            data = {}
 
154
            errors = {}
 
155
 
 
156
        ctx['data'] = data or {}
 
157
        ctx['offering'] = self.context
 
158
        ctx['roles_auth'] = self.context.get_permissions(req.user)
 
159
        ctx['errors'] = errors
 
160
 
 
161
class OfferingProjectsView(XHTMLView):
 
162
    """View the projects for an offering."""
 
163
    template = 'templates/offering_projects.html'
 
164
    permission = 'edit'
 
165
    tab = 'subjects'
 
166
 
 
167
    def populate(self, req, ctx):
 
168
        self.plugin_styles[Plugin] = ["project.css"]
 
169
        self.plugin_scripts[Plugin] = ["project.js"]
 
170
        ctx['req'] = req
 
171
        ctx['offering'] = self.context
 
172
        ctx['projectsets'] = []
 
173
        ctx['OfferingRESTView'] = OfferingRESTView
 
174
 
 
175
        #Open the projectset Fragment, and render it for inclusion
 
176
        #into the ProjectSets page
 
177
        #XXX: This could be a lot cleaner
 
178
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
 
179
 
 
180
        set_fragment = os.path.join(os.path.dirname(__file__),
 
181
                "templates/projectset_fragment.html")
 
182
        project_fragment = os.path.join(os.path.dirname(__file__),
 
183
                "templates/project_fragment.html")
 
184
 
 
185
        for projectset in self.context.project_sets:
 
186
            settmpl = loader.load(set_fragment)
 
187
            setCtx = Context()
 
188
            setCtx['req'] = req
 
189
            setCtx['projectset'] = projectset
 
190
            setCtx['projects'] = []
 
191
            setCtx['GroupsView'] = GroupsView
 
192
            setCtx['ProjectSetRESTView'] = ProjectSetRESTView
 
193
 
 
194
            for project in projectset.projects:
 
195
                projecttmpl = loader.load(project_fragment)
 
196
                projectCtx = Context()
 
197
                projectCtx['req'] = req
 
198
                projectCtx['project'] = project
 
199
 
 
200
                setCtx['projects'].append(
 
201
                        projecttmpl.generate(projectCtx))
 
202
 
 
203
            ctx['projectsets'].append(settmpl.generate(setCtx))
 
204
 
 
205
 
 
206
class ProjectView(XHTMLView):
 
207
    """View the submissions for a ProjectSet"""
 
208
    template = "templates/project.html"
 
209
    permission = "edit"
 
210
    tab = 'subjects'
 
211
 
 
212
    def build_subversion_url(self, svnroot, submission):
 
213
        princ = submission.assessed.principal
 
214
 
 
215
        if isinstance(princ, User):
 
216
            path = 'users/%s' % princ.login
 
217
        else:
 
218
            path = 'groups/%s_%s_%s_%s' % (
 
219
                    princ.project_set.offering.subject.short_name,
 
220
                    princ.project_set.offering.semester.year,
 
221
                    princ.project_set.offering.semester.semester,
 
222
                    princ.name
 
223
                    )
 
224
        return urlparse.urljoin(
 
225
                    svnroot,
 
226
                    os.path.join(path, submission.path[1:] if
 
227
                                       submission.path.startswith(os.sep) else
 
228
                                       submission.path))
 
229
 
 
230
    def populate(self, req, ctx):
 
231
        self.plugin_styles[Plugin] = ["project.css"]
 
232
 
 
233
        ctx['req'] = req
 
234
        ctx['GroupsView'] = GroupsView
 
235
        ctx['EnrolView'] = EnrolView
 
236
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
 
237
        ctx['build_subversion_url'] = self.build_subversion_url
 
238
        ctx['svn_addr'] = req.config['urls']['svn_addr']
 
239
        ctx['project'] = self.context
 
240
        ctx['user'] = req.user
 
241
 
 
242
class Plugin(ViewPlugin, MediaPlugin):
 
243
    forward_routes = (root_to_subject, subject_to_offering,
 
244
                      offering_to_project, offering_to_projectset)
 
245
    reverse_routes = (subject_url, offering_url, projectset_url, project_url)
 
246
 
 
247
    views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
 
248
             (Offering, ('+enrolments', '+index'), EnrolmentsView),
 
249
             (Offering, ('+enrolments', '+new'), EnrolView),
 
250
             (Offering, ('+projects', '+index'), OfferingProjectsView),
 
251
             (Project, '+index', ProjectView),
 
252
 
 
253
             (Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
 
254
             (ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
 
255
             ]
 
256
 
 
257
    breadcrumbs = {Subject: SubjectBreadcrumb,
 
258
                   Offering: OfferingBreadcrumb,
 
259
                   User: UserBreadcrumb,
 
260
                   Project: ProjectBreadcrumb,
 
261
                   }
 
262
 
 
263
    tabs = [
 
264
        ('subjects', 'Subjects',
 
265
         'View subject content and complete worksheets',
 
266
         'subjects.png', 'subjects', 5)
 
267
    ]
 
268
 
 
269
    media = 'subject-media'