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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: William Grant
  • Date: 2009-03-17 03:43:52 UTC
  • Revision ID: grantw@unimelb.edu.au-20090317034352-tiv0h96wmx39fwh4
Expose jQuery in our URL space.

Adds the concept of external media files, which are static files that we
pull in from third-party dependencies and serve under /+media/+external.

A new config option (media/externals/jquery) has been added to specify
where to find jquery.js - it defaults to /usr/share/javascript/jquery,
which is both a generally sane location and also the Debian/Ubuntu one.

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
32
33
 
33
34
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
 
            'AlreadyEnrolledError'
 
42
            'TestCase', 'TestSuite', 'TestSuiteVar'
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
 
    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)
 
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)
59
70
 
60
71
def get_store():
61
72
    """
76
87
    login = Unicode()
77
88
    passhash = Unicode()
78
89
    state = Unicode()
79
 
    rolenm = Unicode()
 
90
    admin = Bool()
80
91
    unixid = Int()
81
92
    nick = Unicode()
82
93
    pass_exp = DateTime()
88
99
    studentid = Unicode()
89
100
    settings = Unicode()
90
101
 
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
102
    __init__ = _kwarg_init
102
103
 
103
104
    def __repr__(self):
114
115
            return None
115
116
        return self.hash_password(password) == self.passhash
116
117
 
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
118
    @property
124
119
    def password_expired(self):
125
120
        fieldval = self.pass_exp
130
125
        fieldval = self.acct_exp
131
126
        return fieldval is not None and datetime.datetime.now() > fieldval
132
127
 
 
128
    @property
 
129
    def valid(self):
 
130
        return self.state == 'enabled' and not self.account_expired
 
131
 
133
132
    def _get_enrolments(self, justactive):
134
133
        return Store.of(self).find(Enrolment,
135
134
            Enrolment.user_id == self.id,
196
195
        """
197
196
        return store.find(cls, cls.login == unicode(login)).one()
198
197
 
 
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
 
199
204
# SUBJECTS AND ENROLMENTS #
200
205
 
201
206
class Subject(Storm):
214
219
    def __repr__(self):
215
220
        return "<%s '%s'>" % (type(self).__name__, self.short_name)
216
221
 
 
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
 
217
230
class Semester(Storm):
218
231
    __storm_table__ = "semester"
219
232
 
220
233
    id = Int(primary=True, name="semesterid")
221
234
    year = Unicode()
222
235
    semester = Unicode()
223
 
    active = Bool()
 
236
    state = Unicode()
224
237
 
225
238
    offerings = ReferenceSet(id, 'Offering.semester_id')
 
239
    enrolments = ReferenceSet(id,
 
240
                              'Offering.semester_id',
 
241
                              'Offering.id',
 
242
                              'Enrolment.offering_id')
226
243
 
227
244
    __init__ = _kwarg_init
228
245
 
240
257
    groups_student_permissions = Unicode()
241
258
 
242
259
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
 
260
    members = ReferenceSet(id,
 
261
                           'Enrolment.offering_id',
 
262
                           'Enrolment.user_id',
 
263
                           'User.id')
 
264
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
 
265
 
 
266
    worksheets = ReferenceSet(id, 
 
267
        'Worksheet.offering_id', 
 
268
        order_by="Worksheet.seq_no"
 
269
    )
243
270
 
244
271
    __init__ = _kwarg_init
245
272
 
247
274
        return "<%s %r in %r>" % (type(self).__name__, self.subject,
248
275
                                  self.semester)
249
276
 
250
 
    def enrol(self, user):
 
277
    def enrol(self, user, role=u'student'):
251
278
        '''Enrol a user in this offering.'''
252
 
        # We'll get a horrible database constraint violation error if we try
253
 
        # to add a second enrolment.
254
 
        if Store.of(self).find(Enrolment,
255
 
                               Enrolment.user_id == user.id,
256
 
                               Enrolment.offering_id == self.id).count() == 1:
257
 
            raise AlreadyEnrolledError()
258
 
 
259
 
        e = Enrolment(user=user, offering=self, active=True)
