~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-15 05:37:50 UTC
  • Revision ID: grantw@unimelb.edu.au-20100215053750-hihmegnp8e7dshc2
Ignore test coverage files.

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
 
400
428
class Enrolment(Storm):
401
429
    """An enrolment of a user in an offering.
402
430
 
453
481
        return "<%s %d in %r>" % (type(self).__name__, self.id,
454
482
                                  self.offering)
455
483
 
456
 
    def get_permissions(self, user):
457
 
        return self.offering.get_permissions(user)
 
484
    def get_permissions(self, user, config):
 
485
        return self.offering.get_permissions(user, config)
 
486
 
 
487
    def get_groups_for_user(self, user):
 
488
        """List all groups in this offering of which the user is a member."""
 
489
        assert self.is_group
 
490
        return Store.of(self).find(
 
491
            ProjectGroup,
 
492
            ProjectGroupMembership.user_id == user.id,
 
493
            ProjectGroupMembership.project_group_id == ProjectGroup.id,
 
494
            ProjectGroup.project_set_id == self.id)
 
495
 
 
496
    def get_submission_principal(self, user):
 
497
        """Get the principal on behalf of which the user can submit.
 
498
 
 
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.
 
502
        """
 
503
        if self.is_group:
 
504
            groups = self.get_groups_for_user(user)
 
505
            if groups.count() == 1:
 
506
                return groups.one()
 
507
            else:
 
508
                return None
 
509
        else:
 
510
            return user
 
511
 
 
512
    @property
 
513
    def is_group(self):
 
514
        return self.max_students_per_group is not None
458
515
 
459
516
    @property
460
517
    def assigned(self):
463
520
        This will be a Storm ResultSet.
464
521
        """
465
522
        #If its a solo project, return everyone in offering
466
 
        if self.max_students_per_group is None:
 
523
        if self.is_group:
 
524
            return self.project_groups
 
525
        else:
467
526
            return self.offering.students
468
 
        else:
469
 
            return self.project_groups
 
527
 
 
528
class DeadlinePassed(Exception):
 
529
    """An exception indicating that a project cannot be submitted because the
 
530
    deadline has passed."""
 
531
    def __init__(self):
 
532
        pass
 
533
    def __str__(self):
 
534
        return "The project deadline has passed"
470
535
 
471
536
class Project(Storm):
472
537
    """A student project for which submissions can be made."""
494
559
        return "<%s '%s' in %r>" % (type(self).__name__, self.short_name,
495
560
                                  self.project_set.offering)
496
561
 
497
 
    def can_submit(self, principal):
 
562
    def can_submit(self, principal, user):
498
563
        return (self in principal.get_projects() and
499
 
                self.deadline > datetime.datetime.now())
 
564
                not self.has_deadline_passed(user))
500
565
 
501
566
    def submit(self, principal, path, revision, who):
502
567
        """Submit a Subversion path and revision to a project.
508
573
        @param who: The user who is actually making the submission.
509
574
        """
510
575
 
511
 
        if not self.can_submit(principal):
512
 
            raise Exception('cannot submit')
 
576
        if not self.can_submit(principal, who):
 
577
            raise DeadlinePassed()
513
578
 
514
579
        a = Assessed.get(Store.of(self), principal, self)
515
580
        ps = ProjectSubmission()
521
586
 
522
587
        return ps
523
588
 
524
 
    def get_permissions(self, user):
525
 
        return self.project_set.offering.get_permissions(user)
 
589
    def get_permissions(self, user, config):
 
590
        return self.project_set.offering.get_permissions(user, config)
526
591
 
527
592
    @property
528
593
    def latest_submissions(self):
537
602
            )
538
603
        )
539
604
 
 
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()
 
609
 
 
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)
 
613
        if assessed is None:
 
614
            return
 
615
        return assessed.submissions
 
616
 
 
617
 
540
618
 
541
619
class ProjectGroup(Storm):
542
620
    """A group of students working together on a project."""
593
671
            (not active_only) or (Semester.state == u'current'))
594
672
 
595
673
 
596
 
    def get_permissions(self, user):
 
674
    def get_permissions(self, user, config):
597
675
        if user.admin or user in self.members:
598
676
            return set(['submit_project'])
599
677
        else:
635
713
    project = Reference(project_id, Project.id)
636
714
 
637
715
    extensions = ReferenceSet(id, 'ProjectExtension.assessed_id')
