1 # $Id: members.py 44 2006-12-31 07:09:27Z mspang $
5 This module contains functions for registering new members, registering
6 members for terms, searching for members, and other member-related
9 Transactions are used in each method that modifies the database.
10 Future changes to the members database that need to be atomic
11 must also be moved into this module.
15 from csc.adm import terms
16 from csc.backends import db
17 from csc.common.conf import read_config
24 CONFIG_FILE = '/etc/csc/members.cf'
29 def load_configuration():
30 """Load Members Configuration"""
32 # configuration already loaded?
37 cfg_tmp = read_config(CONFIG_FILE)
40 raise MemberException("unable to read configuration file: %s"
43 # check that essential fields are completed
44 mandatory_fields = [ 'server', 'database', 'user', 'password' ]
46 for field in mandatory_fields:
47 if not field in cfg_tmp:
48 raise MemberException("missing configuratino option: %s" % field)
49 if not cfg_tmp[field]:
50 raise MemberException("null configuration option: %s" %field)
52 # update the current configuration with the loaded values
59 class MemberException(Exception):
60 """Exception class for member-related errors."""
62 class DuplicateStudentID(MemberException):
63 """Exception class for student ID conflicts."""
66 class InvalidStudentID(MemberException):
67 """Exception class for malformed student IDs."""
70 class InvalidTerm(MemberException):
71 """Exception class for malformed terms."""
74 class NoSuchMember(MemberException):
75 """Exception class for nonexistent members."""
80 ### Connection Management ###
82 # global database connection
83 connection = db.DBConnection()
87 """Connect to PostgreSQL."""
91 connection.connect(cfg['server'], cfg['database'])
95 """Disconnect from PostgreSQL."""
97 connection.disconnect()
101 """Determine whether the connection has been established."""
103 return connection.connected()
108 def new(realname, studentid=None, program=None):
110 Registers a new CSC member. The member is added
111 to the members table and registered for the current
115 realname - the full real name of the member
116 studentid - the student id number of the member
117 program - the program of study of the member
119 Returns: the memberid of the new member
122 DuplicateStudentID - if the student id already exists in the database
123 InvalidStudentID - if the student id is malformed
125 Example: new("Michael Spang", program="CS") -> 3349
128 # blank attributes should be NULL
129 if studentid == '': studentid = None
130 if program == '': program = None
132 # check the student id format
134 if studentid != None and not re.match(regex, str(studentid)):
135 raise InvalidStudentID("student id is invalid: %s" % studentid)
137 # check for duplicate student id
138 member = connection.select_member_by_studentid(studentid)
140 raise DuplicateStudentID("student id exists in database: %s" % studentid)
143 memberid = connection.insert_member(realname, studentid, program)
145 # register them for this term
146 connection.insert_term(memberid, terms.current())
148 # commit the transaction
156 Look up attributes of a member by memberid.
159 memberid - the member id number
161 Returns: a dictionary of attributes
163 Example: get(3349) -> {
165 'name': 'Michael Spang',
166 'program': 'Computer Science',
171 return connection.select_member_by_id(memberid)
174 def get_userid(userid):
176 Look up attributes of a member by userid.
179 userid - the UNIX user id
181 Returns: a dictionary of attributes
183 Example: get('mspang') -> {
185 'name': 'Michael Spang',
186 'program': 'Computer Science',
191 return connection.select_member_by_account(userid)
194 def get_studentid(studentid):
196 Look up attributes of a member by studnetid.
199 studentid - the student ID number
201 Returns: a dictionary of attributes
203 Example: get(...) -> {
205 'name': 'Michael Spang',
206 'program': 'Computer Science',
211 return connection.select_member_by_studentid(studentid)
216 Build a list of members in a term.
219 term - the term to match members against
221 Returns: a list of member dictionaries
223 Example: list_term('f2006'): -> [
224 { 'memberid': 3349, ... },
230 # retrieve a list of memberids in term
231 memberlist = connection.select_members_by_term(term)
233 # convert the list of memberids to a list of dictionaries
234 memberlist = map(connection.select_member_by_id, memberlist)
241 Build a list of members with matching names.
244 name - the name to match members against
246 Returns: a list of member dictionaries
248 Example: list_name('Spang'): -> [
249 { 'memberid': 3349, ... },
255 # retrieve a list of memberids matching name
256 memberlist = connection.select_members_by_name(name)
258 # convert the list of memberids to a list of dictionaries
259 memberlist = map(connection.select_member_by_id, memberlist)
264 def delete(memberid):
266 Erase all records of a member.
268 Note: real members are never removed
272 memberid - the member id number
274 Returns: attributes and terms of the
277 Example: delete(0) -> ({ 'memberid': 0, name: 'Calum T. Dalek' ...}, ['s1993'])
281 member = connection.select_member_by_id(memberid)
282 term_list = connection.select_terms(memberid)
284 # remove data from the db
285 connection.delete_term_all(memberid)
286 connection.delete_member(memberid)
289 return (member, term_list)
294 Update CSC member attributes. None is NULL.
297 member - a dictionary with member attributes as
298 returned by get, possibly omitting some
299 attributes. member['memberid'] must exist
303 NoSuchMember - if the member id does not exist
304 InvalidStudentID - if the student id number is malformed
305 DuplicateStudentID - if the student id number exists
307 Example: update( {'memberid': 3349, userid: 'mspang'} )
310 if member.has_key('studentid') and member['studentid'] != None:
312 studentid = member['studentid']
314 # check the student id format
316 if studentid != None and not re.match(regex, str(studentid)):
317 raise InvalidStudentID("student id is invalid: %s" % studentid)
319 # check for duplicate student id
320 member = connection.select_member_by_studentid(studentid)
322 raise DuplicateStudentID("student id exists in database: %s" %
325 # not specifying memberid is a bug
326 if not member.has_key('memberid'):
327 raise Exception("no member specified in call to update")
328 memberid = member['memberid']
330 # see if member exists
331 old_member = connection.select_member_by_id(memberid)
333 raise NoSuchMember("memberid does not exist in database: %d" %
337 connection.update_member(member)
339 # commit the transaction
346 def register(memberid, term_list):
348 Registers a member for one or more terms.
351 memberid - the member id number
352 term_list - the term to register for, or a list of terms
355 InvalidTerm - if a term is malformed
357 Example: register(3349, "w2007")
359 Example: register(3349, ["w2007", "s2007"])
362 if not type(term_list) in (list, tuple):
363 term_list = [ term_list ]
365 for term in term_list:
368 if not re.match('^[wsf][0-9]{4}$', term):
369 raise InvalidTerm("term is invalid: %s" % term)
371 # add term to database
372 connection.insert_term(memberid, term)
377 def registered(memberid, term):
379 Determines whether a member is registered
383 memberid - the member id number
384 term - the term to check
386 Returns: whether the member is registered
388 Example: registered(3349, "f2006") -> True
391 return connection.select_term(memberid, term) != None
394 def terms_list(memberid):
396 Retrieves a list of terms a member is
400 memberid - the member id number
402 Returns: list of term strings
404 Example: registered(0) -> 's1993'
407 return connection.select_terms(memberid)
413 if __name__ == '__main__':
418 sid = new("Test User", "99999999", "CS")
420 assert registered(id, terms.current())
422 register(sid, terms.next(terms.current()))
423 assert registered(sid, terms.next(terms.current()))
424 print terms_list(sid)