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

« back to all changes in this revision

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

ivle-makeuser: Moved root-user check earlier to avoid a nonsensical exception
    being thrown.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
'''AJAX backend for the tutorial application.'''
21
21
 
 
22
import os
22
23
import datetime
23
24
 
24
 
import genshi
25
 
from storm.locals import Store
26
 
 
 
25
import ivle.util
 
26
import ivle.console
27
27
import ivle.database
28
 
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
29
 
                          Offering, Subject, Semester, User, WorksheetExercise
30
 
import ivle.worksheet.utils
 
28
import ivle.worksheet
 
29
import ivle.conf
 
30
import ivle.webapp.tutorial.test
 
31
 
31
32
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
32
33
                                   require_permission)
33
34
from ivle.webapp.errors import NotFound
34
35
 
 
36
# If True, getattempts or getattempt will allow browsing of inactive/disabled
 
37
# attempts. If False, will not allow this.
 
38
HISTORY_ALLOW_INACTIVE = False
35
39
 
36
40
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
37
41
 
38
42
 
39
43
class AttemptsRESTView(JSONRESTView):
40
44
    '''REST view of a user's attempts at an exercise.'''
 
45
    def __init__(self, req, subject, worksheet, exercise, username):
 
46
        # TODO: Find exercise within worksheet.
 
47
        self.user = ivle.database.User.get_by_login(req.store, username)
 
48
        if self.user is None:
 
49
            raise NotFound()
 
50
        self.exercise = exercise
 
51
        self.context = self.user # XXX: Not quite right.
41
52
 
42
53
    @require_permission('edit')
43
54
    def GET(self, req):
44
55
        """Handles a GET Attempts action."""
45
 
        attempts = req.store.find(ExerciseAttempt, 
46
 
                ExerciseAttempt.ws_ex_id == self.context.worksheet_exercise.id,
47
 
                ExerciseAttempt.user_id == self.context.user.id)
 
56
        exercise = ivle.database.Exercise.get_by_name(req.store, 
 
57
                                                        self.exercise)
 
58
 
 
59
        attempts = ivle.worksheet.get_exercise_attempts(req.store, self.user,
 
60
                            exercise, allow_inactive=HISTORY_ALLOW_INACTIVE)
48
61
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
49
62
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
50
63
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
56
69
    @require_permission('edit')
57
70
    def PUT(self, req, data):
58
71
        """ Tests the given submission """
59
 
        test_results = ivle.worksheet.utils.test_exercise_submission(
60
 
            req.config, req.user, self.context.worksheet_exercise.exercise,
61
 
            data['code'])
 
72
        exercisefile = ivle.util.open_exercise_file(self.exercise)
 
73
        if exercisefile is None:
 
74
            raise NotFound()
 
75
 
 
76
        # Start a console to run the tests on
 
77
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
 
78
        working_dir = os.path.join("/home", req.user.login)
 
79
        cons = ivle.console.Console(req.user.unixid, jail_path, working_dir)
 
80
 
 
81
        # Parse the file into a exercise object using the test suite
 
82
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
 
83
                                                            exercisefile, cons)
 
84
        exercisefile.close()
 
85
 
 
86
        # Run the test cases. Get the result back as a JSONable object.
 
87
        # Return it.
 
88
        test_results = exercise_obj.run_tests(data['code'])
 
89
 
 
90
        # Close the console
 
91
        cons.close()
 
92
 
 
93
        # Get the Exercise from the database
 
94
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
62
95
 
63
96
        attempt = ivle.database.ExerciseAttempt(user=req.user,
64
 
            worksheet_exercise = self.context.worksheet_exercise,
65
 
            date = datetime.datetime.now(),
66
 
            complete = test_results['passed'],
67
 
            text = unicode(data['code'])
68
 
        )
 
97
                                                exercise=exercise,
 
98
                                                date=datetime.datetime.now(),
 
99
                                                complete=test_results['passed'],
 
100
                                                # XXX
 
101
                                                text=unicode(data['code']))
69
102
 
70
103
        req.store.add(attempt)
71
104
 
72
105
        # Query the DB to get an updated score on whether or not this problem
73
106
        # has EVER been completed (may be different from "passed", if it has
74
107
        # been completed before), and the total number of attempts.
75
 
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
76
 
                req.store, req.user, self.context.worksheet_exercise)
 
108
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
 
109
            req.user, exercise)
77
110
        test_results["completed"] = completed
78
111
        test_results["attempts"] = attempts
79
112
 
83
116
class AttemptRESTView(JSONRESTView):
84
117
    '''REST view of an exercise attempt.'''
85
118
 
 
119
    def __init__(self, req, subject, worksheet, exercise, username, date):
 
