# HG changeset patch # User jittat # Date 2009-11-30 07:52:08 # Node ID ffd23bd6817d84a2a21b809ce85d99d3d328ac99 # Parent e7056be0091a4c8ceb507fc8eb2b38498312093f added problem import git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@435 6386c4cd-e34a-4fa8-8920-d93eb39b512e diff --git a/app/controllers/problems_controller.rb b/app/controllers/problems_controller.rb --- a/app/controllers/problems_controller.rb +++ b/app/controllers/problems_controller.rb @@ -15,6 +15,7 @@ verify :method => :post, :only => [ :destroy, :create, :quick_create, :do_manage, + :do_import, :update ], :redirect_to => { :action => :list } @@ -145,6 +146,20 @@ redirect_to :action => 'manage' end + def import + end + + def do_import + @problem, import_log = Problem.new_from_import_form_params(params) + + if @problem.errors.length != 0 + render :action => 'import' and return + end + + @problem.save + @log = import_log + end + ################################## protected diff --git a/app/models/problem.rb b/app/models/problem.rb --- a/app/models/problem.rb +++ b/app/models/problem.rb @@ -9,4 +9,68 @@ find(:all, :conditions => {:available => true}, :order => "date_added DESC") end + def self.new_from_import_form_params(params) + problem = Problem.new + + # form error checking + + time_limit_s = params[:time_limit] + memory_limit_s = params[:memory_limit] + + time_limit_s = '1' if time_limit_s=='' + memory_limit_s = '32' if memory_limit_s=='' + + time_limit = time_limit_s.to_i + memory_limit = memory_limit_s.to_i + + if time_limit==0 and time_limit_s!='0' + problem.errors.add_to_base('Time limit format errors.') + elsif time_limit<=0 or time_limit >60 + problem.errors.add_to_base('Time limit out of range.') + end + + if memory_limit==0 and memory_limit_s!='0' + problem.errors.add_to_base('Memory limit format errors.') + elsif memory_limit<=0 or memory_limit >512 + problem.errors.add_to_base('Memory limit out of range.') + end + + if params[:file]==nil or params[:file]=='' + problem.errors.add_to_base('No testdata file.') + end + + file = params[:file] + + if problem.errors.length!=0 + return problem + end + + problem.name = params[:name] + if params[:full_name]!='' + problem.full_name = params[:full_name] + else + problem.full_name = params[:name] + end + + if not problem.valid? + return problem + end + + importer = TestdataImporter.new + + if not importer.import_from_file(problem.name, + file, + time_limit, + memory_limit) + problem.errors.add_to_base('Import error.') + end + + problem.full_score = 100 + problem.date_added = Time.new + problem.test_allowed = true + problem.output_only = false + problem.available = false + return problem, importer.log_msg + end + end diff --git a/app/views/problems/do_import.html.haml b/app/views/problems/do_import.html.haml new file mode 100644 --- /dev/null +++ b/app/views/problems/do_import.html.haml @@ -0,0 +1,20 @@ +- content_for :head do + = stylesheet_link_tag 'problems' + = javascript_include_tag :defaults + +%h1 Import problems: successful + +%p + %b Problem: + = "#{@problem.full_name} (#{@problem.name})" + %br/ + Note that the full score has be assigned to the default value, 100. + You should change it to the correct full score. + +%p + = link_to '[Back to problem list]', :action => 'list' + = link_to '[Import other problems]', :action => 'import' + +%h3 Import log +%pre.import-log + = @log diff --git a/app/views/problems/import.html.haml b/app/views/problems/import.html.haml new file mode 100644 --- /dev/null +++ b/app/views/problems/import.html.haml @@ -0,0 +1,43 @@ +- content_for :head do + = stylesheet_link_tag 'problems' + = javascript_include_tag :defaults + +%h1 Import problems + +%p= link_to '[Back to problem list]', :action => 'list' + +- if @problem and @problem.errors + =error_messages_for 'problem' + +- form_tag({:action => 'do_import'}, :multipart => true) do + .submitbox + %table + %tr + %td Name: + %td= text_field_tag 'name' + %tr + %td Full name: + %td + = text_field_tag 'full_name' + %span{:class => 'help'} Leave blank to use the same value as the name above. + %tr + %td Testdata file: + %td= file_field_tag 'file' + %tr + %td Time limit: + %td + = text_field_tag 'time_limit' + %span{:class => 'help'} In seconds. Leave blank to use 1 sec. + %tr + %td Memory limit: + %td + = text_field_tag 'memory_limit' + %span{:class => 'help'} In MB. Leave blank to use 32MB. + %tr + %td + %td= submit_tag 'Import problem' + +- if @log + %h3 Import log + %pre.import-log + = @log diff --git a/app/views/problems/list.rhtml b/app/views/problems/list.rhtml --- a/app/views/problems/list.rhtml +++ b/app/views/problems/list.rhtml @@ -8,6 +8,7 @@

<%= link_to '[New problem]', :action => 'new' %> <%= link_to '[Manage problems]', :action => 'manage' %> +<%= link_to '[Import problems]', :action => 'import' %> <%= link_to '[Turn off all problems]', :action => 'turn_all_off' %> <%= link_to '[Turn on all problems]', :action => 'turn_all_on' %>

