34
34
'get_exercise_attempts', 'get_exercise_attempt',
37
def get_exercise_status(store, user, worksheet_exercise):
37
def get_exercise_status(store, user, worksheet_exercise, as_of=None):
38
38
"""Given a storm.store, User and Exercise, returns information about
39
39
the user's performance on that problem.
40
@param store: A storm.store
42
@param worksheet_exercise: An Exercise.
43
@param as_of: Optional datetime. If supplied, gets the status as of as_of.
40
44
Returns a tuple of:
41
45
- A boolean, whether they have successfully passed this exercise.
42
46
- An int, the number of attempts they have made up to and
48
52
is_relevant = ((ExerciseAttempt.user_id == user.id) &
49
53
(ExerciseAttempt.ws_ex_id == worksheet_exercise.id) &
50
54
(ExerciseAttempt.active == True))
56
is_relevant &= ExerciseAttempt.date <= as_of
52
58
# Get the first successful active attempt, or None if no success yet.
53
59
# (For this user, for this exercise).
162
168
saved.date = date
163
169
saved.text = text
165
def calculate_score(store, user, worksheet):
171
def calculate_score(store, user, worksheet, as_of=None):
167
173
Given a storm.store, User, Exercise and Worksheet, calculates a score for
168
174
the user on the given worksheet.
175
@param store: A storm.store
177
@param worksheet: A Worksheet.
178
@param as_of: Optional datetime. If supplied, gets the score as of as_of.
169
179
Returns a 4-tuple of ints, consisting of:
170
180
(No. mandatory exercises completed,
171
181
Total no. mandatory exercises,
183
193
worksheet = worksheet_exercise.worksheet
184
194
optional = worksheet_exercise.optional
186
done, _ = get_exercise_status(store, user, worksheet_exercise)
196
done, _ = get_exercise_status(store, user, worksheet_exercise, as_of)
187
197
# done is a bool, whether this student has completed that problem
195
205
return mand_done, mand_total, opt_done, opt_total
207
def calculate_mark(mand_done, mand_total):
208
"""Calculate a subject mark, given the result of all worksheets.
209
@param mand_done: The total number of mandatory exercises completed by
210
some student, across all worksheets.
211
@param mand_total: The total number of mandatory exercises across all
212
worksheets in the offering.
213
@return: (percent, mark, mark_total)
214
percent: The percentage of exercises the student has completed, as an
215
integer between 0 and 100 inclusive.
216
mark: The mark the student has received, based on the percentage.
217
mark_total: The total number of marks available (currently hard-coded
220
# We want to display a students mark out of 5. However, they are
221
# allowed to skip 1 in 5 questions and still get 'full marks'.
222
# Hence we divide by 16, essentially making 16 percent worth
223
# 1 star, and 80 or above worth 5.
225
percent_int = (100 * mand_done) // mand_total
227
# Avoid Div0, just give everyone 0 marks if there are none available
229
# percent / 16, rounded down, with a maximum mark of 5
231
mark = min(percent_int // 16, max_mark)
232
return (percent_int, mark, max_mark)
197
234
def update_exerciselist(worksheet):
198
235
"""Runs through the worksheetstream, generating the appropriate
199
236
WorksheetExercises, and de-activating the old ones."""
201
238
# Turns the worksheet into an xml stream, and then finds all the
202
239
# exercise nodes in the stream.
203
worksheetdata = genshi.XML(worksheet.data)
240
worksheetdata = genshi.XML(worksheet.get_xml())
204
241
for kind, data, pos in worksheetdata:
205
242
if kind is genshi.core.START:
206
243
# Data is a tuple of tag name and a list of name->value tuples