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.

134 lines
2.9 KiB

  1. module KGrader
  2. class Submission
  3. attr_reader :course, :semester, :assignment, :student
  4. def initialize(filesystem, course, semester, assignment, student)
  5. @fs = filesystem
  6. @course = course
  7. @semester = semester
  8. @assignment = assignment
  9. @student = student
  10. @root = @fs.submission @course.name, @semester, @assignment.name, student
  11. @status = nil
  12. end
  13. def status
  14. @status ||= @fs.load(statusfile).to_sym
  15. end
  16. def status=(new_status)
  17. File.write statusfile, new_status
  18. @status = new_status
  19. end
  20. def exists?
  21. File.exists? statusfile
  22. end
  23. def create
  24. FileUtils.mkdir_p @root
  25. self.status = :init
  26. nil
  27. end
  28. def fetch(due)
  29. if status == :init
  30. @course.backend.clone repo, @semester, @assignment.id, @student
  31. rewind due
  32. self.status = :ungraded
  33. else
  34. oldrev = revision if status == :graded
  35. self.status = :fetching
  36. @course.backend.update repo
  37. newrev = rewind due
  38. self.status = newrev == oldrev ? :graded : :ungraded
  39. end
  40. nil
  41. end
  42. def grade
  43. self.status = :ungraded
  44. stage
  45. build
  46. test
  47. save
  48. # self.status = :graded # UNCOMMENT
  49. # @fs.jail.reset # UNCOMMENT
  50. # TODO: return grade summary string
  51. end
  52. def commit
  53. if status == :graded && File.exists?(pendingfile)
  54. target = File.join(repo, @assignment.report)
  55. message = @assignment.commit_message @student
  56. FileUtils.cp gradefile, target
  57. @course.backend.commit repo, message, target
  58. FileUtils.rm pendingfile
  59. end
  60. end
  61. private
  62. def repo
  63. File.join @root, 'repo'
  64. end
  65. def statusfile
  66. File.join @root, 'status.txt'
  67. end
  68. def gradefile
  69. File.join @root, 'grade.txt'
  70. end
  71. def pendingfile
  72. File.join @root, 'pending'
  73. end
  74. def revision
  75. @course.backend.revision repo
  76. end
  77. def rewind(date)
  78. log = @course.backend.log repo
  79. target = log.find { |commit| commit[:date] <= date }
  80. if target.nil?
  81. raise SubmissionError, "no commits before due date: #{student}"
  82. end
  83. rev = target[:rev]
  84. @course.backend.update repo, rev
  85. rev
  86. end
  87. def stage
  88. @fs.jail.reset
  89. @fs.jail.init
  90. @assignment.manifest[:provided].each do |entry|
  91. @fs.jail.stage entry[:path], entry[:name]
  92. end
  93. @assignment.manifest[:graded].each do |entry|
  94. @fs.jail.stage File.join(repo, entry[:name]), entry[:name]
  95. end
  96. end
  97. def build
  98. @assignment.build_steps.each do |command|
  99. @fs.jail.exec command
  100. end
  101. end
  102. def test
  103. @assignment.tests.each do |script|
  104. # TODO: execute script in jail
  105. end
  106. end
  107. def save
  108. # TODO: save gradefile
  109. FileUtils.touch pendingfile
  110. end
  111. end
  112. end