638
 
    submissions = ReferenceSet(id, 'ProjectSubmission.assessed_id')
 
716
    submissions = ReferenceSet(
 
717
        id, 'ProjectSubmission.assessed_id', order_by='date_submitted')
639
718
 
640
719
    def __repr__(self):
641
720
        return "<%s %r in %r>" % (type(self).__name__,
650
729
    def principal(self):
651
730
        return self.project_group or self.user
652
731
 
 
732
    @property
 
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
 
737
        if self.is_group:
 
738
            checkout_dir_name = self.principal.short_name
 
739
        else:
 
740
            checkout_dir_name = "mywork"
 
741
        return subjectname + "/" + checkout_dir_name
 
742
 
653
743
    @classmethod
654
744
    def get(cls, store, principal, project):
655
745
        """Find or create an Assessed for the given user or group and project.
664
754
        a = store.find(cls,
665
755
            (t is User) or (cls.project_group_id == principal.id),
666
756
            (t is ProjectGroup) or (cls.user_id == principal.id),
667
 
            Project.id == project.id).one()
 
757
            cls.project_id == project.id).one()
668
758
 
669
759
        if a is None:
670
760
            a = cls()
715
805
    submitter = Reference(submitter_id, User.id)
716
806
    date_submitted = DateTime()
717
807
 
 
808
    def get_verify_url(self, user):
 
809
        """Get the URL for verifying this submission, within the account of
 
810
        the given user."""
 
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]
 
817
            else:
 
818
                submitpath = ''
 
819
        return "/files/%s/%s/%s?r=%d" % (user.login,
 
820
            self.assessed.checkout_location, submitpath, self.revision)
718
821
 
719
822
# WORKSHEETS AND EXERCISES #
720
823
 
751
854
    def __repr__(self):
752
855
        return "<%s %s>" % (type(self).__name__, self.name)
753
856
 
754
 
    def get_permissions(self, user):
 
857
    def get_permissions(self, user, config):
 
858
        return self.global_permissions(user, config)
 
859
 
 
860
    @staticmethod
 
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
 
864
        new exercises."""
755
865
        perms = set()
756
866
        roles = set()
757
867
        if user is not None:
761
871
            elif u'lecturer' in set((e.role for e in user.active_enrolments)):
762
872
                perms.add('edit')
763
873
                perms.add('view')
764
 
            elif u'tutor' in set((e.role for e in user.active_enrolments)):
 
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
765
877
                perms.add('edit')
766
878
                perms.add('view')
767
879
 
828
940
        store.find(WorksheetExercise,
829
941
            WorksheetExercise.worksheet == self).remove()
830
942
 
831
 
    def get_permissions(self, user):
832
 
        return self.offering.get_permissions(user)
 
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:
 
949
            perms.add('edit')
 
950
        else:
 
951
            perms.discard('edit')
 
952
        return perms
833
953
 
834
954
    def get_xml(self):
835
955
        """Returns the xml of this worksheet, converts from rst if required."""
880
1000
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
881
1001
                                  self.worksheet.identifier)
882
1002
 
883
 
    def get_permissions(self, user):
884
 
        return self.worksheet.get_permissions(user)
 
1003
    def get_permissions(self, user, config):
 
1004
        return self.worksheet.get_permissions(user, config)
885
1005
 
886
1006
 
887
1007
class ExerciseSave(Storm):
934
1054
    complete = Bool()
935
1055
    active = Bool()
936
1056
 
937
 
    def get_permissions(self, user):
 
1057
    def get_permissions(self, user, config):
938
1058
        return set(['view']) if user is self.user else set()
939
1059
 
940
1060
class TestSuite(Storm):
959
1079
 
960
1080
    def delete(self):
961
1081
        """Delete this suite, without asking questions."""
962
 
        for vaariable in self.variables:
 
1082
        for variable in self.variables:
963
1083
            variable.delete()
964
1084
        for test_case in self.test_cases:
965
1085
            test_case.delete()
978
1098
    suite = Reference(suiteid, "TestSuite.suiteid")
979
1099
    passmsg = Unicode()
980
1100
    failmsg = Unicode()
981
 
    test_default = Unicode()
 
1101
    test_default = Unicode() # Currently unused - only used for file matching.
982
1102
    seq_no = Int()
983
1103
 
984
1104
    parts = ReferenceSet(testid, "TestCasePart.testid")