Description:
add show testcase feature
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r625:7645a0f771ce - - 12 files changed: 113 inserted, 2 deleted

@@ -0,0 +1,24
1 + class TestcasesController < ApplicationController
2 + before_action :set_testcase, only: [:download_input,:download_sol]
3 + before_action :testcase_authorization
4 +
5 + def download_input
6 + send_data @testcase.input, type: 'text/plain', filename: "#{@testcase.problem.name}.#{@testcase.num}.in"
7 + end
8 +
9 + def download_sol
10 + send_data @testcase.sol, type: 'text/plain', filename: "#{@testcase.problem.name}.#{@testcase.num}.sol"
11 + end
12 +
13 +
14 + private
15 + # Use callbacks to share common setup or constraints between actions.
16 + def set_testcase
17 + @testcase = Testcase.find(params[:id])
18 + end
19 +
20 + # Only allow a trusted parameter "white list" through.
21 + def testcase_params
22 + params[:testcase]
23 + end
24 + end
@@ -0,0 +1,2
1 + module TestcasesHelper
2 + end
@@ -0,0 +1,25
1 + %h1 Test cases
2 + %h2= @problem.long_name
3 +
4 + /navbar
5 + %ul.nav.nav-pills{role: :tablist}
6 + - @problem.testcases.each.with_index do |tc,id|
7 + %li{role: :presentation, class: ('active' if id == 0)}
8 + %a{href:"#tc#{tc.id}", role: 'tab', data: {toggle: 'tab'}}= tc.num
9 +
10 + /actual data
11 + .tab-content
12 + - @problem.testcases.each.with_index do |tc,id|
13 + .tab-pane{id: "tc#{tc.id}",class: ('active' if id == 0)}
14 + .row
15 + .col-md-6
16 + %h3 Input
17 + = link_to "Download",download_input_problem_testcase_path(@problem,tc),class: 'btn btn-info btn-sm'
18 + .col-md-6
19 + %h3 Output
20 + = link_to "Download",download_sol_problem_testcase_path(@problem,tc),class: 'btn btn-info btn-sm'
21 + .row
22 + .col-md-6
23 + %textarea{ rows: 25,readonly: true,style: "width:100%;resize=none;overflow-y: scroll;"}= tc.input
24 + .col-md-6
25 + %textarea{ rows: 25,readonly: true,style: "width:100%;resize=none;overflow-y: scroll;"}= tc.sol
@@ -0,0 +1,7
1 + require 'test_helper'
2 +
3 + class TestcasesControllerTest < ActionController::TestCase
4 + setup do
5 + @testcase = testcases(:one)
6 + end
7 + end
@@ -16,48 +16,57
16 16 def current_user
17 17 return nil unless session[:user_id]
18 18 @current_user ||= User.find(session[:user_id])
19 19 end
20 20
21 21 def admin_authorization
22 22 return false unless authenticate
23 23 user = User.includes(:roles).find(session[:user_id])
24 24 unless user.admin?
25 25 unauthorized_redirect
26 26 return false
27 27 end
28 28 return true
29 29 end
30 30
31 31 def authorization_by_roles(allowed_roles)
32 32 return false unless authenticate
33 33 user = User.find(session[:user_id])
34 34 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
35 35 unauthorized_redirect
36 36 return false
37 37 end
38 38 end
39 39
40 + def testcase_authorization
41 + #admin always has privileged
42 + if @current_user.admin?
43 + return true
44 + end
45 +
46 + unauthorized_redirect if GraderConfiguration["right.view_testcase"]
47 + end
48 +
40 49 protected
41 50
42 51 def authenticate
43 52 unless session[:user_id]
44 53 flash[:notice] = 'You need to login'
45 54 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
46 55 flash[:notice] = 'You need to login but you cannot log in at this time'
47 56 end
48 57 redirect_to :controller => 'main', :action => 'login'
49 58 return false
50 59 end
51 60
52 61 # check if run in single user mode
53 62 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
54 63 user = User.find_by_id(session[:user_id])
55 64 if user==nil or (not user.admin?)
56 65 flash[:notice] = 'You cannot log in at this time'
57 66 redirect_to :controller => 'main', :action => 'login'
58 67 return false
59 68 end
60 69 unless user.enabled?
61 70 flash[:notice] = 'Your account is disabled'
62 71 redirect_to :controller => 'main', :action => 'login'
63 72 return false
@@ -1,27 +1,28
1 1 class ProblemsController < ApplicationController
2 2
3 - before_filter :authenticate, :authorization
3 + before_action :authenticate, :authorization
4 + before_action :testcase_authorization, only: [:show_testcase]
4 5
5 6 in_place_edit_for :problem, :name
6 7 in_place_edit_for :problem, :full_name
7 8 in_place_edit_for :problem, :full_score
8 9
9 10 def index
10 11 @problems = Problem.order(date_added: :desc)
11 12 end
12 13
13 14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 15 verify :method => :post, :only => [ :create, :quick_create,
15 16 :do_manage,
16 17 :do_import,
17 18 ],
18 19 :redirect_to => { :action => :index }
19 20
20 21 def show
21 22 @problem = Problem.find(params[:id])
22 23 end
23 24
24 25 def new
25 26 @problem = Problem.new
26 27 @description = nil
27 28 end
@@ -200,48 +201,52
200 201 params.delete :import_to_db
201 202 end
202 203 @problem, import_log = Problem.create_from_import_form_params(params,
203 204 old_problem)
204 205
205 206 if !@problem.errors.empty?
206 207 render :action => 'import' and return
207 208 end
208 209
209 210 if old_problem!=nil
210 211 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
211 212 end
212 213 @log = import_log
213 214 end
214 215
215 216 def remove_contest
216 217 problem = Problem.find(params[:id])
217 218 contest = Contest.find(params[:contest_id])
218 219 if problem!=nil and contest!=nil
219 220 problem.contests.delete(contest)
220 221 end
221 222 redirect_to :action => 'manage'
222 223 end
223 224
225 + def show_testcase
226 + @problem = Problem.includes(:testcases).find(params[:id])
227 + end
228 +
224 229 ##################################
225 230 protected
226 231
227 232 def allow_test_pair_import?
228 233 if defined? ALLOW_TEST_PAIR_IMPORT
229 234 return ALLOW_TEST_PAIR_IMPORT
230 235 else
231 236 return false
232 237 end
233 238 end
234 239
235 240 def change_date_added
236 241 problems = get_problems_from_params
237 242 year = params[:date_added][:year].to_i
238 243 month = params[:date_added][:month].to_i
239 244 day = params[:date_added][:day].to_i
240 245 date = Date.new(year,month,day)
241 246 problems.each do |p|
242 247 p.date_added = date
243 248 p.save
244 249 end
245 250 end
246 251
247 252 def add_to_contest
@@ -1,36 +1,37
1 1 require 'yaml'
2 2
3 3 #
4 4 # This class also contains various login of the system.
5 5 #
6 6 class GraderConfiguration < ActiveRecord::Base
7 7
8 8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 10 MULTICONTESTS_KEY = 'system.multicontests'
11 11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
12 12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
13 + VIEW_TESTCASE = 'right.view_testcase'
13 14
14 15 cattr_accessor :config_cache
15 16 cattr_accessor :task_grading_info_cache
16 17 cattr_accessor :contest_time_str
17 18 cattr_accessor :contest_time
18 19
19 20 GraderConfiguration.config_cache = nil
20 21 GraderConfiguration.task_grading_info_cache = nil
21 22
22 23 def self.config_cached?
23 24 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
24 25 end
25 26
26 27 def self.get(key)
27 28 if GraderConfiguration.config_cached?
28 29 if GraderConfiguration.config_cache == nil
29 30 self.read_config
30 31 end
31 32 return GraderConfiguration.config_cache[key]
32 33 else
33 34 return GraderConfiguration.read_one_key(key)
34 35 end
35 36 end
36 37
@@ -49,48 +50,52
49 50 #
50 51 # View decision
51 52 #
52 53 def self.show_submitbox_to?(user)
53 54 mode = get(SYSTEM_MODE_CONF_KEY)
54 55 return false if mode=='analysis'
55 56 if (mode=='contest')
56 57 return false if (user.site!=nil) and
57 58 ((user.site.started!=true) or (user.site.finished?))
58 59 end
59 60 return true
60 61 end
61 62
62 63 def self.show_tasks_to?(user)
63 64 if time_limit_mode?
64 65 return false if not user.contest_started?
65 66 end
66 67 return true
67 68 end
68 69
69 70 def self.show_grading_result
70 71 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
71 72 end
72 73
74 + def self.show_testcase
75 + return get(VIEW_TESTCASE)
76 + end
77 +
73 78 def self.allow_test_request(user)
74 79 mode = get(SYSTEM_MODE_CONF_KEY)
75 80 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
76 81 if (mode=='contest')
77 82 return false if ((user.site!=nil) and
78 83 ((user.site.started!=true) or
79 84 (early_timeout and (user.site.time_left < 30.minutes))))
80 85 end
81 86 return false if mode=='analysis'
82 87 return true
83 88 end
84 89
85 90 def self.task_grading_info
86 91 if GraderConfiguration.task_grading_info_cache==nil
87 92 read_grading_info
88 93 end
89 94 return GraderConfiguration.task_grading_info_cache
90 95 end
91 96
92 97 def self.standard_mode?
93 98 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
94 99 end
95 100
96 101 def self.contest_mode?
@@ -3,25 +3,27
3 3 = "-"
4 4 - else
5 5 - unless submission.graded_at
6 6 = t 'main.submitted_at'
7 7 = format_short_time(submission.submitted_at.localtime)
8 8 - else
9 9 %strong= t 'main.graded_at'
10 10 = "#{format_short_time(submission.graded_at.localtime)} "
11 11 %br
12 12 - if GraderConfiguration['ui.show_score']
13 13 %strong=t 'main.score'
14 14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
15 15 = " ["
16 16 %tt
17 17 = submission.grader_comment
18 18 = "]"
19 19 %br
20 20 %strong View:
21 21 - if GraderConfiguration.show_grading_result
22 22 = link_to '[detailed result]', :action => 'result', :id => submission.id
23 23 /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
24 24 = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'}
25 25 = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info'
26 26 = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
27 + - if GraderConfiguration.show_testcase
28 + = link_to "testcases", show_testcase_problem_path(problem_id), class: 'btn btn-xs btn-info'
27 29
@@ -1,51 +1,59
1 1 CafeGrader::Application.routes.draw do
2 2 get "sources/direct_edit"
3 3
4 4 root :to => 'main#login'
5 5
6 6 resources :contests
7 7
8 8 resources :sites
9 9
10 10 resources :announcements do
11 11 member do
12 12 get 'toggle','toggle_front'
13 13 end
14 14 end
15 15
16 16 resources :problems do
17 17 member do
18 18 get 'toggle'
19 19 get 'toggle_test'
20 20 get 'stat'
21 + get 'show_testcase'
21 22 end
22 23 collection do
23 24 get 'turn_all_off'
24 25 get 'turn_all_on'
25 26 get 'import'
26 27 get 'manage'
27 28 end
29 +
30 + resources :testcases, only: [] do
31 + member do
32 + get 'download_input'
33 + get 'download_sol'
34 + end
35 + end
28 36 end
29 37
30 38 resources :grader_configuration, controller: 'configurations'
31 39
32 40 resources :users do
33 41 member do
34 42 get 'toggle_activate', 'toggle_enable'
35 43 get 'stat'
36 44 end
37 45 end
38 46
39 47 resources :submissions do
40 48 member do
41 49 get 'download'
42 50 get 'compiler_msg'
43 51 end
44 52 collection do
45 53 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
46 54 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
47 55 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
48 56 end
49 57 end
50 58
51 59 get 'tasks/view/:file.:ext' => 'tasks#view'
@@ -68,48 +68,54
68 68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
69 69 },
70 70
71 71 {
72 72 :key => 'right.user_view_submission',
73 73 :value_type => 'boolean',
74 74 :default_value => 'false',
75 75 :description => 'If true, any user can view submissions of every one.'
76 76 },
77 77
78 78 {
79 79 :key => 'right.bypass_agreement',
80 80 :value_type => 'boolean',
81 81 :default_value => 'true',
82 82 :description => 'When false, a user must accept usage agreement before login'
83 83 },
84 84
85 85 {
86 86 :key => 'right.heartbeat_response',
87 87 :value_type => 'string',
88 88 :default_value => 'OK',
89 89 :description => 'Heart beat response text'
90 90 },
91 91
92 + {
93 + :key => 'right.view_testcase',
94 + :value_type => 'boolean',
95 + :default_value => 'false',
96 + :description => 'When true, any user can view/download test data'
97 + },
92 98 # If Configuration['system.online_registration'] is true, the
93 99 # system allows online registration, and will use these
94 100 # information for sending confirmation emails.
95 101 {
96 102 :key => 'system.online_registration.smtp',
97 103 :value_type => 'string',
98 104 :default_value => 'smtp.somehost.com'
99 105 },
100 106
101 107 {
102 108 :key => 'system.online_registration.from',
103 109 :value_type => 'string',
104 110 :default_value => 'your.email@address'
105 111 },
106 112
107 113 {
108 114 :key => 'system.admin_email',
109 115 :value_type => 'string',
110 116 :default_value => 'admin@admin.email'
111 117 },
112 118
113 119 {
114 120 :key => 'system.user_setting_enabled',
115 121 :value_type => 'boolean',
@@ -28,31 +28,46
28 28 GraderScript.call_grader "stop #{pid_str}"
29 29 end
30 30
31 31 def self.start_grader(env)
32 32 GraderScript.call_grader "#{env} queue --err-log &"
33 33 GraderScript.call_grader "#{env} test_request -err-log &"
34 34 end
35 35
36 36 def self.call_import_problem(problem_name,
37 37 problem_dir,
38 38 time_limit=1,
39 39 memory_limit=32,
40 40 checker_name='text')
41 41 if GraderScript.grader_control_enabled?
42 42 cur_dir = `pwd`.chomp
43 43 Dir.chdir(GRADER_ROOT_DIR)
44 44
45 45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
46 46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
47 47 " -t #{time_limit} -m #{memory_limit}"
48 48
49 49 output = `#{cmd}`
50 50
51 51 Dir.chdir(cur_dir)
52 -
52 +
53 53 return "import CMD: #{cmd}\n" + output
54 54 end
55 55 return ''
56 56 end
57 57
58 + def self.call_import_testcase(problem_name)
59 + if GraderScript.grader_control_enabled?
60 + cur_dir = `pwd`.chomp
61 + Dir.chdir(GRADER_ROOT_DIR)
62 +
63 + script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase")
64 + cmd = "#{script_name} #{problem_name}"
65 +
66 + output = `#{cmd}`
67 +
68 + Dir.chdir(cur_dir)
69 + return "Testcase import result:\n" + output
70 + end
71 + end
72 +
58 73 end
@@ -17,48 +17,51
17 17 dirname = extract(tempfile)
18 18 return false if not dirname
19 19 if not import_to_db
20 20 @log_msg = GraderScript.call_import_problem(@problem.name,
21 21 dirname,
22 22 time_limit,
23 23 memory_limit,
24 24 checker_name)
25 25 else
26 26 # Import test data to test pairs.
27 27
28 28 @problem.test_pairs.clear
29 29 if import_test_pairs(dirname)
30 30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
31 31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
32 32 else
33 33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
34 34 end
35 35 end
36 36
37 37 @log_msg << import_problem_description(dirname)
38 38 @log_msg << import_problem_pdf(dirname)
39 39 @log_msg << import_full_score(dirname)
40 40
41 + #import test data
42 + @log_msg << GraderScript.call_import_testcase(@problem.name)
43 +
41 44 return true
42 45 end
43 46
44 47 protected
45 48
46 49 def self.long_ext(filename)
47 50 i = filename.index('.')
48 51 len = filename.length
49 52 return filename.slice(i..len)
50 53 end
51 54
52 55 def extract(tempfile)
53 56 testdata_filename = save_testdata_file(tempfile)
54 57 ext = TestdataImporter.long_ext(tempfile.original_filename)
55 58
56 59 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
57 60 if File.exists? extract_dir
58 61 backup_count = 0
59 62 begin
60 63 backup_count += 1
61 64 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
62 65 end while File.exists? backup_dirname
63 66 File.rename(extract_dir, backup_dirname)
64 67 end
You need to be logged in to leave comments. Login now