5 """Manage grades from the command line
8 import configparser as _configparser
9 from email.mime.text import MIMEText as _MIMEText
10 import email.utils as _email_utils
11 import inspect as _inspect
12 import logging as _logging
13 import os.path as _os_path
16 import pgp_mime as _pgp_mime
18 from pygrader import __version__
19 from pygrader import LOG as _LOG
20 from pygrader.email import test_smtp as _test_smtp
21 from pygrader.mailpipe import mailpipe as _mailpipe
22 from pygrader.storage import initialize as _initialize
23 from pygrader.storage import load_course as _load_course
24 from pygrader.tabulate import tabulate as _tabulate
25 from pygrader.template import assignment_email as _assignment_email
26 from pygrader.template import course_email as _course_email
27 from pygrader.template import student_email as _student_email
28 from pygrader.todo import print_todo as _todo
31 if __name__ == '__main__':
32 from argparse import ArgumentParser as _ArgumentParser
34 parser = _ArgumentParser(
35 description=__doc__, version=__version__)
37 '-d', '--base-dir', dest='basedir', default='.',
38 help='Base directory containing grade data')
40 '-c', '--color', default=False, action='store_const', const=True,
41 help='Color printed output with ANSI escape sequences')
43 '-V', '--verbose', default=0, action='count',
44 help='Increase verbosity')
45 subparsers = parser.add_subparsers(title='commands')
47 smtp_parser = subparsers.add_parser(
48 'smtp', help=_test_smtp.__doc__.splitlines()[0])
49 smtp_parser.set_defaults(func=_test_smtp)
50 smtp_parser.add_argument(
52 help='Your address (email author)')
53 smtp_parser.add_argument(
54 '-t', '--target', dest='targets', action='append',
55 help='Address for the email recipient')
57 initialize_parser = subparsers.add_parser(
58 'initialize', help=_initialize.__doc__.splitlines()[0])
59 initialize_parser.set_defaults(func=_initialize)
60 initialize_parser.add_argument(
61 '-D', '--dry-run', default=False, action='store_const', const=True,
62 help="Don't actually send emails, create files, etc.")
64 tabulate_parser = subparsers.add_parser(
65 'tabulate', help=_tabulate.__doc__.splitlines()[0])
66 tabulate_parser.set_defaults(func=_tabulate)
67 tabulate_parser.add_argument(
68 '-s', '--statistics', default=False, action='store_const', const=True,
69 help='Calculate mean and standard deviation for each assignment')
71 email_parser = subparsers.add_parser(
72 'email', help='Send emails containing grade information')
73 email_parser.add_argument(
74 '-D', '--dry-run', default=False, action='store_const', const=True,
75 help="Don't actually send emails, create files, etc.")
76 email_parser.add_argument(
77 '-a', '--author', default='Trevor King',
78 help='Your name (email author)')
79 email_parser.add_argument(
80 '--cc', action='append', help='People to carbon copy')
81 email_subparsers = email_parser.add_subparsers(title='type')
82 assignment_parser = email_subparsers.add_parser(
83 'assignment', help=_assignment_email.__doc__.splitlines()[0])
84 assignment_parser.set_defaults(func=_assignment_email)
85 assignment_parser.add_argument(
86 'assignment', help='Name of the target assignment')
87 student_parser = email_subparsers.add_parser(
88 'student', help=_student_email.__doc__.splitlines()[0])
89 student_parser.set_defaults(func=_student_email)
90 student_parser.add_argument(
91 '-o', '--old', default=False, action='store_const', const=True,
92 help='Include already-notified information in emails')
93 student_parser.add_argument(
94 '-s', '--student', dest='student',
95 help='Explicitly select the student to notify (instead of everyone)')
96 course_parser = email_subparsers.add_parser(
97 'course', help=_course_email.__doc__.splitlines()[0])
98 course_parser.set_defaults(func=_course_email)
99 course_parser.add_argument(
100 '-t', '--target', dest='targets', action='append',
101 help='Name, alias, or group for the email recipient(s)')
103 mailpipe_parser = subparsers.add_parser(
104 'mailpipe', help=_mailpipe.__doc__.splitlines()[0])
105 mailpipe_parser.set_defaults(func=_mailpipe)
106 mailpipe_parser.add_argument(
107 '-D', '--dry-run', default=False, action='store_const', const=True,
108 help="Don't actually send emails, create files, etc.")
109 mailpipe_parser.add_argument(
110 '-m', '--mailbox', choices=['maildir', 'mbox'],
111 help=('Instead of piping a message in via stdout, you can also read '
112 'directly from a mailbox. This option specifies the format of '
113 'your target mailbox.'))
114 mailpipe_parser.add_argument(
115 '-i', '--input', dest='input_', metavar='INPUT',
116 help='Path to the mailbox containing messages to be processed')
117 mailpipe_parser.add_argument(
119 help=('Path to the mailbox that will recieve successfully processed '
120 'messages. If not given, successfully processed messages will '
121 'be left in the input mailbox'))
122 mailpipe_parser.add_argument(
123 '-l', '--max-late', default=0, type=float,
124 help=('Grace period in seconds before an incoming assignment is '
125 'actually marked as late'))
127 todo_parser = subparsers.add_parser(
128 'todo', help=_todo.__doc__.splitlines()[0])
129 todo_parser.set_defaults(func=_todo)
130 todo_parser.add_argument(
131 'source', help='Name of source file/directory')
132 todo_parser.add_argument(
133 'target', help='Name of target file/directory')
136 # p.add_option('-t', '--template', default=None)
138 args = parser.parse_args()
141 _LOG.setLevel(max(_logging.DEBUG, _LOG.level - 10*args.verbose))
142 _pgp_mime.LOG.setLevel(_LOG.level)
144 config = _configparser.ConfigParser()
146 _os_path.expanduser(_os_path.join('~', '.config', 'smtplib.conf')),
149 func_args = _inspect.getargspec(args.func).args
152 if 'basedir' in func_args:
153 kwargs['basedir'] = args.basedir
155 if 'course' in func_args:
156 course = _load_course(basedir=args.basedir)
157 active_groups = course.active_groups()
158 kwargs['course'] = course
159 if hasattr(args, 'assignment'):
160 kwargs['assignment'] = course.assignment(name=args.assignment)
161 if hasattr(args, 'cc') and args.cc:
162 kwargs['cc'] = [course.person(name=cc) for cc in args.cc]
163 for attr in ['author', 'student']:
164 if hasattr(args, attr):
165 name = getattr(args, attr)
166 kwargs[attr] = course.person(name=name)
167 for attr in ['targets']:
168 if hasattr(args, attr):
169 people = getattr(args, attr)
171 people = ['professors'] # for the course email
173 for person in people:
174 if person in active_groups:
175 kwargs[attr].extend(course.find_people(group=person))
177 kwargs[attr].extend(course.find_people(name=person))
178 for attr in ['dry_run', 'mailbox', 'output', 'input_', 'max_late',
179 'old', 'statistics']:
180 if hasattr(args, attr):
181 kwargs[attr] = getattr(args, attr)
182 elif args.func == _test_smtp:
183 for attr in ['author', 'targets']:
184 if hasattr(args, attr):
185 kwargs[attr] = getattr(args, attr)
186 elif args.func == _todo:
187 for attr in ['source', 'target']:
188 if hasattr(args, attr):
189 kwargs[attr] = getattr(args, attr)
191 if 'use_color' in func_args:
192 kwargs['use_color'] = args.color
194 if ('smtp' in func_args and
195 not kwargs.get('dry_run', False) and
196 'smtp' in config.sections()):
197 params = _pgp_mime.get_smtp_params(config)
198 kwargs['smtp'] = _pgp_mime.get_smtp(*params)
201 _LOG.debug('execute {} with {}'.format(args.func, kwargs))
203 ret = args.func(**kwargs)
205 smtp = kwargs.get('smtp', None)
207 _LOG.info('disconnect from SMTP server')