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

« back to all changes in this revision

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

  • Committer: Nick Chadwick
  • Date: 2009-02-18 12:50:31 UTC
  • mto: (1099.1.180 new-dispatch)
  • mto: This revision was merged to the branch mainline in revision 1100.
  • Revision ID: chadnickbok@gmail.com-20090218125031-0gwxxfljq1iqipgz
Working on putting worksheets into the database.

This will lead to a nice editor for each worksheet.

This commit also introduces my changes linking problem attempts to
worksheets AND exercises, not just exercises.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
from ivle.webapp.media import BaseMediaFileView
45
45
from ivle.webapp.errors import NotFound, Forbidden
46
46
from ivle.webapp.tutorial.rst import rst as rstfunc
47
 
from ivle.webapp.tutorial.service import AttemptsRESTView, \
48
 
                                        AttemptRESTView, ExerciseRESTView
 
47
from ivle.webapp.tutorial.service import AttemptsRESTView, AttemptRESTView, \
 
48
                                         ExerciseRESTView, WorksheetRESTView
49
49
 
50
50
# Regex for valid identifiers (subject/worksheet names)
51
51
re_ident = re.compile("[0-9A-Za-z_]+")
55
55
        self.id = id
56
56
        self.name = name
57
57
        self.assessable = assessable
58
 
        self.loc = urllib.quote(id)
59
58
        self.complete_class = ''
60
59
        self.optional_message = ''
61
60
        self.total = 0
92
91
        # The subject directory must have a file "subject.xml" in it,
93
92
        # or it does not exist (404 error).
94
93
        ctx['subject'] = self.context.subject.code
95
 
        try:
96
 
            subjectfile = open(os.path.join(ivle.conf.subjects_base,
97
 
                                    self.context.subject.code, "subject.xml")).read()
98
 
        except:
99
 
            raise NotFound()
100
 
 
101
 
        subjectfile = genshi.Stream(list(genshi.XML(subjectfile)))
102
 
 
103
 
        ctx['worksheets'] = get_worksheets(subjectfile)
 
94
        ctx['year'] = self.context.semester.year
 
95
        ctx['semester'] = self.context.semester.semester
104
96
 
105
97
        # As we go, calculate the total score for this subject
106
98
        # (Assessable worksheets only, mandatory problems only)
 
99
 
 
100
        ctx['worksheets'] = []
107
101
        problems_done = 0
108
102
        problems_total = 0
109
 
        for worksheet in ctx['worksheets']:
110
 
            stored_worksheet = req.store.find(DBWorksheet,
111
 
                DBWorksheet.offering_id == self.context.id,
112
 
                DBWorksheet.name == worksheet.id).one()
113
 
            # If worksheet is not in database yet, we'll simply not display
114
 
            # data about it yet (it should be added as soon as anyone visits
115
 
            # the worksheet itself).
116
 
            if stored_worksheet is not None:
117
 
                # If the assessable status of this worksheet has changed,
118
 
                # update the DB
119
 
                # (Note: This fails the try block if the worksheet is not yet
120
 
                # in the DB, which is fine. The author should visit the
121
 
                # worksheet page to get it into the DB).
122
 
                if worksheet.assessable != stored_worksheet.assessable:
123
 
                    # XXX If statement to avoid unnecessary database writes.
124
 
                    # Is this necessary, or will Storm check for us?
125
 
                    stored_worksheet.assessable = worksheet.assessable
126
 
                if worksheet.assessable:
127
 
                    # Calculate the user's score for this worksheet
128
 
                    mand_done, mand_total, opt_done, opt_total = (
129
 
                        ivle.worksheet.calculate_score(req.store, req.user,
130
 
                            stored_worksheet))
131
 
                    if opt_total > 0:
132
 
                        optional_message = " (excluding optional exercises)"
133
 
                    else:
134
 
                        optional_message = ""
135
 
                    if mand_done >= mand_total:
136
 
                        worksheet.complete_class = "complete"
137
 
                    elif mand_done > 0:
138
 
                        worksheet.complete_class = "semicomplete"
139
 
                    else:
140
 
                        worksheet.complete_class = "incomplete"
141
 
                    problems_done += mand_done
142
 
                    problems_total += mand_total
143
 
                    worksheet.mand_done = mand_done
144
 
                    worksheet.total = mand_total
145
 
                    worksheet.optional_message = optional_message
 
103
        for worksheet in self.context.worksheets:
 
104
            new_worksheet = Worksheet(worksheet.identifier, worksheet.name, 
 
105
                                      worksheet.assessable)
 
106
            if new_worksheet.assessable:
 
