295
213
notes = Unicode()
300
return Store.of(self).find(ProjectGroup,
301
ProjectSet.offering_id == self.offering.id,
302
ProjectGroup.project_set_id == ProjectSet.id,
303
ProjectGroupMembership.project_group_id == ProjectGroup.id,
304
ProjectGroupMembership.user_id == self.user.id)
306
__init__ = _kwarg_init
309
return "<%s %r in %r>" % (type(self).__name__, self.user,
312
class AlreadyEnrolledError(Exception):
317
class ProjectSet(Storm):
318
__storm_table__ = "project_set"
320
id = Int(name="projectsetid", primary=True)
321
offering_id = Int(name="offeringid")
322
offering = Reference(offering_id, Offering.id)
323
max_students_per_group = Int()
325
projects = ReferenceSet(id, 'Project.project_set_id')
326
project_groups = ReferenceSet(id, 'ProjectGroup.project_set_id')
328
__init__ = _kwarg_init
331
return "<%s %d in %r>" % (type(self).__name__, self.id,
334
class Project(Storm):
335
__storm_table__ = "project"
337
id = Int(name="projectid", primary=True)
340
project_set_id = Int(name="projectsetid")
341
project_set = Reference(project_set_id, ProjectSet.id)
342
deadline = DateTime()
344
__init__ = _kwarg_init
347
return "<%s '%s' in %r>" % (type(self).__name__, self.synopsis,
348
self.project_set.offering)
350
class ProjectGroup(Storm):
351
__storm_table__ = "project_group"
353
id = Int(name="groupid", primary=True)
354
name = Unicode(name="groupnm")
355
project_set_id = Int(name="projectsetid")
356
project_set = Reference(project_set_id, ProjectSet.id)
358
created_by_id = Int(name="createdby")
359
created_by = Reference(created_by_id, User.id)
362
members = ReferenceSet(id,
363
"ProjectGroupMembership.project_group_id",
364
"ProjectGroupMembership.user_id",
367
__init__ = _kwarg_init
370
return "<%s %s in %r>" % (type(self).__name__, self.name,
371
self.project_set.offering)
373
class ProjectGroupMembership(Storm):
374
__storm_table__ = "group_member"
375
__storm_primary__ = "user_id", "project_group_id"
377
user_id = Int(name="loginid")
378
user = Reference(user_id, User.id)
379
project_group_id = Int(name="groupid")
380
project_group = Reference(project_group_id, ProjectGroup.id)
382
__init__ = _kwarg_init
385
return "<%s %r in %r>" % (type(self).__name__, self.user,
388
# WORKSHEETS AND EXERCISES #
390
class Exercise(Storm):
391
# Note: Table "problem" is called "Exercise" in the Object layer, since
392
# it's called that everywhere else.
393
__storm_table__ = "problem"
394
#TODO: Add in a field for the user-friendly identifier
395
id = Unicode(primary=True, name="identifier")
397
description = Unicode()
403
worksheets = ReferenceSet(id,
404
'WorksheetExercise.exercise_id',
405
'WorksheetExercise.worksheet_id',
409
test_suites = ReferenceSet(id, 'TestSuite.exercise_id')
411
__init__ = _kwarg_init
414
return "<%s %s>" % (type(self).__name__, self.name)
417
def get_by_name(cls, store, name):
419
Get the Exercise from the db associated with a given store and name.
420
If the exercise is not in the database, creates it and inserts it
423
ex = store.find(cls, cls.name == unicode(name)).one()
426
ex = Exercise(name=unicode(name))
431
class Worksheet(Storm):
432
__storm_table__ = "worksheet"
434
id = Int(primary=True, name="worksheetid")
435
# XXX subject is not linked to a Subject object. This is a property of
436
# the database, and will be refactored.
438
offering_id = Int(name="offeringid")
439
name = Unicode(name="identifier")
443
offering = Reference(offering_id, 'Offering.id')
445
exercises = ReferenceSet(id,
446
'WorksheetExercise.worksheet_id',
447
'WorksheetExercise.exercise_id',
449
# Use worksheet_exercises to get access to the WorksheetExercise objects
450
# binding worksheets to exercises. This is required to access the
452
worksheet_exercises = ReferenceSet(id,
453
'WorksheetExercise.worksheet_id')
456
__init__ = _kwarg_init
459
return "<%s %s>" % (type(self).__name__, self.name)
461
# XXX Refactor this - make it an instance method of Subject rather than a
462
# class method of Worksheet. Can't do that now because Subject isn't
463
# linked referentially to the Worksheet.
465
def get_by_name(cls, store, subjectname, worksheetname):
467
Get the Worksheet from the db associated with a given store, subject
468
name and worksheet name.
470
return store.find(cls, cls.subject == unicode(subjectname),
471
cls.name == unicode(worksheetname)).one()
473
def remove_all_exercises(self, store):
475
Remove all exercises from this worksheet.
476
This does not delete the exercises themselves. It just removes them
479
store.find(WorksheetExercise,
480
WorksheetExercise.worksheet == self).remove()
482
class WorksheetExercise(Storm):
483
__storm_table__ = "worksheet_problem"
484
__storm_primary__ = "worksheet_id", "exercise_id"
486
worksheet_id = Int(name="worksheetid")
487
worksheet = Reference(worksheet_id, Worksheet.id)
488
exercise_id = Unicode(name="problemid")
489
exercise = Reference(exercise_id, Exercise.id)
492
__init__ = _kwarg_init
495
return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
498
class ExerciseSave(Storm):
500
Represents a potential solution to an exercise that a user has submitted
501
to the server for storage.
502
A basic ExerciseSave is just the current saved text for this exercise for
503
this user (doesn't count towards their attempts).
504
ExerciseSave may be extended with additional semantics (such as
507
__storm_table__ = "problem_save"
508
__storm_primary__ = "exercise_id", "user_id", "date"
510
exercise_id = Unicode(name="problemid")
511
exercise = Reference(exercise_id, Exercise.id)
512
user_id = Int(name="loginid")
513
user = Reference(user_id, User.id)
517
worksheet = Reference(worksheetid, Worksheet.id)
519
__init__ = _kwarg_init
522
return "<%s %s by %s at %s>" % (type(self).__name__,
523
self.exercise.name, self.user.login, self.date.strftime("%c"))
525
class ExerciseAttempt(ExerciseSave):
527
An ExerciseAttempt is a special case of an ExerciseSave. Like an
528
ExerciseSave, it constitutes exercise solution data that the user has
529
submitted to the server for storage.
530
In addition, it contains additional information about the submission.
531
complete - True if this submission was successful, rendering this exercise
532
complete for this user.
533
active - True if this submission is "active" (usually true). Submissions
534
may be de-activated by privileged users for special reasons, and then
535
they won't count (either as a penalty or success), but will still be
538
__storm_table__ = "problem_attempt"
539
__storm_primary__ = "exercise_id", "user_id", "date"
541
# The "text" field is the same but has a different name in the DB table
543
text = Unicode(name="attempt")
547
def get_permissions(self, user):
548
return set(['view']) if user is self.user else set()
550
class TestSuite(Storm):
551
"""A Testsuite acts as a container for the test cases of an exercise."""
552
__storm_table__ = "test_suite"
553
__storm_primary__ = "exercise_id", "suiteid"
556
exercise_id = Unicode(name="problemid")
557
exercise = Reference(exercise_id, Exercise.id)
558
test_cases = ReferenceSet(suiteid, 'TestCase.suiteid')
559
description = Unicode()
562
class TestCase(Storm):
563
"""A TestCase is a member of a TestSuite.
565
It contains the data necessary to check if an exercise is correct"""
566
__storm_table__ = "test_case"
567
__storm_primary__ = "testid", "suiteid"
571
suite = Reference(suiteid, TestSuite.suiteid)
575
code_type = Unicode()
580
__init__ = _kwarg_init
216
__init__ = _kwarg_init
219
return "<%s %r in %r>" % (type(self).__name__, self.user,