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

« back to all changes in this revision

Viewing changes to ivle/database.py

Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
getting rather large and messy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
 
30
30
from storm.locals import create_database, Store, Int, Unicode, DateTime, \
31
31
                         Reference, ReferenceSet, Bool, Storm, Desc
32
 
from storm.exceptions import NotOneError
33
32
 
34
33
import ivle.conf
 
34
import ivle.caps
35
35
 
36
36
__all__ = ['get_store',
37
37
            'User',
39
39
            'ProjectSet', 'Project', 'ProjectGroup', 'ProjectGroupMembership',
40
40
            'Exercise', 'Worksheet', 'WorksheetExercise',
41
41
            'ExerciseSave', 'ExerciseAttempt',
42
 
            'TestCase', 'TestSuite', 'TestSuiteVar'
 
42
            'AlreadyEnrolledError'
43
43
        ]
44
44
 
45
45
def _kwarg_init(self, **kwargs):
53
53
    """
54
54
    Returns the Storm connection string, generated from the conf file.
55
55
    """
56
 
 
57
 
    clusterstr = ''
58
 
    if ivle.conf.db_user:
59
 
        clusterstr += ivle.conf.db_user
60
 
        if ivle.conf.db_password:
61
 
            clusterstr += ':' + ivle.conf.db_password
62
 
        clusterstr += '@'
63
 
 
64
 
    host = ivle.conf.db_host or 'localhost'
65
 
    port = ivle.conf.db_port or 5432
66
 
 
67
 
    clusterstr += '%s:%d' % (host, port)
68
 
 
69
 
    return "postgres://%s/%s" % (clusterstr, ivle.conf.db_dbname)
 
56
    return "postgres://%s:%s@%s:%d/%s" % (ivle.conf.db_user,
 
57
        ivle.conf.db_password, ivle.conf.db_host, ivle.conf.db_port,
 
58
        ivle.conf.db_dbname)
70
59
 
71
60
def get_store():
72
61
    """
87
76
    login = Unicode()
88
77
    passhash = Unicode()
89
78
    state = Unicode()
90
 
    admin = Bool()
 
79
    rolenm = Unicode()
91
80
    unixid = Int()
92
81
    nick = Unicode()
93
82
    pass_exp = DateTime()
99
88
    studentid = Unicode()
100
89
    settings = Unicode()
101
90
 
 
91
    def _get_role(self):
 
92
        if self.rolenm is None:
 
93
            return None
 
94
        return ivle.caps.Role(self.rolenm)
 
95
    def _set_role(self, value):
 
96
        if not isinstance(value, ivle.caps.Role):
 
97
            raise TypeError("role must be an ivle.caps.Role")
 
98
        self.rolenm = unicode(value)
 
99
    role = property(_get_role, _set_role)
 
100
 
102
101
    __init__ = _kwarg_init
103
102
 
104
103
    def __repr__(self):
115
114
            return None
116
115
        return self.hash_password(password) == self.passhash
117
116
 
 
117
    def hasCap(self, capability):
 
118
        """Given a capability (which is a Role object), returns True if this
 
119
        User has that capability, False otherwise.
 
120
        """
 
121
        return self.role.hasCap(capability)
 
122
 
118
123
    @property
119
124
    def password_expired(self):
120
125
        fieldval = self.pass_exp
125
130
        fieldval = self.acct_exp
126
131
        return fieldval is not None and datetime.datetime.now() > fieldval
127
132
 
128
 
    @property
129
 
    def valid(self):
130
 
        return self.state == 'enabled' and not self.account_expired
131
 
 
132
133
    def _get_enrolments(self, justactive):
133
134
        return Store.of(self).find(Enrolment,
134
135
            Enrolment.user_id == self.id,
195
196
        """
196
197
        return store.find(cls, cls.login == unicode(login)).one()
197
198
 
198
 
    def get_permissions(self, user):
199
 
        if user and user.admin or user is self:
200
 
            return set(['view', 'edit'])
201
 
        else:
202
 
            return set()
203
 
 
204
199
# SUBJECTS AND ENROLMENTS #
205
200
 
206
201
class Subject(Storm):
219
214
    def __repr__(self):
220
215
        return "<%s '%s'>" % (type(self).__name__, self.short_name)
221
216
 
222
 
    def get_permissions(self, user):
223
 
        perms = set()
224
 
        if user is not None:
225
 
            perms.add('view')
226
 
            if user.admin:
227
 
                perms.add('edit')
228
 
        return perms
