215
215
Semester.id == Offering.semester_id,
216
216
(not active_only) or (Semester.state == u'current'),
217
217
Enrolment.offering_id == Offering.id,
218
Enrolment.user_id == self.id,
219
Enrolment.active == True)
218
Enrolment.user_id == self.id)
222
221
def hash_password(password):
228
227
"""Find a user in a store by login name."""
229
228
return store.find(cls, cls.login == unicode(login)).one()
231
def get_permissions(self, user, config):
230
def get_permissions(self, user):
232
231
"""Determine privileges held by a user over this object.
234
233
If the user requesting privileges is this user or an admin,
235
234
they may do everything. Otherwise they may do nothing.
237
236
if user and user.admin or user is self:
238
return set(['view_public', 'view', 'edit', 'submit_project'])
237
return set(['view', 'edit', 'submit_project'])
240
return set(['view_public'])
242
241
# SUBJECTS AND ENROLMENTS #
250
249
code = Unicode(name="subj_code")
251
250
name = Unicode(name="subj_name")
252
251
short_name = Unicode(name="subj_short_name")
254
254
offerings = ReferenceSet(id, 'Offering.subject_id')
258
258
def __repr__(self):
259
259
return "<%s '%s'>" % (type(self).__name__, self.short_name)
261
def get_permissions(self, user, config):
261
def get_permissions(self, user):
262
262
"""Determine privileges held by a user over this object.
264
264
If the user requesting privileges is an admin, they may edit.
322
322
subject = Reference(subject_id, Subject.id)
323
323
semester_id = Int(name="semesterid")
324
324
semester = Reference(semester_id, Semester.id)
325
description = Unicode()
327
325
groups_student_permissions = Unicode()
329
327
enrolments = ReferenceSet(id, 'Enrolment.offering_id')
332
330
'Enrolment.user_id',
334
332
project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
335
projects = ReferenceSet(id,
336
'ProjectSet.offering_id',
338
'Project.project_set_id')
340
334
worksheets = ReferenceSet(id,
341
335
'Worksheet.offering_id',
372
366
Enrolment.offering_id == self.id).one()
373
367
Store.of(enrolment).remove(enrolment)
375
def get_permissions(self, user, config):
369
def get_permissions(self, user):
377
371
if user is not None:
378
372
enrolment = self.get_enrolment(user)
379
373
if enrolment or user.admin:
380
374
perms.add('view')
381
if enrolment and enrolment.role == u'tutor':
382
perms.add('view_project_submissions')
383
# Site-specific policy on the role of tutors
384
if config['policy']['tutors_can_enrol_students']:
386
perms.add('enrol_student')
387
if config['policy']['tutors_can_edit_worksheets']:
388
perms.add('edit_worksheets')
389
if config['policy']['tutors_can_admin_groups']:
390
perms.add('admin_groups')
391
if (enrolment and enrolment.role in (u'lecturer')) or user.admin:
392
perms.add('view_project_submissions')
393
perms.add('admin_groups')
394
perms.add('edit_worksheets')
395
perms.add('edit') # Can edit projects & details
396
perms.add('enrol') # Can see enrolment screen at all
397
perms.add('enrol_student') # Can enrol students
398
perms.add('enrol_tutor') # Can enrol tutors
400
perms.add('enrol_lecturer') # Can enrol lecturers
375
if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
403
380
def get_enrolment(self, user):
414
391
Enrolment.user_id == User.id,
415
392
Enrolment.offering_id == self.id,
416
393
Enrolment.role == role
417
).order_by(User.login)
420
397
def students(self):
421
398
return self.get_members_by_role(u'student')
423
def get_open_projects_for_user(self, user):
424
"""Find all projects currently open to submissions by a user."""
425
# XXX: Respect extensions.
426
return self.projects.find(Project.deadline > datetime.datetime.now())
428
400
class Enrolment(Storm):
429
401
"""An enrolment of a user in an offering.
481
453
return "<%s %d in %r>" % (type(self).__name__, self.id,
484
def get_permissions(self, user, config):
485
return self.offering.get_permissions(user, config)
487
def get_groups_for_user(self, user):
488
"""List all groups in this offering of which the user is a member."""
490
return Store.of(self).find(
492
ProjectGroupMembership.user_id == user.id,
493
ProjectGroupMembership.project_group_id == ProjectGroup.id,
494
ProjectGroup.project_set_id == self.id)
496
def get_submission_principal(self, user):
497
"""Get the principal on behalf of which the user can submit.
499
If this is a solo project set, the given user is returned. If
500
the user is a member of exactly one group, all the group is
501
returned. Otherwise, None is returned.
504
groups = self.get_groups_for_user(user)
505
if groups.count() == 1:
514
return self.max_students_per_group is not None
456
def get_permissions(self, user):
457
return self.offering.get_permissions(user)
517
460
def assigned(self):
520
463
This will be a Storm ResultSet.
522
465
#If its a solo project, return everyone in offering
466
if self.max_students_per_group is None:
467
return self.offering.students
524
469
return self.project_groups
526
return self.offering.students
528
class DeadlinePassed(Exception):
529
"""An exception indicating that a project cannot be submitted because the
530
deadline has passed."""
534
return "The project deadline has passed"
536
471
class Project(Storm):
537
472
"""A student project for which submissions can be made."""
559
494
return "<%s '%s' in %r>" % (type(self).__name__, self.short_name,
560
495
self.project_set.offering)
562
def can_submit(self, principal, user):
497
def can_submit(self, principal):
563
498
return (self in principal.get_projects() and
564
not self.has_deadline_passed(user))
499
self.deadline > datetime.datetime.now())
566
501
def submit(self, principal, path, revision, who):
567
502
"""Submit a Subversion path and revision to a project.
573
508
@param who: The user who is actually making the submission.
576
if not self.can_submit(principal, who):
577
raise DeadlinePassed()
511
if not self.can_submit(principal):
512
raise Exception('cannot submit')
579
514
a = Assessed.get(Store.of(self), principal, self)
580
515
ps = ProjectSubmission()
589
def get_permissions(self, user, config):
590
return self.project_set.offering.get_permissions(user, config)
524
def get_permissions(self, user):
525
return self.project_set.offering.get_permissions(user)
593
528
def latest_submissions(self):
605
def has_deadline_passed(self, user):
606
"""Check whether the deadline has passed."""
607
# XXX: Need to respect extensions.
608
return self.deadline < datetime.datetime.now()
610
def get_submissions_for_principal(self, principal):
611
"""Fetch a ResultSet of all submissions by a particular principal."""
612
assessed = Assessed.get(Store.of(self), principal, self)
615
return assessed.submissions
619
541
class ProjectGroup(Storm):
620
542
"""A group of students working together on a project."""
671
593
(not active_only) or (Semester.state == u'current'))
674
def get_permissions(self, user, config):
596
def get_permissions(self, user):
675
597
if user.admin or user in self.members:
676
598
return set(['submit_project'])
713
635
project = Reference(project_id, Project.id)
715
637
extensions = ReferenceSet(id, 'ProjectExtension.assessed_id')
716
submissions = ReferenceSet(
717
id, 'ProjectSubmission.assessed_id', order_by='date_submitted')
638
submissions = ReferenceSet(id, 'ProjectSubmission.assessed_id')
719
640
def __repr__(self):
720
641
return "<%s %r in %r>" % (type(self).__name__,
729
650
def principal(self):
730
651
return self.project_group or self.user
733
def checkout_location(self):
734
"""Returns the location of the Subversion workspace for this piece of
735
assessment, relative to each group member's home directory."""
736
subjectname = self.project.project_set.offering.subject.short_name
738
checkout_dir_name = self.principal.short_name
740
checkout_dir_name = "mywork"
741
return subjectname + "/" + checkout_dir_name
744
654
def get(cls, store, principal, project):
745
655
"""Find or create an Assessed for the given user or group and project.
754
664
a = store.find(cls,
755
665
(t is User) or (cls.project_group_id == principal.id),
756
666
(t is ProjectGroup) or (cls.user_id == principal.id),
757
cls.project_id == project.id).one()
667
Project.id == project.id).one()
805
715
submitter = Reference(submitter_id, User.id)
806
716
date_submitted = DateTime()
808
def get_verify_url(self, user):
809
"""Get the URL for verifying this submission, within the account of
811
# If this is a solo project, then self.path will be prefixed with the
812
# subject name. Remove the first path segment.
813
submitpath = self.path[1:] if self.path[:1] == '/' else self.path
814
if not self.assessed.is_group:
815
if '/' in submitpath:
816
submitpath = submitpath.split('/', 1)[1]
819
return "/files/%s/%s/%s?r=%d" % (user.login,
820
self.assessed.checkout_location, submitpath, self.revision)
822
719
# WORKSHEETS AND EXERCISES #
854
751
def __repr__(self):
855
752
return "<%s %s>" % (type(self).__name__, self.name)
857
def get_permissions(self, user, config):
858
return self.global_permissions(user, config)
861
def global_permissions(user, config):
862
"""Gets the set of permissions this user has over *all* exercises.
863
This is used to determine who may view the exercises list, and create
754
def get_permissions(self, user):
867
757
if user is not None:
871
761
elif u'lecturer' in set((e.role for e in user.active_enrolments)):
872
762
perms.add('edit')
873
763
perms.add('view')
874
elif (config['policy']['tutors_can_edit_worksheets']
875
and u'tutor' in set((e.role for e in user.active_enrolments))):
876
# Site-specific policy on the role of tutors
764
elif u'tutor' in set((e.role for e in user.active_enrolments)):
877
765
perms.add('edit')
878
766
perms.add('view')
940
828
store.find(WorksheetExercise,
941
829
WorksheetExercise.worksheet == self).remove()
943
def get_permissions(self, user, config):
944
# Almost the same permissions as for the offering itself
945
perms = self.offering.get_permissions(user, config)
946
# However, "edit" permission is derived from the "edit_worksheets"
947
# permission of the offering
948
if 'edit_worksheets' in perms:
951
perms.discard('edit')
831
def get_permissions(self, user):
832
return self.offering.get_permissions(user)
954
834
def get_xml(self):
955
835
"""Returns the xml of this worksheet, converts from rst if required."""
1000
880
return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
1001
881
self.worksheet.identifier)
1003
def get_permissions(self, user, config):
1004
return self.worksheet.get_permissions(user, config)
883
def get_permissions(self, user):
884
return self.worksheet.get_permissions(user)
1007
887
class ExerciseSave(Storm):
1054
934
complete = Bool()
1057
def get_permissions(self, user, config):
937
def get_permissions(self, user):
1058
938
return set(['view']) if user is self.user else set()
1060
940
class TestSuite(Storm):
1080
960
def delete(self):
1081
961
"""Delete this suite, without asking questions."""
1082
for variable in self.variables:
962
for vaariable in self.variables:
1083
963
variable.delete()
1084
964
for test_case in self.test_cases:
1085
965
test_case.delete()