diff --git a/env_exam.rb b/env_exam.rb new file mode 100644 --- /dev/null +++ b/env_exam.rb @@ -0,0 +1,14 @@ + +PROBLEMS_DIR = "/home/jittat/grader/ev" + +USER_RESULT_DIR = "/home/jittat/grader/result" + +TALKATIVE = true + +def report_comment(comment) + if comment.chomp =~ /^P+$/ # all P's + 'passed' + else + 'failed' + end +end diff --git a/env_grading.rb b/env_grading.rb new file mode 100644 --- /dev/null +++ b/env_grading.rb @@ -0,0 +1,10 @@ + +PROBLEMS_DIR = "/home/jittat/grader/ev" + +USER_RESULT_DIR = "/home/jittat/grader/result" + +TALKATIVE = true + +def report_comment(comment) + comment.chomp +end diff --git a/environment.rb b/environment.rb new file mode 100644 --- /dev/null +++ b/environment.rb @@ -0,0 +1,6 @@ + +# Rails app directory +RAILS_APP_DIR = "/home/jittat/web_grader" + +# load the required environment file +require "env_#{GRADER_ENV}.rb" diff --git a/grader b/grader new file mode 100755 --- /dev/null +++ b/grader @@ -0,0 +1,109 @@ +#!/usr/bin/ruby + +def talk(str) + if TALKATIVE + puts str + end +end + +def execute(command, error_message="") + if not system(command) + puts "ERROR: #{error_message}" + exit(127) + end +end + +def save_source(submission,dir,fname) + f = File.open("#{dir}/#{fname}","w") + f.write(submission.source) + f.close +end + +def call_judge(problem_home,language,problem_out_dir,fname) + ENV['PROBLEM_HOME'] = problem_home + Dir.chdir problem_out_dir + cmd = "#{problem_home}/script/judge #{language} #{fname}" +# puts "CMD: #{cmd}" + system(cmd) +end + +def read_result(test_result_dir) + cmp_msg_fname = "#{test_result_dir}/compiler_message" + cmp_msg = File.open(cmp_msg_fname).read + + result_fname = "#{test_result_dir}/result" + comment_fname = "#{test_result_dir}/comment" + if FileTest.exist?(result_fname) + result = File.open(result_fname).readline.to_i + comment = File.open(comment_fname).readline.chomp + return {:points => result, + :comment => comment, + :cmp_msg => cmp_msg} + else + return {:points => 0, + :comment => 'compile error', + :cmp_msg => cmp_msg} + end +end + +def save_result(submission,result) + submission.graded_at = Time.now + submission.points = result[:points] + submission.grader_comment = report_comment(result[:comment]) + submission.compiler_message = result[:cmp_msg] + submission.save +end + +def grade(submission_id) + sub = Submission.find(submission_id) + user = sub.user + problem = sub.problem + + language = sub.language.name + lang_ext = sub.language.ext + # FIX THIS + talk 'some hack on language' + if language == 'cpp' + language = 'c++' + end + + user_dir = "#{USER_RESULT_DIR}/#{user.login}" + Dir.mkdir(user_dir) if !FileTest.exist?(user_dir) + + problem_out_dir = "#{user_dir}/#{problem.name}" + Dir.mkdir(problem_out_dir) if !FileTest.exist?(problem_out_dir) + + problem_home = "#{PROBLEMS_DIR}/#{problem.name}" + source_name = "#{problem.name}.#{lang_ext}" + + save_source(sub,problem_out_dir,source_name) + call_judge(problem_home,language,problem_out_dir,source_name) + save_result(sub,read_result("#{problem_out_dir}/test-result")) +end + +# reading environment +GRADER_ENV = 'exam' +if ARGV.length > 1 + GRADER_ENV = ARGV[1] +end +require "environment.rb" + +#main program + +talk 'Reading rails environment' + +RAILS_ENV = 'development' +require RAILS_APP_DIR + '/config/environment' + +current_dir = `pwd` + +talk 'Grader queue' +task = Task.find(:first, :order => 'created_at') +if task!=nil + grade(task.submission_id) + task.destroy +else + puts "No job" +end + + diff --git a/import_problem b/import_problem new file mode 100755 --- /dev/null +++ b/import_problem @@ -0,0 +1,89 @@ +#!/usr/bin/ruby + +# import_problem: +# * creates a directory for a problem in the current directory, +# * copy standard scripts +# * copy testdata in the old format and create standard testcase config file + +require 'erb' + +def input_filename(dir,i) + "#{dir}/input-#{i}.txt" +end + +def answer_filename(dir,i) + "#{dir}/answer-#{i}.txt" +end + +def copy_testcase(importing_test_dir,dir,i) + system("cp #{importing_test_dir}/#{i}.in #{input_filename(dir,i)}") + system("cp #{importing_test_dir}/#{i}.sol #{answer_filename(dir,i)}") +end + +def process_options(options) + i = 3 + while ii+1 + i += 1 + end + if ARGV[i]=='-m' + options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1 + i += 1 + end + i += 1 + end +end + +GRADER_DIR = "/home/jittat/grader/grader_ng" + +# print usage +if ARGV.length < 3 + puts "using: import_task problem importing_testcase_dir number_of_testcase [options]" + exit(127) +end + +# processing arguments +problem = ARGV[0] +testcase_dir = ARGV[1] +num_testcases = ARGV[2].to_i + +options = {:time_limit => 1, :mem_limit => 16} +process_options(options) + +# start working +puts "creating directories" + +system("mkdir #{problem}") +system("mkdir #{problem}/script") +system("mkdir #{problem}/test_cases") +system("cp #{GRADER_DIR}/std-script/* #{problem}/script") + +puts "copying testcases" + +1.upto(num_testcases) do |i| + system("mkdir #{problem}/test_cases/#{i}") + copy_testcase("#{testcase_dir}","#{problem}/test_cases/#{i}",i) +end + + +# generating all_tests.cfg +puts "generating testcase config file" + +template = %q{ +problem do + num_tests <%= num_testcases %> + full_score <%= num_testcases*10 %> + time_limit_each <%= options[:time_limit] %> + mem_limit_each <%= options[:mem_limit] %> + score_each 10 +end +} + +all_test_cfg = ERB.new(template) + +cfg_file = File.open("#{problem}/test_cases/all_test.cfg","w") +cfg_file.puts all_test_cfg.result +cfg_file.close + +puts "done" diff --git a/std-script/box b/std-script/box new file mode 100755 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e4f2ea36eea2568fa83f91863a08f81ca9b71f4b GIT binary patch literal 18780 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 []" + exit(0) +end + +language = ARGV[0] +test_num = ARGV[1].to_i +if ARGV.length >= 3 + output_file_name = ARGV[2] +else + output_file_name = "output.txt" +end + +load "#{problem_home}/test_cases/all_tests.cfg" +problem = Problem.get_instance + +output_file = File.new(output_file_name, "r") +answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt") +result_file = File.new("check_result", "w") + +output_file_content = output_file.read +answer_file_content = answer_file.read + +report_correct = lambda { + result_file.write "Correct\n" + result_file.write problem.get_score(test_num) + result_file.write "\n" + result_file.close + exit(0) +} + +report_wrong = lambda { + result_file.write "Incorrect\n" + result_file.write "0\n" + result_file.close + exit(0) +} + +################## +# Your code here # +################## +num_pattern = /^[0-9]*/ +if (output_file_content =~ num_pattern) == nil + report_wrong.call +end + +output_i = output_file_content.to_i +answer_i = answer_file_content.to_i + +if output_i == answer_i + report_correct.call +else + report_wrong.call +end diff --git a/std-script/compile b/std-script/compile new file mode 100755 --- /dev/null +++ b/std-script/compile @@ -0,0 +1,102 @@ +#!/bin/sh + +############################## +# +# Standard Compile Script +# +# Supported compilers: +# gcc, g++, and gpc. +# +############################## + +export C_COMPILER=/usr/bin/gcc +export CPLUSPLUS_COMPILER=/usr/bin/g++ +export PASCAL_COMPILER=/usr/bin/gpc + +export C_OPTIONS="-O2 -Wall" +export CPLUSPLUS_OPTIONS="-O2 -Wall" +export PASCAL_OPTIONS="-O2 -Wall" + +# Check for the correct number of arguments. Otherwise, print usage. +if [ $# -eq 0 -o $# -gt 4 ] +then + echo "Usage: $0 [] [] []" + echo + echo " is defaulted to \"source\"." + echo " is defaulted to \"a.out\"." + echo " is defaulted to \"compiler_message\"." + echo + exit 127 +fi + +# Retrieve the arguments. +if [ $# -ge 1 ] +then + export PROG_LANG=$1 + echo "programming language: ${PROG_LANG}" +fi + +if [ $# -ge 2 ] +then + export SOURCE_FILE=$2 +else + export SOURCE_FILE=source +fi +echo " source file: $SOURCE_FILE" + +if [ $# -ge 3 ] +then + export OUTPUT_FILE=$3 +else + export OUTPUT_FILE=a.out +fi +echo " output file: $OUTPUT_FILE" + +if [ $# -eq 4 ] +then + export MESSAGE_FILE=$4 +else + export MESSAGE_FILE=compiler_message +fi +echo " message file: $MESSAGE_FILE" + +echo + +# Remove any remaining output files or message files. +rm -Rf $OUTPUT_FILE +rm -Rf $MESSAGE_FILE + +# Check if the source file exists before attempt compiling. +if [ ! -f $SOURCE_FILE ] +then + echo "ERROR: The source file does not exist!" + echo "ERROR: The source file did not exist." > $MESSAGE_FILE + exit 127 +fi + +# Compile. +if [ $PROG_LANG = "c" ] +then + $C_COMPILER $C_OPTIONS -o $OUTPUT_FILE $SOURCE_FILE 2>$MESSAGE_FILE +elif [ $PROG_LANG = "c++" ] +then + $CPLUSPLUS_COMPILER $CPLUSPLUS_OPTIONS -o $OUTPUT_FILE $SOURCE_FILE 2>$MESSAGE_FILE +elif [ $PROG_LANG = "pascal" ] +then + $PASCAL_COMPILER $PASCAL_OPTIONS -o $OUTPUT_FILE $SOURCE_FILE 2>$MESSAGE_FILE +else + echo "ERROR: Invalid language specified!" + echo "ERROR: Invalid language specified!" > $MESSAGE_FILE + exit 127 +fi + +# Report success or failure. +if [ -f $OUTPUT_FILE ] +then + echo "Compilation was successful!" +else + echo "ERROR: Something was wrong during the compilation!" + echo "Dumping compiler message:" + echo + cat $MESSAGE_FILE +fi \ No newline at end of file diff --git a/std-script/grade b/std-script/grade new file mode 100755 --- /dev/null +++ b/std-script/grade @@ -0,0 +1,65 @@ +#!/usr/bin/ruby + +def char_comment(comment) + if comment =~ /[iI]ncorrect/ + '-' + elsif comment =~ /[Cc]orrect/ + 'P' + elsif comment =~ /[Tt]ime/ + 'T' + else + '?' + end +end + +problem_home = ENV['PROBLEM_HOME'] +require "#{problem_home}/script/test_dsl.rb" +load "#{problem_home}/test_cases/all_tests.cfg" +problem = Problem.get_instance + +if problem.well_formed? == false + puts "The problem specification is not well formed." + exit(127) +end + +all_score = 0 +all_comment = '' +(1..(problem.runs.length-1)).each do |k| + run = problem.runs[k] + run_score = 0 + run_comment = '' + run.tests.each do |test_num| + result_file_name = "#{test_num}/result" + if not File.exists?(result_file_name) + puts "Cannot find the file #{test_num}/result!" + exit(127) + end + + result_file = File.new(result_file_name, "r") + result_file_lines = result_file.readlines + run_score = run_score + result_file_lines[1].to_i + run_comment += char_comment(result_file_lines[0]) + result_file.close + end + + run_result_file = File.new("result-#{k}", "w") + run_result_file.write run_score + run_result_file.write "\n" + run_result_file.close + + run_comment_file = File.new("comment-#{k}", "w") + run_comment_file.write "#{run_comment}\n" + run_comment_file.close + + all_score = all_score + run_score + all_comment += run_comment +end + +result_file = File.new("result", "w") +result_file.write all_score +result_file.write "\n" +result_file.close + +comment_file = File.new("comment", "w") +comment_file.write "#{all_comment}\n" +comment_file.close diff --git a/std-script/judge b/std-script/judge new file mode 100755 --- /dev/null +++ b/std-script/judge @@ -0,0 +1,114 @@ +#!/usr/bin/ruby + +problem_home = ENV['PROBLEM_HOME'] + +def execute(command, error_message="") + if not system(command) + puts "ERROR: #{error_message}" + exit(127) + end +end + +# ARGV[0] --- language +# ARGV[1] --- program source file +# ARGV[2] --- test result directory +# ARGV[3] --- sandbox directory + +if ARGV.length < 2 || ARGV.length > 4 + puts "Usage: judge [] []" + puts " is defaulted to ./sandbox" + puts " is defaulted to ./test-result" + puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it." + exit(127) +end + +language = ARGV[0] +if language != "c" && language != "c++" && language != "pascal" + puts "You specified a language that is not supported." + exit(127) +end + +source_file = ARGV[1] +if File.exist?(source_file) == false + puts "The source file does not exist." + exit(127) +end + +puts "Making test result and sandbox directories..." + +current_dir = `pwd` +current_dir.strip! + +if ARGV.length >= 3 + test_result_dir = ARGV[2] +else + test_result_dir = "#{current_dir}/test-result" +end +puts "Test result directory: #{test_result_dir}" +system("rm -Rf #{test_result_dir}") +execute("mkdir #{test_result_dir}", "Cannot make directory #{test_result_dir}.") + +if ARGV.length >= 4 + sandbox_dir = ARGV[3] +else + sandbox_dir = "#{current_dir}/sandbox" +end +puts "Sandbox directory: #{sandbox_dir}" +system("rm -Rf #{sandbox_dir}") +execute("mkdir #{sandbox_dir}", "Cannot make directory #{sandbox_dir}") + +# Compile +puts +puts "Compiling..." +execute("cp #{source_file} #{sandbox_dir}", "Cannot copy the source file to #{sandbox_dir}") +begin + Dir.chdir sandbox_dir +rescue + puts "ERROR: Cannot change directory to #{sandbox_dir}." + exit(127) +end +execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!") +compile_message = `cat compiler_message` +compile_message.strip! +execute("mv compiler_message #{test_result_dir}", "Cannot move the compiler message to #{test_result_dir}.") +if compile_message != "" + puts "Cannot compile the source code. See message in #{test_result_dir}/compile_message" + exit(127) +else + execute("mv a.out #{test_result_dir}", "Cannot move the compiled program to #{test_result_dir}") + system("rm -Rf #{sandbox_dir}/*") +end + +require "#{problem_home}/script/test_dsl.rb" +load "#{problem_home}/test_cases/all_tests.cfg" +problem = Problem.get_instance + +if problem.well_formed? == false + puts "The problem specification is not well formed." + exit(127) +end + +# Doing the testing. +(1..(problem.num_tests)).each do |test_num| + puts + execute("cp #{test_result_dir}/a.out #{sandbox_dir}", "Cannot copy the compiled program into #{sandbox_dir}") + execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script") + execute("mkdir #{test_result_dir}/#{test_num}", "Cannot create directory #{test_result_dir}/#{test_num}") + execute("mv #{sandbox_dir}/result #{test_result_dir}/#{test_num}", "Cannot copy the result file into #{test_result_dir}/#{test_num}") + execute("mv #{sandbox_dir}/comment #{test_result_dir}/#{test_num}", "Cannot copy the comment file into #{test_result_dir}/#{test_num}") + execute("rm -Rf #{sandbox_dir}/*", "Cannot clear #{sandbox_dir}") +end + +# Grade +puts +puts "Grading..." +begin + Dir.chdir test_result_dir +rescue + puts "ERROR: Cannot change directory to #{test_result_dir}." + exit(127) +end +execute("#{problem_home}/script/grade", "An error occured during grading!") + +puts +puts "All done!" diff --git a/std-script/run b/std-script/run new file mode 100755 --- /dev/null +++ b/std-script/run @@ -0,0 +1,99 @@ +#!/usr/bin/ruby + +if ARGV.length < 2 || ARGV.length > 3 + puts "Usage: run []" + exit(127) +end + +language = ARGV[0] +test_num = ARGV[1].to_i +if ARGV.length > 2 + program_name = ARGV[2] +else + program_name = "a.out" +end + +problem_home = ENV['PROBLEM_HOME'] +require "#{problem_home}/script/test_dsl.rb" +load "#{problem_home}/test_cases/all_tests.cfg" +problem = Problem.get_instance + +if problem.well_formed? == false + puts "The problem specification is not well formed." + exit(127) +end + +# Check if the test number is okay. +if test_num <= 0 || test_num > problem.num_tests + puts "You have specified a wrong test number." + exit(127) +end + +##################################### +# Set the relavant file names here. # +##################################### + +input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt" + +##################################### + +time_limit = problem.get_time_limit test_num +mem_limit = problem.get_mem_limit(test_num) * 1024 + +# Copy the input file. +#`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .` + +# Run the program. +run_command = "/usr/bin/time -f \"%E\" 2>run_result #{problem_home}/script/box -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}" +puts "Running test #{test_num}..." +puts run_command +puts +system(run_command) + +# Create the result file. +result_file = File.new("result", "w") +comment_file = File.new("comment", "w") + +# Check if the program actually produced any output. +run_result_file = File.new("run_result", "r") +run_result = run_result_file.readlines +run_result_file.close +time_elapsed = run_result[run_result.length-1] + +report = lambda{ |status, points, comment| + result_file.write status.strip + result_file.write "\n" + result_file.write points.to_s.strip + result_file.write "\n" + result_file.write time_elapsed.strip + result_file.write "\n" + result_file.close + `rm run_result` + `rm output.txt` + + comment_file.write comment + comment_file.close + + puts + puts "Done!" + exit(0) +} + +if run_result[0][0,2] != "OK" + puts "There was a runtime error." + report.call(run_result[0], 0, "No comment.\n") +end + +# Run 'check' to evaluate the output. +#puts "There was no runtime error. Proceed to checking the output." +check_command = "#{problem_home}/script/check #{language} #{test_num}" +puts "Checking the output..." +puts check_command +if not system(check_command) + exit(127) +end + +check_file = File.new("check_result", "r") +check_file_lines = check_file.readlines + +report.call(check_file_lines[0], check_file_lines[1], "No comment.\n") diff --git a/std-script/test_dsl.rb b/std-script/test_dsl.rb new file mode 100644 --- /dev/null +++ b/std-script/test_dsl.rb @@ -0,0 +1,178 @@ +class DSLNode + def DSLNode.scalar_attr(*names) + names.each do |name| + define_method name do |*a| + if a.length == 0 + instance_variable_get( "@#{name}" ) + else + instance_variable_set( "@#{name}", a[0] ) + end + end + end + end + + def DSLNode.array_attr(*names) + names.each do |name| + define_method name do |*a| + if a.length == 0 + instance_variable_get( "@#{name}" ) + else + instance_variable_set( "@#{name}", a ) + end + end + end + end +end + +class Problem < DSLNode + def initialize + @runs = [] + @tests = [] + end + + def Problem.getter(name, plural_name, each_name) + eval "def get_#{name}(index) \n \ + if defined?(@tests) and @tests[index] != nil \n \ + if @tests[index].#{name} != nil \n \ + return @tests[index].#{name} \n \ + end \n \ + end \n \ + \n \ + (1..@runs.length-1).each do |i| \n \ + run = @runs[i] \n \ + k = run.tests.index(index) \n \ + if k == nil \n \ + next \n \ + end \n \ + \n \ + if run.#{plural_name} != nil && run.#{plural_name}[k] != nil \n \ + return run.#{plural_name}[k] \n \ + end \n \ + \n \ + if run.#{each_name} != nil \n \ + return run.#{each_name} \n \ + end \n \ + end \n \ + \n \ + if @#{each_name} != nil \n \ + return @#{each_name} \n \ + else \n \ + raise 'The problem is malformed (possibly in more than one way)!' \n \ + end \n \ + end" + end + + scalar_attr :num_tests, :full_score, :score_each, :time_limit_each, :mem_limit_each + array_attr :runs, :tests + getter "score", "scores", "score_each" + getter "mem_limit", "mem_limits", "mem_limit_each" + getter "time_limit", "time_limits", "time_limit_each" + + def run(index, &block) + new_run = Run.new + new_run.instance_eval &block + @runs[index] = new_run + end + + def test(index, &block) + new_test = Test.new + new_test.instance_eval &block + @tests[index] = new_test + end + + def read_test(index) + filename = ENV['PROBLEM_HOME'] + "/test_cases/#{index}/test.cfg" + if File.exists?(filename) + @tests[index] ||= Test.new + content = File.read(filename) + @tests[index].instance_eval content + end + end + + def Problem.set_instance(prob) + @instance = prob + end + + def Problem.get_instance + return @instance + end + + def well_formed? + # Check if run 1 to run @runs.length are present. + (1..(@runs.length-1)).each do |i| + if @runs[i] == nil + puts "run #{i} is not present" + return false + end + end + + # Check if all tests are in one and only one run. + test_present = [] + (1..(@num_tests)).each do |i| + test_present[i] = false + end + (1..(@runs.length-1)).each do |j| + run = @runs[j] + run.tests.each do |t| + if test_present[t] == false + test_present[t] = true + else + puts "test #{t} is present in more than one run" + return false + end + end + end + (1..(@num_tests)).each do |i| + if test_present[i] == false + puts "test #{i} is not present" + return false + end + end + + # Check if we can find the score, mem limit, and time limit for all tests. + (1..(@num_tests)).each do |i| + begin + get_score i + rescue + puts "cannot get score for test #{i}" + return false + end + + begin + get_mem_limit i + rescue + puts "cannot get mem limit for test #{i}" + return false + end + + begin + get_time_limit i + rescue + puts "cannot get time limit for test #{i}" + return false + end + end + + return true + end +end + +class Run < DSLNode + scalar_attr :score_each, :time_limit_each, :mem_limit_each + array_attr :tests, :scores, :time_limits, :mem_limits +end + +class Test < DSLNode + scalar_attr :score, :time_limit, :mem_limit +end + +def problem(&blk) + prob = Problem.new + prob.instance_eval &blk + Problem.set_instance prob + p = Problem.get_instance + (1..(p.num_tests)).each do |i| + p.read_test i + end + p.well_formed? +end