Source code for uw.local.teaching.db.exam_import_rooms
"""Process to import examination rooms.
Defines a parser for the examination room upload file produced by the RO, and
a procedure to process the room updates.
"""
import re
from uw.stringtools import split_upload_text, parse_tabular_file
REMOVED = object ()
# RE for what separates consecutive field entries, usually a comma but
# sometimes a line separator
split_re = re.compile ('[\r\n,]+')
# RE for single location - building code and room number, with allowance for
# room number to be mashed together with the building code
# **TODO: awfully similar to webui.accommodation.location_re - reuse?**
location_re = re.compile ('^(((?P<building>[A-Z]+|(B|C|EV?|M|SJ)[1-9]|CIF[12]) +(UPPER +)?)?(?P<room>([0-9]+[A-Z]?|Chapel|[A-Z](-[A-Z])?)))$')
[docs]def parse_location (field):
if field.casefold () == 'removed':
return REMOVED
building_code = None
result = []
for l in re.split (split_re, field):
l = l.strip ()
if l == '':
continue
room_match = location_re.match (l)
if room_match is None:
raise ValueError ('Cannot understand locations: %r' % field)
building = room_match.group ('building')
if building is not None:
building_code = building
room_code = room_match.group ('room')
# Correct some room numbers
if building_code == 'CGC':
building_code = 'CGR'
if room_code == 'Chapel':
room_code = '3450'
elif building_code.startswith ('CIF'):
rows = room_code.split ('-')
if len (rows) < 2:
rows = rows * 2
for row in [chr (c) for c in range (ord (rows[0]), ord (rows[1]) + 1)]:
if not row in ['I', 'O']:
result.append ((building_code[:3], building_code[3:] + row))
continue
result.append ((building_code, room_code))
return result
columns = [
('Host Key', 'exam_id', int),
('Room Assignment', 'rooms', parse_location),
]
[docs]def parse_exam_rooms_upload (contents):
grid = split_upload_text (contents)
return parse_tabular_file (columns, grid, 0, 'line')
[docs]def process_exam_rooms_upload (cursor, term, rows):
"""Process RO data lines.
:param cursor: DB connection cursor.
:param term: Object representing a UW term.
:param list rows: The update requests parsed from the input file.
Each row is a namedtuple consisting of (exam_id, rooms) produced by parsing the RO data file. Each row should correspond to a single examination,
which will have its rooms udated accordingly.
"""
warnings = []
for r in rows:
e = cursor.execute_optional_tuple ("select exam_id, primary_sitting_id from exam_exam where (term_id, exam_id) = (%(term_id)s, %(exam_id)s)", term_id=term.code (), exam_id=r.exam_id)
if e is None:
warnings.append ((r.line, 'No exam found with exam_id %s' % r.exam_id))
continue
if r.rooms is REMOVED:
cursor.callproc_none ("ro_exam_update_remove_from_schedule", r.exam_id)
else:
if e.primary_sitting_id is None:
warnings.append ((r.line, 'No primary sitting for exam with exam_id %d' % r.exam_id))
continue
cursor.connection.notices.clear ()
for building_code, room_code in r.rooms or []:
cursor.callproc_none ("ro_exam_attach_room", e.primary_sitting_id, e.exam_id, building_code, room_code)
for notice in cursor.connection.notices:
warnings.append ((r.line, notice))
return warnings