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
96
subjectfile = open(os.path.join(ivle.conf.subjects_base,
97
self.context.subject.code, "subject.xml")).read()
101
subjectfile = genshi.Stream(list(genshi.XML(subjectfile)))
103
ctx['worksheets'] = get_worksheets(subjectfile)
94
ctx['year'] = self.context.semester.year
95
ctx['semester'] = self.context.semester.semester
105
97
# As we go, calculate the total score for this subject
106
98
# (Assessable worksheets only, mandatory problems only)
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,
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,
132
optional_message = " (excluding optional exercises)"
134
optional_message = ""
135
if mand_done >= mand_total:
136
worksheet.complete_class = "complete"
138
worksheet.complete_class = "semicomplete"
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,
112
optional_message = " (excluding optional exercises)"
114
optional_message = ""
115
if mand_done >= mand_total:
116
new_worksheet.complete_class = "complete"
118
new_worksheet.complete_class = "semicomplete"
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)
148
129
ctx['problems_total'] = problems_total
189
170
if not self.context:
192
# Read in worksheet data
193
worksheetfilename = os.path.join(ivle.conf.subjects_base,
194
self.context.offering.subject.code, self.worksheetname + ".xml")
196
worksheetfile = open(worksheetfilename)
197
worksheetmtime = os.path.getmtime(worksheetfilename)
201
worksheetmtime = datetime.fromtimestamp(worksheetmtime)
202
worksheetfile = worksheetfile.read()
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)))
210
179
generate_worksheet_data(ctx, req, self.context)
212
update_db_worksheet(req.store, self.context.offering.subject.code, self.worksheetname,
213
worksheetmtime, ctx['exerciselist'])
215
181
ctx['worksheetstream'] = add_exercises(ctx['worksheetstream'], ctx, req)
217
183
class SubjectMediaView(BaseMediaFileView):
395
364
'stream': ex_stream,
396
365
'exid': exercise.id}
399
def update_db_worksheet(store, subject, worksheetname, file_mtime,
400
exercise_list=None, assessable=None):
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.
414
""" worksheet = ivle.database.Worksheet.get_by_name(store, subject,
417
updated_database = False
418
if worksheet is None:
419
# If assessable is not supplied, default to False.
420
if assessable is None:
422
# Create a new Worksheet
423
worksheet = ivle.database.Worksheet(subject=unicode(subject),
424
name=unicode(worksheetname), assessable=assessable,
425
mtime=datetime.now())
427
updated_database = True
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
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)
367
class OfferingAdminView(XHTMLView):
368
"""The admin view for an Offering.
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."""
375
class WorksheetAdminView(XHTMLView):
376
"""The admin view for an offering.
378
This view is designed to replace worksheets.xml, turning them instead
379
into XML directly from RST."""
381
template = "worksheet_admin.html"
382
appname = "Worksheet Admin"
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
395
self.subject = subject
397
self.semester = semester
399
if self.context is None:
402
def populate(self, req, ctx):
403
self.plugin_styles[Plugin] = ["tutorial_admin.css"]
404
self.plugin_scripts[Plugin] = ['tutorial_admin.js']
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
450
415
class Plugin(ViewPlugin, MediaPlugin):