41
40
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
42
41
from ivle.webapp.base.xhtml import XHTMLView
43
from ivle.webapp.base.text import TextView
44
42
from ivle.webapp.errors import BadRequest
45
43
from ivle.webapp import ApplicationRoot
75
73
ctx['user'] = req.user
76
74
ctx['semesters'] = []
78
for semester in req.store.find(Semester).order_by(
79
Desc(Semester.year), Desc(Semester.display_name)):
76
for semester in req.store.find(Semester).order_by(Desc(Semester.year),
77
Desc(Semester.semester)):
81
79
# For admins, show all subjects in the system
82
80
offerings = list(semester.offerings.find())
105
103
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
106
104
ctx['semesters'] = req.store.find(Semester).order_by(
107
Semester.year, Semester.display_name)
110
class SubjectUniquenessValidator(formencode.FancyValidator):
111
"""A FormEncode validator that checks that a subject attribute is unique.
105
Semester.year, Semester.semester)
108
class SubjectShortNameUniquenessValidator(formencode.FancyValidator):
109
"""A FormEncode validator that checks that a subject name is unused.
113
111
The subject referenced by state.existing_subject is permitted
114
112
to hold that name. If any other object holds it, the input is rejected.
116
:param attribute: the name of the attribute to check.
117
:param display: a string to identify the field in case of error.
120
def __init__(self, attribute, display):
121
self.attribute = attribute
122
self.display = display
114
def __init__(self, matching=None):
115
self.matching = matching
124
117
def _to_python(self, value, state):
125
if (state.store.find(Subject, **{self.attribute: value}).one() not in
118
if (state.store.find(
119
Subject, short_name=value).one() not in
126
120
(None, state.existing_subject)):
127
121
raise formencode.Invalid(
128
'%s already taken' % self.display, value, state)
122
'Short name already taken', value, state)
132
126
class SubjectSchema(formencode.Schema):
133
127
short_name = formencode.All(
134
SubjectUniquenessValidator('short_name', 'URL name'),
128
SubjectShortNameUniquenessValidator(),
135
129
URLNameValidator(not_empty=True))
136
130
name = formencode.validators.UnicodeString(not_empty=True)
137
code = formencode.All(
138
SubjectUniquenessValidator('code', 'Subject code'),
139
formencode.validators.UnicodeString(not_empty=True))
131
code = formencode.validators.UnicodeString(not_empty=True)
142
134
class SubjectFormView(BaseFormView):
201
193
def _to_python(self, value, state):
202
194
if (state.store.find(
203
Semester, year=value['year'], url_name=value['url_name']
195
Semester, year=value['year'], semester=value['semester']
204
196
).one() not in (None, state.existing_semester)):
205
197
raise formencode.Invalid(
206
198
'Semester already exists', value, state)
210
202
class SemesterSchema(formencode.Schema):
211
203
year = URLNameValidator()
212
code = formencode.validators.UnicodeString()
213
url_name = URLNameValidator()
214
display_name = formencode.validators.UnicodeString()
204
semester = URLNameValidator()
215
205
state = formencode.All(
216
206
formencode.validators.OneOf(["past", "current", "future"]),
217
207
formencode.validators.UnicodeString())
246
236
def save_object(self, req, data):
247
237
new_semester = Semester()
248
238
new_semester.year = data['year']
249
new_semester.code = data['code']
250
new_semester.url_name = data['url_name']
251
new_semester.display_name = data['display_name']
239
new_semester.semester = data['semester']
252
240
new_semester.state = data['state']
254
242
req.store.add(new_semester)
265
253
def get_default_data(self, req):
267
255
'year': self.context.year,
268
'code': self.context.code,
269
'url_name': self.context.url_name,
270
'display_name': self.context.display_name,
256
'semester': self.context.semester,
271
257
'state': self.context.state,
274
260
def save_object(self, req, data):
275
261
self.context.year = data['year']
276
self.context.code = data['code']
277
self.context.url_name = data['url_name']
278
self.context.display_name = data['display_name']
262
self.context.semester = data['semester']
279
263
self.context.state = data['state']
281
265
return self.context
432
416
super(OfferingEdit, self).populate(req, ctx)
433
417
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
434
418
ctx['semesters'] = req.store.find(Semester).order_by(
435
Semester.year, Semester.display_name)
419
Semester.year, Semester.semester)
436
420
ctx['force_subject'] = None
438
422
def populate_state(self, state):
443
427
'subject': self.context.subject.short_name,
444
428
'semester': self.context.semester.year + '/' +
445
self.context.semester.url_name,
429
self.context.semester.semester,
446
430
'url': self.context.url,
447
431
'description': self.context.description,
448
432
'worksheet_cutoff': self.context.worksheet_cutoff,
476
460
super(OfferingNew, self).populate(req, ctx)
477
461
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
478
462
ctx['semesters'] = req.store.find(Semester).order_by(
479
Semester.year, Semester.display_name)
463
Semester.year, Semester.semester)
480
464
ctx['force_subject'] = None
482
466
def populate_state(self, state):
528
512
super(OfferingCloneWorksheets, self).populate(req, ctx)
529
513
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
530
514
ctx['semesters'] = req.store.find(Semester).order_by(
531
Semester.year, Semester.display_name)
515
Semester.year, Semester.semester)
533
517
def get_default_data(self, req):
751
735
permission = "view_project_submissions"
738
def build_subversion_url(self, req, submission):
739
princ = submission.assessed.principal
741
return os.path.join(princ.get_svn_url(req.config, req),
742
submission.path[1:] if
743
submission.path.startswith(os.sep) else
754
746
def populate(self, req, ctx):
755
747
self.plugin_styles[Plugin] = ["project.css"]
760
752
ctx['EnrolView'] = EnrolView
761
753
ctx['format_datetime'] = ivle.date.make_date_nice
762
754
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
755
ctx['build_subversion_url'] = self.build_subversion_url
763
756
ctx['project'] = self.context
764
757
ctx['user'] = req.user
765
758
ctx['ProjectEdit'] = ProjectEdit
766
759
ctx['ProjectDelete'] = ProjectDelete
767
ctx['ProjectExport'] = ProjectBashExportView
769
class ProjectBashExportView(TextView):
770
"""Produce a Bash script for exporting projects"""
771
template = "templates/project-export.sh"
772
content_type = "text/x-sh"
773
permission = "view_project_submissions"
775
def populate(self, req, ctx):
777
ctx['permissions'] = self.context.get_permissions(req.user,req.config)
778
ctx['format_datetime'] = ivle.date.make_date_nice
779
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
780
ctx['project'] = self.context
781
ctx['user'] = req.user
782
ctx['now'] = datetime.datetime.now()
783
ctx['format_datetime'] = ivle.date.make_date_nice
784
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
786
761
class ProjectUniquenessValidator(formencode.FancyValidator):
787
762
"""A FormEncode validator that checks that a project short_name is unique
982
957
(Project, '+index', ProjectView),
983
958
(Project, '+edit', ProjectEdit),
984
959
(Project, '+delete', ProjectDelete),
985
(Project, ('+export', 'project-export.sh'),
986
ProjectBashExportView),
989
962
breadcrumbs = {Subject: SubjectBreadcrumb,