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

« back to all changes in this revision

Viewing changes to ivle/database.py

Update some docs, and remove other obsolete ones.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
                         Reference, ReferenceSet, Bool, Storm, Desc
32
32
 
33
33
import ivle.conf
 
34
import ivle.caps
34
35
 
35
36
__all__ = ['get_store',
36
37
            'User',
38
39
            'ProjectSet', 'Project', 'ProjectGroup', 'ProjectGroupMembership',
39
40
            'Exercise', 'Worksheet', 'WorksheetExercise',
40
41
            'ExerciseSave', 'ExerciseAttempt',
41
 
            'TestCase', 'TestSuite', 'TestSuiteVar'
 
42
            'AlreadyEnrolledError', 'TestCase', 'TestSuite', 'TestSuiteVar'
42
43
        ]
43
44
 
44
45
def _kwarg_init(self, **kwargs):
52
53
    """
53
54
    Returns the Storm connection string, generated from the conf file.
54
55
    """
55
 
 
56
 
    clusterstr = ''
57
 
    if ivle.conf.db_user:
58
 
        clusterstr += ivle.conf.db_user
59
 
        if ivle.conf.db_password:
60
 
            clusterstr += ':' + ivle.conf.db_password
61
 
        clusterstr += '@'
62
 
 
63
 
    host = ivle.conf.db_host or 'localhost'
64
 
    port = ivle.conf.db_port or 5432
65
 
 
66
 
    clusterstr += '%s:%d' % (host, port)
67
 
 
68
 
    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)
69
59
 
