-# Copyright (C) 2012 W. Trevor King <wking@drexel.edu>
+# Copyright (C) 2012 W. Trevor King <wking@tremily.us>
#
# This file is part of pygrader.
#
>>> stub_course = StubCourse(load=False)
>>> course = load_course(basedir=stub_course.basedir)
>>> course.name
- 'phys101'
+ 'Physics 101'
>>> course.assignments # doctest: +ELLIPSIS
[<pygrader.model.assignment.Assignment object at 0x...>, ...]
>>> course.people # doctest: +ELLIPSIS
[]
>>> print(course.robot)
<Person Robot101>
+ >>> stub_course.cleanup()
"""
_LOG.debug('loading course from {}'.format(basedir))
config = _configparser.ConfigParser()
ret -= offset
return ret
+def parse_boolean(value):
+ """Convert a boolean string into ``True`` or ``False``.
+
+ Supports the same values as ``RawConfigParser``
+
+ >>> parse_boolean('YES')
+ True
+ >>> parse_boolean('Yes')
+ True
+ >>> parse_boolean('tRuE')
+ True
+ >>> parse_boolean('False')
+ False
+ >>> parse_boolean('FALSE')
+ False
+ >>> parse_boolean('no')
+ False
+ >>> parse_boolean('none')
+ Traceback (most recent call last):
+ ...
+ ValueError: Not a boolean: none
+ >>> parse_boolean('') # doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ValueError: Not a boolean:
+
+ It passes through boolean inputs without modification (so you
+ don't have to use strings for default values):
+
+ >>> parse_boolean({}.get('my-option', True))
+ True
+ >>> parse_boolean({}.get('my-option', False))
+ False
+ """
+ if value in [True, False]:
+ return value
+ # Using an underscored method is hackish, but it should be fairly stable.
+ p = _configparser.RawConfigParser()
+ return p._convert_to_boolean(value)
+
def load_assignment(name, data):
r"""Load an assignment from a ``dict``
... data={'points': '1',
... 'weight': '0.1/2',
... 'due': '2011-10-04T00:00-04:00',
+ ... 'submittable': 'yes',
... })
- >>> print('{0.name} (points: {0.points}, weight: {0.weight}, due: {0.due})'.format(a))
- Attendance 1 (points: 1, weight: 0.05, due: 1317700800)
+ >>> print(('{0.name} (points: {0.points}, weight: {0.weight}, '
+ ... 'due: {0.due}, submittable: {0.submittable})').format(a))
+ Attendance 1 (points: 1, weight: 0.05, due: 1317700800, submittable: True)
>>> print(formatdate(a.due, localtime=True))
Tue, 04 Oct 2011 00:00:00 -0400
"""
assert len(wterms) == 2, wterms
weight = float(wterms[0])/float(wterms[1])
due = parse_date(data['due'])
- return _Assignment(name=name, points=points, weight=weight, due=due)
+ submittable = parse_boolean(data.get('submittable', False))
+ return _Assignment(
+ name=name, points=points, weight=weight, due=due,
+ submittable=submittable)
def load_person(name, data={}):
r"""Load a person from a ``dict``
return _Person(name=name, **kwargs)
def load_grades(basedir, assignments, people):
+ "Load all grades in a course directory."
for assignment in assignments:
for person in people:
- _LOG.debug('loading {} grade for {}'.format(assignment, person))
- path = assignment_path(basedir, assignment, person)
- gpath = _os_path.join(path, 'grade')
try:
- g = _load_grade(_io.open(gpath, 'r', encoding=_ENCODING),
- assignment, person)
+ yield load_grade(basedir, assignment, person)
except IOError:
continue
- #g.late = _os.stat(gpath).st_mtime > assignment.due
- g.late = _os_path.exists(_os_path.join(path, 'late'))
- npath = _os_path.join(path, 'notified')
- if _os_path.exists(npath):
- g.notified = newer(npath, gpath)
- else:
- g.notified = False
- yield g
-def _load_grade(stream, assignment, person):
+def load_grade(basedir, assignment, person):
+ "Load a single grade from a course directory."
+ _LOG.debug('loading {} grade for {}'.format(assignment, person))
+ path = assignment_path(basedir, assignment, person)
+ gpath = _os_path.join(path, 'grade')
+ g = parse_grade(_io.open(gpath, 'r', encoding=_ENCODING),
+ assignment, person)
+ #g.late = _os.stat(gpath).st_mtime > assignment.due
+ g.late = _os_path.exists(_os_path.join(path, 'late'))
+ npath = _os_path.join(path, 'notified')
+ if _os_path.exists(npath):
+ g.notified = newer(npath, gpath)
+ else:
+ g.notified = False
+ return g
+
+def parse_grade(stream, assignment, person):
+ "Parse the points and comment from a grade stream."
try:
points = float(stream.readline())
except ValueError:
Lpath = _os_path.join(path, 'late')
_touch(Lpath)
+def save_grade(basedir, grade):
+ "Save a grade into a course directory"
+ path = assignment_path(
+ basedir=basedir, assignment=grade.assignment, person=grade.student)
+ if not _os_path.isdir(path):
+ _os.makedirs(path)
+ gpath = _os_path.join(path, 'grade')
+ with _io.open(gpath, 'w', encoding=_ENCODING) as f:
+ f.write('{}\n'.format(grade.points))
+ if grade.comment:
+ f.write('\n{}\n'.format(grade.comment.strip()))
+ set_notified(basedir=basedir, grade=grade)
+ set_late(
+ basedir=basedir, assignment=grade.assignment, person=grade.student)
+
def _touch(path):
"""Touch a file (`path` is created if it doesn't already exist)