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

« back to all changes in this revision

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

Port ivle.webapp.admin.user's media to the new framework.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
# Path must be empty.
28
28
# The arguments determine what is to be done on this file.
29
29
 
 
30
# "action". One of the tutorialservice actions.
30
31
# "exercise" - The path to a exercise file (including the .xml extension),
31
 
#    relative to the subjects base directory.
32
 
# "code" - Full text of the student's code being submitted.
33
 
# "action". May be "test". (More to come).
 
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.
34
41
 
35
42
# Returns a JSON response string indicating the results.
36
43
 
37
44
import os
38
45
import time
 
46
import datetime
39
47
 
40
48
import cjson
41
49
 
42
 
from common import (db, util)
43
 
import test
44
 
import conf
45
 
 
46
 
def handle(req):
47
 
    """Handler for Ajax backend TutorialService app."""
48
 
    # Set request attributes
49
 
    req.write_html_head_foot = False     # No HTML
50
 
 
51
 
    if req.path != "":
52
 
        req.throw_error(req.HTTP_BAD_REQUEST)
53
 
    # Get all the arguments, if POST.
54
 
    # Ignore arguments if not POST, since we aren't allowed to cause
55
 
    # side-effects on the server.
56
 
    fields = req.get_fieldstorage()
57
 
    act = fields.getfirst('action')
58
 
    exercise = fields.getfirst('exercise')
59
 
    code = fields.getfirst('code')
60
 
 
61
 
    if exercise == None or code == None or act == None:
62
 
        req.throw_error(req.HTTP_BAD_REQUEST)
63
 
    act = act.value
64
 
    exercise = exercise.value
65
 
    code = code.value
66
 
 
67
 
    if act == "save":
68
 
        handle_save(req, exercise, code, fields)
69
 
    elif act == "test":
70
 
        handle_test(req, exercise, code, fields)
71
 
    elif act == "run":
72
 
        handle_run(req, exercise, code, fields)
73
 
    else:
74
 
        req.throw_error(req.HTTP_BAD_REQUEST)
75
 
 
76
 
def handle_save(req, exercise, code, fields):
77
 
    """Handles a save action. This saves the user's code without executing it.
78
 
    """
79
 
    # Need to open JUST so we know this is a real exercise.
80
 
    # (This avoids users submitting code for bogus exercises).
81
 
    exercisefile = util.open_exercise_file(exercise)
82
 
    if exercisefile is None:
83
 
        req.throw_error(req.HTTP_NOT_FOUND,
84
 
            "The exercise was not found.")
85
 
    exercisefile.close()
86
 
 
87
 
    req.write('{"result": "ok"}')
88
 
 
89
 
    conn = db.DB()
90
 
 
91
 
    try:
92
 
        conn.write_problem_save(
93
 
            login = req.user.login,
94
 
            exercisename = exercise,
95
 
            date = time.localtime(),
96
 
            text = code)
97
 
    finally:
98
 
        conn.close()
99
 
 
100
 
def handle_test(req, exercise, code, fields):
101
 
    """Handles a test action."""
102
 
 
103
 
    exercisefile = util.open_exercise_file(exercise)
104
 
    if exercisefile is None:
105
 
        req.throw_error(req.HTTP_NOT_FOUND,
106
 
            "The exercise was not found.")
107
 
 
108
 
    # Parse the file into a exercise object using the test suite
109
 
    exercise_obj = test.parse_exercise_file(exercisefile)
110
 
    exercisefile.close()
111
 
    # Run the test cases. Get the result back as a JSONable object.
112
 
    # Return it.
113
 
    test_results = exercise_obj.run_tests(code)
114
 
 
115
 
    conn = db.DB()
116
 
    try:
117
 
        conn.insert_problem_attempt(
118
 
            login = req.user.login,
119
 
            exercisename = exercise,
120
 
            date = time.localtime(),
121
 
            complete = test_results['passed'],
122
 
            attempt = code)
