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

"""Examination Version Assignment UI.

This implements the UI for exam version assignment section within the upload
UI, including the version section. Features to be added include the basis
allocator and editor.
"""
from operator import attrgetter

from ll.xist.ns import html

from uw.web.wsgi.delegate import *
from uw.web.wsgi.form import use_form_param, parse_form_value
from uw.web.wsgi.function import return_html

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


[docs]def format_version_table (cursor, exam, eva_use, edit): """Format a table showing version assignments. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param eva_use: A database row representing the version assignment basis. :param bool edit: Whether to generate an editable version of the table. :return: (table, warning); table is an HTML table showing the version assignments, while warning is a boolean indicating some assignment values have no versions assigned. """ cursor.callproc_none ("exam_version_set_use", exam.exam_id, eva_use.division_seq, True) columns = [ (eva_use.basis_description, attrgetter ('assignment_display')) ] if edit: def format_control (v): return lambda r: render_checkbox (name="a-%d" % r.assignment_id, value=v, checked=v in r.versions) for version in range (exam.master_count): columns.append ((chr (version + ord ('A')), format_control (version))) else: columns.append (('Versions', attrgetter ('versions_display'))) assignments = cursor.eva_details (exam_id=exam.exam_id, division_seq=eva_use.division_seq) table = make_table_format (*columns) (assignments) warning = any (not a.versions for a in assignments) return table, warning
[docs]def version_assignment_section (cursor, exam, edit): """Format the version assignment information for an examination. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param bool edit: Whether this is for editing, or only display. :return: A list of HTML elements showing the version assignment. If the display is for editing, appropriate links to add, remove, and edit version assignment methods are added. """ result = [] eva_uses = cursor.execute_tuples ("select * from exam_exam_version_assignment_basis where exam_id = %(exam_id)s and (in_use or %(edit)s) order by basis_description", exam_id=exam.exam_id, edit=edit) if not eva_uses: result.append (html.p ('Currently no exam version assignment selected.')) result.append (html.p ('The %s versions will be distributed evenly.' % exam.master_count)) add_uses = [] for eva_use in eva_uses: description = eva_use.basis_description if eva_use.in_use: result.append (html.h3 ('Current Exam Version Assignment by %s' % description)) table, warning_flag = format_version_table (cursor, exam, eva_use, False) if warning_flag: result.append (html.p ('Warning: some values for %s do not have any version assignment.' % description)) result.append (table) if edit: result.append (html.p ( html.a ('Edit…', href="version-edit?division_seq=%d" % eva_use.division_seq), ' ', html.a ('Delete…', href="allocate-delete?division_seq=%d" % eva_use.division_seq), )) else: add_uses.append (eva_use) if edit: result.append (html.h3 ('Add Additional Version Assignment')) if add_uses: result.append (html.form ( html.p ( 'Choose a version assignment method: ', render_select ("division_seq", ((add_use.division_seq, add_use.basis_description) for add_use in add_uses)), ' ', html.input (type="submit", name="!add", value="Add!") ), method="post", action="" )) else: result.append (html.p ('There are currently no additional version assignment methods that can be added.')) result.append (html.p ('You can enable more version assignment methods by creating new class divisions using the ', html.a ('class division editor', href="../../term/%s/%d/division/create" % (exam.term_id, exam.admin_id)), '.')) return result
@return_html def allocate_edit_get_handler (cursor, exam): """GET handler for the version assignment editor. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :return: An HTML page containing an editor for adjusting version assignment information for this examination. """ result = [format_return (dot='Upload Masters')] result.append (version_assignment_section (cursor, exam, edit=True)) return 'Version Assignment Allocator', result
[docs]def check_seats_unfinalized (exam): if exam.sequence_assigned is None: return None else: return 'Error: Seating already finalized', html.p ('Seating has already been finalized for this examination. No further changes can be made to version assignments.')
@return_html def allocate_edit_post_handler (cursor, exam, form): """POST handler for the version assignment editor. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param form: CGI form results. :return: Redirect to editor for newly-added version assignment method. """ if "!add" in form: result = check_seats_unfinalized (exam) if result is not None: return result division_seq = parse_form_value (form.required_field_value ("division_seq"), int) if division_seq is None: return 'Error: No version assignment method specified', html.p ('Please go back and choose a version assignment method.') cursor.callproc_none ("exam_version_set_use", exam.exam_id, division_seq, True) raise status.HTTPFound ("version-edit?division_seq=%d" % division_seq) @return_html def allocate_delete_get_handler (cursor, exam, form): """GET handler for deleting a version assignment. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param form: CGI form results. :return: HTML form to confirm deletion of assignment by the current method. """ division_seq = parse_form_value (form.required_field_value ("division_seq"), int) eva_basis = cursor.eva_basis (exam_id=exam.exam_id, division_seq=division_seq) table, warning_flag = format_version_table (cursor, exam, eva_basis, False) result = html.form ( html.p ('The current version assignment by %s is:' % eva_basis.basis_description), table, html.p ('If you proceed, versions will no longer be allocated by %s. Are you sure you would like to continue?' % eva_basis.basis_description), html.p ( html.input (type="submit", name="!continue", value="Yes"), ' ', html.input (type="submit", name="!cancel", value="No"), ), method="post", action="" ) return 'Delete Version Assignment by %s' % eva_basis.basis_description, result @return_html def allocate_delete_post_handler (cursor, exam, form): """POST handler for deleting a version assignment. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param form: CGI form results. :return: Redirect to the main exam version assignment editor page. """ division_seq = parse_form_value (form.required_field_value ("division_seq"), int) if '!continue' in form: result = check_seats_unfinalized (exam) if result is not None: return result cursor.callproc_none ("exam_version_set_use", exam.exam_id, division_seq, False) raise status.HTTPFound("allocate-edit") @return_html def version_edit_get_handler (cursor, exam, form): """GET handler for editing version assignment by a specific method. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param form: CGI form results. :return: HTML form for editing the details of version assignment by the current method. """ result = [html.p ("Return to ", html.a ('Version Assignment Allocator', href="allocate-edit"), ", ", html.a ('Upload Masters', href="../%s/" % exam.exam_id))] division_seq = parse_form_value (form.required_field_value ("division_seq"), int) eva_basis = cursor.eva_basis (exam_id=exam.exam_id, division_seq=division_seq) table, warning_flag = format_version_table (cursor, exam, eva_basis, True) result.append (html.h3 ("Current Exam Version Assignment by %s" % eva_basis.basis_description)) result.append (html.form ( table, html.p (html.input (type="submit", name="!update", value="Update")), method="post", action="" )) return 'Edit Exam Version Assignment by %s' % eva_basis.basis_description, result @return_html def version_edit_post_handler(cursor, exam, form): """POST handler for editing version assignment by a specific method. :param cursor: DB connection cursor. :param exam: A database row representing an examination. :param form: CGI form results. :return: Redirect to the main exam version assignment editor page. """ division_seq = parse_form_value (form.required_field_value ("division_seq"), int) assignments = cursor.eva_details (exam_id=exam.exam_id, division_seq=division_seq) if "!update" in form: result = check_seats_unfinalized (exam) if result is not None: return result for assignment in assignments: versions_enabled = list (map (int, form.multiple_field_value ("a-%d" % assignment.assignment_id))) for version in range (exam.master_count): cursor.callproc_none ("exam_version_set_choice", exam.exam_id, division_seq, assignment.assignment_id, version, version in versions_enabled) raise status.HTTPFound("allocate-edit") version_edit_handler = use_form_param (delegate_get_post (version_edit_get_handler, version_edit_post_handler)) allocate_edit_handler = delegate_get_post (allocate_edit_get_handler, use_form_param (allocate_edit_post_handler)) allocate_delete_handler = use_form_param (delegate_get_post (allocate_delete_get_handler, allocate_delete_post_handler))