Description:
- after submission, redirecto to edit_submission_path - add more info to submission status - fix bug #20 and #21
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r696:0b7243db68e6 - - 11 files changed: 28 inserted, 18 deleted

@@ -4,193 +4,193
4 before_filter :check_viewability, :except => [:index, :login]
4 before_filter :check_viewability, :except => [:index, :login]
5
5
6 append_before_filter :confirm_and_update_start_time,
6 append_before_filter :confirm_and_update_start_time,
7 :except => [:index,
7 :except => [:index,
8 :login,
8 :login,
9 :confirm_contest_start]
9 :confirm_contest_start]
10
10
11 # to prevent log in box to be shown when user logged out of the
11 # to prevent log in box to be shown when user logged out of the
12 # system only in some tab
12 # system only in some tab
13 prepend_before_filter :reject_announcement_refresh_when_logged_out,
13 prepend_before_filter :reject_announcement_refresh_when_logged_out,
14 :only => [:announcements]
14 :only => [:announcements]
15
15
16 before_filter :authenticate_by_ip_address, :only => [:list]
16 before_filter :authenticate_by_ip_address, :only => [:list]
17
17
18 # COMMENTED OUT: filter in each action instead
18 # COMMENTED OUT: filter in each action instead
19 # before_filter :verify_time_limit, :only => [:submit]
19 # before_filter :verify_time_limit, :only => [:submit]
20
20
21 verify :method => :post, :only => [:submit],
21 verify :method => :post, :only => [:submit],
22 :redirect_to => { :action => :index }
22 :redirect_to => { :action => :index }
23
23
24 # COMMENT OUT: only need when having high load
24 # COMMENT OUT: only need when having high load
25 # caches_action :index, :login
25 # caches_action :index, :login
26
26
27 # NOTE: This method is not actually needed, 'config/routes.rb' has
27 # NOTE: This method is not actually needed, 'config/routes.rb' has
28 # assigned action login as a default action.
28 # assigned action login as a default action.
29 def index
29 def index
30 redirect_to :action => 'login'
30 redirect_to :action => 'login'
31 end
31 end
32
32
33 def login
33 def login
34 saved_notice = flash[:notice]
34 saved_notice = flash[:notice]
35 reset_session
35 reset_session
36 flash.now[:notice] = saved_notice
36 flash.now[:notice] = saved_notice
37
37
38 # EXPERIMENT:
38 # EXPERIMENT:
39 # Hide login if in single user mode and the url does not
39 # Hide login if in single user mode and the url does not
40 # explicitly specify /login
40 # explicitly specify /login
41 #
41 #
42 # logger.info "PATH: #{request.path}"
42 # logger.info "PATH: #{request.path}"
43 # if GraderConfiguration['system.single_user_mode'] and
43 # if GraderConfiguration['system.single_user_mode'] and
44 # request.path!='/main/login'
44 # request.path!='/main/login'
45 # @hidelogin = true
45 # @hidelogin = true
46 # end
46 # end
47
47
48 @announcements = Announcement.frontpage
48 @announcements = Announcement.frontpage
49 render :action => 'login', :layout => 'empty'
49 render :action => 'login', :layout => 'empty'
50 end
50 end
51
51
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
149 end
149 end
150 case_num = params[:num].to_i
150 case_num = params[:num].to_i
151 out_filename = output_filename(@user.login,
151 out_filename = output_filename(@user.login,
152 @submission.problem.name,
152 @submission.problem.name,
153 @submission.id,
153 @submission.id,
154 case_num)
154 case_num)
155 if !FileTest.exists?(out_filename)
155 if !FileTest.exists?(out_filename)
156 flash[:notice] = 'Output not found.'
156 flash[:notice] = 'Output not found.'
157 redirect_to :action => 'list' and return
157 redirect_to :action => 'list' and return
158 end
158 end
159
159
160 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
160 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
161 response.headers['Content-Type'] = "application/force-download"
161 response.headers['Content-Type'] = "application/force-download"
162 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
162 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
163 response.headers["X-Sendfile"] = out_filename
163 response.headers["X-Sendfile"] = out_filename
164 response.headers['Content-length'] = File.size(out_filename)
164 response.headers['Content-length'] = File.size(out_filename)
165 render :nothing => true
165 render :nothing => true
166 else
166 else
167 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
167 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
168 end
168 end
169 end
169 end
170
170
171 def error
171 def error
172 @user = User.find(session[:user_id])
172 @user = User.find(session[:user_id])
173 end
173 end
174
174
175 # announcement refreshing and hiding methods
175 # announcement refreshing and hiding methods
176
176
177 def announcements
177 def announcements
178 if params.has_key? 'recent'
178 if params.has_key? 'recent'
179 prepare_announcements(params[:recent])
179 prepare_announcements(params[:recent])
180 else
180 else
181 prepare_announcements
181 prepare_announcements
182 end
182 end
183 render(:partial => 'announcement',
183 render(:partial => 'announcement',
184 :collection => @announcements,
184 :collection => @announcements,
185 :locals => {:announcement_effect => true})
185 :locals => {:announcement_effect => true})
186 end
186 end
187
187
188 def confirm_contest_start
188 def confirm_contest_start
189 user = User.find(session[:user_id])
189 user = User.find(session[:user_id])
190 if request.method == 'POST'
190 if request.method == 'POST'
191 user.update_start_time
191 user.update_start_time
192 redirect_to :action => 'list'
192 redirect_to :action => 'list'
193 else
193 else
194 @contests = user.contests
194 @contests = user.contests
195 @user = user
195 @user = user
196 end
196 end
@@ -1,150 +1,152
1 require 'csv'
1 require 'csv'
2
2
3 class ReportController < ApplicationController
3 class ReportController < ApplicationController
4
4
5 before_filter :authenticate
5 before_filter :authenticate
6
6
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
103 date_and_time = '%Y-%m-%d %H:%M'
105 date_and_time = '%Y-%m-%d %H:%M'
104 begin
106 begin
105 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
107 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
106 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
108 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
107 rescue
109 rescue
108 @since_time = DateTime.new(1000,1,1)
110 @since_time = DateTime.new(1000,1,1)
109 end
111 end
110 begin
112 begin
111 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
113 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
112 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
114 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
113 rescue
115 rescue
114 @until_time = DateTime.new(3000,1,1)
116 @until_time = DateTime.new(3000,1,1)
115 end
117 end
116
118
117 User.all.each do |user|
119 User.all.each do |user|
118 @logins << { id: user.id,
120 @logins << { id: user.id,
119 login: user.login,
121 login: user.login,
120 full_name: user.full_name,
122 full_name: user.full_name,
121 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
123 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
122 user.id,@since_time,@until_time)
124 user.id,@since_time,@until_time)
123 .count(:id),
125 .count(:id),
124 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
126 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
125 user.id,@since_time,@until_time)
127 user.id,@since_time,@until_time)
126 .minimum(:created_at),
128 .minimum(:created_at),
127 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
129 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
128 user.id,@since_time,@until_time)
130 user.id,@since_time,@until_time)
129 .maximum(:created_at),
131 .maximum(:created_at),
130 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
132 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
131 user.id,@since_time,@until_time)
133 user.id,@since_time,@until_time)
132 .select(:ip_address).uniq
134 .select(:ip_address).uniq
133
135
134 }
136 }
135 end
137 end
136 end
138 end
137
139
138 def submission_stat
140 def submission_stat
139
141
140 date_and_time = '%Y-%m-%d %H:%M'
142 date_and_time = '%Y-%m-%d %H:%M'
141 begin
143 begin
142 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
144 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
143 rescue
145 rescue
144 @since_time = DateTime.new(1000,1,1)
146 @since_time = DateTime.new(1000,1,1)
145 end
147 end
146 begin
148 begin
147 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
149 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
148 rescue
150 rescue
149 @until_time = DateTime.new(3000,1,1)
151 @until_time = DateTime.new(3000,1,1)
150 end
152 end
@@ -1,198 +1,201
1 # Methods added to this helper will be available to all templates in the application.
1 # Methods added to this helper will be available to all templates in the application.
2 module ApplicationHelper
2 module ApplicationHelper
3
3
4 #new bootstrap header
4 #new bootstrap header
5 def navbar_user_header
5 def navbar_user_header
6 left_menu = ''
6 left_menu = ''
7 right_menu = ''
7 right_menu = ''
8 user = User.find(session[:user_id])
8 user = User.find(session[:user_id])
9
9
10 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
10 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
11 left_menu << add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list')
11 left_menu << add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list')
12 left_menu << add_menu("#{I18n.t 'menu.submissions'}", 'main', 'submission')
12 left_menu << add_menu("#{I18n.t 'menu.submissions'}", 'main', 'submission')
13 left_menu << add_menu("#{I18n.t 'menu.test'}", 'test', 'index')
13 left_menu << add_menu("#{I18n.t 'menu.test'}", 'test', 'index')
14 end
14 end
15
15
16 if GraderConfiguration['right.user_hall_of_fame']
16 if GraderConfiguration['right.user_hall_of_fame']
17 left_menu << add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
17 left_menu << add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
18 end
18 end
19
19
20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
21 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
21 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
22 if GraderConfiguration['system.user_setting_enabled']
22 if GraderConfiguration['system.user_setting_enabled']
23 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
23 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
24 end
24 end
25 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
25 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
26
26
27
27
28 result = content_tag(:ul,left_menu.html_safe,class: 'nav navbar-nav') + content_tag(:ul,right_menu.html_safe,class: 'nav navbar-nav navbar-right')
28 result = content_tag(:ul,left_menu.html_safe,class: 'nav navbar-nav') + content_tag(:ul,right_menu.html_safe,class: 'nav navbar-nav navbar-right')
29 end
29 end
30
30
31 def add_menu(title, controller, action, html_option = {})
31 def add_menu(title, controller, action, html_option = {})
32 link_option = {controller: controller, action: action}
32 link_option = {controller: controller, action: action}
33 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
33 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
34 content_tag(:li, link_to(title,link_option),html_option)
34 content_tag(:li, link_to(title,link_option),html_option)
35 end
35 end
36
36
37 def user_header
37 def user_header
38 menu_items = ''
38 menu_items = ''
39 user = User.find(session[:user_id])
39 user = User.find(session[:user_id])
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.gmtime
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>
151 CONTEST_OVER
154 CONTEST_OVER
152 end
155 end
153 if !user.contest_started?
156 if !user.contest_started?
154 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
157 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
155 else
158 else
156 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
159 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
157 " #{format_short_duration(user.contest_time_left)}"
160 " #{format_short_duration(user.contest_time_left)}"
158 end
161 end
159 end
162 end
160
163
161 #
164 #
162 # if the contest is in the anaysis mode
165 # if the contest is in the anaysis mode
163 if GraderConfiguration.analysis_mode?
166 if GraderConfiguration.analysis_mode?
164 header = <<ANALYSISMODE
167 header = <<ANALYSISMODE
165 <tr><td colspan="2" align="center">
168 <tr><td colspan="2" align="center">
166 <span class="contest-over-msg">ANALYSIS MODE</span>
169 <span class="contest-over-msg">ANALYSIS MODE</span>
167 </td></tr>
170 </td></tr>
168 ANALYSISMODE
171 ANALYSISMODE
169 end
172 end
170
173
171 contest_name = GraderConfiguration['contest.name']
174 contest_name = GraderConfiguration['contest.name']
172
175
173 #
176 #
174 # build real title bar
177 # build real title bar
175 result = <<TITLEBAR
178 result = <<TITLEBAR
176 <div class="title">
179 <div class="title">
177 <table>
180 <table>
178 #{header}
181 #{header}
179 <tr>
182 <tr>
180 <td class="left-col">
183 <td class="left-col">
181 #{user.full_name}<br/>
184 #{user.full_name}<br/>
182 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
185 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
183 #{time_left}
186 #{time_left}
184 <br/>
187 <br/>
185 </td>
188 </td>
186 <td class="right-col">#{contest_name}</td>
189 <td class="right-col">#{contest_name}</td>
187 </tr>
190 </tr>
188 </table>
191 </table>
189 </div>
192 </div>
190 TITLEBAR
193 TITLEBAR
191 result.html_safe
194 result.html_safe
192 end
195 end
193
196
194 def markdown(text)
197 def markdown(text)
195 markdown = RDiscount.new(text)
198 markdown = RDiscount.new(text)
196 markdown.to_html.html_safe
199 markdown.to_html.html_safe
197 end
200 end
198
201
@@ -1,134 +1,134
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
87 end
87 end
88
88
89 def self.find_language_in_source(source, source_filename="")
89 def self.find_language_in_source(source, source_filename="")
90 langopt = find_option_in_source(/^LANG:/,source)
90 langopt = find_option_in_source(/^LANG:/,source)
91 if langopt
91 if langopt
92 return (Language.find_by_name(langopt) ||
92 return (Language.find_by_name(langopt) ||
93 Language.find_by_pretty_name(langopt))
93 Language.find_by_pretty_name(langopt))
94 else
94 else
95 if source_filename
95 if source_filename
96 return Language.find_by_extension(source_filename.split('.').last)
96 return Language.find_by_extension(source_filename.split('.').last)
97 else
97 else
98 return nil
98 return nil
99 end
99 end
100 end
100 end
101 end
101 end
102
102
103 def self.find_problem_in_source(source, source_filename="")
103 def self.find_problem_in_source(source, source_filename="")
104 prob_opt = find_option_in_source(/^TASK:/,source)
104 prob_opt = find_option_in_source(/^TASK:/,source)
105 if problem = Problem.find_by_name(prob_opt)
105 if problem = Problem.find_by_name(prob_opt)
106 return problem
106 return problem
107 else
107 else
108 if source_filename
108 if source_filename
109 return Problem.find_by_name(source_filename.split('.').first)
109 return Problem.find_by_name(source_filename.split('.').first)
110 else
110 else
111 return nil
111 return nil
112 end
112 end
113 end
113 end
114 end
114 end
115
115
116 def assign_problem
116 def assign_problem
117 if self.problem_id!=-1
117 if self.problem_id!=-1
118 begin
118 begin
119 self.problem = Problem.find(self.problem_id)
119 self.problem = Problem.find(self.problem_id)
120 rescue ActiveRecord::RecordNotFound
120 rescue ActiveRecord::RecordNotFound
121 self.problem = nil
121 self.problem = nil
122 end
122 end
123 else
123 else
124 self.problem = Submission.find_problem_in_source(self.source,
124 self.problem = Submission.find_problem_in_source(self.source,
125 self.source_filename)
125 self.source_filename)
126 end
126 end
127 end
127 end
128
128
129 def assign_language
129 def assign_language
130 self.language = Submission.find_language_in_source(self.source,
130 self.language = Submission.find_language_in_source(self.source,
131 self.source_filename)
131 self.source_filename)
132 end
132 end
133
133
134 # validation codes
134 # validation codes
@@ -1,26 +1,28
1 -
2 - if submission.nil?
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_short_time(submission.submitted_at.localtime)
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 - = "#{format_short_time(submission.graded_at.localtime)} "
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 %strong View:
22 %strong View:
21 - if GraderConfiguration.show_grading_result
23 - if GraderConfiguration.show_grading_result
22 = link_to '[detailed result]', :action => 'result', :id => submission.id
24 = link_to '[detailed result]', :action => 'result', :id => submission.id
23 - = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
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,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', true
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,126 +1,126
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 - if @submission
35 - if @submission
36 = render :partial => 'submission_short',
36 = render :partial => 'submission_short',
37 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
37 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
38 .row
38 .row
39 .col-md-12
39 .col-md-12
40 %h2 Console
40 %h2 Console
41 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
41 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
42
42
43 :javascript
43 :javascript
44 $(document).ready(function() {
44 $(document).ready(function() {
45 e = ace.edit("editor")
45 e = ace.edit("editor")
46 e.setValue($("#text_sourcecode").val());
46 e.setValue($("#text_sourcecode").val());
47 e.gotoLine(1);
47 e.gotoLine(1);
48 $("#language_id").trigger('change');
48 $("#language_id").trigger('change');
49 brython();
49 brython();
50 });
50 });
51
51
52
52
53 %script#__main__{type:'text/python3'}
53 %script#__main__{type:'text/python3'}
54 :plain
54 :plain
55 import sys
55 import sys
56 import traceback
56 import traceback
57
57
58 from browser import document as doc
58 from browser import document as doc
59 from browser import window, alert, console
59 from browser import window, alert, console
60
60
61 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
61 _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."""
62 for supporting Python development. See www.python.org for more information."""
63
63
64 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
64 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
65 All Rights Reserved.
65 All Rights Reserved.
66
66
67 Copyright (c) 2001-2013 Python Software Foundation.
67 Copyright (c) 2001-2013 Python Software Foundation.
68 All Rights Reserved.
68 All Rights Reserved.
69
69
70 Copyright (c) 2000 BeOpen.com.
70 Copyright (c) 2000 BeOpen.com.
71 All Rights Reserved.
71 All Rights Reserved.
72
72
73 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
73 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
74 All Rights Reserved.
74 All Rights Reserved.
75
75
76 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
76 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
77 All Rights Reserved."""
77 All Rights Reserved."""
78
78
79 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
79 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
80 All rights reserved.
80 All rights reserved.
81
81
82 Redistribution and use in source and binary forms, with or without
82 Redistribution and use in source and binary forms, with or without
83 modification, are permitted provided that the following conditions are met:
83 modification, are permitted provided that the following conditions are met:
84
84
85 Redistributions of source code must retain the above copyright notice, this
85 Redistributions of source code must retain the above copyright notice, this
86 list of conditions and the following disclaimer. Redistributions in binary
86 list of conditions and the following disclaimer. Redistributions in binary
87 form must reproduce the above copyright notice, this list of conditions and
87 form must reproduce the above copyright notice, this list of conditions and
88 the following disclaimer in the documentation and/or other materials provided
88 the following disclaimer in the documentation and/or other materials provided
89 with the distribution.
89 with the distribution.
90 Neither the name of the <ORGANIZATION> nor the names of its contributors may
90 Neither the name of the <ORGANIZATION> nor the names of its contributors may
91 be used to endorse or promote products derived from this software without
91 be used to endorse or promote products derived from this software without
92 specific prior written permission.
92 specific prior written permission.
93
93
94 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
94 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
95 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
95 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
96 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
96 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
97 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
97 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
98 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
98 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
99 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
99 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
100 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
100 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
101 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
101 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
102 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
102 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
103 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
103 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
104 POSSIBILITY OF SUCH DAMAGE.
104 POSSIBILITY OF SUCH DAMAGE.
105 """
105 """
106
106
107 def credits():
107 def credits():
108 print(_credits)
108 print(_credits)
109 credits.__repr__ = lambda:_credits
109 credits.__repr__ = lambda:_credits
110
110
111 def copyright():
111 def copyright():
112 print(_copyright)
112 print(_copyright)
113 copyright.__repr__ = lambda:_copyright
113 copyright.__repr__ = lambda:_copyright
114
114
115 def license():
115 def license():
116 print(_license)
116 print(_license)
117 license.__repr__ = lambda:_license
117 license.__repr__ = lambda:_license
118
118
119 def write(data):
119 def write(data):
120 doc['console'].value += str(data)
120 doc['console'].value += str(data)
121
121
122
122
123 sys.stdout.write = sys.stderr.write = write
123 sys.stdout.write = sys.stderr.write = write
124 history = []
124 history = []
125 current = 0
125 current = 0
126 _status = "main" # or "block" if typing inside a block
126 _status = "main" # or "block" if typing inside a block
@@ -1,72 +1,72
1 require File.expand_path('../boot', __FILE__)
1 require File.expand_path('../boot', __FILE__)
2
2
3 require 'rails/all'
3 require 'rails/all'
4
4
5 if defined?(Bundler)
5 if defined?(Bundler)
6 # If you precompile assets before deploying to production, use this line
6 # If you precompile assets before deploying to production, use this line
7 Bundler.require(*Rails.groups(:assets => %w(development test)))
7 Bundler.require(*Rails.groups(:assets => %w(development test)))
8 # If you want your assets lazily compiled in production, use this line
8 # If you want your assets lazily compiled in production, use this line
9 # Bundler.require(:default, :assets, Rails.env)
9 # Bundler.require(:default, :assets, Rails.env)
10 end
10 end
11
11
12 module CafeGrader
12 module CafeGrader
13 class Application < Rails::Application
13 class Application < Rails::Application
14 # Settings in config/environments/* take precedence over those specified here.
14 # Settings in config/environments/* take precedence over those specified here.
15 # Application configuration should go into files in config/initializers
15 # Application configuration should go into files in config/initializers
16 # -- all .rb files in that directory are automatically loaded.
16 # -- all .rb files in that directory are automatically loaded.
17
17
18 # Custom directories with classes and modules you want to be autoloadable.
18 # Custom directories with classes and modules you want to be autoloadable.
19 config.autoload_paths += %W(#{config.root}/lib)
19 config.autoload_paths += %W(#{config.root}/lib)
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,150 +1,152
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
103 end
105 end
104
106
105 return testdata_filename
107 return testdata_filename
106 end
108 end
107
109
108 def import_test_pairs(dirname)
110 def import_test_pairs(dirname)
109 test_num = 1
111 test_num = 1
110 while FileTest.exists? "#{dirname}/#{test_num}.in"
112 while FileTest.exists? "#{dirname}/#{test_num}.in"
111 in_filename = "#{dirname}/#{test_num}.in"
113 in_filename = "#{dirname}/#{test_num}.in"
112 sol_filename = "#{dirname}/#{test_num}.sol"
114 sol_filename = "#{dirname}/#{test_num}.sol"
113
115
114 break if not FileTest.exists? sol_filename
116 break if not FileTest.exists? sol_filename
115
117
116 test_pair = TestPair.new(:input => open(in_filename).read,
118 test_pair = TestPair.new(:input => open(in_filename).read,
117 :solution => open(sol_filename).read,
119 :solution => open(sol_filename).read,
118 :problem => @problem)
120 :problem => @problem)
119 break if not test_pair.save
121 break if not test_pair.save
120
122
121 test_num += 1
123 test_num += 1
122 end
124 end
123 return test_num > 1
125 return test_num > 1
124 end
126 end
125
127
126 def import_problem_description(dirname)
128 def import_problem_description(dirname)
127 html_files = Dir["#{dirname}/*.html"]
129 html_files = Dir["#{dirname}/*.html"]
128 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
130 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
129 if (html_files.length != 0) or (markdown_files.length != 0)
131 if (html_files.length != 0) or (markdown_files.length != 0)
130 description = @problem.description || Description.new
132 description = @problem.description || Description.new
131
133
132 if html_files.length != 0
134 if html_files.length != 0
133 filename = html_files[0]
135 filename = html_files[0]
134 description.markdowned = false
136 description.markdowned = false
135 else
137 else
136 filename = markdown_files[0]
138 filename = markdown_files[0]
137 description.markdowned = true
139 description.markdowned = true
138 end
140 end
139
141
140 description.body = open(filename).read
142 description.body = open(filename).read
141 description.save
143 description.save
142 @problem.description = description
144 @problem.description = description
143 @problem.save
145 @problem.save
144 return "\nProblem description imported from #{filename}."
146 return "\nProblem description imported from #{filename}."
145 else
147 else
146 return ''
148 return ''
147 end
149 end
148 end
150 end
149
151
150 def import_problem_pdf(dirname)
152 def import_problem_pdf(dirname)
You need to be logged in to leave comments. Login now