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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: Matt Giuca
  • Date: 2009-03-24 03:14:44 UTC
  • mto: This revision was merged to the branch mainline in revision 1322.
  • Revision ID: matt.giuca@gmail.com-20090324031444-dbpn1bj6dr4zh4sx
Added auto-generated (and hand-modified) Sphinx files (conf, Makefile and
    index).
    Our IVLE documentation will be stored as Sphinx files.
    make html to generate the documentation.

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, IntegrityError
32
33
 
33
34
import ivle.conf
34
 
import ivle.caps
 
35
from ivle.worksheet.rst import rst
35
36
 
36
37
__all__ = ['get_store',
37
38
            'User',
39
40
            'ProjectSet', 'Project', 'ProjectGroup', 'ProjectGroupMembership',
40
41
            'Exercise', 'Worksheet', 'WorksheetExercise',
41
42
            'ExerciseSave', 'ExerciseAttempt',
42
 
            'AlreadyEnrolledError', 'TestCase', 'TestSuite', 'TestSuiteVar'
 
43
            'TestCase', 'TestSuite', 'TestSuiteVar'
43
44
        ]
44
45
 
45
46
def _kwarg_init(self, **kwargs):
87
88
    login = Unicode()
88
89
    passhash = Unicode()
89
90
    state = Unicode()
90
 
    rolenm = Unicode()
 
91
    admin = Bool()
91
92
    unixid = Int()
92
93
    nick = Unicode()
93
94
    pass_exp = DateTime()
99
100
    studentid = Unicode()
100
101
    settings = Unicode()
101
102
 
102
 
    def _get_role(self):
103
 
        if self.rolenm is None:
104
 
            return None
105
 
        return ivle.caps.Role(self.rolenm)
106
 
    def _set_role(self, value):
107
 
        if not isinstance(value, ivle.caps.Role):
108
 
            raise TypeError("role must be an ivle.caps.Role")
109
 
        self.rolenm = unicode(value)
110
 
    role = property(_get_role, _set_role)
111
 
 
112
103
    __init__ = _kwarg_init
113
104
 
114
105
    def __repr__(self):
125
116
            return None
126
117
        return self.hash_password(password) == self.passhash
127
118
 
128
 
    def hasCap(self, capability):
129
 
        """Given a capability (which is a Role object), returns True if this
130
 
        User has that capability, False otherwise.
131
 
        """
132
 
        return self.role.hasCap(capability)
133
 
 
134
119
    @property
135
120
    def password_expired(self):
136
121
        fieldval = self.pass_exp
212
197
        return store.find(cls, cls.login == unicode(login)).one()
213
198
 
214
199
    def get_permissions(self, user):
215
 
        if user and user.rolenm == 'admin' or user is self:
 
200
        if user and user.admin or user is self:
216
201
            return set(['view', 'edit'])
217
202
        else:
218
203
            return set()
239
224
        perms = set()
240
225
        if user is not None:
241
226
            perms.add('view')
242
 
            if user.rolenm == 'admin':
 
227
            if user.admin:
243
228
                perms.add('edit')
244
229
        return perms
245
230
 
249
234
    id = Int(primary=True, name="semesterid")
250
235
    year = Unicode()
251
236
    semester = Unicode()
252
 
    active = Bool()
 
237
    state = Unicode()
253
238
 
254
239
    offerings = ReferenceSet(id, 'Offering.semester_id')
 
240
    enrolments = ReferenceSet(id,
 
241
                              'Offering.semester_id',
 
242
                              'Offering.id',
 
243
                              'Enrolment.offering_id')
255
244
 
256
245
    __init__ = _kwarg_init
257
246
 
275
264
                           'User.id')
276
265
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
277
266
 
278
 
    worksheets = ReferenceSet(id, 'Worksheet.offering_id')
 
267
    worksheets = ReferenceSet(id, 
 
268
        'Worksheet.offering_id', 
 
269
        order_by="seq_no"
 
270
    )
279
271
 
280
272
    __init__ = _kwarg_init
281
273
 
283
275
        return "<%s %r in %r>" % (type(self).__name__, self.subject,
284
276
                                  self.semester)
285
277
 
286
 
    def enrol(self, user):
 
278
    def enrol(self, user, role=u'student'):
287
279
        '''Enrol a user in this offering.'''
288
 
        # We'll get a horrible database constraint violation error if we try
289
 
        # to add a second enrolment.
290
 
        if Store.of(self).find(Enrolment,
291
 
                               Enrolment.user_id == user.id,
292
 
                               Enrolment.offering_id == self.id).count() == 1:
