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

« back to all changes in this revision

Viewing changes to lib/common/db.py

  • Committer: wagrant
  • Date: 2008-12-23 01:58:44 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1063
Set PATH_INFO and PATH_TRANSLATED properly for student CGI scripts.

If the requested path doesn't exist, interpretservice now traverses up the
directory tree until it finds a prefix of the path that does exist. If we are
allowed to execute that path, we do so, giving it the trimmed bits in
PATH_INFO. If execution is not permissible, we 404 anyway.

Fixes [ 2094777 ] Serve: Allow path after CGI script

Show diffs side-by-side

added added

removed removed

Lines of Context:
187
187
        if dry: return query
188
188
        self.db.query(query)
189
189
 
 
190
    def return_insert(self, dict, tablename, tablefields, returning,
 
191
        disallowed=frozenset([]), dry=False):
 
192
        """Inserts a new row in a table, using data from a supplied
 
193
        dictionary (which will be checked by check_dict) and returns certain 
 
194
        fields as a dict.
 
195
        dict: Dictionary mapping column names to values. The values may be
 
196
            any of the following types:
 
197
            str, int, long, float, NoneType.
 
198
        tablename: String, name of the table to insert into. Will NOT be
 
199
            escaped - must be a valid identifier.
 
200
        returning: List of fields to return, not escaped
 
201
        tablefields, disallowed: see check_dict.
 
202
        dry: Returns the SQL query as a string, and does not execute it.
 
203
        Raises a DBException if the dictionary contains invalid fields.
 
204
        """
 
205
        if not DB.check_dict(dict, tablefields, disallowed):
 
206
            extras = set(dict.keys()) - tablefields
 
207
            raise DBException("Supplied dictionary contains invalid fields. (%s)" % (repr(extras)))
 
208
        # Build two lists concurrently: field names and values, as SQL strings
 
209
        fieldnames = []
 
210
        values = []
 
211
        for k,v in dict.items():
 
212
            fieldnames.append(k)
 
213
            values.append(_escape(v))
 
214
        if len(fieldnames) == 0: return
 
215
        fieldnames = ', '.join(fieldnames)
 
216
        values = ', '.join(values)
 
217
        returns = ', '.join(returning)
 
218
        query = ("INSERT INTO %s (%s) VALUES (%s) RETURNING (%s);"
 
219
            % (tablename, fieldnames, values, returns))
 
220
        if dry: return query
 
221
        return self.db.query(query)
 
222
 
 
223
 
190
224
    def update(self, primarydict, updatedict, tablename, tablefields,
191
225
        primary_keys, disallowed_update=frozenset([]), dry=False):
192
226
        """Updates a row in a table, matching against primarydict to find the
569
603
    UNION
570
604
        (SELECT problemid, loginid, date, text FROM problem_attempt
571
605
         AS problem_attempt (problemid, loginid, date, text)
572
 
         WHERE loginid = %d AND problemid = %d)
 
606
         WHERE loginid = %d AND problemid = %d AND active)
573
607
    )
574
608
    AS _
575
609
    ORDER BY date DESC
582
616
        else:
583
617
            return None
584
618
 
 
619
    def get_problem_attempts(self, login, exercisename, allow_inactive=True,
 
620
                             dry=False):
 
621
        """Given a login name and exercise name, returns a list of dicts, one
 
622
        for each attempt made for that exercise.
 
623
        Dicts are {'date': 'formatted_time', 'complete': bool}.
 
624
        Ordered with the newest first.
 
625
        
 
626
        Note: By default, returns de-activated problem attempts (unlike
 
627
        get_problem_stored_text).
 
628
        If allow_inactive is False, will not return disabled attempts.
 
629
 
 
630
        Note: Even if dry, will still physically call get_problem_problemid,
 
631
        which may mutate the DB, and get_user_loginid, which may fail.
 
632
        """
 
633
        problemid = self.get_problem_problemid(exercisename)
 
634
        loginid = self.get_user_loginid(login)  # May raise a DBException
 
635
        andactive = '' if allow_inactive else ' AND active'
 
636
        query = """SELECT date, complete FROM problem_attempt
 
637
    WHERE loginid = %d AND problemid = %d%s
 
638
    ORDER BY date DESC;""" % (loginid, problemid, andactive)
 
639
        if dry: return query
 
640
        result = self.db.query(query).getresult()
 
641
        # Make into dicts (could use dictresult, but want to convert values)
 
642
        return [{'date': date, 'complete': _parse_boolean(complete)}
 
643
                for date, complete in result]
 
644
 
 
645
    def get_problem_attempt(self, login, exercisename, as_of,
 
646
        allow_inactive=True, dry=False):
 
647
        """Given a login name, exercise name, and struct_time, returns the
 