260
 
        self.enrolments.add(e)
 
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
261
315
 
262
316
class Enrolment(Storm):
263
317
    __storm_table__ = "enrolment"
267
321
    user = Reference(user_id, User.id)
268
322
    offering_id = Int(name="offeringid")
269
323
    offering = Reference(offering_id, Offering.id)
 
324
    role = Unicode()
270
325
    notes = Unicode()
271
326
    active = Bool()
272
327
 
 
328
    @property
 
329
    def groups(self):
 
330
        return Store.of(self).find(ProjectGroup,
 
331
                ProjectSet.offering_id == self.offering.id,
 
332
                ProjectGroup.project_set_id == ProjectSet.id,
 
333
                ProjectGroupMembership.project_group_id == ProjectGroup.id,
 
334
                ProjectGroupMembership.user_id == self.user.id)
 
335
 
273
336
    __init__ = _kwarg_init
274
337
 
275
338
    def __repr__(self):
276
339
        return "<%s %r in %r>" % (type(self).__name__, self.user,
277
340
                                  self.offering)
278
341
 
279
 
class AlreadyEnrolledError(Exception):
280
 
    pass
281
 
 
282
342
# PROJECTS #
283
343
 
284
344
class ProjectSet(Storm):
289
349
    offering = Reference(offering_id, Offering.id)
290
350
    max_students_per_group = Int()
291
351
 
 
352
    projects = ReferenceSet(id, 'Project.project_set_id')
 
353
    project_groups = ReferenceSet(id, 'ProjectGroup.project_set_id')
 
354
 
292
355
    __init__ = _kwarg_init
293
356
 
294
357
    def __repr__(self):
323
386
    created_by = Reference(created_by_id, User.id)
324
387
    epoch = DateTime()
325
388
 
 
389
    members = ReferenceSet(id,
 
390
                           "ProjectGroupMembership.project_group_id",
 
391
                           "ProjectGroupMembership.user_id",
 
392
                           "User.id")
 
393
 
326
394
    __init__ = _kwarg_init
327
395
 
328
396
    def __repr__(self):
329
397
        return "<%s %s in %r>" % (type(self).__name__, self.name,
330
398
                                  self.project_set.offering)
331
399
 
332
 
    @property
333
 
    def members(self):
334
 
        return Store.of(self).find(User,
335
 
            ProjectGroupMembership.project_group_id == self.id,
336
 
            User.id == ProjectGroupMembership.user_id)
337
 
 
338
400
class ProjectGroupMembership(Storm):
339
401
    __storm_table__ = "group_member"
340
402
    __storm_primary__ = "user_id", "project_group_id"
353
415
# WORKSHEETS AND EXERCISES #
354
416
 
355
417
class Exercise(Storm):
356
 
    # Note: Table "problem" is called "Exercise" in the Object layer, since
357
 
    # it's called that everywhere else.
358
 
    __storm_table__ = "problem"
359
 
 
360
 
    id = Int(primary=True, name="problemid")
361
 
    name = Unicode(name="identifier")
362
 
    spec = Unicode()
 
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()
363
426
 
364
427
    worksheets = ReferenceSet(id,
365
428
        'WorksheetExercise.exercise_id',
366
429
        'WorksheetExercise.worksheet_id',
367
430
        'Worksheet.id'
368
431
    )
 
432
    
 
433
    test_suites = ReferenceSet(id, 'TestSuite.exercise_id')
369
434
 
370
435
    __init__ = _kwarg_init
371
436
 
372
437
    def __repr__(self):
373
438
        return "<%s %s>" % (type(self).__name__, self.name)
374
439
 
375
 
    @classmethod
376
 
    def get_by_name(cls, store, name):
377
 
        """
378
 
        Get the Exercise from the db associated with a given store and name.
379
 
        If the exercise is not in the database, creates it and inserts it
380
 
        automatically.
381
 
        """
382
 
        ex = store.find(cls, cls.name == unicode(name)).one()
383
 
        if ex is not None:
384
 
            return ex
385
 
        ex = Exercise(name=unicode(name))
