~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-23 08:08:27 UTC
  • Revision ID: grantw@unimelb.edu.au-20100223080827-wklsx122pcw79wi7
Reject off-site non-GET requests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import hashlib
27
27
import datetime
28
28
import os
29
 
import urlparse
30
 
import urllib
31
29
 
32
30
from storm.locals import create_database, Store, Int, Unicode, DateTime, \
33
31
                         Reference, ReferenceSet, Bool, Storm, Desc
231
229
        """Find a user in a store by login name."""
232
230
        return store.find(cls, cls.login == unicode(login)).one()
233
231
 
234
 
    def get_svn_url(self, config):
235
 
        """Get the subversion repository URL for this user or group."""
236
 
        url = config['urls']['svn_addr']
237
 
        path = 'users/%s' % self.login
238
 
        return urlparse.urljoin(url, path)
239
 
 
240
232
    def get_permissions(self, user, config):
241
233
        """Determine privileges held by a user over this object.
242
234
 
333
325
    semester = Reference(semester_id, Semester.id)
334
326
    description = Unicode()
335
327
    url = Unicode()
336
 
    show_worksheet_marks = Bool()
337
 
    worksheet_cutoff = DateTime()
338
328
    groups_student_permissions = Unicode()
339
329
 
340
330
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
403
393
                perms.add('view_project_submissions')
404
394
                perms.add('admin_groups')
405
395
                perms.add('edit_worksheets')
406
 
                perms.add('view_worksheet_marks')
407
396
                perms.add('edit')           # Can edit projects & details
408
397
                perms.add('enrol')          # Can see enrolment screen at all
409
398
                perms.add('enrol_student')  # Can enrol students
437
426
        # XXX: Respect extensions.
438
427
        return self.projects.find(Project.deadline > datetime.datetime.now())
439
428
 
440
 
    def has_worksheet_cutoff_passed(self, user):
441
 
        """Check whether the worksheet cutoff has passed.
442
 
        A user is required, in case we support extensions.
443
 
        """
444
 
        if self.worksheet_cutoff is None:
445
 
            return False
446
 
        else:
447
 
            return self.worksheet_cutoff < datetime.datetime.now()
448
 
 
449
429
    def clone_worksheets(self, source):
450
430
        """Clone all worksheets from the specified source to this offering."""
451
431
        import ivle.worksheet.utils
455
435
            newws.identifier = worksheet.identifier
456
436
            newws.name = worksheet.name
457
437
            newws.assessable = worksheet.assessable
458
 
            newws.published = worksheet.published
459
438
            newws.data = worksheet.data
460
439
            newws.format = worksheet.format
461
440
            newws.offering = self
666
645
            return
667
646
        return assessed.submissions
668
647
 
669
 
    @property
670
 
    def can_delete(self):
671
 
        """Can only delete if there are no submissions."""
672
 
        return self.submissions.count() == 0
673
648
 
674
 
    def delete(self):
675
 
        """Delete the project. Fails if can_delete is False."""
676
 
        if not self.can_delete:
677
 
            raise IntegrityError()
678
 
        for assessed in self.assesseds:
679
 
            assessed.delete()
680
 
        Store.of(self).remove(self)
681
649
 
682
650
class ProjectGroup(Storm):
683
651
    """A group of students working together on a project."""
733
701
            Semester.id == Offering.semester_id,
734
702
            (not active_only) or (Semester.state == u'current'))
735
703
 
736
 
    def get_svn_url(self, config):
737
 
        """Get the subversion repository URL for this user or group."""
738
 
        url = config['urls']['svn_addr']
739
 
        path = 'groups/%s_%s_%s_%s' % (
740
 
                self.project_set.offering.subject.short_name,
741
 
                self.project_set.offering.semester.year,
742
 
                self.project_set.offering.semester.semester,
743
 
                self.name
744
 
                )
745
 
        return urlparse.urljoin(url, path)
746
704
 
747
705
    def get_permissions(self, user, config):
748
706
        if user.admin or user in self.members:
840
798
 
841
799
        return a
842
800
 
843
 
    def delete(self):
844
 
        """Delete the assessed. Fails if there are any submissions. Deletes
845
 
        extensions."""
846
 
        if self.submissions.count() > 0:
847
 
            raise IntegrityError()
848
 
        for extension in self.extensions:
849
 
            extension.delete()
850
 
        Store.of(self).remove(self)
851
801
 
852
802
class ProjectExtension(Storm):
853
803
    """An extension granted to a user or group on a particular project.
865
815
    approver = Reference(approver_id, User.id)
866
816
    notes = Unicode()
867
817
 
868
 
    def delete(self):
869
 
        """Delete the extension."""
870
 
        Store.of(self).remove(self)
871
 
 
872
818
class SubmissionError(Exception):
873
819
    """Denotes a validation error during submission."""
874
820
    pass
908
854
        return "/files/%s/%s/%s?r=%d" % (user.login,
909
855
            self.assessed.checkout_location, submitpath, self.revision)
910
856
 
911
 
    def get_svn_url(self, config):
912
 
        """Get subversion URL for this submission"""
913
 
        princ = self.assessed.principal
914
 
        base = princ.get_svn_url(config)
915
 
        if self.path.startswith(os.sep):
916
 
            return os.path.join(base,
917
 
                    urllib.quote(self.path[1:].encode('utf-8')))
918
 
        else:
919
 
            return os.path.join(base, urllib.quote(self.path.encode('utf-8')))
