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

« back to all changes in this revision

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

  • Committer: David Coles
  • Date: 2009-12-09 01:14:51 UTC
  • Revision ID: coles.david@gmail.com-20091209011451-w24f3zkdubh0nl5d
Fix ivle-config's (broken) use of the depricated string module

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
26
27
import urllib
 
28
import urlparse
27
29
import cgi
28
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
 
29
37
from ivle.webapp.base.xhtml import XHTMLView
30
38
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
31
 
from ivle.webapp.errors import NotFound
32
 
from ivle.database import Subject
 
39
from ivle.webapp import ApplicationRoot
 
40
 
 
41
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
 
42
                          ProjectSet, Project, ProjectSubmission
33
43
from ivle import util
 
44
import ivle.date
34
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
35
54
 
36
55
class SubjectsView(XHTMLView):
37
56
    '''The view of the list of subjects.'''
38
 
    template = 'subjects.html'
39
 
    appname = 'subjects' # XXX
 
57
    template = 'templates/subjects.html'
 
58
    tab = 'subjects'
40
59
 
41
60
    def authorize(self, req):
42
61
        return req.user is not None
43
62
 
44
63
    def populate(self, req, ctx):
45
 
        enrolled_subjects = req.user.subjects
46
 
        unenrolled_subjects = [subject for subject in
47
 
                               req.store.find(Subject)
48
 
                               if subject not in enrolled_subjects]
49
 
 
50
 
        ctx['enrolled_subjects'] = []
51
 
        ctx['other_subjects'] = []
52
 
 
53
 
        for subject in enrolled_subjects:
54
 
            new_subj = {}
55
 
            new_subj['name'] = subject.name
56
 
            new_subj['url'] = subject.url
57
 
            ctx['enrolled_subjects'].append(new_subj)
58
 
 
59
 
        if len(unenrolled_subjects) > 0:
60
 
            for subject in unenrolled_subjects:
61
 
                new_subj = {}
62
 
                new_subj['name'] = subject.name
63
 
                new_subj['url'] = subject.url
64
 
                ctx['other_subjects'].append(new_subj)
65
 
 
 
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
66
241
 
67
242
class Plugin(ViewPlugin, MediaPlugin):
68
 
    urls = [
69
 
        ('subjects/', SubjectsView),
70
 
    ]
 
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
                   }
71
262
 
72
263
    tabs = [
73
 
        ('subjects', 'Subjects', 'Announcements and information about the '
74
 
         'subjects you are enrolled in.', 'subjects.png', 'subjects', 5)
 
264
        ('subjects', 'Subjects',
 
265
         'View subject content and complete worksheets',
 
266
         'subjects.png', 'subjects', 5)
75
267
    ]
76
268
 
77
269
    media = 'subject-media'