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

'''Web UI implementation related to class division.
'''

from itertools import groupby
from operator import attrgetter

from ll.xist.ns import html

from uw.stringtools import strip_string, split_upload_text

from uw.web.html.form import render_hidden, render_radio
from uw.web.html.format import make_table_format, format_return

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

from uw.local.util.format import format_person

from .delegate import division_delegate

[docs]def render_enrol_division (cursor, term, admin, roles, urlprefix="", count=False): result = [] divisions = cursor.divisions_by_offering (term_id=term.code (), admin_id=admin.admin_id) if divisions: result.append (html.p ('The following divisions of this offering have been created:')) result.append (make_table_format ( ('Description', lambda r: html.a (r.division_description, href=urlprefix + "view/%d" % r.division_seq)), ('Upload heading', attrgetter ('division_code')), ('Maintainance', attrgetter ('maintainer_description')), ) (divisions)) result.append (html.p (html.a ('View membership summary…', href=urlprefix + "view/"))) else: result.append (html.p ('No divisions of this offering have been created yet.')) if 'ISC' in roles: result.append (html.p (html.a ('Create division…', href=urlprefix + "create"))) if any (d.maintainer_code == 'M' for d in divisions): result.append (html.p ('This form allows you to upload information for the custom divisions you have created.')) result.append (html.p ('The uploaded file must be CSV or tab-delimited. The first line must be column headings, including ', html.tt ('uw_id'), ' to indicate a UW ID column, as well as one column for each division method for which data are to be uploaded. These columns should have headings matching those in the list of division methods. Each of the remaining lines of the upload should contain data for one student.')) result.append (html.form ( html.p ( html.input (type="file", name="data", accept="text/csv"), ' ', html.input (type="submit", name="!upload", value="Upload!"), ), method="post", enctype="multipart/form-data", action=urlprefix )) return (result, len (divisions)) if count else result
@return_html def division_index_get_handler (cursor, term, admin, roles): result = [format_return ('Main Menu', None, None, 'Offering')] result.append (render_enrol_division (cursor, term, admin, roles)) return 'Divisions', result @use_form_param @return_html def division_index_post_handler (cursor, term, admin, roles, form): if not 'ISC' in roles: raise status.HTTPForbidden () if "!create" in form: auto = "auto" in form code = strip_string (form.optional_field_value ("code")) if auto: description = cursor.execute_required_value ("select division_description from division_division_auto where (term_id, admin_id, division_code) = (%(term_id)s, %(admin_id)s, %(code)s)", term_id=term.code (), admin_id=admin.admin_id, code=code) else: description = strip_string (form.optional_field_value ("description")) division_seq = cursor.callproc_required_value ("division_create", term.code (), admin.admin_id, 'Q' if auto else 'M', description, code) if auto: cursor.callproc_none ("division_auto_update", term.code (), admin.admin_id, division_seq) raise status.HTTPFound ("view/%d" % division_seq) elif "!upload" in form: upload = split_upload_text (form.required_field_value ("data").decode ()) header = next (upload) rows = upload columns = dict (cursor.execute_tuples ("select division_code, division_seq from division_division where (term_id, admin_id) = (%(term_id)s, %(admin_id)s)", term_id=term.code (), admin_id=admin.admin_id)) id_column = None column_list = [] for i, h in enumerate (header): if h == 'uw_id': id_column = i elif h in columns: column_list.append ((i, columns[h])) if id_column is None: return 'Error', html.p ('No ', html.tt ('uw_id'), ' column given!') for row in rows: person_id = cursor.execute_optional_value ("select person_id from person_identity natural join teaching_admin_registration where (external_id_type, external_id) = ('UWID', %(uw_id)s) and is_primary and (term_id, admin_id) = (%(term_id)s, %(admin_id)s)", uw_id=row[id_column], term_id=term.code (), admin_id=admin.admin_id) if person_id is not None: for upload_column, division_seq in column_list: cursor.callproc_none ("division_student_record", term.code (), admin.admin_id, person_id, division_seq, row[upload_column]) raise status.HTTPFound ("view/") raise status.HTTPFound ("") @delegate_file_only @delegate_get_post @return_html def division_create_handler (cursor, term, admin, roles): if not 'ISC' in roles: raise status.HTTPForbidden () result = [format_return ('Main Menu', None, None, 'Offering', dot='Divisions')] result.append (html.h2 ('Enable Automatic Division')) result.append (html.p ('This section allows you to enable pre-defined divisions based on course, sections, and instructors. These divisions will be automatically updated when the class registration information is updated.')) auto_divisions = groupby (cursor.execute_tuples ("select * from division_value_auto where (term_id, admin_id) = (%(term_id)s, %(admin_id)s) order by division_description, division_value", term_id=term.code (), admin_id=admin.admin_id), attrgetter ('division_description', 'division_code')) form = html.form (render_hidden ("auto", ""), method="post", action=".") table = html.table () for (division_description, division_code), values in auto_divisions: values = list (values) if len (values) <= 1: continue table.append ( html.tr ( html.td (render_radio ("code", value=division_code)), html.td (make_table_format ( (division_description, attrgetter ('division_value')), ('Count', attrgetter ('count')), ) (values)), ), ) if table: form.append (table) form.append (html.p (html.input (type="submit", name="!create", value="Enable!"))) result.append (form) else: result.append (html.p ('This offering has no course, section, or instructor information that can be used to define automatic divisions.')) result.append (html.h2 ('Create Custom Division')) result.append (html.p ('This section allows you to create your own divisions of the class. You can then upload a data file giving the information for the divisions you have defined. You can upload a new file whenever you want to update the information. This allows you to divide up the class in any way you want.')) result.append (html.form ( html.table ( html.tr ( html.th ('Description:'), html.td (html.input (type="text", name="description"))), html.tr ( html.th ('Data upload heading:'), html.td (html.input (type="text", name="code"))), ), html.p (html.input (type="submit", name="!create", value="Create!")), method="post", action="." )) return "Create Divisions", result @delegate_get_post @return_html def division_view_index_handler (cursor, term, admin, roles): result = [format_return ('Main Menu', None, None, 'Offering', 'Divisions')] division_values = cursor.execute_tuples ("select d.*, division_code, division_description from (select term_id, admin_id, division_seq, division_value, count(*) from division_student_complete where (term_id, admin_id) = (%(term_id)s, %(admin_id)s) and dropped is null group by term_id, admin_id, division_seq, division_value) d natural join division_division order by division_description, division_value", term_id=term.code (), admin_id=admin.admin_id) if division_values: result.append (html.p ('The following divisions of this offering have been created and populated:')) for (division_description, division_seq), values in groupby (division_values, attrgetter ('division_description', 'division_seq')): result.append (make_table_format ( (html.a (division_description, href=division_seq), attrgetter ('division_value')), ('Count', attrgetter ('count')), ) (values)) else: result.append (html.p ('No divisions of this offering have been created yet.')) return 'Divisions', result @delegate_file_only @delegate_get_post @return_html def division_view_handler (cursor, term, admin, roles, division): result = [format_return ('Main Menu', None, None, 'Offering', 'Divisions')] class_students = cursor.execute_tuples ("select * from division_student_complete left join (select person_id, external_id::quest_uw_id as uw_id from person_identity where external_id_type = 'UWID' and is_primary) s using (person_id) where (term_id, admin_id, division_seq) = (%(term_id)s, %(admin_id)s, %(division_seq)s) and dropped is null order by division_value, uw_id", term_id=term.code (), admin_id=admin.admin_id, division_seq=division.division_seq) for value, students in groupby (class_students, attrgetter ('division_value')): students = list (students) result.append (html.h3 (value, ' — ', len (students))) result.append (make_table_format ( ('UW ID', lambda r: '%08d' % r.uw_id), ('Candidate', lambda r: format_person (cursor, r.person_id)), ) (students)) return 'Division by %s' % division.division_description, result division_handler = delegate_action ( delegate_get_post (division_index_get_handler, division_index_post_handler), { 'create': division_create_handler, 'view': division_delegate (division_view_index_handler, division_view_handler), })