Source code for pygrade

# -*- coding: utf-8 -*-

__author__ = 'Aron Culotta'
__email__ = 'aronwc@gmail.com'
__version__ = '0.2.5'

import csv
import errno
import git
import os
import re
import time


[docs]def extract_metadata(text, result): """ >>> extract_metadata('@name=a0/foo.py', {})['files_to_test'] ['a0/foo.py'] >>> extract_metadata('@name=a0/foo.py,a0/bar.py', {})['files_to_test'] ['a0/foo.py', 'a0/bar.py'] >>> extract_metadata('@possible_points=12.4', {})['possible_points'] 12.4 """ match = re.search(r'\@name\s*=\s*(.+)', text) if match: filenames = match.group(1) result['files_to_test'] = re.split('\s*,\s*', filenames) match = re.search(r'\@possible_points\s*=\s*([0-9\.]+)', text) if match: result['possible_points'] = float(match.group(1)) return result
[docs]def read_assignment_metadata(test_file): """ Extracts metadata in the comments of the unit test file. E.g.: @name=a0/boolean_search.py,a0/run.py @possible_points=50 """ result = {'files_to_test': None, 'possible_points': None} for line in open(test_file): extract_metadata(line, result) return result
[docs]def check_students(students): """ Make sure we have requisite fields for each student. """ for s in students: if 'github_repo' not in s: print(' missing github_repo for %s' % str(s)) for k, v in s.items(): s[k] = v.strip() if v else None
[docs]def read_students(path): """ Read a tab-separated file of students. The only required field is 'github_repo', which is this student's github repository. """ students = [line for line in csv.DictReader(open(path), delimiter='\t')] check_students(students) return students
[docs]def path2name(path): """ Get the basename of a file, minus any extensions. >>> path2name('foo/bar/baz.py') 'baz' """ return re.sub(r'\..+', '', os.path.basename(path))
[docs]def get_local_repo(s, path): """ Return the path to the local copy of this student's repository. """ return os.path.join(path, os.path.basename(s['github_repo']))
[docs]def clone_repo(student, path): try: repo = student['github_repo'] topath = get_local_repo(student, path) print(' cloning %s to %s ...' % (repo, topath)) git.repo.base.Repo.clone_from(repo + '.git', topath) return True except git.exc.GitCommandError as e: print(e) return False
[docs]def pull_repo(local_repo): try: repo = git.Repo(local_repo) repo.remotes[0].pull() except git.exc.GitCommandError as e: print(e) return False
[docs]def clone_repos(students, path): """ Clone all student repos. """ for s in students: clone_repo(s, path)
[docs]def mktmpdir(subdir): """ Make a unique, temporary directory for student repos. """ path = '%s-%d' % (subdir, time.time()) try: os.makedirs(path) except OSError as e: if e.errno == errno.EEXIST and os.path.isdir(path): pass else: raise return path