diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -125,6 +125,46 @@ end end + def result + if !Configuration.show_grading_result + redirect_to :action => 'list' and return + end + @user = User.find(session[:user_id]) + @submission = Submission.find(params[:id]) + if @submission.user!=@user + flash[:notice] = 'You are not allowed to view result of other users.' + redirect_to :action => 'list' and return + end + prepare_grading_result(@submission) + end + + def load_output + if !Configuration.show_grading_result or params[:num]==nil + redirect_to :action => 'list' and return + end + @user = User.find(session[:user_id]) + @submission = Submission.find(params[:id]) + if @submission.user!=@user + flash[:notice] = 'You are not allowed to view result of other users.' + redirect_to :action => 'list' and return + end + case_num = params[:num].to_i + out_filename = output_filename(@user.login, + @submission.problem.name, + @submission.id, + case_num) + if !FileTest.exists?(out_filename) + flash[:notice] = 'Output not found.' + redirect_to :action => 'list' and return + end + + response.headers['Content-Type'] = "application/force-download" + response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\"" + response.headers["X-Sendfile"] = out_filename + response.headers['Content-length'] = File.size(out_filename) + render :nothing => true + end + def error @user = User.find(session[:user_id]) end @@ -156,5 +196,105 @@ end end + def prepare_grading_result(submission) + grading_info = Configuration.task_grading_info[submission.problem.name] + @test_runs = [] + if grading_info['testruns'].is_a? Integer + trun_count = grading_info['testruns'] + trun_count.times do |i| + @test_runs << [ read_grading_result(@user.login, + submission.problem.name, + submission.id, + i+1) ] + end + else + grading_info['testruns'].keys.sort.each do |num| + run = [] + testrun = grading_info['testruns'][num] + testrun.each do |c| + run << read_grading_result(@user.login, + submission.problem.name, + submission.id, + c) + end + @test_runs << run + end + end + end + + def grading_result_dir(user_name, problem_name, submission_id, case_num) + return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}" + end + + def output_filename(user_name, problem_name, submission_id, case_num) + dir = grading_result_dir(user_name,problem_name, submission_id, case_num) + return "#{dir}/output.txt" + end + + def read_grading_result(user_name, problem_name, submission_id, case_num) + dir = grading_result_dir(user_name,problem_name, submission_id, case_num) + result_file_name = "#{dir}/result" + if !FileTest.exists?(result_file_name) + return {:num => case_num, :msg => 'program did not run'} + else + results = File.open(result_file_name).readlines + run_stat = extract_running_stat(results) + output_filename = "#{dir}/output.txt" + if FileTest.exists?(output_filename) + output_file = true + output_size = File.size(output_filename) + else + output_file = false + output_size = 0 + end + + return { + :num => case_num, + :msg => results[0], + :run_stat => run_stat, + :output => output_file, + :output_size => output_size + } + end + end + + # copied from grader/script/lib/test_request_helper.rb + def extract_running_stat(results) + running_stat_line = results[-1] + + # extract exit status line + run_stat = "" + if !(/[Cc]orrect/.match(results[0])) + run_stat = results[0].chomp + else + run_stat = 'Program exited normally' + end + + logger.info "Stat line: #{running_stat_line}" + + # extract running time + if res = /r(.*)u(.*)s/.match(running_stat_line) + seconds = (res[1].to_f + res[2].to_f) + time_stat = "Time used: #{seconds} sec." + else + seconds = nil + time_stat = "Time used: n/a sec." + end + + # extract memory usage + if res = /s(.*)m/.match(running_stat_line) + memory_used = res[1].to_i + else + memory_used = -1 + end + + return { + :msg => "#{run_stat}\n#{time_stat}", + :running_time => seconds, + :exit_status => run_stat, + :memory_usage => memory_used + } + end + end diff --git a/app/models/configuration.rb b/app/models/configuration.rb --- a/app/models/configuration.rb +++ b/app/models/configuration.rb @@ -1,3 +1,5 @@ +require 'yaml' + # # This class also contains various login of the system. # @@ -6,6 +8,7 @@ SYSTEM_MODE_CONF_KEY = 'system.mode' @@configurations = nil + @@task_grading_info = nil def self.get(key) if @@configurations == nil @@ -47,6 +50,10 @@ return true end + def self.show_grading_result + return (get(SYSTEM_MODE_CONF_KEY)=='analysis') + end + def self.allow_test_request(user) mode = get(SYSTEM_MODE_CONF_KEY) if (mode=='contest') @@ -55,6 +62,13 @@ return false if mode=='analysis' return true end + + def self.task_grading_info + if @@task_grading_info==nil + read_grading_info + end + return @@task_grading_info + end protected def self.read_config @@ -74,5 +88,11 @@ end end end + + def self.read_grading_info + f = File.open(TASK_GRADING_INFO_FILENAME) + @@task_grading_info = YAML.load(f) + f.close + end end diff --git a/app/views/main/_submission_short.html.haml b/app/views/main/_submission_short.html.haml --- a/app/views/main/_submission_short.html.haml +++ b/app/views/main/_submission_short.html.haml @@ -12,9 +12,12 @@ %tt = submission.grader_comment = "]" + - if Configuration.show_grading_result + = " | " + = link_to '[detailed result]', :action => 'result', :id => submission.id = " | " = link_to('[msg]', {:action => 'compiler_msg', :id => submission.id}, {:popup => true}) = " | " - = link_to('[source]',{:action => 'source', :id => submission.id}) + = link_to('[src]',{:action => 'source', :id => submission.id}) = " | " = link_to '[submissions]', :action => 'submission', :id => problem_name diff --git a/app/views/main/_test_case_result.html.haml b/app/views/main/_test_case_result.html.haml new file mode 100644 --- /dev/null +++ b/app/views/main/_test_case_result.html.haml @@ -0,0 +1,18 @@ +%td + = test_case[:num] +%td{:style => 'padding-left: 5px; padding-right: 5px'} + = test_case[:msg] +- if test_case[:run_stat]!=nil + %td{:style => 'padding-left: 5px; padding-right: 5px'} + = test_case[:run_stat][:exit_status] + %td{:style => 'padding-left: 5px; padding-right: 3px; text-align: center'} + = test_case[:run_stat][:running_time] + %td{:style => 'padding-left: 5px; padding-right: 10px; text-align: right'} + = number_with_delimiter(test_case[:run_stat][:memory_usage]) + %td{:style => 'padding-left: 5px; padding-right: 5px'} + = link_to "output (#{number_to_human_size(test_case[:output_size])})", :action => 'load_output', :id => @submission.id, :num => test_case[:num] if test_case[:output] +- else + %td + %td + %td + %td diff --git a/app/views/main/result.html.haml b/app/views/main/result.html.haml new file mode 100644 --- /dev/null +++ b/app/views/main/result.html.haml @@ -0,0 +1,41 @@ += user_title_bar(@user) + +%h2 + Grading Result for Task + = @submission.problem.full_name + +%p + = "Submission: #{@submission.number}" + %br/ + = "Submitted at: #{format_short_time(@submission.submitted_at)}" + %br/ + = "Graded at #{format_short_time(@submission.graded_at)} " + %br/ + = "score: #{(@submission.points*100/@submission.problem.full_score).to_i} " if Configuration['ui.show_score'] + = " [" + %tt + = @submission.grader_comment + = "]" + +%table.info + %tr.info-head + %th Runs + %th Cases + %th Result + %th Exit + %th Time (s) + %th Memory (KB) + %th Output + - r = 0 + - @test_runs.each do |test_run| + - r += 1 + - case_count = test_run.length + - first_case = true + - test_run.each do |test_case| + %tr{:class => ((r%2==0) ? "info-even" : "info-odd")} + - if first_case + %td{:rowspan => case_count} + = r + - first_case = false + = render :partial => 'test_case_result', :locals => {:test_case => test_case} + diff --git a/app/views/tasks/list.html.erb b/app/views/tasks/list.html.erb --- a/app/views/tasks/list.html.erb +++ b/app/views/tasks/list.html.erb @@ -64,6 +64,23 @@ Macao, China Thailand
+ + Test data for all tasks:
+ .zip
+ .tgz
+ Beads:
+ .zip
+ .tgz
+ Roads:
+ .zip
+ .tgz
+ DNA:
+ .zip
+ .tgz
+ Notes:
+ Beads. The zip files contain the testing library that also outputs some magic numbers to verify the correct usage of the library. Files questions-*.txt are also prefixed with fake data.
+ Roads. The zip files contain verify.cpp that checks the output.
+