@@ -5,5 +5,6 @@ require_relative 'kgrader/course' | |||||
require_relative 'kgrader/errors' | require_relative 'kgrader/errors' | ||||
require_relative 'kgrader/filesystem' | require_relative 'kgrader/filesystem' | ||||
require_relative 'kgrader/roster' | require_relative 'kgrader/roster' | ||||
require_relative 'kgrader/submission' | |||||
require_relative 'kgrader/task' | require_relative 'kgrader/task' | ||||
require_relative 'kgrader/util' | require_relative 'kgrader/util' |
@@ -1,17 +1,39 @@ | |||||
require 'open3' | |||||
module KGrader::Backend | module KGrader::Backend | ||||
class SVN | class SVN | ||||
def initialize(filesystem, config) | |||||
def initialize(filesystem, course, config) | |||||
@fs = filesystem | @fs = filesystem | ||||
@course = course | |||||
@config = config | @config = config | ||||
end | end | ||||
def fetch(semester, assignment, student) | |||||
url = @config['url'] % { | |||||
:semester => semester, :assignment => assignment, :student => student } | |||||
def revision(repo) | |||||
# TODO | # TODO | ||||
puts "[fetching #{student}: #{url}]" | |||||
-1 | |||||
end | |||||
def clone(repo, semester, assignment, student) | |||||
url = get_url semester, assignment, student | |||||
run 'checkout', '--ignore-externals', url, repo | |||||
end | |||||
def update(repo) | |||||
run 'update', '--ignore-externals', '--accept', 'tf', repo | |||||
end | |||||
private | |||||
def run(*cmd) | |||||
Open3.capture2e('svn', *cmd) | |||||
end | |||||
def get_url(semester, assignment, student) | |||||
@config['url'] % { | |||||
:semester => semester, | |||||
:assignment => assignment, | |||||
:student => student | |||||
} | |||||
end | end | ||||
end | end | ||||
end | end |
@@ -8,7 +8,7 @@ module KGrader | |||||
@config = @fs.load @fs.course_config(@name) | @config = @fs.load @fs.course_config(@name) | ||||
type = @config['backend'] | type = @config['backend'] | ||||
@backend = KGrader::backend(type).new self, @config[type] | |||||
@backend = KGrader::backend(type).new @fs, self, @config[type] | |||||
@rosters = {} | @rosters = {} | ||||
@assignments = {} | @assignments = {} | ||||
rescue FilesystemError | rescue FilesystemError | ||||
@@ -1,3 +1,4 @@ | |||||
require 'json' | |||||
require 'yaml' | require 'yaml' | ||||
module KGrader | module KGrader | ||||
@@ -37,6 +38,10 @@ module KGrader | |||||
File.join desk, courseid, semester, '_roster.csv' | File.join desk, courseid, semester, '_roster.csv' | ||||
end | end | ||||
def submission(courseid, semester, assignment, student) | |||||
File.join desk, courseid, semester, assignment, student | |||||
end | |||||
# ------------------------------------------------------------------------- | # ------------------------------------------------------------------------- | ||||
def courses | def courses | ||||
@@ -44,7 +49,7 @@ module KGrader | |||||
end | end | ||||
def assignments(courseid) | def assignments(courseid) | ||||
Dir[assignment courseid '*'].map! { |fn| File.basename File.dirname fn } | |||||
Dir[assignment courseid, '*'].map! { |fn| File.basename File.dirname fn } | |||||
end | end | ||||
def semesters(courseid) | def semesters(courseid) | ||||
@@ -55,6 +60,10 @@ module KGrader | |||||
def load(path) | def load(path) | ||||
case File.extname path | case File.extname path | ||||
when '.txt' | |||||
File.read path | |||||
when '.json' | |||||
JSON.parse File.read(path) | |||||
when '.yml', '.yaml' | when '.yml', '.yaml' | ||||
YAML.load File.read(path) | YAML.load File.read(path) | ||||
when '.csv' | when '.csv' | ||||
@@ -0,0 +1,70 @@ | |||||
module KGrader | |||||
class Submission | |||||
attr_reader :course, :semester, :assignment, :student | |||||
def initialize(filesystem, course, semester, assignment, student) | |||||
@fs = filesystem | |||||
@course = course | |||||
@semester = semester | |||||
@assignment = assignment | |||||
@student = student | |||||
@root = @fs.submission @course.name, @semester, @assignment.name, student | |||||
@status = nil | |||||
end | |||||
def status | |||||
@status ||= @fs.load(statusfile).to_sym | |||||
end | |||||
def status=(new_status) | |||||
File.write statusfile, new_status | |||||
@status = new_status | |||||
end | |||||
def exists? | |||||
File.exists? statusfile | |||||
end | |||||
def create | |||||
FileUtils.mkdir_p @root | |||||
self.status = :init | |||||
end | |||||
def fetch(due) | |||||
if status == :init | |||||
@course.backend.clone repo, @semester, @assignment.id, @student | |||||
rewind due | |||||
self.status = :ungraded | |||||
else | |||||
oldrev = revision if status == :graded | |||||
self.status = :fetching | |||||
@course.backend.update repo | |||||
rewind due | |||||
self.status = revision == oldrev ? :graded : :ungraded | |||||
end | |||||
end | |||||
def grade | |||||
# TODO | |||||
# self.status = :graded | |||||
end | |||||
private | |||||
def statusfile | |||||
File.join @root, "status.txt" | |||||
end | |||||
def repo | |||||
File.join @root, "repo" | |||||
end | |||||
def revision | |||||
@course.backend.revision repo | |||||
end | |||||
def rewind(date) | |||||
# TODO | |||||
end | |||||
end | |||||
end |
@@ -2,52 +2,62 @@ module KGrader | |||||
class Task | class Task | ||||
def initialize(filesystem, course, semester, assignment) | def initialize(filesystem, course, semester, assignment) | ||||
@fs = filesystem | |||||
@course = course | |||||
@semester = semester | |||||
@fs = filesystem | |||||
@course = course | |||||
@semester = semester | |||||
@assignment = @course.assignment assignment | @assignment = @course.assignment assignment | ||||
@students = @course.roster(@semester).students | |||||
@students = @course.roster(@semester).students | |||||
end | end | ||||
def grade(options = {}) | def grade(options = {}) | ||||
students = @students | |||||
students &= options[:students] unless options[:students].nil? | |||||
submissions = get_submissions options[:students] | |||||
due = options.fetch(:due, Time.now) | due = options.fetch(:due, Time.now) | ||||
fetch = options.fetch(:fetch, true) | fetch = options.fetch(:fetch, true) | ||||
regrade = options.fetch(:regrade, false) | regrade = options.fetch(:regrade, false) | ||||
# TODO | |||||
puts "[grading]" | |||||
puts "course => #{@course.name}" | |||||
puts "semester => #{@semester}" | |||||
puts "assignment => #{@assignment.name}" | |||||
puts "students => #{students.join ', '}" | |||||
puts "due => #{due}" | |||||
puts "fetch => #{fetch}" | |||||
puts "regrade => #{regrade}" | |||||
puts | |||||
count = submissions.count | |||||
puts "[grading #{count} student#{'s' if count != 1}]" | |||||
fetch_students students if fetch | |||||
submissions.each do |sub| | |||||
unless sub.exists? | |||||
puts "[init #{sub.student}]" | |||||
sub.create | |||||
end | |||||
end | |||||
if fetch | |||||
submissions.each do |sub| | |||||
puts "[fetch #{sub.student}]" | |||||
sub.fetch due | |||||
end | |||||
end | |||||
submissions.each do |sub| | |||||
sub.status = :ungraded if regrade | |||||
if sub.status == :ungraded | |||||
puts "[grade #{sub.student}]" | |||||
sub.grade | |||||
end | |||||
end | |||||
end | end | ||||
def commit(options = {}) | def commit(options = {}) | ||||
students = @students | |||||
students &= options[:students] unless options[:students].nil? | |||||
submissions = get_submissions options[:students] | |||||
# TODO | # TODO | ||||
puts "[committing]" | puts "[committing]" | ||||
puts "course => #{@course.name}" | puts "course => #{@course.name}" | ||||
puts "semester => #{@semester}" | puts "semester => #{@semester}" | ||||
puts "assignment => #{@assignment.name}" | puts "assignment => #{@assignment.name}" | ||||
puts "students => #{students.join ', '}" | |||||
puts "students => #{submissions.map { |sub| sub.student }.join ', '}" | |||||
end | end | ||||
private | private | ||||
def fetch_students(students) | |||||
students.each do |student| | |||||
@course.backend.fetch @semester, @assignment.id, student | |||||
def get_submissions(students) | |||||
students.nil? ? (students = @students) : (students &= @students) | |||||
students.map do |student| | |||||
Submission.new @fs, @course, @semester, @assignment, student | |||||
end | end | ||||
end | end | ||||
end | end | ||||