Source code for uw.local.grad.webui.view

"""Application display implementation.

This module contains the application search form implementation, as well as the
WSGI structure definition connecting the various application display pages
together.
"""

import re
from datetime import date

from ll.xist.ns import html

from uw.web.html.form import render_select, render_hidden
from uw.web.html.bootstrapform import  render_bootstrap_form_row, render_bootstrap_form, render_bootstrap_submit
from uw.web.html.format import make_table_format
from uw.web.wsgi import status
from uw.web.wsgi.delegate import delegate_action, delegate_get_post
from uw.web.wsgi.form import use_form_param
from uw.web.wsgi.function import return_html

from uw.local import termtools

from ...util.identity import use_remote_identity

from ..db import notification
from ..db.unitapp import UnitApplication

from .list_render import make_application_link_column, application_list_columns, application_term_column

from .view_main import view_main_get_handler
from .view_accept import view_accept_get_handler, view_accept_post_handler
from .view_comments import view_comments_get_handler, view_comments_post_handler
from .view_compilation import view_compilation_handler
from .view_contact import view_contact_handler
from .view_offer import view_offer_get_handler, view_offer_post_handler
from .view_history import view_history_handler
from .view_edit import view_edit_get_handler, view_edit_post_handler

ouacNoPattern = re.compile ("^(?P<year>\d{4})-?(?P<number>\d{6})-?0?$")

