"""Assessment editor pages.
This implements the pages for editing examination details.
"""
from datetime import date, time, datetime
from ll.xist.ns import html
from uw.local.teaching.webui.ui import render_tooltip
from uw.web.html.form import render_select, render_radio, render_checkbox, parse_date_2, parse_time_2, parse_datetime
from uw.web.html.format import format_return, format_datetime
from uw.web.wsgi import status
from uw.web.wsgi.delegate import delegate_get_post
from uw.web.wsgi.form import use_form_param, parse_form_value
from uw.web.wsgi.function import return_html
from .ui import nullif
from .exam_schedule import bool_choices, bool_write, bool_read, integration_choices
[docs]def write_setup_controls (cursor, term, admin, exam=None):
'''Write form controls for editing the basic setup of an examination.
:param cursor: DB connection cursor.
:param term: In-context academic term.
:param admin: In-context admin unit.
:param exam: A database row representing an examination.
:return: List of control titles and form controls.
:rtype: list
Returns controls for editing the start time and duration of the examination.
'''
result = []
if exam is None or exam_may_edit_cover (exam):
if exam is None or exam.primary_start_time is None:
# Allow picking start time
start_time_select = term.render_time_selector ("start", 8, 13)
else:
# Allow choosing primary sitting
sittings = cursor.execute_tuples ("select sitting_id, start_time from exam_exam_sitting as ees join exam_sitting as es using (sitting_id) where (sitting_admin_id, exam_id) = (%(admin_id)s, %(exam_id)s) order by start_time", admin_id=admin.admin_id, exam_id=exam.exam_id)
primary_sitting_id = exam.primary_sitting_id
start_time_select = render_select ("primary", [(r.sitting_id, format_datetime (r.start_time)) for r in sittings], primary_sitting_id, True)
result.append (('Primary Start Time', start_time_select))
valid_admin = False if exam is None else cursor.execute_required_value("select exists (select from auth_offering_personnel_complete where (term_id, admin_id, person_id) = (%(term_id)s, %(admin_id)s, %(person_id)s) and role_current='t')", term_id=exam.term_id, admin_id=exam.schedule_admin_id, person_id=exam.exam_author_person_id)
default_duration = None if exam is None else exam.exam_duration
# Requestor has privileges related to schedule_admin_id
if exam is not None and exam.schedule_admin_id is not None and valid_admin is True:
duration_choices = cursor.execute_tuples ("select extract (epoch from duration)::integer as duration_seconds, exam_time_format_duration (duration) as formatted from teaching_admin_scheduled_duration where admin_id=%(admin_id)s", admin_id=exam.schedule_admin_id)
# Self scheduled exam
else:
if default_duration is not None:
default_duration = str (default_duration.seconds)
duration_choices = cursor.execute_tuples ("select extract (epoch from duration)::text as duration_seconds, exam_time_format_duration (duration) as formatted from (select d * interval '10 minutes' as duration from generate_series (2, 12) as t (d) union select d * interval '30 minutes' as duration from generate_series (5, 10) as s (d) union select %(duration)s) t where duration is not null order by duration", duration=default_duration)
total_seconds = exam and exam.exam_duration and int (exam.exam_duration.total_seconds ())
result.append (('Duration', [
render_select ("duration", duration_choices + [('other', 'Other...')], default_duration, class_="uw-ofs"),
html.div (['\t', 'Hours: ',
render_select ('%sh' % "duration-other", [(x, x) for x in range (0, 7)], None if not exam or not exam.exam_duration else total_seconds//3600),
'\t', 'Minutes: ',
render_select ('%sm' % "duration-other", [(x, x) for x in ('%02d' % x for x in range (0, 60, 5))],
None if not exam or not exam.exam_duration else '%02d' % ((total_seconds - ((total_seconds)//3600)*3600)//60))],
class_="uw-ofs-duration-other")
]))
return result
[docs]def write_seating_controls (cursor, term, admin, exam=None):
'''Write form controls for editing the seating options of an examination.
:param cursor: DB connection cursor.
:param term: In-context academic term.
:param admin: In-context admin unit.
:param exam: A database row representing an examination.
:return: List of control titles and form controls.
:rtype: list
Returns controls for editing the seating options of the examination.
'''
result = []
if exam is not None and exam_may_edit_seat (exam):
sequence_choices = cursor.exam_default_sequence_choices (exam_id=exam.exam_id)
result.append (('First Sequence', [
render_select ("first", sequence_choices, exam.first_sequence, blank=True),
' (do not fill in unless specifically required)'
]))
if exam is None or exam_may_edit_seat (exam):
if exam is None:
exam_assigned = cursor.execute_required_value ("select same_agg (exam_assigned) from exam_exam where (term_id, admin_id) = (%(term_id)s, %(admin_id)s) and exam_assigned is not null", term_id=term.code (), admin_id=admin.admin_id)
allow_tablet_seats = False
allow_single_seating = False
else:
exam_assigned = exam.exam_assigned
allow_tablet_seats = exam.allow_tablet_seats
allow_single_seating = exam.allow_single_seating
result.append (('Seating', [
'Assigned Seating ',
render_select ("assigned", bool_choices, value=bool_write[exam_assigned]),
html.br (),
html.label ('Use Tablet Seats ', render_tooltip ('Seats with a tablet arm attached, unused by default'), ': ', render_checkbox ("tablet", checked=allow_tablet_seats or None)),
html.br (),
html.label ('Use Every Seat ', render_tooltip ('Use every seat for classrooms (not PAC, labs, etc.), by default uses every other seat'), ': ', render_checkbox ("single", checked=allow_single_seating or None)),
]))
return result
[docs]def write_printing_controls (cursor, term, admin, exam=None, remote_identity=None):
'''Write form controls for editing the printing options of an examination.
:param cursor: DB connection cursor.
:param term: In-context academic term.
:param admin: In-context admin unit.
:param exam: A database row representing an examination.
:return: List of control titles and form controls.
:rtype: list
Returns controls for editing the printing options of the examination.
'''
result = []
if exam is None or exam.master_accepted is None:
default_uploader = remote_identity.person_id if exam is None else exam.exam_author_person_id
result.append (('Authorized Uploader', render_select ("uploader", cursor.offering_uploader_choices (term_id=term.code (), admin_id=admin.admin_id), default_uploader, blank=True)))
if exam is None or exam.master_accepted is None:
master_count = 1 if exam is None else exam.master_count
result.append (('Master Versions', html.input (name="master_count", value=master_count, size=2, maxlength=2)))
if exam is None:
scanning_integration = cursor.execute_required_value ("select same_agg (scanning_integration) from exam_exam_plus where (term_id, admin_id) = (%(term_id)s, %(admin_id)s)", term_id=term.code (), admin_id=admin.admin_id)
result.append (('Scanning Integration',
render_select ("scanning", [i[:2] for i in integration_choices], value=scanning_integration),
))
return result
[docs]def write_create_controls (cursor, term, admin, remote_identity):
'''Write form controls for creating an examination.
:param cursor: DB connection cursor.
:param term: In-context academic term.
:param admin: In-context admin unit.
:param remote_identity: The remote identity of the Web user.
:return: List of control titles and form controls.
:rtype: list
Returns controls for editing the various characteristics of the examination
which can be set at creation time.
'''
return write_setup_controls (cursor, term, admin) + write_seating_controls (cursor, term, admin) + write_printing_controls (cursor, term, admin, remote_identity=remote_identity)
[docs]def exam_may_edit_cover (exam):
"""Determine whether exam details related to the cover can be edited.
:param exam: A database row representing an examination.
:return: Represents whether exam cover details can be edited.
:rtype: bool
"""
return ((exam.primary_sitting_id is None or exam.primary_start_time is None
or exam.primary_start_time > datetime.now ())
and exam.master_approved is None)
[docs]def exam_may_edit_seat (exam):
"""Determine whether exam details related to seat assignment can be edited.
:param exam: A database row representing an examination.
:return: Represents whether exam seating assignment details can be edited.
:rtype: bool
"""
return exam.sequence_assigned is None
@return_html
def exam_editor_setup_get_handler (cursor, term, admin, roles, exam):
'''Assessment editing form GET URL handler.
Displays the form for editing basic setup of the current examination.
'''
result = [format_return ('Main Menu', None, None, 'Offering', None, dot='Assessment')]
result.append (write_edit_form (write_setup_controls (cursor, term, admin, exam)))
return "%s: Edit Setup Options" % exam.full_title, result
@use_form_param
@return_html
def exam_editor_setup_post_handler (cursor, term, admin, roles, form, exam):
if not 'ISC' in roles:
raise status.HTTPForbidden ()
result = handle_exam_edit_setup_form (cursor, form, exam)
if result is None:
raise status.HTTPFound (".")
else:
return result
exam_editor_setup_handler = delegate_get_post (exam_editor_setup_get_handler, exam_editor_setup_post_handler)
@return_html
def exam_editor_seating_get_handler (cursor, term, admin, roles, exam):
'''Assessment editing form GET URL handler.
Displays the form for editing seating options of the current examination.
'''
result = [format_return ('Main Menu', None, None, 'Offering', None, dot='Assessment')]
result.append (write_edit_form (write_seating_controls (cursor, term, admin, exam)))
return "%s: Edit Seating Options" % exam.full_title, result
@use_form_param
@return_html
def exam_editor_seating_post_handler (cursor, term, admin, roles, form, exam):
if not 'ISC' in roles:
raise status.HTTPForbidden ()
result = handle_exam_edit_seating_form (cursor, form, exam)
if result is None:
raise status.HTTPFound (".")
else:
return result
exam_editor_seating_handler = delegate_get_post (exam_editor_seating_get_handler, exam_editor_seating_post_handler)
@return_html
def exam_editor_printing_get_handler (cursor, term, admin, roles, exam):
'''Assessment editing form GET URL handler.
Displays the form for editing printing options of the current examination.
'''
result = [format_return ('Main Menu', None, None, 'Offering', None, dot='Assessment')]
result.append (write_edit_form (write_printing_controls (cursor, term, admin, exam)))
return "%s: Edit Printing Options" % exam.full_title, result
@use_form_param
@return_html
def exam_editor_printing_post_handler (cursor, term, admin, roles, form, exam):
if not 'ISC' in roles:
raise status.HTTPForbidden ()
result = handle_exam_edit_printing_form (cursor, form, exam)
if result is None:
raise status.HTTPFound (".")
else:
return result
exam_editor_printing_handler = delegate_get_post (exam_editor_printing_get_handler, exam_editor_printing_post_handler)
@use_form_param
@return_html
def exam_sitting_add_handler (cursor, term, admin, roles, form, exam):
"""Assessment special sitting creation POST URL handler.
Handles the form for adding a new sitting for an examination.
"""
if not 'ISC' in roles:
raise status.HTTPForbidden ()
if '!add-sitting' in form and exam.primary_sitting_id is not None:
start_time = parse_datetime (form, 'ns', parse_date_2, parse_time_2)
if start_time is None:
return "Error: Start time not given", [html.p ('Please go back and fill in the start time.')]
else:
cursor.execute_none ("select exam_require_special_sitting (%(exam_id)s, %(start_time)s)", exam_id=exam.exam_id, start_time=start_time)
raise status.HTTPFound ("./")
exam_sitting_add_handler = delegate_get_post (None, exam_sitting_add_handler)