@@ -31,7 +31,8 @@ To show all known classes, semesters, and assignments: | |||
rake list | |||
To load a roster for a specific semester: | |||
To load a roster (a newline-delimited list of student identifiers) for a | |||
specific semester: | |||
rake roster cs123 myroster.csv semester=sp16 | |||
@@ -26,19 +26,19 @@ module KGrader | |||
end | |||
def roster(course, semester, rosterfile) | |||
course = fetch_course course | |||
course = Course.new(@fs, course) | |||
semester ||= course.current_semester | |||
course.roster(semester).load rosterfile | |||
end | |||
def grade(course, semester, assignment, options = {}) | |||
course = fetch_course course | |||
course = Course.new(@fs, course) | |||
semester ||= course.current_semester | |||
course.task(semester, assignment).grade options | |||
end | |||
def commit(course, semester, assignment, options = {}) | |||
course = fetch_course course | |||
course = Course.new(@fs, course) | |||
semester ||= course.current_semester | |||
course.task(semester, assignment).commit options | |||
end | |||
@@ -58,12 +58,6 @@ module KGrader | |||
end | |||
private | |||
def fetch_course(course) | |||
Course.new(@fs, course) | |||
rescue FilesystemError | |||
KGrader::die "unknown course" | |||
end | |||
def reset_jail | |||
FileUtils.rm_rf @fs.jail | |||
FileUtils.mkdir @fs.jail | |||
@@ -11,6 +11,8 @@ module KGrader | |||
@config = @fs.load @fs.course_config(@name) | |||
@rosters = {} | |||
rescue FilesystemError | |||
raise CourseError, "unknown or invalid course: #{name}" | |||
end | |||
def roster(semester) | |||
@@ -30,12 +32,7 @@ module KGrader | |||
end | |||
def current_semester | |||
case @config['semesters'] | |||
when 'faspYY' | |||
KGrader::season + DateTime.now.strftime('%y') | |||
when 'faspYYYY' | |||
KGrader::season + DateTime.now.strftime('%Y') | |||
end | |||
KGrader::current_semester @config['semesters'] | |||
end | |||
end | |||
end |
@@ -2,6 +2,18 @@ module KGrader | |||
class KGraderError < StandardError | |||
end | |||
class ArgumentError < KGraderError | |||
end | |||
class FilesystemError < KGraderError | |||
end | |||
class ConfigError < KGraderError | |||
end | |||
class CourseError < KGraderError | |||
end | |||
class RosterError < KGraderError | |||
end | |||
end |
@@ -52,10 +52,10 @@ module KGrader | |||
when '.csv' | |||
File.read(path).split("\n").map! { |line| line.split "," } | |||
else | |||
raise FilesystemError, "unknown file type" | |||
raise FilesystemError, "unknown file type: #{path}" | |||
end | |||
rescue SystemCallError # Errno::ENOENT, etc. | |||
raise FilesystemError, "can't read file" | |||
raise FilesystemError, "can't read file: #{path}" | |||
end | |||
end | |||
end |
@@ -13,10 +13,14 @@ module KGrader | |||
@students = @fs.load(filename).map! { |item| item.first } | |||
FileUtils.mkdir_p File.dirname(rosterfile) | |||
File.write rosterfile, @students.join("\n") | |||
rescue FilesystemError => err | |||
raise RosterError, err | |||
end | |||
def students | |||
@students ||= @fs.load(rosterfile).map! { |item| item.first } | |||
rescue FilesystemError | |||
raise RosterError, "unknown semester: #{semester}" | |||
end | |||
private | |||
@@ -6,23 +6,32 @@ module KGrader | |||
@course = course | |||
@semester = semester | |||
@assignment = assignment | |||
@roster = @course.roster @semester | |||
@students = @course.roster(@semester).students | |||
end | |||
def grade(options = {}) | |||
students = @roster.students | |||
students = @students | |||
students &= options[:students] unless options[:students].nil? | |||
due = options.fetch(:due, Time.now) | |||
fetch = options.fetch(:fetch, true) | |||
regrade = options.fetch(:regrade, false) | |||
# TODO | |||
puts "Grading #{@course.name}:#{@semester} assignment #{@assignment}..." | |||
puts "- options: #{options}" | |||
puts "- students: #{students.inspect}" | |||
puts "- due: #{due}" | |||
puts "- fetch: #{fetch}" | |||
puts "- regrade: #{regrade}" | |||
end | |||
def commit(options = {}) | |||
students = @students | |||
students &= options[:students] unless options[:students].nil? | |||
# TODO | |||
puts "Committing #{@course.name}:#{@semester} assignment #{@assignment}..." | |||
puts "- options: #{options}" | |||
puts "- students: #{students.inspect}" | |||
end | |||
end | |||
end |
@@ -1,10 +1,6 @@ | |||
require 'date' | |||
require 'time' | |||
module KGrader | |||
def self.die(error) | |||
Kernel::abort "fatal: #{error}" | |||
end | |||
def self.parse_args(raw, num, keywords) | |||
args = [] | |||
options = {} | |||
@@ -13,7 +9,9 @@ module KGrader | |||
if arg.include? '=' | |||
key, val = arg.split('=', 2) | |||
key = key.to_sym | |||
die "unknown keyword #{key}" unless keywords.include? key | |||
unless keywords.include? key | |||
raise ArgumentError, "unknown keyword: #{key}" | |||
end | |||
options[key] = case keywords[key] | |||
when :string | |||
val | |||
@@ -21,20 +19,28 @@ module KGrader | |||
%w(true yes 1 t y).include? val.downcase | |||
when :array | |||
val.split(",").map! { |x| x.strip.downcase } | |||
when :datetime | |||
DateTime.parse(val) | |||
when :time | |||
Time.parse(val) | |||
end | |||
else | |||
args << arg | |||
end | |||
end | |||
die "too few arguments" if args.size < num | |||
die "too many arguments" if args.size > num | |||
raise ArgumentError, "too few arguments" if args.size < num | |||
raise ArgumentError, "too many arguments" if args.size > num | |||
return args, options | |||
end | |||
def self.season | |||
DateTime.now.strftime('%m').to_i <= 6 ? 'sp' : 'fa' | |||
def self.current_semester(format) | |||
season = Time.now.strftime('%m').to_i <= 6 ? 'sp' : 'fa' | |||
case format | |||
when 'faspYY' | |||
season + Time.now.strftime('%y') | |||
when 'faspYYYY' | |||
season + Time.now.strftime('%Y') | |||
else | |||
raise ConfigError, "unknown semester format: #{format}" | |||
end | |||
end | |||
end |
@@ -1,13 +1,21 @@ | |||
require_relative 'lib/kgrader' | |||
def cli | |||
KGrader::CLI.new Rake.application.original_dir | |||
def die(error) | |||
abort "fatal: [#{error.class}] #{error}" | |||
end | |||
def run | |||
yield KGrader::CLI.new Rake.application.original_dir | |||
rescue KGrader::KGraderError => err | |||
die err | |||
end | |||
def parse_args(num, keywords = {}) | |||
args, options = KGrader::parse_args ARGV.drop(1), num, keywords | |||
args.each { |arg| task arg.to_sym {} } | |||
args + [options] | |||
rescue KGrader::KGraderError => err | |||
die err | |||
end | |||
task :default => :help do ; end | |||
@@ -24,31 +32,31 @@ task :help do | |||
end | |||
task :list do | |||
cli.list | |||
run { |cli| cli.list } | |||
end | |||
task :roster do | |||
course, rosterfile, options = parse_args 2, { :semester => :string } | |||
cli.roster course, options[:semester], rosterfile | |||
run { |cli| cli.roster course, options[:semester], rosterfile } | |||
end | |||
task :grade do | |||
course, assignment, options = parse_args 2, | |||
{ :semester => :string, :students => :array, :due => :datetime, | |||
{ :semester => :string, :students => :array, :due => :time, | |||
:fetch => :bool, :regrade => :bool } | |||
cli.grade course, options[:semester], assignment, options | |||
run { |cli| cli.grade course, options[:semester], assignment, options } | |||
end | |||
task :commit do | |||
course, assignment, options = parse_args 2, | |||
{ :semester => :string, :students => :array } | |||
cli.commit course, options[:semester], assignment, options | |||
run { |cli| cli.commit course, options[:semester], assignment, options } | |||
end | |||
task :clean do | |||
cli.clean | |||
run { |cli| cli.clean } | |||
end | |||
task :clobber do | |||
cli.clobber | |||
run { |cli| cli.clobber } | |||
end |