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

« back to all changes in this revision

Viewing changes to ivle/webapp/tutorial/service.py

ivle.webapp.filesystem.diff: Import BadRequest; it was used.
services/diffservice: Recognise some Subversion errors as 404s a little better.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE - Informatics Virtual Learning Environment
 
2
# Copyright (C) 2007-2008 The University of Melbourne
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 
 
18
# Module: TutorialService
 
19
# Author: Matt Giuca
 
20
# Date:   25/1/2008
 
21
 
 
22
# Provides the AJAX backend for the tutorial application.
 
23
# This allows several actions to be performed on the code the student has
 
24
# typed into one of the exercise boxes.
 
25
 
 
26
# Calling syntax
 
27
# Path must be empty.
 
28
# The arguments determine what is to be done on this file.
 
29
 
 
30
# "action". One of the tutorialservice actions.
 
31
# "exercise" - The path to a exercise file (including the .xml extension),
 
32
#              relative to the subjects base directory.
 
33
# action "save" or "test" (POST only):
 
34
#   "code" - Full text of the student's code being submitted.
 
35
# action "getattempts": No arguments. Returns a list of
 
36
#   {'date': 'formatted_date', 'complete': bool} dicts.
 
37
# action "getattempt":
 
38
#   "date" - Formatted date. Gets most recent attempt before (and including)
 
39
#   that date.
 
40
#   Returns JSON string containing code, or null.
 
41
 
 
42
# Returns a JSON response string indicating the results.
 
43
 
 
44
import os
 
45
import time
 
46
import datetime
 
47
 
 
48
import cjson
 
49
 
 
50
from ivle import util
 
51
from ivle import console
 
52
import ivle.database
 
53
import ivle.worksheet
 
54
import ivle.conf
 
55
import test # XXX: Really .test, not real test.
 
56
 
 
57
from ivle.webapp.base.rest import JSONRESTView
 
58
import ivle.database
 
59
from ivle.webapp.base.rest import named_operation
 
60
 
 
61
# If True, getattempts or getattempt will allow browsing of inactive/disabled
 
62
# attempts. If False, will not allow this.
 
63
HISTORY_ALLOW_INACTIVE = False
 
64
 
 
65
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
 
66
 
 
67
 
 
68
class AttemptsRESTView(JSONRESTView):
 
69
    '''
 
70
    Class to return a list of attempts for a given exercise, or add an Attempt
 
71
    '''
 
72
    def GET(self, req):
 
73
        """Handles a GET Attempts action."""
 
74
        exercise = ivle.database.Exercise.get_by_name(req.store, 
 
75
                                                        self.exercise)
 
76
        user = ivle.database.User.get_by_login(req.store, self.username)
 
77
 
 
78
        attempts = ivle.worksheet.get_exercise_attempts(req.store, user,
 
79
                            exercise, allow_inactive=HISTORY_ALLOW_INACTIVE)
 
80
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
 
81
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
 
82
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
 
83
                for a in attempts]
 
84
        attempts.append(self.exercise)
 
85
        
 
86
        return attempts
 
87
        
 
88
    def PUT(self, req, data):
 
89
        
 
90
        exercisefile = util.open_exercise_file(self.exercise)
 
91
        if exercisefile is None:
 
92
            req.throw_error(req.HTTP_NOT_FOUND,
 
93
                "The exercise was not found.")
 
94
 
 
95
        # Start a console to run the tests on
 
96
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
 
97
        working_dir = os.path.join("/home", req.user.login)
 
98
        cons = console.Console(req.user.unixid, jail_path, working_dir)
 
99
 
 
100
        # Parse the file into a exercise object using the test suite
 
101
        exercise_obj = test.parse_exercise_file(exercisefile, cons)
 
102
        exercisefile.close()
 
103
 
 
104
        # Run the test cases. Get the result back as a JSONable object.
 
105
        # Return it.
 
106
        test_results = exercise_obj.run_tests(code)
 
107
 
 
108
        # Close the console
 
109
        cons.close()
 
110
 
 
111
        # Get the Exercise from the database
 
112
        exercise = ivle.database.Exercise.get_by_name(req.store, exercisesrc)
 
113
 
 
114
        attempt = ivle.database.ExerciseAttempt(user=req.user,
 
115
                                                exercise=exercise,
 
116
                                                date=datetime.datetime.now(),
 
117
                                                complete=test_results['passed'],
 
118
                                                text=unicode(code)) # XXX
 
119
 
 
120
        req.store.add(attempt)
 
121
        req.store.commit()
 
122
        # Query the DB to get an updated score on whether or not this problem
 
123
        # has EVER been completed (may be different from "passed", if it has
 
124
        # been completed before), and the total number of attempts.
 
125
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
 
126
            req.user, exercise)
 
127
        test_results["completed"] = completed
 
128
        test_results["attempts"] = attempts
 
129
 
 
130
        return test_results
 
131
        
 
132
 
 
133
class AttemptRESTView(JSONRESTView):
 
134
    '''
 
135
    View used to extract the data of a specified attempt
 
136
    '''
 
137
    
 
138
    def GET(self, req):
 
139
        # Get an actual date object, rather than a string
 
140
        date = datetime.datetime.strptime(self.date, TIMESTAMP_FORMAT)
 
141
        
 
142
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
143
        attempt = ivle.worksheet.get_exercise_attempt(req.store, req.user,
 
144
            exercise, as_of=date, allow_inactive=HISTORY_ALLOW_INACTIVE)
 
145
        if attempt is not None:
 
146
            attempt = attempt.text
 
147
        # attempt may be None; will write "null"
 
148
        return {'code': attempt}
 
149
        
 
150
class ExerciseRESTView(JSONRESTView):
 
151
    '''
 
152
    Handles a save action. This saves the user's code without executing it.
 
153
    '''
 
154
    @named_operation
 
155
    def save(self, req, user, text):    
 
156
        # Need to open JUST so we know this is a real exercise.
 
157
        # (This avoids users submitting code for bogus exercises).
 
158
        exercisefile = util.open_exercise_file(self.exercise)
 
159
        if exercisefile is None:
 
160
            req.throw_error(req.HTTP_NOT_FOUND,
 
161
                "The exercise was not found.")
 
162
        exercisefile.close()
 
163
 
 
164
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
165
        ivle.worksheet.save_exercise(req.store, req.user, exercise,
 
166
                                     unicode(code), datetime.datetime.now())
 
167
        req.store.commit()
 
168
        return {"result": "ok"}