~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-25 23:04:11 UTC
  • Revision ID: grantw@unimelb.edu.au-20090225230411-lbdyl32ir0m3d59b
Make all of the services executable.

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'
 
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
130
124
        fieldval = self.acct_exp
131
125
        return fieldval is not None and datetime.datetime.now() > fieldval
132
126
 
 
127
    @property
 
128
    def valid(self):
 
129
        return self.state == 'enabled' and not self.account_expired
 
130
 
133
131
    def _get_enrolments(self, justactive):
134
132
        return Store.of(self).find(Enrolment,
135
133
            Enrolment.user_id == self.id,
196
194
        """
197
195
        return store.find(cls, cls.login == unicode(login)).one()
198
196
 
 
197
    def get_permissions(self, user):
 
198
        if user and user.admin or user is self:
 
199
            return set(['view', 'edit'])
 
200
        else:
 
201
            return set()
 
202
 
199
203
# SUBJECTS AND ENROLMENTS #
200
204
 
201
205
class Subject(Storm):
214
218
    def __repr__(self):
215
219
        return "<%s '%s'>" % (type(self).__name__, self.short_name)
216
220
 
 
221
    def get_permissions(self, user):
 
222
        perms = set()
 
223
        if user is not None:
 
224
            perms.add('view')
 
225
            if user.admin:
 
226
                perms.add('edit')
 
227
        return perms
 
228
 
217
229
class Semester(Storm):
218
230
    __storm_table__ = "semester"
219
231
 
220
232
    id = Int(primary=True, name="semesterid")
221
233
    year = Unicode()
222
234
    semester = Unicode()
223
 
    active = Bool()
 
235
    state = Unicode()
224
236
 
225
237
    offerings = ReferenceSet(id, 'Offering.semester_id')
226
238
 
246
258
                           'User.id')
247
259
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
248
260
 
 
261
    worksheets = ReferenceSet(id, 
 
262
        'Worksheet.offering_id', 
 
263
        order_by="Worksheet.seq_no"
 
264
    )
 
265
 
249
266
    __init__ = _kwarg_init
250
267
 
251
268
    def __repr__(self):
252
269
        return "<%s %r in %r>" % (type(self).__name__, self.subject,
253
270
                                  self.semester)
254
271
 
255
 
    def enrol(self, user):
 
272
    def enrol(self, user, role=u'student'):
256
273
        '''Enrol a user in this offering.'''
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,
 
274
        enrolment = Store.of(self).find(Enrolment,
260
275
                               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)
 
276
                               Enrolment.offering_id == self.id).one()
 
277
 
 
278
        if enrolment is None:
 
279
            enrolment = Enrolment(user=user, offering=self)
 
280
            self.enrolments.add(enrolment)
 
281
 
 
282
        enrolment.active = True
 
283
        enrolment.role = role
 
284
 
 
285
    def get_permissions(self, user):
 
286
        perms = set()
 
287
        if user is not None:
 
288
            perms.add('view')
 
289
            if user.admin:
 
290
                perms.add('edit')
 
291
        return perms
266
292
 
267
293
class Enrolment(Storm):
268
294
    __storm_table__ = "enrolment"
272
298
    user = Reference(user_id, User.id)
273
299
    offering_id = Int(name="offeringid")
274
300
    offering = Reference(offering_id, Offering.id)
 
301
    role = Unicode()
275
302
    notes = Unicode()
276
303
    active = Bool()
277
304
 
289
316
        return "<%s %r in %r>" % (type(self).__name__, self.user,
290
317
                                  self.offering)
291
318
 
292
 
class AlreadyEnrolledError(Exception):
293
 
    pass
294
 
 
295
319
# PROJECTS #
296
320
 
297
321
class ProjectSet(Storm):
368
392
# WORKSHEETS AND EXERCISES #
369
393
 
370
394
class Exercise(Storm):
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()
 
395
    __storm_table__ = "exercise"
 
396
    id = Unicode(primary=True, name="identifier")
 
397
    name = Unicode()
 
398
    description = Unicode()
 
399
    partial = Unicode()
 
400
    solution = Unicode()
 
401
    include = Unicode()
 
402
    num_rows = Int()
378
403
 
379
404
    worksheets = ReferenceSet(id,
380
405
        'WorksheetExercise.exercise_id',
381
406
        'WorksheetExercise.worksheet_id',
382
407
        'Worksheet.id'
383
408
    )
 
409
    
 
410
    test_suites = ReferenceSet(id, 'TestSuite.exercise_id')
384
411
 
385
412
    __init__ = _kwarg_init
386
413
 
387
414
    def __repr__(self):
388
415
        return "<%s %s>" % (type(self).__name__, self.name)
389
416
 
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
 
417
    def get_permissions(self, user):
 
418
        perms = set()
 
419
        if user is not None:
 
420
            if user.admin:
 
421
                perms.add('edit')
 
422
                perms.add('view')
 
423
        return perms
404
424
 
405
425
class Worksheet(Storm):
406
426
    __storm_table__ = "worksheet"
407
427
 
408
428
    id = Int(primary=True, name="worksheetid")
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")
 
429
    offering_id = Int(name="offeringid")
 
430
    identifier = Unicode()
 
431
    name = Unicode()
413
432
    assessable = Bool()
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
 
433
    data = Unicode()
 
434
    seq_no = Int()
 
435
    format = Unicode()
 
436
 
 
437
    attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
 
438
    offering = Reference(offering_id, 'Offering.id')
 
439
 
 
440
    all_worksheet_exercises = ReferenceSet(id,
 
441
        'WorksheetExercise.worksheet_id')
 
442
 
 
443
    # Use worksheet_exercises to get access to the *active* WorksheetExercise
 
444
    # objects binding worksheets to exercises. This is required to access the
422
445
    # "optional" field.
423
 
    worksheet_exercises = ReferenceSet(id,
424
 
        'WorksheetExercise.worksheet_id')
 
446
    @property
 
447
    def worksheet_exercises(self):
 
448
        return self.all_worksheet_exercises.find(active=True)
425
449
 
426
450
    __init__ = _kwarg_init
427
451
 
448
472
        """
449
473
        store.find(WorksheetExercise,
450
474
            WorksheetExercise.worksheet == self).remove()
 
475
            
 
476
    def get_permissions(self, user):
 
477
        return self.offering.get_permissions(user)
451
478
 
452
479
class WorksheetExercise(Storm):
453
 
    __storm_table__ = "worksheet_problem"
454
 
    __storm_primary__ = "worksheet_id", "exercise_id"
 
480
    __storm_table__ = "worksheet_exercise"
 
481
    
 
482
    id = Int(primary=True, name="ws_ex_id")
455
483
 
456
484
    worksheet_id = Int(name="worksheetid")
457
485
    worksheet = Reference(worksheet_id, Worksheet.id)
458
 
    exercise_id = Int(name="problemid")
 
486
    exercise_id = Unicode(name="exerciseid")
459
487
    exercise = Reference(exercise_id, Exercise.id)
460
488
    optional = Bool()
 
489
    active = Bool()
 
490
    seq_no = Int()
 
491
    
 
492
    saves = ReferenceSet(id, "ExerciseSave.ws_ex_id")
 
493
    attempts = ReferenceSet(id, "ExerciseAttempt.ws_ex_id")
461
494
 
462
495
    __init__ = _kwarg_init
463
496
 
464
497
    def __repr__(self):
465
498
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
466
 
                                  self.worksheet.name)
 
