~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-11 12:17:37 UTC
  • Revision ID: grantw@unimelb.edu.au-20100211121737-1zsmpp8i8rbyliku
Add a subject listing with new/edit icons.

Show diffs side-by-side

added added

removed removed

Lines of Context:
234
234
        they may do everything. Otherwise they may do nothing.
235
235
        """
236
236
        if user and user.admin or user is self:
237
 
            return set(['view', 'edit', 'submit_project'])
 
237
            return set(['view_public', 'view', 'edit', 'submit_project'])
238
238
        else:
239
 
            return set()
 
239
            return set(['view_public'])
240
240
 
241
241
# SUBJECTS AND ENROLMENTS #
242
242
 
249
249
    code = Unicode(name="subj_code")
250
250
    name = Unicode(name="subj_name")
251
251
    short_name = Unicode(name="subj_short_name")
252
 
    url = Unicode()
253
252
 
254
253
    offerings = ReferenceSet(id, 'Offering.subject_id')
255
254
 
322
321
    subject = Reference(subject_id, Subject.id)
323
322
    semester_id = Int(name="semesterid")
324
323
    semester = Reference(semester_id, Semester.id)
 
324
    description = Unicode()
 
325
    url = Unicode()
325
326
    groups_student_permissions = Unicode()
326
327
 
327
328
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
330
331
                           'Enrolment.user_id',
331
332
                           'User.id')
332
333
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
 
334
    projects = ReferenceSet(id,
 
335
                            'ProjectSet.offering_id',
 
336
                            'ProjectSet.id',
 
337
                            'Project.project_set_id')
333
338
 
334
339
    worksheets = ReferenceSet(id, 
335
340
        'Worksheet.offering_id', 
375
380
            if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
376
381
               or user.admin:
377
382
                perms.add('edit')
 
383
                # XXX Bug #493945 -- should tutors have these permissions?
 
384
                # Potentially move into the next category (lecturer & admin)
 
385
                perms.add('enrol')          # Can see enrolment screen at all
 
386
                perms.add('enrol_student')  # Can enrol students
 
387
            if (enrolment and enrolment.role in (u'lecturer')) or user.admin:
 
388
                perms.add('enrol_tutor')    # Can enrol tutors
 
389
            if user.admin:
 
390
                perms.add('enrol_lecturer') # Can enrol lecturers
378
391
        return perms
379
392
 
380
393
    def get_enrolment(self, user):
391
404
                Enrolment.user_id == User.id,
392
405
                Enrolment.offering_id == self.id,
393
406
                Enrolment.role == role
394
 
                )
 
407
                ).order_by(User.login)
395
408
 
396
409
    @property
397
410
    def students(self):
398
411
        return self.get_members_by_role(u'student')
399
412
 
 
413
    def get_open_projects_for_user(self, user):
 
414
        """Find all projects currently open to submissions by a user."""
 
415
        # XXX: Respect extensions.
 
416
        return self.projects.find(Project.deadline > datetime.datetime.now())
 
417
 
400
418
class Enrolment(Storm):
401
419
    """An enrolment of a user in an offering.
402
420
 
456
474
    def get_permissions(self, user):
457
475
        return self.offering.get_permissions(user)
458
476
 
 
477
    def get_groups_for_user(self, user):
 
478
        """List all groups in this offering of which the user is a member."""
 
479
        assert self.is_group
 
480
        return Store.of(self).find(
 
481
            ProjectGroup,
 
482
            ProjectGroupMembership.user_id == user.id,
 
483
            ProjectGroupMembership.project_group_id == ProjectGroup.id,
 
484
            ProjectGroup.project_set_id == self.id)
 
485
 
 
486
    def get_submission_principal(self, user):
 
487
        """Get the principal on behalf of which the user can submit.
 
488
 
 
489
        If this is a solo project set, the given user is returned. If
 
490
        the user is a member of exactly one group, all the group is
 
491
        returned. Otherwise, None is returned.
 
492
        """
 
493
        if self.is_group:
 
494
            groups = self.get_groups_for_user(user)
 
495
            if groups.count() == 1:
 
496
                return groups.one()
 
497
            else:
 
498
                return None
 
499
        else:
 
500
            return user
 
501
 
 
502
    @property
 
503
    def is_group(self):
 
504
        return self.max_students_per_group is not None
 
505
 
459
506
    @property
460
507
    def assigned(self):
461
508
        """Get the entities (groups or users) assigned to submit this project.
463
510
        This will be a Storm ResultSet.
464
511
        """
465
512
        #If its a solo project, return everyone in offering
466
 
        if self.max_students_per_group is None:
 
513
        if self.is_group:
 
514
            return self.project_groups
 
515
        else:
467
516
            return self.offering.students
468
 
        else:
469
 
            return self.project_groups
 
517
 
 
518
class DeadlinePassed(Exception):
 
519
    """An exception indicating that a project cannot be submitted because the
 
520
    deadline has passed."""
 
521
    def __init__(self):
 
