Source code for uw.local.teaching.webui.order_edit

"""Seat and sequence number assignment order.

Implements the screens for editing the seat and sequence number assignment
order.  Also provides a function to format the assignment order for
convenient display.
"""
from operator import attrgetter

from ll.xist.ns import html

from uw.web.html.form import render_checkbox, render_hidden, render_select
from uw.web.html.format import make_table_format, format_return, format_datetime
from uw.web.html.join import english_join

from uw.web.wsgi.delegate import delegate_action, delegate_get_post, status
from uw.web.wsgi.form import use_form_param, parse_form_value
from uw.web.wsgi.function import return_html

from .delegate import division_delegate
from .exam_edit import exam_may_edit_seat
from .ui import nullif

[docs]def format_order (cursor, exam, order, master_count): """Format a seat/sequence assignment order in English as HTML. :param order_exam: A database row representing the division and order assignment for a particular examination. :param master_count: An integer representing the master count of a particular examination. :return: Two HTML <p> elements. The first explains the seat assignment order while the second explains the sequence number assignment order. """ result = [] seat_order, sequence_order, cover_display = [], [], [] sequence_sort_order, seat_sort_order = False, False if master_count > 1: sequence_order.append ('Version') for r in order: sequence_order.append (r.order_type_description) if r.division_seq < 0: sequence_sort_order = True if r.assign_seats_by: seat_order.append (r.order_type_description) if r.division_seq < 0: seat_sort_order = True if r.cover_display: cover_display.append (r.order_type_description) if seat_order: result.append (html.p ( 'Seats will be assigned within each sitting in order by ', english_join (*seat_order), '; then in random order.' if not seat_sort_order else '.' )) else: result.append (html.p ( 'Seats will be assigned within each sitting in random order.' )) division_division_exam_sitting = cursor.execute_tuples ("select * from division_division_exam_sitting natural join division_division_exam natural join division_division natural join exam_sitting natural join (select admin_id as sitting_admin_id, admin_description from teaching_admin) ta where exam_id = %(exam_id)s order by division_order_index, division_value", exam_id=exam.exam_id) if division_division_exam_sitting: result.append ("In addition, %s division values have been allocated to the following sittings as indicated below:" % english_join (*seat_order)) result.append (make_table_format ( ('Division Value', lambda r: r.division_value), ('Sitting', lambda r: 'At %s with %s' % (format_datetime (r.start_time), r.admin_description)) )(division_division_exam_sitting)) if sequence_order: result.append (html.p ( 'Sequence numbers will be assigned in order by ', english_join (*sequence_order), '; then by sitting, room, and seat.' if not sequence_sort_order else '.' )) else: result.append (html.p ( 'Sequence numbers will be assigned in order by sitting, room, and seat.' )) if cover_display: result.append (html.p ( 'Cover will display the ', english_join (*cover_display), '.' )) else: result.append (html.p ( 'Cover will display only default information.' )) return result
@return_html def order_edit_get_handler (cursor, term, admin, roles, exam): """GET handler for the exam order editor. :param cursor: DB connection cursor. :param term: Object representing a UW term. :param admin: A database row representing an admin unit. :param roles: A list of user roles for a particular admin unit. :param exam: A database row representing an examination. :return: An HTML page containing an editor for adjusting the exam sorting and division order for the relevant examination. """ result = [format_return ('Main Menu', None, None, 'Offering', None, 'Assessment')] order_options = cursor.order_by_exam (exam_id=exam.exam_id, all=True) order_exam = [r for r in order_options if r.in_use] at_end_index = 1 + max([0] + [r.division_order_index for r in order_exam if r.division_order_index]) result.append (html.h3 ('Seat Assignment and Sequence Number Order')) result.append (format_order (cursor, exam, order_exam, exam.master_count)) cols = [('Assign sequence order by', attrgetter ('order_type_description')), ('Assign seats', lambda r: ('Yes ', html.a ('Edit…', href='./%s' % r.division_seq) if r.division_seq > 0 else None) if r.assign_seats_by else 'No'), ('Cover display', lambda r: ('Yes' if r.cover_display else 'No') if r.division_seq > 0 else None)] if at_end_index > 1: cols.append(('Priority', lambda r: (html.form ( render_hidden ("division_seq", r.division_seq), render_select ("division_order_index", [(s.division_order_index, s.division_order_index + 1) for s in order_exam if s.division_seq > 0], value=r.division_order_index), html.input (type="submit", name="!move", value="Move!"), method="post", action="")) if r.division_seq > 0 else None)) cols.append(('Delete', lambda r: html.form ( render_hidden ("division_seq", r.division_seq), html.input (type="submit", name="!delete", value="Delete!"), method="post", action=""))) table = (make_table_format (*cols) (order_exam)) result.append (table) result.append (html.p ('You may use the following form to change the above order:')) result.append (html.form (html.p ( 'Insert new sort key ', render_select ("division_seq", list (map (attrgetter ('division_seq', 'order_type_description'), order_options))), '; ', html.label ('Assign seats: ', render_checkbox("assign_seats_by")), '; ', html.label ('Cover display: ', render_checkbox("cover_display")), '; ', html.input (type="submit", name="!insert", value="Insert!"), ), method="post", action="" )) result.append (html.p ('You can enable more sorting methods by creating new class divisions using the ', html.a ('class division editor', href="../../../division/create"), '.')) return '%s (%s) %s: Update Seat/Sequence Number Assignment Order' % (admin.admin_description, term.description (), exam.title), result @use_form_param @return_html def order_edit_post_handler (cursor, term, admin, roles, exam, form): """POST handler for the exam order editor. :param cursor: DB connection cursor. :param term: Object representing a UW term. :param admin: A database row representing an admin unit. :param roles: A list of user roles for a particular admin unit. :param exam: A database row representing an examination. :param form: :return: Redirects user to the exam order editor and performs requested delete, move, and insert for the relevant examination depending on form results. """ if not 'ISC' in roles: raise status.HTTPForbidden () if not exam_may_edit_seat (exam) or exam.master_accepted: raise status.HTTPFound (".") division_seq = parse_form_value (form.optional_field_value ("division_seq"), int) if not division_seq: return 'Error: No assignment order type selected', html.p ('Please select an assignment order from the drop-down and try again. ', html.a ('Go back.', href="javascript:history.go(-1)")) division_order_index = cursor.execute_required_value ("select coalesce(max(division_order_index) + 1, 0) from exam_sort_order_complete where exam_id = %(exam_id)s", exam_id=exam.exam_id) if division_seq > 0 else None cover_display = form.optional_field_value ("cover_display") is not None assign_seats_by = form.optional_field_value ("assign_seats_by") is not None if "!delete" in form: cursor.callproc_none ("order_exam_delete", exam.exam_id, division_seq) elif "!move" in form: division_order_index = form.required_field_value ("division_order_index") cursor.callproc_none ("exam_division_order_index_set", exam.exam_id, division_seq, division_order_index) elif "!insert" in form and not "!no" in form: if division_seq < 0: if "!yes" in form: cursor.callproc_none ("order_exam_delete", exam.exam_id, parse_form_value (form.required_field_value("old_division_seq"), int)) order = cursor.execute_optional_tuple ("select *, new_division_seq != old_division_seq as is_new from (select division_seq as old_division_seq, order_type_description as old_sort_order_description from exam_sort_order_complete where exam_id = %(exam_id)s and division_seq < 0 and in_use) as old, (select division_seq as new_division_seq, order_type_description as new_sort_order_description from exam_sort_order_complete where (exam_id, division_seq) = (%(exam_id)s, %(division_seq)s)) as new", division_seq=division_seq, exam_id=exam.exam_id) if order and order.is_new: result = [] result.append ([html.p ("In order to sort exam by %s, the current exam sorting of %s will be deleted." % (order.new_sort_order_description, order.old_sort_order_description)), html.p ("Are you sure you want to continue?")]) result.append (html.form ( render_hidden ("division_seq", division_seq), render_hidden ("division_order_index", division_order_index), render_hidden ("assign_seats_by", "") if assign_seats_by else None, render_hidden ("!insert", "Insert"), render_hidden ("old_division_seq", order.old_division_seq), html.input(type="submit", name="!yes", value="Yes"), html.input(type="submit", name="!no", value="No"), method="post", action="")) return 'Delete ' + order.old_sort_order_description + ' Exam Ordering', result cursor.callproc_none ("order_exam_set", exam.exam_id, division_seq, division_order_index, cover_display, assign_seats_by) cursor.callproc_none ("exam_candidate_update", exam.exam_id) raise status.HTTPFound ("") @return_html def division_sitting_get_hanlder (cursor, term, admin, roles, exam, division): result = [format_return (None, 'Assessment', dot='Assignment Order Editor')] division_values = cursor.execute_tuples ("select term_id, admin_id, division_seq, division_value from division_student_complete where (term_id, admin_id, division_seq) = (%(term_id)s, %(admin_id)s, %(division_seq)s) and dropped is null group by term_id, admin_id, division_seq, division_value order by division_value", term_id=exam.term_id, admin_id=exam.admin_id, division_seq=division.division_seq) sittings = cursor.sittings_by_exam (exam_id=exam.exam_id) table = make_table_format ( ('Division Value', attrgetter ('division_value')), ('Sitting', lambda d: render_select ( "%s" % d.division_value, [(r.sitting_id, 'At %s with %s' % (format_datetime (r.start_time), r.admin_description)) for r in sittings], blank=True, value=cursor.execute_optional_value ("select sitting_id from division_division_exam_sitting where (exam_id, division_seq, division_value) = (%(exam_id)s, %(division_seq)s, %(division_value)s)", exam_id=exam.exam_id, division_seq=division.division_seq, division_value=d.division_value),)) )(division_values) result.append (html.form ((table, html.input (type="submit", name="!update", value="Update!")), method="post", action="")) return '%s (%s) %s: Update Division Value Sitting Assignment' % (admin.admin_description, term.description (), exam.title), result @use_form_param @return_html def division_sitting_post_handler (cursor, term, admin, roles, exam, division, form): if not 'ISC' in roles: raise status.HTTPForbidden () if '!update' in form: division_values = cursor.execute_tuples ("select term_id, admin_id, division_seq, division_value from division_student_complete where (term_id, admin_id, division_seq) = (%(term_id)s, %(admin_id)s, %(division_seq)s) group by term_id, admin_id, division_seq, division_value order by division_value", term_id=exam.term_id, admin_id=exam.admin_id, division_seq=division.division_seq) for division_value in division_values: sitting_id = form.optional_field_value (division_value.division_value) cursor.callproc_none ("division_division_exam_sitting_set", exam.exam_id, division.division_seq, division_value.division_value, nullif (sitting_id)) cursor.callproc_none ("exam_candidate_update", exam.exam_id) raise status.HTTPFound (".") order_edit_handler = division_delegate (delegate_get_post (order_edit_get_handler, order_edit_post_handler), delegate_get_post (division_sitting_get_hanlder, division_sitting_post_handler))