[docs]def canonicalizeOuacNo (ouac): """Canonicalize an OUAC application number. :param ouac: OUAC application number. :return: the application number in a fixed format. The number may be provided with or without hyphens in specific locations, and with or without the trailing 0. The result will always omit the hyphens but include the trailing 0. """ match = ouacNoPattern.match (ouac) if match is None: return None return match.group ("year") + match.group ("number") + "0"
[docs]def findCurrentWorkApplicationsByOuacNo (cursor, ouac, staff_person_id): """Find current applications by OUAC application number. :param cursor: DB connection cursor. :param ouac: OUAC application number. :param staff_person_id: the identity of the person on whose behalf the search is being conducted. """ return cursor.execute_values ("select distinct appl_id from work_application_current_staff_lookup where ouac_reference=%(ouac)s AND staff_person_id=%(staff_person_id)s order by appl_id", ouac=ouac, staff_person_id=staff_person_id)
uwidPattern = re.compile ("^\d{8}$")
[docs]def findCurrentWorkApplicationsByUwid (cursor, uwid, staff_person_id): """Find current applications by applicant UW ID. :param cursor: DB connection cursor. :param uwid: the UW ID of the student whose applications should be found. :param staff_person_id: the identity of the person on whose behalf the search is being conducted. """ return cursor.execute_values ("select distinct appl_id from work_application_current_staff_lookup where uw_id=%(uwid)s AND staff_person_id=%(staff_person_id)s order by appl_id", uwid=uwid, staff_person_id=staff_person_id)
[docs]def findCurrentWorkApplicationsByUserid (cursor, userid, staff_person_id): """Find current applications by applicant userid. :param cursor: DB connection cursor. :param uwid: the userid of the student whose applications should be found. :param staff_person_id: the identity of the person on whose behalf the search is being conducted. """ return cursor.execute_values ("select distinct appl_id from work_application_current_staff_lookup where userid=%(userid)s AND staff_person_id=%(staff_person_id)s order by appl_id", userid=userid, staff_person_id=staff_person_id)
[docs]def findNamesByPartialName (cursor, names, staff_person_id, limit=None): """Returns a tuple of (uw_id, FN, MN, LN, PN) where one of the name components matches one (or more) of the name fragments seperated by space through a raw case insentitive comparison. :param cursor: DB connection cursor. :param names: a string with names seperated by space (e.g. "Marie Anne") :param staff_person_id: the identity of the person on whose behalf the search is being conducted. :param limit: The number of results to return, None means no limit :returns: A tuple of postgresql result items. """ limit_str = "" if limit is None else "LIMIT {}".format(limit) names = ['%' + name.replace("%", r"\%").replace("*", "%") + '%' for name in names.split()] return cursor.execute_tuples(("select distinct on (uw_id) uw_id, first_name, middle_name, last_name from std_name " "join work_application_current_staff_lookup USING (uw_id) where array_to_string(ARRAY[first_name, middle_name, last_name], ' ') " "ilike ALL(%(names)s) AND staff_person_id=%(staff_person_id)s {}").format(limit_str), staff_person_id = staff_person_id, names = names)
[docs]def findCurrentWorkApplicationsByName (cursor, search, staff_person_id): """Find current applications by applicant name. :param cursor: DB connection cursor. :param search: part of the name of the student whose applications should be found; "*" may be used to match any string. :param staff_person_id: the identity of the person on whose behalf the search is being conducted. """ names = ['%' + name.replace("%", r"\%").replace("*", "%") + '%' for name in search.split()] return cursor.execute_values ("select distinct appl_id from work_application_search_by_names (%(staff)s, %(names)s)", staff=staff_person_id, names=names)
[docs]def render_search_form (cursor, values, errors, apps): """Render the application search form as HTML. :param cursor: DB connection cursor. :param values: submitted form values. :param errors: errors to display within the form. :param apps: applications found to display below the search form, or None to omit the application list. """ row = render_bootstrap_form_row ([ ('UW ID Number or Userid:', html.input (type="text", name="uwid", id="uwid", size="9", maxlength="8", value=values['uwid'], class_="form-control"), 'col-xs-4'), ('OUAC ID Number:', html.input (type="text", name="ouacid", id="ouacid", size="13", maxlength="12", value=values['ouacid'], class_="form-control"), 'col-xs-4'), ('Applicant Name (“*” matches any string):', html.input (type="text", name="name", id="name", size="21", maxlength="20", value=values['name'], class_="form-control"), 'col-xs-4') ]) row2 = render_bootstrap_form_row ([ ('', html.span (errors['uwid'], class_="text-danger"), 'col-xs-4 text-center'), ('', html.span (errors['ouacid'], class_="text-danger"), 'col-xs-4'), ('', html.span (errors['name'], class_="text-danger"), 'col-xs-4') ]) search_button = render_bootstrap_form_row ([(False, html.div(render_bootstrap_submit("Search…")), "col-xs-12 text-center top-buffer")]) page = html.div ( html.p (html.a ('Main Menu', href="../")), html.p ( 'This page is for searching for a specific application. To view lists of applications by term and status, use the ', html.a ('application list', href="../list/"), ' page. Faculty members will likely want to use the ', html.a ('faculty summary list', href="../faculty/"), ' page.' ), render_bootstrap_form ([row, row2, search_button]), class_ = "container main" ) if apps is None: app_list = html.p () elif apps: apps = [UnitApplication (cursor, cursor.unitapp_by_id (appl_id=appl_id)) for appl_id in apps] table_columns = [make_application_link_column ("../")] table_columns.extend (application_list_columns) table_columns.append (application_term_column) app_table = make_table_format (*table_columns) (apps) app_table["class"] += " col-xs-12 table" app_list = html.div( html.p ("%s applications found. Only the most recent application for each applicant is shown." % len (apps)), html.div (app_table, class_ = "row"), class_ = "main container" ) else: app_list = html.p ('No matching applications found.') page.append (app_list) return 'Search Applications', page
@use_form_param @use_remote_identity @return_html def view_index_handler (cursor, remote_identity, form): """Application search form URL handler. Displays a search form. If form submission values are present, additional information is displayed: - if no applications match, an error message is shown. - if one application matches, the application redirects to a view of that application. - if multiple applications match (typically resulting from a name search), a list of them is shown. Note that permissions checking is implicit - if no permissions, no applications will be found. This could cause some confusion if a former user or a new but not-yet-authorized user tries to search. """ values = {'uwid': None, 'ouacid': None, 'name': None} errors = dict (values) values['ouacid'] = "%u-" % date.today ().year uwid = form.optional_field_value ('uwid') ouacno = form.optional_field_value ('ouacid') sname = form.optional_field_value ('name') if uwid: # Search by UW ID or Userid if uwidPattern.match (uwid): apps = findCurrentWorkApplicationsByUwid (cursor, uwid, remote_identity.person_id) else: apps = findCurrentWorkApplicationsByUserid (cursor, uwid, remote_identity.person_id) values['uwid'] = uwid if not apps: errors['uwid'] = "Not found" elif ouacno and ouacno != values['ouacid']: # Search by OUAC ID ouacno = canonicalizeOuacNo (ouacno) if ouacno is None: apps = [] else: apps = findCurrentWorkApplicationsByOuacNo (cursor, ouacno, remote_identity.person_id) values['ouacid'] = ouacno if not apps: errors['ouacid'] = "Not found" elif sname: # Search by name sname = sname.lower () apps = findCurrentWorkApplicationsByName (cursor, sname, remote_identity.person_id) values['name'] = sname if not apps: errors['name'] = "Not found" else: # Blank form apps = [] if len (apps) == 0: return render_search_form (cursor, values, errors, None) elif len (apps) == 1: raise status.HTTPFound ("%s/" % apps[0]) else: return render_search_form (cursor, values, errors, apps) view_handler = use_remote_identity (delegate_action (delegate_get_post (view_main_get_handler, view_comments_post_handler), { 'contact': view_contact_handler, 'comments': view_comments_get_handler, 'offer': delegate_get_post (view_offer_get_handler, view_offer_post_handler), 'history': view_history_handler, 'edit': delegate_get_post (view_edit_get_handler, view_edit_post_handler), 'accept': delegate_get_post (view_accept_get_handler, view_accept_post_handler), 'compilation': view_compilation_handler, }))