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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# IVLE - Informatics Virtual Learning Environment
# Copyright (C) 2007-2009 The University of Melbourne
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

# Author: Matt Giuca

"""
Worksheet Utility Functions

This module provides functions for tutorial and worksheet computations.
"""

from storm.locals import And, Asc, Desc
import ivle.database

__all__ = ['get_exercise_status', 'get_exercise_stored_text',
           'get_exercise_attempts', 'get_exercise_attempt',
          ]

def get_exercise_status(store, user, exercise):
    """Given a storm.store, User and Exercise, returns information about
    the user's performance on that problem.
    Returns a tuple of:
        - A boolean, whether they have successfully passed this exercise.
        - An int, the number of attempts they have made up to and
          including the first successful attempt (or the total number of
          attempts, if not yet successful).
    """
    ExerciseAttempt = ivle.database.ExerciseAttempt
    # A Storm expression denoting all active attempts by this user for this
    # exercise.
    is_relevant = ((ExerciseAttempt.user_id == user.id) &
                   (ExerciseAttempt.exercise_id == exercise.id) &
                   (ExerciseAttempt.active == True))

    # Get the first successful active attempt, or None if no success yet.
    # (For this user, for this exercise).
    first_success = store.find(ExerciseAttempt, is_relevant,
            ExerciseAttempt.complete == True
        ).order_by(Asc(ExerciseAttempt.date)).first()

    if first_success is not None:
        # Get the total number of active attempts up to and including the
        # first successful attempt.
        # (Subsequent attempts don't count, because the user had already
        # succeeded by then).
        num_attempts = store.find(ExerciseAttempt, is_relevant,
                ExerciseAttempt.date <= first_success.date).count()
    else:
        # User has not yet succeeded.
        # Get the total number of active attempts.
        num_attempts = store.find(ExerciseAttempt, is_relevant).count()

    return first_success is not None, num_attempts

def get_exercise_stored_text(store, user, exercise):
    """Given a storm.store, User and Exercise, returns an
    ivle.database.ExerciseSave object for the last saved/submitted attempt for
    this question (note that ExerciseAttempt is a subclass of ExerciseSave).
    Returns None if the user has not saved or made an attempt on this
    problem.
    If the user has both saved and submitted, it returns whichever was
    made last.
    """
    ExerciseSave = ivle.database.ExerciseSave
    ExerciseAttempt = ivle.database.ExerciseAttempt

    # Get the saved text, or None
    saved = store.find(ExerciseSave,
                ExerciseSave.user_id == user.id,
                ExerciseSave.exercise_id == exercise.id).one()

    # Get the most recent attempt, or None
    attempt = store.find(ExerciseAttempt,
            ExerciseAttempt.user_id == user.id,
            ExerciseAttempt.exercise_id == exercise.id,
            ExerciseAttempt.active == True,
        ).order_by(Asc(ExerciseAttempt.date)).last()

    # Pick the most recent of these two
    if saved is not None:
        if attempt is not None:
            return saved if saved.date > attempt.date else attempt
        else:
            return saved
    else:
        if attempt is not None:
            return attempt
        else:
            return None

def _get_exercise_attempts(store, user, exercise, as_of=None,
        allow_inactive=False):
    """Same as get_exercise_attempts, but doesn't convert Storm's iterator
    into a list."""
    ExerciseAttempt = ivle.database.ExerciseAttempt

    # Get the most recent attempt before as_of, or None
    return store.find(ExerciseAttempt,
            ExerciseAttempt.user_id == user.id,
            ExerciseAttempt.exercise_id == exercise.id,
            True if allow_inactive else ExerciseAttempt.active == True,
            True if as_of is None else ExerciseAttempt.date <= as_of,
        ).order_by(Desc(ExerciseAttempt.date))

def get_exercise_attempts(store, user, exercise, as_of=None,
        allow_inactive=False):
    """Given a storm.store, User and Exercise, returns a list of
    ivle.database.ExerciseAttempt objects, one for each attempt made for the
    exercise, sorted from latest to earliest.

    as_of: Optional datetime.datetime object. If supplied, only returns
        attempts made before or at this time.
    allow_inactive: If True, will return disabled attempts.
    """
    return list(_get_exercise_attempts(store, user, exercise, as_of,
        allow_inactive))

def get_exercise_attempt(store, user, exercise, as_of=None,
        allow_inactive=False):
    """Given a storm.store, User and Exercise, returns an
    ivle.database.ExerciseAttempt object for the last submitted attempt for
    this question.
    Returns None if the user has not made an attempt on this
    problem.

    as_of: Optional datetime.datetime object. If supplied, only returns
        attempts made before or at this time.
    allow_inactive: If True, will return disabled attempts.
    """
    return _get_exercise_attempts(store, user, exercise, as_of,
        allow_inactive).first()

def save_exercise(store, user, exercise, text, date):
    """Save an exercise for a user.

    Given a store, User, Exercise and text and date, save the text to the
    database. This will create the ExerciseSave if needed.
    """
    saved = store.find(ivle.database.ExerciseSave,
                ivle.database.ExerciseSave.user_id == user.id,
                ivle.database.ExerciseSave.exercise_id == exercise.id).one()
    if saved is None:
        saved = ivle.database.ExerciseSave(user=user, exercise=exercise)
        store.add(saved)

    saved.date = date
    saved.text = text

def calculate_score(store, user, worksheet):
    """
    Given a storm.store, User, Exercise and Worksheet, calculates a score for
    the user on the given worksheet.
    Returns a 4-tuple of ints, consisting of:
    (No. mandatory exercises completed,
     Total no. mandatory exercises,
     No. optional exercises completed,
     Total no. optional exercises)
    """
    mand_done = 0
    mand_total = 0
    opt_done = 0
    opt_total = 0

    # Get the student's pass/fail for each exercise in this worksheet
    for worksheet_exercise in worksheet.worksheet_exercises:
        exercise = worksheet_exercise.exercise
        optional = worksheet_exercise.optional

        done, _ = get_exercise_status(store, user, exercise)
        # done is a bool, whether this student has completed that problem
        if optional:
            opt_total += 1
            if done: opt_done += 1
        else:
            mand_total += 1
            if done: mand_done += 1

    return mand_done, mand_total, opt_done, opt_total