229
 
 
230
217
class Semester(Storm):
231
218
    __storm_table__ = "semester"
232
219
 
233
220
    id = Int(primary=True, name="semesterid")
234
221
    year = Unicode()
235
222
    semester = Unicode()
236
 
    state = Unicode()
 
223
    active = Bool()
237
224
 
238
225
    offerings = ReferenceSet(id, 'Offering.semester_id')
239
 
    enrolments = ReferenceSet(id,
240
 
                              'Offering.semester_id',
241
 
                              'Offering.id',
242
 
                              'Enrolment.offering_id')
243
226
 
244
227
    __init__ = _kwarg_init
245
228
 
263
246
                           'User.id')
264
247
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
265
248
 
266
 
    worksheets = ReferenceSet(id, 
267
 
        'Worksheet.offering_id', 
268
 
        order_by="Worksheet.seq_no"
269
 
    )
270
 
 
271
249
    __init__ = _kwarg_init
272
250
 
273
251
    def __repr__(self):
274
252
        return "<%s %r in %r>" % (type(self).__name__, self.subject,
275
253
                                  self.semester)
276
254
 
277
 
    def enrol(self, user, role=u'student'):
 
255
    def enrol(self, user):
278
256
        '''Enrol a user in this offering.'''
279
 
        enrolment = Store.of(self).find(Enrolment,
280
 
                               Enrolment.user_id == user.id,
281
 
                               Enrolment.offering_id == self.id).one()
282
 
 
283
 
        if enrolment is None:
284
 
            enrolment = Enrolment(user=user, offering=self)
285
 
            self.enrolments.add(enrolment)
286
 
 
287
 
        enrolment.active = True
288
 
        enrolment.role = role
289
 
 
290
 
    def unenrol(self, user):
291
 
        '''Unenrol a user from this offering.'''
292
 
        enrolment = Store.of(self).find(Enrolment,
293
 
                               Enrolment.user_id == user.id,
294
 
                               Enrolment.offering_id == self.id).one()
295
 
        Store.of(enrolment).remove(enrolment)
296
 
 
297
 
    def get_permissions(self, user):
298
 
        perms = set()
299
 
        if user is not None:
300
 
            enrolment = self.get_enrolment(user)
301
 
            if enrolment or user.admin:
302
 
                perms.add('view')
303
 
            if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
304
 
               or user.admin:
305
 
                perms.add('edit')
306
 
        return perms
307
 
 
308
 
    def get_enrolment(self, user):
309
 
        try:
310
 
            enrolment = self.enrolments.find(user=user).one()
311
 
        except NotOneError:
312
 
            enrolment = None
313
 
 
314
 
        return enrolment
 
257
        # We'll get a horrible database constraint violation error if we try
 
258
        # to add a second enrolment.
 
259
        if Store.of(self).find(Enrolment,
 
260
                               Enrolment.user_id == user.id,
 
261
                               Enrolment.offering_id == self.id).count() == 1:
 
262
            raise AlreadyEnrolledError()
 
263
 
 
264
        e = Enrolment(user=user, offering=self, active=True)
 
265
        self.enrolments.add(e)
315
266
 
316
267
class Enrolment(Storm):
317
268
    __storm_table__ = "enrolment"
321
272
    user = Reference(user_id, User.id)
322
273
    offering_id = Int(name="offeringid")
323
274
    offering = Reference(offering_id, Offering.id)
324
 
    role = Unicode()
325
275
    notes = Unicode()
326
276
    active = Bool()
327
277
 
339
289
        return "<%s %r in %r>" % (type(self).__name__, self.user,
340
290
                                  self.offering)
341
291
 
 
292
class AlreadyEnrolledError(Exception):
 
293
    pass
 
294
 
342
295
# PROJECTS #
343
296
 
344
297
class ProjectSet(Storm):
415
368
# WORKSHEETS AND EXERCISES #
416
369
 
417
370
class Exercise(Storm):
418
 
    __storm_table__ = "exercise"
419
 
    id = Unicode(primary=True, name="identifier")
420
 
    name = Unicode()
421
 
    description = Unicode()
422
 
    partial = Unicode()
423
 
    solution = Unicode()
424
 
    include = Unicode()
425
 
    num_rows = Int()
 
371
    # Note: Table "problem" is called "Exercise" in the Object layer, since
 
372
    # it's called that everywhere else.
 
373
    __storm_table__ = "problem"
 
374
 
 
375
    id = Int(primary=True, name="problemid")
 
