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

« back to all changes in this revision

Viewing changes to ivle/worksheet.py

ivle.studpath.url_to_jailpaths: Fix the doctest to use new paths.

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, Store
27
 
import genshi
28
 
 
 
26
from storm.locals import And, Asc, Desc
29
27
import ivle.database
30
 
from ivle.database import ExerciseAttempt, ExerciseSave, Worksheet, \
31
 
                          WorksheetExercise, Exercise
32
28
 
33
29
__all__ = ['get_exercise_status', 'get_exercise_stored_text',
34
30
           'get_exercise_attempts', 'get_exercise_attempt',
35
31
          ]
36
32
 
37
 
def get_exercise_status(store, user, worksheet_exercise):
 
33
def get_exercise_status(store, user, exercise):
38
34
    """Given a storm.store, User and Exercise, returns information about
39
35
    the user's performance on that problem.
40
36
    Returns a tuple of:
43
39
          including the first successful attempt (or the total number of
44
40
          attempts, if not yet successful).
45
41
    """
 
42
    ExerciseAttempt = ivle.database.ExerciseAttempt
46
43
    # A Storm expression denoting all active attempts by this user for this
47
44
    # exercise.
48
45
    is_relevant = ((ExerciseAttempt.user_id == user.id) &
49
 
            (ExerciseAttempt.ws_ex_id == worksheet_exercise.id) &
50
 
            (ExerciseAttempt.active == True))
 
46
                   (ExerciseAttempt.exercise_id == exercise.id) &
 
47
                   (ExerciseAttempt.active == True))
51
48
 
52
49
    # Get the first successful active attempt, or None if no success yet.
53
50
    # (For this user, for this exercise).
69
66
 
70
67
    return first_success is not None, num_attempts
71
68
 
72
 
def get_exercise_stored_text(store, user, worksheet_exercise):
73
 
    """Given a storm.store, User and WorksheetExercise, returns an
 
69
def get_exercise_stored_text(store, user, exercise):
 
70
    """Given a storm.store, User and Exercise, returns an
74
71
    ivle.database.ExerciseSave object for the last saved/submitted attempt for
75
72
    this question (note that ExerciseAttempt is a subclass of ExerciseSave).
76
73
    Returns None if the user has not saved or made an attempt on this
78
75
    If the user has both saved and submitted, it returns whichever was
79
76
    made last.
80
77
    """
 
78
    ExerciseSave = ivle.database.ExerciseSave
 
79
    ExerciseAttempt = ivle.database.ExerciseAttempt
81
80
 
82
81
    # Get the saved text, or None
83
82
    saved = store.find(ExerciseSave,
84
83
                ExerciseSave.user_id == user.id,
85
 
                ExerciseSave.ws_ex_id == worksheet_exercise.id).one()
 
84
                ExerciseSave.exercise_id == exercise.id).one()
86
85
 
87
86
    # Get the most recent attempt, or None
88
87
    attempt = store.find(ExerciseAttempt,
89
88
            ExerciseAttempt.user_id == user.id,
 
89
            ExerciseAttempt.exercise_id == exercise.id,
90
90
            ExerciseAttempt.active == True,
91
 
            ExerciseAttempt.ws_ex_id == worksheet_exercise.id
92
91
        ).order_by(Asc(ExerciseAttempt.date)).last()
93
92
 
94
93
    # Pick the most recent of these two
103
102
        else:
104
103
            return None
105
104
 
106
 
def _get_exercise_attempts(store, user, worksheet_exercise, as_of=None,
 
105
def _get_exercise_attempts(store, user, exercise, as_of=None,
107
106
        allow_inactive=False):
108
107
    """Same as get_exercise_attempts, but doesn't convert Storm's iterator
109
108
    into a list."""
 
109
    ExerciseAttempt = ivle.database.ExerciseAttempt
110
110
 
111
111
    # Get the most recent attempt before as_of, or None
112
112
    return store.find(ExerciseAttempt,
113
113
            ExerciseAttempt.user_id == user.id,
114
 
            ExerciseAttempt.ws_ex_id == worksheet_exercise.id,
 
114
            ExerciseAttempt.exercise_id == exercise.id,
115
115
            True if allow_inactive else ExerciseAttempt.active == True,
116
116
            True if as_of is None else ExerciseAttempt.date <= as_of,
117
117
        ).order_by(Desc(ExerciseAttempt.date))
118
118
 
119
 
def get_exercise_attempts(store, user, worksheet_exercise, as_of=None,
 
119
def get_exercise_attempts(store, user, exercise, as_of=None,
120
120
        allow_inactive=False):
121
121
    """Given a storm.store, User and Exercise, returns a list of
122
122
    ivle.database.ExerciseAttempt objects, one for each attempt made for the
126
126
        attempts made before or at this time.
127
127
    allow_inactive: If True, will return disabled attempts.