293
 
            raise AlreadyEnrolledError()
294
 
 
295
 
        e = Enrolment(user=user, offering=self, active=True)
296
 
        self.enrolments.add(e)
 
280
        enrolment = Store.of(self).find(Enrolment,
 
281
                               Enrolment.user_id == user.id,
 
282
                               Enrolment.offering_id == self.id).one()
 
283
 
 
284
        if enrolment is None:
 
285
            enrolment = Enrolment(user=user, offering=self)
 
286
            self.enrolments.add(enrolment)
 
287
 
 
288
        enrolment.active = True
 
289
        enrolment.role = role
 
290
 
 
291
    def unenrol(self, user):
 
292
        '''Unenrol a user from this offering.'''
 
293
        enrolment = Store.of(self).find(Enrolment,
 
294
                               Enrolment.user_id == user.id,
 
295
                               Enrolment.offering_id == self.id).one()
 
296
        Store.of(enrolment).remove(enrolment)
297
297
 
298
298
    def get_permissions(self, user):
299
299
        perms = set()
300
300
        if user is not None:
301
 
            perms.add('view')
302
 
            if user.rolenm == 'admin':
 
301
            enrolment = self.get_enrolment(user)
 
302
            if enrolment or user.admin:
 
303
                perms.add('view')
 
304
            if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
 
305
               or user.admin:
303
306
                perms.add('edit')
304
307
        return perms
305
308
 
 
309
    def get_enrolment(self, user):
 
310
        try:
 
311
            enrolment = self.enrolments.find(user=user).one()
 
312
        except NotOneError:
 
313
            enrolment = None
 
314
 
 
315
        return enrolment
 
316
 
306
317
class Enrolment(Storm):
307
318
    __storm_table__ = "enrolment"
308
319
    __storm_primary__ = "user_id", "offering_id"
311
322
    user = Reference(user_id, User.id)
312
323
    offering_id = Int(name="offeringid")
313
324
    offering = Reference(offering_id, Offering.id)
 
325
    role = Unicode()
314
326
    notes = Unicode()
315
327
    active = Bool()
316
328
 
328
340
        return "<%s %r in %r>" % (type(self).__name__, self.user,
329
341
                                  self.offering)
330
342
 
331
 
class AlreadyEnrolledError(Exception):
332
 
    pass
333
 
 
334
343
# PROJECTS #
335
344
 
336
345
class ProjectSet(Storm):
407
416
# WORKSHEETS AND EXERCISES #
408
417
 
409
418
class Exercise(Storm):
410
 
    # Note: Table "problem" is called "Exercise" in the Object layer, since
411
 
    # it's called that everywhere else.
412
 
    __storm_table__ = "problem"
413
 
#TODO: Add in a field for the user-friendly identifier
 
419
    __storm_table__ = "exercise"
414
420
    id = Unicode(primary=True, name="identifier")
415
421
    name = Unicode()
416
422
    description = Unicode()
419
425
    include = Unicode()
420
426
    num_rows = Int()
421
427
 
 
428
    worksheet_exercises =  ReferenceSet(id,
 
429
        'WorksheetExercise.exercise_id')
 
430
 
422
431
    worksheets = ReferenceSet(id,
423
432
        'WorksheetExercise.exercise_id',
424
433
        'WorksheetExercise.worksheet_id',
425
434
        'Worksheet.id'
426
435
    )
427
436
    
428
 
    test_suites = ReferenceSet(id, 'TestSuite.exercise_id')
 
437
    test_suites = ReferenceSet(id, 
 
438
        'TestSuite.exercise_id',
 
439
        order_by='seq_no')
429
440
 
430
441
    __init__ = _kwarg_init
431
442
 
432
443
    def __repr__(self):
433
444
        return "<%s %s>" % (type(self).__name__, self.name)
434
445
 
 
446
    def get_permissions(self, user):
 
447
        perms = set()
 
448
        roles = set()
 
449
        if user is not None:
 
450
            if user.admin:
 
451
                perms.add('edit')
 
452
                perms.add('view')
 
453
            elif 'lecturer' in set((e.role for e in user.active_enrolments)):
 
454
                perms.add('edit')
 
455
                perms.add('view')
 
456
            
 
457
        return perms
 
458
    
 
459
    def get_description(self):
 
460
        return rst(self.description)
 
461
 
 
462
    def delete(self):
 
463
        """Deletes the exercise, providing it has no associated worksheets."""
 
464
        if (self.worksheet_exercises.count() > 0):
 
