31
31
import ivle.database
32
32
from ivle.database import ExerciseAttempt, ExerciseSave, Worksheet, \
33
WorksheetExercise, Exercise, User
33
WorksheetExercise, Exercise
34
34
import ivle.webapp.tutorial.test
36
36
__all__ = ['ExerciseNotFound', 'get_exercise_status',
37
'get_exercise_statistics',
38
37
'get_exercise_stored_text', 'get_exercise_attempts',
39
38
'get_exercise_attempt', 'test_exercise_submission',
84
83
return first_success is not None, num_attempts
86
def get_exercise_statistics(store, worksheet_exercise):
87
"""Return statistics about an exercise (with respect to a given
89
(number of students completed, number of students attempted)."""
90
# Count the set of Users whose ID matches an attempt in this worksheet
91
num_completed = store.find(User, User.id == ExerciseAttempt.user_id,
92
ExerciseAttempt.ws_ex_id == worksheet_exercise.id,
93
ExerciseAttempt.complete == True).config(distinct=True).count()
94
num_attempted = store.find(User, User.id == ExerciseAttempt.user_id,
95
ExerciseAttempt.ws_ex_id == worksheet_exercise.id,
96
).config(distinct=True).count()
97
return num_completed, num_attempted
99
85
def get_exercise_stored_text(store, user, worksheet_exercise):
100
86
"""Given a storm.store, User and WorksheetExercise, returns an
101
87
ivle.database.ExerciseSave object for the last saved/submitted attempt for
259
245
# Turns the worksheet into an xml stream, and then finds all the
260
246
# exercise nodes in the stream.
261
worksheetdata = genshi.XML(worksheet.data_xhtml)
247
worksheetdata = genshi.XML(worksheet.get_xml())
262
248
for kind, data, pos in worksheetdata:
263
249
if kind is genshi.core.START:
264
250
# Data is a tuple of tag name and a list of name->value tuples
325
311
return test_results
328
class FakeWorksheetForMarks:
329
"""This class represents a worksheet and a particular students progress
332
Do not confuse this with a worksheet in the database. This worksheet
333
has extra information for use in the output, such as marks."""
334
def __init__(self, id, name, assessable, published):
337
self.assessable = assessable
338
self.published = published
339
self.complete_class = ''
340
self.optional_message = ''
344
return ("Worksheet(id=%s, name=%s, assessable=%s)"
345
% (repr(self.id), repr(self.name), repr(self.assessable)))
348
# XXX: This really shouldn't be needed.
349
def create_list_of_fake_worksheets_and_stats(config, store, user, offering):
350
"""Take an offering's real worksheets, converting them into stats.
352
The worksheet listing views expect special fake worksheet objects
353
that contain counts of exercises, whether they've been completed,
354
that sort of thing. A fake worksheet object is used to contain
355
these values, because nobody has managed to refactor the need out
362
# Offering.worksheets is ordered by the worksheets seq_no
363
worksheets = offering.worksheets
365
# Unless we can edit worksheets, hide unpublished ones.
366
if 'edit_worksheets' not in offering.get_permissions(user, config):
367
worksheets = worksheets.find(published=True)
369
for worksheet in worksheets:
370
new_worksheet = FakeWorksheetForMarks(
371
worksheet.identifier, worksheet.name, worksheet.assessable,
373
if new_worksheet.assessable:
374
# Calculate the user's score for this worksheet
375
mand_done, mand_total, opt_done, opt_total = (
376
ivle.worksheet.utils.calculate_score(store, user, worksheet))
378
optional_message = " (excluding optional exercises)"
380
optional_message = ""
381
if mand_done >= mand_total:
382
new_worksheet.complete_class = "complete"
384
new_worksheet.complete_class = "semicomplete"
386
new_worksheet.complete_class = "incomplete"
387
problems_done += mand_done
388
problems_total += mand_total
389
new_worksheet.mand_done = mand_done
390
new_worksheet.total = mand_total
391
new_worksheet.optional_message = optional_message
392
new_worksheets.append(new_worksheet)
394
return new_worksheets, problems_total, problems_done