"""Automatic submission of grade revisions.
Python process to submit pending grade revisions to the Registar's Office.
"""
import argparse
from itertools import groupby
from operator import attrgetter
from sys import exit
from uw.emailutils import emailaddr
from uw.sql.wrap import open_psycopg2_db_service_cursor
from uw.web.html.format import format_datetime
from ...outgoing.make_mail import mailout
from ...util.mail import odyssey_email
from ..db.cursor import Cursor
mathuo_email = emailaddr ('Math Undergrad Office', 'hankmuo4@uwaterloo.ca')
rogrades_email = emailaddr ('RO Grades', 'records.math@uwaterloo.ca')
mail_width = 76
[docs]def main ():
"""Main program to submit grade revisions.
Either --pending or --resend N must be specified. The first option submits
any pending revisions while the other re-sends the information for a
previous submission. Additionally, --daily may be specified, which will
cause the program to do nothing if ran on a non-business day; and --versose
may be specified to cause an explanation to be printed if the program
decides to do nothing.
"""
parser = argparse.ArgumentParser ()
parser.add_argument ('--verbose', action='store_true')
parser.add_argument ('--daily', action='store_true')
group = parser.add_mutually_exclusive_group (required=True)
group.add_argument ('--pending', action='store_true')
group.add_argument ('--resend', nargs="?", default=argparse.SUPPRESS, type=int, metavar='submission_id')
parser.add_argument ('--debug', action='store_true')
args = parser.parse_args ()
cursor = open_psycopg2_db_service_cursor (cursor_class=Cursor)
if args.pending:
if args.daily and cursor.execute_required_value ("select business_day is null from uw_date where date = current_date"):
if args.verbose:
print("Not a business day, won't submit grade revisions")
exit ()
submission_id = cursor.callproc_required_value ("grade_record_submission")
else:
submission_id = args.resend
current_time = cursor.execute_required_value ("select localtimestamp")
revisions = cursor.execute_tuples ("select grc.*, off_course_section_format (term_id, class_id) as section, uw_term_format (term_id) as term, group_code, program_code from grade_revision_complete as grc join std_term_program using (term_id, uw_id) join pps_program using (program_code) where submission_id = %(submission_id)s order by term_id, class_id, surname, givennames, uw_id", submission_id=submission_id)
if not revisions:
if args.verbose:
print("No pending revisions, won't submit grade revisions")
exit ()
preparers = cursor.execute_tuples ("select distinct pp.*, external_id as userid from person_person pp natural join (select * from person_identity where external_id_type = 'USER') t join grade_revision_submitted on (pp.person_id = preparer_person_id) where submission_id = %(submission_id)s", submission_id=submission_id)
preparer_descriptions = dict ((r.person_id, '%(surname)s, %(givennames)s (%(userid)s)' % r._asdict ()) for r in preparers)
content_lines = [
'The following grade revisions have been submitted (submission_id=%d):' % submission_id,
]
for term, r in groupby (revisions, attrgetter ('term_id', 'term')):
content_lines.extend ([
'',
'',
('**** %04d ** %s ' % term).ljust (mail_width, '*'),
])
for section, s in groupby (r, attrgetter ('class_id', 'section')):
s = tuple (s)
content_lines.extend ([
'',
('-- %04d -- %s ' % section).ljust (mail_width, '-'),
])
preparers = set (p.preparer_person_id for p in s)
if len (preparers) != 1:
prepared_lines = []
footnotes = {}
def footnote (person_id):
if person_id in footnotes:
return footnotes[person_id]
else:
result = len (footnotes) + 1
footnotes[person_id] = result
prepared_lines.append ('*%d: %s' % (result, preparer_descriptions[person_id]))
return result
for t in s:
t = t._asdict ()
t['names'] = '%(surname)s, %(givennames)s' % t
line = '%(group_code)s-%(program_code)s %(external_id)s %(course_grade)3s -> %(student_grade)3s %(names)-30.30s' % t
if len (preparers) != 1:
line += ' *%d' % footnote (t['preparer_person_id'])
content_lines.append (line)
if len (preparers) == 1:
(p,) = preparers
content_lines.append ('Prepared by: %s' % preparer_descriptions[p])
else:
content_lines.append ('Prepared by:')
content_lines.extend (prepared_lines)
content = '\n'.join (content_lines)
if args.debug:
print(content)
else:
message = mailout (cursor, odyssey_email, 'Grade Revisions Submitted %s' % format_datetime (current_time), content)
message.add_recipient ('To', rogrades_email)
message.add_recipient ('Cc', mathuo_email)
message.add_recipient ('Cc', odyssey_email)
message.done ()
cursor.connection.commit ()