Remove remnants of SQL interface
This commit is contained in:
parent
f1702130d7
commit
31c05d869f
|
@ -7,7 +7,7 @@ Standards-Version: 3.7.2
|
||||||
|
|
||||||
Package: ceo
|
Package: ceo
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: python2.4, python-ldap, python-pygresql, python-urwid, krb5-user, less, python-pam, ${shlibs:Depends}
|
Depends: python2.4, python-ldap, python-urwid, krb5-user, less, python-pam, ${shlibs:Depends}
|
||||||
Recommends: quota
|
Recommends: quota
|
||||||
Description: Computer Science Club Administrative Utilities
|
Description: Computer Science Club Administrative Utilities
|
||||||
This package contains the CSC Electronic Office
|
This package contains the CSC Electronic Office
|
||||||
|
|
|
@ -30,7 +30,6 @@ install: build
|
||||||
usr/lib/csc usr/bin etc/ldap/schema
|
usr/lib/csc usr/bin etc/ldap/schema
|
||||||
dh_install pylib/* usr/lib/$(PYTHON)/site-packages/
|
dh_install pylib/* usr/lib/$(PYTHON)/site-packages/
|
||||||
dh_install etc/* etc/csc/
|
dh_install etc/* etc/csc/
|
||||||
dh_install sql/* usr/share/csc/
|
|
||||||
|
|
||||||
dh_install bin/ceo bin/ceo-old bin/addhomedir bin/ceoquery bin/csc-chsh bin/csc-chfn usr/lib/csc/
|
dh_install bin/ceo bin/ceo-old bin/addhomedir bin/ceoquery bin/csc-chsh bin/csc-chfn usr/lib/csc/
|
||||||
dh_install build/ceo build/ceo-old build/addhomedir build/ceoquery build/csc-chsh build/csc-chfn usr/bin/
|
dh_install build/ceo build/ceo-old build/addhomedir build/ceoquery build/csc-chsh build/csc-chfn usr/bin/
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# /etc/csc/members.cf: CSC Members Configuration
|
# /etc/csc/members.cf: CSC Members Configuration
|
||||||
|
|
||||||
include /etc/csc/pgsql.cf
|
|
||||||
include /etc/csc/ldap.cf
|
include /etc/csc/ldap.cf
|
||||||
|
|
||||||
### Validation Tuning ###
|
### Validation Tuning ###
|
||||||
|
|
11
etc/pgsql.cf
11
etc/pgsql.cf
|
@ -1,11 +0,0 @@
|
||||||
# /etc/csc/pgsql.cf: PostgreSQL database configuration
|
|
||||||
|
|
||||||
### Database Configuration ###
|
|
||||||
|
|
||||||
# server = "localhost"
|
|
||||||
server = ""
|
|
||||||
database = "ceo"
|
|
||||||
|
|
||||||
# not used
|
|
||||||
user = "ceo"
|
|
||||||
password = "secret"
|
|
|
@ -76,14 +76,14 @@ class NoSuchMember(MemberException):
|
||||||
ldap_connection = ldapi.LDAPConnection()
|
ldap_connection = ldapi.LDAPConnection()
|
||||||
|
|
||||||
def connect():
|
def connect():
|
||||||
"""Connect to PostgreSQL."""
|
"""Connect to LDAP."""
|
||||||
|
|
||||||
load_configuration()
|
load_configuration()
|
||||||
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
|
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
|
||||||
|
|
||||||
|
|
||||||
def disconnect():
|
def disconnect():
|
||||||
"""Disconnect from PostgreSQL."""
|
"""Disconnect from LDAP."""
|
||||||
|
|
||||||
ldap_connection.disconnect()
|
ldap_connection.disconnect()
|
||||||
|
|
||||||
|
|
|
@ -1,671 +0,0 @@
|
||||||
"""
|
|
||||||
Database Backend Interface
|
|
||||||
|
|
||||||
This module is intended to be a thin wrapper around CEO database operations.
|
|
||||||
Methods on the connection class correspond in a straightforward way to SQL
|
|
||||||
queries. These methods may restructure and clean up query output but may make
|
|
||||||
no other assumptions about its content or purpose.
|
|
||||||
|
|
||||||
This module makes use of the PyGreSQL Python bindings to libpq,
|
|
||||||
PostgreSQL's native C client library.
|
|
||||||
"""
|
|
||||||
import pgdb
|
|
||||||
|
|
||||||
|
|
||||||
class DBException(Exception):
|
|
||||||
"""Exception class for database-related errors."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class DBConnection(object):
|
|
||||||
"""
|
|
||||||
A connection to CEO's backend database. All database queries
|
|
||||||
and updates are made via this class.
|
|
||||||
|
|
||||||
Exceptions: (all methods)
|
|
||||||
DBException - on database query failure
|
|
||||||
|
|
||||||
Note: Updates will never take place until commit() is called.
|
|
||||||
|
|
||||||
Note: In the event that any method of this class raises a
|
|
||||||
DBException and that exception is caught, rollback()
|
|
||||||
must be called before further queries will be successful.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
connection = DBConnection()
|
|
||||||
connection.connect("localhost", "ceo")
|
|
||||||
|
|
||||||
# make queries and updates, i.e.
|
|
||||||
connection.insert_member("Calum T. Dalek")
|
|
||||||
|
|
||||||
connection.commit()
|
|
||||||
connection.disconnect()
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.cnx = None
|
|
||||||
self.cursor = None
|
|
||||||
|
|
||||||
|
|
||||||
def connect(self, hostname=None, database=None, username=None, password=None):
|
|
||||||
"""
|
|
||||||
Establishes the connection to CEO's PostgreSQL database.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
hostname - hostname:port to connect to
|
|
||||||
database - name of database
|
|
||||||
username - user to authenticate as
|
|
||||||
password - password of username
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.cnx: raise DBException("unable to connect: already connected")
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.cnx = pgdb.connect(host=hostname, database=database,
|
|
||||||
user=username, password=password)
|
|
||||||
self.cursor = self.cnx.cursor()
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("unable to connect: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
def disconnect(self):
|
|
||||||
"""Closes the connection to CEO's PostgreSQL database."""
|
|
||||||
|
|
||||||
if self.cursor:
|
|
||||||
self.cursor.close()
|
|
||||||
self.cursor = None
|
|
||||||
|
|
||||||
if self.cnx:
|
|
||||||
self.cnx.close()
|
|
||||||
self.cnx = None
|
|
||||||
|
|
||||||
|
|
||||||
def connected(self):
|
|
||||||
"""Determine whether the connection has been established."""
|
|
||||||
|
|
||||||
return self.cnx is not None
|
|
||||||
|
|
||||||
|
|
||||||
def commit(self):
|
|
||||||
"""Commits the current transaction and starts a new one."""
|
|
||||||
|
|
||||||
self.cnx.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def rollback(self):
|
|
||||||
"""Aborts the current transaction."""
|
|
||||||
|
|
||||||
self.cnx.rollback()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Member-related methods ###
|
|
||||||
|
|
||||||
def select_members(self, sql, params=None):
|
|
||||||
"""
|
|
||||||
Retrieves a list CSC members selected by given SQL statement.
|
|
||||||
|
|
||||||
This is a helper function that should generally not be called directly.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
sql - the SELECT sql statement
|
|
||||||
params - parameters for the SQL statement
|
|
||||||
|
|
||||||
The sql statement must select the six columns
|
|
||||||
(memberid, name, studentid, program, type, userid)
|
|
||||||
from the members table in that order.
|
|
||||||
|
|
||||||
Returns: a memberid-keyed dictionary whose values are
|
|
||||||
column-keyed dictionaries with member attributes
|
|
||||||
"""
|
|
||||||
|
|
||||||
# retrieve a list of all members
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
members_list = self.cursor.fetchall()
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("SELECT statement failed: %s" % e)
|
|
||||||
|
|
||||||
# build a dictionary of dictionaries from the result (a list of lists)
|
|
||||||
members_dict = {}
|
|
||||||
for member in members_list:
|
|
||||||
members_dict[member[0]] = {
|
|
||||||
'memberid': member[0],
|
|
||||||
'name': member[1],
|
|
||||||
'studentid': member[2],
|
|
||||||
'program': member[3],
|
|
||||||
'type': member[4],
|
|
||||||
'userid': member[5],
|
|
||||||
}
|
|
||||||
|
|
||||||
return members_dict
|
|
||||||
|
|
||||||
|
|
||||||
def select_single_member(self, sql, params=None):
|
|
||||||
"""
|
|
||||||
Retrieves a single member by memberid.
|
|
||||||
|
|
||||||
This is a helper function that should generally not be called directly.
|
|
||||||
|
|
||||||
See: self.select_members()
|
|
||||||
|
|
||||||
Returns: a column-keyed dictionary with member attributes, or
|
|
||||||
None if no member matching member exists
|
|
||||||
"""
|
|
||||||
|
|
||||||
# retrieve the member
|
|
||||||
results = self.select_members(sql, params)
|
|
||||||
|
|
||||||
# too many members returned
|
|
||||||
if len(results) > 1:
|
|
||||||
raise DBException("multiple members selected: sql='%s' params=%s" % (sql, repr(params)))
|
|
||||||
|
|
||||||
# no such member
|
|
||||||
elif len(results) < 1:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# return the single match
|
|
||||||
memberid = results.keys()[0]
|
|
||||||
return results[memberid]
|
|
||||||
|
|
||||||
|
|
||||||
def select_all_members(self):
|
|
||||||
"""
|
|
||||||
Retrieves a list of all CSC members (past and present).
|
|
||||||
|
|
||||||
See: self.select_members()
|
|
||||||
|
|
||||||
Example: connection.select_all_members() -> {
|
|
||||||
0: { 'memberid': 0, 'name': 'Calum T. Dalek' ...}
|
|
||||||
3349: { 'memberid': 3349, 'name': 'Michael Spang' ...}
|
|
||||||
...
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
sql = "SELECT memberid, name, studentid, program, type, userid FROM members"
|
|
||||||
return self.select_members(sql)
|
|
||||||
|
|
||||||
|
|
||||||
def select_members_by_name(self, name_re):
|
|
||||||
"""
|
|
||||||
Retrieves a list of all CSC members whose name matches name_re.
|
|
||||||
|
|
||||||
See: self.select_members()
|
|
||||||
|
|
||||||
Example: connection.select_members_by_name('Michael') -> {
|
|
||||||
3349: { 'memberid': 3349, 'name': 'Michael Spang' ...}
|
|
||||||
...
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE name ~* %s"
|
|
||||||
params = [ str(name_re) ]
|
|
||||||
|
|
||||||
return self.select_members(sql, params)
|
|
||||||
|
|
||||||
|
|
||||||
def select_members_by_term(self, term):
|
|
||||||
"""
|
|
||||||
Retrieves a list of all CSC members who were members in the specified term.
|
|
||||||
|
|
||||||
See: self.select_members()
|
|
||||||
|
|
||||||
Example: connection.select_members_by_term('f2006') -> {
|
|
||||||
3349: { 'memberid': 3349, 'name': 'Michael Spang' ...}
|
|
||||||
...
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
sql = "SELECT members.memberid, name, studentid, program, type, userid FROM members JOIN terms ON members.memberid=terms.memberid WHERE term=%s"
|
|
||||||
params = [ str(term) ]
|
|
||||||
|
|
||||||
return self.select_members(sql, params)
|
|
||||||
|
|
||||||
|
|
||||||
def select_member_by_id(self, memberid):
|
|
||||||
"""
|
|
||||||
Retrieves a single member by memberid.
|
|
||||||
|
|
||||||
See: self.select_single_member()
|
|
||||||
|
|
||||||
Example: connection.select_member_by_id(0) ->
|
|
||||||
{ 'memberid': 0, 'name': 'Calum T. Dalek' ...}
|
|
||||||
"""
|
|
||||||
sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE memberid=%d"
|
|
||||||
params = [ int(memberid) ]
|
|
||||||
|
|
||||||
return self.select_single_member(sql, params)
|
|
||||||
|
|
||||||
|
|
||||||
def select_member_by_userid(self, username):
|
|
||||||
"""
|
|
||||||
Retrieves a single member by UNIX account username.
|
|
||||||
|
|
||||||
See: self.select_single_member()
|
|
||||||
|
|
||||||
Example: connection.select_member_by_userid('ctdalek') ->
|
|
||||||
{ 'memberid': 0, 'name': 'Calum T. Dalek' ...}
|
|
||||||
"""
|
|
||||||
sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE userid=%s"
|
|
||||||
params = [ username ]
|
|
||||||
|
|
||||||
return self.select_single_member(sql, params)
|
|
||||||
|
|
||||||
|
|
||||||
def select_member_by_studentid(self, studentid):
|
|
||||||
"""
|
|
||||||
Retrieves a single member by student id number.
|
|
||||||
|
|
||||||
See: self.select_single_member()
|
|
||||||
|
|
||||||
Example: connection.select_member_by_studentid('nnnnnnnn') ->
|
|
||||||
{ 'memberid': 3349, 'name': 'Michael Spang' ...}
|
|
||||||
"""
|
|
||||||
sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE studentid=%s"
|
|
||||||
params = [ studentid ]
|
|
||||||
|
|
||||||
return self.select_single_member(sql, params)
|
|
||||||
|
|
||||||
|
|
||||||
def insert_member(self, name, studentid=None, program=None, mtype='user', userid=None):
|
|
||||||
"""
|
|
||||||
Creates a member with the specified attributes.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
name - full name of member
|
|
||||||
studentid - student id number
|
|
||||||
program - program of study
|
|
||||||
mtype - member type
|
|
||||||
userid - account id
|
|
||||||
|
|
||||||
Example: connection.insert_member('Michael Spang', '99999999', 'Math/CS') -> 3349
|
|
||||||
|
|
||||||
Returns: a memberid of created user
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# retrieve the next memberid
|
|
||||||
sql = "SELECT nextval('memberid_seq')"
|
|
||||||
self.cursor.execute(sql)
|
|
||||||
result = self.cursor.fetchone()
|
|
||||||
memberid = result[0]
|
|
||||||
|
|
||||||
# insert the member
|
|
||||||
sql = "INSERT INTO members (memberid, name, studentid, program, type, userid) VALUES (%d, %s, %s, %s, %s, %s)"
|
|
||||||
params = [ memberid, name, studentid, program, mtype, userid ]
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
|
|
||||||
return memberid
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("failed to create member: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_member(self, memberid):
|
|
||||||
"""
|
|
||||||
Deletes a member. Note that a member cannot
|
|
||||||
be deleted until it has been unregistered from
|
|
||||||
all terms.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
memberid - the member id number to delete
|
|
||||||
|
|
||||||
Example: connection.delete_member(3349)
|
|
||||||
"""
|
|
||||||
sql = "DELETE FROM members WHERE memberid=%d"
|
|
||||||
params = [ memberid ]
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("DELETE statement failed: %s" %e)
|
|
||||||
|
|
||||||
|
|
||||||
def update_member(self, member):
|
|
||||||
"""
|
|
||||||
Modifies member attributes.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
member - a column-keyed dictionary with the new state of the member.
|
|
||||||
member['memberid'] must be present. ommitted columns
|
|
||||||
will not be changed. None is NULL.
|
|
||||||
|
|
||||||
Returns: the full new state of the member as a column-keyed dictionary
|
|
||||||
|
|
||||||
Example: connection.update_member({
|
|
||||||
'memberid': 3349,
|
|
||||||
'name': 'Michael C. Spang',
|
|
||||||
'program': 'CS!'
|
|
||||||
}) -> {
|
|
||||||
'memberid': 3349,
|
|
||||||
'name': 'Michael C. Spang',
|
|
||||||
'program': CS!',
|
|
||||||
'studentid': '99999999' # unchanged
|
|
||||||
}
|
|
||||||
|
|
||||||
Equivalent Example:
|
|
||||||
member = connection.select_member_by_id(3349)
|
|
||||||
member['name'] = 'Michael C. Spang'
|
|
||||||
member['program'] = 'CS!'
|
|
||||||
connection.update_member(member) -> { see above }
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
|
|
||||||
# memberid to update
|
|
||||||
memberid = member['memberid']
|
|
||||||
|
|
||||||
# retrieve current state of member
|
|
||||||
member_state = self.select_member_by_id(memberid)
|
|
||||||
|
|
||||||
# build a list of changes to make
|
|
||||||
changes = []
|
|
||||||
for column in member.keys():
|
|
||||||
if member[column] != member_state[column]:
|
|
||||||
|
|
||||||
# column's value must be updated
|
|
||||||
changes.append( (column, member[column]) )
|
|
||||||
member_state[column] = member[column]
|
|
||||||
|
|
||||||
# no changes?
|
|
||||||
if len(changes) < 1:
|
|
||||||
return member_state
|
|
||||||
|
|
||||||
# make the necessary changes in an update statement
|
|
||||||
changes = zip(*changes)
|
|
||||||
sql = "UPDATE members SET " + ", ".join(["%s=%%s"] * len(changes[0])) % changes[0] + " WHERE memberid=%d"
|
|
||||||
params = changes[1] + ( memberid, )
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
|
|
||||||
return member_state
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("member update failed: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Term-related methods ###
|
|
||||||
|
|
||||||
def select_term(self, memberid, term):
|
|
||||||
"""
|
|
||||||
Determines whether a member is registered for a term.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
memberid - the member id number
|
|
||||||
term - the term to check
|
|
||||||
|
|
||||||
Returns: a matching term, or None
|
|
||||||
|
|
||||||
Example: connection.select_term(3349, 'f2006') -> 'f2006'
|
|
||||||
"""
|
|
||||||
sql = "SELECT term FROM terms WHERE memberid=%d AND term=%s"
|
|
||||||
params = [ memberid, term ]
|
|
||||||
|
|
||||||
# retrieve matches
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
result = self.cursor.fetchall()
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("SELECT statement failed: %s" % e)
|
|
||||||
|
|
||||||
if len(result) > 1:
|
|
||||||
raise DBException("multiple rows in terms with memberid=%d term=%s" % (memberid, term))
|
|
||||||
elif len(result) == 0:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return result[0][0]
|
|
||||||
|
|
||||||
|
|
||||||
def select_terms(self, memberid):
|
|
||||||
"""
|
|
||||||
Retrieves a list of terms a member is registered for.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
memberid - the member id number
|
|
||||||
|
|
||||||
Returns: a sorted list of terms
|
|
||||||
|
|
||||||
Example: connection.select_terms(3349) -> ['f2006']
|
|
||||||
"""
|
|
||||||
sql = "SELECT term FROM terms WHERE memberid=%d"
|
|
||||||
params = [ memberid ]
|
|
||||||
|
|
||||||
# retrieve the list of terms
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
result = self.cursor.fetchall()
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("SELECT statement failed: %s" % e)
|
|
||||||
|
|
||||||
result = [ row[0] for row in result ]
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def insert_term(self, memberid, term):
|
|
||||||
"""
|
|
||||||
Registers a member for a term.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
memberid - the member id number to register
|
|
||||||
term - string representation of the term
|
|
||||||
|
|
||||||
Example: connection.insert_term(3349, 'f2006')
|
|
||||||
"""
|
|
||||||
sql = "INSERT INTO terms (memberid, term) VALUES (%d, %s)"
|
|
||||||
params = [ memberid, term ]
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("INSERT statement failed: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_term(self, memberid, term):
|
|
||||||
"""
|
|
||||||
Unregisters a member for a term.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
memberid - the member id number to register
|
|
||||||
term - string representation of the term
|
|
||||||
|
|
||||||
Example: connection.delete_term(3349, 'f2006')
|
|
||||||
"""
|
|
||||||
sql = "DELETE FROM terms WHERE memberid=%d and term=%s"
|
|
||||||
params = [ memberid, term ]
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("DELETE statement failed: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_term_all(self, memberid):
|
|
||||||
"""
|
|
||||||
Unregisters a member for all registered terms.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
memberid - the member id number to unregister
|
|
||||||
|
|
||||||
Example: connection.delete_term_all(3349)
|
|
||||||
"""
|
|
||||||
sql = "DELETE FROM terms WHERE memberid=%d"
|
|
||||||
params = [ memberid ]
|
|
||||||
|
|
||||||
# retrieve a list of terms
|
|
||||||
try:
|
|
||||||
self.cursor.execute(sql, params)
|
|
||||||
except pgdb.Error, e:
|
|
||||||
raise DBException("DELETE statement failed: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
### Miscellaneous methods ###
|
|
||||||
|
|
||||||
def trim_memberid_sequence(self):
|
|
||||||
"""
|
|
||||||
Sets the value of the member id sequence to the id of the newest
|
|
||||||
member. For use after testing to prevent large intervals of unused
|
|
||||||
memberids from developing.
|
|
||||||
|
|
||||||
Note: this does nothing unless the most recently added member(s) have been deleted
|
|
||||||
"""
|
|
||||||
self.cursor.execute("SELECT setval('memberid_seq', (SELECT max(memberid) FROM members))")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Tests ###
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
from csc.common.test import *
|
|
||||||
|
|
||||||
conffile = "/etc/csc/pgsql.cf"
|
|
||||||
|
|
||||||
cfg = dict([map(str.strip, a.split("=", 1)) for a in map(str.strip, open(conffile).read().split("\n")) if "=" in a ])
|
|
||||||
hostnm = cfg['server'][1:-1]
|
|
||||||
dbase = cfg['database'][1:-1]
|
|
||||||
|
|
||||||
# t=test m=member s=student d=default e=expected u=updated
|
|
||||||
tmname = 'Test Member'
|
|
||||||
tmuname = 'Member Test'
|
|
||||||
tmsid = '00000004'
|
|
||||||
tmusid = '00000008'
|
|
||||||
tmprogram = 'Undecidable'
|
|
||||||
tmuprogram = 'Nondetermined'
|
|
||||||
tmtype = 'Untyped'
|
|
||||||
tmutype = 'Poly'
|
|
||||||
tmuserid = 'tmem'
|
|
||||||
tmuuserid = 'identifier'
|
|
||||||
tm2name = 'Test Member 2'
|
|
||||||
tm2sid = '00000005'
|
|
||||||
tm2program = 'Undeclared'
|
|
||||||
tm3name = 'T. M. 3'
|
|
||||||
dtype = 'user'
|
|
||||||
tmterm = 'w0000'
|
|
||||||
tm3term = 'f1112'
|
|
||||||
tm3term2 = 's1010'
|
|
||||||
|
|
||||||
emdict = { 'name': tmname, 'program': tmprogram, 'studentid': tmsid, 'type': tmtype, 'userid': tmuserid }
|
|
||||||
emudict = { 'name': tmuname, 'program': tmuprogram, 'studentid': tmusid, 'type': tmutype, 'userid': tmuuserid }
|
|
||||||
em2dict = { 'name': tm2name, 'program': tm2program, 'studentid': tm2sid, 'type': dtype, 'userid': None }
|
|
||||||
em3dict = { 'name': tm3name, 'program': None, 'studentid': None, 'type': dtype, 'userid': None }
|
|
||||||
|
|
||||||
test(DBConnection)
|
|
||||||
connection = DBConnection()
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.connect)
|
|
||||||
connection.connect(hostnm, dbase)
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.connected)
|
|
||||||
assert_equal(True, connection.connected())
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.insert_member)
|
|
||||||
tmid = connection.insert_member(tmname, tmsid, tmprogram, tmtype, tmuserid)
|
|
||||||
tm2id = connection.insert_member(tm2name, tm2sid, tm2program)
|
|
||||||
tm3id = connection.insert_member(tm3name)
|
|
||||||
assert_equal(True, int(tmid) >= 0)
|
|
||||||
assert_equal(True, int(tmid) >= 0)
|
|
||||||
success()
|
|
||||||
|
|
||||||
emdict['memberid'] = tmid
|
|
||||||
emudict['memberid'] = tmid
|
|
||||||
em2dict['memberid'] = tm2id
|
|
||||||
em3dict['memberid'] = tm3id
|
|
||||||
|
|
||||||
test(connection.select_member_by_id)
|
|
||||||
m1 = connection.select_member_by_id(tmid)
|
|
||||||
m2 = connection.select_member_by_id(tm2id)
|
|
||||||
m3 = connection.select_member_by_id(tm3id)
|
|
||||||
assert_equal(emdict, m1)
|
|
||||||
assert_equal(em2dict, m2)
|
|
||||||
assert_equal(em3dict, m3)
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.select_all_members)
|
|
||||||
members = connection.select_all_members()
|
|
||||||
assert_equal(True, tmid in members)
|
|
||||||
assert_equal(True, tm2id in members)
|
|
||||||
assert_equal(True, tm3id in members)
|
|
||||||
assert_equal(emdict, members[tmid])
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.select_members_by_name)
|
|
||||||
members = connection.select_members_by_name(tmname)
|
|
||||||
assert_equal(True, tmid in members)
|
|
||||||
assert_equal(False, tm3id in members)
|
|
||||||
assert_equal(emdict, members[tmid])
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.select_member_by_userid)
|
|
||||||
assert_equal(emdict, connection.select_member_by_userid(tmuserid))
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.insert_term)
|
|
||||||
connection.insert_term(tmid, tmterm)
|
|
||||||
connection.insert_term(tm3id, tm3term)
|
|
||||||
connection.insert_term(tm3id, tm3term2)
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.select_members_by_term)
|
|
||||||
members = connection.select_members_by_term(tmterm)
|
|
||||||
assert_equal(True, tmid in members)
|
|
||||||
assert_equal(False, tm2id in members)
|
|
||||||
assert_equal(False, tm3id in members)
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.select_term)
|
|
||||||
assert_equal(tmterm, connection.select_term(tmid, tmterm))
|
|
||||||
assert_equal(None, connection.select_term(tm2id, tmterm))
|
|
||||||
assert_equal(tm3term, connection.select_term(tm3id, tm3term))
|
|
||||||
assert_equal(tm3term2, connection.select_term(tm3id, tm3term2))
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.select_terms)
|
|
||||||
trms = connection.select_terms(tmid)
|
|
||||||
trms2 = connection.select_terms(tm2id)
|
|
||||||
assert_equal([tmterm], trms)
|
|
||||||
assert_equal([], trms2)
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.delete_term)
|
|
||||||
assert_equal(tm3term, connection.select_term(tm3id, tm3term))
|
|
||||||
connection.delete_term(tm3id, tm3term)
|
|
||||||
assert_equal(None, connection.select_term(tm3id, tm3term))
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.update_member)
|
|
||||||
connection.update_member({'memberid': tmid, 'name': tmuname})
|
|
||||||
connection.update_member({'memberid': tmid, 'program': tmuprogram, 'studentid': tmusid })
|
|
||||||
connection.update_member({'memberid': tmid, 'userid': tmuuserid, 'type': tmutype })
|
|
||||||
assert_equal(emudict, connection.select_member_by_id(tmid))
|
|
||||||
connection.update_member(emdict)
|
|
||||||
assert_equal(emdict, connection.select_member_by_id(tmid))
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.delete_term_all)
|
|
||||||
connection.delete_term_all(tm2id)
|
|
||||||
connection.delete_term_all(tm3id)
|
|
||||||
assert_equal([], connection.select_terms(tm2id))
|
|
||||||
assert_equal([], connection.select_terms(tm3id))
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.delete_member)
|
|
||||||
connection.delete_member(tm3id)
|
|
||||||
assert_equal(None, connection.select_member_by_id(tm3id))
|
|
||||||
negative(connection.delete_member, (tmid,), DBException, "delete of term-registered member")
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.rollback)
|
|
||||||
connection.rollback()
|
|
||||||
assert_equal(None, connection.select_member_by_id(tm2id))
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.commit)
|
|
||||||
connection.commit()
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.trim_memberid_sequence)
|
|
||||||
connection.trim_memberid_sequence()
|
|
||||||
success()
|
|
||||||
|
|
||||||
test(connection.disconnect)
|
|
||||||
connection.disconnect()
|
|
||||||
assert_equal(False, connection.connected())
|
|
||||||
connection.disconnect()
|
|
||||||
success()
|
|
|
@ -1,13 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Initializes a database for CEO.
|
|
||||||
|
|
||||||
# initialize the database
|
|
||||||
createdb $1
|
|
||||||
createlang plpythonu $1
|
|
||||||
|
|
||||||
# initialize the tables
|
|
||||||
psql $1 < structure.sql
|
|
||||||
|
|
||||||
# initialize check triggers
|
|
||||||
psql $1 < verify_studentid.sql
|
|
||||||
psql $1 < verify_term.sql
|
|
|
@ -1,24 +0,0 @@
|
||||||
-- Table structure for CEO's SQL database.
|
|
||||||
|
|
||||||
-- Usage:
|
|
||||||
-- su postgres
|
|
||||||
-- createdb ceo
|
|
||||||
-- psql ceo < structure.sql
|
|
||||||
|
|
||||||
CREATE SEQUENCE memberid_seq;
|
|
||||||
|
|
||||||
CREATE TABLE members (
|
|
||||||
memberid integer PRIMARY KEY DEFAULT nextval('memberid_seq') NOT NULL,
|
|
||||||
name character varying(50) NOT NULL,
|
|
||||||
studentid character varying(10) UNIQUE,
|
|
||||||
program character varying(50),
|
|
||||||
"type" character varying(10),
|
|
||||||
userid character varying(32) UNIQUE
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE terms (
|
|
||||||
memberid integer NOT NULL,
|
|
||||||
term character(5) NOT NULL,
|
|
||||||
UNIQUE(memberid, term)
|
|
||||||
);
|
|
|
@ -1,30 +0,0 @@
|
||||||
-- PL/Python trigger to verify student ids for validity
|
|
||||||
-- Dedicated to office staff who can't type student ids.
|
|
||||||
|
|
||||||
-- To (re)install:
|
|
||||||
-- su postgres
|
|
||||||
-- psql ceo < verify_studentid.sql
|
|
||||||
|
|
||||||
-- To uninstall:
|
|
||||||
-- su postgres
|
|
||||||
-- echo 'DROP FUNCTION verify_studentid() CASCADE' | psql ceo
|
|
||||||
|
|
||||||
DROP FUNCTION verify_studentid() CASCADE;
|
|
||||||
|
|
||||||
CREATE FUNCTION verify_studentid() RETURNS trigger AS '
|
|
||||||
import re
|
|
||||||
|
|
||||||
# update this line if the student id format changes
|
|
||||||
STUDENTID_REGEX = "^[0-9]{8}$"
|
|
||||||
|
|
||||||
studentid = TD["new"]["studentid"]
|
|
||||||
if studentid and not re.match(STUDENTID_REGEX, studentid):
|
|
||||||
plpy.error("student id is invalid (%s)" % studentid)
|
|
||||||
|
|
||||||
' LANGUAGE plpythonu;
|
|
||||||
|
|
||||||
CREATE TRIGGER verify_studentid_insert BEFORE INSERT on members
|
|
||||||
FOR ROW EXECUTE PROCEDURE verify_studentid();
|
|
||||||
|
|
||||||
CREATE TRIGGER verify_studentid_update BEFORE UPDATE ON members
|
|
||||||
FOR ROW EXECUTE PROCEDURE verify_studentid();
|
|
|
@ -1,29 +0,0 @@
|
||||||
-- PL/Python trigger to verify terms for validity
|
|
||||||
|
|
||||||
-- To (re)install:
|
|
||||||
-- su postgres
|
|
||||||
-- psql ceo < verify_term.sql
|
|
||||||
|
|
||||||
-- To uninstall:
|
|
||||||
-- su postgres
|
|
||||||
-- echo 'DROP FUNCTION verify_term() CASCADE' | psql ceo
|
|
||||||
|
|
||||||
DROP FUNCTION verify_term() CASCADE;
|
|
||||||
|
|
||||||
CREATE FUNCTION verify_term() RETURNS trigger AS '
|
|
||||||
import re
|
|
||||||
|
|
||||||
# update this line if the term format changes
|
|
||||||
TERM_REGEX = "^[wsf][0-9]{4}$"
|
|
||||||
|
|
||||||
term = TD["new"]["term"]
|
|
||||||
if term and not re.match(TERM_REGEX, term):
|
|
||||||
plpy.error("term is invalid (%s)" % term)
|
|
||||||
|
|
||||||
' LANGUAGE plpythonu;
|
|
||||||
|
|
||||||
CREATE TRIGGER verify_term_insert BEFORE INSERT on terms
|
|
||||||
FOR ROW EXECUTE PROCEDURE verify_term();
|
|
||||||
|
|
||||||
CREATE TRIGGER verify_term_update BEFORE UPDATE ON terms
|
|
||||||
FOR ROW EXECUTE PROCEDURE verify_term();
|
|
Loading…
Reference in New Issue