107
                # Calculate the user's score for this worksheet
 
108
                mand_done, mand_total, opt_done, opt_total = (
 
109
                    ivle.worksheet.calculate_score(req.store, req.user,
 
110
                        worksheet))
 
111
                if opt_total > 0:
 
112
                    optional_message = " (excluding optional exercises)"
 
113
                else:
 
114
                    optional_message = ""
 
115
                if mand_done >= mand_total:
 
116
                    new_worksheet.complete_class = "complete"
 
117
                elif mand_done > 0:
 
118
                    new_worksheet.complete_class = "semicomplete"
 
119
                else:
 
120
                    new_worksheet.complete_class = "incomplete"
 
121
                problems_done += mand_done
 
122
                problems_total += mand_total
 
123
                new_worksheet.mand_done = mand_done
 
124
                new_worksheet.total = mand_total
 
125
                new_worksheet.optional_message = optional_message
 
126
            ctx['worksheets'].append(new_worksheet)
146
127
 
147
128
 
148
129
        ctx['problems_total'] = problems_total
176
157
            Offering.semester_id == Semester.id,
177
158
            Semester.year == year,
178
159
            Semester.semester == semester,
179
 
            DBWorksheet.name == worksheet).one()
 
160
            DBWorksheet.identifier == worksheet).one()
180
161
        
181
162
        self.worksheetname = worksheet
182
163
        self.year = year
189
170
        if not self.context:
190
171
            raise NotFound()
191
172
 
192
 
        # Read in worksheet data
193
 
        worksheetfilename = os.path.join(ivle.conf.subjects_base,
194
 
                               self.context.offering.subject.code, self.worksheetname + ".xml")
195
 
        try:
196
 
            worksheetfile = open(worksheetfilename)
197
 
            worksheetmtime = os.path.getmtime(worksheetfilename)
198
 
        except:
199
 
            raise NotFound()
200
 
 
201
 
        worksheetmtime = datetime.fromtimestamp(worksheetmtime)
202
 
        worksheetfile = worksheetfile.read()
203
 
 
204
173
        ctx['subject'] = self.context.offering.subject.code
205
174
        ctx['worksheet'] = self.worksheetname
206
175
        ctx['semester'] = self.semester
207
176
        ctx['year'] = self.year
208
 
        ctx['worksheetstream'] = genshi.Stream(list(genshi.XML(worksheetfile)))
 
177
        ctx['worksheetstream'] = genshi.Stream(list(genshi.XML(self.context.data)))
209
178
 
210
179
        generate_worksheet_data(ctx, req, self.context)
211
180
 
212
 
        update_db_worksheet(req.store, self.context.offering.subject.code, self.worksheetname,
213
 
            worksheetmtime, ctx['exerciselist'])
214
 
 
215
181
        ctx['worksheetstream'] = add_exercises(ctx['worksheetstream'], ctx, req)
216
182
 
217
183
class SubjectMediaView(BaseMediaFileView):
349
315
    curctx['filename'] = exercisesrc
350
316
 
351
317
    # Retrieve the exercise details from the database
352
 
    exercise = req.store.find(Exercise, Exercise.id == exercisesrc).one()
 
318
    exercise = req.store.find(Exercise, Exercise.id == unicode(exercisesrc)).one()
353
319
    
354
320
    if exercise is None:
355
 
        raise NotFound()
 
321
        raise NotFound(exercisesrc)
356
322
 
357
323
    # Read exercise file and present the exercise
358
324
    # Note: We do not use the testing framework because it does a lot more
362
328
    #TODO: Replace calls to minidom with calls to the database directly
363
329
    curctx['exercise'] = exercise
364
330
    if exercise.description is not None:
365
 
        curctx['description'] = genshi.XML('<div id="description">' + 
366
 
                                           exercise.description + '</div>')
 
331
        desc = rstfunc(exercise.description)
 
332
        curctx['description'] = genshi.XML('<div id="description">' + desc + 
 
333
                                           '</div>')
 
334
        #curctx['description'] = genshi.XML('<div id="description">' + 
 
335
        #                                   exercise.description + '</div>')
367
336
    else:
368
337
        curctx['description'] = None
369
338
 
395
364
            'stream': ex_stream,
396
365
            'exid': exercise.id}
397
366
 
398
 
 
399
 
def update_db_worksheet(store, subject, worksheetname, file_mtime,
400
 
    exercise_list=None, assessable=None):
