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

« back to all changes in this revision

Viewing changes to lib/common/db.py

  • Committer: wagrant
  • Date: 2008-07-17 04:28:35 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:906
editor.js: Warn before saving if the open file is an old revision.

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
 
 
224
190
    def update(self, primarydict, updatedict, tablename, tablefields,
225
191
        primary_keys, disallowed_update=frozenset([]), dry=False):
226
192
        """Updates a row in a table, matching against primarydict to find the
603
569
    UNION
604
570
        (SELECT problemid, loginid, date, text FROM problem_attempt
605
571
         AS problem_attempt (problemid, loginid, date, text)
606
 
         WHERE loginid = %d AND problemid = %d AND active)
 
572
         WHERE loginid = %d AND problemid = %d)
607
573
    )
608
574
    AS _
609
575
    ORDER BY date DESC
616
582
        else:
617
583
            return None
618
584
 
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
 
 
676
585
    def get_problem_status(self, login, exercisename, dry=False):
677
586
        """Given a login name and exercise name, returns information about the
678
587
        user's performance on that problem.
697
606
        # Will return an empty table if the problem has never been
698
607
        # successfully completed.
699
608
        query = """SELECT COUNT(*) FROM problem_attempt
700
 
    WHERE loginid = %d AND problemid = %d AND active AND date <=
 
609
    WHERE loginid = %d AND problemid = %d AND date <=
701
610
        (SELECT date FROM problem_attempt
702
 
            WHERE loginid = %d AND problemid = %d AND complete AND active
 
611
            WHERE loginid = %d AND problemid = %d AND complete = TRUE
703
612
            ORDER BY date ASC
704
613
            LIMIT 1);""" % (loginid, problemid, loginid, problemid)
705
614
        if dry: return query
715
624
            # completed.
716
625
            # Return the total number of attempts, and False for success.
717
626
            query = """SELECT COUNT(*) FROM problem_attempt
718
 
    WHERE loginid = %d AND problemid = %d AND active;""" % (loginid, problemid)
 
627
    WHERE loginid = %d AND problemid = %d;""" % (loginid, problemid)
719
628
            result = self.db.query(query)
720
629
            count = int(result.getresult()[0][0])
721
630
            return (False, count)
901
810
            raise
902
811
        return mand_done, mand_total, opt_done, opt_total
903
812
 
904
 
    # ENROLMENT INFORMATION
905
 
 
906
813
    def add_enrolment(self, login, subj_code, semester, year=None, dry=False):
907
814
        """
908
815
        Enrol a student in the given offering of a subject.
922
829
    VALUES (
923
830
        (SELECT loginid FROM login WHERE login=%s),
924
831
        (SELECT offeringid
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)
 
832
            FROM (offering INNER JOIN subject
 
833
                ON subject.subjectid = offering.subject)
 
834
            WHERE subj_code=%s AND semester=%s AND year=%s)
929
835
        );""" % (_escape(login), _escape(subj_code), _escape(semester),
930
836
                 _escape(year))
931
837
        if dry:
936
842
            return False
937
843
        return True
938
844
 
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;"""%offeringid
979
 
        if dry:
980
 
            return query
981
 
        return self.db.query(query).dictresult()
982
 
 
983
 
 
984
 
    def get_enrolment(self, login, dry=False):
985
 
        """
986
 
        Get all offerings (in IVLE) the student is enrolled in.
987
 
        Returns a list of dicts (all values strings), with the keys:
988
 
        offeringid, subj_code, subj_name, subj_short_name, year, semester, url
989
 
        """
990
 
        query = """\
991
 
SELECT offering.offeringid, subj_code, subj_name, subj_short_name,
992
 
       semester.year, semester.semester, subject.url
993
 
FROM login, enrolment, offering, subject, semester
994
 
WHERE enrolment.offeringid=offering.offeringid
995
 
  AND login.loginid=enrolment.loginid
996
 
  AND offering.subject=subject.subjectid
997
 
  AND semester.semesterid=offering.semesterid
998
 
  AND enrolment.active
999
 
  AND login=%s;""" % _escape(login)
1000
 
        if dry:
1001
 
            return query
1002
 
        return self.db.query(query).dictresult()
1003
 
 
1004
 
    def get_enrolment_groups(self, login, offeringid, dry=False):
1005
 
        """
1006
 
        Get all groups the user is member of in the given offering.
1007
 
        Returns a list of dicts (all values strings), with the keys:
1008
 
        name, nick
1009
 
        """
1010
 
        query = """\
1011
 
SELECT project_group.groupnm as name, project_group.nick as nick
1012
 
FROM project_set, project_group, group_member, login
1013
 
WHERE login.login=%s
1014
 
  AND project_set.offeringid=%s
1015
 
  AND group_member.loginid=login.loginid
1016
 
  AND project_group.groupid=group_member.groupid
1017
 
  AND project_group.projectsetid=project_set.projectsetid
1018
 
""" % (_escape(login), _escape(offeringid))
1019
 
        if dry:
1020
 
            return query
1021
 
        return self.db.query(query).dictresult()
1022
 
 
1023
 
    def get_subjects_status(self, login, dry=False):
1024
 
        """
1025
 
        Get all subjects in IVLE, split into lists of enrolled and unenrolled
1026
 
        subjects.
1027
 
        Returns a tuple of lists (enrolled, unenrolled) of dicts
1028
 
        (all values strings) with the keys:
1029
 
        subj_code, subj_name, subj_short_name, url
1030
 
        """
