~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-18 03:31:47 UTC
  • Revision ID: grantw@unimelb.edu.au-20100218033147-z1es9tzrx7eg85gu
Ensure that we always close the DB connection at request termination, even in the case of an exception.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
 
26
26
import hashlib
27
27
import datetime
28
 
import os
29
28
 
30
29
from storm.locals import create_database, Store, Int, Unicode, DateTime, \
31
30
                         Reference, ReferenceSet, Bool, Storm, Desc
325
324
    semester = Reference(semester_id, Semester.id)
326
325
    description = Unicode()
327
326
    url = Unicode()
328
 
    show_worksheet_marks = Bool()
329
 
    worksheet_cutoff = DateTime()
330
327
    groups_student_permissions = Unicode()
331
328
 
332
329
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
395
392
                perms.add('view_project_submissions')
396
393
                perms.add('admin_groups')
397
394
                perms.add('edit_worksheets')
398
 
                perms.add('view_worksheet_marks')
399
395
                perms.add('edit')           # Can edit projects & details
400
396
                perms.add('enrol')          # Can see enrolment screen at all
401
397
                perms.add('enrol_student')  # Can enrol students
438
434
            newws.identifier = worksheet.identifier
439
435
            newws.name = worksheet.name
440
436
            newws.assessable = worksheet.assessable
441
 
            newws.published = worksheet.published
442
437
            newws.data = worksheet.data
443
438
            newws.format = worksheet.format
444
439
            newws.offering = self
612
607
 
613
608
        a = Assessed.get(Store.of(self), principal, self)
614
609
        ps = ProjectSubmission()
615
 
        # Raise SubmissionError if the path is illegal
616
 
        ps.path = ProjectSubmission.test_and_normalise_path(path)
 
610
        ps.path = path
617
611
        ps.revision = revision
618
612
        ps.date_submitted = datetime.datetime.now()
619
613
        ps.assessed = a
819
813
    approver = Reference(approver_id, User.id)
820
814
    notes = Unicode()
821
815
 
822
 
class SubmissionError(Exception):
823
 
    """Denotes a validation error during submission."""
824
 
    pass
825
 
 
826
816
class ProjectSubmission(Storm):
827
817
    """A submission from a user or group repository to a particular project.
828
818
 
858
848
        return "/files/%s/%s/%s?r=%d" % (user.login,
859
849
            self.assessed.checkout_location, submitpath, self.revision)
860
850
 
861
 
    @staticmethod
862
 
    def test_and_normalise_path(path):
863
 
        """Test that path is valid, and normalise it. This prevents possible
864
 
        injections using malicious paths.
865
 
        Returns the updated path, if successful.
866
 
        Raises SubmissionError if invalid.
