39
39
'ProjectSet', 'Project', 'ProjectGroup', 'ProjectGroupMembership',
40
40
'Exercise', 'Worksheet', 'WorksheetExercise',
41
41
'ExerciseSave', 'ExerciseAttempt',
42
'TestCase', 'TestSuite', 'TestSuiteVar'
42
'AlreadyEnrolledError'
45
45
def _kwarg_init(self, **kwargs):
54
54
Returns the Storm connection string, generated from the conf file.
59
clusterstr += ivle.conf.db_user
60
if ivle.conf.db_password:
61
clusterstr += ':' + ivle.conf.db_password
64
host = ivle.conf.db_host or 'localhost'
65
port = ivle.conf.db_port or 5432
67
clusterstr += '%s:%d' % (host, port)
69
return "postgres://%s/%s" % (clusterstr, ivle.conf.db_dbname)
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,
99
88
studentid = Unicode()
100
89
settings = Unicode()
92
if self.rolenm is 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)
102
101
__init__ = _kwarg_init
104
103
def __repr__(self):
116
115
return self.hash_password(password) == self.passhash
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.
121
return self.role.hasCap(capability)
119
124
def password_expired(self):
120
125
fieldval = self.pass_exp
219
214
def __repr__(self):
220
215
return "<%s '%s'>" % (type(self).__name__, self.short_name)
222
def get_permissions(self, user):
230
217
class Semester(Storm):
231
218
__storm_table__ = "semester"
233
220
id = Int(primary=True, name="semesterid")
235
222
semester = Unicode()
238
225
offerings = ReferenceSet(id, 'Offering.semester_id')
260
247
project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
262
worksheets = ReferenceSet(id,
263
'Worksheet.offering_id',
267
249
__init__ = _kwarg_init
269
251
def __repr__(self):
270
252
return "<%s %r in %r>" % (type(self).__name__, self.subject,
273
def enrol(self, user, role=u'student'):
255
def enrol(self, user):
274
256
'''Enrol a user in this offering.'''
275
enrolment = Store.of(self).find(Enrolment,
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,
276
260
Enrolment.user_id == user.id,
277
Enrolment.offering_id == self.id).one()
279
if enrolment is None:
280
enrolment = Enrolment(user=user, offering=self)
281
self.enrolments.add(enrolment)
283
enrolment.active = True
284
enrolment.role = role
286
def get_permissions(self, user):
261
Enrolment.offering_id == self.id).count() == 1:
262
raise AlreadyEnrolledError()
264
e = Enrolment(user=user, offering=self, active=True)
265
self.enrolments.add(e)
294
267
class Enrolment(Storm):
295
268
__storm_table__ = "enrolment"
393
368
# WORKSHEETS AND EXERCISES #
395
370
class Exercise(Storm):
396
__storm_table__ = "exercise"
397
id = Unicode(primary=True, name="identifier")
399
description = Unicode()
371
# Note: Table "problem" is called "Exercise" in the Object layer, since
372
# it's called that everywhere else.
373
__storm_table__ = "problem"
375
id = Int(primary=True, name="problemid")
376
name = Unicode(name="identifier")
405
379
worksheets = ReferenceSet(id,
406
380
'WorksheetExercise.exercise_id',
407
381
'WorksheetExercise.worksheet_id',
411
test_suites = ReferenceSet(id,
412
'TestSuite.exercise_id',
415
385
__init__ = _kwarg_init
417
387
def __repr__(self):
418
388
return "<%s %s>" % (type(self).__name__, self.name)
420
def get_permissions(self, user):
391
def get_by_name(cls, store, name):
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
397
ex = store.find(cls, cls.name == unicode(name)).one()
400
ex = Exercise(name=unicode(name))
428
405
class Worksheet(Storm):
429
406
__storm_table__ = "worksheet"
431
408
id = Int(primary=True, name="worksheetid")
432
offering_id = Int(name="offeringid")
433
identifier = Unicode()
409
# XXX subject is not linked to a Subject object. This is a property of
410
# the database, and will be refactored.
412
name = Unicode(name="identifier")
435
413
assessable = Bool()
440
attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
441
offering = Reference(offering_id, 'Offering.id')
443
all_worksheet_exercises = ReferenceSet(id,
416
exercises = ReferenceSet(id,
417
'WorksheetExercise.worksheet_id',
418
'WorksheetExercise.exercise_id',
420
# Use worksheet_exercises to get access to the WorksheetExercise objects
421
# binding worksheets to exercises. This is required to access the
423
worksheet_exercises = ReferenceSet(id,
444
424
'WorksheetExercise.worksheet_id')
446
# Use worksheet_exercises to get access to the *active* WorksheetExercise
447
# objects binding worksheets to exercises. This is required to access the
451
def worksheet_exercises(self):
452
return self.all_worksheet_exercises.find(active=True)
454
426
__init__ = _kwarg_init
456
428
def __repr__(self):
477
449
store.find(WorksheetExercise,
478
450
WorksheetExercise.worksheet == self).remove()
480
def get_permissions(self, user):
481
return self.offering.get_permissions(user)
484
"""Returns the xml of this worksheet, converts from rst if required."""
485
if self.format == u'rst':
486
ws_xml = '<worksheet>' + rst(self.data) + '</worksheet>'
491
452
class WorksheetExercise(Storm):
492
__storm_table__ = "worksheet_exercise"
494
id = Int(primary=True, name="ws_ex_id")
453
__storm_table__ = "worksheet_problem"
454
__storm_primary__ = "worksheet_id", "exercise_id"
496
456
worksheet_id = Int(name="worksheetid")
497
457
worksheet = Reference(worksheet_id, Worksheet.id)
498
exercise_id = Unicode(name="exerciseid")
458
exercise_id = Int(name="problemid")
499
459
exercise = Reference(exercise_id, Exercise.id)
500
460
optional = Bool()
504
saves = ReferenceSet(id, "ExerciseSave.ws_ex_id")
505
attempts = ReferenceSet(id, "ExerciseAttempt.ws_ex_id")
507
462
__init__ = _kwarg_init
509
464
def __repr__(self):
510
465
return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
511
self.worksheet.identifier)
513
468
class ExerciseSave(Storm):
519
474
ExerciseSave may be extended with additional semantics (such as
520
475
ExerciseAttempt).
522
__storm_table__ = "exercise_save"
523
__storm_primary__ = "ws_ex_id", "user_id"
525
ws_ex_id = Int(name="ws_ex_id")
526
worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
477
__storm_table__ = "problem_save"
478
__storm_primary__ = "exercise_id", "user_id", "date"
480
exercise_id = Int(name="problemid")
481
exercise = Reference(exercise_id, Exercise.id)
528
482
user_id = Int(name="loginid")
529
483
user = Reference(user_id, User.id)
530
484
date = DateTime()
549
503
they won't count (either as a penalty or success), but will still be
552
__storm_table__ = "exercise_attempt"
553
__storm_primary__ = "ws_ex_id", "user_id", "date"
506
__storm_table__ = "problem_attempt"
507
__storm_primary__ = "exercise_id", "user_id", "date"
555
509
# The "text" field is the same but has a different name in the DB table
556
510
# for some reason.
557
511
text = Unicode(name="attempt")
558
512
complete = Bool()
561
def get_permissions(self, user):
562
return set(['view']) if user is self.user else set()
564
class TestSuite(Storm):
565
"""A Testsuite acts as a container for the test cases of an exercise."""
566
__storm_table__ = "test_suite"
567
__storm_primary__ = "exercise_id", "suiteid"
570
exercise_id = Unicode(name="exerciseid")
571
description = Unicode()
575
exercise = Reference(exercise_id, Exercise.id)
576
test_cases = ReferenceSet(suiteid, 'TestCase.suiteid', order_by="seq_no")
577
variables = ReferenceSet(suiteid, 'TestSuiteVar.suiteid', order_by='arg_no')
579
class TestCase(Storm):
580
"""A TestCase is a member of a TestSuite.
582
It contains the data necessary to check if an exercise is correct"""
583
__storm_table__ = "test_case"
584
__storm_primary__ = "testid", "suiteid"
588
suite = Reference(suiteid, "TestSuite.suiteid")
591
test_default = Unicode()
594
parts = ReferenceSet(testid, "TestCasePart.testid")
596
__init__ = _kwarg_init
598
class TestSuiteVar(Storm):
599
"""A container for the arguments of a Test Suite"""
600
__storm_table__ = "suite_variable"
601
__storm_primary__ = "varid"
606
var_value = Unicode()
610
suite = Reference(suiteid, "TestSuite.suiteid")
612
__init__ = _kwarg_init
614
class TestCasePart(Storm):
615
"""A container for the test elements of a Test Case"""
616
__storm_table__ = "test_case_part"
617
__storm_primary__ = "partid"
622
part_type = Unicode()
623
test_type = Unicode()
627
test = Reference(testid, "TestCase.testid")
629
__init__ = _kwarg_init