128
128
    """
129
 
    return list(_get_exercise_attempts(store, user, worksheet_exercise, as_of,
 
129
    return list(_get_exercise_attempts(store, user, exercise, as_of,
130
130
        allow_inactive))
131
131
 
132
 
def get_exercise_attempt(store, user, worksheet_exercise, as_of=None,
 
132
def get_exercise_attempt(store, user, exercise, as_of=None,
133
133
        allow_inactive=False):
134
 
    """Given a storm.store, User and WorksheetExercise, returns an
 
134
    """Given a storm.store, User and Exercise, returns an
135
135
    ivle.database.ExerciseAttempt object for the last submitted attempt for
136
136
    this question.
137
137
    Returns None if the user has not made an attempt on this
141
141
        attempts made before or at this time.
142
142
    allow_inactive: If True, will return disabled attempts.
143
143
    """
144
 
    return _get_exercise_attempts(store, user, worksheet_exercise, as_of,
 
144
    return _get_exercise_attempts(store, user, exercise, as_of,
145
145
        allow_inactive).first()
146
146
 
147
 
def save_exercise(store, user, worksheet_exercise, text, date):
 
147
def save_exercise(store, user, exercise, text, date):
148
148
    """Save an exercise for a user.
149
149
 
150
 
    Given a store, User, WorksheetExercise, text and date, save the text to the
 
150
    Given a store, User, Exercise and text and date, save the text to the
151
151
    database. This will create the ExerciseSave if needed.
152
152
    """
153
153
    saved = store.find(ivle.database.ExerciseSave,
154
154
                ivle.database.ExerciseSave.user_id == user.id,
155
 
                ivle.database.ExerciseSave.ws_ex_id == worksheet_exercise.id
156
 
                ).one()
 
155
                ivle.database.ExerciseSave.exercise_id == exercise.id).one()
157
156
    if saved is None:
158
 
        saved = ivle.database.ExerciseSave(user=user, 
159
 
                                        worksheet_exercise=worksheet_exercise)
 
157
        saved = ivle.database.ExerciseSave(user=user, exercise=exercise)
160
158
        store.add(saved)
161
159
 
162
160
    saved.date = date
180
178
    # Get the student's pass/fail for each exercise in this worksheet
181
179
    for worksheet_exercise in worksheet.worksheet_exercises:
182
180
        exercise = worksheet_exercise.exercise
183
 
        worksheet = worksheet_exercise.worksheet
184
181
        optional = worksheet_exercise.optional
185
182
 
186
 
        done, _ = get_exercise_status(store, user, worksheet_exercise)
 
183
        done, _ = get_exercise_status(store, user, exercise)
187
184
        # done is a bool, whether this student has completed that problem
188
185
        if optional:
189
186
            opt_total += 1
193
190
            if done: mand_done += 1
194
191
 
195
192
    return mand_done, mand_total, opt_done, opt_total
196
 
 
197
 
def update_exerciselist(worksheet):
198
 
    """Runs through the worksheetstream, generating the appropriate
199
 
    WorksheetExercises, and de-activating the old ones."""
200
 
    exercises = []
201
 
    # Turns the worksheet into an xml stream, and then finds all the 
202
 
    # exercise nodes in the stream.
203
 
    worksheetdata = genshi.XML(worksheet.get_xml())
204
 
    for kind, data, pos in worksheetdata:
205
 
        if kind is genshi.core.START:
206
 
            # Data is a tuple of tag name and a list of name->value tuples
207
 
            if data[0] == 'exercise':
208
 
                src = ""
209
 
                optional = False
210
 
                for attr in data[1]:
211
 
                    if attr[0] == 'src':
212
 
                        src = attr[1]
213
 
                    if attr[0] == 'optional':
214
 
                        optional = attr[1] == 'true'
215
 
                if src != "":
216
 
                    exercises.append((src, optional))
217
 
    ex_num = 0
218
 
    # Set all current worksheet_exercises to be inactive
219
 
    db_worksheet_exercises = Store.of(worksheet).find(WorksheetExercise,
220
 
        WorksheetExercise.worksheet_id == worksheet.id)
221
 
    for worksheet_exercise in db_worksheet_exercises:
222
 
        worksheet_exercise.active = False
223
 
    
224
 
    for exerciseid, optional in exercises:
225
 
        worksheet_exercise = Store.of(worksheet).find(WorksheetExercise,
226
 
            WorksheetExercise.worksheet_id == worksheet.id,
227
 
            Exercise.id == WorksheetExercise.exercise_id,
228
 
            Exercise.id == exerciseid).one()
229
 
        if worksheet_exercise is None:
230
 
            exercise = Store.of(worksheet).find(Exercise,
231
 
                Exercise.id == exerciseid
232
 
            ).one()
233
 
            if exercise is None:
234
 
                raise NotFound()
235
 
            worksheet_exercise = WorksheetExercise()
236
 
            worksheet_exercise.worksheet_id = worksheet.id
237
 
            worksheet_exercise.exercise_id = exercise.id
238
 
            Store.of(worksheet).add(worksheet_exercise)
239
 
        worksheet_exercise.active = True
240
 
        worksheet_exercise.seq_no = ex_num
241
 
        worksheet_exercise.optional = optional
242