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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: William Grant
  • Date: 2009-05-26 03:06:53 UTC
  • Revision ID: grantw@unimelb.edu.au-20090526030653-axxawt0o5ws4icbt
Remove ivle.conf usage from ivle.studpath.

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,
235
234
        they may do everything. Otherwise they may do nothing.
236
235
        """
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'])
239
238
        else:
240
 
            return set(['view_public'])
 
239
            return set()
241
240
 
242
241
# SUBJECTS AND ENROLMENTS #
243
242
 
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
400
class Enrolment(Storm):
429
401
    """An enrolment of a user in an offering.
430
402
 
481
453
        return "<%s %d in %r>" % (type(self).__name__, self.id,
482
454
                                  self.offering)
483
455
 
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
 
456
    def get_permissions(self, user):
 
457
        return self.offering.get_permissions(user)
515
458
 
516
459
    @property
517
460
    def assigned(self):
520
463
        This will be a Storm ResultSet.
521
464
        """
522
465
        #If its a solo project, return everyone in offering
523
 
        if self.is_group:
 
466
        if self.max_students_per_group is None:
 
467
            return self.offering.students
 
468
        else:
524
469
            return self.project_groups
525
 
        else:
526
 
            return self.offering.students
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"
535
470
 
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)
561
496
 
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())
565
500
 
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.
574
509
        """
575
510
 
576
 
        if not self.can_submit(principal, who):
577
 
            raise DeadlinePassed()
 
511
        if not self.can_submit(principal):
 
512
            raise Exception('cannot submit')
578
513
 
579
514
        a = Assessed.get(Store.of(self), principal, self)
580
515
        ps = ProjectSubmission()
586
521
 
587
522
        return ps
588
523
 
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)
591
526
 
592
527
    @property
593
528
    def latest_submissions(self):
602
537
            )
603
538
        )
604
539
 
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
 
 
618
540
 
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'))
672
594
 
673
595
 
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'])
677
599
        else:
713
635
    project = Reference(project_id, Project.id)
714
636
 
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')
718
639
 
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
731
652
 
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
 
 
743
653
    @classmethod
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()
758
668
 
759
669
        if a is None:
760
670
            a = cls()
805
715
    submitter = Reference(submitter_id, User.id)
806
716
    date_submitted = DateTime()
807
717
 
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)
821
718
 
822
719
# WORKSHEETS AND EXERCISES #
823
720
 
854
751
    def __repr__(self):
855
752
        return "<%s %s>" % (type(self).__name__, self.name)
856
753
 
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."""
 
754
    def get_permissions(self, user):
865
755
        perms = set()
866
756
        roles = set()
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')
879
767
 
940
828
        store.find(WorksheetExercise,
941
829
            WorksheetExercise.worksheet == self).remove()
942
830
 
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
 
831
    def get_permissions(self, user):
 
832
        return self.offering.get_permissions(user)
953
833
 
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)
1002
882
 
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)
1005
885
 
1006
886
 
1007
887
class ExerciseSave(Storm):
1054
934
    complete = Bool()
1055
935
    active = Bool()
1056
936
 
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()
1059
939
 
1060
940
class TestSuite(Storm):
1079
959
 
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()
1098
978
    suite = Reference(suiteid, "TestSuite.suiteid")
1099
979
    passmsg = Unicode()
1100
980
    failmsg = Unicode()
1101
 
    test_default = Unicode() # Currently unused - only used for file matching.
 
981
    test_default = Unicode()
1102
982
    seq_no = Int()
1103
983
 
1104
984
    parts = ReferenceSet(testid, "TestCasePart.testid")