"""Display assessment schedule for a student.
Implements the page for students to look up their own assessment schedule, as
well as the page allowing staff to see any student's schedule.
"""
from operator import attrgetter
from ll.xist.ns import html, specials
from uw.web.wsgi.authority import check_authority
from uw.web.wsgi.delegate import *
from uw.web.wsgi.form import use_form_param
from uw.web.wsgi.function import return_html, return_json
from uw.web.wsgi.status import HTTPFound, HTTPNotFound, HTTPForbidden
from uw.web.html.format import make_table_format, format_datetime, format_time
from .authority import check_global_authority
from ...util.identity import use_remote_identity
from .ui import format_duration
portal_url = "https://portal.uwaterloo.ca/Index#/Home/exams"
[docs]def special_sitting (exam):
"""Determine whether special formatting is required for a candidate.
:param exam: database row of candidate assessment information
:return: whether building code is PAC as that is the only building with
special formatting of the assessment instructions
:rtype: bool
"""
return exam.building_code == 'PAC'
[docs]def include_sequence (exam):
"""Determine whether sequence number information should be included.
:param exam: candidate information for a single assessment
:return: whether the sequence number should be listed. In order to be
shown the sequence number has to be assigned and the start time has
to be in the past
:rtype: bool
"""
return exam.sequence_text is not None and exam.start_time is not None and exam.start_time < datetime.datetime.now ()
@delegate_get_post
@use_remote_identity
@return_html
def own_schedule_handler (cursor, remote_identity):
"""Student assessment schedule URL handler.
:param cursor: DB connection cursor
:return: tuple of (title, HTML for currently-scheduled assessments for
the relevant person)
:rtype: (str, list)
"""
result = []
exams = cursor.exams_by_candidate (person_id=remote_identity.person_id)
if exams:
result.append (html.p ('The following assessments are currently scheduled in this system for you:'))
result.append (format_exam_schedule (exams))
else:
result.append (html.p ('No assessments are currently scheduled in this system for you.'))
return 'Assessment Schedule (%s)' % remote_identity.userid, result
@delegate_get_post
@use_remote_identity
@use_form_param
@return_html
def candidate_choice_handler (cursor, form, remote_identity, global_roles):
"""Candidate choice URL handler.
:param cursor: DB connection cursor
:param form: CGI form results
:return: tuple of (title, HTML form to choose (enter in id of) a candidate)
:rtype: (str, list)
"""
if 'candidate' in form:
candidate_input = form.required_field_value ("candidate")
candidate_query = "select userid from person_identity_complete where %s = %%(candidate)s" % ("uw_id" if candidate_input.isdigit () else "userid")
print (candidate_query)
candidate = cursor.execute_optional_value (candidate_query, candidate=candidate_input)
if candidate:
raise HTTPFound (candidate)
else:
return 'Error: Student not found', [html.p ('Error student %s could not be found. Please go back and try again.' % candidate_input)]
result = []
result.append ([html.p ('You have permission to lookup student schedules. Please fill in either the UWID or userid of the desired student.'),
html.form (html.p ('Schedule Lookup: ', html.input (maxlength=8, type="text", name="candidate", size=9)),
action="" , method="get"
)])
return 'Student Assessment Schedule Lookup', result
@delegate_get_post
@return_html
def candidate_schedule (cursor, candidate, global_roles):
"""Staff student assessment schedule URL handler.
:param cursor: DB connection cursor
:param candidate: candidate for assessment
:return: tuple of (title and userid, currently-scheduled assessments
for the in-context userid)
:rtype: (str, list)
The URL corresponding to this handler must be controlled by web server
configuration. This allows authorized staff members to append a userid
to the candidate schedule URL in order to see the schedule of the person
with that userid.
"""
result = []
candidate_identity = cursor.execute_optional_tuple ("select * from person_identity_complete where userid=%(userid)s", userid=candidate)
if candidate_identity is None:
raise HTTPNotFound ()
exams = cursor.exams_by_candidate (person_id=candidate_identity.person_id)
if exams:
result.append (html.p ('The following assessments are currently scheduled in this system for %s:' % candidate_identity.userid))
result.append (format_exam_schedule (exams))
else:
result.append (html.p ('No assessments are currently scheduled in this system for %s.' % candidate_identity.userid))
return 'Assessment Schedule (%s)' % candidate_identity.userid, result
@delegate_get_post
@use_remote_identity
@return_json
def candidate_schedule_json (cursor, candidate, remote_identity):
"""Portal student assessment schedule URL handler.
:param cursor: DB connection cursor
:param candidate: candidate for assessment
:return: currently-scheduled assessments for the in-context userid
as a JSON array
:rtype: list
"""
if remote_identity.userid != '_instruct_api_schedule':
raise HTTPForbidden ()
result = []
candidate_identity = cursor.execute_optional_tuple ("select * from person_identity_complete where userid=%(userid)s", userid=candidate)
if candidate_identity is None:
raise HTTPNotFound ()
exams = cursor.exams_by_candidate (person_id=candidate_identity.person_id)
result = []
for exam in exams:
d = {}
d['exam_full_title'] = exam.exam_full_title
if exam.start_time is not None:
d['start_time'] = exam.start_time.isoformat ()
if exam.duration is not None:
d['duration_text'] = exam.duration_text
d['duration_minutes'] = exam.duration.seconds // 60
if exam.candidate_assigned:
if special_sitting (exam):
d['seating_instructions'] = format_pac_instructions (exam).string ()
else:
d['building_code'] = exam.building_code
d['room_code'] = exam.room_code
d['seat_code'] = exam.seat_code
if exam.room_note is not None:
d['room_note'] = exam.room_note
else:
d['rooms'] = exam.rooms
if include_sequence (exam):
d['sequence_text'] = exam.sequence_text
result.append (d)
return result
schedule_handler = delegate_value ('candidate',
check_authority (check_global_authority (['SCHED']), candidate_choice_handler),
always (check_authority (check_global_authority (['SCHED']), candidate_schedule))
, own_schedule_handler)