376
    name = Unicode(name="identifier")
 
377
    spec = Unicode()
426
378
 
427
379
    worksheets = ReferenceSet(id,
428
380
        'WorksheetExercise.exercise_id',
429
381
        'WorksheetExercise.worksheet_id',
430
382
        'Worksheet.id'
431
383
    )
432
 
    
433
 
    test_suites = ReferenceSet(id, 'TestSuite.exercise_id')
434
384
 
435
385
    __init__ = _kwarg_init
436
386
 
437
387
    def __repr__(self):
438
388
        return "<%s %s>" % (type(self).__name__, self.name)
439
389
 
440
 
    def get_permissions(self, user):
441
 
        perms = set()
442
 
        if user is not None:
443
 
            if user.admin:
444
 
                perms.add('edit')
445
 
                perms.add('view')
446
 
        return perms
 
390
    @classmethod
 
391
    def get_by_name(cls, store, name):
 
392
        """
 
393
        Get the Exercise from the db associated with a given store and name.
 
394
        If the exercise is not in the database, creates it and inserts it
 
395
        automatically.
 
396
        """
 
397
        ex = store.find(cls, cls.name == unicode(name)).one()
 
398
        if ex is not None:
 
399
            return ex
 
400
        ex = Exercise(name=unicode(name))
 
401
        store.add(ex)
 
402
        store.commit()
 
403
        return ex
447
404
 
448
405
class Worksheet(Storm):
449
406
    __storm_table__ = "worksheet"
450
407
 
451
408
    id = Int(primary=True, name="worksheetid")
452
 
    offering_id = Int(name="offeringid")
453
 
    identifier = Unicode()
454
 
    name = Unicode()
 
409
    # XXX subject is not linked to a Subject object. This is a property of
 
410
    # the database, and will be refactored.
 
411
    subject = Unicode()
 
412
    name = Unicode(name="identifier")
455
413
    assessable = Bool()
456
 
    data = Unicode()
457
 
    seq_no = Int()
458
 
    format = Unicode()
459
 
 
460
 
    attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
461
 
    offering = Reference(offering_id, 'Offering.id')
