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

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: Matt Giuca
  • Date: 2010-02-18 07:55:53 UTC
  • Revision ID: matt.giuca@gmail.com-20100218075553-d7qcind022hp8kaw
browser.js: submit now decodes the URL of a file before submitting it. This ensures that files with 'special' characters are treated correctly before being sent in a POST request (since the URLs come URL-encoded, not decoding them first would result in double-encoding). Fixes Launchpad bug #523656.

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,
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):
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
 
428
472
        return "<%s %r in %r>" % (type(self).__name__, self.user,
429
473
                                  self.offering)
430
474
 
 
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
 
431
488
# PROJECTS #
432
489
 
433
490
class ProjectSet(Storm):
453
510
        return "<%s %d in %r>" % (type(self).__name__, self.id,
454
511
                                  self.offering)
455
512
 
456
 
    def get_permissions(self, user):
457
 
        return self.offering.get_permissions(user)
 
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
458
544
 
459
545
    @property
460
546
    def assigned(self):
463
549
        This will be a Storm ResultSet.
464
550
        """
465
551
        #If its a solo project, return everyone in offering
466
 
        if self.max_students_per_group is None:
 
552
        if self.is_group:
 
553
            return self.project_groups
 
554
        else:
467
555
            return self.offering.students
468
 
        else:
469
 
            return self.project_groups
 
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"
470
564
 
471
565
class Project(Storm):
472
566
    """A student project for which submissions can be made."""
494
588
        return "<%s '%s' in %r>" % (type(self).__name__, self.short_name,
495
589
                                  self.project_set.offering)
496
590
 
497
 
    def can_submit(self, principal):
 
591
    def can_submit(self, principal, user):
498
592
        return (self in principal.get_projects() and
499
 
                self.deadline > datetime.datetime.now())
 
593
                not self.has_deadline_passed(user))
500
594
 
501
595
    def submit(self, principal, path, revision, who):
502
596
        """Submit a Subversion path and revision to a project.
508
602
        @param who: The user who is actually making the submission.
509
603
        """
510
604
 
511
 
        if not self.can_submit(principal):
512
 
            raise Exception('cannot submit')
 
605
        if not self.can_submit(principal, who):
 
606
            raise DeadlinePassed()
513
607
 
514
608
        a = Assessed.get(Store.of(self), principal, self)
515
609
        ps = ProjectSubmission()
521
615
 
522
616
        return ps
523
617
 
524
 
    def get_permissions(self, user):
525
 
        return self.project_set.offering.get_permissions(user)
 
618
    def get_permissions(self, user, config):
 
619
        return self.project_set.offering.get_permissions(user, config)
526
620
 
527
621
    @property
528
622
    def latest_submissions(self):
537
631
            )
538
632
        )
539
633
 
 
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
 
540
647
 
541
648
class ProjectGroup(Storm):
542
649
    """A group of students working together on a project."""
593
700
            (not active_only) or (Semester.state == u'current'))
594
701
 
595
702
 
596
 
    def get_permissions(self, user):
 
703
    def get_permissions(self, user, config):
597
704
        if user.admin or user in self.members:
598
705
            return set(['submit_project'])
599
706
        else:
635
742
    project = Reference(project_id, Project.id)
636
743
 
637
744
    extensions = ReferenceSet(id, 'ProjectExtension.assessed_id')
638
 
    submissions = ReferenceSet(id, 'ProjectSubmission.assessed_id')
 
745
    submissions = ReferenceSet(
 
746
        id, 'ProjectSubmission.assessed_id', order_by='date_submitted')
639
747
 
640
748
    def __repr__(self):
641
749
        return "<%s %r in %r>" % (type(self).__name__,
650
758
    def principal(self):
651
759
        return self.project_group or self.user
652
760
 
 
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
 
653
772
    @classmethod
654
773
    def get(cls, store, principal, project):
655
774
        """Find or create an Assessed for the given user or group and project.
664
783
        a = store.find(cls,
665
784
            (t is User) or (cls.project_group_id == principal.id),
666
785
            (t is ProjectGroup) or (cls.user_id == principal.id),
667
 
            Project.id == project.id).one()
 
786
            cls.project_id == project.id).one()
668
787
 
669
788
        if a is None:
670
789
            a = cls()
715
834
    submitter = Reference(submitter_id, User.id)
716
835
    date_submitted = DateTime()
717
836
 
 
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)
718
850
 
719
851
# WORKSHEETS AND EXERCISES #
720
852
 
751
883
    def __repr__(self):
752
884
        return "<%s %s>" % (type(self).__name__, self.name)
753
885
 
754
 
    def get_permissions(self, user):
 
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."""
755
894
        perms = set()
756
895
        roles = set()
757
896
        if user is not None:
761
900
            elif u'lecturer' in set((e.role for e in user.active_enrolments)):
762
901
                perms.add('edit')
763
902
                perms.add('view')
764
 
            elif u'tutor' in set((e.role for e in user.active_enrolments)):
 
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
765
906
                perms.add('edit')
766
907
                perms.add('view')
767
908
 
828
969
        store.find(WorksheetExercise,
829
970
            WorksheetExercise.worksheet == self).remove()
830
971
 
831
 
    def get_permissions(self, user):
832
 
        return self.offering.get_permissions(user)
 
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
833
982
 
834
983
    def get_xml(self):
835
984
        """Returns the xml of this worksheet, converts from rst if required."""
880
1029
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
881
1030
                                  self.worksheet.identifier)
882
1031
 
883
 
    def get_permissions(self, user):
884
 
        return self.worksheet.get_permissions(user)
 
1032
    def get_permissions(self, user, config):
 
1033
        return self.worksheet.get_permissions(user, config)
885
1034
 
886
1035
 
887
1036
class ExerciseSave(Storm):
934
1083
    complete = Bool()
935
1084
    active = Bool()
936
1085
 
937
 
    def get_permissions(self, user):
 
1086
    def get_permissions(self, user, config):
938
1087
        return set(['view']) if user is self.user else set()
939
1088
 
940
1089
class TestSuite(Storm):
959
1108
 
960
1109
    def delete(self):
961
1110
        """Delete this suite, without asking questions."""
962
 
        for vaariable in self.variables:
 
1111
        for variable in self.variables:
963
1112
            variable.delete()
964
1113
        for test_case in self.test_cases:
965
1114
            test_case.delete()
978
1127
    suite = Reference(suiteid, "TestSuite.suiteid")
979
1128
    passmsg = Unicode()
980
1129
    failmsg = Unicode()
981
 
    test_default = Unicode()
 
1130
    test_default = Unicode() # Currently unused - only used for file matching.
982
1131
    seq_no = Int()
983
1132
 
984
1133
    parts = ReferenceSet(testid, "TestCasePart.testid")