A code autograder for student homework submissions
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

86 lines
2.2 KiB

  1. require 'ruby-progressbar'
  2. module KGrader
  3. class Task
  4. def initialize(filesystem, course, semester, assignment, students = nil)
  5. @fs = filesystem
  6. @course = course
  7. @semester = semester
  8. @assignment = @course.assignment assignment
  9. @submissions = get_submissions students
  10. end
  11. def grade(options = {})
  12. due = options.fetch(:due, Time.now)
  13. fetch = options.fetch(:fetch, true)
  14. regrade = options.fetch(:regrade, false)
  15. superscore = options.fetch(:superscore, false)
  16. if options.include?(:due) && !fetch
  17. raise TaskError, "can't set a new due date without fetching"
  18. end
  19. if regrade && superscore
  20. raise TaskError, "can't regrade and superscore at the same time"
  21. end
  22. prepare
  23. subtask 'setup' do |sub|
  24. sub.create unless sub.exists?
  25. end
  26. if fetch
  27. subtask 'fetch' do |sub|
  28. sub.fetch due
  29. end
  30. end
  31. subtask 'grade' do |sub|
  32. if sub.status == :init || sub.status == :fetching
  33. next 'skip (need to fetch first)'
  34. elsif sub.status == :graded && !regrade && !superscore
  35. next
  36. else
  37. sub.grade superscore
  38. end
  39. end
  40. end
  41. def commit
  42. prepare
  43. subtask 'commit', &:commit
  44. end
  45. private
  46. def get_submissions(students)
  47. roster = @course.roster(@semester).students
  48. students.nil? ? (students = roster) : (students &= roster)
  49. students.map do |student|
  50. Submission.new @fs, @course, @semester, @assignment, student
  51. end
  52. end
  53. def student_len
  54. @student_len ||= @submissions.map { |sub| sub.student.length }.max
  55. end
  56. def prepare
  57. @course.backend.prepare @semester, @assignment
  58. end
  59. def subtask(name)
  60. progress = ProgressBar.create title: name, total: @submissions.size,
  61. throttle_rate: 0, format: '%t [%b>%i] %j%% %e '
  62. @submissions.each.with_index do |sub, i|
  63. job = "#{name} [#{sub.student.ljust student_len}]"
  64. progress.title = "#{job}:"
  65. result = yield sub
  66. progress.title = name if i == @submissions.size - 1
  67. progress.log "#{job}#{': ' if result}#{result}" if result
  68. progress.increment
  69. end
  70. end
  71. end
  72. end