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