Source code for uw.local.teaching.bin.daily_notify

"""Automatic daily notification process.

Python process meant to run every day to notify course staff of lack of seats,
inform AAS of needed information, and automatically assign seats.
"""

from sys import argv

from psycopg2 import InternalError

from uw.emailutils import emailaddr

from uw.sql.wrap import ConnectionPool, open_psycopg2_db_service_connection
from uw.web.html.join import english_join

from ..db.cursor import Cursor

from ...outgoing.make_mail import mailout
from ...util.mail import odyssey_email

from ..db.exam_print import submit_period_print_jobs, print_examination, message_to_course_staff

from ..webui.schedule import portal_url

[docs]def main_exam_page_url (exam): return 'https://odyssey.uwaterloo.ca/teaching/term/%s/%s/exam/%s/' % (exam.term_id, exam.admin_id, exam.exam_id)
[docs]def remind_approve (cursor, exam_id): exam = cursor.execute_required_tuple ("select * from exam_exam_plus where exam_id = %(exam_id)s", exam_id=exam_id) print('%s %s approval reminder' % (exam.primary_start_time, exam.full_title)) content_lines = [ 'You are receiving this email because you are recorded as being responsible for the %s being held %s.' % (exam.full_title, exam.times_formatted), '', 'At present, you have not approved the masters.', 'Masters must be approved in order for printing to take place.', '', 'Please visit the following URL to approve the uploaded masters:', '', 'https://odyssey.uwaterloo.ca/teaching/upload/%s/' % (exam.exam_id), ] content = '\n'.join (content_lines) message = mailout (cursor, odyssey_email, 'Masters not approved for %s at %s' % (exam.full_title, exam.times_formatted), content) message.add_recipient_person ('To', exam.exam_author_person_id) message.add_recipient ('Cc', odyssey_email) message.done () cursor.connection.commit ()
[docs]def assign_seats (cursor, exam_id, do_assign_seats): exam = cursor.execute_required_tuple ("select * from exam_exam_plus where exam_id = %(exam_id)s", exam_id=exam_id) print('%s %s assigning seats' % (exam.primary_start_time, exam.full_title)) cursor.callproc_none ("exam_assign_designate_seats", exam.exam_id) problem_sittings = [] for ees in cursor.sittings_by_exam (exam_id=exam.exam_id): if ees.count_assigned_seats < ees.count_assigned_candidates or ees.count_rush_seats < ees.count_rush_candidates: problem_sittings.append (ees) if problem_sittings: content_lines = [ 'You are receiving this email because you are recorded as being responsible for the %s being held %s.' % (exam.full_title, exam.times_formatted), '', 'At present, not enough seats are available for this assessment.', 'The following are several approaches to addressing this:', '', '- Book more rooms', '- Allow use of every seat or tablet seats', '- Request cramming of extra candidates into one or more rooms', '- Some candidates may be moved to AAS (AccessAbility Services)', '- Assume that some candidates will drop', '', 'Please visit the following URL to review the situation:', '', main_exam_page_url (exam), ] content = '\n'.join (content_lines) message = mailout (cursor, odyssey_email, 'Not enough seats for %s at %s' % (exam.full_title, exam.times_formatted), content) message_to_course_staff (cursor, message, exam.term_id, exam.admin_id, ['ISC']) message.add_recipient ('Cc', odyssey_email) message.done () cursor.connection.commit () elif do_assign_seats: try: cursor.callproc_required_value ("exam_assign_finish_assignment", exam_id) if exam.exam_assigned and exam.administer_admin_id != 20070: sittings_loc = cursor.execute_required_tuple("select array_agg (distinct building_code) filter (where building_code = 'PAC' or building_code = 'CIF') as sitting_gym_locs, array_agg (distinct building_code) filter (where building_code <> 'PAC' and building_code <> 'CIF' and sitting_admin_id <> 20060) as locs, bool_or (sitting_admin_id = 20060) as sitting_aas from exam_exam_sitting_room natural join exam_sitting natural join room_room where exam_id = %(exam_id)s", exam_id=exam.exam_id) content_lines = [ 'You are receiving this email because you are recorded as being responsible for the %s being held %s.' % (exam.full_title, exam.times_formatted), 'Seats and sequence numbers have been assigned for this assessment.', ''] if sittings_loc.sitting_gym_locs: content_lines.extend ([ 'This assessment has sittings located in the {0}. Posting Lists for seat assignment will be posted by University staff around the {0}.'.format (english_join (*sittings_loc.sitting_gym_locs)), 'Please do not hang up any additional postings. You may download and bring a Posting List to further aid students as needed.', '', ]) if sittings_loc.sitting_aas: content_lines.extend ([ 'This assessment has sittings run by AccessAbility Services (AAS). All AAS assessments will be delivered to and handled by AAS.', '' ]) if sittings_loc.locs: content_lines.extend ([ 'This assessment has sittings located in %s. Please download and bring a copy of the Posting List to these sittings.' % (english_join (*sittings_loc.locs)), '', ]) content_lines.extend ([ 'You may see assessment details and download the Posting List on the assessment main page:', '', main_exam_page_url (exam), '', 'Students should be able to see their own personal assessment schedule in Odyssey using the link below. Students should also be able to use uWaterloo Student Portal.', '', "https://odyssey.uwaterloo.ca/teaching/schedule", ]) content = '\n'.join (content_lines) message = mailout (cursor, odyssey_email, 'Assignment completed for %s on %s' % (exam.full_title, exam.times_formatted), content) message_to_course_staff (cursor, message, exam.term_id, exam.admin_id, ['ISC', 'INST']) message.add_recipient ('Cc', odyssey_email) message.done () cursor.connection.commit () except InternalError as x: cursor.connection.rollback () print('Unable to assign seats:') print(x)
[docs]def main (): connection_pool = ConnectionPool (open_psycopg2_db_service_connection (), Cursor) cursor = connection_pool.connect ().cursor () # Identify assessments that need seating checked or # seat and sequence assignment. exams = cursor.execute_tuples ("select * from exam_to_process") for exam in exams: if exam.do_remind_approve: remind_approve (cursor, exam.exam_id) if exam.do_assign_seats: assign_seats (cursor, exam.exam_id, exam.do_assign_seats) # Identify assessments that should be printed. exams = cursor.execute_tuples ("select * from exam_to_print") for exam in exams: print_examination (cursor, exam) # Identify RO deadlines that have passed. periods = cursor.execute_tuples ("select * from exam_print_deadline_plus where (term_id, administer_admin_id) = (uw_term_current (), 20050) and uw_next_business_day (deadline_date) <= current_date and deadline_processed is null order by period_id") for period in periods: print('Processing period: ', period) submit_period_print_jobs (cursor, period) cursor.connection.commit ()