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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: William Grant
  • Date: 2010-02-16 04:11:46 UTC
  • Revision ID: grantw@unimelb.edu.au-20100216041146-rvfbuwin7fncc0nw
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.

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)
 
218
            Enrolment.user_id == self.id,
 
219
            Enrolment.active == True)
219
220
 
220
221
    @staticmethod
221
222
    def hash_password(password):
227
228
        """Find a user in a store by login name."""
228
229
        return store.find(cls, cls.login == unicode(login)).one()
229
230
 
230
 
    def get_permissions(self, user):
 
231
    def get_permissions(self, user, config):
231
232
        """Determine privileges held by a user over this object.
232
233
 
233
234
        If the user requesting privileges is this user or an admin,
234
235
        they may do everything. Otherwise they may do nothing.
235
236
        """
236
237
        if user and user.admin or user is self:
237
 
            return set(['view', 'edit', 'submit_project'])
 
238
            return set(['view_public', 'view', 'edit', 'submit_project'])
238
239
        else:
239
 
            return set()
 
240
            return set(['view_public'])
240
241
 
241
242
# SUBJECTS AND ENROLMENTS #
242
243
 
249
250
    code = Unicode(name="subj_code")
250
251
    name = Unicode(name="subj_name")
251
252
    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):
 
261
    def get_permissions(self, user, config):
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()
325
327
    groups_student_permissions = Unicode()
326
328
 
327
329
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
330
332
                           'Enrolment.user_id',
331
333
                           'User.id')
332
334
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
 
335
    projects = ReferenceSet(id,
 
336
                            'ProjectSet.offering_id',
 
337
                            'ProjectSet.id',
 
338
                            'Project.project_set_id')
333
339
 
334
340
    worksheets = ReferenceSet(id, 
335
341
        'Worksheet.offering_id', 
366
372
                               Enrolment.offering_id == self.id).one()
367
373
        Store.of(enrolment).remove(enrolment)
368
374
 
369
 
    def get_permissions(self, user):
 
375
    def get_permissions(self, user, config):
370
376
        perms = set()
371
377
        if user is not None:
372
378
            enrolment = self.get_enrolment(user)
373
379
            if enrolment or user.admin:
374
380
                perms.add('view')
375
 
            if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
376
 
               or user.admin:
377
 
                perms.add('edit')
 
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
378
401
        return perms
379
402
 
380
403
    def get_enrolment(self, user):
391
414
                Enrolment.user_id == User.id,
392
415
                Enrolment.offering_id == self.id,
393
416
                Enrolment.role == role
394
 
                )
 
417
                ).order_by(User.login)
395
418
 
396
419
    @property
397
420
    def students(self):
398
421
        return self.get_members_by_role(u'student')
399
422
 
 
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
 
400
444
class Enrolment(Storm):
401
445
    """An enrolment of a user in an offering.
402
446
 
453
497
        return "<%s %d in %r>" % (type(self).__name__, self.id,
454
498
                                  self.offering)
455
499
 
456
 
    def get_permissions(self, user):
457
 
        return self.offering.get_permissions(user)
 
500
    def get_permissions(self, user, config):
 
501
        return self.offering.get_permissions(user, config)
 
502
 
 
503
    def get_groups_for_user(self, user):
 
504
        """List all groups in this offering of which the user is a member."""
 
505
        assert self.is_group
 
506
        return Store.of(self).find(
 
507
            ProjectGroup,
 
508
            ProjectGroupMembership.user_id == user.id,
 
509
            ProjectGroupMembership.project_group_id == ProjectGroup.id,
 
510
            ProjectGroup.project_set_id == self.id)
 
511
 
 
512
    def get_submission_principal(self, user):
 
513
        """Get the principal on behalf of which the user can submit.
 
514
 
 
515
        If this is a solo project set, the given user is returned. If
 
516
        the user is a member of exactly one group, all the group is
 
517
        returned. Otherwise, None is returned.
 
518
        """
 
519
        if self.is_group:
 
520
            groups = self.get_groups_for_user(user)
 
521
            if groups.count() == 1:
 
522
                return groups.one()
 
523
            else:
 
524
                return None
 
525
        else:
 
526
            return user
 
527
 
 
528
    @property
 
529
    def is_group(self):
 
530
        return self.max_students_per_group is not None
458
531
 
459
532
    @property
460
533
    def assigned(self):
463
536
        This will be a Storm ResultSet.
464
537
        """
