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
36
36
__all__ = ['get_store',
39
39
'ProjectSet', 'Project', 'ProjectGroup', 'ProjectGroupMembership',
40
40
'Exercise', 'Worksheet', 'WorksheetExercise',
41
41
'ExerciseSave', 'ExerciseAttempt',
42
'AlreadyEnrolledError', 'TestCase', 'TestSuite', 'TestSuiteVar'
42
'TestCase', 'TestSuite', 'TestSuiteVar'
45
45
def _kwarg_init(self, **kwargs):
99
99
studentid = Unicode()
100
100
settings = Unicode()
103
if self.rolenm is 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)
112
102
__init__ = _kwarg_init
114
104
def __repr__(self):
126
116
return self.hash_password(password) == self.passhash
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.
132
return self.role.hasCap(capability)
135
119
def password_expired(self):
136
120
fieldval = self.pass_exp
212
196
return store.find(cls, cls.login == unicode(login)).one()
214
198
def get_permissions(self, user):
215
if user and user.rolenm == 'admin' or user is self:
199
if user and user.admin or user is self:
216
200
return set(['view', 'edit'])
249
233
id = Int(primary=True, name="semesterid")
251
235
semester = Unicode()
254
238
offerings = ReferenceSet(id, 'Offering.semester_id')
239
enrolments = ReferenceSet(id,
240
'Offering.semester_id',
242
'Enrolment.offering_id')
256
244
__init__ = _kwarg_init
286
274
return "<%s %r in %r>" % (type(self).__name__, self.subject,
289
def enrol(self, user):
277
def enrol(self, user, role=u'student'):
290
278
'''Enrol a user in this offering.'''
291
# We'll get a horrible database constraint violation error if we try
292
# to add a second enrolment.
293
if Store.of(self).find(Enrolment,
294
Enrolment.user_id == user.id,
295
Enrolment.offering_id == self.id).count() == 1:
296
raise AlreadyEnrolledError()
298
e = Enrolment(user=user, offering=self, active=True)
299
self.enrolments.add(e)
279
enrolment = Store.of(self).find(Enrolment,
280
Enrolment.user_id == user.id,
281
Enrolment.offering_id == self.id).one()
283
if enrolment is None:
284
enrolment = Enrolment(user=user, offering=self)
285
self.enrolments.add(enrolment)
287
enrolment.active = True
288
enrolment.role = role
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)
301
297
def get_permissions(self, user):
303
299
if user is not None:
305
if user.rolenm == 'admin':
300
enrolment = self.get_enrolment(user)
301
if enrolment or user.admin:
303
if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
306
305
perms.add('edit')
308
def get_enrolment(self, user):
310
enrolment = self.enrolments.find(user=user).one()
309
316
class Enrolment(Storm):
310
317
__storm_table__ = "enrolment"
311
318
__storm_primary__ = "user_id", "offering_id"
410
415
# WORKSHEETS AND EXERCISES #
412
417
class Exercise(Storm):
413
# Note: Table "problem" is called "Exercise" in the Object layer, since
414
# it's called that everywhere else.
415
__storm_table__ = "problem"
418
__storm_table__ = "exercise"
416
419
id = Unicode(primary=True, name="identifier")
418
421
description = Unicode()
450
460
attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
451
461
offering = Reference(offering_id, 'Offering.id')
453
# Use worksheet_exercises to get access to the WorksheetExercise objects
454
# binding worksheets to exercises. This is required to access the
463
all_worksheet_exercises = ReferenceSet(id,
464
'WorksheetExercise.worksheet_id')
466
# Use worksheet_exercises to get access to the *active* WorksheetExercise
467
# objects binding worksheets to exercises. This is required to access the
455
468
# "optional" field.
456
worksheet_exercises = ReferenceSet(id,
457
'WorksheetExercise.worksheet_id')
470
def worksheet_exercises(self):
471
return self.all_worksheet_exercises.find(active=True)
460
473
__init__ = _kwarg_init
487
500
return self.offering.get_permissions(user)
489
502
class WorksheetExercise(Storm):
490
__storm_table__ = "worksheet_problem"
503
__storm_table__ = "worksheet_exercise"
492
id = Int(primary=True, name="ws_prob_id")
505
id = Int(primary=True, name="ws_ex_id")
494
507
worksheet_id = Int(name="worksheetid")
495
508
worksheet = Reference(worksheet_id, Worksheet.id)
496
exercise_id = Unicode(name="problemid")
509
exercise_id = Unicode(name="exerciseid")
497
510
exercise = Reference(exercise_id, Exercise.id)
498
511
optional = Bool()
508
521
return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
509
522
self.worksheet.identifier)
524
def get_permissions(self, user):
525
return self.worksheet.get_permissions(user)
511
527
class ExerciseSave(Storm):
513
529
Represents a potential solution to an exercise that a user has submitted
517
533
ExerciseSave may be extended with additional semantics (such as
518
534
ExerciseAttempt).
520
__storm_table__ = "problem_save"
536
__storm_table__ = "exercise_save"
521
537
__storm_primary__ = "ws_ex_id", "user_id"
523
ws_ex_id = Int(name="ws_prob_id")
539
ws_ex_id = Int(name="ws_ex_id")
524
540
worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
526
542
user_id = Int(name="loginid")
547
563
they won't count (either as a penalty or success), but will still be
550
__storm_table__ = "problem_attempt"
566
__storm_table__ = "exercise_attempt"
551
567
__storm_primary__ = "ws_ex_id", "user_id", "date"
553
569
# The "text" field is the same but has a different name in the DB table
596
612
class TestSuiteVar(Storm):
597
613
"""A container for the arguments of a Test Suite"""
598
__storm_table__ = "suite_variables"
614
__storm_table__ = "suite_variable"
599
615
__storm_primary__ = "varid"
612
628
class TestCasePart(Storm):
613
629
"""A container for the test elements of a Test Case"""
614
__storm_table__ = "test_case_parts"
630
__storm_table__ = "test_case_part"
615
631
__storm_primary__ = "partid"