@@ -10,6 +10,13 @@ It is written in Ruby. | |||
Installation | |||
------------ | |||
kgrader was developed using Ruby 2.2, though it should work with any recent | |||
version of Ruby. | |||
Install dependencies with gem: | |||
gem install ruby-progressbar | |||
Download kgrader over git: | |||
git clone https://github.com/earwig/kgrader.git kgrader | |||
@@ -24,21 +24,21 @@ module KGrader | |||
course.roster(semester).load rosterfile | |||
end | |||
def grade(course, semester, assignment, options = {}) | |||
def grade(course, semester, assignment, students = nil, options = {}) | |||
course = Course.new(@fs, course) | |||
semester ||= course.current_semester | |||
course.task(semester, assignment).grade options | |||
course.task(semester, assignment, students).grade options | |||
end | |||
def commit(course, semester, assignment, options = {}) | |||
def commit(course, semester, assignment, students = nil) | |||
course = Course.new(@fs, course) | |||
semester ||= course.current_semester | |||
course.task(semester, assignment).commit options | |||
course.task(semester, assignment, students).commit | |||
end | |||
def clean | |||
# TODO: also purge uncommitted grades | |||
clear_jail | |||
# TODO: also purge uncommitted grades: set all graded to ungraded and delete all pending files | |||
end | |||
def clobber | |||
@@ -23,8 +23,8 @@ module KGrader | |||
@assignments[name] ||= Assignment.new @fs, self, name | |||
end | |||
def task(semester, assignment) | |||
Task.new @fs, self, semester, assignment | |||
def task(semester, assignment, students = nil) | |||
Task.new @fs, self, semester, assignment, students | |||
end | |||
def rosters | |||
@@ -10,7 +10,7 @@ module KGrader | |||
end | |||
def load(filename) | |||
@students = @fs.load(filename).map! { |item| item.first } | |||
@students = @fs.load(filename).map! &:first | |||
FileUtils.mkdir_p File.dirname(rosterfile) | |||
File.write rosterfile, @students.join("\n") | |||
rescue FilesystemError => err | |||
@@ -18,7 +18,7 @@ module KGrader | |||
end | |||
def students | |||
@students ||= @fs.load(rosterfile).map! { |item| item.first } | |||
@students ||= @fs.load(rosterfile).map! &:first | |||
rescue FilesystemError | |||
raise RosterError, "unknown semester: #{semester}" | |||
end | |||
@@ -29,6 +29,7 @@ module KGrader | |||
def create | |||
FileUtils.mkdir_p @root | |||
self.status = :init | |||
nil | |||
end | |||
def fetch(due) | |||
@@ -43,20 +44,48 @@ module KGrader | |||
rewind due | |||
self.status = revision == oldrev ? :graded : :ungraded | |||
end | |||
nil | |||
end | |||
def grade | |||
# TODO | |||
# TODO: | |||
# self.status = :ungraded | |||
# [grade the stuff] | |||
# [save report to gradefile] | |||
# FileUtils.touch pendingfile | |||
# self.status = :graded | |||
# return grade summary string | |||
sleep rand / 2 | |||
'100%' | |||
end | |||
def commit | |||
# TODO: | |||
# if status == :graded && File.exists? pendingfile | |||
# [copy gradefile to repo and commit] | |||
# FileUtils.rm pendingfile | |||
# end | |||
sleep rand / 2 | |||
nil | |||
end | |||
private | |||
def repo | |||
File.join @root, "repo" | |||
end | |||
def statusfile | |||
File.join @root, "status.txt" | |||
end | |||
def repo | |||
File.join @root, "repo" | |||
def gradefile | |||
File.join @root, "grade.txt" | |||
end | |||
def pendingfile | |||
File.join @root, "pending" | |||
end | |||
def revision | |||
@@ -1,64 +1,71 @@ | |||
require 'ruby-progressbar' | |||
module KGrader | |||
class Task | |||
def initialize(filesystem, course, semester, assignment) | |||
@fs = filesystem | |||
@course = course | |||
@semester = semester | |||
@assignment = @course.assignment assignment | |||
@students = @course.roster(@semester).students | |||
def initialize(filesystem, course, semester, assignment, students = nil) | |||
@fs = filesystem | |||
@course = course | |||
@semester = semester | |||
@assignment = @course.assignment assignment | |||
@submissions = get_submissions students | |||
end | |||
def grade(options = {}) | |||
submissions = get_submissions options[:students] | |||
due = options.fetch(:due, Time.now) | |||
fetch = options.fetch(:fetch, true) | |||
regrade = options.fetch(:regrade, false) | |||
count = submissions.count | |||
puts "[grading #{count} student#{'s' if count != 1}]" | |||
submissions.each do |sub| | |||
unless sub.exists? | |||
puts "[init #{sub.student}]" | |||
sub.create | |||
end | |||
subtask 'setup' do |sub| | |||
sub.create unless sub.exists? | |||
end | |||
if fetch | |||
submissions.each do |sub| | |||
puts "[fetch #{sub.student}]" | |||
subtask 'fetch' do |sub| | |||
sub.fetch due | |||
end | |||
end | |||
submissions.each do |sub| | |||
sub.status = :ungraded if regrade | |||
if sub.status == :ungraded | |||
puts "[grade #{sub.student}]" | |||
subtask 'grade' do |sub| | |||
if sub.status == :init || sub.status == :fetching | |||
next 'skip (need to fetch first)' | |||
elsif sub.status == :graded && !regrade | |||
next | |||
else | |||
sub.grade | |||
end | |||
end | |||
end | |||
def commit(options = {}) | |||
submissions = get_submissions options[:students] | |||
# TODO | |||
puts "[committing]" | |||
puts "course => #{@course.name}" | |||
puts "semester => #{@semester}" | |||
puts "assignment => #{@assignment.name}" | |||
puts "students => #{submissions.map { |sub| sub.student }.join ', '}" | |||
def commit | |||
subtask 'commit', &:commit | |||
end | |||
private | |||
def get_submissions(students) | |||
students.nil? ? (students = @students) : (students &= @students) | |||
roster = @course.roster(@semester).students | |||
students.nil? ? (students = roster) : (students &= roster) | |||
students.map do |student| | |||
Submission.new @fs, @course, @semester, @assignment, student | |||
end | |||
end | |||
def student_len | |||
@student_len ||= @submissions.map { |sub| sub.student.length }.max | |||
end | |||
def subtask(name) | |||
progress = ProgressBar.create title: name, total: @submissions.size, | |||
throttle_rate: 0, format: '%t [%b>%i] %j%% %e ' | |||
@submissions.each.with_index do |sub, i| | |||
job = "#{name} [#{sub.student.ljust student_len}]" | |||
progress.title = "#{job}:" | |||
result = yield sub | |||
progress.title = name if i == @submissions.size - 1 | |||
progress.log "#{job}#{': ' if result}#{result}" if result | |||
progress.increment | |||
end | |||
end | |||
end | |||
end |
@@ -45,13 +45,15 @@ task :grade do | |||
course, assignment, options = parse_args 2..2, | |||
{ :semester => :string, :students => :array, :due => :time, | |||
:fetch => :bool, :regrade => :bool } | |||
run { |cli| cli.grade course, options[:semester], assignment, options } | |||
semester, students = options[:semester], options[:students] | |||
run { |cli| cli.grade course, semester, assignment, students, options } | |||
end | |||
task :commit do | |||
course, assignment, options = parse_args 2..2, | |||
{ :semester => :string, :students => :array } | |||
run { |cli| cli.commit course, options[:semester], assignment, options } | |||
semester, students = options[:semester], options[:students] | |||
run { |cli| cli.commit course, semester, assignment, students } | |||
end | |||
task :clean do | |||