386
 
        store.add(ex)
387
 
        store.commit()
388
 
        return ex
 
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
389
447
 
390
448
class Worksheet(Storm):
391
449
    __storm_table__ = "worksheet"
392
450
 
393
451
    id = Int(primary=True, name="worksheetid")
394
 
    # XXX subject is not linked to a Subject object. This is a property of
395
 
    # the database, and will be refactored.
396
 
    subject = Unicode()
397
 
    name = Unicode(name="identifier")
 
452
    offering_id = Int(name="offeringid")
 
453
    identifier = Unicode()
 
454
    name = Unicode()
398
455
    assessable = Bool()
399
 
    mtime = DateTime()
400
 
 
401
 
    exercises = ReferenceSet(id,
402
 
        'WorksheetExercise.worksheet_id',
403
 
        'WorksheetExercise.exercise_id',
404
 
        Exercise.id)
405
 
    # Use worksheet_exercises to get access to the WorksheetExercise objects
406
 
    # binding worksheets to exercises. This is required to access the
 
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,
 
464
        'WorksheetExercise.worksheet_id')
 
465
 
 
466
    # Use worksheet_exercises to get access to the *active* WorksheetExercise
 
467
    # objects binding worksheets to exercises. This is required to access the
407
468
    # "optional" field.
408
 
    worksheet_exercises = ReferenceSet(id,
409
 
        'WorksheetExercise.worksheet_id')
 
469
    @property
 
470
    def worksheet_exercises(self):
 
471
        return self.all_worksheet_exercises.find(active=True)
410
472
 
411
473
    __init__ = _kwarg_init
412
474
 
433
495
        """
434
496
        store.find(WorksheetExercise,
435
497
            WorksheetExercise.worksheet == self).remove()
 
498
            
 
499
    def get_permissions(self, user):
 
500
        return self.offering.get_permissions(user)
436
501
 
437
502
class WorksheetExercise(Storm):
438
 
    __storm_table__ = "worksheet_problem"
439
 
    __storm_primary__ = "worksheet_id", "exercise_id"
 
503
    __storm_table__ = "worksheet_exercise"
 
504
    
 
505
    id = Int(primary=True, name="ws_ex_id")
440
506
 
441
507
    worksheet_id = Int(name="worksheetid")
442
508
    worksheet = Reference(worksheet_id, Worksheet.id)
443
 
    exercise_id = Int(name="problemid")
 
509
    exercise_id = Unicode(name="exerciseid")
444
510
    exercise = Reference(exercise_id, Exercise.id)
445
511
    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")
446
517
 
447
518
    __init__ = _kwarg_init
448
519
 
449
520
    def __repr__(self):
450
521
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
451
 
                                  self.worksheet.name)
 
522
                                  self.worksheet.identifier)
 
523
 
 
524
    def get_permissions(self, user):
 
525
        return self.worksheet.get_permissions(user)
452
526
 
453
527
class ExerciseSave(Storm):
454
528
    """
459
533
    ExerciseSave may be extended with additional semantics (such as
460
534
    ExerciseAttempt).
461
535
    """
462
 
    __storm_table__ = "problem_save"
463
 
    __storm_primary__ = "exercise_id", "user_id", "date"
464
 
 
465
 
    exercise_id = Int(name="problemid")
466
 
    exercise = Reference(exercise_id, Exercise.id)
 
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
 
467
542
    user_id = Int(name="loginid")
468
543
    user = Reference(user_id, User.id)
469
544
    date = DateTime()
488
563
        they won't count (either as a penalty or success), but will still be
489
564
        stored.
490
565
    """
491
 
    __storm_table__ = "problem_attempt"
492
 
    __storm_primary__ = "exercise_id", "user_id", "date"
 
566
    __storm_table__ = "exercise_attempt"
 
567
    __storm_primary__ = "ws_ex_id", "user_id", "date"
493
568
 
494
569
    # The "text" field is the same but has a different name in the DB table
495
570
    # for some reason.
496
571
    text = Unicode(name="attempt")
497
572
    complete = Bool()
498
573
    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