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
@@ -1,128 +1,137
1 class ApplicationController < ActionController::Base
1 class ApplicationController < ActionController::Base
2 protect_from_forgery
2 protect_from_forgery
3
3
4 before_filter :current_user
4 before_filter :current_user
5
5
6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
8
8
9 #report and redirect for unauthorized activities
9 #report and redirect for unauthorized activities
10 def unauthorized_redirect
10 def unauthorized_redirect
11 flash[:notice] = 'You are not authorized to view the page you requested'
11 flash[:notice] = 'You are not authorized to view the page you requested'
12 redirect_to :controller => 'main', :action => 'login'
12 redirect_to :controller => 'main', :action => 'login'
13 end
13 end
14
14
15 # Returns the current logged-in user (if any).
15 # Returns the current logged-in user (if any).
16 def current_user
16 def current_user
17 return nil unless session[:user_id]
17 return nil unless session[:user_id]
18 @current_user ||= User.find(session[:user_id])
18 @current_user ||= User.find(session[:user_id])
19 end
19 end
20
20
21 def admin_authorization
21 def admin_authorization
22 return false unless authenticate
22 return false unless authenticate
23 user = User.includes(:roles).find(session[:user_id])
23 user = User.includes(:roles).find(session[:user_id])
24 unless user.admin?
24 unless user.admin?
25 unauthorized_redirect
25 unauthorized_redirect
26 return false
26 return false
27 end
27 end
28 return true
28 return true
29 end
29 end
30
30
31 def authorization_by_roles(allowed_roles)
31 def authorization_by_roles(allowed_roles)
32 return false unless authenticate
32 return false unless authenticate
33 user = User.find(session[:user_id])
33 user = User.find(session[:user_id])
34 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
34 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
35 unauthorized_redirect
35 unauthorized_redirect
36 return false
36 return false
37 end
37 end
38 end
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 protected
49 protected
41
50
42 def authenticate
51 def authenticate
43 unless session[:user_id]
52 unless session[:user_id]
44 flash[:notice] = 'You need to login'
53 flash[:notice] = 'You need to login'
45 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
54 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
46 flash[:notice] = 'You need to login but you cannot log in at this time'
55 flash[:notice] = 'You need to login but you cannot log in at this time'
47 end
56 end
48 redirect_to :controller => 'main', :action => 'login'
57 redirect_to :controller => 'main', :action => 'login'
49 return false
58 return false
50 end
59 end
51
60
52 # check if run in single user mode
61 # check if run in single user mode
53 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
62 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
54 user = User.find_by_id(session[:user_id])
63 user = User.find_by_id(session[:user_id])
55 if user==nil or (not user.admin?)
64 if user==nil or (not user.admin?)
56 flash[:notice] = 'You cannot log in at this time'
65 flash[:notice] = 'You cannot log in at this time'
57 redirect_to :controller => 'main', :action => 'login'
66 redirect_to :controller => 'main', :action => 'login'
58 return false
67 return false
59 end
68 end
60 unless user.enabled?
69 unless user.enabled?
61 flash[:notice] = 'Your account is disabled'
70 flash[:notice] = 'Your account is disabled'
62 redirect_to :controller => 'main', :action => 'login'
71 redirect_to :controller => 'main', :action => 'login'
63 return false
72 return false
64 end
73 end
65 return true
74 return true
66 end
75 end
67
76
68 if GraderConfiguration.multicontests?
77 if GraderConfiguration.multicontests?
69 user = User.find(session[:user_id])
78 user = User.find(session[:user_id])
70 return true if user.admin?
79 return true if user.admin?
71 begin
80 begin
72 if user.contest_stat(true).forced_logout
81 if user.contest_stat(true).forced_logout
73 flash[:notice] = 'You have been automatically logged out.'
82 flash[:notice] = 'You have been automatically logged out.'
74 redirect_to :controller => 'main', :action => 'index'
83 redirect_to :controller => 'main', :action => 'index'
75 end
84 end
76 rescue
85 rescue
77 end
86 end
78 end
87 end
79 return true
88 return true
80 end
89 end
81
90
82 def authenticate_by_ip_address
91 def authenticate_by_ip_address
83 #this assume that we have already authenticate normally
92 #this assume that we have already authenticate normally
84 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
93 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
85 user = User.find(session[:user_id])
94 user = User.find(session[:user_id])
86 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
95 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
87 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
96 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
88 redirect_to :controller => 'main', :action => 'login'
97 redirect_to :controller => 'main', :action => 'login'
89 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
98 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
90 return false
99 return false
91 end
100 end
92 unless user.last_ip
101 unless user.last_ip
93 user.last_ip = request.remote_ip
102 user.last_ip = request.remote_ip
94 user.save
103 user.save
95 end
104 end
96 end
105 end
97 return true
106 return true
98 end
107 end
99
108
100 def authorization
109 def authorization
101 return false unless authenticate
110 return false unless authenticate
102 user = User.find(session[:user_id])
111 user = User.find(session[:user_id])
103 unless user.roles.detect { |role|
112 unless user.roles.detect { |role|
104 role.rights.detect{ |right|
113 role.rights.detect{ |right|
105 right.controller == self.class.controller_name and
114 right.controller == self.class.controller_name and
106 (right.action == 'all' or right.action == action_name)
115 (right.action == 'all' or right.action == action_name)
107 }
116 }
108 }
117 }
109 flash[:notice] = 'You are not authorized to view the page you requested'
118 flash[:notice] = 'You are not authorized to view the page you requested'
110 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
119 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
111 redirect_to :controller => 'main', :action => 'login'
120 redirect_to :controller => 'main', :action => 'login'
112 return false
121 return false
113 end
122 end
114 end
123 end
115
124
116 def verify_time_limit
125 def verify_time_limit
117 return true if session[:user_id]==nil
126 return true if session[:user_id]==nil
118 user = User.find(session[:user_id], :include => :site)
127 user = User.find(session[:user_id], :include => :site)
119 return true if user==nil or user.site == nil
128 return true if user==nil or user.site == nil
120 if user.contest_finished?
129 if user.contest_finished?
121 flash[:notice] = 'Error: the contest you are participating is over.'
130 flash[:notice] = 'Error: the contest you are participating is over.'
122 redirect_to :back
131 redirect_to :back
123 return false
132 return false
124 end
133 end
125 return true
134 return true
126 end
135 end
127
136
128 end
137 end
@@ -1,279 +1,284
1 class ProblemsController < ApplicationController
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 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_score
8 in_place_edit_for :problem, :full_score
8
9
9 def index
10 def index
10 @problems = Problem.order(date_added: :desc)
11 @problems = Problem.order(date_added: :desc)
11 end
12 end
12
13
13 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 verify :method => :post, :only => [ :create, :quick_create,
15 verify :method => :post, :only => [ :create, :quick_create,
15 :do_manage,
16 :do_manage,
16 :do_import,
17 :do_import,
17 ],
18 ],
18 :redirect_to => { :action => :index }
19 :redirect_to => { :action => :index }
19
20
20 def show
21 def show
21 @problem = Problem.find(params[:id])
22 @problem = Problem.find(params[:id])
22 end
23 end
23
24
24 def new
25 def new
25 @problem = Problem.new
26 @problem = Problem.new
26 @description = nil
27 @description = nil
27 end
28 end
28
29
29 def create
30 def create
30 @problem = Problem.new(params[:problem])
31 @problem = Problem.new(params[:problem])
31 @description = Description.new(params[:description])
32 @description = Description.new(params[:description])
32 if @description.body!=''
33 if @description.body!=''
33 if !@description.save
34 if !@description.save
34 render :action => new and return
35 render :action => new and return
35 end
36 end
36 else
37 else
37 @description = nil
38 @description = nil
38 end
39 end
39 @problem.description = @description
40 @problem.description = @description
40 if @problem.save
41 if @problem.save
41 flash[:notice] = 'Problem was successfully created.'
42 flash[:notice] = 'Problem was successfully created.'
42 redirect_to action: :index
43 redirect_to action: :index
43 else
44 else
44 render :action => 'new'
45 render :action => 'new'
45 end
46 end
46 end
47 end
47
48
48 def quick_create
49 def quick_create
49 @problem = Problem.new(params[:problem])
50 @problem = Problem.new(params[:problem])
50 @problem.full_name = @problem.name if @problem.full_name == ''
51 @problem.full_name = @problem.name if @problem.full_name == ''
51 @problem.full_score = 100
52 @problem.full_score = 100
52 @problem.available = false
53 @problem.available = false
53 @problem.test_allowed = true
54 @problem.test_allowed = true
54 @problem.output_only = false
55 @problem.output_only = false
55 @problem.date_added = Time.new
56 @problem.date_added = Time.new
56 if @problem.save
57 if @problem.save
57 flash[:notice] = 'Problem was successfully created.'
58 flash[:notice] = 'Problem was successfully created.'
58 redirect_to action: :index
59 redirect_to action: :index
59 else
60 else
60 flash[:notice] = 'Error saving problem'
61 flash[:notice] = 'Error saving problem'
61 redirect_to action: :index
62 redirect_to action: :index
62 end
63 end
63 end
64 end
64
65
65 def edit
66 def edit
66 @problem = Problem.find(params[:id])
67 @problem = Problem.find(params[:id])
67 @description = @problem.description
68 @description = @problem.description
68 end
69 end
69
70
70 def update
71 def update
71 @problem = Problem.find(params[:id])
72 @problem = Problem.find(params[:id])
72 @description = @problem.description
73 @description = @problem.description
73 if @description.nil? and params[:description][:body]!=''
74 if @description.nil? and params[:description][:body]!=''
74 @description = Description.new(params[:description])
75 @description = Description.new(params[:description])
75 if !@description.save
76 if !@description.save
76 flash[:notice] = 'Error saving description'
77 flash[:notice] = 'Error saving description'
77 render :action => 'edit' and return
78 render :action => 'edit' and return
78 end
79 end
79 @problem.description = @description
80 @problem.description = @description
80 elsif @description
81 elsif @description
81 if !@description.update_attributes(params[:description])
82 if !@description.update_attributes(params[:description])
82 flash[:notice] = 'Error saving description'
83 flash[:notice] = 'Error saving description'
83 render :action => 'edit' and return
84 render :action => 'edit' and return
84 end
85 end
85 end
86 end
86 if params[:file] and params[:file].content_type != 'application/pdf'
87 if params[:file] and params[:file].content_type != 'application/pdf'
87 flash[:notice] = 'Error: Uploaded file is not PDF'
88 flash[:notice] = 'Error: Uploaded file is not PDF'
88 render :action => 'edit' and return
89 render :action => 'edit' and return
89 end
90 end
90 if @problem.update_attributes(params[:problem])
91 if @problem.update_attributes(params[:problem])
91 flash[:notice] = 'Problem was successfully updated.'
92 flash[:notice] = 'Problem was successfully updated.'
92 unless params[:file] == nil or params[:file] == ''
93 unless params[:file] == nil or params[:file] == ''
93 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 if not FileTest.exists? out_dirname
96 if not FileTest.exists? out_dirname
96 Dir.mkdir out_dirname
97 Dir.mkdir out_dirname
97 end
98 end
98
99
99 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
100 if FileTest.exists? out_filename
101 if FileTest.exists? out_filename
101 File.delete out_filename
102 File.delete out_filename
102 end
103 end
103
104
104 File.open(out_filename,"wb") do |file|
105 File.open(out_filename,"wb") do |file|
105 file.write(params[:file].read)
106 file.write(params[:file].read)
106 end
107 end
107 @problem.description_filename = "#{@problem.name}.pdf"
108 @problem.description_filename = "#{@problem.name}.pdf"
108 @problem.save
109 @problem.save
109 end
110 end
110 redirect_to :action => 'show', :id => @problem
111 redirect_to :action => 'show', :id => @problem
111 else
112 else
112 render :action => 'edit'
113 render :action => 'edit'
113 end
114 end
114 end
115 end
115
116
116 def destroy
117 def destroy
117 p = Problem.find(params[:id]).destroy
118 p = Problem.find(params[:id]).destroy
118 redirect_to action: :index
119 redirect_to action: :index
119 end
120 end
120
121
121 def toggle
122 def toggle
122 @problem = Problem.find(params[:id])
123 @problem = Problem.find(params[:id])
123 @problem.update_attributes(available: !(@problem.available) )
124 @problem.update_attributes(available: !(@problem.available) )
124 respond_to do |format|
125 respond_to do |format|
125 format.js { }
126 format.js { }
126 end
127 end
127 end
128 end
128
129
129 def toggle_test
130 def toggle_test
130 @problem = Problem.find(params[:id])
131 @problem = Problem.find(params[:id])
131 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 respond_to do |format|
133 respond_to do |format|
133 format.js { }
134 format.js { }
134 end
135 end
135 end
136 end
136
137
137 def turn_all_off
138 def turn_all_off
138 Problem.available.all.each do |problem|
139 Problem.available.all.each do |problem|
139 problem.available = false
140 problem.available = false
140 problem.save
141 problem.save
141 end
142 end
142 redirect_to action: :index
143 redirect_to action: :index
143 end
144 end
144
145
145 def turn_all_on
146 def turn_all_on
146 Problem.where.not(available: true).each do |problem|
147 Problem.where.not(available: true).each do |problem|
147 problem.available = true
148 problem.available = true
148 problem.save
149 problem.save
149 end
150 end
150 redirect_to action: :index
151 redirect_to action: :index
151 end
152 end
152
153
153 def stat
154 def stat
154 @problem = Problem.find(params[:id])
155 @problem = Problem.find(params[:id])
155 unless @problem.available or session[:admin]
156 unless @problem.available or session[:admin]
156 redirect_to :controller => 'main', :action => 'list'
157 redirect_to :controller => 'main', :action => 'list'
157 return
158 return
158 end
159 end
159 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
160 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
160
161
161 #stat summary
162 #stat summary
162 range =65
163 range =65
163 @histogram = { data: Array.new(range,0), summary: {} }
164 @histogram = { data: Array.new(range,0), summary: {} }
164 user = Hash.new(0)
165 user = Hash.new(0)
165 @submissions.find_each do |sub|
166 @submissions.find_each do |sub|
166 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
167 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
167 @histogram[:data][d.to_i] += 1 if d < range
168 @histogram[:data][d.to_i] += 1 if d < range
168 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
169 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
169 end
170 end
170 @histogram[:summary][:max] = [@histogram[:data].max,1].max
171 @histogram[:summary][:max] = [@histogram[:data].max,1].max
171
172
172 @summary = { attempt: user.count, solve: 0 }
173 @summary = { attempt: user.count, solve: 0 }
173 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
174 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
174 end
175 end
175
176
176 def manage
177 def manage
177 @problems = Problem.order(date_added: :desc)
178 @problems = Problem.order(date_added: :desc)
178 end
179 end
179
180
180 def do_manage
181 def do_manage
181 if params.has_key? 'change_date_added'
182 if params.has_key? 'change_date_added'
182 change_date_added
183 change_date_added
183 elsif params.has_key? 'add_to_contest'
184 elsif params.has_key? 'add_to_contest'
184 add_to_contest
185 add_to_contest
185 elsif params.has_key? 'enable_problem'
186 elsif params.has_key? 'enable_problem'
186 set_available(true)
187 set_available(true)
187 elsif params.has_key? 'disable_problem'
188 elsif params.has_key? 'disable_problem'
188 set_available(false)
189 set_available(false)
189 end
190 end
190 redirect_to :action => 'manage'
191 redirect_to :action => 'manage'
191 end
192 end
192
193
193 def import
194 def import
194 @allow_test_pair_import = allow_test_pair_import?
195 @allow_test_pair_import = allow_test_pair_import?
195 end
196 end
196
197
197 def do_import
198 def do_import
198 old_problem = Problem.find_by_name(params[:name])
199 old_problem = Problem.find_by_name(params[:name])
199 if !allow_test_pair_import? and params.has_key? :import_to_db
200 if !allow_test_pair_import? and params.has_key? :import_to_db
200 params.delete :import_to_db
201 params.delete :import_to_db
201 end
202 end
202 @problem, import_log = Problem.create_from_import_form_params(params,
203 @problem, import_log = Problem.create_from_import_form_params(params,
203 old_problem)
204 old_problem)
204
205
205 if !@problem.errors.empty?
206 if !@problem.errors.empty?
206 render :action => 'import' and return
207 render :action => 'import' and return
207 end
208 end
208
209
209 if old_problem!=nil
210 if old_problem!=nil
210 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
211 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
211 end
212 end
212 @log = import_log
213 @log = import_log
213 end
214 end
214
215
215 def remove_contest
216 def remove_contest
216 problem = Problem.find(params[:id])
217 problem = Problem.find(params[:id])
217 contest = Contest.find(params[:contest_id])
218 contest = Contest.find(params[:contest_id])
218 if problem!=nil and contest!=nil
219 if problem!=nil and contest!=nil
219 problem.contests.delete(contest)
220 problem.contests.delete(contest)
220 end
221 end
221 redirect_to :action => 'manage'
222 redirect_to :action => 'manage'
222 end
223 end
223
224
225 + def show_testcase
226 + @problem = Problem.includes(:testcases).find(params[:id])
227 + end
228 +
224 ##################################
229 ##################################
225 protected
230 protected
226
231
227 def allow_test_pair_import?
232 def allow_test_pair_import?
228 if defined? ALLOW_TEST_PAIR_IMPORT
233 if defined? ALLOW_TEST_PAIR_IMPORT
229 return ALLOW_TEST_PAIR_IMPORT
234 return ALLOW_TEST_PAIR_IMPORT
230 else
235 else
231 return false
236 return false
232 end
237 end
233 end
238 end
234
239
235 def change_date_added
240 def change_date_added
236 problems = get_problems_from_params
241 problems = get_problems_from_params
237 year = params[:date_added][:year].to_i
242 year = params[:date_added][:year].to_i
238 month = params[:date_added][:month].to_i
243 month = params[:date_added][:month].to_i
239 day = params[:date_added][:day].to_i
244 day = params[:date_added][:day].to_i
240 date = Date.new(year,month,day)
245 date = Date.new(year,month,day)
241 problems.each do |p|
246 problems.each do |p|
242 p.date_added = date
247 p.date_added = date
243 p.save
248 p.save
244 end
249 end
245 end
250 end
246
251
247 def add_to_contest
252 def add_to_contest
248 problems = get_problems_from_params
253 problems = get_problems_from_params
249 contest = Contest.find(params[:contest][:id])
254 contest = Contest.find(params[:contest][:id])
250 if contest!=nil and contest.enabled
255 if contest!=nil and contest.enabled
251 problems.each do |p|
256 problems.each do |p|
252 p.contests << contest
257 p.contests << contest
253 end
258 end
254 end
259 end
255 end
260 end
256
261
257 def set_available(avail)
262 def set_available(avail)
258 problems = get_problems_from_params
263 problems = get_problems_from_params
259 problems.each do |p|
264 problems.each do |p|
260 p.available = avail
265 p.available = avail
261 p.save
266 p.save
262 end
267 end
263 end
268 end
264
269
265 def get_problems_from_params
270 def get_problems_from_params
266 problems = []
271 problems = []
267 params.keys.each do |k|
272 params.keys.each do |k|
268 if k.index('prob-')==0
273 if k.index('prob-')==0
269 name, id, order = k.split('-')
274 name, id, order = k.split('-')
270 problems << Problem.find(id)
275 problems << Problem.find(id)
271 end
276 end
272 end
277 end
273 problems
278 problems
274 end
279 end
275
280
276 def get_problems_stat
281 def get_problems_stat
277 end
282 end
278
283
279 end
284 end
@@ -1,177 +1,182
1 require 'yaml'
1 require 'yaml'
2
2
3 #
3 #
4 # This class also contains various login of the system.
4 # This class also contains various login of the system.
5 #
5 #
6 class GraderConfiguration < ActiveRecord::Base
6 class GraderConfiguration < ActiveRecord::Base
7
7
8 SYSTEM_MODE_CONF_KEY = 'system.mode'
8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 MULTICONTESTS_KEY = 'system.multicontests'
10 MULTICONTESTS_KEY = 'system.multicontests'
11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
13 + VIEW_TESTCASE = 'right.view_testcase'
13
14
14 cattr_accessor :config_cache
15 cattr_accessor :config_cache
15 cattr_accessor :task_grading_info_cache
16 cattr_accessor :task_grading_info_cache
16 cattr_accessor :contest_time_str
17 cattr_accessor :contest_time_str
17 cattr_accessor :contest_time
18 cattr_accessor :contest_time
18
19
19 GraderConfiguration.config_cache = nil
20 GraderConfiguration.config_cache = nil
20 GraderConfiguration.task_grading_info_cache = nil
21 GraderConfiguration.task_grading_info_cache = nil
21
22
22 def self.config_cached?
23 def self.config_cached?
23 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
24 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
24 end
25 end
25
26
26 def self.get(key)
27 def self.get(key)
27 if GraderConfiguration.config_cached?
28 if GraderConfiguration.config_cached?
28 if GraderConfiguration.config_cache == nil
29 if GraderConfiguration.config_cache == nil
29 self.read_config
30 self.read_config
30 end
31 end
31 return GraderConfiguration.config_cache[key]
32 return GraderConfiguration.config_cache[key]
32 else
33 else
33 return GraderConfiguration.read_one_key(key)
34 return GraderConfiguration.read_one_key(key)
34 end
35 end
35 end
36 end
36
37
37 def self.[](key)
38 def self.[](key)
38 self.get(key)
39 self.get(key)
39 end
40 end
40
41
41 def self.reload
42 def self.reload
42 self.read_config
43 self.read_config
43 end
44 end
44
45
45 def self.clear
46 def self.clear
46 GraderConfiguration.config_cache = nil
47 GraderConfiguration.config_cache = nil
47 end
48 end
48
49
49 #
50 #
50 # View decision
51 # View decision
51 #
52 #
52 def self.show_submitbox_to?(user)
53 def self.show_submitbox_to?(user)
53 mode = get(SYSTEM_MODE_CONF_KEY)
54 mode = get(SYSTEM_MODE_CONF_KEY)
54 return false if mode=='analysis'
55 return false if mode=='analysis'
55 if (mode=='contest')
56 if (mode=='contest')
56 return false if (user.site!=nil) and
57 return false if (user.site!=nil) and
57 ((user.site.started!=true) or (user.site.finished?))
58 ((user.site.started!=true) or (user.site.finished?))
58 end
59 end
59 return true
60 return true
60 end
61 end
61
62
62 def self.show_tasks_to?(user)
63 def self.show_tasks_to?(user)
63 if time_limit_mode?
64 if time_limit_mode?
64 return false if not user.contest_started?
65 return false if not user.contest_started?
65 end
66 end
66 return true
67 return true
67 end
68 end
68
69
69 def self.show_grading_result
70 def self.show_grading_result
70 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
71 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
71 end
72 end
72
73
74 + def self.show_testcase
75 + return get(VIEW_TESTCASE)
76 + end
77 +
73 def self.allow_test_request(user)
78 def self.allow_test_request(user)
74 mode = get(SYSTEM_MODE_CONF_KEY)
79 mode = get(SYSTEM_MODE_CONF_KEY)
75 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
80 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
76 if (mode=='contest')
81 if (mode=='contest')
77 return false if ((user.site!=nil) and
82 return false if ((user.site!=nil) and
78 ((user.site.started!=true) or
83 ((user.site.started!=true) or
79 (early_timeout and (user.site.time_left < 30.minutes))))
84 (early_timeout and (user.site.time_left < 30.minutes))))
80 end
85 end
81 return false if mode=='analysis'
86 return false if mode=='analysis'
82 return true
87 return true
83 end
88 end
84
89
85 def self.task_grading_info
90 def self.task_grading_info
86 if GraderConfiguration.task_grading_info_cache==nil
91 if GraderConfiguration.task_grading_info_cache==nil
87 read_grading_info
92 read_grading_info
88 end
93 end
89 return GraderConfiguration.task_grading_info_cache
94 return GraderConfiguration.task_grading_info_cache
90 end
95 end
91
96
92 def self.standard_mode?
97 def self.standard_mode?
93 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
98 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
94 end
99 end
95
100
96 def self.contest_mode?
101 def self.contest_mode?
97 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
102 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
98 end
103 end
99
104
100 def self.indv_contest_mode?
105 def self.indv_contest_mode?
101 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
106 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
102 end
107 end
103
108
104 def self.multicontests?
109 def self.multicontests?
105 return get(MULTICONTESTS_KEY) == true
110 return get(MULTICONTESTS_KEY) == true
106 end
111 end
107
112
108 def self.time_limit_mode?
113 def self.time_limit_mode?
109 mode = get(SYSTEM_MODE_CONF_KEY)
114 mode = get(SYSTEM_MODE_CONF_KEY)
110 return ((mode == 'contest') or (mode == 'indv-contest'))
115 return ((mode == 'contest') or (mode == 'indv-contest'))
111 end
116 end
112
117
113 def self.analysis_mode?
118 def self.analysis_mode?
114 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
119 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
115 end
120 end
116
121
117 def self.contest_time_limit
122 def self.contest_time_limit
118 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
123 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
119
124
120 if not defined? GraderConfiguration.contest_time_str
125 if not defined? GraderConfiguration.contest_time_str
121 GraderConfiguration.contest_time_str = nil
126 GraderConfiguration.contest_time_str = nil
122 end
127 end
123
128
124 if GraderConfiguration.contest_time_str != contest_time_str
129 if GraderConfiguration.contest_time_str != contest_time_str
125 GraderConfiguration.contest_time_str = contest_time_str
130 GraderConfiguration.contest_time_str = contest_time_str
126 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
131 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
127 h = tmatch[1].to_i
132 h = tmatch[1].to_i
128 m = tmatch[2].to_i
133 m = tmatch[2].to_i
129
134
130 GraderConfiguration.contest_time = h.hour + m.minute
135 GraderConfiguration.contest_time = h.hour + m.minute
131 else
136 else
132 GraderConfiguration.contest_time = nil
137 GraderConfiguration.contest_time = nil
133 end
138 end
134 end
139 end
135 return GraderConfiguration.contest_time
140 return GraderConfiguration.contest_time
136 end
141 end
137
142
138 protected
143 protected
139
144
140 def self.convert_type(val,type)
145 def self.convert_type(val,type)
141 case type
146 case type
142 when 'string'
147 when 'string'
143 return val
148 return val
144
149
145 when 'integer'
150 when 'integer'
146 return val.to_i
151 return val.to_i
147
152
148 when 'boolean'
153 when 'boolean'
149 return (val=='true')
154 return (val=='true')
150 end
155 end
151 end
156 end
152
157
153 def self.read_config
158 def self.read_config
154 GraderConfiguration.config_cache = {}
159 GraderConfiguration.config_cache = {}
155 GraderConfiguration.all.each do |conf|
160 GraderConfiguration.all.each do |conf|
156 key = conf.key
161 key = conf.key
157 val = conf.value
162 val = conf.value
158 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
163 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
159 end
164 end
160 end
165 end
161
166
162 def self.read_one_key(key)
167 def self.read_one_key(key)
163 conf = GraderConfiguration.find_by_key(key)
168 conf = GraderConfiguration.find_by_key(key)
164 if conf
169 if conf
165 return GraderConfiguration.convert_type(conf.value,conf.value_type)
170 return GraderConfiguration.convert_type(conf.value,conf.value_type)
166 else
171 else
167 return nil
172 return nil
168 end
173 end
169 end
174 end
170
175
171 def self.read_grading_info
176 def self.read_grading_info
172 f = File.open(TASK_GRADING_INFO_FILENAME)
177 f = File.open(TASK_GRADING_INFO_FILENAME)
173 GraderConfiguration.task_grading_info_cache = YAML.load(f)
178 GraderConfiguration.task_grading_info_cache = YAML.load(f)
174 f.close
179 f.close
175 end
180 end
176
181
177 end
182 end
@@ -1,27 +1,29
1
1
2 - if submission.nil?
2 - if submission.nil?
3 = "-"
3 = "-"
4 - else
4 - else
5 - unless submission.graded_at
5 - unless submission.graded_at
6 = t 'main.submitted_at'
6 = t 'main.submitted_at'
7 = format_short_time(submission.submitted_at.localtime)
7 = format_short_time(submission.submitted_at.localtime)
8 - else
8 - else
9 %strong= t 'main.graded_at'
9 %strong= t 'main.graded_at'
10 = "#{format_short_time(submission.graded_at.localtime)} "
10 = "#{format_short_time(submission.graded_at.localtime)} "
11 %br
11 %br
12 - if GraderConfiguration['ui.show_score']
12 - if GraderConfiguration['ui.show_score']
13 %strong=t 'main.score'
13 %strong=t 'main.score'
14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
15 = " ["
15 = " ["
16 %tt
16 %tt
17 = submission.grader_comment
17 = submission.grader_comment
18 = "]"
18 = "]"
19 %br
19 %br
20 %strong View:
20 %strong View:
21 - if GraderConfiguration.show_grading_result
21 - if GraderConfiguration.show_grading_result
22 = link_to '[detailed result]', :action => 'result', :id => submission.id
22 = link_to '[detailed result]', :action => 'result', :id => submission.id
23 /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
23 /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
24 = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'}
24 = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'}
25 = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info'
25 = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info'
26 = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
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,77 +1,85
1 CafeGrader::Application.routes.draw do
1 CafeGrader::Application.routes.draw do
2 get "sources/direct_edit"
2 get "sources/direct_edit"
3
3
4 root :to => 'main#login'
4 root :to => 'main#login'
5
5
6 resources :contests
6 resources :contests
7
7
8 resources :sites
8 resources :sites
9
9
10 resources :announcements do
10 resources :announcements do
11 member do
11 member do
12 get 'toggle','toggle_front'
12 get 'toggle','toggle_front'
13 end
13 end
14 end
14 end
15
15
16 resources :problems do
16 resources :problems do
17 member do
17 member do
18 get 'toggle'
18 get 'toggle'
19 get 'toggle_test'
19 get 'toggle_test'
20 get 'stat'
20 get 'stat'
21 + get 'show_testcase'
21 end
22 end
22 collection do
23 collection do
23 get 'turn_all_off'
24 get 'turn_all_off'
24 get 'turn_all_on'
25 get 'turn_all_on'
25 get 'import'
26 get 'import'
26 get 'manage'
27 get 'manage'
27 end
28 end
29 +
30 + resources :testcases, only: [] do
31 + member do
32 + get 'download_input'
33 + get 'download_sol'
34 + end
35 + end
28 end
36 end
29
37
30 resources :grader_configuration, controller: 'configurations'
38 resources :grader_configuration, controller: 'configurations'
31
39
32 resources :users do
40 resources :users do
33 member do
41 member do
34 get 'toggle_activate', 'toggle_enable'
42 get 'toggle_activate', 'toggle_enable'
35 get 'stat'
43 get 'stat'
36 end
44 end
37 end
45 end
38
46
39 resources :submissions do
47 resources :submissions do
40 member do
48 member do
41 get 'download'
49 get 'download'
42 get 'compiler_msg'
50 get 'compiler_msg'
43 end
51 end
44 collection do
52 collection do
45 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
53 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
46 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
54 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
47 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
55 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
48 end
56 end
49 end
57 end
50
58
51 get 'tasks/view/:file.:ext' => 'tasks#view'
59 get 'tasks/view/:file.:ext' => 'tasks#view'
52 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
60 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
53 get 'heartbeat/:id/edit' => 'heartbeat#edit'
61 get 'heartbeat/:id/edit' => 'heartbeat#edit'
54
62
55 #main
63 #main
56 get "main/list"
64 get "main/list"
57 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
65 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
58
66
59 #report
67 #report
60 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
68 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
61 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
69 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
62 get "report/login"
70 get "report/login"
63 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
71 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
64 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
72 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
65
73
66 #grader
74 #grader
67 get 'graders/list', to: 'graders#list', as: 'grader_list'
75 get 'graders/list', to: 'graders#list', as: 'grader_list'
68
76
69
77
70 get 'heartbeat/:id/edit' => 'heartbeat#edit'
78 get 'heartbeat/:id/edit' => 'heartbeat#edit'
71
79
72 # See how all your routes lay out with "rake routes"
80 # See how all your routes lay out with "rake routes"
73
81
74 # This is a legacy wild controller route that's not recommended for RESTful applications.
82 # This is a legacy wild controller route that's not recommended for RESTful applications.
75 # Note: This route will make all actions in every controller accessible via GET requests.
83 # Note: This route will make all actions in every controller accessible via GET requests.
76 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
84 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
77 end
85 end
@@ -1,245 +1,251
1 CONFIGURATIONS =
1 CONFIGURATIONS =
2 [
2 [
3 {
3 {
4 :key => 'system.single_user_mode',
4 :key => 'system.single_user_mode',
5 :value_type => 'boolean',
5 :value_type => 'boolean',
6 :default_value => 'false',
6 :default_value => 'false',
7 :description => 'Only admins can log in to the system when running under single user mode.'
7 :description => 'Only admins can log in to the system when running under single user mode.'
8 },
8 },
9
9
10 {
10 {
11 :key => 'ui.front.title',
11 :key => 'ui.front.title',
12 :value_type => 'string',
12 :value_type => 'string',
13 :default_value => 'Grader'
13 :default_value => 'Grader'
14 },
14 },
15
15
16 {
16 {
17 :key => 'ui.front.welcome_message',
17 :key => 'ui.front.welcome_message',
18 :value_type => 'string',
18 :value_type => 'string',
19 :default_value => 'Welcome!'
19 :default_value => 'Welcome!'
20 },
20 },
21
21
22 {
22 {
23 :key => 'ui.show_score',
23 :key => 'ui.show_score',
24 :value_type => 'boolean',
24 :value_type => 'boolean',
25 :default_value => 'true'
25 :default_value => 'true'
26 },
26 },
27
27
28 {
28 {
29 :key => 'contest.time_limit',
29 :key => 'contest.time_limit',
30 :value_type => 'string',
30 :value_type => 'string',
31 :default_value => 'unlimited',
31 :default_value => 'unlimited',
32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
33 },
33 },
34
34
35 {
35 {
36 :key => 'system.mode',
36 :key => 'system.mode',
37 :value_type => 'string',
37 :value_type => 'string',
38 :default_value => 'standard',
38 :default_value => 'standard',
39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 },
40 },
41
41
42 {
42 {
43 :key => 'contest.name',
43 :key => 'contest.name',
44 :value_type => 'string',
44 :value_type => 'string',
45 :default_value => 'Grader',
45 :default_value => 'Grader',
46 :description => 'This name will be shown on the user header bar.'
46 :description => 'This name will be shown on the user header bar.'
47 },
47 },
48
48
49 {
49 {
50 :key => 'contest.multisites',
50 :key => 'contest.multisites',
51 :value_type => 'boolean',
51 :value_type => 'boolean',
52 :default_value => 'false',
52 :default_value => 'false',
53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
54 },
54 },
55
55
56 #---------------------------- right --------------------------------
56 #---------------------------- right --------------------------------
57 {
57 {
58 :key => 'right.user_hall_of_fame',
58 :key => 'right.user_hall_of_fame',
59 :value_type => 'boolean',
59 :value_type => 'boolean',
60 :default_value => 'false',
60 :default_value => 'false',
61 :description => 'If true, any user can access hall of fame page.'
61 :description => 'If true, any user can access hall of fame page.'
62 },
62 },
63
63
64 {
64 {
65 :key => 'right.multiple_ip_login',
65 :key => 'right.multiple_ip_login',
66 :value_type => 'boolean',
66 :value_type => 'boolean',
67 :default_value => 'true',
67 :default_value => 'true',
68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
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 :key => 'right.user_view_submission',
72 :key => 'right.user_view_submission',
73 :value_type => 'boolean',
73 :value_type => 'boolean',
74 :default_value => 'false',
74 :default_value => 'false',
75 :description => 'If true, any user can view submissions of every one.'
75 :description => 'If true, any user can view submissions of every one.'
76 },
76 },
77
77
78 {
78 {
79 :key => 'right.bypass_agreement',
79 :key => 'right.bypass_agreement',
80 :value_type => 'boolean',
80 :value_type => 'boolean',
81 :default_value => 'true',
81 :default_value => 'true',
82 :description => 'When false, a user must accept usage agreement before login'
82 :description => 'When false, a user must accept usage agreement before login'
83 },
83 },
84
84
85 {
85 {
86 :key => 'right.heartbeat_response',
86 :key => 'right.heartbeat_response',
87 :value_type => 'string',
87 :value_type => 'string',
88 :default_value => 'OK',
88 :default_value => 'OK',
89 :description => 'Heart beat response text'
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 # If Configuration['system.online_registration'] is true, the
98 # If Configuration['system.online_registration'] is true, the
93 # system allows online registration, and will use these
99 # system allows online registration, and will use these
94 # information for sending confirmation emails.
100 # information for sending confirmation emails.
95 {
101 {
96 :key => 'system.online_registration.smtp',
102 :key => 'system.online_registration.smtp',
97 :value_type => 'string',
103 :value_type => 'string',
98 :default_value => 'smtp.somehost.com'
104 :default_value => 'smtp.somehost.com'
99 },
105 },
100
106
101 {
107 {
102 :key => 'system.online_registration.from',
108 :key => 'system.online_registration.from',
103 :value_type => 'string',
109 :value_type => 'string',
104 :default_value => 'your.email@address'
110 :default_value => 'your.email@address'
105 },
111 },
106
112
107 {
113 {
108 :key => 'system.admin_email',
114 :key => 'system.admin_email',
109 :value_type => 'string',
115 :value_type => 'string',
110 :default_value => 'admin@admin.email'
116 :default_value => 'admin@admin.email'
111 },
117 },
112
118
113 {
119 {
114 :key => 'system.user_setting_enabled',
120 :key => 'system.user_setting_enabled',
115 :value_type => 'boolean',
121 :value_type => 'boolean',
116 :default_value => 'true',
122 :default_value => 'true',
117 :description => 'If this option is true, users can change their settings'
123 :description => 'If this option is true, users can change their settings'
118 },
124 },
119
125
120 {
126 {
121 :key => 'system.user_setting_enabled',
127 :key => 'system.user_setting_enabled',
122 :value_type => 'boolean',
128 :value_type => 'boolean',
123 :default_value => 'true',
129 :default_value => 'true',
124 :description => 'If this option is true, users can change their settings'
130 :description => 'If this option is true, users can change their settings'
125 },
131 },
126
132
127 # If Configuration['contest.test_request.early_timeout'] is true
133 # If Configuration['contest.test_request.early_timeout'] is true
128 # the user will not be able to use test request at 30 minutes
134 # the user will not be able to use test request at 30 minutes
129 # before the contest ends.
135 # before the contest ends.
130 {
136 {
131 :key => 'contest.test_request.early_timeout',
137 :key => 'contest.test_request.early_timeout',
132 :value_type => 'boolean',
138 :value_type => 'boolean',
133 :default_value => 'false'
139 :default_value => 'false'
134 },
140 },
135
141
136 {
142 {
137 :key => 'system.multicontests',
143 :key => 'system.multicontests',
138 :value_type => 'boolean',
144 :value_type => 'boolean',
139 :default_value => 'false'
145 :default_value => 'false'
140 },
146 },
141
147
142 {
148 {
143 :key => 'contest.confirm_indv_contest_start',
149 :key => 'contest.confirm_indv_contest_start',
144 :value_type => 'boolean',
150 :value_type => 'boolean',
145 :default_value => 'false'
151 :default_value => 'false'
146 },
152 },
147
153
148 {
154 {
149 :key => 'contest.default_contest_name',
155 :key => 'contest.default_contest_name',
150 :value_type => 'string',
156 :value_type => 'string',
151 :default_value => 'none',
157 :default_value => 'none',
152 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
158 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
153 }
159 }
154
160
155 ]
161 ]
156
162
157
163
158 def create_configuration_key(key,
164 def create_configuration_key(key,
159 value_type,
165 value_type,
160 default_value,
166 default_value,
161 description='')
167 description='')
162 conf = (GraderConfiguration.find_by_key(key) ||
168 conf = (GraderConfiguration.find_by_key(key) ||
163 GraderConfiguration.new(:key => key,
169 GraderConfiguration.new(:key => key,
164 :value_type => value_type,
170 :value_type => value_type,
165 :value => default_value))
171 :value => default_value))
166 conf.description = description
172 conf.description = description
167 conf.save
173 conf.save
168 end
174 end
169
175
170 def seed_config
176 def seed_config
171 CONFIGURATIONS.each do |conf|
177 CONFIGURATIONS.each do |conf|
172 if conf.has_key? :description
178 if conf.has_key? :description
173 desc = conf[:description]
179 desc = conf[:description]
174 else
180 else
175 desc = ''
181 desc = ''
176 end
182 end
177 create_configuration_key(conf[:key],
183 create_configuration_key(conf[:key],
178 conf[:value_type],
184 conf[:value_type],
179 conf[:default_value],
185 conf[:default_value],
180 desc)
186 desc)
181 end
187 end
182 end
188 end
183
189
184 def seed_roles
190 def seed_roles
185 return if Role.find_by_name('admin')
191 return if Role.find_by_name('admin')
186
192
187 role = Role.create(:name => 'admin')
193 role = Role.create(:name => 'admin')
188 user_admin_right = Right.create(:name => 'user_admin',
194 user_admin_right = Right.create(:name => 'user_admin',
189 :controller => 'user_admin',
195 :controller => 'user_admin',
190 :action => 'all')
196 :action => 'all')
191 problem_admin_right = Right.create(:name=> 'problem_admin',
197 problem_admin_right = Right.create(:name=> 'problem_admin',
192 :controller => 'problems',
198 :controller => 'problems',
193 :action => 'all')
199 :action => 'all')
194
200
195 graders_right = Right.create(:name => 'graders_admin',
201 graders_right = Right.create(:name => 'graders_admin',
196 :controller => 'graders',
202 :controller => 'graders',
197 :action => 'all')
203 :action => 'all')
198
204
199 role.rights << user_admin_right;
205 role.rights << user_admin_right;
200 role.rights << problem_admin_right;
206 role.rights << problem_admin_right;
201 role.rights << graders_right;
207 role.rights << graders_right;
202 role.save
208 role.save
203 end
209 end
204
210
205 def seed_root
211 def seed_root
206 return if User.find_by_login('root')
212 return if User.find_by_login('root')
207
213
208 root = User.new(:login => 'root',
214 root = User.new(:login => 'root',
209 :full_name => 'Administrator',
215 :full_name => 'Administrator',
210 :alias => 'root')
216 :alias => 'root')
211 root.password = 'ioionrails';
217 root.password = 'ioionrails';
212
218
213 class << root
219 class << root
214 public :encrypt_new_password
220 public :encrypt_new_password
215 def valid?(context=nil)
221 def valid?(context=nil)
216 true
222 true
217 end
223 end
218 end
224 end
219
225
220 root.encrypt_new_password
226 root.encrypt_new_password
221
227
222 root.roles << Role.find_by_name('admin')
228 root.roles << Role.find_by_name('admin')
223
229
224 root.activated = true
230 root.activated = true
225 root.save
231 root.save
226 end
232 end
227
233
228 def seed_users_and_roles
234 def seed_users_and_roles
229 seed_roles
235 seed_roles
230 seed_root
236 seed_root
231 end
237 end
232
238
233 def seed_more_languages
239 def seed_more_languages
234 Language.delete_all
240 Language.delete_all
235 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
241 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
236 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
242 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
237 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
243 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
238 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
244 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
239 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
245 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
240 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
246 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
241 end
247 end
242
248
243 seed_config
249 seed_config
244 seed_users_and_roles
250 seed_users_and_roles
245 seed_more_languages
251 seed_more_languages
@@ -1,58 +1,73
1 module GraderScript
1 module GraderScript
2
2
3 def self.grader_control_enabled?
3 def self.grader_control_enabled?
4 if defined? GRADER_ROOT_DIR
4 if defined? GRADER_ROOT_DIR
5 GRADER_ROOT_DIR != ''
5 GRADER_ROOT_DIR != ''
6 else
6 else
7 false
7 false
8 end
8 end
9 end
9 end
10
10
11 def self.raw_dir
11 def self.raw_dir
12 File.join GRADER_ROOT_DIR, "raw"
12 File.join GRADER_ROOT_DIR, "raw"
13 end
13 end
14
14
15 def self.call_grader(params)
15 def self.call_grader(params)
16 if GraderScript.grader_control_enabled?
16 if GraderScript.grader_control_enabled?
17 cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params
17 cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params
18 system(cmd)
18 system(cmd)
19 end
19 end
20 end
20 end
21
21
22 def self.stop_grader(pid)
22 def self.stop_grader(pid)
23 GraderScript.call_grader "stop #{pid}"
23 GraderScript.call_grader "stop #{pid}"
24 end
24 end
25
25
26 def self.stop_graders(pids)
26 def self.stop_graders(pids)
27 pid_str = (pids.map { |process| process.pid.to_s }).join ' '
27 pid_str = (pids.map { |process| process.pid.to_s }).join ' '
28 GraderScript.call_grader "stop #{pid_str}"
28 GraderScript.call_grader "stop #{pid_str}"
29 end
29 end
30
30
31 def self.start_grader(env)
31 def self.start_grader(env)
32 GraderScript.call_grader "#{env} queue --err-log &"
32 GraderScript.call_grader "#{env} queue --err-log &"
33 GraderScript.call_grader "#{env} test_request -err-log &"
33 GraderScript.call_grader "#{env} test_request -err-log &"
34 end
34 end
35
35
36 def self.call_import_problem(problem_name,
36 def self.call_import_problem(problem_name,
37 problem_dir,
37 problem_dir,
38 time_limit=1,
38 time_limit=1,
39 memory_limit=32,
39 memory_limit=32,
40 checker_name='text')
40 checker_name='text')
41 if GraderScript.grader_control_enabled?
41 if GraderScript.grader_control_enabled?
42 cur_dir = `pwd`.chomp
42 cur_dir = `pwd`.chomp
43 Dir.chdir(GRADER_ROOT_DIR)
43 Dir.chdir(GRADER_ROOT_DIR)
44
44
45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
47 " -t #{time_limit} -m #{memory_limit}"
47 " -t #{time_limit} -m #{memory_limit}"
48
48
49 output = `#{cmd}`
49 output = `#{cmd}`
50
50
51 Dir.chdir(cur_dir)
51 Dir.chdir(cur_dir)
52 -
52 +
53 return "import CMD: #{cmd}\n" + output
53 return "import CMD: #{cmd}\n" + output
54 end
54 end
55 return ''
55 return ''
56 end
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 end
73 end
@@ -1,190 +1,193
1 require 'tmpdir'
1 require 'tmpdir'
2
2
3 class TestdataImporter
3 class TestdataImporter
4
4
5 attr :log_msg
5 attr :log_msg
6
6
7 def initialize(problem)
7 def initialize(problem)
8 @problem = problem
8 @problem = problem
9 end
9 end
10
10
11 def import_from_file(tempfile,
11 def import_from_file(tempfile,
12 time_limit,
12 time_limit,
13 memory_limit,
13 memory_limit,
14 checker_name='text',
14 checker_name='text',
15 import_to_db=false)
15 import_to_db=false)
16
16
17 dirname = extract(tempfile)
17 dirname = extract(tempfile)
18 return false if not dirname
18 return false if not dirname
19 if not import_to_db
19 if not import_to_db
20 @log_msg = GraderScript.call_import_problem(@problem.name,
20 @log_msg = GraderScript.call_import_problem(@problem.name,
21 dirname,
21 dirname,
22 time_limit,
22 time_limit,
23 memory_limit,
23 memory_limit,
24 checker_name)
24 checker_name)
25 else
25 else
26 # Import test data to test pairs.
26 # Import test data to test pairs.
27
27
28 @problem.test_pairs.clear
28 @problem.test_pairs.clear
29 if import_test_pairs(dirname)
29 if import_test_pairs(dirname)
30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
32 else
32 else
33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
34 end
34 end
35 end
35 end
36
36
37 @log_msg << import_problem_description(dirname)
37 @log_msg << import_problem_description(dirname)
38 @log_msg << import_problem_pdf(dirname)
38 @log_msg << import_problem_pdf(dirname)
39 @log_msg << import_full_score(dirname)
39 @log_msg << import_full_score(dirname)
40
40
41 + #import test data
42 + @log_msg << GraderScript.call_import_testcase(@problem.name)
43 +
41 return true
44 return true
42 end
45 end
43
46
44 protected
47 protected
45
48
46 def self.long_ext(filename)
49 def self.long_ext(filename)
47 i = filename.index('.')
50 i = filename.index('.')
48 len = filename.length
51 len = filename.length
49 return filename.slice(i..len)
52 return filename.slice(i..len)
50 end
53 end
51
54
52 def extract(tempfile)
55 def extract(tempfile)
53 testdata_filename = save_testdata_file(tempfile)
56 testdata_filename = save_testdata_file(tempfile)
54 ext = TestdataImporter.long_ext(tempfile.original_filename)
57 ext = TestdataImporter.long_ext(tempfile.original_filename)
55
58
56 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
59 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
57 if File.exists? extract_dir
60 if File.exists? extract_dir
58 backup_count = 0
61 backup_count = 0
59 begin
62 begin
60 backup_count += 1
63 backup_count += 1
61 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
64 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
62 end while File.exists? backup_dirname
65 end while File.exists? backup_dirname
63 File.rename(extract_dir, backup_dirname)
66 File.rename(extract_dir, backup_dirname)
64 end
67 end
65 Dir.mkdir extract_dir
68 Dir.mkdir extract_dir
66
69
67 if ext=='.tar.gz' or ext=='.tgz'
70 if ext=='.tar.gz' or ext=='.tgz'
68 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
71 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
69 elsif ext=='.tar'
72 elsif ext=='.tar'
70 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
73 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
71 elsif ext=='.zip'
74 elsif ext=='.zip'
72 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
75 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
73 else
76 else
74 return nil
77 return nil
75 end
78 end
76
79
77 system(cmd)
80 system(cmd)
78
81
79 files = Dir["#{extract_dir}/**/*1*.in"]
82 files = Dir["#{extract_dir}/**/*1*.in"]
80 return nil if files.length==0
83 return nil if files.length==0
81
84
82 File.delete(testdata_filename)
85 File.delete(testdata_filename)
83
86
84 return File.dirname(files[0])
87 return File.dirname(files[0])
85 end
88 end
86
89
87 def save_testdata_file(tempfile)
90 def save_testdata_file(tempfile)
88 ext = TestdataImporter.long_ext(tempfile.original_filename)
91 ext = TestdataImporter.long_ext(tempfile.original_filename)
89 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
92 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
90
93
91 return nil if tempfile==""
94 return nil if tempfile==""
92
95
93 if tempfile.instance_of?(Tempfile)
96 if tempfile.instance_of?(Tempfile)
94 tempfile.close
97 tempfile.close
95 FileUtils.move(tempfile.path,testdata_filename)
98 FileUtils.move(tempfile.path,testdata_filename)
96 else
99 else
97 File.open(testdata_filename, "wb") do |f|
100 File.open(testdata_filename, "wb") do |f|
98 f.write(tempfile.read)
101 f.write(tempfile.read)
99 end
102 end
100 end
103 end
101
104
102 return testdata_filename
105 return testdata_filename
103 end
106 end
104
107
105 def import_test_pairs(dirname)
108 def import_test_pairs(dirname)
106 test_num = 1
109 test_num = 1
107 while FileTest.exists? "#{dirname}/#{test_num}.in"
110 while FileTest.exists? "#{dirname}/#{test_num}.in"
108 in_filename = "#{dirname}/#{test_num}.in"
111 in_filename = "#{dirname}/#{test_num}.in"
109 sol_filename = "#{dirname}/#{test_num}.sol"
112 sol_filename = "#{dirname}/#{test_num}.sol"
110
113
111 break if not FileTest.exists? sol_filename
114 break if not FileTest.exists? sol_filename
112
115
113 test_pair = TestPair.new(:input => open(in_filename).read,
116 test_pair = TestPair.new(:input => open(in_filename).read,
114 :solution => open(sol_filename).read,
117 :solution => open(sol_filename).read,
115 :problem => @problem)
118 :problem => @problem)
116 break if not test_pair.save
119 break if not test_pair.save
117
120
118 test_num += 1
121 test_num += 1
119 end
122 end
120 return test_num > 1
123 return test_num > 1
121 end
124 end
122
125
123 def import_problem_description(dirname)
126 def import_problem_description(dirname)
124 html_files = Dir["#{dirname}/*.html"]
127 html_files = Dir["#{dirname}/*.html"]
125 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
128 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
126 if (html_files.length != 0) or (markdown_files.length != 0)
129 if (html_files.length != 0) or (markdown_files.length != 0)
127 description = @problem.description || Description.new
130 description = @problem.description || Description.new
128
131
129 if html_files.length != 0
132 if html_files.length != 0
130 filename = html_files[0]
133 filename = html_files[0]
131 description.markdowned = false
134 description.markdowned = false
132 else
135 else
133 filename = markdown_files[0]
136 filename = markdown_files[0]
134 description.markdowned = true
137 description.markdowned = true
135 end
138 end
136
139
137 description.body = open(filename).read
140 description.body = open(filename).read
138 description.save
141 description.save
139 @problem.description = description
142 @problem.description = description
140 @problem.save
143 @problem.save
141 return "\nProblem description imported from #{filename}."
144 return "\nProblem description imported from #{filename}."
142 else
145 else
143 return ''
146 return ''
144 end
147 end
145 end
148 end
146
149
147 def import_problem_pdf(dirname)
150 def import_problem_pdf(dirname)
148 pdf_files = Dir["#{dirname}/*.pdf"]
151 pdf_files = Dir["#{dirname}/*.pdf"]
149 puts "CHECKING... #{dirname}"
152 puts "CHECKING... #{dirname}"
150 if pdf_files.length != 0
153 if pdf_files.length != 0
151 puts "HAS PDF FILE"
154 puts "HAS PDF FILE"
152 filename = pdf_files[0]
155 filename = pdf_files[0]
153
156
154 @problem.save if not @problem.id
157 @problem.save if not @problem.id
155 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
158 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
156 if not FileTest.exists? out_dirname
159 if not FileTest.exists? out_dirname
157 Dir.mkdir out_dirname
160 Dir.mkdir out_dirname
158 end
161 end
159
162
160 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
163 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
161
164
162 if FileTest.exists? out_filename
165 if FileTest.exists? out_filename
163 File.delete out_filename
166 File.delete out_filename
164 end
167 end
165
168
166 File.rename(filename, out_filename)
169 File.rename(filename, out_filename)
167 @problem.description_filename = "#{@problem.name}.pdf"
170 @problem.description_filename = "#{@problem.name}.pdf"
168 @problem.save
171 @problem.save
169 return "\nProblem pdf imported from #{filename}."
172 return "\nProblem pdf imported from #{filename}."
170 else
173 else
171 return ""
174 return ""
172 end
175 end
173 end
176 end
174
177
175 #just set the full score to the total number of test case
178 #just set the full score to the total number of test case
176 #it is not perfect but works on most normal use case
179 #it is not perfect but works on most normal use case
177 def import_full_score(dirname)
180 def import_full_score(dirname)
178 num = 0
181 num = 0
179 loop do
182 loop do
180 num += 1
183 num += 1
181 in_file = Dir["#{dirname}/#{num}*.in"]
184 in_file = Dir["#{dirname}/#{num}*.in"]
182 break if in_file.length == 0
185 break if in_file.length == 0
183 end
186 end
184 full_score = (num - 1) * 10
187 full_score = (num - 1) * 10
185 @problem.full_score = full_score
188 @problem.full_score = full_score
186 @problem.save
189 @problem.save
187 return "\nFull score is set to #{full_score}."
190 return "\nFull score is set to #{full_score}."
188 end
191 end
189
192
190 end
193 end
You need to be logged in to leave comments. Login now