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

« back to all changes in this revision

Viewing changes to ivle/worksheet/utils.py

  • Committer: William Grant
  • Date: 2009-05-26 02:53:37 UTC
  • Revision ID: grantw@unimelb.edu.au-20090526025337-bok6syd4y9zpot8b
.. and one more

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
This module provides functions for tutorial and worksheet computations.
24
24
"""
25
25
 
26
 
from storm.locals import And, Asc, Desc
 
26
from storm.locals import And, Asc, Desc, Store
 
27
import genshi
 
28
 
27
29
import ivle.database
28
 
from ivle.database import ExerciseAttempt, ExerciseSave, Worksheet
 
30
from ivle.database import ExerciseAttempt, ExerciseSave, Worksheet, \
 
31
                          WorksheetExercise, Exercise
29
32
 
30
33
__all__ = ['get_exercise_status', 'get_exercise_stored_text',
31
34
           'get_exercise_attempts', 'get_exercise_attempt',
32
35
          ]
33
36
 
34
 
def get_exercise_status(store, user, worksheet_exercise):
 
37
def get_exercise_status(store, user, worksheet_exercise, as_of=None):
35
38
    """Given a storm.store, User and Exercise, returns information about
36
39
    the user's performance on that problem.
 
40
    @param store: A storm.store
 
41
    @param user: A User.
 
42
    @param worksheet_exercise: An Exercise.
 
43
    @param as_of: Optional datetime. If supplied, gets the status as of as_of.
37
44
    Returns a tuple of:
38
45
        - A boolean, whether they have successfully passed this exercise.
39
46
        - An int, the number of attempts they have made up to and
45
52
    is_relevant = ((ExerciseAttempt.user_id == user.id) &
46
53
            (ExerciseAttempt.ws_ex_id == worksheet_exercise.id) &
47
54
            (ExerciseAttempt.active == True))
 
55
    if as_of is not None:
 
56
        is_relevant &= ExerciseAttempt.date <= as_of
48
57
 
49
58
    # Get the first successful active attempt, or None if no success yet.
50
59
    # (For this user, for this exercise).
159
168
    saved.date = date
160
169
    saved.text = text
161
170
 
162
 
def calculate_score(store, user, worksheet):
 
171
def calculate_score(store, user, worksheet, as_of=None):
163
172
    """
164
173
    Given a storm.store, User, Exercise and Worksheet, calculates a score for
165
174
    the user on the given worksheet.
 
175
    @param store: A storm.store
 
176
    @param user: A User.
 
177
    @param worksheet: A Worksheet.
 
178
    @param as_of: Optional datetime. If supplied, gets the score as of as_of.
166
179
    Returns a 4-tuple of ints, consisting of:
167
180
    (No. mandatory exercises completed,
168
181
     Total no. mandatory exercises,
180
193
        worksheet = worksheet_exercise.worksheet
181
194
        optional = worksheet_exercise.optional
182
195
 
183
 
        done, _ = get_exercise_status(store, user, worksheet_exercise)
 
196
        done, _ = get_exercise_status(store, user, worksheet_exercise, as_of)
184
197
        # done is a bool, whether this student has completed that problem
185
198
        if optional:
186
199
            opt_total += 1
190
203
            if done: mand_done += 1
191
204
 
192
205
    return mand_done, mand_total, opt_done, opt_total
 
206
 
 
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
 
218
            as 5).
 
219
    """
 
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.
 
224
    if mand_total > 0:
 
225
        percent_int = (100 * mand_done) // mand_total
 
226
    else:
 
227
        # Avoid Div0, just give everyone 0 marks if there are none available
 
228
        percent_int = 0
 
229
    # percent / 16, rounded down, with a maximum mark of 5
 
230
    max_mark = 5
 
231
    mark = min(percent_int // 16, max_mark)
 
232
    return (percent_int, mark, max_mark)
 
233
 
 
234
def update_exerciselist(worksheet):
 
235
    """Runs through the worksheetstream, generating the appropriate
 
236
    WorksheetExercises, and de-activating the old ones."""
 
237
    exercises = []
 
238
    # Turns the worksheet into an xml stream, and then finds all the 
 
239
    # exercise nodes in the stream.
 
240
    worksheetdata = genshi.XML(worksheet.get_xml())
 
241
    for kind, data, pos in worksheetdata:
 
242
        if kind is genshi.core.START:
 
243
            # Data is a tuple of tag name and a list of name->value tuples
 
244
            if data[0] == 'exercise':
 
245
                src = ""
 
246
                optional = False
 
247
                for attr in data[1]:
 
248
                    if attr[0] == 'src':
 
249
                        src = attr[1]
 
250
                    if attr[0] == 'optional':
 
251
                        optional = attr[1] == 'true'
 
252
                if src != "":
 
253
                    exercises.append((src, optional))
 
254
    ex_num = 0
 
255
    # Set all current worksheet_exercises to be inactive
 
256
    db_worksheet_exercises = Store.of(worksheet).find(WorksheetExercise,
 
257
        WorksheetExercise.worksheet_id == worksheet.id)
 
258
    for worksheet_exercise in db_worksheet_exercises:
 
259
        worksheet_exercise.active = False
 
260
    
 
261
    for exerciseid, optional in exercises:
 
262
        worksheet_exercise = Store.of(worksheet).find(WorksheetExercise,
 
263
            WorksheetExercise.worksheet_id == worksheet.id,
 
264
            Exercise.id == WorksheetExercise.exercise_id,
 
265
            Exercise.id == exerciseid).one()
 
266
        if worksheet_exercise is None:
 
267
            exercise = Store.of(worksheet).find(Exercise,
 
268
                Exercise.id == exerciseid
 
269
            ).one()
 
270
            if exercise is None:
 
271
                raise NotFound()
 
272
            worksheet_exercise = WorksheetExercise()
 
273
            worksheet_exercise.worksheet_id = worksheet.id
 
274
            worksheet_exercise.exercise_id = exercise.id
 
275
            Store.of(worksheet).add(worksheet_exercise)
 
276
        worksheet_exercise.active = True
 
277
        worksheet_exercise.seq_no = ex_num
 
278
        worksheet_exercise.optional = optional
 
279