Description:
saves submitted output, checks time outs; updated submission front-end flows
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r383:31aa87b1b1e9 - - 8 files changed: 116 inserted, 20 deleted

@@ -0,0 +1,5
1 + class AddOutputToSubmissions < ActiveRecord::Migration
2 + def change
3 + add_column :submissions, :output, :text
4 + end
5 + end
@@ -1,37 +1,77
1 var TOIContest = {
1 var TOIContest = {
2 NO_TIMEOUT: -1,
2 NO_TIMEOUT: -1,
3 + SUBMISSION_TIMEOUT: 300,
3
4
4 timeOuts: {},
5 timeOuts: {},
6 + timeStarted: 0,
5
7
6 problemSelectClick: function() {
8 problemSelectClick: function() {
7 $$(".submission-submit-divs").each(function(item) {
9 $$(".submission-submit-divs").each(function(item) {
8 item.hide();
10 item.hide();
9 });
11 });
10 var problem_id = $('submission_problem_id').value;
12 var problem_id = $('submission_problem_id').value;
11 if ( problem_id < 0 ) {
13 if ( problem_id < 0 ) {
12 return;
14 return;
13 }
15 }
14 $("submission_submit_div_" + problem_id + "_id").show();
16 $("submission_submit_div_" + problem_id + "_id").show();
15 },
17 },
16
18
17 - confirmDownload: function() {
19 + confirmDownload: function(pid) {
18 - return confirm("แน่ใจ?");
20 + result = confirm("คุณแน่ใจที่จะส่งข้อนี้หรือไม่?\nเมื่อคุณดาวน์โหลดข้อมูลชุดทดสอบแล้ว คุณจะต้องส่งข้อมูลส่งออกและโปรแกรมภายในเวลา 5 นาที");
21 + if ( result ) {
22 + if ( TOIContest.timeOuts[ pid ] == TOIContest.NO_TIMEOUT ) {
23 + TOIContest.refreshTimeOuts();
24 +
25 + TOIContest.timeOuts[ pid ] = TOIContest.SUBMISSION_TIMEOUT;
26 +
27 + TOIContest.refreshTimeOutMessages();
28 + }
29 + }
30 + return result;
19 },
31 },
20
32
21 refreshTimeOutMessages: function() {
33 refreshTimeOutMessages: function() {
22 for ( var pid in TOIContest.timeOuts ) {
34 for ( var pid in TOIContest.timeOuts ) {
23 var timeOut = TOIContest.timeOuts[ pid ];
35 var timeOut = TOIContest.timeOuts[ pid ];
24 if ( timeOut != TOIContest.NO_TIMEOUT ) {
36 if ( timeOut != TOIContest.NO_TIMEOUT ) {
25 if ( timeOut > 0 ) {
37 if ( timeOut > 0 ) {
26 var minLeft = parseInt(timeOut / 60);
38 var minLeft = parseInt(timeOut / 60);
27 var secLeft = parseInt(timeOut % 60);
39 var secLeft = parseInt(timeOut % 60);
28 $('submission_time_left_' + pid + '_id').innerHTML = '| <b>เหลือเวลาอีก ' + minLeft + ':' + secLeft + ' นาที</b>';
40 $('submission_time_left_' + pid + '_id').innerHTML = '| <b>เหลือเวลาอีก ' + minLeft + ':' + secLeft + ' นาที</b>';
41 + $('submission_form_'+ pid + '_id').show();
29 } else {
42 } else {
30 $('submission_time_left_' + pid + '_id').innerHTML = '| <b>หมดเวลาส่ง</a>';
43 $('submission_time_left_' + pid + '_id').innerHTML = '| <b>หมดเวลาส่ง</a>';
31 $('submission_form_'+ pid + '_id').hide();
44 $('submission_form_'+ pid + '_id').hide();
32 }
45 }
46 + } else {
47 + $('submission_form_'+ pid + '_id').hide();
33 }
48 }
34 }
49 }
35 - }
50 + },
51 +
52 + refreshTimeOuts: function() {
53 + if ( TOIContest.timeStarted == 0 ) {
54 + TOIContest.timeStarted = (new Date()).getTime();
55 + }
56 +
57 + var timeElapsed = ((new Date()).getTime() - TOIContest.timeStarted)/1000;
58 + for ( var pid in TOIContest.timeOuts ) {
59 + var timeOut = TOIContest.timeOuts[ pid ];
60 + if ( timeOut > timeElapsed ) {
61 + TOIContest.timeOuts[ pid ] -= timeElapsed;
62 + } else if ( timeOut > 0 ) {
63 + TOIContest.timeOuts[ pid ] = 0;
64 + }
65 + }
66 + },
67 +
68 + registerRefreshEvent: function() {
69 + TOIContest.timeStarted = (new Date()).getTime();
70 + setTimeout(function () {
71 + TOIContest.refreshTimeOuts();
72 + TOIContest.refreshTimeOutMessages();
73 + TOIContest.registerRefreshEvent();
74 + }, 1000);
75 + },
36 };
76 };
37
77
@@ -1,6 +1,7
1 + # coding: utf-8
1 class MainController < ApplicationController
2 class MainController < ApplicationController
2
3
3 before_filter :authenticate, :except => [:index, :login]
4 before_filter :authenticate, :except => [:index, :login]
4 before_filter :check_viewability, :except => [:index, :login]
5 before_filter :check_viewability, :except => [:index, :login]
5
6
6 append_before_filter :confirm_and_update_start_time,
7 append_before_filter :confirm_and_update_start_time,
@@ -45,30 +46,56
45
46
46 @announcements = Announcement.find_for_frontpage
47 @announcements = Announcement.find_for_frontpage
47 render :action => 'login', :layout => 'empty'
48 render :action => 'login', :layout => 'empty'
48 end
49 end
49
50
50 def list
51 def list
52 + if session[:current_problem_id]
53 + @current_problem = Problem.find(session[:current_problem_id])
54 + session[:current_problem_id] = nil
55 + end
51 prepare_list_information
56 prepare_list_information
52 end
57 end
53
58
54 def help
59 def help
55 @user = User.find(session[:user_id])
60 @user = User.find(session[:user_id])
56 end
61 end
57
62
58 def submit
63 def submit
59 user = User.find(session[:user_id])
64 user = User.find(session[:user_id])
60
65
61 @submission = Submission.new
66 @submission = Submission.new
62 - @submission.problem_id = params[:submission][:problem_id]
67 + @current_problem = Problem.find(params[:submission][:problem_id])
68 +
69 + if !@current_problem
70 + flash[:notice] = 'Error: คุณยังไม่ได้ระบุข้อที่ต้องการส่ง'
71 + redirect_to :action => 'list'
72 + end
73 +
74 + assignment = user.get_test_pair_assignment_for(@current_problem)
75 + if !assignment
76 + flash[:notice] = 'Error: คุณยังไม่ได้ดาวน์โหลดข้อมูลทดสอบ'
77 + prepare_list_information
78 + render :action => 'list' and return
79 + end
80 + if assignment.expired?
81 + flash[:notice] = 'Error: หมดเวลาส่งสำหรับข้อนี้'
82 + prepare_list_information
83 + render :action => 'list' and return
84 + end
85 +
86 + @submission.problem = @current_problem
63 @submission.user = user
87 @submission.user = user
64 @submission.language_id = 0
88 @submission.language_id = 0
65 if (params['file']) and (params['file']!='')
89 if (params['file']) and (params['file']!='')
66 @submission.source = params['file'].read
90 @submission.source = params['file'].read
67 @submission.source_filename = params['file'].original_filename
91 @submission.source_filename = params['file'].original_filename
68 end
92 end
93 + if (params['output_file']) and (params['output_file']!='')
94 + @submission.output = params['output_file'].read
95 + end
69 @submission.submitted_at = Time.new.gmtime
96 @submission.submitted_at = Time.new.gmtime
70
97
71 if GraderConfiguration.time_limit_mode? and user.contest_finished?
98 if GraderConfiguration.time_limit_mode? and user.contest_finished?
72 @submission.errors.add(:base,"The contest is over.")
99 @submission.errors.add(:base,"The contest is over.")
73 prepare_list_information
100 prepare_list_information
74 render :action => 'list' and return
101 render :action => 'list' and return
@@ -77,17 +104,23
77 if @submission.valid?
104 if @submission.valid?
78 if @submission.save == false
105 if @submission.save == false
79 flash[:notice] = 'Error saving your submission'
106 flash[:notice] = 'Error saving your submission'
80 elsif Task.create(:submission_id => @submission.id,
107 elsif Task.create(:submission_id => @submission.id,
81 :status => Task::STATUS_INQUEUE) == false
108 :status => Task::STATUS_INQUEUE) == false
82 flash[:notice] = 'Error adding your submission to task queue'
109 flash[:notice] = 'Error adding your submission to task queue'
110 + else
111 + flash[:notice] = 'จัดเก็บคำตอบและโปรแกรมที่คุณส่งเรียบร้อย'
83 end
112 end
84 else
113 else
85 prepare_list_information
114 prepare_list_information
86 render :action => 'list' and return
115 render :action => 'list' and return
87 end
116 end
117 +
118 + if @current_problem
119 + session[:current_problem_id] = @current_problem.id
120 + end
88 redirect_to :action => 'list'
121 redirect_to :action => 'list'
89 end
122 end
90
123
91 def source
124 def source
92 submission = Submission.find(params[:id])
125 submission = Submission.find(params[:id])
93 if ((submission.user_id == session[:user_id]) and
126 if ((submission.user_id == session[:user_id]) and
@@ -222,21 +255,17
222 flash[:notice] = 'Error: problem is not available'
255 flash[:notice] = 'Error: problem is not available'
223 redirect_to :action => 'list'
256 redirect_to :action => 'list'
224 else
257 else
225 test_pair = TestPair.get_for(problem, true)
258 test_pair = TestPair.get_for(problem, true)
226
259
227 user = User.find(session[:user_id])
260 user = User.find(session[:user_id])
228 - assignent = user.get_test_pair_assignment_for(problem)
261 + assignment = user.get_test_pair_assignment_for(problem)
229
262
230 - if !assignent
263 + if !assignment
231 - assignent = TestPairAssignment.new
264 + assignment = TestPairAssignment.create_for(user, problem, test_pair)
232 - assignent.user = user
265 + assignment.save
233 - assignent.problem = problem
234 - assignent.test_pair = test_pair
235 - assignent.submitted = false
236 - assignent.save
237 end
266 end
238
267
239 send_data(test_pair.input,
268 send_data(test_pair.input,
240 {:filename => problem.name + '-input.txt',
269 {:filename => problem.name + '-input.txt',
241 :type => 'text/plain'})
270 :type => 'text/plain'})
242 end
271 end
@@ -249,23 +278,24
249
278
250 if !problem or !problem.available
279 if !problem or !problem.available
251 flash[:notice] = 'Error: problem is not available'
280 flash[:notice] = 'Error: problem is not available'
252 redirect_to :action => 'list' and return
281 redirect_to :action => 'list' and return
253 end
282 end
254
283
284 + @current_problem = problem
255 test_pair = TestPair.get_for(problem, false)
285 test_pair = TestPair.get_for(problem, false)
256 if (params['output_file']) and (params['output_file']!='')
286 if (params['output_file']) and (params['output_file']!='')
257 output = params['output_file'].read
287 output = params['output_file'].read
258
288
259 - @current_problem = problem
260 @grading_result = grade(output, test_pair.solution)
289 @grading_result = grade(output, test_pair.solution)
261 prepare_list_information
290 prepare_list_information
262 render :action => 'list' and return
291 render :action => 'list' and return
263 else
292 else
264 flash[:notice] = 'Error: output file errors'
293 flash[:notice] = 'Error: output file errors'
265 - redirect_to :action => 'list'
294 + prepare_list_information
295 + render :action => 'list' and return
266 end
296 end
267 end
297 end
268
298
269 protected
299 protected
270
300
271 def grade(output, solution)
301 def grade(output, solution)
@@ -7,12 +7,17
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 +
14 + validates_presence_of :output
15 + validates_length_of :output, :maximum => 100_000, :allow_blank => true, :message => 'too long'
16 + validates_length_of :output, :minimum => 1, :allow_blank => true, :message => 'too short'
17 +
13 validate :must_have_valid_problem
18 validate :must_have_valid_problem
14 validate :must_specify_language
19 validate :must_specify_language
15
20
16 before_save :assign_latest_number_if_new_recond
21 before_save :assign_latest_number_if_new_recond
17
22
18 def self.find_last_by_user_and_problem(user_id, problem_id)
23 def self.find_last_by_user_and_problem(user_id, problem_id)
@@ -3,7 +3,17
3 belongs_to :test_pair
3 belongs_to :test_pair
4 belongs_to :user
4 belongs_to :user
5
5
6 def expired?
6 def expired?
7 return created_at + TEST_ASSIGNMENT_EXPIRATION_DURATION < Time.new.gmtime
7 return created_at + TEST_ASSIGNMENT_EXPIRATION_DURATION < Time.new.gmtime
8 end
8 end
9 +
10 + def self.create_for(user, problem, test_pair)
11 + assignment = TestPairAssignment.new
12 + assignment.user = user
13 + assignment.problem = problem
14 + assignment.test_pair = test_pair
15 + assignment.submitted = false
16 + assignment.save
17 + return assignment
18 + end
9 end
19 end
@@ -1,16 +1,17
1 + <% selected_problem_id = @current_problem ? @current_problem.id : -1 %>
1 <div class="submitbox">
2 <div class="submitbox">
2 <b>Problem:</b> <%= select 'submission', 'problem_id',
3 <b>Problem:</b> <%= select 'submission', 'problem_id',
3 [['กรุณาเลือกข้อที่ต้องการส่งหรือทดสอบ','-1']] +
4 [['กรุณาเลือกข้อที่ต้องการส่งหรือทดสอบ','-1']] +
4 @problems.collect {|p| [p.full_name, p.id]},
5 @problems.collect {|p| [p.full_name, p.id]},
5 - { :selected => '-1' },
6 + { :selected => selected_problem_id },
6 { :onchange => 'TOIContest.problemSelectClick()' } %>
7 { :onchange => 'TOIContest.problemSelectClick()' } %>
7 </div>
8 </div>
8
9
9 <% @problems.each do |problem| %>
10 <% @problems.each do |problem| %>
10 - <div class="submission-submit-divs" id="submission_submit_div_<%= problem.id %>_id" style="displanny: none;">
11 + <div class="submission-submit-divs" id="submission_submit_div_<%= problem.id %>_id" style="display: none;">
11 <div style="border: 1px solid #c0c0c0; padding: 5px; margin: 5px 0 5px 0;">
12 <div style="border: 1px solid #c0c0c0; padding: 5px; margin: 5px 0 5px 0;">
12 <b><%= problem.full_name %>: ข้อมูลสำหรับตรวจสอบ</b> (สามารถดาวน์โหลดและส่งกี่ครั้งก็ได้,ไม่มีคะแนน):
13 <b><%= problem.full_name %>: ข้อมูลสำหรับตรวจสอบ</b> (สามารถดาวน์โหลดและส่งกี่ครั้งก็ได้,ไม่มีคะแนน):
13 <%= link_to 'ดาวน์โหลด input', :action => 'verifying_testcase', :id => problem.id %>
14 <%= link_to 'ดาวน์โหลด input', :action => 'verifying_testcase', :id => problem.id %>
14 <% if @current_problem and @current_problem.id == problem.id %>
15 <% if @current_problem and @current_problem.id == problem.id %>
15 <% if @grading_result %>
16 <% if @grading_result %>
16 | <b>ผลการตรวจ:</b> <%= "#{@grading_result[:score]}/#{@grading_result[:full_score]} [#{@grading_result[:msg]}]" %>
17 | <b>ผลการตรวจ:</b> <%= "#{@grading_result[:score]}/#{@grading_result[:full_score]} [#{@grading_result[:msg]}]" %>
@@ -21,16 +22,17
21 <%= file_field_tag 'output_file' %>
22 <%= file_field_tag 'output_file' %>
22 <%= submit_tag 'Submit' %>
23 <%= submit_tag 'Submit' %>
23 <% end %>
24 <% end %>
24 </div>
25 </div>
25 <div style="border: 1px solid #c0c0c0; padding: 5px; margin: 5px 0 5px 0;">
26 <div style="border: 1px solid #c0c0c0; padding: 5px; margin: 5px 0 5px 0;">
26 <b><%= problem.full_name %>: ข้อมูลทดสอบจริง</b> (ส่งกี่ครั้งก็ได้ภายในเวลา 5 นาทีหลังดาวน์โหลด):
27 <b><%= problem.full_name %>: ข้อมูลทดสอบจริง</b> (ส่งกี่ครั้งก็ได้ภายในเวลา 5 นาทีหลังดาวน์โหลด):
27 - <%= link_to 'ดาวน์โหลด input และเริ่มจับเวลา', { :action => 'testcase', :id => problem.id}, { :onclick => 'return TOIContest.confirmDownload()' } %>
28 + <%= link_to 'ดาวน์โหลด input และเริ่มจับเวลา', { :action => 'testcase', :id => problem.id}, { :onclick => "return TOIContest.confirmDownload(#{problem.id})" } %>
28 <span id="submission_time_left_<%= problem.id %>_id"></span>
29 <span id="submission_time_left_<%= problem.id %>_id"></span>
29 - <%= form_tag({:controller => 'main', :action => 'submit', :id => problem.id}, {:method => 'post', :multipart => true, :id => "submission_form_#{problem.id}_id" }) do %>
30 + <%= form_tag({:controller => 'main', :action => 'submit'}, {:method => 'post', :multipart => true, :id => "submission_form_#{problem.id}_id" }) do %>
31 + <%= hidden_field_tag 'submission[problem_id]', problem.id %>
30 ข้อมูลส่งออก: <%= file_field_tag 'output_file' %>
32 ข้อมูลส่งออก: <%= file_field_tag 'output_file' %>
31 - โปรแกรมคำตอบ: <%= file_field_tag 'source_file' %>
33 + โปรแกรมคำตอบ: <%= file_field_tag 'file' %>
32 <%= submit_tag 'Submit' %>
34 <%= submit_tag 'Submit' %>
33 <% end %>
35 <% end %>
34 </div>
36 </div>
35 </div>
37 </div>
36 <% end %>
38 <% end %>
@@ -46,14 +46,17
46 %hr/
46 %hr/
47
47
48 %script{:type => 'text/javascript'}
48 %script{:type => 'text/javascript'}
49 = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';"
49 = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';"
50 Announcement.registerRefreshEventTimer();
50 Announcement.registerRefreshEventTimer();
51
51
52 + TOIContest.problemSelectClick()
53 +
52 TOIContest.timeOuts = {};
54 TOIContest.timeOuts = {};
53 - @problems.each do |p|
55 - @problems.each do |p|
54 - if (@submission_timeouts.has_key? p.id) and (@submission_timeouts[p.id] != nil)
56 - if (@submission_timeouts.has_key? p.id) and (@submission_timeouts[p.id] != nil)
55 = "TOIContest.timeOuts[#{p.id}] = #{@submission_timeouts[p.id]};"
57 = "TOIContest.timeOuts[#{p.id}] = #{@submission_timeouts[p.id]};"
56 - else
58 - else
57 = "TOIContest.timeOuts[#{p.id}] = TOIContest.NO_TIMEOUT;"
59 = "TOIContest.timeOuts[#{p.id}] = TOIContest.NO_TIMEOUT;"
58
60
59 TOIContest.refreshTimeOutMessages();
61 TOIContest.refreshTimeOutMessages();
62 + TOIContest.registerRefreshEvent();
@@ -8,13 +8,13
8 # system, you should be using db:schema:load, not running all the migrations
8 # system, you should be using db:schema:load, not running all the migrations
9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 # you'll amass, the slower it'll run and the greater likelihood for issues).
10 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 #
11 #
12 # It's strongly recommended to check this file into your version control system.
12 # It's strongly recommended to check this file into your version control system.
13
13
14 - ActiveRecord::Schema.define(:version => 20150128165518) do
14 + ActiveRecord::Schema.define(:version => 20150129021221) do
15
15
16 create_table "announcements", :force => true do |t|
16 create_table "announcements", :force => true do |t|
17 t.string "author"
17 t.string "author"
18 t.text "body"
18 t.text "body"
19 t.boolean "published"
19 t.boolean "published"
20 t.datetime "created_at", :null => false
20 t.datetime "created_at", :null => false
@@ -163,12 +163,13
163 t.text "compiler_message"
163 t.text "compiler_message"
164 t.datetime "graded_at"
164 t.datetime "graded_at"
165 t.integer "points"
165 t.integer "points"
166 t.text "grader_comment"
166 t.text "grader_comment"
167 t.integer "number"
167 t.integer "number"
168 t.string "source_filename"
168 t.string "source_filename"
169 + t.text "output"
169 end
170 end
170
171
171 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
172 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
172 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
173 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
173
174
174 create_table "tasks", :force => true do |t|
175 create_table "tasks", :force => true do |t|
You need to be logged in to leave comments. Login now