70
60
def get_store():
71
61
    """
86
76
    login = Unicode()
87
77
    passhash = Unicode()
88
78
    state = Unicode()
89
 
    admin = Bool()
 
79
    rolenm = Unicode()
90
80
    unixid = Int()
91
81
    nick = Unicode()
92
82
    pass_exp = DateTime()
98
88
    studentid = Unicode()
99
89
    settings = Unicode()
100
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
 
101
101
    __init__ = _kwarg_init
102
102
 
103
103
    def __repr__(self):
114
114
            return None
115
115
        return self.hash_password(password) == self.passhash
116
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
 
117
123
    @property
118
124
    def password_expired(self):
119
125
        fieldval = self.pass_exp
195
201
        return store.find(cls, cls.login == unicode(login)).one()
196
202
 
197
203
    def get_permissions(self, user):
198
 
        if user and user.admin or user is self:
 
204
        if user and user.rolenm == 'admin' or user is self:
199
205
            return set(['view', 'edit'])
200
206
        else:
201
207
            return set()
222
228
        perms = set()
223
229
        if user is not None:
224
230
            perms.add('view')
225
 
            if user.admin:
 
231
            if user.rolenm == 'admin':
226
232
                perms.add('edit')
227
233
        return perms
228
234
 
232
238
    id = Int(primary=True, name="semesterid")
233
239
    year = Unicode()
234
240
    semester = Unicode()
235
 
    state = Unicode()
 
241
    active = Bool()
236
242
 
237
243
    offerings = ReferenceSet(id, 'Offering.semester_id')
238
 
    enrolments = ReferenceSet(id,
239
 
                              'Offering.semester_id',
240
 
                              'Offering.id',
241
 
                              'Enrolment.offering_id')
242
244
 
243
245
    __init__ = _kwarg_init
244
246
 
262
264
                           'User.id')
263
265
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
264
266
 
265
 
    worksheets = ReferenceSet(id, 
266
 
        'Worksheet.offering_id', 
267
 
        order_by="Worksheet.seq_no"
268
 
    )
 
267
    worksheets = ReferenceSet(id, 'Worksheet.offering_id')
269
268
 
270
269
    __init__ = _kwarg_init
271
270
 
273
272
        return "<%s %r in %r>" % (type(self).__name__, self.subject,
274
273
                                  self.semester)
275
274
 
276
 
    def enrol(self, user, role=u'student'):
 
275
    def enrol(self, user):
277
276
        '''Enrol a user in this offering.'''
278
 
        enrolment = Store.of(self).find(Enrolment,
 
277
        # We'll get a horrible database constraint violation error if we try
 
278
        # to add a second enrolment.
 
279
        if Store.of(self).find(Enrolment,
279
280
                               Enrolment.user_id == user.id,
280
 
                               Enrolment.offering_id == self.id).one()
281
 
 
282
 
        if enrolment is None:
283
 
            enrolment = Enrolment(user=user, offering=self)
284
 
            self.enrolments.add(enrolment)
285
 
 
286
 
        enrolment.active = True
287
 
        enrolment.role = role
 
281
                               Enrolment.offering_id == self.id).count() == 1:
 
282
            raise AlreadyEnrolledError()
 
283
 
 
284
        e = Enrolment(user=user, offering=self, active=True)
 
285
        self.enrolments.add(e)
288
286
 
289
287
    def get_permissions(self, user):
290
288
        perms = set()
291
289
        if user is not None:
292
290
            perms.add('view')
293
 
            if user.admin:
 
291
            if user.rolenm == 'admin':
294
292
                perms.add('edit')
295
293
        return perms
296
294
 
302
300
    user = Reference(user_id, User.id)
303
301
    offering_id = Int(name="offeringid")
304
302
    offering = Reference(offering_id, Offering.id)
305
 
    role = Unicode()
306
303
    notes = Unicode()
307
304
    active = Bool()
308
305
 
320
317
        return "<%s %r in %r>" % (type(self).__name__, self.user,
321
318
                                  self.offering)
322
319
 
 
320
class AlreadyEnrolledError(Exception):
 
321
    pass
 
322
 
323
323
# PROJECTS #
324
324
 
325
325
class ProjectSet(Storm):
396
396
# WORKSHEETS AND EXERCISES #
397
397
 
398
398
class Exercise(Storm):
399
 
    __storm_table__ = "exercise"
 
399
    # Note: Table "problem" is called "Exercise" in the Object layer, since
 
400
    # it's called that everywhere else.
 
401
    __storm_table__ = "problem"
 
402
#TODO: Add in a field for the user-friendly identifier
400
403
    id = Unicode(primary=True, name="identifier")
401
404
    name = Unicode()
402
405
    description = Unicode()
418
421
    def __repr__(self):
419
422
        return "<%s %s>" % (type(self).__name__, self.name)
420
423
 
421
 
    def get_permissions(self, user):
422
 
        perms = set()
423
 
        if user is not None:
424
 
            if user.admin:
425
 
                perms.add('edit')
426
 
                perms.add('view')
427
 
        return perms
428
424
 
429
425
class Worksheet(Storm):
430
426
    __storm_table__ = "worksheet"
431
427
 
432
428
    id = Int(primary=True, name="worksheetid")
 
429
    # XXX subject is not linked to a Subject object. This is a property of
 
430
    # the database, and will be refactored.
433
431
    offering_id = Int(name="offeringid")
434
 
    identifier = Unicode()
435
 
    name = Unicode()
 
432
    name = Unicode(name="identifier")
436
433
    assessable = Bool()
437
 
    data = Unicode()
438
 
    seq_no = Int()
439
 
    format = Unicode()
 
434
    mtime = DateTime()
440
435
 
441
436
    attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
442
437
    offering = Reference(offering_id, 'Offering.id')
443
438
 
444
 
    all_worksheet_exercises = ReferenceSet(id,
 
439
    exercises = ReferenceSet(id,
 
440
        'WorksheetExercise.worksheet_id',
 
441
        'WorksheetExercise.exercise_id',
 
442
        Exercise.id)
 
443
    # Use worksheet_exercises to get access to the WorksheetExercise objects
 
444
    # binding worksheets to exercises. This is required to access the
 
445
    # "optional" field.
 
446
    worksheet_exercises = ReferenceSet(id,
445
447
        'WorksheetExercise.worksheet_id')
446
 
 
447
 
    # Use worksheet_exercises to get access to the *active* WorksheetExercise
448
 
    # objects binding worksheets to exercises. This is required to access the
449
 
    # "optional" field.
450
 
    @property
451
 
    def worksheet_exercises(self):
452
 
        return self.all_worksheet_exercises.find(active=True)
 
448
        
453
449
 
454
450
    __init__ = _kwarg_init
455
451
 
481
477
        return self.offering.get_permissions(user)
482
478
 
483
479
class WorksheetExercise(Storm):
484
 
    __storm_table__ = "worksheet_exercise"
485
 
    
486
 
    id = Int(primary=True, name="ws_ex_id")
 
480
    __storm_table__ = "worksheet_problem"
 
481
    __storm_primary__ = "worksheet_id", "exercise_id"
487
482
 
488
483
    worksheet_id = Int(name="worksheetid")
489
484
    worksheet = Reference(worksheet_id, Worksheet.id)
490
 
    exercise_id = Unicode(name="exerciseid")
 
485
    exercise_id = Unicode(name="problemid")
491
486
    exercise = Reference(exercise_id, Exercise.id)
492
487
    optional = Bool()
493
 
    active = Bool()
494
 
    seq_no = Int()
495
 
    
496
 
    saves = ReferenceSet(id, "ExerciseSave.ws_ex_id")
497
 
    attempts = ReferenceSet(id, "ExerciseAttempt.ws_ex_id")
498
488
 
499
489
    __init__ = _kwarg_init
500
490
 
501
491
    def __repr__(self):
502
492
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
503
 
                                  self.worksheet.identifier)
 
493
                                  self.worksheet.name)
504
494
 
505
495
class ExerciseSave(Storm):
506
496
    """