465
538
        #If its a solo project, return everyone in offering
466
 
        if self.max_students_per_group is None:
 
539
        if self.is_group:
 
540
            return self.project_groups
 
541
        else:
467
542
            return self.offering.students
468
 
        else:
469
 
            return self.project_groups
 
543
 
 
544
class DeadlinePassed(Exception):
 
545
    """An exception indicating that a project cannot be submitted because the
 
546
    deadline has passed."""
 
547
    def __init__(self):
 
548
        pass
 
549
    def __str__(self):
 
550
        return "The project deadline has passed"
470
551
 
471
552
class Project(Storm):
472
553
    """A student project for which submissions can be made."""
494
575
        return "<%s '%s' in %r>" % (type(self).__name__, self.short_name,
495
576
                                  self.project_set.offering)
496
577
 
497
 
    def can_submit(self, principal):
 
578
    def can_submit(self, principal, user):
498
579
        return (self in principal.get_projects() and
499
 
                self.deadline > datetime.datetime.now())
 
580
                not self.has_deadline_passed(user))
500
581
 
501
582
    def submit(self, principal, path, revision, who):
502
583
        """Submit a Subversion path and revision to a project.
508
589
        @param who: The user who is actually making the submission.
509
590
        """
510
591
 
511
 
        if not self.can_submit(principal):
512
 
            raise Exception('cannot submit')
 
592
        if not self.can_submit(principal, who):
 
593
            raise DeadlinePassed()
513
594
 
514
595
        a = Assessed.get(Store.of(self), principal, self)
515
596
        ps = ProjectSubmission()
521
602
 
522
603
        return ps
523
604
 
524
 
    def get_permissions(self, user):
525
 
        return self.project_set.offering.get_permissions(user)
 
605
    def get_permissions(self, user, config):
 
606
        return self.project_set.offering.get_permissions(user, config)
526
607
 
527
608
    @property
528
609
    def latest_submissions(self):
537
618
            )
538
619
        )
539
620
 
 
621
    def has_deadline_passed(self, user):
 
622
        """Check whether the deadline has passed."""
 
623
        # XXX: Need to respect extensions.
 
624
        return self.deadline < datetime.datetime.now()
 
625
 
 
626
    def get_submissions_for_principal(self, principal):
 
627
        """Fetch a ResultSet of all submissions by a particular principal."""
 
628
        assessed = Assessed.get(Store.of(self), principal, self)
 
629
        if assessed is None:
 
630
            return
 
631
        return assessed.submissions
 
632
 
 
633
 
540
634
 
541
635
class ProjectGroup(Storm):
542
636
    """A group of students working together on a project."""
593
687
            (not active_only) or (Semester.state == u'current'))
594
688
 
595
689
 
596
 
    def get_permissions(self, user):
 
690
    def get_permissions(self, user, config):
597
691
        if user.admin or user in self.members:
598
692
            return set(['submit_project'])
599
693
        else:
635
729
    project = Reference(project_id, Project.id)
636
730
 
637
731
    extensions = ReferenceSet(id, 'ProjectExtension.assessed_id')
638
 
    submissions = ReferenceSet(id, 'ProjectSubmission.assessed_id')
 
732
    submissions = ReferenceSet(
 
733
        id, 'ProjectSubmission.assessed_id', order_by='date_submitted')
639
734
 
640
735
    def __repr__(self):
