"""Course admin unit-related TA assignment display routines.
These are functions related to displaying TA information for course admin units.
"""
from operator import attrgetter
from itertools import groupby
from ll.xist.ns import html
from uw.web.html.form import render_select, render_hidden
from uw.web.html.format import make_table_format, format_return, format_date
from uw.web.wsgi.delegate import delegate_get_post
from uw.web.wsgi.form import use_form_param, parse_form_value
from uw.web.wsgi.function import return_html
from uw.web.wsgi.status import HTTPFound, HTTPForbidden
from uw.local.util.format import format_mailto_url, format_person
from uw.local.termtools import fromCode
from .ui import format_units, format_actual
from ..ui import check_mark
@delegate_get_post
@return_html
def admin_index (cursor, unit, term, roles):
result = [format_return ('Term')]
if 'TAMAN' in roles:
result.append (html.p ('This page shows all jobs which are active for the term and whose course is offered in the term. To active and deactive jobs, use the ', html.a ('Activate/Deactivate Positions', href="../positions/"), ' page. Normally changes to course offerings will be automatically imported from Quest, or may occur as a result of changes in the way course staff are organizing the courses.'))
admins = groupby (cursor.execute_tuples ("select * from ta_position_term_relevant_plus natural left join teaching_admin_person where (unit_code, term_id) = (%(unit_code)s, %(term_id)s) order by admin_description, job_description", unit_code=unit.unit_code, term_id=term.code ()), attrgetter ('admin_id'))
result.append (format_admins (cursor, admins, roles))
return '%s Jobs' % term.description (), result
[docs]def render_admin_job_details (cursor, unit, term, admin, ems_roles=set (), ta_roles=set (), prefix="", display_prefix=""):
"""Render job assignments for an offering as HTML.
All students assigned jobs with the specified term and admin unit will
be listed, along with their positions. If editable is True then form
elements will be included for editing the job assignments. Otherwise,
the display is intended for clients and will be adjusted appropriately
depending on the visibility state of assignments for the term.
"""
result = []
table_columns = [
(html.col (style="width: 25em;"), 'Student',
lambda ta: format_person (cursor, ta.person_id)),
(html.col (style="width: 5em;"), 'Units',
lambda ta: format_units (ta.assigned_units)),
]
if ems_roles:
h = html.h3
if cursor.eval_approved (unit_code=unit.unit_code, term_id=term.code (), admin_id=admin.admin_id) or not {'ISC', 'INST', 'ISA'} & ems_roles:
name = 'View…'
else:
name = 'Edit…'
table_columns.append ((html.col (style="width: 5em;"), 'Evaluation',
lambda ta: (html.a (name, href="%s%d/" % (display_prefix, ta.eval_id)), ' ', (check_mark) if ta.complete else None)
if ta.eval_id and {'ISC', 'INST', 'ISA'} & ems_roles else None))
if unit.assignments_finalized:
result.append (html.p ('TA assignments have been finalized. Occasional changes may still be necessary. However, you can expect to be informed separately if this happens.'))
elif unit.assignments_tentative:
result.append (html.p ('TA assignments are still tentative. Assignments are expected to be finalized %s.'
% format_date (unit.finalized_date, 'later')))
else:
result.append (html.p ('TA assignments are still in progress. Tentative assignments are expected to be ready %s.'
% format_date (unit.tentative_date, 'later')))
return result
if unit.eval_due:
result.append (html.p ("TA evaluations deadline has passed. Best TA Awards have been finalized."))
elif unit.eval_due_date is not None:
result.append (html.p ("TA evaluations are due %s" % format_date (unit.eval_due_date)))
else:
h = html.h2
if 'TAMAN' in ta_roles:
name = 'Edit…'
else:
name = 'View…'
table_columns.append ((html.col (style="width: 10em;"), 'Actions',
lambda ta: html.a (name, href="../../../%s/student/%d/" % (ta.term_id, ta.person_id))))
jobs = cursor.execute_tuples ("select * from ta_position_term_relevant_plus where (term_id, unit_code, admin_id) = (%(term_id)s, %(unit_code)s, %(admin_id)s) order by job_description", term_id=term.code (), unit_code=unit.unit_code, admin_id=admin.admin_id)
links = []
ta_person_ids = []
result.append (links)
for job in jobs:
tas = cursor.execute_tuples ("select * from ta_position_assignment natural join person_person left join ta_eval_complete using (unit_code, term_id, admin_id, job_code, person_id) where (term_id, unit_code, admin_id, job_code) = (%(term_id)s, %(unit_code)s, %(admin_id)s, %(job_code)s) order by surname, givennames", term_id=term.code (), unit_code=unit.unit_code, admin_id=admin.admin_id, job_code=job.job_code)
if job.position_target_units == job.total_assigned_units:
units = format_units (job.position_target_units, True)
else:
units = '%s planned, %s assigned' % (format_units (job.position_target_units, True), format_units (job.total_assigned_units, True))
job_person_ids = [r.person_id for r in tas]
ta_person_ids.extend (job_person_ids)
email = html.a ('Email all…', href=format_mailto_url (cursor, job_person_ids, admin.admin_description))
h = html.h3
if ems_roles or ta_roles:
result.append (h (job.job_description, ' — ', units, ' — ', email))
else:
result.append (h (job.job_description, ' — ', units))
if not job.position_active:
result.append (html.p (
'This job is inactive. Either it should be activated', [' on the ',
html.a ('Activate/Deactivate Positions', href="%spositions/" % prefix),
' page'] if 'TAMAN' in ta_roles else None, ', or all students should be removed from this job and the job cancelled for the term.'
))
if job.position_target_units:
result.append (make_table_format (*table_columns, use_colgroup=True) (tas))
if 'TAMAN' in ta_roles:
if job.position_active:
# Job active, allow editing planned units
change_units = [
html.p (
'Change planned units to ',
html.input (type="text", name="units", value=format_units (job.position_target_units)),
),
html.p (
'Update comment: ',
html.input (type="text", name="comment", value=job.position_comment),
),
html.p (
html.input (type="submit", name="!edit", value="Update Units and Comment!"),
)
]
elif not tas:
# Allow cancelling job only if no TAs assigned
change_units = [
html.p (
'Update comment: ',
html.input (type="text", name="comment", value=job.position_comment),
),
html.p (
html.input (type="submit", name="!edit", value="Update Units and Comment!"),
),
html.p (
html.input (type="submit", name="!cancel", value="Cancel Job!"),
)
]
else:
# Job inactive but TAs assigned, must unassign TAs first
change_units = (
html.p (
'Update comment: ',
html.input (type="text", name="comment", value=job.position_comment),
),
html.p (
html.input (type="submit", name="!edit", value="Update Units and Comment!"),
)
)
result.append (html.form (
html.p (
render_hidden ("unit", job.unit_code),
render_hidden ("job", job.job_code),
change_units
),
method="post", action=""
))
if ems_roles:
# Download link code doesn't really belong in this file but this is
# only called from offering page (not editable) and from TA
# application (editable) so should be OK.
links.append (html.p (
html.a ('Email all…', href=format_mailto_url (cursor, ta_person_ids, admin.admin_description)),
'; ',
html.a ('Download CSV', href="%s../../talist" % display_prefix),
))
return result
@return_html
def admin_get_handler (cursor, unit, term, admin, roles):
result = [format_return ('Term', 'Course List')]
result.append (render_admin_job_details (cursor, unit, term, admin,ta_roles=roles, prefix="../../"))
return '%s Job Details for %s' % (term.description (), admin.admin_description), result
@use_form_param
@return_html
def admin_post_handler (cursor, unit, term, admin, form, roles):
if not 'TAMAN' in roles:
raise HTTPForbidden ()
if "!edit" in form:
job_code = form.required_field_value ("job")
units = form.optional_field_value ("units")
if units is not None:
try:
units = parse_form_value (units, float)
except ValueError as x:
return 'Error: Cannot understand number of units', html.p (
'“%s” does not look like a number of units.' % units
)
cursor.callproc_none ("ta_edit_position", unit.unit_code, term.code (), admin.admin_id, job_code, units)
comment = form.optional_field_value ("comment")
if comment is not None:
cursor.callproc_none ("ta_edit_position_comment", unit.unit_code, term.code (), admin.admin_id, job_code, comment)
if "!cancel" in form:
job_code = form.required_field_value ("job")
cursor.callproc_none ("ta_drop_position", unit.unit_code, term.code (), admin.admin_id, job_code)
raise HTTPFound ("")
admin_handler = delegate_get_post (admin_get_handler, admin_post_handler)
@delegate_get_post
@return_html
def admin_misallocated_handler (cursor, unit, term, roles):
result = [format_return ('Term')]
admins = [(position.admin_id, position) for position in cursor.execute_tuples ("select * from ta_position_term_relevant_plus natural left join teaching_admin_person where (unit_code, term_id) = (%(unit_code)s, %(term_id)s) and position_target_units is distinct from total_assigned_units order by admin_description, job_description", unit_code=unit.unit_code, term_id=term.code ())]
noplan = []
under = []
over = []
for admin_id, position in admins:
if position.position_target_units is None:
noplan.append ((admin_id, [position]))
elif position.position_target_units > position.total_assigned_units:
under.append ((admin_id, [position]))
else:
over.append ((admin_id, [position]))
result.append (html.h2 ('No Plan'))
result.append (format_admins (cursor, noplan, roles))
result.append (html.h2 ('Underallocated'))
result.append (format_admins (cursor, under, roles))
result.append (html.h2 ('Overallocated'))
result.append (format_admins (cursor, over, roles))
return '%s Misallocated Jobs' % term.description (), result
@delegate_get_post
@return_html
def admin_history (cursor, unit, term, admin, roles):
result = [format_return ('TA Management', 'Term', None)]
term_ids = cursor.execute_values ("select term_id from teaching_admin_term where admin_id = %(admin_id)s order by term_id desc", admin_id=admin.admin_id)
for term_id in term_ids:
hist_term = fromCode (term_id)
result.append (html.h2 (hist_term.description ()))
ta_term_admin_unit = cursor.execute_optional_tuple ("select ttu.*, unit_description from ta_term_unit_plus ttu natural join uw_unit natural join (select unit_code, term_id from ta_position_term_relevant_plus where (term_id, admin_id) = (%(term_id)s, %(admin_id)s) group by term_id, unit_code) tpt where unit_code = %(unit_code)s", term_id=hist_term.code(), admin_id=admin.admin_id, unit_code=unit.unit_code)
if ta_term_admin_unit:
result.append (render_admin_job_details (cursor, ta_term_admin_unit, hist_term, admin,
display_prefix='../../%s/award/eval/' % hist_term.code ()))
else:
result.append (html.h3 ("No Assignments for %s" % hist_term.description ()))
return '%s Assignment History' % admin.admin_description, result