648
        text of the submitted attempt for this question as of that date.
 
649
        Returns None if the user had not made an attempt on this problem at
 
650
        that date.
 
651
        
 
652
        Note: By default, returns de-activated problem attempts (unlike
 
653
        get_problem_stored_text).
 
654
        If allow_inactive is False, will not return disabled attempts.
 
655
 
 
656
        Note: Even if dry, will still physically call get_problem_problemid,
 
657
        which may mutate the DB, and get_user_loginid, which may fail.
 
658
        """
 
659
        problemid = self.get_problem_problemid(exercisename)
 
660
        loginid = self.get_user_loginid(login)  # May raise a DBException
 
661
        # Very similar to query in get_problem_stored_text, but without
 
662
        # looking in problem_save, and restricting to a certain date.
 
663
        andactive = '' if allow_inactive else ' AND active'
 
664
        query = """SELECT attempt FROM problem_attempt
 
665
    WHERE loginid = %d AND problemid = %d%s AND date <= %s
 
666
    ORDER BY date DESC
 
667
    LIMIT 1;""" % (loginid, problemid, andactive, _escape(as_of))
 
668
        if dry: return query
 
669
        result = self.db.query(query)
 
670
        if result.ntuples() == 1:
 
671
            # The user has made at least 1 attempt. Return the newest.
 
672
            return result.getresult()[0][0]
 
673
        else:
 
674
            return None
 
675
 
585
676
    def get_problem_status(self, login, exercisename, dry=False):
586
677
        """Given a login name and exercise name, returns information about the
587
678
        user's performance on that problem.
606
697
        # Will return an empty table if the problem has never been
607
698
        # successfully completed.
608
699
        query = """SELECT COUNT(*) FROM problem_attempt
609
 
    WHERE loginid = %d AND problemid = %d AND date <=
 
700
    WHERE loginid = %d AND problemid = %d AND active AND date <=
610
701
        (SELECT date FROM problem_attempt
611
 
            WHERE loginid = %d AND problemid = %d AND complete = TRUE
 
702
            WHERE loginid = %d AND problemid = %d AND complete AND active
612
703
            ORDER BY date ASC
613
704
            LIMIT 1);""" % (loginid, problemid, loginid, problemid)
614
705
        if dry: return query
624
715
            # completed.
625
716
            # Return the total number of attempts, and False for success.
626
717
            query = """SELECT COUNT(*) FROM problem_attempt
627
 
    WHERE loginid = %d AND problemid = %d;""" % (loginid, problemid)
 
718
    WHERE loginid = %d AND problemid = %d AND active;""" % (loginid, problemid)
628
719
            result = self.db.query(query)
629
720
            count = int(result.getresult()[0][0])
630
721
            return (False, count)
810
901
            raise
811
902
        return mand_done, mand_total, opt_done, opt_total
812
903
 
 
904
    # ENROLMENT INFORMATION
 
905
 
813
906
    def add_enrolment(self, login, subj_code, semester, year=None, dry=False):
814
907
        """
815
908
        Enrol a student in the given offering of a subject.
829
922
    VALUES (
830
923
        (SELECT loginid FROM login WHERE login=%s),
831
924
        (SELECT offeringid
832
 
            FROM (offering INNER JOIN subject
833
 
                ON subject.subjectid = offering.subject)
834
 
            WHERE subj_code=%s AND semester=%s AND year=%s)
 
925
            FROM offering, subject, semester
 
926
                WHERE subject.subjectid = offering.subject
 
927
                AND semester.semesterid = offering.semesterid
 
928
                AND subj_code=%s AND semester=%s AND year=%s)
835
929
        );""" % (_escape(login), _escape(subj_code), _escape(semester),
836
930
                 _escape(year))
837
931
        if dry:
842
936
            return False
843
937
        return True
844
938
 
 
939
    # SUBJECTS AND ENROLEMENT
 
940
 
 
941
    def get_subjects(self, dry=False):
 
942
        """
 
943
        Get all subjects in IVLE.
 
944
        Returns a list of dicts (all values strings), with the keys:
 
945
        subj_code, subj_name, subj_short_name, url
 
946
        """
 
947
        return self.get_all("subject",
 
948
            ("subjectid", "subj_code", "subj_name", "subj_short_name", "url"),
 
949
            dry)
 
950
 
 
951
    def get_offering_semesters(self, subjectid, dry=False):
 
952
        """
 
