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
309
325
ctx['worksheets'], problems_total, problems_done = (
310
326
ivle.worksheet.utils.create_list_of_fake_worksheets_and_stats(
311
req.config, req.store, req.user, self.context))
327
req.config, req.store, req.user, self.context,
328
as_of=self.context.worksheet_cutoff))
313
330
ctx['exercises_total'] = problems_total
314
331
ctx['exercises_done'] = problems_done
385
402
description = formencode.validators.UnicodeString(
386
403
if_missing=None, not_empty=False)
387
404
url = formencode.validators.URL(if_missing=None, not_empty=False)
405
worksheet_cutoff = DateTimeValidator(if_missing=None, not_empty=False)
388
406
show_worksheet_marks = formencode.validators.StringBoolean(
389
407
if_missing=False)
414
432
super(OfferingEdit, self).populate(req, ctx)
415
433
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
416
434
ctx['semesters'] = req.store.find(Semester).order_by(
417
Semester.year, Semester.semester)
435
Semester.year, Semester.display_name)
418
436
ctx['force_subject'] = None
420
438
def populate_state(self, state):
425
443
'subject': self.context.subject.short_name,
426
444
'semester': self.context.semester.year + '/' +
427
self.context.semester.semester,
445
self.context.semester.url_name,
428
446
'url': self.context.url,
429
447
'description': self.context.description,
448
'worksheet_cutoff': self.context.worksheet_cutoff,
430
449
'show_worksheet_marks': self.context.show_worksheet_marks,
436
455
self.context.semester = data['semester']
437
456
self.context.description = data['description']
438
457
self.context.url = unicode(data['url']) if data['url'] else None
458
self.context.worksheet_cutoff = data['worksheet_cutoff']
439
459
self.context.show_worksheet_marks = data['show_worksheet_marks']
440
460
return self.context
456
476
super(OfferingNew, self).populate(req, ctx)
457
477
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
458
478
ctx['semesters'] = req.store.find(Semester).order_by(
459
Semester.year, Semester.semester)
479
Semester.year, Semester.display_name)
460
480
ctx['force_subject'] = None
462
482
def populate_state(self, state):
471
491
new_offering.semester = data['semester']
472
492
new_offering.description = data['description']
473
493
new_offering.url = unicode(data['url']) if data['url'] else None
494
new_offering.worksheet_cutoff = data['worksheet_cutoff']
474
495
new_offering.show_worksheet_marks = data['show_worksheet_marks']
476
497
req.store.add(new_offering)
507
528
super(OfferingCloneWorksheets, self).populate(req, ctx)
508
529
ctx['subjects'] = req.store.find(Subject).order_by(Subject.name)
509
530
ctx['semesters'] = req.store.find(Semester).order_by(
510
Semester.year, Semester.semester)
531
Semester.year, Semester.display_name)
512
533
def get_default_data(self, req):
730
751
permission = "view_project_submissions"
733
def build_subversion_url(self, svnroot, submission):
734
princ = submission.assessed.principal
736
if isinstance(princ, User):
737
path = 'users/%s' % princ.login
739
path = 'groups/%s_%s_%s_%s' % (
740
princ.project_set.offering.subject.short_name,
741
princ.project_set.offering.semester.year,
742
princ.project_set.offering.semester.semester,
745
return urlparse.urljoin(
747
os.path.join(path, submission.path[1:] if
748
submission.path.startswith(os.sep) else
751
754
def populate(self, req, ctx):
752
755
self.plugin_styles[Plugin] = ["project.css"]
757
760
ctx['EnrolView'] = EnrolView
758
761
ctx['format_datetime'] = ivle.date.make_date_nice
759
762
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
760
ctx['build_subversion_url'] = self.build_subversion_url
761
ctx['svn_addr'] = req.config['urls']['svn_addr']
762
763
ctx['project'] = self.context
763
764
ctx['user'] = req.user
764
765
ctx['ProjectEdit'] = ProjectEdit
765
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
767
786
class ProjectUniquenessValidator(formencode.FancyValidator):
768
787
"""A FormEncode validator that checks that a project short_name is unique
963
982
(Project, '+index', ProjectView),
964
983
(Project, '+edit', ProjectEdit),
965
984
(Project, '+delete', ProjectDelete),
985
(Project, ('+export', 'project-export.sh'),
986
ProjectBashExportView),
968
989
breadcrumbs = {Subject: SubjectBreadcrumb,