38
38
from ivle.database import Subject, Offering, Semester, Exercise, \
39
39
ExerciseSave, WorksheetExercise
40
40
from ivle.database import Worksheet as DBWorksheet
41
import ivle.worksheet.utils
42
42
from ivle.webapp.base.views import BaseView
43
43
from ivle.webapp.base.xhtml import XHTMLView
44
44
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
45
45
from ivle.webapp.media import BaseMediaFileView, media_url
46
46
from ivle.webapp.errors import NotFound, Forbidden
47
from ivle.webapp.tutorial.rst import rst as rstfunc
47
from ivle.worksheet.rst import rst as rstfunc
48
48
from ivle.webapp.tutorial.service import AttemptsRESTView, AttemptRESTView, \
49
WorksheetExerciseRESTView, WorksheetRESTView, WorksheetsRESTView
49
WorksheetExerciseRESTView, WorksheetRESTView, WorksheetsRESTView
51
from ivle.webapp.tutorial.exercise_service import ExercisesRESTView, \
52
55
"""This class represents a worksheet and a particular students progress
345
348
# an attempt, then use that text instead of the supplied "partial".
346
349
# Get exercise stored text will return a save, or the most recent attempt,
347
350
# whichever is more recent
348
save = ivle.worksheet.get_exercise_stored_text(
351
save = ivle.worksheet.utils.get_exercise_stored_text(
349
352
req.store, req.user, worksheet_exercise)
351
354
# Also get the number of attempts taken and whether this is complete.
352
355
complete, curctx['attempts'] = \
353
ivle.worksheet.get_exercise_status(req.store, req.user,
356
ivle.worksheet.utils.get_exercise_status(req.store, req.user,
354
357
worksheet_exercise)
355
358
if save is not None:
356
359
curctx['exercisesave'] = save.text
490
493
ctx['worksheets'] = self.context.worksheets
492
495
ctx['mediapath'] = media_url(req, Plugin, 'images/')
498
class ExerciseEditView(XHTMLView):
499
"""View for editing a worksheet."""
502
template = 'templates/exercise_edit.html'
504
def __init__(self, req, exercise):
505
self.context = req.store.find(Exercise,
506
Exercise.id == exercise).one()
508
if self.context is None:
511
def populate(self, req, ctx):
512
self.plugin_styles[Plugin] = ['exercise_admin.css']
513
self.plugin_scripts[Plugin] = ['exercise_admin.js']
515
ctx['mediapath'] = media_url(req, Plugin, 'images/')
517
ctx['exercise'] = self.context
518
#XXX: These should come from somewhere else
520
ctx['var_types'] = (u'file', u'var', u'arg', u'exception')
521
ctx['part_types'] = (u'stdout',u'stderr', u'result',
522
u'exception', u'file', u'code')
524
ctx['test_types'] = ('norm', 'check')
526
class ExerciseDeleteView(XHTMLView):
527
"""View for confirming the deletion of an exercise."""
530
template = 'templates/exercise_delete.html'
532
def __init__(self, req, exercise):
533
self.context = req.store.find(Exercise,
534
Exercise.id == exercise).one()
536
if self.context is None:
539
def populate(self, req, ctx):
541
# If post, delete the exercise, or display a message explaining that
542
# the exercise cannot be deleted
543
if req.method == 'POST':
544
ctx['method'] = 'POST'
546
self.context.delete()
547
ctx['deleted'] = True
549
ctx['deleted'] = False
551
# If get, display a delete confirmation page
553
ctx['method'] = 'GET'
554
if self.context.worksheet_exercises.count() is not 0:
555
ctx['has_worksheets'] = True
557
ctx['has_worksheets'] = False
558
# Variables for the template
559
ctx['exercise'] = self.context
560
ctx['path'] = "/+exercises/" + self.context.id + "/+delete"
562
class ExerciseAddView(XHTMLView):
563
"""View for creating a new exercise."""
566
template = 'templates/exercise_add.html'
567
#XXX: This should be done somewhere else
568
def authorize(self, req):
569
for offering in req.store.find(Offering):
570
if 'edit' in offering.get_permissions(req.user):
574
def populate(self, req, ctx):
575
self.plugin_scripts[Plugin] = ['exercise_admin.js']
578
class ExercisesView(XHTMLView):
579
"""View for seeing the list of all exercises"""
582
template = 'templates/exercises.html'
583
#XXX: This should be done somewhere else
584
def authorize(self, req):
585
for offering in req.store.find(Offering):
586
if 'edit' in offering.get_permissions(req.user):
590
def populate(self, req, ctx):
591
self.plugin_styles[Plugin] = ['exercise_admin.css']
592
ctx['exercises'] = req.store.find(Exercise).order_by(Exercise.id)
593
ctx['mediapath'] = media_url(req, Plugin, 'images/')
496
595
class Plugin(ViewPlugin, MediaPlugin):
597
# Worksheet View Urls
498
598
('subjects/:subject/+worksheets/+media/*(path)', SubjectMediaView),
499
599
('subjects/:subject/:year/:semester/+worksheets', OfferingView),
500
600
('subjects/:subject/:year/:semester/+worksheets/+new', WorksheetAddView),
501
601
('subjects/:subject/:year/:semester/+worksheets/+edit', WorksheetsEditView),
502
602
('subjects/:subject/:year/:semester/+worksheets/:worksheet', WorksheetView),
503
603
('subjects/:subject/:year/:semester/+worksheets/:worksheet/+edit', WorksheetEditView),
504
606
('api/subjects/:subject/:year/:semester/+worksheets', WorksheetsRESTView),
505
607
('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise/'
506
608
'+attempts/:username', AttemptsRESTView),
508
610
'+attempts/:username/:date', AttemptRESTView),
509
611
('api/subjects/:subject/:year/:semester/+worksheets/:worksheet', WorksheetRESTView),
510
612
('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise', WorksheetExerciseRESTView),
615
('+exercises', ExercisesView),
616
('+exercises/+add', ExerciseAddView),
617
('+exercises/:exercise/+edit', ExerciseEditView),
618
('+exercises/:exercise/+delete', ExerciseDeleteView),
621
('api/+exercises', ExercisesRESTView),
622
('api/+exercises/*exercise', ExerciseRESTView),