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 1 class ApplicationController < ActionController::Base
2 2 protect_from_forgery
3 3
4 4 before_filter :current_user
5 5
6 6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
7 7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
8 8
9 9 #report and redirect for unauthorized activities
10 10 def unauthorized_redirect
11 11 flash[:notice] = 'You are not authorized to view the page you requested'
12 12 redirect_to :controller => 'main', :action => 'login'
13 13 end
14 14
15 15 # Returns the current logged-in user (if any).
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
64 73 end
65 74 return true
66 75 end
67 76
68 77 if GraderConfiguration.multicontests?
69 78 user = User.find(session[:user_id])
70 79 return true if user.admin?
71 80 begin
72 81 if user.contest_stat(true).forced_logout
73 82 flash[:notice] = 'You have been automatically logged out.'
74 83 redirect_to :controller => 'main', :action => 'index'
75 84 end
76 85 rescue
77 86 end
78 87 end
79 88 return true
80 89 end
81 90
82 91 def authenticate_by_ip_address
83 92 #this assume that we have already authenticate normally
84 93 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
85 94 user = User.find(session[:user_id])
86 95 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
87 96 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
88 97 redirect_to :controller => 'main', :action => 'login'
89 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 99 return false
91 100 end
92 101 unless user.last_ip
93 102 user.last_ip = request.remote_ip
94 103 user.save
95 104 end
96 105 end
97 106 return true
98 107 end
99 108
100 109 def authorization
101 110 return false unless authenticate
102 111 user = User.find(session[:user_id])
103 112 unless user.roles.detect { |role|
104 113 role.rights.detect{ |right|
105 114 right.controller == self.class.controller_name and
106 115 (right.action == 'all' or right.action == action_name)
107 116 }
108 117 }
109 118 flash[:notice] = 'You are not authorized to view the page you requested'
110 119 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
111 120 redirect_to :controller => 'main', :action => 'login'
112 121 return false
113 122 end
114 123 end
115 124
116 125 def verify_time_limit
117 126 return true if session[:user_id]==nil
118 127 user = User.find(session[:user_id], :include => :site)
119 128 return true if user==nil or user.site == nil
120 129 if user.contest_finished?
121 130 flash[:notice] = 'Error: the contest you are participating is over.'
122 131 redirect_to :back
123 132 return false
124 133 end
125 134 return true
126 135 end
127 136
128 137 end
@@ -1,99 +1,100
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
28 29
29 30 def create
30 31 @problem = Problem.new(params[:problem])
31 32 @description = Description.new(params[:description])
32 33 if @description.body!=''
33 34 if !@description.save
34 35 render :action => new and return
35 36 end
36 37 else
37 38 @description = nil
38 39 end
39 40 @problem.description = @description
40 41 if @problem.save
41 42 flash[:notice] = 'Problem was successfully created.'
42 43 redirect_to action: :index
43 44 else
44 45 render :action => 'new'
45 46 end
46 47 end
47 48
48 49 def quick_create
49 50 @problem = Problem.new(params[:problem])
50 51 @problem.full_name = @problem.name if @problem.full_name == ''
51 52 @problem.full_score = 100
52 53 @problem.available = false
53 54 @problem.test_allowed = true
54 55 @problem.output_only = false
55 56 @problem.date_added = Time.new
56 57 if @problem.save
57 58 flash[:notice] = 'Problem was successfully created.'
58 59 redirect_to action: :index
59 60 else
60 61 flash[:notice] = 'Error saving problem'
61 62 redirect_to action: :index
62 63 end
63 64 end
64 65
65 66 def edit
66 67 @problem = Problem.find(params[:id])
67 68 @description = @problem.description
68 69 end
69 70
70 71 def update
71 72 @problem = Problem.find(params[:id])
72 73 @description = @problem.description
73 74 if @description.nil? and params[:description][:body]!=''
74 75 @description = Description.new(params[:description])
75 76 if !@description.save
76 77 flash[:notice] = 'Error saving description'
77 78 render :action => 'edit' and return
78 79 end
79 80 @problem.description = @description
80 81 elsif @description
81 82 if !@description.update_attributes(params[:description])
82 83 flash[:notice] = 'Error saving description'
83 84 render :action => 'edit' and return
84 85 end
85 86 end
86 87 if params[:file] and params[:file].content_type != 'application/pdf'
87 88 flash[:notice] = 'Error: Uploaded file is not PDF'
88 89 render :action => 'edit' and return
89 90 end
90 91 if @problem.update_attributes(params[:problem])
91 92 flash[:notice] = 'Problem was successfully updated.'
92 93 unless params[:file] == nil or params[:file] == ''
93 94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 96 if not FileTest.exists? out_dirname
96 97 Dir.mkdir out_dirname
97 98 end
98 99
99 100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
@@ -128,152 +129,156
128 129
129 130 def toggle_test
130 131 @problem = Problem.find(params[:id])
131 132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 133 respond_to do |format|
133 134 format.js { }
134 135 end
135 136 end
136 137
137 138 def turn_all_off
138 139 Problem.available.all.each do |problem|
139 140 problem.available = false
140 141 problem.save
141 142 end
142 143 redirect_to action: :index
143 144 end
144 145
145 146 def turn_all_on
146 147 Problem.where.not(available: true).each do |problem|
147 148 problem.available = true
148 149 problem.save
149 150 end
150 151 redirect_to action: :index
151 152 end
152 153
153 154 def stat
154 155 @problem = Problem.find(params[:id])
155 156 unless @problem.available or session[:admin]
156 157 redirect_to :controller => 'main', :action => 'list'
157 158 return
158 159 end
159 160 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
160 161
161 162 #stat summary
162 163 range =65
163 164 @histogram = { data: Array.new(range,0), summary: {} }
164 165 user = Hash.new(0)
165 166 @submissions.find_each do |sub|
166 167 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
167 168 @histogram[:data][d.to_i] += 1 if d < range
168 169 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
169 170 end
170 171 @histogram[:summary][:max] = [@histogram[:data].max,1].max
171 172
172 173 @summary = { attempt: user.count, solve: 0 }
173 174 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
174 175 end
175 176
176 177 def manage
177 178 @problems = Problem.order(date_added: :desc)
178 179 end
179 180
180 181 def do_manage
181 182 if params.has_key? 'change_date_added'
182 183 change_date_added
183 184 elsif params.has_key? 'add_to_contest'
184 185 add_to_contest
185 186 elsif params.has_key? 'enable_problem'
186 187 set_available(true)
187 188 elsif params.has_key? 'disable_problem'
188 189 set_available(false)
189 190 end
190 191 redirect_to :action => 'manage'
191 192 end
192 193
193 194 def import
194 195 @allow_test_pair_import = allow_test_pair_import?
195 196 end
196 197
197 198 def do_import
198 199 old_problem = Problem.find_by_name(params[:name])
199 200 if !allow_test_pair_import? and params.has_key? :import_to_db
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
248 253 problems = get_problems_from_params
249 254 contest = Contest.find(params[:contest][:id])
250 255 if contest!=nil and contest.enabled
251 256 problems.each do |p|
252 257 p.contests << contest
253 258 end
254 259 end
255 260 end
256 261
257 262 def set_available(avail)
258 263 problems = get_problems_from_params
259 264 problems.each do |p|
260 265 p.available = avail
261 266 p.save
262 267 end
263 268 end
264 269
265 270 def get_problems_from_params
266 271 problems = []
267 272 params.keys.each do |k|
268 273 if k.index('prob-')==0
269 274 name, id, order = k.split('-')
270 275 problems << Problem.find(id)
271 276 end
272 277 end
273 278 problems
274 279 end
275 280
276 281 def get_problems_stat
277 282 end
278 283
279 284 end
@@ -1,168 +1,173
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
37 38 def self.[](key)
38 39 self.get(key)
39 40 end
40 41
41 42 def self.reload
42 43 self.read_config
43 44 end
44 45
45 46 def self.clear
46 47 GraderConfiguration.config_cache = nil
47 48 end
48 49
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?
97 102 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
98 103 end
99 104
100 105 def self.indv_contest_mode?
101 106 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
102 107 end
103 108
104 109 def self.multicontests?
105 110 return get(MULTICONTESTS_KEY) == true
106 111 end
107 112
108 113 def self.time_limit_mode?
109 114 mode = get(SYSTEM_MODE_CONF_KEY)
110 115 return ((mode == 'contest') or (mode == 'indv-contest'))
111 116 end
112 117
113 118 def self.analysis_mode?
114 119 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
115 120 end
116 121
117 122 def self.contest_time_limit
118 123 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
119 124
120 125 if not defined? GraderConfiguration.contest_time_str
121 126 GraderConfiguration.contest_time_str = nil
122 127 end
123 128
124 129 if GraderConfiguration.contest_time_str != contest_time_str
125 130 GraderConfiguration.contest_time_str = contest_time_str
126 131 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
127 132 h = tmatch[1].to_i
128 133 m = tmatch[2].to_i
129 134
130 135 GraderConfiguration.contest_time = h.hour + m.minute
131 136 else
132 137 GraderConfiguration.contest_time = nil
133 138 end
134 139 end
135 140 return GraderConfiguration.contest_time
136 141 end
137 142
138 143 protected
139 144
140 145 def self.convert_type(val,type)
141 146 case type
142 147 when 'string'
143 148 return val
144 149
145 150 when 'integer'
146 151 return val.to_i
147 152
148 153 when 'boolean'
149 154 return (val=='true')
150 155 end
151 156 end
152 157
153 158 def self.read_config
154 159 GraderConfiguration.config_cache = {}
155 160 GraderConfiguration.all.each do |conf|
156 161 key = conf.key
157 162 val = conf.value
158 163 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
159 164 end
160 165 end
161 166
162 167 def self.read_one_key(key)
163 168 conf = GraderConfiguration.find_by_key(key)
164 169 if conf
165 170 return GraderConfiguration.convert_type(conf.value,conf.value_type)
166 171 else
167 172 return nil
168 173 end
@@ -1,27 +1,29
1 1
2 2 - if submission.nil?
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,77 +1,85
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'
52 60 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
53 61 get 'heartbeat/:id/edit' => 'heartbeat#edit'
54 62
55 63 #main
56 64 get "main/list"
57 65 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
58 66
59 67 #report
60 68 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
61 69 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
62 70 get "report/login"
63 71 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
64 72 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
65 73
66 74 #grader
67 75 get 'graders/list', to: 'graders#list', as: 'grader_list'
68 76
69 77
70 78 get 'heartbeat/:id/edit' => 'heartbeat#edit'
71 79
72 80 # See how all your routes lay out with "rake routes"
73 81
74 82 # This is a legacy wild controller route that's not recommended for RESTful applications.
75 83 # Note: This route will make all actions in every controller accessible via GET requests.
76 84 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
77 85 end
@@ -1,187 +1,193
1 1 CONFIGURATIONS =
2 2 [
3 3 {
4 4 :key => 'system.single_user_mode',
5 5 :value_type => 'boolean',
6 6 :default_value => 'false',
7 7 :description => 'Only admins can log in to the system when running under single user mode.'
8 8 },
9 9
10 10 {
11 11 :key => 'ui.front.title',
12 12 :value_type => 'string',
13 13 :default_value => 'Grader'
14 14 },
15 15
16 16 {
17 17 :key => 'ui.front.welcome_message',
18 18 :value_type => 'string',
19 19 :default_value => 'Welcome!'
20 20 },
21 21
22 22 {
23 23 :key => 'ui.show_score',
24 24 :value_type => 'boolean',
25 25 :default_value => 'true'
26 26 },
27 27
28 28 {
29 29 :key => 'contest.time_limit',
30 30 :value_type => 'string',
31 31 :default_value => 'unlimited',
32 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 36 :key => 'system.mode',
37 37 :value_type => 'string',
38 38 :default_value => 'standard',
39 39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 40 },
41 41
42 42 {
43 43 :key => 'contest.name',
44 44 :value_type => 'string',
45 45 :default_value => 'Grader',
46 46 :description => 'This name will be shown on the user header bar.'
47 47 },
48 48
49 49 {
50 50 :key => 'contest.multisites',
51 51 :value_type => 'boolean',
52 52 :default_value => 'false',
53 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 56 #---------------------------- right --------------------------------
57 57 {
58 58 :key => 'right.user_hall_of_fame',
59 59 :value_type => 'boolean',
60 60 :default_value => 'false',
61 61 :description => 'If true, any user can access hall of fame page.'
62 62 },
63 63
64 64 {
65 65 :key => 'right.multiple_ip_login',
66 66 :value_type => 'boolean',
67 67 :default_value => 'true',
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',
116 122 :default_value => 'true',
117 123 :description => 'If this option is true, users can change their settings'
118 124 },
119 125
120 126 {
121 127 :key => 'system.user_setting_enabled',
122 128 :value_type => 'boolean',
123 129 :default_value => 'true',
124 130 :description => 'If this option is true, users can change their settings'
125 131 },
126 132
127 133 # If Configuration['contest.test_request.early_timeout'] is true
128 134 # the user will not be able to use test request at 30 minutes
129 135 # before the contest ends.
130 136 {
131 137 :key => 'contest.test_request.early_timeout',
132 138 :value_type => 'boolean',
133 139 :default_value => 'false'
134 140 },
135 141
136 142 {
137 143 :key => 'system.multicontests',
138 144 :value_type => 'boolean',
139 145 :default_value => 'false'
140 146 },
141 147
142 148 {
143 149 :key => 'contest.confirm_indv_contest_start',
144 150 :value_type => 'boolean',
145 151 :default_value => 'false'
146 152 },
147 153
148 154 {
149 155 :key => 'contest.default_contest_name',
150 156 :value_type => 'string',
151 157 :default_value => 'none',
152 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 164 def create_configuration_key(key,
159 165 value_type,
160 166 default_value,
161 167 description='')
162 168 conf = (GraderConfiguration.find_by_key(key) ||
163 169 GraderConfiguration.new(:key => key,
164 170 :value_type => value_type,
165 171 :value => default_value))
166 172 conf.description = description
167 173 conf.save
168 174 end
169 175
170 176 def seed_config
171 177 CONFIGURATIONS.each do |conf|
172 178 if conf.has_key? :description
173 179 desc = conf[:description]
174 180 else
175 181 desc = ''
176 182 end
177 183 create_configuration_key(conf[:key],
178 184 conf[:value_type],
179 185 conf[:default_value],
180 186 desc)
181 187 end
182 188 end
183 189
184 190 def seed_roles
185 191 return if Role.find_by_name('admin')
186 192
187 193 role = Role.create(:name => 'admin')
@@ -1,58 +1,73
1 1 module GraderScript
2 2
3 3 def self.grader_control_enabled?
4 4 if defined? GRADER_ROOT_DIR
5 5 GRADER_ROOT_DIR != ''
6 6 else
7 7 false
8 8 end
9 9 end
10 10
11 11 def self.raw_dir
12 12 File.join GRADER_ROOT_DIR, "raw"
13 13 end
14 14
15 15 def self.call_grader(params)
16 16 if GraderScript.grader_control_enabled?
17 17 cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params
18 18 system(cmd)
19 19 end
20 20 end
21 21
22 22 def self.stop_grader(pid)
23 23 GraderScript.call_grader "stop #{pid}"
24 24 end
25 25
26 26 def self.stop_graders(pids)
27 27 pid_str = (pids.map { |process| process.pid.to_s }).join ' '
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
@@ -1,136 +1,139
1 1 require 'tmpdir'
2 2
3 3 class TestdataImporter
4 4
5 5 attr :log_msg
6 6
7 7 def initialize(problem)
8 8 @problem = problem
9 9 end
10 10
11 11 def import_from_file(tempfile,
12 12 time_limit,
13 13 memory_limit,
14 14 checker_name='text',
15 15 import_to_db=false)
16 16
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
65 68 Dir.mkdir extract_dir
66 69
67 70 if ext=='.tar.gz' or ext=='.tgz'
68 71 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
69 72 elsif ext=='.tar'
70 73 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
71 74 elsif ext=='.zip'
72 75 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
73 76 else
74 77 return nil
75 78 end
76 79
77 80 system(cmd)
78 81
79 82 files = Dir["#{extract_dir}/**/*1*.in"]
80 83 return nil if files.length==0
81 84
82 85 File.delete(testdata_filename)
83 86
84 87 return File.dirname(files[0])
85 88 end
86 89
87 90 def save_testdata_file(tempfile)
88 91 ext = TestdataImporter.long_ext(tempfile.original_filename)
89 92 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
90 93
91 94 return nil if tempfile==""
92 95
93 96 if tempfile.instance_of?(Tempfile)
94 97 tempfile.close
95 98 FileUtils.move(tempfile.path,testdata_filename)
96 99 else
97 100 File.open(testdata_filename, "wb") do |f|
98 101 f.write(tempfile.read)
99 102 end
100 103 end
101 104
102 105 return testdata_filename
103 106 end
104 107
105 108 def import_test_pairs(dirname)
106 109 test_num = 1
107 110 while FileTest.exists? "#{dirname}/#{test_num}.in"
108 111 in_filename = "#{dirname}/#{test_num}.in"
109 112 sol_filename = "#{dirname}/#{test_num}.sol"
110 113
111 114 break if not FileTest.exists? sol_filename
112 115
113 116 test_pair = TestPair.new(:input => open(in_filename).read,
114 117 :solution => open(sol_filename).read,
115 118 :problem => @problem)
116 119 break if not test_pair.save
117 120
118 121 test_num += 1
119 122 end
120 123 return test_num > 1
121 124 end
122 125
123 126 def import_problem_description(dirname)
124 127 html_files = Dir["#{dirname}/*.html"]
125 128 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
126 129 if (html_files.length != 0) or (markdown_files.length != 0)
127 130 description = @problem.description || Description.new
128 131
129 132 if html_files.length != 0
130 133 filename = html_files[0]
131 134 description.markdowned = false
132 135 else
133 136 filename = markdown_files[0]
134 137 description.markdowned = true
135 138 end
136 139
You need to be logged in to leave comments. Login now