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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: William Grant
  • Date: 2009-12-08 04:48:54 UTC
  • Revision ID: grantw@unimelb.edu.au-20091208044854-1nemu4568azzeh9r
Make the group admin UI a per-projectset view.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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)
220
219
 
221
220
    @staticmethod
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()
230
229
 
231
 
    def get_permissions(self, user, config):
 
230
    def get_permissions(self, user):
232
231
        """Determine privileges held by a user over this object.
233
232
 
234
233
        If the user requesting privileges is this user or an admin,
250
249
    code = Unicode(name="subj_code")
251
250
    name = Unicode(name="subj_name")
252
251
    short_name = Unicode(name="subj_short_name")
 
252
    url = Unicode()
253
253
 
254
254
    offerings = ReferenceSet(id, 'Offering.subject_id')
255
255
 
258
258
    def __repr__(self):
259
259
        return "<%s '%s'>" % (type(self).__name__, self.short_name)
260
260
 
261
 
    def get_permissions(self, user, config):
 
261
    def get_permissions(self, user):
262
262
        """Determine privileges held by a user over this object.
263
263
 
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()
326
 
    url = Unicode()
327
325
    groups_student_permissions = Unicode()
328
326
 
329
327
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
332
330
                           'Enrolment.user_id',
333
331
                           'User.id')
334
332
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
335
 
    projects = ReferenceSet(id,
336
 
                            'ProjectSet.offering_id',
337
 
                            'ProjectSet.id',
338
 
                            'Project.project_set_id')
339
333
 
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)
374
368
 
375
 
    def get_permissions(self, user, config):
 
369
    def get_permissions(self, user):
376
370
        perms = set()
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']:
385
 
                    perms.add('enrol')
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
399
 
            if user.admin:
400
 
                perms.add('enrol_lecturer') # Can enrol lecturers
 
375
            if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
 
376
               or user.admin:
 
377
                perms.add('edit')
401
378
        return perms
402
379
 
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)
 
394
                )
418
395
 
419
396
    @property
420
397
    def students(self):
421
398
        return self.get_members_by_role(u'student')
422
399
 
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())
427
 
 
428
 
    def clone_worksheets(self, source):
429
 
        """Clone all worksheets from the specified source to this offering."""
430
 
        import ivle.worksheet.utils
431
 
        for worksheet in source.worksheets:
432
 
            newws = Worksheet()
433
 
            newws.seq_no = worksheet.seq_no
434
 
            newws.identifier = worksheet.identifier
435
 
            newws.name = worksheet.name
436
 
            newws.assessable = worksheet.assessable
437
 
            newws.data = worksheet.data
438
 
            newws.format = worksheet.format
439
 
            newws.offering = self
440
 
            Store.of(self).add(newws)
441
 
            ivle.worksheet.utils.update_exerciselist(newws)
442
 
 
443
 
 
444
400
class Enrolment(Storm):
445
401
    """An enrolment of a user in an offering.
446
402
 
472
428
        return "<%s %r in %r>" % (type(self).__name__, self.user,
473
429
                                  self.offering)
474
430
 
475
 
    def get_permissions(self, user, config):
476
 
        # A user can edit any enrolment that they could have created.
477
 
        perms = set()
478
 
        if ('enrol_' + str(self.role)) in self.offering.get_permissions(
479
 
            user, config):
480
 
            perms.add('edit')
481
 
        return perms
482
 
 
483
 
    def delete(self):
484
 
        """Delete this enrolment."""
485
 
        Store.of(self).remove(self)
486
 
 
487
 
 
488
431
# PROJECTS #
489
432
 
490
433
class ProjectSet(Storm):
510
453
        return "<%s %d in %r>" % (type(self).__name__, self.id,
511
454
                                  self.offering)
512
455
 
513
 
    def get_permissions(self, user, config):
514
 
        return self.offering.get_permissions(user, config)
515
 
 
516
 
    def get_groups_for_user(self, user):
517
 
        """List all groups in this offering of which the user is a member."""
518
 
        assert self.is_group
519
 
        return Store.of(self).find(
520
 
            ProjectGroup,
521
 
            ProjectGroupMembership.user_id == user.id,
522
 
            ProjectGroupMembership.project_group_id == ProjectGroup.id,
523
 
            ProjectGroup.project_set_id == self.id)
524
 
 
525
 
    def get_submission_principal(self, user):
526
 
        """Get the principal on behalf of which the user can submit.
527
 
 
528
 
        If this is a solo project set, the given user is returned. If
529
 
        the user is a member of exactly one group, all the group is
530
 
        returned. Otherwise, None is returned.
