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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: William Grant
  • Date: 2009-02-26 01:52:38 UTC
  • Revision ID: grantw@unimelb.edu.au-20090226015238-t5bhg1wk2npe8uzd
Rework ivle.webapp.admin.subjects#SubjectsView to split offerings nicely by
semester, making the current semester bigger.

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