867
 
        """
868
 
        # Ensure the path is absolute to prevent being tacked onto working
869
 
        # directories.
870
 
        # Prevent '\n' because it will break all sorts of things.
871
 
        # Prevent '[' and ']' because they can be used to inject into the
872
 
        # svn.conf.
873
 
        # Normalise to avoid resulting in ".." path segments.
874
 
        if not os.path.isabs(path):
875
 
            raise SubmissionError("Path is not absolute")
876
 
        if any(c in path for c in "\n[]"):
877
 
            raise SubmissionError("Path must not contain '\\n', '[' or ']'")
878
 
        return os.path.normpath(path)
879
 
 
880
851
# WORKSHEETS AND EXERCISES #
881
852
 
882
853
class Exercise(Storm):
889
860
    id = Unicode(primary=True, name="identifier")
890
861
    name = Unicode()
891
862
    description = Unicode()
892
 
    _description_xhtml_cache = Unicode(name='description_xhtml_cache')
893
863
    partial = Unicode()
894
864
    solution = Unicode()
895
865
    include = Unicode()
938
908
 
939
909
        return perms
940
910
 
941
 
    def _cache_description_xhtml(self, invalidate=False):
942
 
        # Don't regenerate an existing cache unless forced.
943
 
        if self._description_xhtml_cache is not None and not invalidate:
944
 
            return
945
 
 
946
 
        if self.description:
947
 
            self._description_xhtml_cache = rst(self.description)
948
 
        else:
949
 
            self._description_xhtml_cache = None
950
 
 
951
 
    @property
952
 
    def description_xhtml(self):
953
 
        """The XHTML exercise description, converted from reStructuredText."""
954
 
        self._cache_description_xhtml()
955
 
        return self._description_xhtml_cache
956
 
 
957
 
    def set_description(self, description):
958
 
        self.description = description
959
 
        self._cache_description_xhtml(invalidate=True)
 
911
    def get_description(self):
 
912
        """Return the description interpreted as reStructuredText."""
 
913
        return rst(self.description)
960
914
 
961
915
    def delete(self):
962
916
        """Deletes the exercise, providing it has no associated worksheets."""
979
933
    identifier = Unicode()
980
934
    name = Unicode()
981
935
    assessable = Bool()
982
 
    published = Bool()
983
936
    data = Unicode()
984
 
    _data_xhtml_cache = Unicode(name='data_xhtml_cache')
985
937
    seq_no = Int()
986
938
    format = Unicode()
987
939
 
1018
970
            WorksheetExercise.worksheet == self).remove()
1019
971
 
1020
972
    def get_permissions(self, user, config):
1021
 
        offering_perms = self.offering.get_permissions(user, config)
1022
 
 
1023
 
        perms = set()
1024
 
 
1025
 
        # Anybody who can view an offering can view a published
1026
 
        # worksheet.
1027
 
        if 'view' in offering_perms and self.published:
1028
 
            perms.add('view')
1029
 
 
1030
 
        # Any worksheet editors can both view and edit.
1031
 
        if 'edit_worksheets' in offering_perms:
1032
 
            perms.add('view')
 
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:
1033
978
            perms.add('edit')
1034
 
 
 
979
        else:
 
980
            perms.discard('edit')
1035
981
        return perms
1036
982
 
1037
 
    def _cache_data_xhtml(self, invalidate=False):
1038
 
        # Don't regenerate an existing cache unless forced.
1039
 
        if self._data_xhtml_cache is not None and not invalidate:
1040
 
            return
1041
 
 
1042
 
        if self.format == u'rst':
1043
 
            self._data_xhtml_cache = rst(self.data)
1044
 
        else:
1045
 
            self._data_xhtml_cache = None
1046
 
 
1047
 
    @property
1048
 
    def data_xhtml(self):
1049
 
        """The XHTML of this worksheet, converted from rST if required."""
1050
 
        # Update the rST -> XHTML cache, if required.
1051
 
        self._cache_data_xhtml()
1052
 
 
1053
 
        if self.format == u'rst':
1054
 
            return self._data_xhtml_cache
 
983
    def get_xml(self):
 
984
        """Returns the xml of this worksheet, converts from rst if required."""
 
985
        if self.format == u'rst':
 
986
            ws_xml = rst(self.data)
 
987
            return ws_xml
1055
988
        else:
1056
989
            return self.data
1057
990
 
1058
 
    def set_data(self, data):
1059
 
        self.data = data
1060
 
        self._cache_data_xhtml(invalidate=True)
1061
 
 
1062
991
    def delete(self):
1063
992
        """Deletes the worksheet, provided it has no attempts on any exercises.
1064
993
 
1126
1055
 
1127
1056
    def __repr__(self):
1128
1057
        return "<%s %s by %s at %s>" % (type(self).__name__,
1129
 
            self.worksheet_exercise.exercise.name, self.user.login,
1130
 
            self.date.strftime("%c"))
 
1058
            self.exercise.name, self.user.login, self.date.strftime("%c"))
1131
1059
 
1132
1060
class ExerciseAttempt(ExerciseSave):
1133
1061
    """An attempt at solving an exercise.