Description:
merge
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r701:f26733606a76 - - 21 files changed: 104 inserted, 29 deleted
@@ -52,97 +52,97 | |||||
|
52 | def list |
|
52 | def list |
|
53 | prepare_list_information |
|
53 | prepare_list_information |
|
54 | end |
|
54 | end |
|
55 |
|
55 | ||
|
56 | def help |
|
56 | def help |
|
57 | @user = User.find(session[:user_id]) |
|
57 | @user = User.find(session[:user_id]) |
|
58 | end |
|
58 | end |
|
59 |
|
59 | ||
|
60 | def submit |
|
60 | def submit |
|
61 | user = User.find(session[:user_id]) |
|
61 | user = User.find(session[:user_id]) |
|
62 |
|
62 | ||
|
63 | @submission = Submission.new |
|
63 | @submission = Submission.new |
|
64 | @submission.problem_id = params[:submission][:problem_id] |
|
64 | @submission.problem_id = params[:submission][:problem_id] |
|
65 | @submission.user = user |
|
65 | @submission.user = user |
|
66 | @submission.language_id = 0 |
|
66 | @submission.language_id = 0 |
|
67 | if (params['file']) and (params['file']!='') |
|
67 | if (params['file']) and (params['file']!='') |
|
68 | @submission.source = File.open(params['file'].path,'r:UTF-8',&:read) |
|
68 | @submission.source = File.open(params['file'].path,'r:UTF-8',&:read) |
|
69 | @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '') |
|
69 | @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '') |
|
70 | @submission.source_filename = params['file'].original_filename |
|
70 | @submission.source_filename = params['file'].original_filename |
|
71 | end |
|
71 | end |
|
72 |
|
72 | ||
|
73 | if (params[:editor_text]) |
|
73 | if (params[:editor_text]) |
|
74 | language = Language.find_by_id(params[:language_id]) |
|
74 | language = Language.find_by_id(params[:language_id]) |
|
75 | @submission.source = params[:editor_text] |
|
75 | @submission.source = params[:editor_text] |
|
76 | @submission.source_filename = "live_edit.#{language.ext}" |
|
76 | @submission.source_filename = "live_edit.#{language.ext}" |
|
77 | @submission.language = language |
|
77 | @submission.language = language |
|
78 | end |
|
78 | end |
|
79 |
|
79 | ||
|
80 | @submission.submitted_at = Time.new.gmtime |
|
80 | @submission.submitted_at = Time.new.gmtime |
|
81 | @submission.ip_address = request.remote_ip |
|
81 | @submission.ip_address = request.remote_ip |
|
82 |
|
82 | ||
|
83 | if GraderConfiguration.time_limit_mode? and user.contest_finished? |
|
83 | if GraderConfiguration.time_limit_mode? and user.contest_finished? |
|
84 | @submission.errors.add(:base,"The contest is over.") |
|
84 | @submission.errors.add(:base,"The contest is over.") |
|
85 | prepare_list_information |
|
85 | prepare_list_information |
|
86 | render :action => 'list' and return |
|
86 | render :action => 'list' and return |
|
87 | end |
|
87 | end |
|
88 |
|
88 | ||
|
89 | if @submission.valid?(@current_user) |
|
89 | if @submission.valid?(@current_user) |
|
90 | if @submission.save == false |
|
90 | if @submission.save == false |
|
91 | flash[:notice] = 'Error saving your submission' |
|
91 | flash[:notice] = 'Error saving your submission' |
|
92 | elsif Task.create(:submission_id => @submission.id, |
|
92 | elsif Task.create(:submission_id => @submission.id, |
|
93 | :status => Task::STATUS_INQUEUE) == false |
|
93 | :status => Task::STATUS_INQUEUE) == false |
|
94 | flash[:notice] = 'Error adding your submission to task queue' |
|
94 | flash[:notice] = 'Error adding your submission to task queue' |
|
95 | end |
|
95 | end |
|
96 | else |
|
96 | else |
|
97 | prepare_list_information |
|
97 | prepare_list_information |
|
98 | render :action => 'list' and return |
|
98 | render :action => 'list' and return |
|
99 | end |
|
99 | end |
|
100 | - redirect_to :action => 'list' |
|
100 | + redirect_to edit_submission_path(@submission) |
|
101 | end |
|
101 | end |
|
102 |
|
102 | ||
|
103 | def source |
|
103 | def source |
|
104 | submission = Submission.find(params[:id]) |
|
104 | submission = Submission.find(params[:id]) |
|
105 | if ((submission.user_id == session[:user_id]) and |
|
105 | if ((submission.user_id == session[:user_id]) and |
|
106 | (submission.problem != nil) and |
|
106 | (submission.problem != nil) and |
|
107 | (submission.problem.available)) |
|
107 | (submission.problem.available)) |
|
108 | send_data(submission.source, |
|
108 | send_data(submission.source, |
|
109 | {:filename => submission.download_filename, |
|
109 | {:filename => submission.download_filename, |
|
110 | :type => 'text/plain'}) |
|
110 | :type => 'text/plain'}) |
|
111 | else |
|
111 | else |
|
112 | flash[:notice] = 'Error viewing source' |
|
112 | flash[:notice] = 'Error viewing source' |
|
113 | redirect_to :action => 'list' |
|
113 | redirect_to :action => 'list' |
|
114 | end |
|
114 | end |
|
115 | end |
|
115 | end |
|
116 |
|
116 | ||
|
117 | def compiler_msg |
|
117 | def compiler_msg |
|
118 | @submission = Submission.find(params[:id]) |
|
118 | @submission = Submission.find(params[:id]) |
|
119 | if @submission.user_id == session[:user_id] |
|
119 | if @submission.user_id == session[:user_id] |
|
120 | render :action => 'compiler_msg', :layout => 'empty' |
|
120 | render :action => 'compiler_msg', :layout => 'empty' |
|
121 | else |
|
121 | else |
|
122 | flash[:notice] = 'Error viewing source' |
|
122 | flash[:notice] = 'Error viewing source' |
|
123 | redirect_to :action => 'list' |
|
123 | redirect_to :action => 'list' |
|
124 | end |
|
124 | end |
|
125 | end |
|
125 | end |
|
126 |
|
126 | ||
|
127 | def result |
|
127 | def result |
|
128 | if !GraderConfiguration.show_grading_result |
|
128 | if !GraderConfiguration.show_grading_result |
|
129 | redirect_to :action => 'list' and return |
|
129 | redirect_to :action => 'list' and return |
|
130 | end |
|
130 | end |
|
131 | @user = User.find(session[:user_id]) |
|
131 | @user = User.find(session[:user_id]) |
|
132 | @submission = Submission.find(params[:id]) |
|
132 | @submission = Submission.find(params[:id]) |
|
133 | if @submission.user!=@user |
|
133 | if @submission.user!=@user |
|
134 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
134 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
135 | redirect_to :action => 'list' and return |
|
135 | redirect_to :action => 'list' and return |
|
136 | end |
|
136 | end |
|
137 | prepare_grading_result(@submission) |
|
137 | prepare_grading_result(@submission) |
|
138 | end |
|
138 | end |
|
139 |
|
139 | ||
|
140 | def load_output |
|
140 | def load_output |
|
141 | if !GraderConfiguration.show_grading_result or params[:num]==nil |
|
141 | if !GraderConfiguration.show_grading_result or params[:num]==nil |
|
142 | redirect_to :action => 'list' and return |
|
142 | redirect_to :action => 'list' and return |
|
143 | end |
|
143 | end |
|
144 | @user = User.find(session[:user_id]) |
|
144 | @user = User.find(session[:user_id]) |
|
145 | @submission = Submission.find(params[:id]) |
|
145 | @submission = Submission.find(params[:id]) |
|
146 | if @submission.user!=@user |
|
146 | if @submission.user!=@user |
|
147 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
147 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
148 | redirect_to :action => 'list' and return |
|
148 | redirect_to :action => 'list' and return |
@@ -120,187 +120,191 | |||||
|
120 | end |
|
120 | end |
|
121 |
|
121 | ||
|
122 | def toggle |
|
122 | def toggle |
|
123 | @problem = Problem.find(params[:id]) |
|
123 | @problem = Problem.find(params[:id]) |
|
124 | @problem.update_attributes(available: !(@problem.available) ) |
|
124 | @problem.update_attributes(available: !(@problem.available) ) |
|
125 | respond_to do |format| |
|
125 | respond_to do |format| |
|
126 | format.js { } |
|
126 | format.js { } |
|
127 | end |
|
127 | end |
|
128 | end |
|
128 | end |
|
129 |
|
129 | ||
|
130 | def toggle_test |
|
130 | def toggle_test |
|
131 | @problem = Problem.find(params[:id]) |
|
131 | @problem = Problem.find(params[:id]) |
|
132 | @problem.update_attributes(test_allowed: !(@problem.test_allowed?) ) |
|
132 | @problem.update_attributes(test_allowed: !(@problem.test_allowed?) ) |
|
133 | respond_to do |format| |
|
133 | respond_to do |format| |
|
134 | format.js { } |
|
134 | format.js { } |
|
135 | end |
|
135 | end |
|
136 | end |
|
136 | end |
|
137 |
|
137 | ||
|
138 | def toggle_view_testcase |
|
138 | def toggle_view_testcase |
|
139 | @problem = Problem.find(params[:id]) |
|
139 | @problem = Problem.find(params[:id]) |
|
140 | @problem.update_attributes(view_testcase: !(@problem.view_testcase?) ) |
|
140 | @problem.update_attributes(view_testcase: !(@problem.view_testcase?) ) |
|
141 | respond_to do |format| |
|
141 | respond_to do |format| |
|
142 | format.js { } |
|
142 | format.js { } |
|
143 | end |
|
143 | end |
|
144 | end |
|
144 | end |
|
145 |
|
145 | ||
|
146 | def turn_all_off |
|
146 | def turn_all_off |
|
147 | Problem.available.all.each do |problem| |
|
147 | Problem.available.all.each do |problem| |
|
148 | problem.available = false |
|
148 | problem.available = false |
|
149 | problem.save |
|
149 | problem.save |
|
150 | end |
|
150 | end |
|
151 | redirect_to action: :index |
|
151 | redirect_to action: :index |
|
152 | end |
|
152 | end |
|
153 |
|
153 | ||
|
154 | def turn_all_on |
|
154 | def turn_all_on |
|
155 | Problem.where.not(available: true).each do |problem| |
|
155 | Problem.where.not(available: true).each do |problem| |
|
156 | problem.available = true |
|
156 | problem.available = true |
|
157 | problem.save |
|
157 | problem.save |
|
158 | end |
|
158 | end |
|
159 | redirect_to action: :index |
|
159 | redirect_to action: :index |
|
160 | end |
|
160 | end |
|
161 |
|
161 | ||
|
162 | def stat |
|
162 | def stat |
|
163 | @problem = Problem.find(params[:id]) |
|
163 | @problem = Problem.find(params[:id]) |
|
164 | unless @problem.available or session[:admin] |
|
164 | unless @problem.available or session[:admin] |
|
165 | redirect_to :controller => 'main', :action => 'list' |
|
165 | redirect_to :controller => 'main', :action => 'list' |
|
166 | return |
|
166 | return |
|
167 | end |
|
167 | end |
|
168 | - @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id) |
|
168 | + @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id) |
|
169 |
|
169 | ||
|
170 | #stat summary |
|
170 | #stat summary |
|
171 | range =65 |
|
171 | range =65 |
|
172 | @histogram = { data: Array.new(range,0), summary: {} } |
|
172 | @histogram = { data: Array.new(range,0), summary: {} } |
|
173 | user = Hash.new(0) |
|
173 | user = Hash.new(0) |
|
174 | @submissions.find_each do |sub| |
|
174 | @submissions.find_each do |sub| |
|
175 | d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60 |
|
175 | d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60 |
|
176 | @histogram[:data][d.to_i] += 1 if d < range |
|
176 | @histogram[:data][d.to_i] += 1 if d < range |
|
177 | user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max |
|
177 | user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max |
|
178 | end |
|
178 | end |
|
179 | @histogram[:summary][:max] = [@histogram[:data].max,1].max |
|
179 | @histogram[:summary][:max] = [@histogram[:data].max,1].max |
|
180 |
|
180 | ||
|
181 | @summary = { attempt: user.count, solve: 0 } |
|
181 | @summary = { attempt: user.count, solve: 0 } |
|
182 | user.each_value { |v| @summary[:solve] += 1 if v == 1 } |
|
182 | user.each_value { |v| @summary[:solve] += 1 if v == 1 } |
|
183 | end |
|
183 | end |
|
184 |
|
184 | ||
|
185 | def manage |
|
185 | def manage |
|
186 | @problems = Problem.order(date_added: :desc) |
|
186 | @problems = Problem.order(date_added: :desc) |
|
187 | end |
|
187 | end |
|
188 |
|
188 | ||
|
189 | def do_manage |
|
189 | def do_manage |
|
190 | if params.has_key? 'change_date_added' |
|
190 | if params.has_key? 'change_date_added' |
|
191 | change_date_added |
|
191 | change_date_added |
|
192 | elsif params.has_key? 'add_to_contest' |
|
192 | elsif params.has_key? 'add_to_contest' |
|
193 | add_to_contest |
|
193 | add_to_contest |
|
194 | elsif params.has_key? 'enable_problem' |
|
194 | elsif params.has_key? 'enable_problem' |
|
195 | set_available(true) |
|
195 | set_available(true) |
|
196 | elsif params.has_key? 'disable_problem' |
|
196 | elsif params.has_key? 'disable_problem' |
|
197 | set_available(false) |
|
197 | set_available(false) |
|
198 | elsif params.has_key? 'add_group' |
|
198 | elsif params.has_key? 'add_group' |
|
199 | group = Group.find(params[:group_id]) |
|
199 | group = Group.find(params[:group_id]) |
|
200 | ok = [] |
|
200 | ok = [] |
|
201 | failed = [] |
|
201 | failed = [] |
|
202 | get_problems_from_params.each do |p| |
|
202 | get_problems_from_params.each do |p| |
|
203 | begin |
|
203 | begin |
|
204 | group.problems << p |
|
204 | group.problems << p |
|
205 | ok << p.full_name |
|
205 | ok << p.full_name |
|
206 | rescue => e |
|
206 | rescue => e |
|
207 | failed << p.full_name |
|
207 | failed << p.full_name |
|
208 | end |
|
208 | end |
|
209 | end |
|
209 | end |
|
210 | flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0 |
|
210 | flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0 |
|
211 | flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0 |
|
211 | flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0 |
|
|
212 | + elsif params.has_key? 'add_tags' | ||
|
|
213 | + get_problems_from_params.each do |p| | ||
|
|
214 | + p.tag_ids += params[:tag_ids] | ||
|
|
215 | + end | ||
|
212 | end |
|
216 | end |
|
213 |
|
217 | ||
|
214 | redirect_to :action => 'manage' |
|
218 | redirect_to :action => 'manage' |
|
215 | end |
|
219 | end |
|
216 |
|
220 | ||
|
217 | def import |
|
221 | def import |
|
218 | @allow_test_pair_import = allow_test_pair_import? |
|
222 | @allow_test_pair_import = allow_test_pair_import? |
|
219 | end |
|
223 | end |
|
220 |
|
224 | ||
|
221 | def do_import |
|
225 | def do_import |
|
222 | old_problem = Problem.find_by_name(params[:name]) |
|
226 | old_problem = Problem.find_by_name(params[:name]) |
|
223 | if !allow_test_pair_import? and params.has_key? :import_to_db |
|
227 | if !allow_test_pair_import? and params.has_key? :import_to_db |
|
224 | params.delete :import_to_db |
|
228 | params.delete :import_to_db |
|
225 | end |
|
229 | end |
|
226 | @problem, import_log = Problem.create_from_import_form_params(params, |
|
230 | @problem, import_log = Problem.create_from_import_form_params(params, |
|
227 | old_problem) |
|
231 | old_problem) |
|
228 |
|
232 | ||
|
229 | if !@problem.errors.empty? |
|
233 | if !@problem.errors.empty? |
|
230 | render :action => 'import' and return |
|
234 | render :action => 'import' and return |
|
231 | end |
|
235 | end |
|
232 |
|
236 | ||
|
233 | if old_problem!=nil |
|
237 | if old_problem!=nil |
|
234 | flash[:notice] = "The test data has been replaced for problem #{@problem.name}" |
|
238 | flash[:notice] = "The test data has been replaced for problem #{@problem.name}" |
|
235 | end |
|
239 | end |
|
236 | @log = import_log |
|
240 | @log = import_log |
|
237 | end |
|
241 | end |
|
238 |
|
242 | ||
|
239 | def remove_contest |
|
243 | def remove_contest |
|
240 | problem = Problem.find(params[:id]) |
|
244 | problem = Problem.find(params[:id]) |
|
241 | contest = Contest.find(params[:contest_id]) |
|
245 | contest = Contest.find(params[:contest_id]) |
|
242 | if problem!=nil and contest!=nil |
|
246 | if problem!=nil and contest!=nil |
|
243 | problem.contests.delete(contest) |
|
247 | problem.contests.delete(contest) |
|
244 | end |
|
248 | end |
|
245 | redirect_to :action => 'manage' |
|
249 | redirect_to :action => 'manage' |
|
246 | end |
|
250 | end |
|
247 |
|
251 | ||
|
248 | ################################## |
|
252 | ################################## |
|
249 | protected |
|
253 | protected |
|
250 |
|
254 | ||
|
251 | def allow_test_pair_import? |
|
255 | def allow_test_pair_import? |
|
252 | if defined? ALLOW_TEST_PAIR_IMPORT |
|
256 | if defined? ALLOW_TEST_PAIR_IMPORT |
|
253 | return ALLOW_TEST_PAIR_IMPORT |
|
257 | return ALLOW_TEST_PAIR_IMPORT |
|
254 | else |
|
258 | else |
|
255 | return false |
|
259 | return false |
|
256 | end |
|
260 | end |
|
257 | end |
|
261 | end |
|
258 |
|
262 | ||
|
259 | def change_date_added |
|
263 | def change_date_added |
|
260 | problems = get_problems_from_params |
|
264 | problems = get_problems_from_params |
|
261 | date = Date.parse(params[:date_added]) |
|
265 | date = Date.parse(params[:date_added]) |
|
262 | problems.each do |p| |
|
266 | problems.each do |p| |
|
263 | p.date_added = date |
|
267 | p.date_added = date |
|
264 | p.save |
|
268 | p.save |
|
265 | end |
|
269 | end |
|
266 | end |
|
270 | end |
|
267 |
|
271 | ||
|
268 | def add_to_contest |
|
272 | def add_to_contest |
|
269 | problems = get_problems_from_params |
|
273 | problems = get_problems_from_params |
|
270 | contest = Contest.find(params[:contest][:id]) |
|
274 | contest = Contest.find(params[:contest][:id]) |
|
271 | if contest!=nil and contest.enabled |
|
275 | if contest!=nil and contest.enabled |
|
272 | problems.each do |p| |
|
276 | problems.each do |p| |
|
273 | p.contests << contest |
|
277 | p.contests << contest |
|
274 | end |
|
278 | end |
|
275 | end |
|
279 | end |
|
276 | end |
|
280 | end |
|
277 |
|
281 | ||
|
278 | def set_available(avail) |
|
282 | def set_available(avail) |
|
279 | problems = get_problems_from_params |
|
283 | problems = get_problems_from_params |
|
280 | problems.each do |p| |
|
284 | problems.each do |p| |
|
281 | p.available = avail |
|
285 | p.available = avail |
|
282 | p.save |
|
286 | p.save |
|
283 | end |
|
287 | end |
|
284 | end |
|
288 | end |
|
285 |
|
289 | ||
|
286 | def get_problems_from_params |
|
290 | def get_problems_from_params |
|
287 | problems = [] |
|
291 | problems = [] |
|
288 | params.keys.each do |k| |
|
292 | params.keys.each do |k| |
|
289 | if k.index('prob-')==0 |
|
293 | if k.index('prob-')==0 |
|
290 | name, id, order = k.split('-') |
|
294 | name, id, order = k.split('-') |
|
291 | problems << Problem.find(id) |
|
295 | problems << Problem.find(id) |
|
292 | end |
|
296 | end |
|
293 | end |
|
297 | end |
|
294 | problems |
|
298 | problems |
|
295 | end |
|
299 | end |
|
296 |
|
300 | ||
|
297 | def get_problems_stat |
|
301 | def get_problems_stat |
|
298 | end |
|
302 | end |
|
299 |
|
303 | ||
|
300 | private |
|
304 | private |
|
301 |
|
305 | ||
|
302 | def problem_params |
|
306 | def problem_params |
|
303 | - params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description) |
|
307 | + params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[]) |
|
304 | end |
|
308 | end |
|
305 |
|
309 | ||
|
306 | end |
|
310 | end |
@@ -7,96 +7,98 | |||||
|
7 | before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score] |
|
7 | before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score] |
|
8 |
|
8 | ||
|
9 | before_filter(only: [:problem_hof]) { |c| |
|
9 | before_filter(only: [:problem_hof]) { |c| |
|
10 | return false unless authenticate |
|
10 | return false unless authenticate |
|
11 |
|
11 | ||
|
12 | admin_authorization unless GraderConfiguration["right.user_view_submission"] |
|
12 | admin_authorization unless GraderConfiguration["right.user_view_submission"] |
|
13 | } |
|
13 | } |
|
14 |
|
14 | ||
|
15 | def max_score |
|
15 | def max_score |
|
16 | end |
|
16 | end |
|
17 |
|
17 | ||
|
18 | def current_score |
|
18 | def current_score |
|
19 | @problems = Problem.available_problems |
|
19 | @problems = Problem.available_problems |
|
20 | @users = User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
20 | @users = User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
21 | @scorearray = calculate_max_score(@problems, @users,0,0,true) |
|
21 | @scorearray = calculate_max_score(@problems, @users,0,0,true) |
|
22 |
|
22 | ||
|
23 | #rencer accordingly |
|
23 | #rencer accordingly |
|
24 | if params[:button] == 'download' then |
|
24 | if params[:button] == 'download' then |
|
25 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
25 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
26 | send_data csv, filename: 'max_score.csv' |
|
26 | send_data csv, filename: 'max_score.csv' |
|
27 | else |
|
27 | else |
|
28 | #render template: 'user_admin/user_stat' |
|
28 | #render template: 'user_admin/user_stat' |
|
29 | render 'current_score' |
|
29 | render 'current_score' |
|
30 | end |
|
30 | end |
|
31 | end |
|
31 | end |
|
32 |
|
32 | ||
|
33 | def show_max_score |
|
33 | def show_max_score |
|
34 | #process parameters |
|
34 | #process parameters |
|
35 | #problems |
|
35 | #problems |
|
36 | @problems = [] |
|
36 | @problems = [] |
|
37 | if params[:problem_id] |
|
37 | if params[:problem_id] |
|
38 | params[:problem_id].each do |id| |
|
38 | params[:problem_id].each do |id| |
|
39 | next unless id.strip != "" |
|
39 | next unless id.strip != "" |
|
40 | pid = Problem.find_by_id(id.to_i) |
|
40 | pid = Problem.find_by_id(id.to_i) |
|
41 | @problems << pid if pid |
|
41 | @problems << pid if pid |
|
42 | end |
|
42 | end |
|
43 | end |
|
43 | end |
|
44 |
|
44 | ||
|
45 | #users |
|
45 | #users |
|
46 | @users = if params[:user] == "all" then |
|
46 | @users = if params[:user] == "all" then |
|
47 | User.includes(:contests).includes(:contest_stat) |
|
47 | User.includes(:contests).includes(:contest_stat) |
|
48 | else |
|
48 | else |
|
49 | User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
49 | User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
50 | end |
|
50 | end |
|
51 |
|
51 | ||
|
52 | #set up range from param |
|
52 | #set up range from param |
|
53 | @since_id = params.fetch(:from_id, 0).to_i |
|
53 | @since_id = params.fetch(:from_id, 0).to_i |
|
54 | @until_id = params.fetch(:to_id, 0).to_i |
|
54 | @until_id = params.fetch(:to_id, 0).to_i |
|
|
55 | + @since_id = nil if @since_id == 0 | ||
|
|
56 | + @until_id = nil if @until_id == 0 | ||
|
55 |
|
57 | ||
|
56 | #calculate the routine |
|
58 | #calculate the routine |
|
57 | @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id) |
|
59 | @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id) |
|
58 |
|
60 | ||
|
59 | #rencer accordingly |
|
61 | #rencer accordingly |
|
60 | if params[:button] == 'download' then |
|
62 | if params[:button] == 'download' then |
|
61 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
63 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
62 | send_data csv, filename: 'max_score.csv' |
|
64 | send_data csv, filename: 'max_score.csv' |
|
63 | else |
|
65 | else |
|
64 | #render template: 'user_admin/user_stat' |
|
66 | #render template: 'user_admin/user_stat' |
|
65 | render 'max_score' |
|
67 | render 'max_score' |
|
66 | end |
|
68 | end |
|
67 |
|
69 | ||
|
68 | end |
|
70 | end |
|
69 |
|
71 | ||
|
70 | def score |
|
72 | def score |
|
71 | if params[:commit] == 'download csv' |
|
73 | if params[:commit] == 'download csv' |
|
72 | @problems = Problem.all |
|
74 | @problems = Problem.all |
|
73 | else |
|
75 | else |
|
74 | @problems = Problem.available_problems |
|
76 | @problems = Problem.available_problems |
|
75 | end |
|
77 | end |
|
76 | @users = User.includes(:contests, :contest_stat).where(enabled: true) |
|
78 | @users = User.includes(:contests, :contest_stat).where(enabled: true) |
|
77 | @scorearray = Array.new |
|
79 | @scorearray = Array.new |
|
78 | @users.each do |u| |
|
80 | @users.each do |u| |
|
79 | ustat = Array.new |
|
81 | ustat = Array.new |
|
80 | ustat[0] = u |
|
82 | ustat[0] = u |
|
81 | @problems.each do |p| |
|
83 | @problems.each do |p| |
|
82 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
84 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
83 | if (sub!=nil) and (sub.points!=nil) and p and p.full_score |
|
85 | if (sub!=nil) and (sub.points!=nil) and p and p.full_score |
|
84 | ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)] |
|
86 | ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)] |
|
85 | else |
|
87 | else |
|
86 | ustat << [0,false] |
|
88 | ustat << [0,false] |
|
87 | end |
|
89 | end |
|
88 | end |
|
90 | end |
|
89 | @scorearray << ustat |
|
91 | @scorearray << ustat |
|
90 | end |
|
92 | end |
|
91 | if params[:commit] == 'download csv' then |
|
93 | if params[:commit] == 'download csv' then |
|
92 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
94 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
93 | send_data csv, filename: 'last_score.csv' |
|
95 | send_data csv, filename: 'last_score.csv' |
|
94 | else |
|
96 | else |
|
95 | render template: 'user_admin/user_stat' |
|
97 | render template: 'user_admin/user_stat' |
|
96 | end |
|
98 | end |
|
97 |
|
99 | ||
|
98 | end |
|
100 | end |
|
99 |
|
101 | ||
|
100 | def login_stat |
|
102 | def login_stat |
|
101 | @logins = Array.new |
|
103 | @logins = Array.new |
|
102 |
|
104 |
@@ -1,58 +1,60 | |||||
|
1 | class TagsController < ApplicationController |
|
1 | class TagsController < ApplicationController |
|
2 | before_action :set_tag, only: [:show, :edit, :update, :destroy] |
|
2 | before_action :set_tag, only: [:show, :edit, :update, :destroy] |
|
3 |
|
3 | ||
|
4 | # GET /tags |
|
4 | # GET /tags |
|
5 | def index |
|
5 | def index |
|
6 | @tags = Tag.all |
|
6 | @tags = Tag.all |
|
7 | end |
|
7 | end |
|
8 |
|
8 | ||
|
9 | # GET /tags/1 |
|
9 | # GET /tags/1 |
|
10 | def show |
|
10 | def show |
|
11 | end |
|
11 | end |
|
12 |
|
12 | ||
|
13 | # GET /tags/new |
|
13 | # GET /tags/new |
|
14 | def new |
|
14 | def new |
|
15 | @tag = Tag.new |
|
15 | @tag = Tag.new |
|
16 | end |
|
16 | end |
|
17 |
|
17 | ||
|
18 | # GET /tags/1/edit |
|
18 | # GET /tags/1/edit |
|
19 | def edit |
|
19 | def edit |
|
20 | end |
|
20 | end |
|
21 |
|
21 | ||
|
22 | # POST /tags |
|
22 | # POST /tags |
|
23 | def create |
|
23 | def create |
|
24 | @tag = Tag.new(tag_params) |
|
24 | @tag = Tag.new(tag_params) |
|
25 |
|
25 | ||
|
26 | if @tag.save |
|
26 | if @tag.save |
|
27 | redirect_to @tag, notice: 'Tag was successfully created.' |
|
27 | redirect_to @tag, notice: 'Tag was successfully created.' |
|
28 | else |
|
28 | else |
|
29 | render :new |
|
29 | render :new |
|
30 | end |
|
30 | end |
|
31 | end |
|
31 | end |
|
32 |
|
32 | ||
|
33 | # PATCH/PUT /tags/1 |
|
33 | # PATCH/PUT /tags/1 |
|
34 | def update |
|
34 | def update |
|
35 | if @tag.update(tag_params) |
|
35 | if @tag.update(tag_params) |
|
36 | redirect_to @tag, notice: 'Tag was successfully updated.' |
|
36 | redirect_to @tag, notice: 'Tag was successfully updated.' |
|
37 | else |
|
37 | else |
|
38 | render :edit |
|
38 | render :edit |
|
39 | end |
|
39 | end |
|
40 | end |
|
40 | end |
|
41 |
|
41 | ||
|
42 | # DELETE /tags/1 |
|
42 | # DELETE /tags/1 |
|
43 | def destroy |
|
43 | def destroy |
|
|
44 | + #remove any association | ||
|
|
45 | + ProblemTag.where(tag_id: @tag.id).destroy_all | ||
|
44 | @tag.destroy |
|
46 | @tag.destroy |
|
45 | redirect_to tags_url, notice: 'Tag was successfully destroyed.' |
|
47 | redirect_to tags_url, notice: 'Tag was successfully destroyed.' |
|
46 | end |
|
48 | end |
|
47 |
|
49 | ||
|
48 | private |
|
50 | private |
|
49 | # Use callbacks to share common setup or constraints between actions. |
|
51 | # Use callbacks to share common setup or constraints between actions. |
|
50 | def set_tag |
|
52 | def set_tag |
|
51 | @tag = Tag.find(params[:id]) |
|
53 | @tag = Tag.find(params[:id]) |
|
52 | end |
|
54 | end |
|
53 |
|
55 | ||
|
54 | # Only allow a trusted parameter "white list" through. |
|
56 | # Only allow a trusted parameter "white list" through. |
|
55 | def tag_params |
|
57 | def tag_params |
|
56 | params.require(:tag).permit(:name, :description, :public) |
|
58 | params.require(:tag).permit(:name, :description, :public) |
|
57 | end |
|
59 | end |
|
58 | end |
|
60 | end |
@@ -40,111 +40,114 | |||||
|
40 |
|
40 | ||
|
41 | if (user!=nil) and (session[:admin]) |
|
41 | if (user!=nil) and (session[:admin]) |
|
42 | # admin menu |
|
42 | # admin menu |
|
43 | menu_items << "<b>Administrative task:</b> " |
|
43 | menu_items << "<b>Administrative task:</b> " |
|
44 | append_to menu_items, '[Announcements]', 'announcements', 'index' |
|
44 | append_to menu_items, '[Announcements]', 'announcements', 'index' |
|
45 | append_to menu_items, '[Msg console]', 'messages', 'console' |
|
45 | append_to menu_items, '[Msg console]', 'messages', 'console' |
|
46 | append_to menu_items, '[Problems]', 'problems', 'index' |
|
46 | append_to menu_items, '[Problems]', 'problems', 'index' |
|
47 | append_to menu_items, '[Users]', 'user_admin', 'index' |
|
47 | append_to menu_items, '[Users]', 'user_admin', 'index' |
|
48 | append_to menu_items, '[Results]', 'user_admin', 'user_stat' |
|
48 | append_to menu_items, '[Results]', 'user_admin', 'user_stat' |
|
49 | append_to menu_items, '[Report]', 'report', 'multiple_login' |
|
49 | append_to menu_items, '[Report]', 'report', 'multiple_login' |
|
50 | append_to menu_items, '[Graders]', 'graders', 'list' |
|
50 | append_to menu_items, '[Graders]', 'graders', 'list' |
|
51 | append_to menu_items, '[Contests]', 'contest_management', 'index' |
|
51 | append_to menu_items, '[Contests]', 'contest_management', 'index' |
|
52 | append_to menu_items, '[Sites]', 'sites', 'index' |
|
52 | append_to menu_items, '[Sites]', 'sites', 'index' |
|
53 | append_to menu_items, '[System config]', 'configurations', 'index' |
|
53 | append_to menu_items, '[System config]', 'configurations', 'index' |
|
54 | menu_items << "<br/>" |
|
54 | menu_items << "<br/>" |
|
55 | end |
|
55 | end |
|
56 |
|
56 | ||
|
57 | # main page |
|
57 | # main page |
|
58 | append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list' |
|
58 | append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list' |
|
59 | append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list' |
|
59 | append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list' |
|
60 |
|
60 | ||
|
61 | if (user!=nil) and (GraderConfiguration.show_tasks_to?(user)) |
|
61 | if (user!=nil) and (GraderConfiguration.show_tasks_to?(user)) |
|
62 | append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list' |
|
62 | append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list' |
|
63 | append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission' |
|
63 | append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission' |
|
64 | append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index' |
|
64 | append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index' |
|
65 | end |
|
65 | end |
|
66 |
|
66 | ||
|
67 | if GraderConfiguration['right.user_hall_of_fame'] |
|
67 | if GraderConfiguration['right.user_hall_of_fame'] |
|
68 | append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof' |
|
68 | append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof' |
|
69 | end |
|
69 | end |
|
70 | append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help' |
|
70 | append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help' |
|
71 |
|
71 | ||
|
72 | if GraderConfiguration['system.user_setting_enabled'] |
|
72 | if GraderConfiguration['system.user_setting_enabled'] |
|
73 | append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index' |
|
73 | append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index' |
|
74 | end |
|
74 | end |
|
75 | append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login' |
|
75 | append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login' |
|
76 |
|
76 | ||
|
77 | menu_items.html_safe |
|
77 | menu_items.html_safe |
|
78 | end |
|
78 | end |
|
79 |
|
79 | ||
|
80 | def append_to(option,label, controller, action) |
|
80 | def append_to(option,label, controller, action) |
|
81 | option << ' ' if option!='' |
|
81 | option << ' ' if option!='' |
|
82 | option << link_to_unless_current(label, |
|
82 | option << link_to_unless_current(label, |
|
83 | :controller => controller, |
|
83 | :controller => controller, |
|
84 | :action => action) |
|
84 | :action => action) |
|
85 | end |
|
85 | end |
|
86 |
|
86 | ||
|
87 | def format_short_time(time) |
|
87 | def format_short_time(time) |
|
88 |
- now = Time.now |
|
88 | + now = Time.zone.now |
|
89 | st = '' |
|
89 | st = '' |
|
90 | - if (time.yday != now.yday) or |
|
90 | + if (time.yday != now.yday) or (time.year != now.year) |
|
91 | - (time.year != now.year) |
|
91 | + st = time.strftime("%d/%m/%y ") |
|
92 | - st = time.strftime("%x ") |
|
||
|
93 | end |
|
92 | end |
|
94 | st + time.strftime("%X") |
|
93 | st + time.strftime("%X") |
|
95 | end |
|
94 | end |
|
96 |
|
95 | ||
|
97 | def format_short_duration(duration) |
|
96 | def format_short_duration(duration) |
|
98 | return '' if duration==nil |
|
97 | return '' if duration==nil |
|
99 | d = duration.to_f |
|
98 | d = duration.to_f |
|
100 | return Time.at(d).gmtime.strftime("%X") |
|
99 | return Time.at(d).gmtime.strftime("%X") |
|
101 | end |
|
100 | end |
|
102 |
|
101 | ||
|
|
102 | + def format_full_time_ago(time) | ||
|
|
103 | + st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')' | ||
|
|
104 | + end | ||
|
|
105 | + | ||
|
103 | def read_textfile(fname,max_size=2048) |
|
106 | def read_textfile(fname,max_size=2048) |
|
104 | begin |
|
107 | begin |
|
105 | File.open(fname).read(max_size) |
|
108 | File.open(fname).read(max_size) |
|
106 | rescue |
|
109 | rescue |
|
107 | nil |
|
110 | nil |
|
108 | end |
|
111 | end |
|
109 | end |
|
112 | end |
|
110 |
|
113 | ||
|
111 | def toggle_button(on,toggle_url,id, option={}) |
|
114 | def toggle_button(on,toggle_url,id, option={}) |
|
112 | btn_size = option[:size] || 'btn-xs' |
|
115 | btn_size = option[:size] || 'btn-xs' |
|
113 | link_to (on ? "Yes" : "No"), toggle_url, |
|
116 | link_to (on ? "Yes" : "No"), toggle_url, |
|
114 | {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle", |
|
117 | {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle", |
|
115 | id: id, |
|
118 | id: id, |
|
116 | data: {remote: true, method: 'get'}} |
|
119 | data: {remote: true, method: 'get'}} |
|
117 | end |
|
120 | end |
|
118 |
|
121 | ||
|
119 | def get_ace_mode(language) |
|
122 | def get_ace_mode(language) |
|
120 | # return ace mode string from Language |
|
123 | # return ace mode string from Language |
|
121 |
|
124 | ||
|
122 | case language.pretty_name |
|
125 | case language.pretty_name |
|
123 | when 'Pascal' |
|
126 | when 'Pascal' |
|
124 | 'ace/mode/pascal' |
|
127 | 'ace/mode/pascal' |
|
125 | when 'C++','C' |
|
128 | when 'C++','C' |
|
126 | 'ace/mode/c_cpp' |
|
129 | 'ace/mode/c_cpp' |
|
127 | when 'Ruby' |
|
130 | when 'Ruby' |
|
128 | 'ace/mode/ruby' |
|
131 | 'ace/mode/ruby' |
|
129 | when 'Python' |
|
132 | when 'Python' |
|
130 | 'ace/mode/python' |
|
133 | 'ace/mode/python' |
|
131 | when 'Java' |
|
134 | when 'Java' |
|
132 | 'ace/mode/java' |
|
135 | 'ace/mode/java' |
|
133 | else |
|
136 | else |
|
134 | 'ace/mode/c_cpp' |
|
137 | 'ace/mode/c_cpp' |
|
135 | end |
|
138 | end |
|
136 | end |
|
139 | end |
|
137 |
|
140 | ||
|
138 |
|
141 | ||
|
139 | def user_title_bar(user) |
|
142 | def user_title_bar(user) |
|
140 | header = '' |
|
143 | header = '' |
|
141 | time_left = '' |
|
144 | time_left = '' |
|
142 |
|
145 | ||
|
143 | # |
|
146 | # |
|
144 | # if the contest is over |
|
147 | # if the contest is over |
|
145 | if GraderConfiguration.time_limit_mode? |
|
148 | if GraderConfiguration.time_limit_mode? |
|
146 | if user.contest_finished? |
|
149 | if user.contest_finished? |
|
147 | header = <<CONTEST_OVER |
|
150 | header = <<CONTEST_OVER |
|
148 | <tr><td colspan="2" align="center"> |
|
151 | <tr><td colspan="2" align="center"> |
|
149 | <span class="contest-over-msg">THE CONTEST IS OVER</span> |
|
152 | <span class="contest-over-msg">THE CONTEST IS OVER</span> |
|
150 | </td></tr> |
|
153 | </td></tr> |
@@ -1,86 +1,86 | |||||
|
1 | class Submission < ActiveRecord::Base |
|
1 | class Submission < ActiveRecord::Base |
|
2 |
|
2 | ||
|
3 | belongs_to :language |
|
3 | belongs_to :language |
|
4 | belongs_to :problem |
|
4 | belongs_to :problem |
|
5 | belongs_to :user |
|
5 | belongs_to :user |
|
6 |
|
6 | ||
|
7 | before_validation :assign_problem |
|
7 | before_validation :assign_problem |
|
8 | before_validation :assign_language |
|
8 | before_validation :assign_language |
|
9 |
|
9 | ||
|
10 | validates_presence_of :source |
|
10 | validates_presence_of :source |
|
11 | validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long' |
|
11 | validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long' |
|
12 | validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short' |
|
12 | validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short' |
|
13 | validate :must_have_valid_problem |
|
13 | validate :must_have_valid_problem |
|
14 | validate :must_specify_language |
|
14 | validate :must_specify_language |
|
15 |
|
15 | ||
|
16 | has_one :task |
|
16 | has_one :task |
|
17 |
|
17 | ||
|
18 | before_save :assign_latest_number_if_new_recond |
|
18 | before_save :assign_latest_number_if_new_recond |
|
19 |
|
19 | ||
|
20 | def self.find_last_by_user_and_problem(user_id, problem_id) |
|
20 | def self.find_last_by_user_and_problem(user_id, problem_id) |
|
21 | where("user_id = ? AND problem_id = ?",user_id,problem_id).last |
|
21 | where("user_id = ? AND problem_id = ?",user_id,problem_id).last |
|
22 | end |
|
22 | end |
|
23 |
|
23 | ||
|
24 | def self.find_all_last_by_problem(problem_id) |
|
24 | def self.find_all_last_by_problem(problem_id) |
|
25 | # need to put in SQL command, maybe there's a better way |
|
25 | # need to put in SQL command, maybe there's a better way |
|
26 | Submission.includes(:user).find_by_sql("SELECT * FROM submissions " + |
|
26 | Submission.includes(:user).find_by_sql("SELECT * FROM submissions " + |
|
27 | "WHERE id = " + |
|
27 | "WHERE id = " + |
|
28 | "(SELECT MAX(id) FROM submissions AS subs " + |
|
28 | "(SELECT MAX(id) FROM submissions AS subs " + |
|
29 | "WHERE subs.user_id = submissions.user_id AND " + |
|
29 | "WHERE subs.user_id = submissions.user_id AND " + |
|
30 | "problem_id = " + problem_id.to_s + " " + |
|
30 | "problem_id = " + problem_id.to_s + " " + |
|
31 | "GROUP BY user_id) " + |
|
31 | "GROUP BY user_id) " + |
|
32 | "ORDER BY user_id") |
|
32 | "ORDER BY user_id") |
|
33 | end |
|
33 | end |
|
34 |
|
34 | ||
|
35 | def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id) |
|
35 | def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id) |
|
36 | records = Submission.where(problem_id: problem_id,user_id: user_id) |
|
36 | records = Submission.where(problem_id: problem_id,user_id: user_id) |
|
37 | - records = records.where('id >= ?',since_id) if since_id > 0 |
|
37 | + records = records.where('id >= ?',since_id) if since_id and since_id > 0 |
|
38 | - records = records.where('id <= ?',until_id) if until_id > 0 |
|
38 | + records = records.where('id <= ?',until_id) if until_id and until_id > 0 |
|
39 | records.all |
|
39 | records.all |
|
40 | end |
|
40 | end |
|
41 |
|
41 | ||
|
42 | def self.find_last_for_all_available_problems(user_id) |
|
42 | def self.find_last_for_all_available_problems(user_id) |
|
43 | submissions = Array.new |
|
43 | submissions = Array.new |
|
44 | problems = Problem.available_problems |
|
44 | problems = Problem.available_problems |
|
45 | problems.each do |problem| |
|
45 | problems.each do |problem| |
|
46 | sub = Submission.find_last_by_user_and_problem(user_id, problem.id) |
|
46 | sub = Submission.find_last_by_user_and_problem(user_id, problem.id) |
|
47 | submissions << sub if sub!=nil |
|
47 | submissions << sub if sub!=nil |
|
48 | end |
|
48 | end |
|
49 | submissions |
|
49 | submissions |
|
50 | end |
|
50 | end |
|
51 |
|
51 | ||
|
52 | def self.find_by_user_problem_number(user_id, problem_id, number) |
|
52 | def self.find_by_user_problem_number(user_id, problem_id, number) |
|
53 | where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first |
|
53 | where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first |
|
54 | end |
|
54 | end |
|
55 |
|
55 | ||
|
56 | def self.find_all_by_user_problem(user_id, problem_id) |
|
56 | def self.find_all_by_user_problem(user_id, problem_id) |
|
57 | where("user_id = ? AND problem_id = ?",user_id,problem_id) |
|
57 | where("user_id = ? AND problem_id = ?",user_id,problem_id) |
|
58 | end |
|
58 | end |
|
59 |
|
59 | ||
|
60 | def download_filename |
|
60 | def download_filename |
|
61 | if self.problem.output_only |
|
61 | if self.problem.output_only |
|
62 | return self.source_filename |
|
62 | return self.source_filename |
|
63 | else |
|
63 | else |
|
64 | timestamp = self.submitted_at.localtime.strftime("%H%M%S") |
|
64 | timestamp = self.submitted_at.localtime.strftime("%H%M%S") |
|
65 | return "#{self.problem.name}-#{timestamp}.#{self.language.ext}" |
|
65 | return "#{self.problem.name}-#{timestamp}.#{self.language.ext}" |
|
66 | end |
|
66 | end |
|
67 | end |
|
67 | end |
|
68 |
|
68 | ||
|
69 | protected |
|
69 | protected |
|
70 |
|
70 | ||
|
71 | def self.find_option_in_source(option, source) |
|
71 | def self.find_option_in_source(option, source) |
|
72 | if source==nil |
|
72 | if source==nil |
|
73 | return nil |
|
73 | return nil |
|
74 | end |
|
74 | end |
|
75 | i = 0 |
|
75 | i = 0 |
|
76 | source.each_line do |s| |
|
76 | source.each_line do |s| |
|
77 | if s =~ option |
|
77 | if s =~ option |
|
78 | words = s.split |
|
78 | words = s.split |
|
79 | return words[1] |
|
79 | return words[1] |
|
80 | end |
|
80 | end |
|
81 | i = i + 1 |
|
81 | i = i + 1 |
|
82 | if i==10 |
|
82 | if i==10 |
|
83 | return nil |
|
83 | return nil |
|
84 | end |
|
84 | end |
|
85 | end |
|
85 | end |
|
86 | return nil |
|
86 | return nil |
@@ -1,26 +1,28 | |||||
|
1 | - |
|
||
|
2 |
|
|
1 | - if submission.nil? |
|
3 | = "-" |
|
2 | = "-" |
|
4 | - else |
|
3 | - else |
|
|
4 | + %strong= "Submission ID:" | ||
|
|
5 | + = submission.id | ||
|
|
6 | + %br | ||
|
5 | - unless submission.graded_at |
|
7 | - unless submission.graded_at |
|
6 | - = t 'main.submitted_at' |
|
8 | + %strong= t 'main.submitted_at:' |
|
7 |
- = format_ |
|
9 | + = format_full_time_ago(submission.submitted_at.localtime) |
|
8 | - else |
|
10 | - else |
|
9 | - %strong= t 'main.graded_at' |
|
11 | + %strong= t 'main.graded_at:' |
|
10 |
- = |
|
12 | + = format_full_time_ago(submission.graded_at.localtime) |
|
11 | %br |
|
13 | %br |
|
12 | - if GraderConfiguration['ui.show_score'] |
|
14 | - if GraderConfiguration['ui.show_score'] |
|
13 | %strong=t 'main.score' |
|
15 | %strong=t 'main.score' |
|
14 | = "#{(submission.points*100/submission.problem.full_score).to_i} " |
|
16 | = "#{(submission.points*100/submission.problem.full_score).to_i} " |
|
15 | = " [" |
|
17 | = " [" |
|
16 | %tt |
|
18 | %tt |
|
17 | = submission.grader_comment |
|
19 | = submission.grader_comment |
|
18 | = "]" |
|
20 | = "]" |
|
19 | %br |
|
21 | %br |
|
20 |
|
|
22 | %strong View: |
|
21 |
|
|
23 | - if GraderConfiguration.show_grading_result |
|
22 |
|
|
24 | = link_to '[detailed result]', :action => 'result', :id => submission.id |
|
23 |
- |
|
25 | + = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'} if submission.graded_at |
|
24 | = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info' |
|
26 | = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info' |
|
25 | = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' |
|
27 | = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' |
|
26 |
|
28 |
@@ -1,94 +1,95 | |||||
|
1 | %header.navbar.navbar-default.navbar-fixed-top |
|
1 | %header.navbar.navbar-default.navbar-fixed-top |
|
2 | %nav |
|
2 | %nav |
|
3 | .container-fluid |
|
3 | .container-fluid |
|
4 | .navbar-header |
|
4 | .navbar-header |
|
5 | %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} } |
|
5 | %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} } |
|
6 | %span.sr-only Togggle Navigation |
|
6 | %span.sr-only Togggle Navigation |
|
7 | %span.icon-bar |
|
7 | %span.icon-bar |
|
8 | %span.icon-bar |
|
8 | %span.icon-bar |
|
9 | %span.icon-bar |
|
9 | %span.icon-bar |
|
10 | %a.navbar-brand{href: main_list_path} |
|
10 | %a.navbar-brand{href: main_list_path} |
|
11 | %span.glyphicon.glyphicon-home |
|
11 | %span.glyphicon.glyphicon-home |
|
12 | MAIN |
|
12 | MAIN |
|
13 | .collapse.navbar-collapse#navbar-collapse |
|
13 | .collapse.navbar-collapse#navbar-collapse |
|
14 | %ul.nav.navbar-nav |
|
14 | %ul.nav.navbar-nav |
|
15 | / submission |
|
15 | / submission |
|
16 | - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user)) |
|
16 | - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user)) |
|
17 | %li.dropdown |
|
17 | %li.dropdown |
|
18 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
18 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
19 | = "#{I18n.t 'menu.submissions'}" |
|
19 | = "#{I18n.t 'menu.submissions'}" |
|
20 | %span.caret |
|
20 | %span.caret |
|
21 | %ul.dropdown-menu |
|
21 | %ul.dropdown-menu |
|
22 | = add_menu("View", 'submissions', 'index') |
|
22 | = add_menu("View", 'submissions', 'index') |
|
23 | = add_menu("Self Test", 'test', 'index') |
|
23 | = add_menu("Self Test", 'test', 'index') |
|
24 | / hall of fame |
|
24 | / hall of fame |
|
25 | - if GraderConfiguration['right.user_hall_of_fame'] |
|
25 | - if GraderConfiguration['right.user_hall_of_fame'] |
|
26 | = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof') |
|
26 | = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof') |
|
27 | / display MODE button (with countdown in contest mode) |
|
27 | / display MODE button (with countdown in contest mode) |
|
28 | - if GraderConfiguration.analysis_mode? |
|
28 | - if GraderConfiguration.analysis_mode? |
|
29 | %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE" |
|
29 | %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE" |
|
30 | - elsif GraderConfiguration.time_limit_mode? |
|
30 | - elsif GraderConfiguration.time_limit_mode? |
|
31 | - if @current_user.contest_finished? |
|
31 | - if @current_user.contest_finished? |
|
32 | %div.navbar-btn.btn.btn-danger#countdown= "Contest is over" |
|
32 | %div.navbar-btn.btn.btn-danger#countdown= "Contest is over" |
|
33 | - elsif !@current_user.contest_started? |
|
33 | - elsif !@current_user.contest_started? |
|
34 | %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started') |
|
34 | %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started') |
|
35 | - else |
|
35 | - else |
|
36 | %div.navbar-btn.btn.btn-primary#countdown asdf |
|
36 | %div.navbar-btn.btn.btn-primary#countdown asdf |
|
37 | :javascript |
|
37 | :javascript |
|
38 | $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'}); |
|
38 | $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'}); |
|
39 | / admin section |
|
39 | / admin section |
|
40 | - if (@current_user!=nil) and (session[:admin]) |
|
40 | - if (@current_user!=nil) and (session[:admin]) |
|
41 | / management |
|
41 | / management |
|
42 | %li.dropdown |
|
42 | %li.dropdown |
|
43 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
43 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
44 | Manage |
|
44 | Manage |
|
45 | %span.caret |
|
45 | %span.caret |
|
46 | %ul.dropdown-menu |
|
46 | %ul.dropdown-menu |
|
47 | = add_menu( 'Announcements', 'announcements', 'index') |
|
47 | = add_menu( 'Announcements', 'announcements', 'index') |
|
48 | = add_menu( 'Problems', 'problems', 'index') |
|
48 | = add_menu( 'Problems', 'problems', 'index') |
|
|
49 | + = add_menu( 'Tags', 'tags', 'index') | ||
|
49 | = add_menu( 'Users', 'user_admin', 'index') |
|
50 | = add_menu( 'Users', 'user_admin', 'index') |
|
50 | = add_menu( 'User Groups', 'groups', 'index') |
|
51 | = add_menu( 'User Groups', 'groups', 'index') |
|
51 | = add_menu( 'Graders', 'graders', 'list') |
|
52 | = add_menu( 'Graders', 'graders', 'list') |
|
52 | = add_menu( 'Message ', 'messages', 'console') |
|
53 | = add_menu( 'Message ', 'messages', 'console') |
|
53 | %li.divider{role: 'separator'} |
|
54 | %li.divider{role: 'separator'} |
|
54 | = add_menu( 'System config', 'configurations', 'index') |
|
55 | = add_menu( 'System config', 'configurations', 'index') |
|
55 | %li.divider{role: 'separator'} |
|
56 | %li.divider{role: 'separator'} |
|
56 | = add_menu( 'Sites', 'sites', 'index') |
|
57 | = add_menu( 'Sites', 'sites', 'index') |
|
57 | = add_menu( 'Contests', 'contest_management', 'index') |
|
58 | = add_menu( 'Contests', 'contest_management', 'index') |
|
58 | / report |
|
59 | / report |
|
59 | %li.dropdown |
|
60 | %li.dropdown |
|
60 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
61 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
61 | Report |
|
62 | Report |
|
62 | %span.caret |
|
63 | %span.caret |
|
63 | %ul.dropdown-menu |
|
64 | %ul.dropdown-menu |
|
64 | = add_menu( 'Current Score', 'report', 'current_score') |
|
65 | = add_menu( 'Current Score', 'report', 'current_score') |
|
65 | = add_menu( 'Score Report', 'report', 'max_score') |
|
66 | = add_menu( 'Score Report', 'report', 'max_score') |
|
66 | = add_menu( 'Report', 'report', 'multiple_login') |
|
67 | = add_menu( 'Report', 'report', 'multiple_login') |
|
67 | - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0 |
|
68 | - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0 |
|
68 | =link_to "#{ungraded} backlogs!", |
|
69 | =link_to "#{ungraded} backlogs!", |
|
69 | grader_list_path, |
|
70 | grader_list_path, |
|
70 | class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission' |
|
71 | class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission' |
|
71 |
|
72 | ||
|
72 | %ul.nav.navbar-nav.navbar-right |
|
73 | %ul.nav.navbar-nav.navbar-right |
|
73 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help') |
|
74 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help') |
|
74 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}}) |
|
75 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}}) |
|
75 | - if GraderConfiguration['system.user_setting_enabled'] |
|
76 | - if GraderConfiguration['system.user_setting_enabled'] |
|
76 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}}) |
|
77 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}}) |
|
77 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}}) |
|
78 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}}) |
|
78 |
|
79 | ||
|
79 | / |
|
80 | / |
|
80 | - if (@current_user!=nil) and (session[:admin]) |
|
81 | - if (@current_user!=nil) and (session[:admin]) |
|
81 | %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar |
|
82 | %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar |
|
82 | .container-fluid |
|
83 | .container-fluid |
|
83 | .collapse.navbar-collapse |
|
84 | .collapse.navbar-collapse |
|
84 | %ul.nav.navbar-nav |
|
85 | %ul.nav.navbar-nav |
|
85 | = add_menu( '[Announcements]', 'announcements', 'index') |
|
86 | = add_menu( '[Announcements]', 'announcements', 'index') |
|
86 | = add_menu( '[Msg console]', 'messages', 'console') |
|
87 | = add_menu( '[Msg console]', 'messages', 'console') |
|
87 | = add_menu( '[Problems]', 'problems', 'index') |
|
88 | = add_menu( '[Problems]', 'problems', 'index') |
|
88 | = add_menu( '[Users]', 'user_admin', 'index') |
|
89 | = add_menu( '[Users]', 'user_admin', 'index') |
|
89 | = add_menu( '[Results]', 'user_admin', 'user_stat') |
|
90 | = add_menu( '[Results]', 'user_admin', 'user_stat') |
|
90 | = add_menu( '[Report]', 'report', 'multiple_login') |
|
91 | = add_menu( '[Report]', 'report', 'multiple_login') |
|
91 | = add_menu( '[Graders]', 'graders', 'list') |
|
92 | = add_menu( '[Graders]', 'graders', 'list') |
|
92 | = add_menu( '[Contests]', 'contest_management', 'index') |
|
93 | = add_menu( '[Contests]', 'contest_management', 'index') |
|
93 | = add_menu( '[Sites]', 'sites', 'index') |
|
94 | = add_menu( '[Sites]', 'sites', 'index') |
|
94 | = add_menu( '[System config]', 'configurations', 'index') |
|
95 | = add_menu( '[System config]', 'configurations', 'index') |
@@ -1,52 +1,55 | |||||
|
1 | = error_messages_for 'problem' |
|
1 | = error_messages_for 'problem' |
|
2 | / [form:problem] |
|
2 | / [form:problem] |
|
3 | .form-group |
|
3 | .form-group |
|
4 | %label{:for => "problem_name"} Name |
|
4 | %label{:for => "problem_name"} Name |
|
5 | = text_field 'problem', 'name', class: 'form-control' |
|
5 | = text_field 'problem', 'name', class: 'form-control' |
|
6 | %small |
|
6 | %small |
|
7 | Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead. |
|
7 | Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead. |
|
8 | .form-group |
|
8 | .form-group |
|
9 | %label{:for => "problem_full_name"} Full name |
|
9 | %label{:for => "problem_full_name"} Full name |
|
10 | = text_field 'problem', 'full_name', class: 'form-control' |
|
10 | = text_field 'problem', 'full_name', class: 'form-control' |
|
11 | .form-group |
|
11 | .form-group |
|
12 | %label{:for => "problem_full_score"} Full score |
|
12 | %label{:for => "problem_full_score"} Full score |
|
13 | = text_field 'problem', 'full_score', class: 'form-control' |
|
13 | = text_field 'problem', 'full_score', class: 'form-control' |
|
14 | .form-group |
|
14 | .form-group |
|
|
15 | + %label{:for => "problem_full_score"} Tags | ||
|
|
16 | + = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'}) | ||
|
|
17 | + .form-group | ||
|
15 | %label{:for => "problem_date_added"} Date added |
|
18 | %label{:for => "problem_date_added"} Date added |
|
16 | = date_select 'problem', 'date_added', class: 'form-control' |
|
19 | = date_select 'problem', 'date_added', class: 'form-control' |
|
17 | - # TODO: these should be put in model Problem, but I can't think of |
|
20 | - # TODO: these should be put in model Problem, but I can't think of |
|
18 | - # nice default values for them. These values look fine only |
|
21 | - # nice default values for them. These values look fine only |
|
19 | - # in this case (of lazily adding new problems). |
|
22 | - # in this case (of lazily adding new problems). |
|
20 | - @problem.available = true if @problem!=nil and @problem.available==nil |
|
23 | - @problem.available = true if @problem!=nil and @problem.available==nil |
|
21 | - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil |
|
24 | - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil |
|
22 | - @problem.output_only = false if @problem!=nil and @problem.output_only==nil |
|
25 | - @problem.output_only = false if @problem!=nil and @problem.output_only==nil |
|
23 | .checkbox |
|
26 | .checkbox |
|
24 | %label{:for => "problem_available"} |
|
27 | %label{:for => "problem_available"} |
|
25 | = check_box :problem, :available |
|
28 | = check_box :problem, :available |
|
26 | Available? |
|
29 | Available? |
|
27 | .checkbox |
|
30 | .checkbox |
|
28 | %label{:for => "problem_test_allowed"} |
|
31 | %label{:for => "problem_test_allowed"} |
|
29 | = check_box :problem, :test_allowed |
|
32 | = check_box :problem, :test_allowed |
|
30 | Test allowed? |
|
33 | Test allowed? |
|
31 | .checkbox |
|
34 | .checkbox |
|
32 | %label{:for => "problem_output_only"} |
|
35 | %label{:for => "problem_output_only"} |
|
33 | = check_box :problem, :output_only |
|
36 | = check_box :problem, :output_only |
|
34 | Output only? |
|
37 | Output only? |
|
35 | = error_messages_for 'description' |
|
38 | = error_messages_for 'description' |
|
36 | .form-group |
|
39 | .form-group |
|
37 | %label{:for => "description_body"} Description |
|
40 | %label{:for => "description_body"} Description |
|
38 | %br/ |
|
41 | %br/ |
|
39 | = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' |
|
42 | = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' |
|
40 | .form-group |
|
43 | .form-group |
|
41 | %label{:for => "description_markdowned"} Markdowned? |
|
44 | %label{:for => "description_markdowned"} Markdowned? |
|
42 | = select "description", | |
|
45 | = select "description", | |
|
43 | "markdowned", | |
|
46 | "markdowned", | |
|
44 | [['True',true],['False',false]], | |
|
47 | [['True',true],['False',false]], | |
|
45 | {:selected => (@description) ? @description.markdowned : false } | |
|
48 | {:selected => (@description) ? @description.markdowned : false } | |
|
46 | .form-group |
|
49 | .form-group |
|
47 | %label{:for => "problem_url"} URL |
|
50 | %label{:for => "problem_url"} URL |
|
48 | %br/ |
|
51 | %br/ |
|
49 | = text_field 'problem', 'url',class: 'form-control' |
|
52 | = text_field 'problem', 'url',class: 'form-control' |
|
50 | %p |
|
53 | %p |
|
51 | Task PDF #{file_field_tag 'file'} |
|
54 | Task PDF #{file_field_tag 'file'} |
|
52 | / [eoform:problem] |
|
55 | / [eoform:problem] |
@@ -1,60 +1,65 | |||||
|
1 | - content_for :head do |
|
1 | - content_for :head do |
|
2 | = stylesheet_link_tag 'problems' |
|
2 | = stylesheet_link_tag 'problems' |
|
3 | %h1 Problems |
|
3 | %h1 Problems |
|
4 | %p |
|
4 | %p |
|
5 | = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm' |
|
5 | = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm' |
|
6 | = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm' |
|
6 | = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm' |
|
7 | = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm' |
|
7 | = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm' |
|
8 | = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm' |
|
8 | = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm' |
|
9 | = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm' |
|
9 | = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm' |
|
10 | .submitbox |
|
10 | .submitbox |
|
11 | = form_tag :action => 'quick_create' do |
|
11 | = form_tag :action => 'quick_create' do |
|
12 | %b Quick New: |
|
12 | %b Quick New: |
|
13 | %label{:for => "problem_name"} Name |
|
13 | %label{:for => "problem_name"} Name |
|
14 | = text_field 'problem', 'name' |
|
14 | = text_field 'problem', 'name' |
|
15 | | |
|
15 | | |
|
16 | %label{:for => "problem_full_name"} Full name |
|
16 | %label{:for => "problem_full_name"} Full name |
|
17 | = text_field 'problem', 'full_name' |
|
17 | = text_field 'problem', 'full_name' |
|
18 | = submit_tag "Create" |
|
18 | = submit_tag "Create" |
|
19 | %table.table.table-condense.table-hover |
|
19 | %table.table.table-condense.table-hover |
|
20 | %thead |
|
20 | %thead |
|
21 | %th Name |
|
21 | %th Name |
|
22 | %th Full name |
|
22 | %th Full name |
|
23 | %th.text-right Full score |
|
23 | %th.text-right Full score |
|
|
24 | + %th Tags | ||
|
24 | %th |
|
25 | %th |
|
25 | Submit |
|
26 | Submit |
|
26 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?] |
|
27 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?] |
|
27 | %th Date added |
|
28 | %th Date added |
|
28 | %th.text-center |
|
29 | %th.text-center |
|
29 | Avail? |
|
30 | Avail? |
|
30 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?] |
|
31 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?] |
|
31 | %th.text-center |
|
32 | %th.text-center |
|
32 | View Data? |
|
33 | View Data? |
|
33 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?] |
|
34 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?] |
|
34 | %th.text-center |
|
35 | %th.text-center |
|
35 | Test? |
|
36 | Test? |
|
36 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?] |
|
37 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?] |
|
37 | - if GraderConfiguration.multicontests? |
|
38 | - if GraderConfiguration.multicontests? |
|
38 | %th Contests |
|
39 | %th Contests |
|
39 | - for problem in @problems |
|
40 | - for problem in @problems |
|
40 | %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"} |
|
41 | %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"} |
|
41 | - @problem=problem |
|
42 | - @problem=problem |
|
42 | %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1 |
|
43 | %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1 |
|
43 | %td |
|
44 | %td |
|
44 | = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1 |
|
45 | = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1 |
|
45 | = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem |
|
46 | = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem |
|
46 | %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1 |
|
47 | %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1 |
|
|
48 | + %td | ||
|
|
49 | + - problem.tags.each do |t| | ||
|
|
50 | + - #%button.btn.btn-default.btn-xs= t.name | ||
|
|
51 | + %span.label.label-default= t.name | ||
|
47 | %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary' |
|
52 | %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary' |
|
48 | %td= problem.date_added |
|
53 | %td= problem.date_added |
|
49 | %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}") |
|
54 | %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}") |
|
50 | %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}") |
|
55 | %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}") |
|
51 | %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}") |
|
56 | %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}") |
|
52 | - if GraderConfiguration.multicontests? |
|
57 | - if GraderConfiguration.multicontests? |
|
53 | %td |
|
58 | %td |
|
54 | = problem.contests.collect { |c| c.name }.join(', ') |
|
59 | = problem.contests.collect { |c| c.name }.join(', ') |
|
55 | %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block' |
|
60 | %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block' |
|
56 | %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
61 | %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
57 | %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
62 | %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
58 | %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block' |
|
63 | %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block' |
|
59 | %br/ |
|
64 | %br/ |
|
60 | = link_to '[New problem]', :action => 'new' |
|
65 | = link_to '[New problem]', :action => 'new' |
@@ -4,104 +4,115 | |||||
|
4 |
|
4 | ||
|
5 | :javascript |
|
5 | :javascript |
|
6 | $(document).ready( function() { |
|
6 | $(document).ready( function() { |
|
7 | function shiftclick(start,stop,value) { |
|
7 | function shiftclick(start,stop,value) { |
|
8 | $('tr input').each( function(id,input) { |
|
8 | $('tr input').each( function(id,input) { |
|
9 | var $input=$(input); |
|
9 | var $input=$(input); |
|
10 | var iid=parseInt($input.attr('id').split('-')[2]); |
|
10 | var iid=parseInt($input.attr('id').split('-')[2]); |
|
11 | if(iid>=start&&iid<=stop){ |
|
11 | if(iid>=start&&iid<=stop){ |
|
12 | $input.prop('checked',value) |
|
12 | $input.prop('checked',value) |
|
13 | } |
|
13 | } |
|
14 | }); |
|
14 | }); |
|
15 | } |
|
15 | } |
|
16 |
|
16 | ||
|
17 | $('tr input').click( function(e) { |
|
17 | $('tr input').click( function(e) { |
|
18 | if (e.shiftKey) { |
|
18 | if (e.shiftKey) { |
|
19 | stop = parseInt($(this).attr('id').split('-')[2]); |
|
19 | stop = parseInt($(this).attr('id').split('-')[2]); |
|
20 | var orig_stop = stop |
|
20 | var orig_stop = stop |
|
21 | if (typeof start !== 'undefined') { |
|
21 | if (typeof start !== 'undefined') { |
|
22 | if (start > stop) { |
|
22 | if (start > stop) { |
|
23 | var tmp = start; |
|
23 | var tmp = start; |
|
24 | start = stop; |
|
24 | start = stop; |
|
25 | stop = tmp; |
|
25 | stop = tmp; |
|
26 | } |
|
26 | } |
|
27 | shiftclick(start,stop,$(this).is(':checked') ) |
|
27 | shiftclick(start,stop,$(this).is(':checked') ) |
|
28 | } |
|
28 | } |
|
29 | start = orig_stop |
|
29 | start = orig_stop |
|
30 | } else { |
|
30 | } else { |
|
31 | start = parseInt($(this).attr('id').split('-')[2]); |
|
31 | start = parseInt($(this).attr('id').split('-')[2]); |
|
32 | } |
|
32 | } |
|
33 | }); |
|
33 | }); |
|
34 | }); |
|
34 | }); |
|
35 |
|
35 | ||
|
36 |
|
36 | ||
|
37 | %h1 Manage problems |
|
37 | %h1 Manage problems |
|
38 |
|
38 | ||
|
39 | %p= link_to '[Back to problem list]', problems_path |
|
39 | %p= link_to '[Back to problem list]', problems_path |
|
40 |
|
40 | ||
|
41 | = form_tag :action=>'do_manage' do |
|
41 | = form_tag :action=>'do_manage' do |
|
42 | .panel.panel-primary |
|
42 | .panel.panel-primary |
|
43 | .panel-heading |
|
43 | .panel-heading |
|
44 | Action |
|
44 | Action |
|
45 | .panel-body |
|
45 | .panel-body |
|
46 | .submit-box |
|
46 | .submit-box |
|
47 | What do you want to do to the selected problem? |
|
47 | What do you want to do to the selected problem? |
|
48 | %br/ |
|
48 | %br/ |
|
49 | (You can shift-click to select a range of problems) |
|
49 | (You can shift-click to select a range of problems) |
|
50 | %ul.form-inline |
|
50 | %ul.form-inline |
|
51 | %li |
|
51 | %li |
|
52 |
- Change |
|
52 | + Change "Date added" to |
|
53 | .input-group.date |
|
53 | .input-group.date |
|
54 | = text_field_tag :date_added, class: 'form-control' |
|
54 | = text_field_tag :date_added, class: 'form-control' |
|
55 | %span.input-group-addon |
|
55 | %span.input-group-addon |
|
56 | %span.glyphicon.glyphicon-calendar |
|
56 | %span.glyphicon.glyphicon-calendar |
|
57 | -# = select_date Date.current, :prefix => 'date_added' |
|
57 | -# = select_date Date.current, :prefix => 'date_added' |
|
58 | |
|
58 | |
|
59 | = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-primary btn-sm' |
|
59 | = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-primary btn-sm' |
|
60 | %li |
|
60 | %li |
|
61 |
- Set |
|
61 | + Set "Available" to |
|
62 | = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-primary btn-sm' |
|
62 | = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-primary btn-sm' |
|
63 | = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-primary btn-sm' |
|
63 | = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-primary btn-sm' |
|
64 |
|
64 | ||
|
65 | - if GraderConfiguration.multicontests? |
|
65 | - if GraderConfiguration.multicontests? |
|
66 | %li |
|
66 | %li |
|
67 | - Add to |
|
67 | + Add selected problems to contest |
|
68 | = select("contest","id",Contest.all.collect {|c| [c.title, c.id]}) |
|
68 | = select("contest","id",Contest.all.collect {|c| [c.title, c.id]}) |
|
69 | = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-primary btn-sm' |
|
69 | = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-primary btn-sm' |
|
70 | %li |
|
70 | %li |
|
71 | - Add problems to group |
|
71 | + Add selected problems to user group |
|
72 | = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2' |
|
72 | = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2' |
|
73 |
- = submit_tag 'Add', name: 'add_group', class: 'btn btn- |
|
73 | + = submit_tag 'Add', name: 'add_group', class: 'btn btn-primary' |
|
|
74 | + %li | ||
|
|
75 | + Add the following tags to the selected problems | ||
|
|
76 | + = select_tag "tag_ids", options_from_collection_for_select( Tag.all, 'id','name'), id: 'tags_name',class: 'select2', multiple: true, data: {placeholder: 'Select tags by clicking', width: "200px"} | ||
|
|
77 | + = submit_tag 'Add', name: 'add_tags', class: 'btn btn-primary' | ||
|
74 |
|
78 | ||
|
75 | - |
|
79 | + %table.table.table-hover.datatable |
|
76 | - %table.table.table-hover |
|
80 | + %thead |
|
77 | %tr{style: "text-align: left;"} |
|
81 | %tr{style: "text-align: left;"} |
|
78 | %th= check_box_tag 'select_all' |
|
82 | %th= check_box_tag 'select_all' |
|
79 | %th Name |
|
83 | %th Name |
|
80 | %th Full name |
|
84 | %th Full name |
|
|
85 | + %th Tags | ||
|
81 | %th Available |
|
86 | %th Available |
|
82 | %th Date added |
|
87 | %th Date added |
|
83 | - if GraderConfiguration.multicontests? |
|
88 | - if GraderConfiguration.multicontests? |
|
84 | %th Contests |
|
89 | %th Contests |
|
85 |
|
90 | ||
|
|
91 | + %tbody | ||
|
86 | - num = 0 |
|
92 | - num = 0 |
|
87 | - for problem in @problems |
|
93 | - for problem in @problems |
|
88 | - num += 1 |
|
94 | - num += 1 |
|
89 | %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"} |
|
95 | %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"} |
|
90 | %td= check_box_tag "prob-#{problem.id}-#{num}" |
|
96 | %td= check_box_tag "prob-#{problem.id}-#{num}" |
|
91 | %td= problem.name |
|
97 | %td= problem.name |
|
92 | %td= problem.full_name |
|
98 | %td= problem.full_name |
|
|
99 | + %td | ||
|
|
100 | + - problem.tags.each do |t| | ||
|
|
101 | + %span.label.label-default= t.name | ||
|
93 | %td= problem.available |
|
102 | %td= problem.available |
|
94 | %td= problem.date_added |
|
103 | %td= problem.date_added |
|
95 | - if GraderConfiguration.multicontests? |
|
104 | - if GraderConfiguration.multicontests? |
|
96 | %td |
|
105 | %td |
|
97 | - problem.contests.each do |contest| |
|
106 | - problem.contests.each do |contest| |
|
98 | = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])" |
|
107 | = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])" |
|
99 |
|
108 | ||
|
100 | :javascript |
|
109 | :javascript |
|
101 | $('.input-group.date').datetimepicker({ |
|
110 | $('.input-group.date').datetimepicker({ |
|
102 | format: 'DD/MMM/YYYY', |
|
111 | format: 'DD/MMM/YYYY', |
|
103 | showTodayButton: true, |
|
112 | showTodayButton: true, |
|
104 | widgetPositioning: {horizontal: 'auto', vertical: 'bottom'}, |
|
113 | widgetPositioning: {horizontal: 'auto', vertical: 'bottom'}, |
|
105 |
|
114 | ||
|
106 | }); |
|
115 | }); |
|
107 | - |
|
116 | + $('.datatable').DataTable({ |
|
|
117 | + paging: false | ||
|
|
118 | + }); |
@@ -1,57 +1,59 | |||||
|
1 | :css |
|
1 | :css |
|
2 | .fix-width { |
|
2 | .fix-width { |
|
3 | font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier" |
|
3 | font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier" |
|
4 | } |
|
4 | } |
|
5 |
|
5 | ||
|
6 | %h1 Problem stat: #{@problem.name} |
|
6 | %h1 Problem stat: #{@problem.name} |
|
7 | %h2 Overview |
|
7 | %h2 Overview |
|
8 |
|
8 | ||
|
9 |
|
9 | ||
|
10 | %table.info |
|
10 | %table.info |
|
11 | %thead |
|
11 | %thead |
|
12 | %tr.info-head |
|
12 | %tr.info-head |
|
13 | %th Stat |
|
13 | %th Stat |
|
14 | %th Value |
|
14 | %th Value |
|
15 | %tbody |
|
15 | %tbody |
|
16 | %tr{class: cycle('info-even','info-odd')} |
|
16 | %tr{class: cycle('info-even','info-odd')} |
|
17 | %td Submissions |
|
17 | %td Submissions |
|
18 | %td= @submissions.count |
|
18 | %td= @submissions.count |
|
19 | %tr{class: cycle('info-even','info-odd')} |
|
19 | %tr{class: cycle('info-even','info-odd')} |
|
20 | %td Solved/Attempted User |
|
20 | %td Solved/Attempted User |
|
21 | %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) |
|
21 | %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) |
|
22 |
|
22 | ||
|
23 | %h2 Submissions Count |
|
23 | %h2 Submissions Count |
|
24 | = render partial: 'application/bar_graph', locals: { histogram: @histogram } |
|
24 | = render partial: 'application/bar_graph', locals: { histogram: @histogram } |
|
25 |
|
25 | ||
|
26 | %h2 Submissions |
|
26 | %h2 Submissions |
|
27 | - if @submissions and @submissions.count > 0 |
|
27 | - if @submissions and @submissions.count > 0 |
|
28 | %table#main_table.table.table-condensed.table-striped |
|
28 | %table#main_table.table.table-condensed.table-striped |
|
29 | %thead |
|
29 | %thead |
|
30 | %tr |
|
30 | %tr |
|
31 | %th ID |
|
31 | %th ID |
|
32 | %th Login |
|
32 | %th Login |
|
33 | %th Name |
|
33 | %th Name |
|
34 | %th Submitted_at |
|
34 | %th Submitted_at |
|
|
35 | + %th language | ||
|
35 | %th Points |
|
36 | %th Points |
|
36 | %th comment |
|
37 | %th comment |
|
37 | %th IP |
|
38 | %th IP |
|
38 | %tbody |
|
39 | %tbody |
|
39 | - row_odd,curr = true,'' |
|
40 | - row_odd,curr = true,'' |
|
40 | - @submissions.each do |sub| |
|
41 | - @submissions.each do |sub| |
|
41 | - next unless sub.user |
|
42 | - next unless sub.user |
|
42 | - row_odd,curr = !row_odd, sub.user if curr != sub.user |
|
43 | - row_odd,curr = !row_odd, sub.user if curr != sub.user |
|
43 | %tr |
|
44 | %tr |
|
44 | %td= link_to sub.id, submission_path(sub) |
|
45 | %td= link_to sub.id, submission_path(sub) |
|
45 | %td= link_to sub.user.login, stat_user_path(sub.user) |
|
46 | %td= link_to sub.user.login, stat_user_path(sub.user) |
|
46 | %td= sub.user.full_name |
|
47 | %td= sub.user.full_name |
|
47 | %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago" |
|
48 | %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago" |
|
|
49 | + %td= sub.language.name | ||
|
48 | %td= sub.points |
|
50 | %td= sub.points |
|
49 | %td.fix-width= sub.grader_comment |
|
51 | %td.fix-width= sub.grader_comment |
|
50 | %td= sub.ip_address |
|
52 | %td= sub.ip_address |
|
51 | - else |
|
53 | - else |
|
52 | No submission |
|
54 | No submission |
|
53 |
|
55 | ||
|
54 | :javascript |
|
56 | :javascript |
|
55 | $("#main_table").DataTable({ |
|
57 | $("#main_table").DataTable({ |
|
56 | paging: false |
|
58 | paging: false |
|
57 | }); |
|
59 | }); |
@@ -1,34 +1,69 | |||||
|
1 | %table.table.sortable.table-striped.table-bordered.table-condensed |
|
1 | %table.table.sortable.table-striped.table-bordered.table-condensed |
|
2 | %thead |
|
2 | %thead |
|
3 | %tr |
|
3 | %tr |
|
4 | %th Login |
|
4 | %th Login |
|
5 | %th Name |
|
5 | %th Name |
|
6 | / %th Activated? |
|
6 | / %th Activated? |
|
7 | / %th Logged_in |
|
7 | / %th Logged_in |
|
8 | / %th Contest(s) |
|
8 | / %th Contest(s) |
|
9 | %th Remark |
|
9 | %th Remark |
|
10 | - @problems.each do |p| |
|
10 | - @problems.each do |p| |
|
11 | %th.text-right= p.name.gsub('_',' ') |
|
11 | %th.text-right= p.name.gsub('_',' ') |
|
12 | %th.text-right Total |
|
12 | %th.text-right Total |
|
13 | %th.text-right Passed |
|
13 | %th.text-right Passed |
|
14 | %tbody |
|
14 | %tbody |
|
|
15 | + - sum = Array.new(@scorearray[0].count,0) | ||
|
|
16 | + - nonzero = Array.new(@scorearray[0].count,0) | ||
|
|
17 | + - full = Array.new(@scorearray[0].count,0) | ||
|
15 | - @scorearray.each do |sc| |
|
18 | - @scorearray.each do |sc| |
|
16 | %tr |
|
19 | %tr |
|
17 | - total,num_passed = 0,0 |
|
20 | - total,num_passed = 0,0 |
|
18 | - sc.each_index do |i| |
|
21 | - sc.each_index do |i| |
|
19 | - if i == 0 |
|
22 | - if i == 0 |
|
20 | %td= link_to sc[i].login, stat_user_path(sc[i]) |
|
23 | %td= link_to sc[i].login, stat_user_path(sc[i]) |
|
21 | %td= sc[i].full_name |
|
24 | %td= sc[i].full_name |
|
22 | / %td= sc[i].activated |
|
25 | / %td= sc[i].activated |
|
23 | / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no' |
|
26 | / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no' |
|
24 | / %td= sc[i].contests.collect {|c| c.name}.join(', ') |
|
27 | / %td= sc[i].contests.collect {|c| c.name}.join(', ') |
|
25 | %td= sc[i].remark |
|
28 | %td= sc[i].remark |
|
26 | - else |
|
29 | - else |
|
27 | %td.text-right= sc[i][0] |
|
30 | %td.text-right= sc[i][0] |
|
28 | - total += sc[i][0] |
|
31 | - total += sc[i][0] |
|
29 | - num_passed += 1 if sc[i][1] |
|
32 | - num_passed += 1 if sc[i][1] |
|
|
33 | + - sum[i] += sc[i][0] | ||
|
|
34 | + - nonzero[i] += 1 if sc[i][0] > 0 | ||
|
|
35 | + - full[i] += 1 if sc[i][1] | ||
|
30 | %td.text-right= total |
|
36 | %td.text-right= total |
|
31 | %td.text-right= num_passed |
|
37 | %td.text-right= num_passed |
|
|
38 | + %tfoot | ||
|
|
39 | + %tr | ||
|
|
40 | + %td Summation | ||
|
|
41 | + %td | ||
|
|
42 | + %td | ||
|
|
43 | + - sum.each.with_index do |s,i| | ||
|
|
44 | + - next if i == 0 | ||
|
|
45 | + %td.text-right= number_with_delimiter(s) | ||
|
|
46 | + %td | ||
|
|
47 | + %td | ||
|
|
48 | + %tr | ||
|
|
49 | + %td partial solver | ||
|
|
50 | + %td | ||
|
|
51 | + %td | ||
|
|
52 | + - nonzero.each.with_index do |s,i| | ||
|
|
53 | + - next if i == 0 | ||
|
|
54 | + %td.text-right= number_with_delimiter(s) | ||
|
|
55 | + %td | ||
|
|
56 | + %td | ||
|
|
57 | + %tr | ||
|
|
58 | + %td Full solver | ||
|
|
59 | + %td | ||
|
|
60 | + %td | ||
|
|
61 | + - full.each.with_index do |s,i| | ||
|
|
62 | + - next if i == 0 | ||
|
|
63 | + %td.text-right= number_with_delimiter(s) | ||
|
|
64 | + %td | ||
|
|
65 | + %td | ||
|
|
66 | + | ||
|
32 |
|
67 | ||
|
33 | :javascript |
|
68 | :javascript |
|
34 | $.bootstrapSortable(true,'reversed') |
|
69 | $.bootstrapSortable(true,'reversed') |
@@ -1,49 +1,49 | |||||
|
1 | %h1 Maximum score |
|
1 | %h1 Maximum score |
|
2 |
|
2 | ||
|
3 | = form_tag report_show_max_score_path |
|
3 | = form_tag report_show_max_score_path |
|
4 | .row |
|
4 | .row |
|
5 | .col-md-4 |
|
5 | .col-md-4 |
|
6 | .panel.panel-primary |
|
6 | .panel.panel-primary |
|
7 | .panel-heading |
|
7 | .panel-heading |
|
8 | Problems |
|
8 | Problems |
|
9 | .panel-body |
|
9 | .panel-body |
|
10 | %p |
|
10 | %p |
|
11 | Select problem(s) that we wish to know the score. |
|
11 | Select problem(s) that we wish to know the score. |
|
12 | = label_tag :problem_id, "Problems" |
|
12 | = label_tag :problem_id, "Problems" |
|
13 | = select_tag 'problem_id[]', |
|
13 | = select_tag 'problem_id[]', |
|
14 | options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]), |
|
14 | options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]), |
|
15 | { class: 'select2 form-control', multiple: "true" } |
|
15 | { class: 'select2 form-control', multiple: "true" } |
|
16 | .col-md-4 |
|
16 | .col-md-4 |
|
17 | .panel.panel-primary |
|
17 | .panel.panel-primary |
|
18 | .panel-heading |
|
18 | .panel-heading |
|
19 | Submission range |
|
19 | Submission range |
|
20 | .panel-body |
|
20 | .panel-body |
|
21 | %p |
|
21 | %p |
|
22 | Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively. |
|
22 | Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively. |
|
23 | .form-group |
|
23 | .form-group |
|
24 | = label_tag :from, "Min" |
|
24 | = label_tag :from, "Min" |
|
25 | = text_field_tag 'from_id', @since_id, class: "form-control" |
|
25 | = text_field_tag 'from_id', @since_id, class: "form-control" |
|
26 | .form-group |
|
26 | .form-group |
|
27 | = label_tag :from, "Max" |
|
27 | = label_tag :from, "Max" |
|
28 | = text_field_tag 'to_id', @until_id, class: "form-control" |
|
28 | = text_field_tag 'to_id', @until_id, class: "form-control" |
|
29 | .col-md-4 |
|
29 | .col-md-4 |
|
30 | .panel.panel-primary |
|
30 | .panel.panel-primary |
|
31 | .panel-heading |
|
31 | .panel-heading |
|
32 | Users |
|
32 | Users |
|
33 | .panel-body |
|
33 | .panel-body |
|
34 | .radio |
|
34 | .radio |
|
35 | %label |
|
35 | %label |
|
36 |
- = radio_button_tag 'users', 'all', |
|
36 | + = radio_button_tag 'users', 'all', (params[:users] == "all") |
|
37 | All users |
|
37 | All users |
|
38 | .radio |
|
38 | .radio |
|
39 | %label |
|
39 | %label |
|
40 | - = radio_button_tag 'users', 'enabled' |
|
40 | + = radio_button_tag 'users', 'enabled', (params[:users] == "enabled") |
|
41 | Only enabled users |
|
41 | Only enabled users |
|
42 | .row |
|
42 | .row |
|
43 | .col-md-12 |
|
43 | .col-md-12 |
|
44 | = button_tag 'Show', class: "btn btn-primary btn-large", value: "show" |
|
44 | = button_tag 'Show', class: "btn btn-primary btn-large", value: "show" |
|
45 | = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download" |
|
45 | = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download" |
|
46 |
|
46 | ||
|
47 | - if @scorearray |
|
47 | - if @scorearray |
|
48 | %h2 Result |
|
48 | %h2 Result |
|
49 | =render "score_table" |
|
49 | =render "score_table" |
@@ -1,35 +1,36 | |||||
|
1 | %h1 Editing site |
|
1 | %h1 Editing site |
|
2 | = error_messages_for :site |
|
2 | = error_messages_for :site |
|
3 | = form_for(@site) do |f| |
|
3 | = form_for(@site) do |f| |
|
4 | .row |
|
4 | .row |
|
5 | .col-md-4 |
|
5 | .col-md-4 |
|
6 | .form-group.field |
|
6 | .form-group.field |
|
7 | = f.label :name, "Name" |
|
7 | = f.label :name, "Name" |
|
8 | = f.text_field :name, class: 'form-control' |
|
8 | = f.text_field :name, class: 'form-control' |
|
9 | .form-group.field |
|
9 | .form-group.field |
|
10 | = f.label :password, "Password" |
|
10 | = f.label :password, "Password" |
|
11 | = f.text_field :password, class: 'form-control' |
|
11 | = f.text_field :password, class: 'form-control' |
|
12 | .form-group.field |
|
12 | .form-group.field |
|
13 | = f.label :started, "Started" |
|
13 | = f.label :started, "Started" |
|
14 | = f.check_box :started, class: 'form-control' |
|
14 | = f.check_box :started, class: 'form-control' |
|
15 | .form-group.field |
|
15 | .form-group.field |
|
16 | = f.label :start_time, "Start time" |
|
16 | = f.label :start_time, "Start time" |
|
17 | -# = f.datetime_select :start_time, :include_blank => true |
|
17 | -# = f.datetime_select :start_time, :include_blank => true |
|
18 | .input-group.date |
|
18 | .input-group.date |
|
19 | = f.text_field :start_time, class:'form-control' , value: (@site.start_time ? @site.start_time.strftime('%d/%b/%Y %H:%M') : '') |
|
19 | = f.text_field :start_time, class:'form-control' , value: (@site.start_time ? @site.start_time.strftime('%d/%b/%Y %H:%M') : '') |
|
20 | %span.input-group-addon |
|
20 | %span.input-group-addon |
|
21 | %span.glyphicon.glyphicon-calendar |
|
21 | %span.glyphicon.glyphicon-calendar |
|
22 | .actions |
|
22 | .actions |
|
23 | = f.submit "Update", class: 'btn btn-primary' |
|
23 | = f.submit "Update", class: 'btn btn-primary' |
|
24 | .col-md-8 |
|
24 | .col-md-8 |
|
25 |
|
25 | ||
|
26 | = link_to 'Show', @site |
|
26 | = link_to 'Show', @site |
|
27 | | |
|
27 | | |
|
28 | = link_to 'Back', sites_path |
|
28 | = link_to 'Back', sites_path |
|
29 |
|
29 | ||
|
30 |
|
30 | ||
|
31 | :javascript |
|
31 | :javascript |
|
32 | $('.input-group.date').datetimepicker({ |
|
32 | $('.input-group.date').datetimepicker({ |
|
33 | format: 'DD/MMM/YYYY HH:mm', |
|
33 | format: 'DD/MMM/YYYY HH:mm', |
|
|
34 | + showTodayButton: true, | ||
|
34 | }); |
|
35 | }); |
|
35 |
|
36 |
@@ -1,82 +1,83 | |||||
|
1 | %h2 Live submit |
|
1 | %h2 Live submit |
|
2 | %br |
|
2 | %br |
|
3 |
|
3 | ||
|
4 | %textarea#text_sourcecode{style: "display:none"}~ @source |
|
4 | %textarea#text_sourcecode{style: "display:none"}~ @source |
|
5 | .container |
|
5 | .container |
|
6 | .row |
|
6 | .row |
|
7 | .col-md-12 |
|
7 | .col-md-12 |
|
8 | .alert.alert-info |
|
8 | .alert.alert-info |
|
9 | Write your code in the following box, choose language, and click submit button when finished |
|
9 | Write your code in the following box, choose language, and click submit button when finished |
|
10 | .row |
|
10 | .row |
|
11 | .col-md-8 |
|
11 | .col-md-8 |
|
12 | %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'} |
|
12 | %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'} |
|
13 | .col-md-4 |
|
13 | .col-md-4 |
|
14 | - # submission form |
|
14 | - # submission form |
|
15 | = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do |
|
15 | = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do |
|
16 |
|
16 | ||
|
17 | = hidden_field_tag 'editor_text', @source |
|
17 | = hidden_field_tag 'editor_text', @source |
|
18 | = hidden_field_tag 'submission[problem_id]', @problem.id |
|
18 | = hidden_field_tag 'submission[problem_id]', @problem.id |
|
19 | .form-group |
|
19 | .form-group |
|
20 | = label_tag "Task:" |
|
20 | = label_tag "Task:" |
|
21 | = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true |
|
21 | = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true |
|
22 |
|
22 | ||
|
23 | .form-group |
|
23 | .form-group |
|
24 | = label_tag 'Language' |
|
24 | = label_tag 'Language' |
|
25 | = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px" |
|
25 | = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px" |
|
26 | .form-group |
|
26 | .form-group |
|
27 | = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit', |
|
27 | = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit', |
|
28 | data: {confirm: "Submitting this source code for task #{@problem.long_name}?"} |
|
28 | data: {confirm: "Submitting this source code for task #{@problem.long_name}?"} |
|
29 | - # latest submission status |
|
29 | - # latest submission status |
|
30 | - .panel.panel-info |
|
30 | + .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"} |
|
31 | .panel-heading |
|
31 | .panel-heading |
|
32 | Latest Submission Status |
|
32 | Latest Submission Status |
|
33 | = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission |
|
33 | = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission |
|
34 | .panel-body |
|
34 | .panel-body |
|
|
35 | + %div#latest_status | ||
|
35 | - if @submission |
|
36 | - if @submission |
|
36 | = render :partial => 'submission_short', |
|
37 | = render :partial => 'submission_short', |
|
37 | :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id } |
|
38 | :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id } |
|
38 | .row |
|
39 | .row |
|
39 | .col-md-12 |
|
40 | .col-md-12 |
|
40 | %h2 Console |
|
41 | %h2 Console |
|
41 | %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20} |
|
42 | %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20} |
|
42 |
|
43 | ||
|
43 | :javascript |
|
44 | :javascript |
|
44 | $(document).ready(function() { |
|
45 | $(document).ready(function() { |
|
45 | e = ace.edit("editor") |
|
46 | e = ace.edit("editor") |
|
46 | e.setValue($("#text_sourcecode").val()); |
|
47 | e.setValue($("#text_sourcecode").val()); |
|
47 | e.gotoLine(1); |
|
48 | e.gotoLine(1); |
|
48 | $("#language_id").trigger('change'); |
|
49 | $("#language_id").trigger('change'); |
|
49 | brython(); |
|
50 | brython(); |
|
50 | }); |
|
51 | }); |
|
51 |
|
52 | ||
|
52 |
|
53 | ||
|
53 | %script#__main__{type:'text/python3'} |
|
54 | %script#__main__{type:'text/python3'} |
|
54 | :plain |
|
55 | :plain |
|
55 | import sys |
|
56 | import sys |
|
56 | import traceback |
|
57 | import traceback |
|
57 |
|
58 | ||
|
58 | from browser import document as doc |
|
59 | from browser import document as doc |
|
59 | from browser import window, alert, console |
|
60 | from browser import window, alert, console |
|
60 |
|
61 | ||
|
61 | _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands |
|
62 | _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands |
|
62 | for supporting Python development. See www.python.org for more information.""" |
|
63 | for supporting Python development. See www.python.org for more information.""" |
|
63 |
|
64 | ||
|
64 | _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
65 | _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
65 | All Rights Reserved. |
|
66 | All Rights Reserved. |
|
66 |
|
67 | ||
|
67 | Copyright (c) 2001-2013 Python Software Foundation. |
|
68 | Copyright (c) 2001-2013 Python Software Foundation. |
|
68 | All Rights Reserved. |
|
69 | All Rights Reserved. |
|
69 |
|
70 | ||
|
70 | Copyright (c) 2000 BeOpen.com. |
|
71 | Copyright (c) 2000 BeOpen.com. |
|
71 | All Rights Reserved. |
|
72 | All Rights Reserved. |
|
72 |
|
73 | ||
|
73 | Copyright (c) 1995-2001 Corporation for National Research Initiatives. |
|
74 | Copyright (c) 1995-2001 Corporation for National Research Initiatives. |
|
74 | All Rights Reserved. |
|
75 | All Rights Reserved. |
|
75 |
|
76 | ||
|
76 | Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. |
|
77 | Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. |
|
77 | All Rights Reserved.""" |
|
78 | All Rights Reserved.""" |
|
78 |
|
79 | ||
|
79 | _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
80 | _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
80 | All rights reserved. |
|
81 | All rights reserved. |
|
81 |
|
82 | ||
|
82 | Redistribution and use in source and binary forms, with or without |
|
83 | Redistribution and use in source and binary forms, with or without |
@@ -1,2 +1,2 | |||||
|
1 | :plain |
|
1 | :plain |
|
2 | - $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}") |
|
2 | + $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name, problem_id: @problem.id}})}") |
@@ -20,53 +20,53 | |||||
|
20 |
|
20 | ||
|
21 | # Only load the plugins named here, in the order given (default is alphabetical). |
|
21 | # Only load the plugins named here, in the order given (default is alphabetical). |
|
22 | # :all can be used as a placeholder for all plugins not explicitly named. |
|
22 | # :all can be used as a placeholder for all plugins not explicitly named. |
|
23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] |
|
23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] |
|
24 |
|
24 | ||
|
25 | # Activate observers that should always be running. |
|
25 | # Activate observers that should always be running. |
|
26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer |
|
26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer |
|
27 |
|
27 | ||
|
28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
|
28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
|
29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. |
|
29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. |
|
30 | config.time_zone = 'UTC' |
|
30 | config.time_zone = 'UTC' |
|
31 |
|
31 | ||
|
32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
|
32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
|
33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
|
33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
|
34 | config.i18n.default_locale = :en |
|
34 | config.i18n.default_locale = :en |
|
35 |
|
35 | ||
|
36 | # Configure the default encoding used in templates for Ruby 1.9. |
|
36 | # Configure the default encoding used in templates for Ruby 1.9. |
|
37 | config.encoding = "utf-8" |
|
37 | config.encoding = "utf-8" |
|
38 |
|
38 | ||
|
39 | # Configure sensitive parameters which will be filtered from the log file. |
|
39 | # Configure sensitive parameters which will be filtered from the log file. |
|
40 | config.filter_parameters += [:password] |
|
40 | config.filter_parameters += [:password] |
|
41 |
|
41 | ||
|
42 | # Enable escaping HTML in JSON. |
|
42 | # Enable escaping HTML in JSON. |
|
43 | config.active_support.escape_html_entities_in_json = true |
|
43 | config.active_support.escape_html_entities_in_json = true |
|
44 |
|
44 | ||
|
45 | # Use SQL instead of Active Record's schema dumper when creating the database. |
|
45 | # Use SQL instead of Active Record's schema dumper when creating the database. |
|
46 | # This is necessary if your schema can't be completely dumped by the schema dumper, |
|
46 | # This is necessary if your schema can't be completely dumped by the schema dumper, |
|
47 | # like if you have constraints or database-specific column types |
|
47 | # like if you have constraints or database-specific column types |
|
48 | # config.active_record.schema_format = :sql |
|
48 | # config.active_record.schema_format = :sql |
|
49 |
|
49 | ||
|
50 | # Enable the asset pipeline |
|
50 | # Enable the asset pipeline |
|
51 | config.assets.enabled = true |
|
51 | config.assets.enabled = true |
|
52 |
|
52 | ||
|
53 | # Version of your assets, change this if you want to expire all your assets |
|
53 | # Version of your assets, change this if you want to expire all your assets |
|
54 | config.assets.version = '1.0' |
|
54 | config.assets.version = '1.0' |
|
55 |
|
55 | ||
|
56 | # ---------------- IMPORTANT ---------------------- |
|
56 | # ---------------- IMPORTANT ---------------------- |
|
57 | # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader" |
|
57 | # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader" |
|
58 | # moreover, using the following line instead also known to works |
|
58 | # moreover, using the following line instead also known to works |
|
59 | #config.action_controller.relative_url_root = '/grader' |
|
59 | #config.action_controller.relative_url_root = '/grader' |
|
60 |
|
60 | ||
|
61 | #font path |
|
61 | #font path |
|
62 | config.assets.paths << "#{Rails}/vendor/assets/fonts" |
|
62 | config.assets.paths << "#{Rails}/vendor/assets/fonts" |
|
63 |
|
63 | ||
|
64 | config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
64 | config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
65 | config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
65 | config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
66 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
66 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
67 | login main messages problems report site sites sources tasks |
|
67 | login main messages problems report site sites sources tasks |
|
68 | - test user_admin users ).each do |controller| |
|
68 | + test user_admin users testcases).each do |controller| |
|
69 | config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
69 | config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
70 | end |
|
70 | end |
|
71 | end |
|
71 | end |
|
72 | end |
|
72 | end |
@@ -1,23 +1,23 | |||||
|
1 | # Be sure to restart your server when you modify this file. |
|
1 | # Be sure to restart your server when you modify this file. |
|
2 |
|
2 | ||
|
3 | # Version of your assets, change this if you want to expire all your assets. |
|
3 | # Version of your assets, change this if you want to expire all your assets. |
|
4 | Rails.application.config.assets.version = '1.0' |
|
4 | Rails.application.config.assets.version = '1.0' |
|
5 |
|
5 | ||
|
6 | # Add additional assets to the asset load path. |
|
6 | # Add additional assets to the asset load path. |
|
7 | # Rails.application.config.assets.paths << Emoji.images_path |
|
7 | # Rails.application.config.assets.paths << Emoji.images_path |
|
8 | # Add Yarn node_modules folder to the asset load path. |
|
8 | # Add Yarn node_modules folder to the asset load path. |
|
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') |
|
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') |
|
10 | Rails.application.config.assets.paths << Rails.root.join('vendor/assets/fonts') |
|
10 | Rails.application.config.assets.paths << Rails.root.join('vendor/assets/fonts') |
|
11 |
|
11 | ||
|
12 | # Precompile additional assets. |
|
12 | # Precompile additional assets. |
|
13 | # application.js, application.css, and all non-JS/CSS in the app/assets |
|
13 | # application.js, application.css, and all non-JS/CSS in the app/assets |
|
14 | # folder are already added. |
|
14 | # folder are already added. |
|
15 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) |
|
15 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) |
|
16 |
|
16 | ||
|
17 | Rails.application.config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
17 | Rails.application.config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
18 | Rails.application.config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
18 | Rails.application.config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
19 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
19 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
20 | login main messages problems report site sites sources tasks groups |
|
20 | login main messages problems report site sites sources tasks groups |
|
21 | - test user_admin users tags).each do |controller| |
|
21 | + test user_admin users tags testcases).each do |controller| |
|
22 | Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
22 | Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
23 | end |
|
23 | end |
@@ -1,73 +1,74 | |||||
|
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 | + #call the import problem script | ||
|
36 | def self.call_import_problem(problem_name, |
|
37 | def self.call_import_problem(problem_name, |
|
37 | problem_dir, |
|
38 | problem_dir, |
|
38 | time_limit=1, |
|
39 | time_limit=1, |
|
39 | memory_limit=32, |
|
40 | memory_limit=32, |
|
40 | checker_name='text') |
|
41 | checker_name='text') |
|
41 | if GraderScript.grader_control_enabled? |
|
42 | if GraderScript.grader_control_enabled? |
|
42 | cur_dir = `pwd`.chomp |
|
43 | cur_dir = `pwd`.chomp |
|
43 | Dir.chdir(GRADER_ROOT_DIR) |
|
44 | Dir.chdir(GRADER_ROOT_DIR) |
|
44 |
|
45 | ||
|
45 | script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem") |
|
46 | script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem") |
|
46 | cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" + |
|
47 | cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" + |
|
47 | " -t #{time_limit} -m #{memory_limit}" |
|
48 | " -t #{time_limit} -m #{memory_limit}" |
|
48 |
|
49 | ||
|
49 | output = `#{cmd}` |
|
50 | output = `#{cmd}` |
|
50 |
|
51 | ||
|
51 | Dir.chdir(cur_dir) |
|
52 | Dir.chdir(cur_dir) |
|
52 |
|
53 | ||
|
53 | return "import CMD: #{cmd}\n" + output |
|
54 | return "import CMD: #{cmd}\n" + output |
|
54 | end |
|
55 | end |
|
55 | return '' |
|
56 | return '' |
|
56 | end |
|
57 | end |
|
57 |
|
58 | ||
|
58 | def self.call_import_testcase(problem_name) |
|
59 | def self.call_import_testcase(problem_name) |
|
59 | if GraderScript.grader_control_enabled? |
|
60 | if GraderScript.grader_control_enabled? |
|
60 | cur_dir = `pwd`.chomp |
|
61 | cur_dir = `pwd`.chomp |
|
61 | Dir.chdir(GRADER_ROOT_DIR) |
|
62 | Dir.chdir(GRADER_ROOT_DIR) |
|
62 |
|
63 | ||
|
63 | script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase") |
|
64 | script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase") |
|
64 | cmd = "#{script_name} #{problem_name}" |
|
65 | cmd = "#{script_name} #{problem_name}" |
|
65 |
|
66 | ||
|
66 | output = `#{cmd}` |
|
67 | output = `#{cmd}` |
|
67 |
|
68 | ||
|
68 | Dir.chdir(cur_dir) |
|
69 | Dir.chdir(cur_dir) |
|
69 | return "Testcase import result:\n" + output |
|
70 | return "Testcase import result:\n" + output |
|
70 | end |
|
71 | end |
|
71 | end |
|
72 | end |
|
72 |
|
73 | ||
|
73 | end |
|
74 | end |
@@ -1,102 +1,104 | |||||
|
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 | + #Create or update problem according to the parameter | ||
|
11 |
def import_from_file(tempfile, |
|
12 | def import_from_file(tempfile, |
|
12 |
time_limit, |
|
13 | time_limit, |
|
13 | memory_limit, |
|
14 | memory_limit, |
|
14 | checker_name='text', |
|
15 | checker_name='text', |
|
15 | import_to_db=false) |
|
16 | import_to_db=false) |
|
16 |
|
17 | ||
|
17 | dirname = extract(tempfile) |
|
18 | dirname = extract(tempfile) |
|
18 | return false if not dirname |
|
19 | return false if not dirname |
|
19 | if not import_to_db |
|
20 | if not import_to_db |
|
20 | @log_msg = GraderScript.call_import_problem(@problem.name, |
|
21 | @log_msg = GraderScript.call_import_problem(@problem.name, |
|
21 | dirname, |
|
22 | dirname, |
|
22 | time_limit, |
|
23 | time_limit, |
|
23 | memory_limit, |
|
24 | memory_limit, |
|
24 | checker_name) |
|
25 | checker_name) |
|
25 | else |
|
26 | else |
|
26 | # Import test data to test pairs. |
|
27 | # Import test data to test pairs. |
|
27 |
|
28 | ||
|
28 | @problem.test_pairs.clear |
|
29 | @problem.test_pairs.clear |
|
29 | if import_test_pairs(dirname) |
|
30 | if import_test_pairs(dirname) |
|
30 | test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}" |
|
31 | test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}" |
|
31 | @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)" |
|
32 | @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)" |
|
32 | else |
|
33 | else |
|
33 | @log_msg = "Importing test pair failed. (0 test pairs imported)" |
|
34 | @log_msg = "Importing test pair failed. (0 test pairs imported)" |
|
34 | end |
|
35 | end |
|
35 | end |
|
36 | end |
|
36 |
|
37 | ||
|
37 | @log_msg << import_problem_description(dirname) |
|
38 | @log_msg << import_problem_description(dirname) |
|
38 | @log_msg << import_problem_pdf(dirname) |
|
39 | @log_msg << import_problem_pdf(dirname) |
|
39 | @log_msg << import_full_score(dirname) |
|
40 | @log_msg << import_full_score(dirname) |
|
40 |
|
41 | ||
|
41 | #import test data |
|
42 | #import test data |
|
42 | @log_msg << GraderScript.call_import_testcase(@problem.name) |
|
43 | @log_msg << GraderScript.call_import_testcase(@problem.name) |
|
43 |
|
44 | ||
|
44 | return true |
|
45 | return true |
|
45 | end |
|
46 | end |
|
46 |
|
47 | ||
|
47 | protected |
|
48 | protected |
|
48 |
|
49 | ||
|
49 | def self.long_ext(filename) |
|
50 | def self.long_ext(filename) |
|
50 | i = filename.index('.') |
|
51 | i = filename.index('.') |
|
51 | len = filename.length |
|
52 | len = filename.length |
|
52 | return filename.slice(i..len) |
|
53 | return filename.slice(i..len) |
|
53 | end |
|
54 | end |
|
54 |
|
55 | ||
|
|
56 | + # extract an archive file located at +tempfile+ to the +raw_dir+ | ||
|
55 | def extract(tempfile) |
|
57 | def extract(tempfile) |
|
56 | testdata_filename = save_testdata_file(tempfile) |
|
58 | testdata_filename = save_testdata_file(tempfile) |
|
57 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
59 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
58 |
|
60 | ||
|
59 | extract_dir = File.join(GraderScript.raw_dir, @problem.name) |
|
61 | extract_dir = File.join(GraderScript.raw_dir, @problem.name) |
|
60 | if File.exists? extract_dir |
|
62 | if File.exists? extract_dir |
|
61 | backup_count = 0 |
|
63 | backup_count = 0 |
|
62 | begin |
|
64 | begin |
|
63 | backup_count += 1 |
|
65 | backup_count += 1 |
|
64 | backup_dirname = "#{extract_dir}.backup.#{backup_count}" |
|
66 | backup_dirname = "#{extract_dir}.backup.#{backup_count}" |
|
65 | end while File.exists? backup_dirname |
|
67 | end while File.exists? backup_dirname |
|
66 | File.rename(extract_dir, backup_dirname) |
|
68 | File.rename(extract_dir, backup_dirname) |
|
67 | end |
|
69 | end |
|
68 | Dir.mkdir extract_dir |
|
70 | Dir.mkdir extract_dir |
|
69 |
|
71 | ||
|
70 | if ext=='.tar.gz' or ext=='.tgz' |
|
72 | if ext=='.tar.gz' or ext=='.tgz' |
|
71 | cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}" |
|
73 | cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}" |
|
72 | elsif ext=='.tar' |
|
74 | elsif ext=='.tar' |
|
73 | cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}" |
|
75 | cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}" |
|
74 | elsif ext=='.zip' |
|
76 | elsif ext=='.zip' |
|
75 | cmd = "unzip -o #{testdata_filename} -d #{extract_dir}" |
|
77 | cmd = "unzip -o #{testdata_filename} -d #{extract_dir}" |
|
76 | else |
|
78 | else |
|
77 | return nil |
|
79 | return nil |
|
78 | end |
|
80 | end |
|
79 |
|
81 | ||
|
80 | system(cmd) |
|
82 | system(cmd) |
|
81 |
|
83 | ||
|
82 | files = Dir["#{extract_dir}/**/*1*.in"] |
|
84 | files = Dir["#{extract_dir}/**/*1*.in"] |
|
83 | return nil if files.length==0 |
|
85 | return nil if files.length==0 |
|
84 |
|
86 | ||
|
85 | File.delete(testdata_filename) |
|
87 | File.delete(testdata_filename) |
|
86 |
|
88 | ||
|
87 | return File.dirname(files[0]) |
|
89 | return File.dirname(files[0]) |
|
88 | end |
|
90 | end |
|
89 |
|
91 | ||
|
90 | def save_testdata_file(tempfile) |
|
92 | def save_testdata_file(tempfile) |
|
91 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
93 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
92 | testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}") |
|
94 | testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}") |
|
93 |
|
95 | ||
|
94 | return nil if tempfile=="" |
|
96 | return nil if tempfile=="" |
|
95 |
|
97 | ||
|
96 | if tempfile.instance_of?(Tempfile) |
|
98 | if tempfile.instance_of?(Tempfile) |
|
97 | tempfile.close |
|
99 | tempfile.close |
|
98 | FileUtils.move(tempfile.path,testdata_filename) |
|
100 | FileUtils.move(tempfile.path,testdata_filename) |
|
99 | else |
|
101 | else |
|
100 | File.open(testdata_filename, "wb") do |f| |
|
102 | File.open(testdata_filename, "wb") do |f| |
|
101 | f.write(tempfile.read) |
|
103 | f.write(tempfile.read) |
|
102 | end |
|
104 | end |
You need to be logged in to leave comments.
Login now