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

"""Faculty interface UI implementation.

This implements the faculty page, which attempts to show each faculty member
the applications that are relevant to them.
"""

from operator import attrgetter
from itertools import groupby

from ll.xist.ns import html

from datetime import date, timedelta

from uw.web.html.join import english_join
from uw.web.html.form import render_select, render_hidden
from uw.web.html.format import make_table_format, format_return
from uw.web.html.bootstrapform import render_bootstrap_form, render_bootstrap_form_row, render_bootstrap_submit
from uw.web.wsgi import status
from uw.web.wsgi.delegate import 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 uw.local.dbtools import term_delegate

from .util import render_frontpage_header
from ...util.identity import use_remote_identity

from ..db.bulk_unitapp import bulk_app_loader
from ..db.unitapp import UnitApplication

from .list import render_term_select
from .list_render import (make_application_link_column, application_list_columns,
                          application_term_column, application_select_column,
                          render_sort_table)

[docs]def make_table_columns (is_current, base_prefix, confirm=False): """Assemble table column definitions for application list. :param bool is_current: whether the application list is for "current" applications, in which case the term column will be included. :param base_prefix: the base URL prefix for links to the applications. :param bool confirm: whether the application list is for the action confirmation screen. If not, a column with checkboxes for selecting applications will be included. :return: a list of column definitions for use with :func:`uw.web.html.format.make_table_format`. """ table_columns = [] table_columns.append (make_application_link_column (base_prefix + "../", confirm=confirm)) table_columns.extend (application_list_columns) if is_current: table_columns.append (application_term_column) if not confirm: table_columns.append (application_select_column) return table_columns
@return_html def faculty_index_get_handler (cursor, remote_identity, form, term=None): """Faculty "front page" GET URL handler. This is displayed in two slightly different forms depending on whether or not a specific term is in context. If no term is in context, applications for multiple upcoming terms are displayed. """ is_current = term is None base_prefix = "" if is_current else "../../" term_code = form.optional_field_value ('term') if term_code is not None: raise status.HTTPFound (base_prefix + "term/%s/" % term_code) if is_current: term = termtools.fromDate (date.today () + timedelta (days=6*30)) term_clause = 'admit_term_id >= %(term)s' else: term_clause = 'admit_term_id = %(term)s' research_areas = english_join (*[area.faculty_interest_description for area in get_research_areas (cursor, remote_identity.person_id) if area.selected]) if research_areas is None: research_areas = "You have not yet specified any research areas of interest." else: research_areas = ["You have expressed interest in seeing applications concerning the following research areas: ", research_areas, "."] states = dict (cursor.execute_tuples ("select view_state, view_state_description from work_application_faculty_state")) states[None] = 'Unseen' apps = cursor.execute_tuples ("select appl_id, view_state from (SELECT DISTINCT uw_id, appl_id, faculty_person_id FROM work_application_faculty_interest) AS wafi NATURAL JOIN work_application_term NATURAL JOIN work_application_status_current NATURAL JOIN work_application_status_code NATURAL LEFT JOIN work_application_faculty LEFT JOIN work_application_faculty_state USING (view_state) NATURAL JOIN std_name_primary where faculty_person_id=%%(person_id)s AND %s AND status_code NOT IN ('NEW', 'HLD', 'INR', 'REJ', 'OLD') order by view_state_sequence desc, last_name, first_name" % term_clause, person_id=remote_identity.person_id, term=term.code ()) unitapps = bulk_app_loader (cursor, [r.appl_id for r in apps]) unitapps = dict ((u.appl_id, u) for u in unitapps) if is_current: title = "Your Current Graduate Applications" term_choice = 'Applications for %s and later are shown. You may choose a specific term: ' % term.description () extra_breadcrumbs = [] else: title = "Your Graduate Applications for %s" % term.description () term_choice = 'You may ', html.a ('return to current applications', href="../../"), ' or choose a different term: ' extra_breadcrumbs = ['Current Applications', None] table_columns = make_table_columns (is_current, base_prefix) rows = [] for substate, app2 in groupby (apps, attrgetter ('view_state')): sort_table = render_sort_table (table_columns, (unitapps[app.appl_id] for app in app2), fixed=True) sort_table["class"] += " col-xs-12 table-fixed" table_row = [ html.h3 (states[substate]), html.div ( sort_table, ) ] rows.append (table_row) where = "(staff_person_id, admit_term_id, requested_person_id) = (%(staff_person_id)s, %(term_code)s, %(staff_person_id)s)" db = "natural join std_name_primary" requests = cursor.execute_values ("select appl_id from work_application_staff_lookup natural join work_application_request %s where %s order by last_name, first_name, middle_name, uw_id, appl_id" % (db, where), staff_person_id=remote_identity.person_id, term_code=term.code ()) requests = bulk_app_loader (cursor, requests) sort_table = render_sort_table (table_columns, requests, fixed=True) sort_table["class"] += " col-xs-12 table-fixed" table_row = [ html.h3 ('Requested Supervisor:'), html.p ('The following table only displays application requests recorded in this system. This list is not complete with the quest data.'), html.div ( sort_table, ) ] rows.append (table_row) return title, [ html.div( format_return ('Main Menu', *extra_breadcrumbs), render_bootstrap_form ([ render_bootstrap_form_row ([ (False, html.span (term_choice), "col-xs-7 vcenter"), (False, render_term_select (cursor, "term", remote_identity.person_id, term, class_="form-control"), "col-xs-3"), (False, render_bootstrap_submit ("Search…", class_="form-control hide-overflow"), "col-xs-2") ], tail_class="flex") ]), html.p ( research_areas, " You may ", html.a ("edit your research areas", href="interest/"), " at any time." ), render_frontpage_header ('research interest areas and recommendations', base_prefix), html.form ( html.div( rows, class_="row" ), html.p ( 'You may select a number of applications and indicate all at once that you have no interest in them. To do so, select them using the checkboxes at right, then click: ', render_bootstrap_submit ("Mark as No Interest…", name="!noint", class_="btn-xs")), method="post", action="" ), class_ = "main container") ] @return_html def faculty_index_post_handler (cursor, remote_identity, form, term=None): """Faculty "front page" POST URL handler. This handles responses to the "no interest" form used by faculty to indicate that they have no interest in a number of applications all at once. """ if "!noint" in form: apps = [UnitApplication (cursor, cursor.unitapp_by_id (appl_id=appl_id)) for appl_id in form.multiple_field_value ("appl_id")] if "confirm" in form: for app in apps: app.set_faculty_opinion (remote_identity.person_id, 'NOI') else: is_current = term is None base_prefix = "" if is_current else "../../" table_columns = make_table_columns (is_current, base_prefix, True) make_table = make_table_format (*table_columns) result = [ html.form ( html.p ('The following applications will be marked “No Interest” on your list. '), html.div ( make_table (apps), ), html.p ( render_hidden ("confirm", ""), render_bootstrap_submit ("Mark as No Interest…", name="!noint", class_="btn-xs") ), method="post", action="" ) ] return 'Confirm Marking “No Interest”', result raise status.HTTPFound ("") faculty_index_handler = use_remote_identity (use_form_param (delegate_get_post (faculty_index_get_handler, faculty_index_post_handler)))
[docs]def get_research_areas (cursor, person_id): """Get the research areas for a person. :param cursor: DB connection cursor. :param person_id: the identity of the person. Returns the appropriate rows from _grad.work_faculty_interest_complete. """ return cursor.execute_tuples ("select * from work_faculty_interest_complete where person_id=%(person_id)s and (selected or faculty_interest_expires is null) order by group_code, unit_code, faculty_interest_description", person_id=person_id)
@use_remote_identity @return_html def faculty_interest_get_handler (cursor, remote_identity, term=None): """Faculty research interest editor GET URL handler. Display the user's research interests as a form for editing them. """ return_list = ['Current Applications'] if term is not None: return_list.append (None) return_list.append ('%s Applications' % term.description ()) areas = get_research_areas (cursor, remote_identity.person_id) return 'Your Research Interest Areas', [ format_return (*return_list), html.form ( html.p ( [ html.label ( html.input (type="checkbox", name=area.faculty_interest_area, checked=area.selected or None), " ", area.faculty_interest_description ), html.br () ] for area in areas ), html.p (render_bootstrap_submit ("Update!")), action="", method="post" ) ] @use_remote_identity @use_form_param @return_html def faculty_interest_post_handler (cursor, remote_identity, form, **params): """Faculty research interest editor POST URL handler. Handle form submissions for modifying faculty research interest areas. """ for area in get_research_areas (cursor, remote_identity.person_id): old = area.selected new = area.faculty_interest_area in form if old and not new: # Remove interest area cursor.execute_none ("delete from work_faculty_interest where (group_code, unit_code, faculty_interest_area, faculty_person_id) = (%(group_code)s, %(unit_code)s, %(faculty_interest_area)s, %(faculty_person_id)s)", group_code=area.group_code, unit_code=area.unit_code, faculty_interest_area=area.faculty_interest_area, faculty_person_id=area.person_id) elif not old and new: # Add interest area cursor.execute_none ("insert into work_faculty_interest values (%(group_code)s, %(unit_code)s, %(faculty_interest_area)s, %(faculty_person_id)s)", group_code=area.group_code, unit_code=area.unit_code, faculty_interest_area=area.faculty_interest_area, faculty_person_id=area.person_id) raise status.HTTPFound ("") faculty_interest_handler = delegate_get_post (faculty_interest_get_handler, faculty_interest_post_handler) @use_remote_identity @use_form_param @return_html def faculty_notice_get_handler (cursor, remote_identity, form): """Notification preferences GET URL handler. Display the user's notification preferences as a form for editing them. These preferences actually apply to all roles, not just faculty, although the specific preferences which are applicable differ. This code is in this module for historical reasons. """ if cursor.execute_optional_value ("select pref_suppress from notification_preference where person_id=%(person_id)s", person_id=remote_identity.person_id): return 'Your Notification Preferences', [ html.form ( html.p ("Currently you have turned off all notifications (“Vacation mode”)."), html.p ( html.input (type="hidden", name="!vacation", value="off"), render_bootstrap_submit ("Turn on Notification!") ), action="", method="post" ) ] methods = cursor.execute_tuples ("select pref_handling, pref_description from notification_handling order by pref_sequence") methoddict = dict (methods) categories = cursor.execute_tuples ("select DISTINCT category_code, pref_handling, category_sequence, category_description from notification_category_active_preference NATURAL JOIN notification_category NATURAL JOIN notification_applicability NATURAL JOIN (SELECT role_id, person_id FROM work_role_assignment) AS wra where person_id=%(person_id)s order by category_sequence", person_id=remote_identity.person_id) return 'Your Notification Preferences', [ html.form ( html.p ("You may set the notification method for each notification category separately:"), html.table ( html.tr ( html.th ("Category"), html.th ("Current Setting"), html.th ("New Setting") ), [ html.tr ( html.td (category.category_description), html.td (methoddict[category.pref_handling]), html.td (render_select (category.category_code, methods, category.pref_handling, class_="form-control input-margin")) ) for category in categories ] ), render_bootstrap_submit ("Update Notification Methods!"), action="", method="post" ), html.form ( html.p ("You may also turn off all notifications entirely (“Vacation mode”):"), html.p ( html.input (type="hidden", name="!vacation", value="on"), render_bootstrap_submit ("Turn off Notification!") ), action="", method="post" ) ] @use_remote_identity @use_form_param @return_html def faculty_notice_post_handler (cursor, remote_identity, form): """Notification preferences POST URL handler. Handle form submissions for modifying notification preferences. """ if '!vacation' in form: cursor.execute_none ("insert into notification_preference values (%(person_id)s, %(pref_suppress)s)", person_id=remote_identity.person_id, pref_suppress=(form.required_field_value ('!vacation') == 'on')) else: for category in cursor.execute_tuples ("select * from notification_category"): if category.category_code in form: cursor.execute_none ("insert into notification_category_preference values (%(person_id)s, %(category_code)s, %(pref_handling)s)", person_id=remote_identity.person_id, category_code=category.category_code, pref_handling=form.required_field_value (category.category_code)) raise status.HTTPFound ("notice") faculty_notice_handler = delegate_get_post (faculty_notice_get_handler, faculty_notice_post_handler)