531
 
        """
532
 
        if self.is_group:
533
 
            groups = self.get_groups_for_user(user)
534
 
            if groups.count() == 1:
535
 
                return groups.one()
536
 
            else:
537
 
                return None
538
 
        else:
539
 
            return user
540
 
 
541
 
    @property
542
 
    def is_group(self):
543
 
        return self.max_students_per_group is not None
 
456
    def get_permissions(self, user):
 
457
        return self.offering.get_permissions(user)
544
458
 
545
459
    @property
546
460
    def assigned(self):
549
463
        This will be a Storm ResultSet.
550
464
        """
551
465
        #If its a solo project, return everyone in offering
552
 
        if self.is_group:
 
466
        if self.max_students_per_group is None:
 
467
            return self.offering.students
 
468
        else:
553
469
            return self.project_groups
554
 
        else:
555
 
            return self.offering.students
556
 
 
557
 
class DeadlinePassed(Exception):
558
 
    """An exception indicating that a project cannot be submitted because the
559
 
    deadline has passed."""
560
 
    def __init__(self):
561
 
        pass
562
 
    def __str__(self):
563
 
        return "The project deadline has passed"
564
470
 
565
471
class Project(Storm):
566
472
    """A student project for which submissions can be made."""
588
494
        return "<%s '%s' in %r>" % (type(self).__name__, self.short_name,
589
495
                                  self.project_set.offering)
590
496
 
591
 
    def can_submit(self, principal, user):
 
497
    def can_submit(self, principal):
592
498
        return (self in principal.get_projects() and
593
 
                not self.has_deadline_passed(user))
 
499
                self.deadline > datetime.datetime.now())
594
500
 
595
501
    def submit(self, principal, path, revision, who):
596
502
        """Submit a Subversion path and revision to a project.
602
508
        @param who: The user who is actually making the submission.
603
509
        """
604
510
 
605
 
        if not self.can_submit(principal, who):
606
 
            raise DeadlinePassed()
 
511
        if not self.can_submit(principal):
 
512
            raise Exception('cannot submit')
607
513
 
608
514
        a = Assessed.get(Store.of(self), principal, self)
609
515
        ps = ProjectSubmission()
615
521
 
616
522
        return ps
617
523
 
618
 
    def get_permissions(self, user, config):
619
 
        return self.project_set.offering.get_permissions(user, config)
 
524
    def get_permissions(self, user):
 
525
        return self.project_set.offering.get_permissions(user)
620
526
 
621
527
    @property
622
528
    def latest_submissions(self):
631
537
            )
632
538
        )
633
539
 
634
 
    def has_deadline_passed(self, user):
635
 
        """Check whether the deadline has passed."""
636
 
        # XXX: Need to respect extensions.
637
 
        return self.deadline < datetime.datetime.now()
638
 
 
639
 
    def get_submissions_for_principal(self, principal):
640
 
        """Fetch a ResultSet of all submissions by a particular principal."""
641
 
        assessed = Assessed.get(Store.of(self), principal, self)
642
 
        if assessed is None:
643
 
            return
644
 
        return assessed.submissions
645
 
 
646
 
 
647
540
 
648
541
class ProjectGroup(Storm):
649
542
    """A group of students working together on a project."""
700
593
            (not active_only) or (Semester.state == u'current'))
701
594
 
702
595
 
703
 
    def get_permissions(self, user, config):
 
596
    def get_permissions(self, user):
704
597
        if user.admin or user in self.members:
705
598
            return set(['submit_project'])
706
599
        else:
742
635
    project = Reference(project_id, Project.id)
743
636
 
744
637
    extensions = ReferenceSet(id, 'ProjectExtension.assessed_id')
745
 
    submissions = ReferenceSet(
746
 
        id, 'ProjectSubmission.assessed_id', order_by='date_submitted')
 
638
    submissions = ReferenceSet(id, 'ProjectSubmission.assessed_id')
747
639
 
748
640
    def __repr__(self):
