From ca45c8e1059a05255108c2cf25ccf29ee0aff128 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Fri, 1 Apr 2016 01:03:00 -0500 Subject: [PATCH] More SVN fetch code, grading. --- lib/kgrader.rb | 1 + lib/kgrader/backend/svn.rb | 34 ++++++++++++++++++---- lib/kgrader/course.rb | 2 +- lib/kgrader/filesystem.rb | 11 +++++++- lib/kgrader/submission.rb | 70 ++++++++++++++++++++++++++++++++++++++++++++++ lib/kgrader/task.rb | 58 ++++++++++++++++++++++---------------- 6 files changed, 144 insertions(+), 32 deletions(-) create mode 100644 lib/kgrader/submission.rb diff --git a/lib/kgrader.rb b/lib/kgrader.rb index b34a778..85fa912 100644 --- a/lib/kgrader.rb +++ b/lib/kgrader.rb @@ -5,5 +5,6 @@ require_relative 'kgrader/course' require_relative 'kgrader/errors' require_relative 'kgrader/filesystem' require_relative 'kgrader/roster' +require_relative 'kgrader/submission' require_relative 'kgrader/task' require_relative 'kgrader/util' diff --git a/lib/kgrader/backend/svn.rb b/lib/kgrader/backend/svn.rb index e6142b6..db175d9 100644 --- a/lib/kgrader/backend/svn.rb +++ b/lib/kgrader/backend/svn.rb @@ -1,17 +1,39 @@ +require 'open3' + module KGrader::Backend class SVN - def initialize(filesystem, config) + def initialize(filesystem, course, config) @fs = filesystem + @course = course @config = config end - def fetch(semester, assignment, student) - url = @config['url'] % { - :semester => semester, :assignment => assignment, :student => student } - + def revision(repo) # 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 diff --git a/lib/kgrader/course.rb b/lib/kgrader/course.rb index 06ef294..a80fab1 100644 --- a/lib/kgrader/course.rb +++ b/lib/kgrader/course.rb @@ -8,7 +8,7 @@ module KGrader @config = @fs.load @fs.course_config(@name) type = @config['backend'] - @backend = KGrader::backend(type).new self, @config[type] + @backend = KGrader::backend(type).new @fs, self, @config[type] @rosters = {} @assignments = {} rescue FilesystemError diff --git a/lib/kgrader/filesystem.rb b/lib/kgrader/filesystem.rb index ce9bb6c..439dce0 100644 --- a/lib/kgrader/filesystem.rb +++ b/lib/kgrader/filesystem.rb @@ -1,3 +1,4 @@ +require 'json' require 'yaml' module KGrader @@ -37,6 +38,10 @@ module KGrader File.join desk, courseid, semester, '_roster.csv' end + def submission(courseid, semester, assignment, student) + File.join desk, courseid, semester, assignment, student + end + # ------------------------------------------------------------------------- def courses @@ -44,7 +49,7 @@ module KGrader end def assignments(courseid) - Dir[assignment courseid '*'].map! { |fn| File.basename File.dirname fn } + Dir[assignment courseid, '*'].map! { |fn| File.basename File.dirname fn } end def semesters(courseid) @@ -55,6 +60,10 @@ module KGrader def load(path) case File.extname path + when '.txt' + File.read path + when '.json' + JSON.parse File.read(path) when '.yml', '.yaml' YAML.load File.read(path) when '.csv' diff --git a/lib/kgrader/submission.rb b/lib/kgrader/submission.rb new file mode 100644 index 0000000..3b66c2b --- /dev/null +++ b/lib/kgrader/submission.rb @@ -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 diff --git a/lib/kgrader/task.rb b/lib/kgrader/task.rb index c7d7179..f13d7bb 100644 --- a/lib/kgrader/task.rb +++ b/lib/kgrader/task.rb @@ -2,52 +2,62 @@ module KGrader class Task def initialize(filesystem, course, semester, assignment) - @fs = filesystem - @course = course - @semester = semester + @fs = filesystem + @course = course + @semester = semester @assignment = @course.assignment assignment - @students = @course.roster(@semester).students + @students = @course.roster(@semester).students end def grade(options = {}) - students = @students - students &= options[:students] unless options[:students].nil? - + submissions = get_submissions options[:students] due = options.fetch(:due, Time.now) fetch = options.fetch(:fetch, true) 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 def commit(options = {}) - students = @students - students &= options[:students] unless options[:students].nil? + submissions = get_submissions options[:students] # TODO puts "[committing]" puts "course => #{@course.name}" puts "semester => #{@semester}" puts "assignment => #{@assignment.name}" - puts "students => #{students.join ', '}" + puts "students => #{submissions.map { |sub| sub.student }.join ', '}" end 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