499
                                  self.worksheet.identifier)
467
500
 
468
501
class ExerciseSave(Storm):
469
502
    """
474
507
    ExerciseSave may be extended with additional semantics (such as
475
508
    ExerciseAttempt).
476
509
    """
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)
 
510
    __storm_table__ = "exercise_save"
 
511
    __storm_primary__ = "ws_ex_id", "user_id"
 
512
 
 
513
    ws_ex_id = Int(name="ws_ex_id")
 
514
    worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
 
515
 
482
516
    user_id = Int(name="loginid")
483
517
    user = Reference(user_id, User.id)
484
518
    date = DateTime()
503
537
        they won't count (either as a penalty or success), but will still be
504
538
        stored.
505
539
    """
506
 
    __storm_table__ = "problem_attempt"
507
 
    __storm_primary__ = "exercise_id", "user_id", "date"
 
540
    __storm_table__ = "exercise_attempt"
 
541
    __storm_primary__ = "ws_ex_id", "user_id", "date"
508
542
 
509
543
    # The "text" field is the same but has a different name in the DB table
510
544
    # for some reason.
511
545
    text = Unicode(name="attempt")
512
546
    complete = Bool()
513
547
    active = Bool()
 
548
    
 
549
    def get_permissions(self, user):
 
550
        return set(['view']) if user is self.user else set()
 
551
  
 
552
class TestSuite(Storm):
 
553
    """A Testsuite acts as a container for the test cases of an exercise."""
 
554
    __storm_table__ = "test_suite"
 
555
    __storm_primary__ = "exercise_id", "suiteid"
 
556
    
 
557
    suiteid = Int()
 
558
    exercise_id = Unicode(name="exerciseid")
 
559
    description = Unicode()
 
560
    seq_no = Int()
 
561
    function = Unicode()
 
562
    stdin = Unicode()
 
563
    exercise = Reference(exercise_id, Exercise.id)
 
564
    test_cases = ReferenceSet(suiteid, 'TestCase.suiteid')
 
565
    variables = ReferenceSet(suiteid, 'TestSuiteVar.suiteid')
 
566
 
 
567
class TestCase(Storm):
 
568
    """A TestCase is a member of a TestSuite.
 
569
    
 
570
    It contains the data necessary to check if an exercise is correct"""
 
571
    __storm_table__ = "test_case"
 
572
    __storm_primary__ = "testid", "suiteid"
 
573
    
 
574
    testid = Int()
 
575
    suiteid = Int()
 
576
    suite = Reference(suiteid, "TestSuite.suiteid")
 
577
    passmsg = Unicode()
 
578
    failmsg = Unicode()
 
579
    test_default = Unicode()
 
580
    seq_no = Int()
 
581
    
 
582
    parts = ReferenceSet(testid, "TestCasePart.testid")
 
583
    
 
584
    __init__ = _kwarg_init
 
585
 
 
586
class TestSuiteVar(Storm):
 
587
    """A container for the arguments of a Test Suite"""
 
588
    __storm_table__ = "suite_variable"
 
589
    __storm_primary__ = "varid"
 
590
    
 
591
    varid = Int()
 
592
    suiteid = Int()
 
593
    var_name = Unicode()
 
594
    var_value = Unicode()
 
595
    var_type = Unicode()
 
596
    arg_no = Int()
 
597
    
 
598
    suite = Reference(suiteid, "TestSuite.suiteid")
 
599
    
 
600
    __init__ = _kwarg_init
 
601
    
 
602
class TestCasePart(Storm):
 
603
    """A container for the test elements of a Test Case"""
 
604
    __storm_table__ = "test_case_part"
 
605
    __storm_primary__ = "partid"
 
606
    
 
607
    partid = Int()
 
608
    testid = Int()
 
609
    
 
610
    part_type = Unicode()
 
611
    test_type = Unicode()
 
612
    data = Unicode()
 
613
    filename = Unicode()
 
614
    
 
615
    test = Reference(testid, "TestCase.testid")
 
616
    
 
617
    __init__ = _kwarg_init