920
 
 
921
 
    def get_svn_export_command(self, req):
922
 
        """Returns a Unix shell command to export a submission"""
923
 
        svn_url = self.get_svn_url(req.config)
924
 
        username = (req.user.login if req.user.login.isalnum() else
925
 
                "'%s'"%req.user.login)
926
 
        export_dir = self.assessed.principal.short_name
927
 
        return "svn export --username %s -r%d '%s' %s"%(req.user.login,
928
 
                self.revision, svn_url, export_dir)
929
 
 
930
857
    @staticmethod
931
858
    def test_and_normalise_path(path):
932
859
        """Test that path is valid, and normalise it. This prevents possible
958
885
    id = Unicode(primary=True, name="identifier")
959
886
    name = Unicode()
960
887
    description = Unicode()
961
 
    _description_xhtml_cache = Unicode(name='description_xhtml_cache')
962
888
    partial = Unicode()
963
889
    solution = Unicode()
964
890
    include = Unicode()
1007
933
 
1008
934
        return perms
1009
935
 
1010
 
    def _cache_description_xhtml(self, invalidate=False):
1011
 
        # Don't regenerate an existing cache unless forced.
1012
 
        if self._description_xhtml_cache is not None and not invalidate:
1013
 
            return
1014
 
 
1015
 
        if self.description:
1016
 
            self._description_xhtml_cache = rst(self.description)
1017
 
        else:
1018
 
            self._description_xhtml_cache = None
1019
 
 
1020
 
    @property
1021
 
    def description_xhtml(self):
1022
 
        """The XHTML exercise description, converted from reStructuredText."""
1023
 
        self._cache_description_xhtml()
1024
 
        return self._description_xhtml_cache
1025
 
 
1026
 
    def set_description(self, description):
1027
 
        self.description = description
1028
 
        self._cache_description_xhtml(invalidate=True)
 
936
    def get_description(self):
 
937
        """Return the description interpreted as reStructuredText."""
 
938
        return rst(self.description)
1029
939
 
1030
940
    def delete(self):
1031
941
        """Deletes the exercise, providing it has no associated worksheets."""
1048
958
    identifier = Unicode()
1049
959
    name = Unicode()
1050
960
    assessable = Bool()
1051
 
    published = Bool()
1052
961
    data = Unicode()
1053
 
    _data_xhtml_cache = Unicode(name='data_xhtml_cache')
1054
962
    seq_no = Int()
1055
963
    format = Unicode()
1056
964
 
1087
995
            WorksheetExercise.worksheet == self).remove()
1088
996
 
1089
997
    def get_permissions(self, user, config):
1090
 
        offering_perms = self.offering.get_permissions(user, config)
1091
 
 
1092
 
        perms = set()
1093
 
 
1094
 
        # Anybody who can view an offering can view a published
1095
 
        # worksheet.
1096
 
        if 'view' in offering_perms and self.published:
1097
 
            perms.add('view')
1098
 
 
1099
 
        # Any worksheet editors can both view and edit.
1100
 
        if 'edit_worksheets' in offering_perms:
1101
 
            perms.add('view')
 
998
        # Almost the same permissions as for the offering itself
 
999
        perms = self.offering.get_permissions(user, config)
 
1000
        # However, "edit" permission is derived from the "edit_worksheets"
 
1001
        # permission of the offering
 
1002
        if 'edit_worksheets' in perms:
1102
1003
            perms.add('edit')
1103
 
 
 
1004
        else:
 
1005
            perms.discard('edit')
1104
1006
        return perms
1105
1007
 
1106
 
    def _cache_data_xhtml(self, invalidate=False):
1107
 
        # Don't regenerate an existing cache unless forced.
1108
 
        if self._data_xhtml_cache is not None and not invalidate:
1109
 
            return
1110
 
 
1111
 
        if self.format == u'rst':
1112
 
            self._data_xhtml_cache = rst(self.data)
1113
 
        else:
1114
 
            self._data_xhtml_cache = None
1115
 
 
1116
 
    @property
1117
 
    def data_xhtml(self):
1118
 
        """The XHTML of this worksheet, converted from rST if required."""
1119
 
        # Update the rST -> XHTML cache, if required.
1120
 
        self._cache_data_xhtml()
1121
 
 
1122
 
        if self.format == u'rst':
1123
 
            return self._data_xhtml_cache
 
1008
    def get_xml(self):
 
1009
        """Returns the xml of this worksheet, converts from rst if required."""
 
1010
        if self.format == u'rst':
 
1011
            ws_xml = rst(self.data)
 
1012
            return ws_xml
1124
1013
        else:
1125
1014
            return self.data
1126
1015
 
1127
 
    def set_data(self, data):
1128
 
        self.data = data
1129
 
        self._cache_data_xhtml(invalidate=True)
1130
 
 
1131
1016
    def delete(self):
1132
1017
        """Deletes the worksheet, provided it has no attempts on any exercises.
1133
1018
 
1195
1080
 
1196
1081
    def __repr__(self):
1197
1082
        return "<%s %s by %s at %s>" % (type(self).__name__,
1198
 
            self.worksheet_exercise.exercise.name, self.user.login,
1199
 
            self.date.strftime("%c"))
 
1083
            self.exercise.name, self.user.login, self.date.strftime("%c"))
1200
1084
 
1201
1085
class ExerciseAttempt(ExerciseSave):
1202
1086
    """An attempt at solving an exercise.