123
 
 
 
50
from ivle import util
 
51
from ivle import console
 
52
import ivle.database
 
53
import ivle.worksheet
 
54
import ivle.conf
 
55
import ivle.webapp.tutorial.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
                
 
85
        return attempts
 
86
        
 
87
    def PUT(self, req, data):
 
88
        ''' Tests the given submission '''
 
89
        exercisefile = util.open_exercise_file(self.exercise)
 
90
        if exercisefile is None:
 
91
            req.throw_error(req.HTTP_NOT_FOUND,
 
92
                "The exercise was not found.")
 
93
 
 
94
        # Start a console to run the tests on
 
95
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
 
96
        working_dir = os.path.join("/home", req.user.login)
 
97
        cons = console.Console(req.user.unixid, jail_path, working_dir)
 
98
 
 
99
        # Parse the file into a exercise object using the test suite
 
100
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
 
101
                                                            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(data['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, self.exercise)
 
113
 
 
114
        attempt = ivle.database.ExerciseAttempt(user=req.user,
 
115
                                                exercise=exercise,
 
116
                                                date=datetime.datetime.now(),
 
117
                                                complete=test_results['passed'],
 
118
                                                # XXX
 
119
                                                text=unicode(data['code']))
 
120
 
 
121
        req.store.add(attempt)
 
122
        req.store.commit()
124
123
        # Query the DB to get an updated score on whether or not this problem
125
124
        # has EVER been completed (may be different from "passed", if it has
126
125
        # been completed before), and the total number of attempts.
127
 
        completed, attempts = conn.get_problem_status(req.user.login,
128
 
            exercise)
 
126
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
 
127
            req.user, exercise)
129
128
        test_results["completed"] = completed
130
129
        test_results["attempts"] = attempts
131
130
 
132
 
        req.write(cjson.encode(test_results))
133
 
    finally:
134
 
        conn.close()
135
 
 
136
 
def handle_run(req, exercise, code, fields):
137
 
    """Handles a run action."""
138
 
    # Extremely makeshift.
139
 
    # For now, just echo the code back
140
 
    output = code
141
 
    out_json = {"stdout": output}
142
 
    req.write(cjson.encode(out_json))
 
131
        return test_results
 
132
        
 
133
 
 
134
class AttemptRESTView(JSONRESTView):
 
135
    '''
 
136
    View used to extract the data of a specified attempt
 
137
    '''
 
138
    
 
139
    def GET(self, req):
 
140
        # Get an actual date object, rather than a string
 
141
        date = datetime.datetime.strptime(self.date, TIMESTAMP_FORMAT)
 
142
        
 
143
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
144
        attempt = ivle.worksheet.get_exercise_attempt(req.store, req.user,
 
145
            exercise, as_of=date, allow_inactive=HISTORY_ALLOW_INACTIVE)
 
146
        if attempt is not None:
 
147
            attempt = attempt.text
 
148
        # attempt may be None; will write "null"
 
149
        return {'code': attempt}
 
150
        
 
151
class ExerciseRESTView(JSONRESTView):
 
152
    '''
 
153
    Handles a save action. This saves the user's code without executing it.
 
154
    '''
 
155
    @named_operation
 
156
    def save(self, req, text):
 
157
        # Need to open JUST so we know this is a real exercise.
 
158
        # (This avoids users submitting code for bogus exercises).
 
159
        exercisefile = util.open_exercise_file(self.exercise)
 
160
        if exercisefile is None:
 
161
            req.throw_error(req.HTTP_NOT_FOUND,
 
162
                "The exercise was not found.")
 
163
        exercisefile.close()
 
164
 
 
165
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
166
        ivle.worksheet.save_exercise(req.store, req.user, exercise,
 
167
                                     unicode(text), datetime.datetime.now())
 
168
        req.store.commit()
 
169
        return {"result": "ok"}