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

« back to all changes in this revision

Viewing changes to lib/common/db.py

  • Committer: dcoles
  • Date: 2008-05-30 00:36:54 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:756
Added SciPy install requirements

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
 
    def add_enrolment(self, login, subj_code, semester, year=None, dry=False):
907
 
        """
908
 
        Enrol a student in the given offering of a subject.
909
 
        Returns True on success, False on failure (which usually means either
910
 
        the student is already enrolled in the subject, the student was not
911
 
        found, or no offering existed with the given details).
912
 
        The return value can usually be ignored.
913
 
        """
914
 
        subj_code = str(subj_code)
915
 
        semester = str(semester)
916
 
        if year is None:
917
 
            year = str(time.gmtime().tm_year)
918
 
        else:
919
 
            year = str(year)
920
 
        query = """\
921
 
INSERT INTO enrolment (loginid, offeringid)
922
 
    VALUES (
923
 
        (SELECT loginid FROM login WHERE login=%s),
924
 
        (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)
929
 
        );""" % (_escape(login), _escape(subj_code), _escape(semester),
930
 
                 _escape(year))
931
 
        if dry:
932
 
            return query
933
 
        try:
934
 
            result = self.db.query(query)
935
 
        except pg.ProgrammingError:
936
 
            return False
937
 
        return True
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
 
 
1137
813
    def close(self):
1138
814
        """Close the DB connection. Do not call any other functions after
1139
815
        this. (The behaviour of doing so is undefined).