953
        Get the semester information for a subject as well as providing 
 
954
        information about if the subject is active and which semester it is in.
 
955
        """
 
956
        query = """\
 
957
SELECT offeringid, subj_name, year, semester, active
 
958
FROM semester, offering, subject
 
959
WHERE offering.semesterid = semester.semesterid AND
 
960
    offering.subject = subject.subjectid AND
 
961
    offering.subject = %d;"""%subjectid
 
962
        if dry:
 
963
            return query
 
964
        results = self.db.query(query).dictresult()
 
965
        # Parse boolean varibles
 
966
        for result in results:
 
967
            result['active'] = _parse_boolean(result['active'])
 
968
        return results
 
969
 
 
970
    def get_offering_members(self, offeringid, dry=False):
 
971
        """
 
972
        Gets the logins of all the people enroled in an offering
 
973
        """
 
974
        query = """\
 
975
SELECT login.login AS login, login.fullname AS fullname
 
976
FROM login, enrolment
 
977
WHERE login.loginid = enrolment.loginid AND
 
978
    enrolment.offeringid = %d
 
979
    ORDER BY login.login;"""%offeringid
 
980
        if dry:
 
981
            return query
 
982
        return self.db.query(query).dictresult()
 
983
 
 
984
 
 
985
    def get_enrolment(self, login, dry=False):
 
986
        """
 
987
        Get all offerings (in IVLE) the student is enrolled in.
 
988
        Returns a list of dicts (all values strings), with the keys:
 
989
        offeringid, subj_code, subj_name, subj_short_name, year, semester, url
 
990
        """
 
991
        query = """\
 
992
SELECT offering.offeringid, subj_code, subj_name, subj_short_name,
 
993
       semester.year, semester.semester, subject.url
 
994
FROM login, enrolment, offering, subject, semester
 
995
WHERE enrolment.offeringid=offering.offeringid
 
996
  AND login.loginid=enrolment.loginid
 
997
  AND offering.subject=subject.subjectid
 
998
  AND semester.semesterid=offering.semesterid
 
999
  AND enrolment.active
 
1000
  AND login=%s;""" % _escape(login)
 
1001
        if dry:
 
1002
            return query
 
1003
        return self.db.query(query).dictresult()
 
1004
 
 
1005
    def get_enrolment_groups(self, login, offeringid, dry=False):
 
1006
        """
 
1007
        Get all groups the user is member of in the given offering.
 
1008
        Returns a list of dicts (all values strings), with the keys:
 
1009
        name, nick
 
1010
        """
 
1011
        query = """\
 
1012
SELECT project_group.groupnm as name, project_group.nick as nick
 
1013
FROM project_set, project_group, group_member, login
 
1014
WHERE login.login=%s
 
1015
  AND project_set.offeringid=%s
 
1016
  AND group_member.loginid=login.loginid
 
1017
  AND project_group.groupid=group_member.groupid
 
1018
  AND project_group.projectsetid=project_set.projectsetid
 
1019
""" % (_escape(login), _escape(offeringid))
 
1020
        if dry:
 
1021
            return query
 
1022
        return self.db.query(query).dictresult()
 
1023
 
 
1024
    def get_subjects_status(self, login, dry=False):
 
1025
        """
 
1026
        Get all subjects in IVLE, split into lists of enrolled and unenrolled
 
1027
        subjects.
 
1028
        Returns a tuple of lists (enrolled, unenrolled) of dicts
 
1029
        (all values strings) with the keys:
 
1030
        subj_code, subj_name, subj_short_name, url
 
1031
        """
 
1032
        enrolments = self.get_enrolment(login)
 
1033
        all_subjects = self.get_subjects()
 
1034
 
 
1035
        enrolled_set = set(x['subj_code'] for x in enrolments)
 
1036
 
 
1037
        enrolled_subjects = [x for x in all_subjects
 
1038
                             if x['subj_code'] in enrolled_set]
 
1039
        unenrolled_subjects = [x for x in all_subjects
 
1040
                               if x['subj_code'] not in enrolled_set]
 
1041
        enrolled_subjects.sort(key=lambda x: x['subj_code'])
 
1042
        unenrolled_subjects.sort(key=lambda x: x['subj_code'])
 
1043
        return (enrolled_subjects, unenrolled_subjects)
 
1044
 
 
1045
 
 
1046
    # PROJECT GROUPS
 
1047
    def get_groups_by_user(self, login, offeringid=None, dry=False):
 
1048
        """
 