401
 
    """
402
 
    Determines if the database is missing this worksheet or out of date,
403
 
    and inserts or updates its details about the worksheet.
404
 
    file_mtime is a datetime.datetime with the modification time of the XML
405
 
    file. The database will not be updated unless worksheetmtime is newer than
406
 
    the mtime in the database.
407
 
    exercise_list is a list of (filename, optional) pairs as returned by
408
 
    present_table_of_contents.
409
 
    assessable is boolean.
410
 
    exercise_list and assessable are optional, and if omitted, will not change
411
 
    the existing data. If the worksheet does not yet exist, and assessable
412
 
    is omitted, it defaults to False.
413
 
    """
414
 
"""    worksheet = ivle.database.Worksheet.get_by_name(store, subject,
415
 
                                                    worksheetname)
416
 
 
417
 
    updated_database = False
418
 
    if worksheet is None:
419
 
        # If assessable is not supplied, default to False.
420
 
        if assessable is None:
421
 
            assessable = False
422
 
        # Create a new Worksheet
423
 
        worksheet = ivle.database.Worksheet(subject=unicode(subject),
424
 
            name=unicode(worksheetname), assessable=assessable,
425
 
            mtime=datetime.now())
426
 
        store.add(worksheet)
427
 
        updated_database = True
428
 
    else:
429
 
        if file_mtime > worksheet.mtime:
430
 
            # File on disk is newer than database. Need to update.
431
 
            worksheet.mtime = datetime.now()
432
 
            if exercise_list is not None:
433
 
                # exercise_list is supplied, so delete any existing problems
434
 
                worksheet.remove_all_exercises(store)
435
 
            if assessable is not None:
436
 
                worksheet.assessable = assessable
437
 
            updated_database = True
438
 
 
439
 
    if updated_database and exercise_list is not None:
440
 
        # Insert each exercise into the worksheet
441
 
        for exercise_name, optional in exercise_list:
442
 
            # Get the Exercise from the DB
443
 
            exercise = store.find(Exercise, Exercise.id == exercise_name).one()
444
 
            # Create a new binding between the worksheet and the exercise
445
 
            worksheetexercise = ivle.database.WorksheetExercise(
446
 
                    worksheet=worksheet, exercise=exercise, optional=optional)
447
 
 
448
 
    store.commit()"""
 
367
class OfferingAdminView(XHTMLView):
 
368
    """The admin view for an Offering.
 
369
    
 
370
    This class is designed to check the user has admin privileges, and
 
371
    then allow them to edit the RST for the offering, which controls which
 
372
    worksheets are actually displayed on the page."""
 
373
    pass
 
374
 
 
375
class WorksheetAdminView(XHTMLView):
 
376
    """The admin view for an offering.
 
377
    
 
378
    This view is designed to replace worksheets.xml, turning them instead
 
379
    into XML directly from RST."""
 
380
    permission = "edit"
 
381
    template = "worksheet_admin.html"
 
382
    appname = "Worksheet Admin"
 
383
 
 
384
    def __init__(self, req, subject, year, semester, worksheet):
 
385
        self.context = req.store.find(DBWorksheet,
 
386
            DBWorksheet.identifier == worksheet,
 
387
            DBWorksheet.offering_id == Offering.id,
 
388
            Offering.semester_id == Semester.id,
 
389
            Semester.year == year,
 
390
            Semester.semester == semester,
 
391
            Offering.subject_id == Subject.id,
 
392
            Subject.code == subject
 
393
        ).one()
 
394
        
 
395
        self.subject = subject
 
396
        self.year = year
 
397
        self.semester = semester
 
398
        
 
399
        if self.context is None:
 
400
            raise NotFound()
 
401
            
 
402
    def populate(self, req, ctx):
 
403
        self.plugin_styles[Plugin] = ["tutorial_admin.css"]
 
404
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
 
405
        
 
406
        ctx['worksheet'] = self.context
 
407
        ctx['subject'] = self.subject
 
408
        ctx['year'] = self.year
 
409
        ctx['semester'] = self.semester
 
410
        ctx['upload_path'] = "/api/subjects/" + self.subject + "/" + \
 
411
            self.year + "/" + self.semester + "/edit/+worksheets/" + \
 
412
            self.context.identifier
 
413
 
449
414
 
450
415
class Plugin(ViewPlugin, MediaPlugin):
451
416
    urls = [
457
422
        ('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise/'
458
423
                '+attempts/:username/:date', AttemptRESTView),
459
424
        ('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise', ExerciseRESTView),
 
425
        ('subjects/:subject/:year/:semester/edit/+worksheets/:worksheet', WorksheetAdminView),
 
426
        ('api/subjects/:subject/:year/:semester/edit/+worksheets/:worksheet', WorksheetRESTView)
460
427
    ]
461
428
 
462
429
    tabs = [