749
641
        return "<%s %r in %r>" % (type(self).__name__,
758
650
    def principal(self):
759
651
        return self.project_group or self.user
760
652
 
761
 
    @property
762
 
    def checkout_location(self):
763
 
        """Returns the location of the Subversion workspace for this piece of
764
 
        assessment, relative to each group member's home directory."""
765
 
        subjectname = self.project.project_set.offering.subject.short_name
766
 
        if self.is_group:
767
 
            checkout_dir_name = self.principal.short_name
768
 
        else:
769
 
            checkout_dir_name = "mywork"
770
 
        return subjectname + "/" + checkout_dir_name
771
 
 
772
653
    @classmethod
773
654
    def get(cls, store, principal, project):
774
655
        """Find or create an Assessed for the given user or group and project.
783
664
        a = store.find(cls,
784
665
            (t is User) or (cls.project_group_id == principal.id),
785
666
            (t is ProjectGroup) or (cls.user_id == principal.id),
786
 
            cls.project_id == project.id).one()
 
667
            Project.id == project.id).one()
787
668
 
788
669
        if a is None:
789
670
            a = cls()
834
715
    submitter = Reference(submitter_id, User.id)
835
716
    date_submitted = DateTime()
836
717
 
837
 
    def get_verify_url(self, user):
838
 
        """Get the URL for verifying this submission, within the account of
839
 
        the given user."""
840
 
        # If this is a solo project, then self.path will be prefixed with the
841
 
        # subject name. Remove the first path segment.
842
 
        submitpath = self.path[1:] if self.path[:1] == '/' else self.path
843
 
        if not self.assessed.is_group:
844
 
            if '/' in submitpath:
845
 
                submitpath = submitpath.split('/', 1)[1]
846
 
            else:
847
 
                submitpath = ''
848
 
        return "/files/%s/%s/%s?r=%d" % (user.login,
849
 
            self.assessed.checkout_location, submitpath, self.revision)
850
718
 
851
719
# WORKSHEETS AND EXERCISES #
852
720
 
883
751
    def __repr__(self):
884
752
        return "<%s %s>" % (type(self).__name__, self.name)
885
753
 
886
 
    def get_permissions(self, user, config):
887
 
        return self.global_permissions(user, config)
888
 
 
889
 
    @staticmethod
890
 
    def global_permissions(user, config):
891
 
        """Gets the set of permissions this user has over *all* exercises.
892
 
        This is used to determine who may view the exercises list, and create
893
 
        new exercises."""
 
754
    def get_permissions(self, user):
894
755
        perms = set()
895
756
        roles = set()
896
757
        if user is not None:
900
761
            elif u'lecturer' in set((e.role for e in user.active_enrolments)):
901
762
                perms.add('edit')
902
763
                perms.add('view')
903
 
            elif (config['policy']['tutors_can_edit_worksheets']
904
 
            and u'tutor' in set((e.role for e in user.active_enrolments))):
905
 
                # Site-specific policy on the role of tutors
 
764
            elif u'tutor' in set((e.role for e in user.active_enrolments)):
906
765
                perms.add('edit')
907
766
                perms.add('view')
908
767
 
969
828
        store.find(WorksheetExercise,
970
829
            WorksheetExercise.worksheet == self).remove()
971
830
 
972
 
    def get_permissions(self, user, config):
973
 
        # Almost the same permissions as for the offering itself
974
 
        perms = self.offering.get_permissions(user, config)
975
 
        # However, "edit" permission is derived from the "edit_worksheets"
976
 
        # permission of the offering
977
 
        if 'edit_worksheets' in perms:
978
 
            perms.add('edit')
979
 
        else:
980
 
            perms.discard('edit')
981
 
        return perms
 
831
    def get_permissions(self, user):
 
832
        return self.offering.get_permissions(user)
982
833
 
983
834
    def get_xml(self):
984
835
        """Returns the xml of this worksheet, converts from rst if required."""
1029
880
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
1030
881
                                  self.worksheet.identifier)
1031
882
 
1032
 
    def get_permissions(self, user, config):
1033
 
        return self.worksheet.get_permissions(user, config)
 
883
    def get_permissions(self, user):
 
884
        return self.worksheet.get_permissions(user)
1034
885
 
1035
886
 
1036
887
class ExerciseSave(Storm):
1083
934
    complete = Bool()
1084
935
    active = Bool()
1085
936
 
1086
 
    def get_permissions(self, user, config):
 
937
    def get_permissions(self, user):
1087
938
        return set(['view']) if user is self.user else set()
1088
939
 
1089
940
class TestSuite(Storm):
1108
959
 
1109
960
    def delete(self):
1110
961
        """Delete this suite, without asking questions."""
1111
 
        for variable in self.variables:
 
962
        for vaariable in self.variables:
1112
963
            variable.delete()
1113
964
        for test_case in self.test_cases:
1114
965
            test_case.delete()
1127
978
    suite = Reference(suiteid, "TestSuite.suiteid")
1128
979
    passmsg = Unicode()
1129
980
    failmsg = Unicode()
1130
 
    test_default = Unicode() # Currently unused - only used for file matching.
 
981
    test_default = Unicode()
1131
982
    seq_no = Int()
1132
983
 
1133
984
    parts = ReferenceSet(testid, "TestCasePart.testid")