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