1049
        Get all project groups the student is in, corresponding to a
 
1050
        particular subject offering (or all offerings, if omitted).
 
1051
        Returns a list of tuples:
 
1052
        (int groupid, str groupnm, str group_nick, bool is_member).
 
1053
        (Note: If is_member is false, it means they have just been invited to
 
1054
        this group, not a member).
 
1055
        """
 
1056
        if offeringid is None:
 
1057
            and_offering = ""
 
1058
        else:
 
1059
            and_projectset_table = ", project_set"
 
1060
            and_offering = """
 
1061
AND project_group.projectsetid = project_set.projectsetid
 
1062
AND project_set.offeringid = %s""" % _escape(offeringid)
 
1063
        # Union both the groups this user is a member of, and the groups this
 
1064
        # user is invited to.
 
1065
        query = """\
 
1066
    SELECT project_group.groupid, groupnm, project_group.nick, True
 
1067
    FROM project_group, group_member, login %(and_projectset_table)s
 
1068
    WHERE project_group.groupid = group_member.groupid
 
1069
      AND group_member.loginid = login.loginid
 
1070
      AND login = %(login)s
 
1071
      %(and_offering)s
 
1072
UNION
 
1073
    SELECT project_group.groupid, groupnm, project_group.nick, False
 
1074
    FROM project_group, group_invitation, login %(and_projectset_table)s
 
1075
    WHERE project_group.groupid = group_invitation.groupid
 
1076
      AND group_invitation.loginid = login.loginid
 
1077
      AND login = %(login)s
 
1078
      %(and_offering)s
 
1079
;""" % {"login": _escape(login), "and_offering": and_offering,
 
1080
        "and_projectset_table": and_projectset_table}
 
1081
        if dry:
 
1082
            return query
 
1083
        # Convert 't' -> True, 'f' -> False
 
1084
        return [(groupid, groupnm, nick, ismember == 't')
 
1085
                for groupid, groupnm, nick, ismember
 
1086
                in self.db.query(query).getresult()]
 
1087
 
 
1088
    def get_offering_info(self, projectsetid, dry=False):
 
1089
        """Takes information from projectset and returns useful information 
 
1090
        about the subject and semester. Returns as a dictionary.
 
1091
        """
 
1092
        query = """\
 
1093
SELECT subjectid, subj_code, subj_name, subj_short_name, url, year, semester, 
 
1094
active
 
1095
FROM subject, offering, semester, project_set
 
1096
WHERE offering.subject = subject.subjectid AND
 
1097
    offering.semesterid = semester.semesterid AND
 
1098
    project_set.offeringid = offering.offeringid AND
 
1099
    project_set.projectsetid = %d;"""%projectsetid
 
1100
        if dry:
 
1101
            return query
 
1102
        return self.db.query(query).dictresult()[0]
 
1103
 
 
1104
    def get_projectgroup_members(self, groupid, dry=False):
 
1105
        """Returns the logins of all students in a project group
 
1106
        """
 
1107
        query = """\
 
1108
SELECT login.login as login, login.fullname as fullname
 
1109
FROM login, group_member
 
1110
WHERE login.loginid = group_member.loginid AND
 
1111
    group_member.groupid = %d
 
1112
ORDER BY login.login;"""%groupid
 
1113
        if dry:
 
1114
            return query
 
1115
        return self.db.query(query).dictresult()
 
1116
 
 
1117
    def get_projectsets_by_offering(self, offeringid, dry=False):
 
1118
        """Returns all the projectsets in a particular offering"""
 
1119
        query = """\
 
1120
SELECT projectsetid, max_students_per_group
 
1121
FROM project_set
 
1122
WHERE project_set.offeringid = %d;"""%offeringid
 
1123
        if dry:
 
1124
            return query
 
1125
        return self.db.query(query).dictresult()
 
1126
 
 
1127
    def get_groups_by_projectset(self, projectsetid, dry=False):
 
1128
        """Returns all the groups that are in a particular projectset"""
 
1129
        query = """\
 
1130
SELECT groupid, groupnm, nick, createdby, epoch
 
1131
FROM project_group
 
1132
WHERE project_group.projectsetid = %d;"""%projectsetid
 
1133
        if dry:
 
1134
            return query
 
1135
        return self.db.query(query).dictresult()
 
1136
 
845
1137
    def close(self):
846
1138
        """Close the DB connection. Do not call any other functions after
847
1139
        this. (The behaviour of doing so is undefined).