465
            raise IntegrityError()
 
466
        for suite in self.test_suites:
 
467
            suite.delete()
 
468
        Store.of(self).remove(self)
435
469
 
436
470
class Worksheet(Storm):
437
471
    __storm_table__ = "worksheet"
438
472
 
439
473
    id = Int(primary=True, name="worksheetid")
440
 
    # XXX subject is not linked to a Subject object. This is a property of
441
 
    # the database, and will be refactored.
442
474
    offering_id = Int(name="offeringid")
443
 
    name = Unicode(name="identifier")
 
475
    identifier = Unicode()
 
476
    name = Unicode()
444
477
    assessable = Bool()
445
 
    mtime = DateTime()
 
478
    data = Unicode()
 
479
    seq_no = Int()
 
480
    format = Unicode()
446
481
 
447
482
    attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
448
483
    offering = Reference(offering_id, 'Offering.id')
449
484
 
450
 
    exercises = ReferenceSet(id,
451
 
        'WorksheetExercise.worksheet_id',
452
 
        'WorksheetExercise.exercise_id',
453
 
        Exercise.id)
454
 
    # Use worksheet_exercises to get access to the WorksheetExercise objects
455
 
    # binding worksheets to exercises. This is required to access the
 
485
    all_worksheet_exercises = ReferenceSet(id,
 
486
        'WorksheetExercise.worksheet_id')
 
487
 
 
488
    # Use worksheet_exercises to get access to the *active* WorksheetExercise
 
489
    # objects binding worksheets to exercises. This is required to access the
456
490
    # "optional" field.
457
 
    worksheet_exercises = ReferenceSet(id,
458
 
        'WorksheetExercise.worksheet_id')
459
 
        
 
491
 
 
492
    @property
 
493
    def worksheet_exercises(self):
 
494
        return self.all_worksheet_exercises.find(active=True)
460
495
 
461
496
    __init__ = _kwarg_init
462
497
 
475
510
        return store.find(cls, cls.subject == unicode(subjectname),
476
511
            cls.name == unicode(worksheetname)).one()
477
512
 
478
 
    def remove_all_exercises(self, store):
 
513
    def remove_all_exercises(self):
479
514
        """
480
515
        Remove all exercises from this worksheet.
481
516
        This does not delete the exercises themselves. It just removes them
482
517
        from the worksheet.
483
518
        """
 
519
        store = Store.of(self)
 
520
        for ws_ex in self.all_worksheet_exercises:
 
521
            if ws_ex.saves.count() > 0 or ws_ex.attempts.count() > 0:
 
522
                raise IntegrityError()
484
523
        store.find(WorksheetExercise,
485
524
            WorksheetExercise.worksheet == self).remove()
486
525
            
487
526
    def get_permissions(self, user):
488
527
        return self.offering.get_permissions(user)
489
 
 
 
528
    
 
529
    def get_xml(self):
 
530
        """Returns the xml of this worksheet, converts from rst if required."""
 
531
        if self.format == u'rst':
 
532
            ws_xml = rst(self.data)
 
533
            return ws_xml
 
534
        else:
 
535
            return self.data
 
536
    
 
537
    def delete(self):
 
538
        """Deletes the worksheet, provided it has no attempts on any exercises.
 
539
        
 
540
        Returns True if delete succeeded, or False if this worksheet has
 
541
        attempts attached."""
 
542
        for ws_ex in self.all_worksheet_exercises:
 
543
            if ws_ex.saves.count() > 0 or ws_ex.attempts.count() > 0:
 
544
                raise IntegrityError()
 
545
        
 
546
        self.remove_all_exercises()
 
547
        Store.of(self).remove(self)
 
548
        
490
549
class WorksheetExercise(Storm):
491
 
    __storm_table__ = "worksheet_problem"
492
 
    __storm_primary__ = "worksheet_id", "exercise_id"
 
550
    __storm_table__ = "worksheet_exercise"
 
551
    
 
552
    id = Int(primary=True, name="ws_ex_id")
493
553
 
494
554
    worksheet_id = Int(name="worksheetid")
495
555
    worksheet = Reference(worksheet_id, Worksheet.id)
496
 
    exercise_id = Unicode(name="problemid")
 
556
    exercise_id = Unicode(name="exerciseid")
497
557
    exercise = Reference(exercise_id, Exercise.id)
498
558
    optional = Bool()
 
559
    active = Bool()
 
560
    seq_no = Int()
 
561
    
 
562
    saves = ReferenceSet(id, "ExerciseSave.ws_ex_id")
 