522
        pass
 
523
    def __str__(self):
 
524
        return "The project deadline has passed"
470
525
 
471
526
class Project(Storm):
472
527
    """A student project for which submissions can be made."""
494
549
        return "<%s '%s' in %r>" % (type(self).__name__, self.short_name,
495
550
                                  self.project_set.offering)
496
551
 
497
 
    def can_submit(self, principal):
 
552
    def can_submit(self, principal, user):
498
553
        return (self in principal.get_projects() and
499
 
                self.deadline > datetime.datetime.now())
 
554
                not self.has_deadline_passed(user))
500
555
 
501
556
    def submit(self, principal, path, revision, who):
502
557
        """Submit a Subversion path and revision to a project.
508
563
        @param who: The user who is actually making the submission.
509
564
        """
510
565
 
511
 
        if not self.can_submit(principal):
512
 
            raise Exception('cannot submit')
 
566
        if not self.can_submit(principal, who):
 
567
            raise DeadlinePassed()
513
568
 
514
569
        a = Assessed.get(Store.of(self), principal, self)
515
570
        ps = ProjectSubmission()
537
592
            )
538
593
        )
539
594
 
 
595
    def has_deadline_passed(self, user):
 
596
        """Check whether the deadline has passed."""
 
597
        # XXX: Need to respect extensions.
 
598
        return self.deadline < datetime.datetime.now()
 
599
 
 
600
    def get_submissions_for_principal(self, principal):
 
601
        """Fetch a ResultSet of all submissions by a particular principal."""
 
602
        assessed = Assessed.get(Store.of(self), principal, self)
 
603
        if assessed is None:
 
604
            return
 
605
        return assessed.submissions
 
606
 
 
607
 
540
608
 
541
609
class ProjectGroup(Storm):
542
610
    """A group of students working together on a project."""
635
703
    project = Reference(project_id, Project.id)
636
704
 
637
705
    extensions = ReferenceSet(id, 'ProjectExtension.assessed_id')
638
 
    submissions = ReferenceSet(id, 'ProjectSubmission.assessed_id')
 
706
    submissions = ReferenceSet(
 
707
        id, 'ProjectSubmission.assessed_id', order_by='date_submitted')
639
708
 
640
709
    def __repr__(self):
641
710
        return "<%s %r in %r>" % (type(self).__name__,
650
719
    def principal(self):
651
720
        return self.project_group or self.user
652
721
 
 
722
    @property
 
723
    def checkout_location(self):
 
724
        """Returns the location of the Subversion workspace for this piece of
 
725
        assessment, relative to each group member's home directory."""
 
726
        subjectname = self.project.project_set.offering.subject.short_name
 
727
        if self.is_group:
 
728
            checkout_dir_name = self.principal.short_name
 
729
        else:
 
730
            checkout_dir_name = "mywork"
 
731
        return subjectname + "/" + checkout_dir_name
 
732
 
653
733
    @classmethod
654
734
    def get(cls, store, principal, project):
655
735
        """Find or create an Assessed for the given user or group and project.
664
744
        a = store.find(cls,
665
745
            (t is User) or (cls.project_group_id == principal.id),
666
746
            (t is ProjectGroup) or (cls.user_id == principal.id),
667
 
            Project.id == project.id).one()
 
747
            cls.project_id == project.id).one()
668
748
 
669
749
        if a is None:
670
750
            a = cls()
715
795
    submitter = Reference(submitter_id, User.id)
716
796
    date_submitted = DateTime()
717
797
 
 
798
    def get_verify_url(self, user):
 
799
        """Get the URL for verifying this submission, within the account of
 
800
        the given user."""
 
801
        # If this is a solo project, then self.path will be prefixed with the
 
802
        # subject name. Remove the first path segment.
 
803
        submitpath = self.path[1:] if self.path[:1] == '/' else self.path
 
804
        if not self.assessed.is_group:
 
805
            if '/' in submitpath:
 
806
                submitpath = submitpath.split('/', 1)[1]
 
807
            else:
 
808
                submitpath = ''
 
809
        return "/files/%s/%s/%s?r=%d" % (user.login,
 
810
            self.assessed.checkout_location, submitpath, self.revision)
718
811
 
719
812
# WORKSHEETS AND EXERCISES #
720
813
 
959
1052
 
960
1053
    def delete(self):
961
1054
        """Delete this suite, without asking questions."""
962
 
        for vaariable in self.variables:
 
1055
        for variable in self.variables:
963
1056
            variable.delete()
964
1057
        for test_case in self.test_cases:
965
1058
            test_case.delete()
978
1071
    suite = Reference(suiteid, "TestSuite.suiteid")
979
1072
    passmsg = Unicode()
980
1073
    failmsg = Unicode()
981
 
    test_default = Unicode()
 
1074
    test_default = Unicode() # Currently unused - only used for file matching.
982
1075
    seq_no = Int()
983
1076
 
984
1077
    parts = ReferenceSet(testid, "TestCasePart.testid")