641
736
        return "<%s %r in %r>" % (type(self).__name__,
650
745
    def principal(self):
651
746
        return self.project_group or self.user
652
747
 
 
748
    @property
 
749
    def checkout_location(self):
 
750
        """Returns the location of the Subversion workspace for this piece of
 
751
        assessment, relative to each group member's home directory."""
 
752
        subjectname = self.project.project_set.offering.subject.short_name
 
753
        if self.is_group:
 
754
            checkout_dir_name = self.principal.short_name
 
755
        else:
 
756
            checkout_dir_name = "mywork"
 
757
        return subjectname + "/" + checkout_dir_name
 
758
 
653
759
    @classmethod
654
760
    def get(cls, store, principal, project):
655
761
        """Find or create an Assessed for the given user or group and project.
664
770
        a = store.find(cls,
665
771
            (t is User) or (cls.project_group_id == principal.id),
666
772
            (t is ProjectGroup) or (cls.user_id == principal.id),
667
 
            Project.id == project.id).one()
 
773
            cls.project_id == project.id).one()
668
774
 
669
775
        if a is None:
670
776
            a = cls()
715
821
    submitter = Reference(submitter_id, User.id)
716
822
    date_submitted = DateTime()
717
823
 
 
824
    def get_verify_url(self, user):
 
825
        """Get the URL for verifying this submission, within the account of
 
826
        the given user."""
 
827
        # If this is a solo project, then self.path will be prefixed with the
 
828
        # subject name. Remove the first path segment.
 
829
        submitpath = self.path[1:] if self.path[:1] == '/' else self.path
 
830
        if not self.assessed.is_group:
 
831
            if '/' in submitpath:
 
832
                submitpath = submitpath.split('/', 1)[1]
 
833
            else:
 
834
                submitpath = ''
 
835
        return "/files/%s/%s/%s?r=%d" % (user.login,
 
836
            self.assessed.checkout_location, submitpath, self.revision)
718
837
 
719
838
# WORKSHEETS AND EXERCISES #
720
839
 
751
870
    def __repr__(self):
752
871
        return "<%s %s>" % (type(self).__name__, self.name)
753
872
 
754
 
    def get_permissions(self, user):
 
873
    def get_permissions(self, user, config):
 
874
        return self.global_permissions(user, config)
 
875
 
 
876
    @staticmethod
 
877
    def global_permissions(user, config):
 
878
        """Gets the set of permissions this user has over *all* exercises.
 
879
        This is used to determine who may view the exercises list, and create
 
880
        new exercises."""
755
881
        perms = set()
756
882
        roles = set()
757
883
        if user is not None:
761
887
            elif u'lecturer' in set((e.role for e in user.active_enrolments)):
762
888
                perms.add('edit')
763
889
                perms.add('view')
764
 
            elif u'tutor' in set((e.role for e in user.active_enrolments)):
 
890
            elif (config['policy']['tutors_can_edit_worksheets']
 
891
            and u'tutor' in set((e.role for e in user.active_enrolments))):
 
892
                # Site-specific policy on the role of tutors
765
893
                perms.add('edit')
766
894
                perms.add('view')
767
895
 
828
956
        store.find(WorksheetExercise,
829
957
            WorksheetExercise.worksheet == self).remove()
830
958
 
831
 
    def get_permissions(self, user):
832
 
        return self.offering.get_permissions(user)
 
959
    def get_permissions(self, user, config):
 
960
        # Almost the same permissions as for the offering itself
 
961
        perms = self.offering.get_permissions(user, config)
 
962
        # However, "edit" permission is derived from the "edit_worksheets"
 
963
        # permission of the offering
 
964
        if 'edit_worksheets' in perms:
 
965
            perms.add('edit')
 
966
        else:
 
967
            perms.discard('edit')
 
968
        return perms
833
969
 
834
970
    def get_xml(self):
835
971
        """Returns the xml of this worksheet, converts from rst if required."""
880
1016
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
881
1017
                                  self.worksheet.identifier)
882
1018
 
883
 
    def get_permissions(self, user):
884
 
        return self.worksheet.get_permissions(user)
 
1019
    def get_permissions(self, user, config):
 
1020
        return self.worksheet.get_permissions(user, config)
885
1021
 
886
1022
 
887
1023
class ExerciseSave(Storm):
934
1070
    complete = Bool()
935
1071
    active = Bool()
936
1072
 
937
 
    def get_permissions(self, user):
 
1073
    def get_permissions(self, user, config):
938
1074
        return set(['view']) if user is self.user else set()
939
1075
 
940
1076
class TestSuite(Storm):
959
1095
 
960
1096
    def delete(self):
961
1097
        """Delete this suite, without asking questions."""
962
 
        for vaariable in self.variables:
 
1098
        for variable in self.variables:
963
1099
            variable.delete()
964
1100
        for test_case in self.test_cases:
965
1101
            test_case.delete()
978
1114
    suite = Reference(suiteid, "TestSuite.suiteid")
979
1115
    passmsg = Unicode()
980
1116
    failmsg = Unicode()
981
 
    test_default = Unicode()
 
1117
    test_default = Unicode() # Currently unused - only used for file matching.
982
1118
    seq_no = Int()
983
1119
 
984
1120
    parts = ReferenceSet(testid, "TestCasePart.testid")