~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/tutorial/__init__.py

  • Committer: William Grant
  • Date: 2009-03-17 04:48:07 UTC
  • mfrom: (1099.1.243 exercise-ui)
  • Revision ID: grantw@unimelb.edu.au-20090317044807-pozdt54fapazp2sp
Merge lp:~ivle-dev/ivle/exercise-ui.

Lecturers can now add and edit exercises, and worksheets can be written
in RST directly inside IVLE.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
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
 
50
 
 
51
from ivle.webapp.tutorial.exercise_service import ExercisesRESTView, \
 
52
                                                  ExerciseRESTView
50
53
 
51
54
class Worksheet:
52
55
    """This class represents a worksheet and a particular students progress
106
109
            if new_worksheet.assessable:
107
110
                # Calculate the user's score for this worksheet
108
111
                mand_done, mand_total, opt_done, opt_total = (
109
 
                    ivle.worksheet.calculate_score(req.store, req.user,
 
112
                    ivle.worksheet.utils.calculate_score(req.store, req.user,
110
113
                        worksheet))
111
114
                if opt_total > 0:
112
115
                    optional_message = " (excluding optional exercises)"
167
170
 
168
171
    def populate(self, req, ctx):
169
172
        self.plugin_scripts[Plugin] = ['tutorial.js']
170
 
        self.plugin_styles[Plugin] = ['tutorial.css']
 
173
        self.plugin_styles[Plugin] = ['tutorial.css', 'worksheet.css']
171
174
 
172
175
        if not self.context:
173
176
            raise NotFound()
176
179
        ctx['worksheet'] = self.context
177
180
        ctx['semester'] = self.semester
178
181
        ctx['year'] = self.year
 
182
 
 
183
        ctx['worksheetstream'] = genshi.Stream(list(genshi.XML(self.context.get_xml())))
179
184
        ctx['user'] = req.user
180
 
        ctx['worksheetstream'] = genshi.Stream(list(genshi.XML(self.context.data)))
181
185
 
182
186
        generate_worksheet_data(ctx, req, self.context)
183
187
 
332
336
    # work than we need. We just need to get the exercise name and a few other
333
337
    # fields from the XML.
334
338
 
335
 
    #TODO: Replace calls to minidom with calls to the database directly
336
339
    curctx['exercise'] = exercise
337
340
    if exercise.description is not None:
338
341
        desc = rstfunc(exercise.description)
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)
350
353
 
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
491
494
        
492
495
        ctx['mediapath'] = media_url(req, Plugin, 'images/')
493
 
        
494
 
 
 
496
 
 
497
 
 
498
class ExerciseEditView(XHTMLView):
 
499
    """View for editing a worksheet."""
 
500
    
 
501
    permission = 'edit'
 
502
    template = 'templates/exercise_edit.html'
 
503
    
 
504
    def __init__(self, req, exercise):
 
505
        self.context = req.store.find(Exercise, 
 
506
            Exercise.id == exercise).one()
 
507
 
 
508
        if self.context is None:
 
509
            raise NotFound()
 
510
    
 
511
    def populate(self, req, ctx):
 
512
        self.plugin_styles[Plugin] = ['exercise_admin.css']
 
513
        self.plugin_scripts[Plugin] = ['exercise_admin.js']
 
514
            
 
515
        ctx['mediapath'] = media_url(req, Plugin, 'images/')
 
516
        
 
517
        ctx['exercise'] = self.context
 
518
        #XXX: These should come from somewhere else
 
519
 
 
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')
 
523
        
 
524
        ctx['test_types'] = ('norm', 'check')
 
525
 
 
526
class ExerciseDeleteView(XHTMLView):
 
527
    """View for confirming the deletion of an exercise."""
 
528
    
 
529
    permission = 'edit'
 
530
    template = 'templates/exercise_delete.html'
 
531
    
 
532
    def __init__(self, req, exercise):
 
533
        self.context = req.store.find(Exercise,
 
534
            Exercise.id == exercise).one()
 
535
        
 
536
        if self.context is None:
 
537
            raise NotFound()
 
538
 
 
539
    def populate(self, req, ctx):
 
540
 
 
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'
 
545
            try:
 
546
                self.context.delete()
 
547
                ctx['deleted'] = True
 
548
            except:
 
549
                ctx['deleted'] = False
 
550
 
 
551
        # If get, display a delete confirmation page
 
552
        else:
 
553
            ctx['method'] = 'GET'
 
554
            if self.context.worksheet_exercises.count() is not 0:
 
555
                ctx['has_worksheets'] = True
 
556
            else:
 
557
                ctx['has_worksheets'] = False
 
558
        # Variables for the template
 
559
        ctx['exercise'] = self.context
 
560
        ctx['path'] = "/+exercises/" + self.context.id + "/+delete"
 
561
 
 
562
class ExerciseAddView(XHTMLView):
 
563
    """View for creating a new exercise."""
 
564
    
 
565
    permission = 'edit'
 
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):
 
571
                return True
 
572
        return False
 
573
        
 
574
    def populate(self, req, ctx):
 
575
        self.plugin_scripts[Plugin] = ['exercise_admin.js']
 
576
 
 
577
 
 
578
class ExercisesView(XHTMLView):
 
579
    """View for seeing the list of all exercises"""
 
580
    
 
581
    permission = 'edit'
 
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):
 
587
                return True
 
588
        return False
 
589
    
 
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/')
495
594
 
496
595
class Plugin(ViewPlugin, MediaPlugin):
497
596
    urls = [
 
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),
 
604
        
 
605
        # Worksheet Api Urls
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),
 
613
 
 
614
        # Exercise View Urls
 
615
        ('+exercises', ExercisesView),
 
616
        ('+exercises/+add', ExerciseAddView),
 
617
        ('+exercises/:exercise/+edit', ExerciseEditView),
 
618
        ('+exercises/:exercise/+delete', ExerciseDeleteView),
 
619
        
 
620
        # Exercise Api Urls
 
621
        ('api/+exercises', ExercisesRESTView),
 
622
        ('api/+exercises/*exercise', ExerciseRESTView),
511
623
    ]
512
624
 
513
625
    media = 'media'