511
501
    ExerciseSave may be extended with additional semantics (such as
512
502
    ExerciseAttempt).
513
503
    """
514
 
    __storm_table__ = "exercise_save"
515
 
    __storm_primary__ = "ws_ex_id", "user_id"
516
 
 
517
 
    ws_ex_id = Int(name="ws_ex_id")
518
 
    worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
519
 
 
 
504
    __storm_table__ = "problem_save"
 
505
    __storm_primary__ = "exercise_id", "user_id", "date"
 
506
 
 
507
    exercise_id = Unicode(name="problemid")
 
508
    exercise = Reference(exercise_id, Exercise.id)
520
509
    user_id = Int(name="loginid")
521
510
    user = Reference(user_id, User.id)
522
511
    date = DateTime()
523
512
    text = Unicode()
 
513
    worksheetid = Int()
 
514
    worksheet = Reference(worksheetid, Worksheet.id)
524
515
 
525
516
    __init__ = _kwarg_init
526
517
 
541
532
        they won't count (either as a penalty or success), but will still be
542
533
        stored.
543
534
    """
544
 
    __storm_table__ = "exercise_attempt"
545
 
    __storm_primary__ = "ws_ex_id", "user_id", "date"
 
535
    __storm_table__ = "problem_attempt"
 
536
    __storm_primary__ = "exercise_id", "user_id", "date"
546
537
 
547
538
    # The "text" field is the same but has a different name in the DB table
548
539
    # for some reason.
559
550
    __storm_primary__ = "exercise_id", "suiteid"
560
551
    
561
552
    suiteid = Int()
562
 
    exercise_id = Unicode(name="exerciseid")
 
553
    exercise_id = Unicode(name="problemid")
563
554
    description = Unicode()
564
555
    seq_no = Int()
565
556
    function = Unicode()
589
580
 
590
581
class TestSuiteVar(Storm):
591
582
    """A container for the arguments of a Test Suite"""
592
 
    __storm_table__ = "suite_variable"
 
583
    __storm_table__ = "suite_variables"
593
584
    __storm_primary__ = "varid"
594
585
    
595
586
    varid = Int()
605
596
    
606
597
class TestCasePart(Storm):
607
598
    """A container for the test elements of a Test Case"""
608
 
    __storm_table__ = "test_case_part"
 
599
    __storm_table__ = "test_case_parts"
609
600
    __storm_primary__ = "partid"
610
601
    
611
602
    partid = Int()