1031
 
        enrolments = self.get_enrolment(login)
1032
 
        all_subjects = self.get_subjects()
1033
 
 
1034
 
        enrolled_set = set(x['subj_code'] for x in enrolments)
1035
 
 
1036
 
        enrolled_subjects = [x for x in all_subjects
1037
 
                             if x['subj_code'] in enrolled_set]
1038
 
        unenrolled_subjects = [x for x in all_subjects
1039
 
                               if x['subj_code'] not in enrolled_set]
1040
 
        enrolled_subjects.sort(key=lambda x: x['subj_code'])
1041
 
        unenrolled_subjects.sort(key=lambda x: x['subj_code'])
1042
 
        return (enrolled_subjects, unenrolled_subjects)
1043
 
 
1044
 
 
1045
 
    # PROJECT GROUPS
1046
 
    def get_groups_by_user(self, login, offeringid=None, dry=False):
1047
 
        """
1048
 
        Get all project groups the student is in, corresponding to a
1049
 
        particular subject offering (or all offerings, if omitted).
1050
 
        Returns a list of tuples:
1051
 
        (int groupid, str groupnm, str group_nick, bool is_member).
1052
 
        (Note: If is_member is false, it means they have just been invited to
1053
 
        this group, not a member).
1054
 
        """
1055
 
        if offeringid is None:
1056
 
            and_offering = ""
1057
 
        else:
1058
 
            and_projectset_table = ", project_set"
1059
 
            and_offering = """
1060
 
AND project_group.projectsetid = project_set.projectsetid
1061
 
AND project_set.offeringid = %s""" % _escape(offeringid)
1062
 
        # Union both the groups this user is a member of, and the groups this
1063
 
        # user is invited to.
1064
 
        query = """\
1065
 
    SELECT project_group.groupid, groupnm, project_group.nick, True
1066
 
    FROM project_group, group_member, login %(and_projectset_table)s
1067
 
    WHERE project_group.groupid = group_member.groupid
1068
 
      AND group_member.loginid = login.loginid
1069
 
      AND login = %(login)s
1070
 
      %(and_offering)s
1071
 
UNION
1072
 
    SELECT project_group.groupid, groupnm, project_group.nick, False
1073
 
    FROM project_group, group_invitation, login %(and_projectset_table)s
1074
 
    WHERE project_group.groupid = group_invitation.groupid
1075
 
      AND group_invitation.loginid = login.loginid
1076
 
      AND login = %(login)s
1077
 
      %(and_offering)s
1078
 
;""" % {"login": _escape(login), "and_offering": and_offering,
1079
 
        "and_projectset_table": and_projectset_table}
1080
 
        if dry:
1081
 
            return query
1082
 
        # Convert 't' -> True, 'f' -> False
1083
 
        return [(groupid, groupnm, nick, ismember == 't')
1084
 
                for groupid, groupnm, nick, ismember
1085
 
                in self.db.query(query).getresult()]
1086
 
 
1087
 
    def get_offering_info(self, projectsetid, dry=False):
1088
 
        """Takes information from projectset and returns useful information 
1089
 
        about the subject and semester. Returns as a dictionary.
1090
 
        """
1091
 
        query = """\
1092
 
SELECT subjectid, subj_code, subj_name, subj_short_name, url, year, semester, 
1093
 
active
1094
 
FROM subject, offering, semester, project_set
1095
 
WHERE offering.subject = subject.subjectid AND
1096
 
    offering.semesterid = semester.semesterid AND
1097
 
    project_set.offeringid = offering.offeringid AND
1098
 
    project_set.projectsetid = %d;"""%projectsetid
1099
 
        if dry:
1100
 
            return query
1101
 
        return self.db.query(query).dictresult()[0]
1102
 
 
1103
 
    def get_projectgroup_members(self, groupid, dry=False):
1104
 
        """Returns the logins of all students in a project group
1105
 
        """
1106
 
        query = """\
1107
 
SELECT login.login as login, login.fullname as fullname
1108
 
FROM login, group_member
1109
 
WHERE login.loginid = group_member.loginid AND
1110
 
    group_member.groupid = %d
1111
 
ORDER BY login.login;"""%groupid
1112
 
        if dry:
1113
 
            return query
1114
 
        return self.db.query(query).dictresult()
1115
 
 
1116
 
    def get_projectsets_by_offering(self, offeringid, dry=False):
1117
 
        """Returns all the projectsets in a particular offering"""
1118
 
        query = """\
1119
 
SELECT projectsetid, max_students_per_group
1120
 
FROM project_set
1121
 
WHERE project_set.offeringid = %d;"""%offeringid
1122
 
        if dry:
1123
 
            return query
1124
 
        return self.db.query(query).dictresult()
1125
 
 
1126
 
    def get_groups_by_projectset(self, projectsetid, dry=False):
1127
 
        """Returns all the groups that are in a particular projectset"""
1128
 
        query = """\
1129
 
SELECT groupid, groupnm, nick, createdby, epoch
1130
 
FROM project_group
1131
 
WHERE project_group.projectsetid = %d;"""%projectsetid
1132
 
        if dry:
1133
 
            return query
1134
 
        return self.db.query(query).dictresult()
1135
 
 
1136
845
    def close(self):
1137
846
        """Close the DB connection. Do not call any other functions after
1138
847
        this. (The behaviour of doing so is undefined).