462
 
 
463
 
    all_worksheet_exercises = ReferenceSet(id,
 
414
    mtime = DateTime()
 
415
 
 
416
    exercises = ReferenceSet(id,
 
417
        'WorksheetExercise.worksheet_id',
 
418
        'WorksheetExercise.exercise_id',
 
419
        Exercise.id)
 
420
    # Use worksheet_exercises to get access to the WorksheetExercise objects
 
421
    # binding worksheets to exercises. This is required to access the
 
422
    # "optional" field.
 
423
    worksheet_exercises = ReferenceSet(id,
464
424
        'WorksheetExercise.worksheet_id')
465
425
 
466
 
    # Use worksheet_exercises to get access to the *active* WorksheetExercise
467
 
    # objects binding worksheets to exercises. This is required to access the
468
 
    # "optional" field.
469
 
    @property
470
 
    def worksheet_exercises(self):
471
 
        return self.all_worksheet_exercises.find(active=True)
472
 
 
473
426
    __init__ = _kwarg_init
474
427
 
475
428
    def __repr__(self):
495
448
        """
496
449
        store.find(WorksheetExercise,
497
450
            WorksheetExercise.worksheet == self).remove()
498
 
            
499
 
    def get_permissions(self, user):
500
 
        return self.offering.get_permissions(user)
501
451
 
502
452
class WorksheetExercise(Storm):
503
 
    __storm_table__ = "worksheet_exercise"
504
 
    
505
 
    id = Int(primary=True, name="ws_ex_id")
 
453
    __storm_table__ = "worksheet_problem"
 
454
    __storm_primary__ = "worksheet_id", "exercise_id"
506
455
 
507
456
    worksheet_id = Int(name="worksheetid")
508
457
    worksheet = Reference(worksheet_id, Worksheet.id)
509
 
    exercise_id = Unicode(name="exerciseid")
 
458
    exercise_id = Int(name="problemid")
510
459
    exercise = Reference(exercise_id, Exercise.id)
511
460
    optional = Bool()
512
 
    active = Bool()
513
 
    seq_no = Int()
514
 
    
515
 
    saves = ReferenceSet(id, "ExerciseSave.ws_ex_id")
516
 
    attempts = ReferenceSet(id, "ExerciseAttempt.ws_ex_id")
517
461
 
518
462
    __init__ = _kwarg_init
519
463
 
520
464
    def __repr__(self):
521
465
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
522
 
                                  self.worksheet.identifier)
523
 
 
524
 
    def get_permissions(self, user):
525
 
        return self.worksheet.get_permissions(user)
 
466
                                  self.worksheet.name)
526
467
 
527
468
class ExerciseSave(Storm):
528
469
    """
533
474
    ExerciseSave may be extended with additional semantics (such as
534
475
    ExerciseAttempt).
535
476
    """
536
 
    __storm_table__ = "exercise_save"
537
 
    __storm_primary__ = "ws_ex_id", "user_id"
538
 
 
539
 
    ws_ex_id = Int(name="ws_ex_id")
540
 
    worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
541
 
 
 
477
    __storm_table__ = "problem_save"
 
478
    __storm_primary__ = "exercise_id", "user_id", "date"
 
479
 
 
480
    exercise_id = Int(name="problemid")
 
481
    exercise = Reference(exercise_id, Exercise.id)
542
482
    user_id = Int(name="loginid")
543
483
    user = Reference(user_id, User.id)
544
484
    date = DateTime()
563
503
        they won't count (either as a penalty or success), but will still be
564
504
        stored.
565
505
    """
566
 
    __storm_table__ = "exercise_attempt"
567
 
    __storm_primary__ = "ws_ex_id", "user_id", "date"
 
506
    __storm_table__ = "problem_attempt"
 
507
    __storm_primary__ = "exercise_id", "user_id", "date"
568
508
 
569
509
    # The "text" field is the same but has a different name in the DB table
570
510
    # for some reason.
571
511
    text = Unicode(name="attempt")
572
512
    complete = Bool()
573
513
    active = Bool()
574
 
    
575
 
    def get_permissions(self, user):
576
 
        return set(['view']) if user is self.user else set()
577
 
  
578
 
class TestSuite(Storm):
579
 
    """A Testsuite acts as a container for the test cases of an exercise."""
580
 
    __storm_table__ = "test_suite"
581
 
    __storm_primary__ = "exercise_id", "suiteid"
582
 
    
583
 
    suiteid = Int()
584
 
    exercise_id = Unicode(name="exerciseid")
585
 
    description = Unicode()
586
 
    seq_no = Int()
587
 
    function = Unicode()
588
 
    stdin = Unicode()
589
 
    exercise = Reference(exercise_id, Exercise.id)
590
 
    test_cases = ReferenceSet(suiteid, 'TestCase.suiteid')
591
 
    variables = ReferenceSet(suiteid, 'TestSuiteVar.suiteid')
592
 
 
593
 
class TestCase(Storm):
594
 
    """A TestCase is a member of a TestSuite.
595
 
    
596
 
    It contains the data necessary to check if an exercise is correct"""
597
 
    __storm_table__ = "test_case"
598
 
    __storm_primary__ = "testid", "suiteid"
599
 
    
600
 
    testid = Int()
601
 
    suiteid = Int()
602
 
    suite = Reference(suiteid, "TestSuite.suiteid")
603
 
    passmsg = Unicode()
604
 
    failmsg = Unicode()
605
 
    test_default = Unicode()
606
 
    seq_no = Int()
607
 
    
608
 
    parts = ReferenceSet(testid, "TestCasePart.testid")
609
 
    
610
 
    __init__ = _kwarg_init
611
 
 
612
 
class TestSuiteVar(Storm):
613
 
    """A container for the arguments of a Test Suite"""
614
 
    __storm_table__ = "suite_variable"
615
 
    __storm_primary__ = "varid"
616
 
    
617
 
    varid = Int()
618
 
    suiteid = Int()
619
 
    var_name = Unicode()
620
 
    var_value = Unicode()
621
 
    var_type = Unicode()
622
 
    arg_no = Int()
623
 
    
624
 
    suite = Reference(suiteid, "TestSuite.suiteid")
625
 
    
626
 
    __init__ = _kwarg_init
627
 
    
628
 
class TestCasePart(Storm):
629
 
    """A container for the test elements of a Test Case"""
630
 
    __storm_table__ = "test_case_part"
631
 
    __storm_primary__ = "partid"
632
 
    
633
 
    partid = Int()
634
 
    testid = Int()
635
 
    
636
 
    part_type = Unicode()
637
 
    test_type = Unicode()
638
 
    data = Unicode()
639
 
    filename = Unicode()
640
 
    
641
 
    test = Reference(testid, "TestCase.testid")
642
 
    
643
 
    __init__ = _kwarg_init