Description:
added announcement refresh, more styling, prep for announcement hiding git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@412 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r192:f7680fc5e3dd - - 7 files changed: 102 inserted, 10 deleted

@@ -0,0 +1,4
1 + environment.rb
2 + database.yml
3 + tasks.yml
4 +
@@ -0,0 +1,33
1 +
2 + var Announcement = {
3 +
4 + mostRecentId: 0,
5 +
6 + setMostRecentId: function(id) {
7 + Announcement.mostRecentId = id;
8 + },
9 +
10 + updateRecentId: function(id) {
11 + if(Announcement.mostRecentId < id)
12 + Announcement.mostRecentId = id;
13 + },
14 +
15 + refreshAnnouncement: function() {
16 + var url = '/main/announcements';
17 + new Ajax.Request(url, {
18 + method: 'get',
19 + parameters: { recent: Announcement.mostRecentId },
20 + onSuccess: function(transport) {
21 + var announcementBody = $("announcementbox-body");
22 + announcementBody.insert({ top: transport.responseText });
23 + }
24 + });
25 + Announcement.registerRefreshEventTimer();
26 + },
27 +
28 + registerRefreshEventTimer: function() {
29 + setTimeout(function () {
30 + Announcement.refreshAnnouncement();
31 + }, 30000);
32 + }
33 + }; No newline at end of file
@@ -1,4 +1,2
1 1 *~
2 2 log
3 - config/environment.rb
4 - config/database.yml
@@ -1,295 +1,317
1 1 class MainController < ApplicationController
2 2
3 3 SYSTEM_MODE_CONF_KEY = 'system.mode'
4 4
5 5 before_filter :authenticate, :except => [:index, :login]
6 6 before_filter :check_viewability, :except => [:index, :login]
7 7
8 8 # COMMENTED OUT: filter in each action instead
9 9 # before_filter :verify_time_limit, :only => [:submit]
10 10
11 11 verify :method => :post, :only => [:submit],
12 12 :redirect_to => { :action => :index }
13 13
14 14 # COMMENT OUT: only need when having high load
15 15 # caches_action :index, :login
16 16
17 17 # NOTE: This method is not actually needed, 'config/routes.rb' has
18 18 # assigned action login as a default action.
19 19 def index
20 20 redirect_to :action => 'login'
21 21 end
22 22
23 23 def login
24 24 saved_notice = flash[:notice]
25 25 reset_session
26 26 flash[:notice] = saved_notice
27 27
28 28 # EXPERIMENT:
29 29 # Hide login if in single user mode and the url does not
30 30 # explicitly specify /login
31 31 #
32 32 # logger.info "PATH: #{request.path}"
33 33 # if Configuration['system.single_user_mode'] and
34 34 # request.path!='/main/login'
35 35 # @hidelogin = true
36 36 # end
37 37
38 38 @announcements = Announcement.find_for_frontpage
39 39 render :action => 'login', :layout => 'empty'
40 40 end
41 41
42 42 def list
43 43 prepare_list_information
44 44 end
45 45
46 46 def help
47 47 @user = User.find(session[:user_id])
48 48 end
49 49
50 50 def submit
51 51 user = User.find(session[:user_id])
52 52
53 53 @submission = Submission.new(params[:submission])
54 54 @submission.user = user
55 55 @submission.language_id = 0
56 56 if (params['file']) and (params['file']!='')
57 57 @submission.source = params['file'].read
58 58 @submission.source_filename = params['file'].original_filename
59 59 end
60 60 @submission.submitted_at = Time.new.gmtime
61 61
62 62 if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
63 63 user.site!=nil and user.site.finished?
64 64 @submission.errors.add_to_base "The contest is over."
65 65 prepare_list_information
66 66 render :action => 'list' and return
67 67 end
68 68
69 69 if @submission.valid?
70 70 if @submission.save == false
71 71 flash[:notice] = 'Error saving your submission'
72 72 elsif Task.create(:submission_id => @submission.id,
73 73 :status => Task::STATUS_INQUEUE) == false
74 74 flash[:notice] = 'Error adding your submission to task queue'
75 75 end
76 76 else
77 77 prepare_list_information
78 78 render :action => 'list' and return
79 79 end
80 80 redirect_to :action => 'list'
81 81 end
82 82
83 83 def source
84 84 submission = Submission.find(params[:id])
85 85 if submission.user_id == session[:user_id]
86 86 send_data(submission.source,
87 87 {:filename => submission.download_filename,
88 88 :type => 'text/plain'})
89 89 else
90 90 flash[:notice] = 'Error viewing source'
91 91 redirect_to :action => 'list'
92 92 end
93 93 end
94 94
95 95 def compiler_msg
96 96 @submission = Submission.find(params[:id])
97 97 if @submission.user_id == session[:user_id]
98 98 render :action => 'compiler_msg', :layout => 'empty'
99 99 else
100 100 flash[:notice] = 'Error viewing source'
101 101 redirect_to :action => 'list'
102 102 end
103 103 end
104 104
105 105 def submission
106 106 @user = User.find(session[:user_id])
107 107 @problems = Problem.find_available_problems
108 108 if params[:id]==nil
109 109 @problem = nil
110 110 @submissions = nil
111 111 else
112 112 @problem = Problem.find_by_name(params[:id])
113 113 if not @problem.available
114 114 redirect_to :action => 'list'
115 115 flash[:notice] = 'Error: submissions for that problem are not viewable.'
116 116 return
117 117 end
118 118 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
119 119 end
120 120 end
121 121
122 122 def result
123 123 if !Configuration.show_grading_result
124 124 redirect_to :action => 'list' and return
125 125 end
126 126 @user = User.find(session[:user_id])
127 127 @submission = Submission.find(params[:id])
128 128 if @submission.user!=@user
129 129 flash[:notice] = 'You are not allowed to view result of other users.'
130 130 redirect_to :action => 'list' and return
131 131 end
132 132 prepare_grading_result(@submission)
133 133 end
134 134
135 135 def load_output
136 136 if !Configuration.show_grading_result or params[:num]==nil
137 137 redirect_to :action => 'list' and return
138 138 end
139 139 @user = User.find(session[:user_id])
140 140 @submission = Submission.find(params[:id])
141 141 if @submission.user!=@user
142 142 flash[:notice] = 'You are not allowed to view result of other users.'
143 143 redirect_to :action => 'list' and return
144 144 end
145 145 case_num = params[:num].to_i
146 146 out_filename = output_filename(@user.login,
147 147 @submission.problem.name,
148 148 @submission.id,
149 149 case_num)
150 150 if !FileTest.exists?(out_filename)
151 151 flash[:notice] = 'Output not found.'
152 152 redirect_to :action => 'list' and return
153 153 end
154 154
155 155 response.headers['Content-Type'] = "application/force-download"
156 156 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
157 157 response.headers["X-Sendfile"] = out_filename
158 158 response.headers['Content-length'] = File.size(out_filename)
159 159 render :nothing => true
160 160 end
161 161
162 162 def error
163 163 @user = User.find(session[:user_id])
164 164 end
165 165
166 + # announcement refreshing and hiding methods
167 +
168 + def announcements
169 + if params.has_key? 'recent'
170 + prepare_announcements(params[:recent])
171 + else
172 + prepare_announcements
173 + end
174 + render(:partial => 'announcement',
175 + :collection => @announcements,
176 + :locals => {:announcement_effect => true})
177 + end
178 +
166 179 protected
180 +
181 + def prepare_announcements(recent=nil)
182 + if Configuration.show_tasks_to?(@user)
183 + @announcements = Announcement.find_published(true)
184 + else
185 + @announcements = Announcement.find_published
186 + end
187 + if recent!=nil
188 + recent_id = recent.to_i
189 + @announcements = @announcements.find_all { |a| a.id > recent_id }
190 + end
191 + end
192 +
167 193 def prepare_list_information
168 194 @problems = Problem.find_available_problems
169 195 @prob_submissions = Array.new
170 196 @user = User.find(session[:user_id])
171 197 @problems.each do |p|
172 198 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
173 199 if sub!=nil
174 200 @prob_submissions << { :count => sub.number, :submission => sub }
175 201 else
176 202 @prob_submissions << { :count => 0, :submission => nil }
177 203 end
178 204 end
179 - if Configuration.show_tasks_to?(@user)
180 - @announcements = Announcement.find_published(true)
181 - else
182 - @announcements = Announcement.find_published
183 - end
205 + prepare_announcements
184 206 end
185 207
186 208 def check_viewability
187 209 @user = User.find(session[:user_id])
188 210 if (!Configuration.show_tasks_to?(@user)) and
189 211 ((action_name=='submission') or (action_name=='submit'))
190 212 redirect_to :action => 'list' and return
191 213 end
192 214 end
193 215
194 216 def prepare_grading_result(submission)
195 217 grading_info = Configuration.task_grading_info[submission.problem.name]
196 218 @test_runs = []
197 219 if grading_info['testruns'].is_a? Integer
198 220 trun_count = grading_info['testruns']
199 221 trun_count.times do |i|
200 222 @test_runs << [ read_grading_result(@user.login,
201 223 submission.problem.name,
202 224 submission.id,
203 225 i+1) ]
204 226 end
205 227 else
206 228 grading_info['testruns'].keys.sort.each do |num|
207 229 run = []
208 230 testrun = grading_info['testruns'][num]
209 231 testrun.each do |c|
210 232 run << read_grading_result(@user.login,
211 233 submission.problem.name,
212 234 submission.id,
213 235 c)
214 236 end
215 237 @test_runs << run
216 238 end
217 239 end
218 240 end
219 241
220 242 def grading_result_dir(user_name, problem_name, submission_id, case_num)
221 243 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
222 244 end
223 245
224 246 def output_filename(user_name, problem_name, submission_id, case_num)
225 247 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
226 248 return "#{dir}/output.txt"
227 249 end
228 250
229 251 def read_grading_result(user_name, problem_name, submission_id, case_num)
230 252 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
231 253 result_file_name = "#{dir}/result"
232 254 if !FileTest.exists?(result_file_name)
233 255 return {:num => case_num, :msg => 'program did not run'}
234 256 else
235 257 results = File.open(result_file_name).readlines
236 258 run_stat = extract_running_stat(results)
237 259 output_filename = "#{dir}/output.txt"
238 260 if FileTest.exists?(output_filename)
239 261 output_file = true
240 262 output_size = File.size(output_filename)
241 263 else
242 264 output_file = false
243 265 output_size = 0
244 266 end
245 267
246 268 return {
247 269 :num => case_num,
248 270 :msg => results[0],
249 271 :run_stat => run_stat,
250 272 :output => output_file,
251 273 :output_size => output_size
252 274 }
253 275 end
254 276 end
255 277
256 278 # copied from grader/script/lib/test_request_helper.rb
257 279 def extract_running_stat(results)
258 280 running_stat_line = results[-1]
259 281
260 282 # extract exit status line
261 283 run_stat = ""
262 284 if !(/[Cc]orrect/.match(results[0]))
263 285 run_stat = results[0].chomp
264 286 else
265 287 run_stat = 'Program exited normally'
266 288 end
267 289
268 290 logger.info "Stat line: #{running_stat_line}"
269 291
270 292 # extract running time
271 293 if res = /r(.*)u(.*)s/.match(running_stat_line)
272 294 seconds = (res[1].to_f + res[2].to_f)
273 295 time_stat = "Time used: #{seconds} sec."
274 296 else
275 297 seconds = nil
276 298 time_stat = "Time used: n/a sec."
277 299 end
278 300
279 301 # extract memory usage
280 302 if res = /s(.*)m/.match(running_stat_line)
281 303 memory_used = res[1].to_i
282 304 else
283 305 memory_used = -1
284 306 end
285 307
286 308 return {
287 309 :msg => "#{run_stat}\n#{time_stat}",
288 310 :running_time => seconds,
289 311 :exit_status => run_stat,
290 312 :memory_usage => memory_used
291 313 }
292 314 end
293 315
294 316 end
295 317
@@ -1,4 +1,17
1 - .announcement
1 + .announcement{:id => "announcement-#{announcement.id}", :style => "#{'opacity: 0;' if (defined? announcement_effect) and announcement_effect }"}
2 + -# .announcement-title
3 + -# .toggles
4 + -# %a{:href => '#', :onclick => "$(\"announcement-body-#{announcement.id}\").hide(); return false;"}
5 + -# [hide]
6 + -# %a{:href => '#', :onclick => "$(\"announcement-body-#{announcement.id}\").show(); return false;"}
7 + -# [show]
8 + -# Announcement Title
9 + .announcement-body{:id => "announcement-body-#{announcement.id}"}
2 10 = markdown(announcement.body)
3 11 -#.pub-info
4 12 -# %p= "#{announcement.author}, #{announcement.created_at}"
13 + :javascript
14 + Announcement.updateRecentId(#{announcement.id});
15 + - if (defined? announcement_effect) and announcement_effect
16 + :javascript
17 + $("announcement-#{announcement.id}").appear();
@@ -1,30 +1,39
1 + - content_for :head do
2 + = javascript_include_tag :defaults
3 + %script{:type => 'text/javascript', :src => '/javascripts/announcement_refresh.js'}
4 +
1 5 = user_title_bar(@user)
2 6
3 7 - if @announcements.length!=0
4 8 .announcementbox
5 9 %span{:class => 'title'}
6 10 Announcements
11 + #announcementbox-body
7 12 = render :partial => 'announcement', :collection => @announcements
8 13
9 14 - if Configuration.show_submitbox_to?(@user)
10 15 .submitbox
11 16 = error_messages_for 'submission'
12 17 = render :partial => 'submission_box'
13 18
14 19
15 20 %hr/
16 21
17 - - if (Configuration.contest_mode) and (@user.site!=nil) and (@user.site.started!=true)
22 + - if (Configuration.contest_mode) and (@user.site!=nil) |
23 + and (@user.site.started!=true)
18 24 %p=t 'main.start_soon'
19 25
20 26 - if Configuration.show_tasks_to?(@user)
21 27 %table.info
22 28 %tr.info-head
23 29 %th
24 30 %th Tasks
25 31 %th # of sub(s)
26 32 %th Results
27 33 = render :partial => 'problem', :collection => @problems
28 34
29 35 %hr/
30 36
37 + :javascript
38 + Announcement.registerRefreshEventTimer();
39 +
@@ -48,276 +48,289
48 48 text-align: right;
49 49 font-size: 12px;
50 50 }
51 51
52 52 /* This is the top bar, displaying user's full name */
53 53 div.title {
54 54 // background: #ddddff;
55 55 // border: 1px dashed blue;
56 56 padding: 10px 0px;
57 57 line-height: 1.5em;
58 58 font-size: 13px;
59 59 }
60 60
61 61 div.title span.contest-over-msg {
62 62 font-size: 15px;
63 63 color: red;
64 64 }
65 65
66 66 div.title table {
67 67 width: 100%;
68 68 }
69 69
70 70 div.title td.left-col {
71 71 text-align: left;
72 72 vertical-align: top;
73 73 color: #444444;
74 74 }
75 75
76 76 div.title td.right-col {
77 77 text-align: right;
78 78 vertical-align: top;
79 79 font-size: 14px;
80 80 font-weight: bold;
81 81 color: #116699;
82 82 }
83 83
84 84 /* Standard table with header and rows with alternating background */
85 85 table.info {
86 86 margin: 10px 0;
87 87 border: 1px solid #666666;
88 88 border-collapse: collapse;
89 89 font-size: 12px;
90 90 }
91 91
92 92 table.info th {
93 93 border: 1px solid #666666;
94 94 line-height: 1.5em;
95 95 padding: 0 0.5em;
96 96 }
97 97
98 98 table.info td {
99 99 border-left: 1px solid #666666;
100 100 border-right: 1px solid #666666;
101 101 line-height: 1.5em;
102 102 padding: 0 0.5em;
103 103 }
104 104
105 105 tr.info-head {
106 106 background: #777777;
107 107 color: white;
108 108 }
109 109
110 110 tr.info-odd {
111 111 background: #eeeeee;
112 112 }
113 113
114 114 tr.info-even {
115 115 background: #f9f9f9;
116 116 }
117 117
118 118 /*******************************
119 119 [Main]
120 120 ********************************/
121 121 div.submitbox {
122 122 background: #eeeeff;
123 123 border: 1px dotted #99aaee;
124 124 padding: 5px;
125 125 margin: 10px 0px;
126 126 color: black;
127 127 font-weight: bold;
128 128 font-size: 13px;
129 129 }
130 130
131 131 div.errorExplanation {
132 132 border: 1px dotted gray;
133 133 color: #bb2222;
134 134 padding: 5px 15px 5px 15px;
135 135 margin-bottom: 5px;
136 136 background-color: white;
137 137 }
138 138
139 139 /*******************************
140 140 [Settings]
141 141 ********************************/
142 142 table.uinfo {
143 143 border-collapse: collapse;
144 144 border: 1px solid black;
145 145 font-size: 13px;
146 146 }
147 147
148 148 td.uinfo {
149 149 vertical-align: top;
150 150 border: 1px solid black;
151 151 padding: 5px;
152 152 }
153 153
154 154 th.uinfo {
155 155 background: lightgreen;
156 156 vertical-align: top;
157 157 text-align: right;
158 158 border: 1px solid black;
159 159 padding: 5px;
160 160 }
161 161
162 162 /*******************************
163 163 [Submission]
164 164 ********************************/
165 165 div.compilermsgbody {
166 166 font-family: monospace;
167 167 }
168 168
169 169 div.task-menu {
170 170 text-align: center;
171 171 font-size: 13px;
172 172 line-height: 1.75em;
173 173 font-weight: bold;
174 174 border-top: 1px dashed gray;
175 175 border-bottom: 1px dashed gray;
176 176 margin-top: 2px;
177 177 margin-bottom: 4px;
178 178 }
179 179
180 180 /*******************************
181 181 [Submission]
182 182 ********************************/
183 183 table.taskdesc {
184 184 border: 2px solid #dddddd;
185 185 border-collapse: collapse;
186 186 margin: 10px auto;
187 187 width: 90%;
188 188 font-size: 13px;
189 189 }
190 190
191 191 table.taskdesc p {
192 192 font-size: 13px;
193 193 }
194 194
195 195 table.taskdesc tr.name {
196 196 border: 2px solid #dddddd;
197 197 background: #dddddd;
198 198 color: #333333;
199 199 font-weight: bold;
200 200 font-size: 14px;
201 201 line-height: 1.5em;
202 202 text-align: center;
203 203 }
204 204
205 205 table.taskdesc td.desc-odd {
206 206 padding: 5px;
207 207 padding-left: 20px;
208 208 background: #fefeee;
209 209 }
210 210
211 211 table.taskdesc td.desc-even {
212 212 padding: 5px;
213 213 padding-left: 20px;
214 214 background: #feeefe;
215 215 }
216 216
217 217 /**********************
218 218 Announcement
219 219 ***********************/
220 220
221 221 div.announcementbox {
222 222 margin: 10px 0px;
223 223 background: #bbddee;
224 224 padding: 1px;
225 225 }
226 226
227 227 div.announcementbox span.title {
228 228 font-weight: bold;
229 229 color: #224455;
230 230 padding-left: 10px;
231 231 line-height: 1.6em;
232 232 }
233 233
234 234 div.announcement {
235 235 margin: 2px;
236 236 background: white;
237 237 padding: 1px;
238 238 padding-left: 10px;
239 239 padding-right: 10px;
240 + padding-top: 5px;
241 + padding-bottom: 5px;
240 242 }
241 243
242 - div.announcement p {
244 + .announcement p {
243 245 font-size: 12px;
246 + margin: 2px;
244 247 }
245 248
246 249 div.pub-info, div.pub-info p {
247 250 text-align: right;
248 251 font-style: italic;
249 252 font-size: 9px;
250 253 }
251 254
255 + .announcement .toggles {
256 + font-weight: normal;
257 + float: right;
258 + font-size: 80%;
259 + }
260 +
261 + .announcement .announcement-title {
262 + font-weight: bold;
263 + }
264 +
252 265 /******************
253 266 Messages
254 267 ******************/
255 268
256 269 div.message {
257 270 margin: 10px 0 0;
258 271 }
259 272
260 273 div.message div.message {
261 274 margin: 0 0 0 30px;
262 275 }
263 276
264 277 div.message div.body {
265 278 border: 2px solid #dddddd;
266 279 background: #fff8f8;
267 280 padding-left: 5px;
268 281 }
269 282
270 283 div.message div.reply-body {
271 284 border: 2px solid #bbbbbb;
272 285 background: #fffff8;
273 286 padding-left: 5px;
274 287 }
275 288
276 289 div.message div.stat {
277 290 font-size: 10px;
278 291 line-height: 1.75em;
279 292 padding: 0 5px;
280 293 color: #333333;
281 294 background: #dddddd;
282 295 font-weight: bold;
283 296 }
284 297
285 298 div.message div.message div.stat {
286 299 font-size: 10px;
287 300 line-height: 1.75em;
288 301 padding: 0 5px;
289 302 color: #444444;
290 303 background: #bbbbbb;
291 304 font-weight: bold;
292 305 }
293 306
294 307 /********************
295 308 Registration
296 309 ********************/
297 310 div.contest-title {
298 311 color: white;
299 312 text-align: center;
300 313 line-height: 2em;
301 314 }
302 315
303 316 div.registration-desc {
304 317 border: 1px dotted gray;
305 318 background: #f5f5f5;
306 319 padding: 5px;
307 320 margin: 10px 0;
308 321 font-size: 12px;
309 322 line-height: 1.5em;
310 323 }
311 324
312 325 /********************
313 326 [Test Interface]
314 327 ********************/
315 328
316 329 div.test-desc {
317 330 border: 1px dotted gray;
318 331 background: #f5f5f5;
319 332 padding: 5px;
320 333 margin: 10px 0;
321 334 font-size: 12px;
322 335 line-height: 1.5em;
323 336 }
You need to be logged in to leave comments. Login now