37
37
from ivle.webapp.base.xhtml import XHTMLView
38
38
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
39
from ivle.webapp.errors import NotFound
39
from ivle.webapp import ApplicationRoot
41
41
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
42
42
ProjectSet, Project, ProjectSubmission
43
43
from ivle import util
46
from ivle.webapp.admin.projectservice import ProjectSetRESTView,\
46
from ivle.webapp.admin.projectservice import ProjectSetRESTView
48
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
51
55
class SubjectsView(XHTMLView):
52
56
'''The view of the list of subjects.'''
61
65
ctx['semesters'] = []
62
66
for semester in req.store.find(Semester).order_by(Desc(Semester.year),
63
67
Desc(Semester.semester)):
64
enrolments = semester.enrolments.find(user=req.user)
65
if enrolments.count():
66
ctx['semesters'].append((semester, enrolments))
69
# For admins, show all subjects in the system
70
offerings = list(semester.offerings.find())
72
offerings = [enrolment.offering for enrolment in
73
semester.enrolments.find(user=req.user)]
75
ctx['semesters'].append((semester, offerings))
69
78
class UserValidator(formencode.FancyValidator):
102
class RoleEnrolmentValidator(formencode.FancyValidator):
103
"""A FormEncode validator that checks permission to enrol users with a
106
The state must have an 'offering' attribute.
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',
93
115
class EnrolSchema(formencode.Schema):
94
116
user = formencode.All(NoEnrolmentValidator(), UserValidator())
117
role = formencode.All(formencode.validators.OneOf(
118
["lecturer", "tutor", "student"]),
119
RoleEnrolmentValidator(),
120
formencode.validators.UnicodeString())
123
class EnrolmentsView(XHTMLView):
124
"""A page which displays all users enrolled in an offering."""
125
template = 'templates/enrolments.html'
128
def populate(self, req, ctx):
129
ctx['offering'] = self.context
97
131
class EnrolView(XHTMLView):
98
132
"""A form to enrol a user in an offering."""
99
133
template = 'templates/enrol.html'
103
def __init__(self, req, subject, year, semester):
104
"""Find the given offering by subject, year and semester."""
105
self.context = req.store.find(Offering,
106
Offering.subject_id == Subject.id,
107
Subject.short_name == subject,
108
Offering.semester_id == Semester.id,
109
Semester.year == year,
110
Semester.semester == semester).one()
115
137
def filter(self, stream, ctx):
116
138
return stream | HTMLFormFiller(data=ctx['data'])
122
144
validator = EnrolSchema()
123
145
req.offering = self.context # XXX: Getting into state.
124
146
data = validator.to_python(data, state=req)
125
self.context.enrol(data['user'])
147
self.context.enrol(data['user'], data['role'])
126
148
req.store.commit()
127
149
req.throw_redirect(req.uri)
128
150
except formencode.Invalid, e:
140
163
template = 'templates/offering_projects.html'
141
164
permission = 'edit'
144
def __init__(self, req, subject, year, semester):
145
self.context = req.store.find(Offering,
146
Offering.subject_id == Subject.id,
147
Subject.short_name == subject,
148
Offering.semester_id == Semester.id,
149
Semester.year == year,
150
Semester.semester == semester).one()
155
def project_url(self, projectset, project):
156
return "/subjects/%s/%s/%s/+projects/%s" % (
157
self.context.subject.short_name,
158
self.context.semester.year,
159
self.context.semester.semester,
163
def new_project_url(self, projectset):
164
return "/api/subjects/" + self.context.subject.short_name + "/" +\
165
self.context.semester.year + "/" + \
166
self.context.semester.semester + "/+projectsets/" +\
167
str(projectset.id) + "/+projects/+new"
169
167
def populate(self, req, ctx):
170
168
self.plugin_styles[Plugin] = ["project.css"]
171
169
self.plugin_scripts[Plugin] = ["project.js"]
172
171
ctx['offering'] = self.context
173
172
ctx['projectsets'] = []
173
ctx['OfferingRESTView'] = OfferingRESTView
175
175
#Open the projectset Fragment, and render it for inclusion
176
176
#into the ProjectSets page
185
185
for projectset in self.context.project_sets:
186
186
settmpl = loader.load(set_fragment)
187
187
setCtx = Context()
188
189
setCtx['projectset'] = projectset
189
setCtx['new_project_url'] = self.new_project_url(projectset)
190
190
setCtx['projects'] = []
191
setCtx['GroupsView'] = GroupsView
192
setCtx['ProjectSetRESTView'] = ProjectSetRESTView
192
194
for project in projectset.projects:
193
195
projecttmpl = loader.load(project_fragment)
194
196
projectCtx = Context()
197
projectCtx['req'] = req
195
198
projectCtx['project'] = project
196
projectCtx['project_url'] = self.project_url(projectset, project)
198
200
setCtx['projects'].append(
199
201
projecttmpl.generate(projectCtx))
207
209
permission = "edit"
210
def __init__(self, req, subject, year, semester, project):
211
self.context = req.store.find(Project,
212
Project.short_name == project,
213
Project.project_set_id == ProjectSet.id,
214
ProjectSet.offering_id == Offering.id,
215
Offering.semester_id == Semester.id,
216
Semester.year == year,
217
Semester.semester == semester,
218
Offering.subject_id == Subject.id,
219
Subject.short_name == subject).one()
220
if self.context is None:
223
212
def build_subversion_url(self, svnroot, submission):
224
213
princ = submission.assessed.principal
241
230
def populate(self, req, ctx):
242
231
self.plugin_styles[Plugin] = ["project.css"]
234
ctx['GroupsView'] = GroupsView
235
ctx['EnrolView'] = EnrolView
244
236
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
245
237
ctx['build_subversion_url'] = self.build_subversion_url
246
238
ctx['svn_addr'] = req.config['urls']['svn_addr']
248
240
ctx['user'] = req.user
250
242
class Plugin(ViewPlugin, MediaPlugin):
252
('subjects/', SubjectsView),
253
('subjects/:subject/:year/:semester/+enrolments/+new', EnrolView),
254
('subjects/:subject/:year/:semester/+projects', OfferingProjectsView),
255
('subjects/:subject/:year/:semester/+projects/:project', ProjectView),
257
('api/subjects/:subject/:year/:semester/+projectsets/+new',
259
('api/subjects/:subject/:year/:semester/+projectsets/:projectset/+projects/+new',
261
('api/subjects/:subject/:year/:semester/+projects/:project',
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)
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),
253
(Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
254
(ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
257
breadcrumbs = {Subject: SubjectBreadcrumb,
258
Offering: OfferingBreadcrumb,
259
User: UserBreadcrumb,
260
Project: ProjectBreadcrumb,
267
264
('subjects', 'Subjects',