563
    attempts = ReferenceSet(id, "ExerciseAttempt.ws_ex_id")
499
564
 
500
565
    __init__ = _kwarg_init
501
566
 
502
567
    def __repr__(self):
503
568
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
504
 
                                  self.worksheet.name)
 
569
                                  self.worksheet.identifier)
 
570
 
 
571
    def get_permissions(self, user):
 
572
        return self.worksheet.get_permissions(user)
 
573
    
505
574
 
506
575
class ExerciseSave(Storm):
507
576
    """
512
581
    ExerciseSave may be extended with additional semantics (such as
513
582
    ExerciseAttempt).
514
583
    """
515
 
    __storm_table__ = "problem_save"
516
 
    __storm_primary__ = "exercise_id", "user_id", "date"
517
 
 
518
 
    exercise_id = Unicode(name="problemid")
519
 
    exercise = Reference(exercise_id, Exercise.id)
 
584
    __storm_table__ = "exercise_save"
 
585
    __storm_primary__ = "ws_ex_id", "user_id"
 
586
 
 
587
    ws_ex_id = Int(name="ws_ex_id")
 
588
    worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
 
589
 
520
590
    user_id = Int(name="loginid")
521
591
    user = Reference(user_id, User.id)
522
592
    date = DateTime()
523
593
    text = Unicode()
524
 
    worksheetid = Int()
525
 
    worksheet = Reference(worksheetid, Worksheet.id)
526
594
 
527
595
    __init__ = _kwarg_init
528
596
 
543
611
        they won't count (either as a penalty or success), but will still be
544
612
        stored.
545
613
    """
546
 
    __storm_table__ = "problem_attempt"
547
 
    __storm_primary__ = "exercise_id", "user_id", "date"
 
614
    __storm_table__ = "exercise_attempt"
 
615
    __storm_primary__ = "ws_ex_id", "user_id", "date"
548
616
 
549
617
    # The "text" field is the same but has a different name in the DB table
550
618
    # for some reason.
561
629
    __storm_primary__ = "exercise_id", "suiteid"
562
630
    
563
631
    suiteid = Int()
564
 
    exercise_id = Unicode(name="problemid")
 
632
    exercise_id = Unicode(name="exerciseid")
565
633
    description = Unicode()
566
634
    seq_no = Int()
567
635
    function = Unicode()
568
636
    stdin = Unicode()
569
637
    exercise = Reference(exercise_id, Exercise.id)
570
 
    test_cases = ReferenceSet(suiteid, 'TestCase.suiteid')
571
 
    variables = ReferenceSet(suiteid, 'TestSuiteVar.suiteid')
 
638
    test_cases = ReferenceSet(suiteid, 'TestCase.suiteid', order_by="seq_no")
 
639
    variables = ReferenceSet(suiteid, 'TestSuiteVar.suiteid', order_by='arg_no')
 
640
    
 
641
    def delete(self):
 
642
        """Delete this suite, without asking questions."""
 
643
        for vaariable in self.variables:
 
644
            variable.delete()
 
645
        for test_case in self.test_cases:
 
646
            test_case.delete()
 
647
        Store.of(self).remove(self)
572
648
 
573
649
class TestCase(Storm):
574
650
    """A TestCase is a member of a TestSuite.
588
664
    parts = ReferenceSet(testid, "TestCasePart.testid")
589
665
    
590
666
    __init__ = _kwarg_init
 
667
    
 
668
    def delete(self):
 
669
        for part in self.parts:
 
670
            part.delete()
 
671
        Store.of(self).remove(self)
591
672
 
592
673
class TestSuiteVar(Storm):
593
674
    """A container for the arguments of a Test Suite"""
594
 
    __storm_table__ = "suite_variables"
 
675
    __storm_table__ = "suite_variable"
595
676
    __storm_primary__ = "varid"
596
677
    
597
678
    varid = Int()
605
686
    
606
687
    __init__ = _kwarg_init
607
688
    
 
689
    def delete(self):
 
690
        Store.of(self).remove(self)
 
691
    
608
692
class TestCasePart(Storm):
609
693
    """A container for the test elements of a Test Case"""
610
 
    __storm_table__ = "test_case_parts"
 
694
    __storm_table__ = "test_case_part"
611
695
    __storm_primary__ = "partid"
612
696
    
613
697
    partid = Int()
621
705
    test = Reference(testid, "TestCase.testid")
622
706
    
623
707
    __init__ = _kwarg_init
 
708
    
 
709
    def delete(self):
 
710
        Store.of(self).remove(self)