120
        # TODO: Find exercise within worksheet.
 
121
        user = ivle.database.User.get_by_login(req.store, username)
 
122
        if user is None:
 
123
            raise NotFound()
 
124
 
 
125
        try:
 
126
            date = datetime.datetime.strptime(date, TIMESTAMP_FORMAT)
 
127
        except ValueError:
 
128
            raise NotFound()
 
129
 
 
130
        exercise = ivle.database.Exercise.get_by_name(req.store, exercise)
 
131
        attempt = ivle.worksheet.get_exercise_attempt(req.store, user,
 
132
            exercise, as_of=date, allow_inactive=HISTORY_ALLOW_INACTIVE)
 
133
 
 
134
        if attempt is None:
 
135
            raise NotFound()
 
136
 
 
137
        self.context = attempt
 
138
 
86
139
    @require_permission('view')
87
140
    def GET(self, req):
88
141
        return {'code': self.context.text}
89
142
 
90
143
 
91
 
class WorksheetExerciseRESTView(JSONRESTView):
92
 
    '''REST view of a worksheet exercise.'''
93
 
 
94
 
    @named_operation('view')
 
144
class ExerciseRESTView(JSONRESTView):
 
145
    '''REST view of an exercise.'''
 
146
 
 
147
    def get_permissions(self, user):
 
148
        # XXX: Do it properly.
 
149
        if user is not None:
 
150
            return set(['save'])
 
151
        else:
 
152
            return set()
 
153
 
 
154
    @named_operation('save')
95
155
    def save(self, req, text):
96
 
        # Find the appropriate WorksheetExercise to save to. If its not found,
97
 
        # the user is submitting against a non-existant worksheet/exercise
98
 
 
99
 
        old_save = req.store.find(ExerciseSave,
100
 
            ExerciseSave.ws_ex_id == self.context.id,
101
 
            ExerciseSave.user == req.user).one()
102
 
        
103
 
        #Overwrite the old, or create a new if there isn't one
104
 
        if old_save is None:
105
 
            new_save = ExerciseSave()
106
 
            req.store.add(new_save)
107
 
        else:
108
 
            new_save = old_save
109
 
        
110
 
        new_save.worksheet_exercise = self.context
111
 
        new_save.user = req.user
112
 
        new_save.text = unicode(text)
113
 
        new_save.date = datetime.datetime.now()
114
 
 
 
156
        # Need to open JUST so we know this is a real exercise.
 
157
        # (This avoids users submitting code for bogus exercises).
 
158
        exercisefile = ivle.util.open_exercise_file(self.exercise)
 
159
        if exercisefile is None:
 
160
            raise NotFound()
 
161
        exercisefile.close()
 
162
 
 
163
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
164
        ivle.worksheet.save_exercise(req.store, req.user, exercise,
 
165
                                     unicode(text), datetime.datetime.now())
115
166
        return {"result": "ok"}
116
 
 
117
 
 
118
 
class WorksheetsRESTView(JSONRESTView):
119
 
    """View used to update and create Worksheets."""
120
 
 
121
 
    @named_operation('edit')
122
 
    def move_up(self, req, worksheetid):
123
 
        """Takes a list of worksheet-seq_no pairs and updates their 
124
 
        corresponding Worksheet objects to match."""
125
 
        
126
 
        worksheet_below = req.store.find(Worksheet,
127
 
            Worksheet.offering_id == self.context.id,
128
 
            Worksheet.identifier == unicode(worksheetid)).one()
129
 
        if worksheet_below is None:
130
 
            raise NotFound('worksheet_below')
131
 
        worksheet_above = req.store.find(Worksheet,
132
 
            Worksheet.offering_id == self.context.id,
133
 
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
134
 
        if worksheet_above is None:
135
 
            raise NotFound('worksheet_above')
136
 
 
137
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
138
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
139
 
        
140
 
        return {'result': 'ok'}
141
 
 
142
 
    @named_operation('edit')
143
 
    def move_down(self, req, worksheetid):
144
 
        """Takes a list of worksheet-seq_no pairs and updates their 
145
 
        corresponding Worksheet objects to match."""
146
 
        
147
 
        worksheet_above = req.store.find(Worksheet,
148
 
            Worksheet.offering_id == self.context.id,
149
 
            Worksheet.identifier == unicode(worksheetid)).one()
150
 
        if worksheet_above is None:
151
 
            raise NotFound('worksheet_below')
152
 
        worksheet_below = req.store.find(Worksheet,
153
 
            Worksheet.offering_id == self.context.id,
154
 
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
155
 
        if worksheet_below is None:
156
 
            raise NotFound('worksheet_above')
157
 
 
158
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
159
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
160
 
        
161
 
        return {'result': 'ok'}