@@ -54,4 +55,4 @@
-<%= link_to 'New problem', :action => 'new' %> +<%= link_to '[New problem]', :action => 'new' %> diff --git a/config/environment.rb.SAMPLE b/config/environment.rb.SAMPLE --- a/config/environment.rb.SAMPLE +++ b/config/environment.rb.SAMPLE @@ -74,8 +74,10 @@ # Include your application configuration below # If you want to manage graders through web interface, set the path to -# script directory below -GRADER_SCRIPT_DIR = '' +# the grader directory below. This dir is where raw, ev, ev-exam, +# scripts reside. All grader scripts will be in +# #{GRADER_ROOT_DIR}/scripts. +GRADER_ROOT_DIR = '' # These are where inputs and outputs of test requests are stored TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input' diff --git a/lib/grader_script.rb b/lib/grader_script.rb --- a/lib/grader_script.rb +++ b/lib/grader_script.rb @@ -1,35 +1,58 @@ module GraderScript def self.grader_control_enabled? - if defined? GRADER_SCRIPT_DIR - GRADER_SCRIPT_DIR != '' + if defined? GRADER_ROOT_DIR + GRADER_ROOT_DIR != '' else false end end - def self.stop_grader(pid) + def self.raw_dir + File.join GRADER_ROOT_DIR, "raw" + end + + def self.call_grader(params) if GraderScript.grader_control_enabled? - cmd = "#{GRADER_SCRIPT_DIR}/grader stop #{pid}" + cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params system(cmd) end end + def self.stop_grader(pid) + GraderScript.call_grader "stop #{pid}" + end + def self.stop_graders(pids) - if GraderScript.grader_control_enabled? - pid_str = (pids.map { |process| process.pid.to_a }).join ' ' - cmd = "#{GRADER_SCRIPT_DIR}/grader stop " + pid_str - system(cmd) - end + pid_str = (pids.map { |process| process.pid.to_a }).join ' ' + GraderScript.call_grader "stop #{pid_str}" end def self.start_grader(env) + GraderScript.call_grader "#{env} queue &" + GraderScript.call_grader "#{env} test_request &" + end + + def self.call_import_problem(problem_name, + problem_dir, + time_limit=1, + memory_limit=32, + checker_name='text') if GraderScript.grader_control_enabled? - cmd = "#{GRADER_SCRIPT_DIR}/grader #{env} queue &" - system(cmd) - cmd = "#{GRADER_SCRIPT_DIR}/grader #{env} test_request &" - system(cmd) - end + cur_dir = `pwd`.chomp + Dir.chdir(GRADER_ROOT_DIR) + + script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem") + cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" + + " -t #{time_limit} -m #{memory_limit}" + + output = `#{cmd}` + + Dir.chdir(cur_dir) + + return output + end + return '' end end diff --git a/lib/testdata_importer.rb b/lib/testdata_importer.rb new file mode 100644 --- /dev/null +++ b/lib/testdata_importer.rb @@ -0,0 +1,76 @@ +require 'tmpdir' + +class TestdataImporter + + attr :log_msg + + def import_from_file(problem_name, + tempfile, + time_limit, + memory_limit) + + dirname = TestdataImporter.extract(problem_name, tempfile) + return false if not dirname + @log_msg = GraderScript.call_import_problem(problem_name, + dirname, + time_limit, + memory_limit) + return true + end + + protected + + def self.long_ext(filename) + i = filename.index('.') + len = filename.length + return filename.slice(i..len) + end + + def self.extract(problem_name, tempfile) + testdata_filename = TestdataImporter.save_testdata_file(problem_name, + tempfile) + ext = TestdataImporter.long_ext(tempfile.original_filename) + + extract_dir = File.join(GraderScript.raw_dir, problem_name) + begin + Dir.mkdir extract_dir + rescue Errno::EEXIST + end + + if ext=='.tar.gz' or ext=='.tgz' + cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}" + elsif ext=='.tar' + cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}" + elsif ext=='.zip' + cmd = "unzip #{testdata_filename} -d #{extract_dir}" + else + return nil + end + + system(cmd) + + files = Dir["#{extract_dir}/**/1*.in"] + return nil if files.length==0 + + return File.dirname(files[0]) + end + + def self.save_testdata_file(problem_name, tempfile) + ext = TestdataImporter.long_ext(tempfile.original_filename) + testdata_filename = File.join(Dir.tmpdir,"#{problem_name}#{ext}") + + return nil if tempfile=="" + + if tempfile.instance_of?(Tempfile) + tempfile.close + FileUtils.move(tempfile.path,testdata_filename) + else + File.open(testdata_filename, "wb") do |f| + f.write(tempfile.read) + end + end + + return testdata_filename + end + +end diff --git a/public/stylesheets/problems.css b/public/stylesheets/problems.css --- a/public/stylesheets/problems.css +++ b/public/stylesheets/problems.css @@ -6,3 +6,9 @@ tr.not-available { background: #ffc0c0; } + +.import-log { + background: lightgray; + border: solid black 1px; + padding: 10px; +} \ No newline at end of file