diff --git a/app/controllers/problems_controller.rb b/app/controllers/problems_controller.rb
--- a/app/controllers/problems_controller.rb
+++ b/app/controllers/problems_controller.rb
@@ -150,11 +150,25 @@
def stat
@problem = Problem.find(params[:id])
- if !@problem.available
+ unless @problem.available or session[:admin]
redirect_to :controller => 'main', :action => 'list'
- else
- @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
+ return
end
+ @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
+
+ #stat summary
+ range =65
+ @histogram = { data: Array.new(range,0), summary: {} }
+ user = Hash.new(0)
+ @submissions.find_each do |sub|
+ d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
+ @histogram[:data][d.to_i] += 1 if d < range
+ user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
+ end
+ @histogram[:summary][:max] = [@histogram[:data].max,1].max
+
+ @summary = { attempt: user.count, solve: 0 }
+ user.each_value { |v| @summary[:solve] += 1 if v == 1 }
end
def manage
@@ -257,4 +271,7 @@
problems
end
+ def get_problems_stat
+ end
+
end
diff --git a/app/controllers/report_controller.rb b/app/controllers/report_controller.rb
--- a/app/controllers/report_controller.rb
+++ b/app/controllers/report_controller.rb
@@ -105,91 +105,87 @@
end
end
- if @problem
- #aggregrate by language
- @by_lang = {}
- Submission.where(problem_id: @problem.id).find_each do |sub|
- lang = Language.find_by_id(sub.language_id)
- next unless lang
- next unless sub.points >= @problem.full_score
+ return unless @problem
+
+ @by_lang = {} #aggregrate by language
- #initialize
- unless @by_lang.has_key?(lang.pretty_name)
- @by_lang[lang.pretty_name] = {
- runtime: { avail: false, value: 2**30-1 },
- memory: { avail: false, value: 2**30-1 },
- length: { avail: false, value: 2**30-1 },
- first: { avail: false, value: DateTime.new(3000,1,1) }
- }
- end
+ range =65
+ @histogram = { data: Array.new(range,0), summary: {} }
+ @summary = {count: 0, solve: 0, attempt: 0}
+ user = Hash.new(0)
+ Submission.where(problem_id: @problem.id).find_each do |sub|
+ #histogram
+ d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
+ @histogram[:data][d.to_i] += 1 if d < range
- if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
- @by_lang[lang.pretty_name][:runtime] = {
- avail: true,
- user_id: sub.user_id,
- value: sub.max_runtime,
- sub_id: sub.id
- }
- end
+ @summary[:count] += 1
+ user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
- if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
- @by_lang[lang.pretty_name][:memory] = {
- avail: true,
- user_id: sub.user_id,
- value: sub.peak_memory,
- sub_id: sub.id
- }
- end
+ lang = Language.find_by_id(sub.language_id)
+ next unless lang
+ next unless sub.points >= @problem.full_score
- if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and
- !sub.user.admin?
- @by_lang[lang.pretty_name][:first] = {
- avail: true,
- user_id: sub.user_id,
- value: sub.submitted_at,
- sub_id: sub.id
- }
- end
+ #initialize
+ unless @by_lang.has_key?(lang.pretty_name)
+ @by_lang[lang.pretty_name] = {
+ runtime: { avail: false, value: 2**30-1 },
+ memory: { avail: false, value: 2**30-1 },
+ length: { avail: false, value: 2**30-1 },
+ first: { avail: false, value: DateTime.new(3000,1,1) }
+ }
+ end
- if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
- @by_lang[lang.pretty_name][:length] = {
- avail: true,
- user_id: sub.user_id,
- value: sub.effective_code_length,
- sub_id: sub.id
- }
- end
- end
-
- #process user_id
- @by_lang.each do |lang,prop|
- prop.each do |k,v|
- v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
- end
+ if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
+ @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
end
- #sum into best
- if @by_lang and @by_lang.first
- @best = @by_lang.first[1].clone
- @by_lang.each do |lang,prop|
- if @best[:runtime][:value] >= prop[:runtime][:value]
- @best[:runtime] = prop[:runtime]
- @best[:runtime][:lang] = lang
- end
- if @best[:memory][:value] >= prop[:memory][:value]
- @best[:memory] = prop[:memory]
- @best[:memory][:lang] = lang
- end
- if @best[:length][:value] >= prop[:length][:value]
- @best[:length] = prop[:length]
- @best[:length][:lang] = lang
- end
- if @best[:first][:value] >= prop[:first][:value]
- @best[:first] = prop[:first]
- @best[:first][:lang] = lang
- end
+ if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
+ @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
+ end
+
+ if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and
+ !sub.user.admin?
+ @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
+ end
+
+ if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
+ @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
+ end
+ end
+
+ #process user_id
+ @by_lang.each do |lang,prop|
+ prop.each do |k,v|
+ v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
+ end
+ end
+
+ #sum into best
+ if @by_lang and @by_lang.first
+ @best = @by_lang.first[1].clone
+ @by_lang.each do |lang,prop|
+ if @best[:runtime][:value] >= prop[:runtime][:value]
+ @best[:runtime] = prop[:runtime]
+ @best[:runtime][:lang] = lang
+ end
+ if @best[:memory][:value] >= prop[:memory][:value]
+ @best[:memory] = prop[:memory]
+ @best[:memory][:lang] = lang
+ end
+ if @best[:length][:value] >= prop[:length][:value]
+ @best[:length] = prop[:length]
+ @best[:length][:lang] = lang
+ end
+ if @best[:first][:value] >= prop[:first][:value]
+ @best[:first] = prop[:first]
+ @best[:first][:lang] = lang
end
end
end
+
+ @histogram[:summary][:max] = [@histogram[:data].max,1].max
+ @summary[:attempt] = user.count
+ user.each_value { |v| @summary[:solve] += 1 if v == 1 }
end
+
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -111,7 +111,25 @@
def profile
@user = User.find(params[:id])
- @submission = Submission.where(user_id: params[:id]).all
+ @submission = Submission.includes(:problem).where(user_id: params[:id])
+
+ range = 120
+ @histogram = { data: Array.new(range,0), summary: {} }
+ @summary = {count: 0, solve: 0, attempt: 0}
+ problem = Hash.new(0)
+
+ @submission.find_each do |sub|
+ #histogram
+ d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
+ @histogram[:data][d.to_i] += 1 if d < range
+
+ @summary[:count] += 1
+ problem[sub.problem] = [problem[sub.problem], (sub.points >= sub.problem.full_score) ? 1 : 0].max
+ end
+
+ @histogram[:summary][:max] = [@histogram[:data].max,1].max
+ @summary[:attempt] = problem.count
+ problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
end
protected
diff --git a/app/models/problem.rb b/app/models/problem.rb
--- a/app/models/problem.rb
+++ b/app/models/problem.rb
@@ -54,6 +54,13 @@
def self.download_file_basedir
return "#{Rails.root}/data/tasks"
end
+
+ def get_submission_stat
+ result = Hash.new
+ #total number of submission
+ result[:total_sub] = Submission.where(problem_id: self.id).count
+ result[:attempted_user] = Submission.where(problem_id: self.id).group_by(:user_id)
+ end
protected
diff --git a/app/views/application/_bar_graph.html.haml b/app/views/application/_bar_graph.html.haml
new file mode 100644
--- /dev/null
+++ b/app/views/application/_bar_graph.html.haml
@@ -0,0 +1,44 @@
+- param = {} unless param
+- graph_height = param[:graph_height] || 100
+- bar_width = param[:bar_width] || 14
+- graph_width = (bar_width * histogram[:data].count) + 20
+:css
+ .hist_bar {
+ width: #{bar_width-1}px;
+ position: absolute;
+ background-color: lightblue;
+ }
+ .hist_fill {
+ width: #{bar_width-1}px;
+ position: absolute;
+ background-color: #eee;
+ }
+ .hist_text {
+ position: absolute;
+ font-size:5px;
+ }
+
+%div{style: "position: relative; width: #{graph_width}px; height: 125px; background-color:#fff;" }
+ //draw background
+ - histogram[:data].each_index do |i|
+ - height = histogram[:data][i] * graph_height / histogram[:summary][:max]
+ - top = graph_height - height
+ - left = graph_width - (i+1)*bar_width
+ %div.hist_fill{style: "top: 0px; height: #{graph_height - height}px; left: #{left}px;" }
+ // draw horizontal line
+ - line = 3
+ - line.times do |i|
+ - top = graph_height - graph_height * (i+0.5)/ line
+ %div{style: "position:absolute;width: #{graph_width-21}px;height: 1px;left: 20px;top:#{top}px;background-color: #333;"}
+ %div.hist_text{style: "position:absolute;left: 0px;top:#{top-6}px"}
+ =((i+0.5) * histogram[:summary][:max] / line).to_i
+ // draw the actual bar and text
+ - @histogram[:data].each_index do |i|
+ - height = histogram[:data][i] * graph_height / histogram[:summary][:max]
+ - top = graph_height - height
+ - left = graph_width - (i+1)*bar_width
+ %div.hist_bar{style: "top: #{top}px; height: #{height}px; left: #{left}px; dae: #{histogram[:data][i]}" }
+ - if i % 7 == 1
+ %div.hist_text{style: "top:#{graph_height + 5}px;left: #{left}px;"} #{(Time.zone.today - i.day).strftime('%-d')}
+ - if (Time.now.in_time_zone - i.day).day == 15
+ %div.hist_text{style: "top:#{graph_height + 15}px;left: #{left}px;"} #{(Time.zone.today - i.day).strftime('%b')}
diff --git a/app/views/graders/submission.html.haml b/app/views/graders/submission.html.haml
--- a/app/views/graders/submission.html.haml
+++ b/app/views/graders/submission.html.haml
@@ -21,12 +21,18 @@
%tbody
%tr{class: cycle('info-even','info-odd')}
%td.field User:
- %td.value= "(#{@submission.user.login}) #{@submission.user.full_name}"
+ %td.value
+ - if @submission.user
+ = link_to "(#{@submission.user.login})", controller: "users", action: "profile", id: @submission.user
+ = @submission.user.full_name
+ - else
+ = "(n/a)"
%tr{class: cycle('info-even','info-odd')}
%td.field Problem:
%td.value
- if @submission.problem!=nil
- = "(#{@submission.problem.name}) #{@submission.problem.full_name}"
+ = link_to "(#{@submission.problem.name})", controller: "problems", action: "stat", id: @submission.problem
+ = @submission.problem.full_name
- else
= "(n/a)"
%tr{class: cycle('info-even','info-odd')}
diff --git a/app/views/problems/stat.html.haml b/app/views/problems/stat.html.haml
--- a/app/views/problems/stat.html.haml
+++ b/app/views/problems/stat.html.haml
@@ -6,6 +6,23 @@
%h1 Problem stat: #{@problem.name}
%h2 Overview
+
+%table.info
+ %thead
+ %tr.info-head
+ %th Stat
+ %th Value
+ %tbody
+ %tr{class: cycle('info-even','info-odd')}
+ %td Submissions
+ %td= @submissions.count
+ %tr{class: cycle('info-even','info-odd')}
+ %td Solved/Attempted User
+ %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
+
+%h2 Submissions Count
+= render partial: 'application/bar_graph', locals: { histogram: @histogram }
+
%h2 Submissions
- if @submissions and @submissions.count > 0
%table.info#main_table
diff --git a/app/views/report/_task_hof.html.haml b/app/views/report/_task_hof.html.haml
--- a/app/views/report/_task_hof.html.haml
+++ b/app/views/report/_task_hof.html.haml
@@ -1,48 +1,99 @@
+- content_for :header do
+ = javascript_include_tag 'local_jquery'
+
+:javascript
+ $(document).ready( function() {
+ $("#mem_remark").hover( function() {
+ $("#mem_remark_box").show();
+ }, function() {
+ $("#mem_remark_box").hide();
+ });
+ });
+ alert("hahaha");
:css
.hof_user { color: orangered; font-style: italic; }
.hof_language { color: green; font-style: italic; }
.hof_value { color: deeppink;font-style: italic; }
+ .info_param { font-weight: bold;text-align: right; }
+ .tooltip {
+ font-family: Verdana,sans-serif;
+ font-weight: normal;
+ text-align: left;
+ font-size: 1.0em;
+ color: black;
+ line-height: 1.1;
+ display: none;
+ min-width: 20em;
+ position: absolute;
+ left: 25px;
+ bottom: 5px;
+ border: 1px solid;
+ padding: 5px;
+ background-color: #FFF;
+ word-wrap: break-word;
+ z-index: 9999;
+ overflow: auto;
+ }
-%h2 Overall of #{Problem.find(params[:id]).full_name}
+%h1 (#{Problem.find(params[:id]).name}) #{Problem.find(params[:id]).full_name}
+
+%h2 Problem Stat
+%table.info
+ %thead
+ %tr.info-head
+ %th Stat
+ %th Value
+ %tbody
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param Submissions
+ %td= @summary[:count]
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param Solved/Attempted User
+ %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
+ - if @best
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param Best Runtime
+ %td
+ by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
+ using #{@best[:runtime][:lang]}
+ with #{@best[:runtime][:value] * 1000} milliseconds
+ at submission
+ = link_to("#" + @best[:runtime][:sub_id].to_s, controller: 'graders', action: 'submission', id:@best[:runtime][:sub_id])
+
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param
+ Best Memory Usage
+ %sup{ id: "mem_remark", style: "position:relative; color: blue;"}
+ [?]
+ %span.tooltip#mem_remark_box
+ This counts only for submission with 100% score.
+ Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)
+ %td
+ by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
+ using #{@best[:memory][:lang]}
+ with #{number_with_delimiter(@best[:memory][:value])} kbytes
+ at submission
+ = link_to("#" + @best[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id:@best[:memory][:sub_id])
+
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param Shortest Code
+ %td
+ by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]}
+ using #{@best[:length][:lang]}
+ with #{@best[:length][:value]} bytes
+ at submission
+ = link_to("#" + @best[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:length][:sub_id])
+
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param First solver
+ %td
+ #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver
+ using #{@best[:first][:lang]}
+ on #{@best[:first][:value]}
+ at submission
+ = link_to("#" + @best[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:first][:sub_id])
- if @best
- %b Best Runtime:
- by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
- using #{@best[:runtime][:lang]}
- with #{@best[:runtime][:value] * 1000} milliseconds
- at submission
- = link_to("#" + @best[:runtime][:sub_id].to_s, controller: 'graders', action: 'submission', id:@best[:runtime][:sub_id])
- %br/
-
- %b Best Memory Usage:
- by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
- using #{@best[:memory][:lang]}
- with #{number_with_delimiter(@best[:memory][:value])} kbytes
- at submission
- = link_to("#" + @best[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id:@best[:memory][:sub_id])
- %br/
-
- %b Shortest Code:
- by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]}
- using #{@best[:length][:lang]}
- with #{@best[:length][:value]} bytes
- at submission
- = link_to("#" + @best[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:length][:sub_id])
- %br/
-
- %b First solver:
- #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver
- using #{@best[:first][:lang]}
- on #{@best[:first][:value]}
- at submission
- = link_to("#" + @best[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:first][:sub_id])
- %br/
-
-
- %p
- This counts only for submission with 100% score
- Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)
-
%h2 By language
%table.info
@@ -75,5 +126,3 @@
= "(#{value[:first][:value]} @"
= "#{link_to("#" + value[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:first][:sub_id])} )".html_safe
-- else
- %h3 No submissions
diff --git a/app/views/report/problem_hof.html.haml b/app/views/report/problem_hof.html.haml
--- a/app/views/report/problem_hof.html.haml
+++ b/app/views/report/problem_hof.html.haml
@@ -6,7 +6,7 @@
/ %h1 All-Time Hall of Fame
-%h1 Tasks Hall of Fame
+%h1 Hall of Fame
.task-menu
Tasks
%br/
@@ -18,5 +18,6 @@
Please select a problem.
- else
=render partial: 'task_hof'
+ %h2 Submission History
+ =render partial: 'application/bar_graph', locals: { histogram: @histogram }
-
diff --git a/app/views/users/profile.html.haml b/app/views/users/profile.html.haml
--- a/app/views/users/profile.html.haml
+++ b/app/views/users/profile.html.haml
@@ -1,9 +1,9 @@
- content_for :header do
= javascript_include_tag 'local_jquery'
-%script{:type=>"text/javascript"}
+:javascript
$(function () {
- $('#submission_table').tablesorter({widgets: ['zebra']});
+ $('#submission_table').tablesorter({widgets: ['zebra']});
});
:css
@@ -11,16 +11,30 @@
font-family: Droid Sans Mono,Consolas, monospace, mono, Courier New, Courier;
}
-%h1= @user.full_name + ' Profile'
+%h1= @user.full_name
-%h2 Basic info
Login: #{@user.login}
Full name: #{@user.full_name}
%h2 Problem Stat
+%table.info
+ %thead
+ %tr.info-head
+ %th Stat
+ %th Value
+ %tbody
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param Submissions
+ %td= @summary[:count]
+ %tr{class: cycle('info-even','info-odd')}
+ %td.info_param Solved/Attempted Problem
+ %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
-%h2 Submissions
+%h2 Submission History
+
+=render partial: 'application/bar_graph', locals: {histogram: @histogram, param: {bar_width: 7}}
+
%table.tablesorter-cafe#submission_table
%thead
@@ -39,7 +53,7 @@
- next unless s.problem
%tr
%td= link_to "#{s.id}", controller: "graders", action: "submission", id: s.id
- %td= s.problem.name
+ %td= link_to s.problem.name, controller: "problems", action: "stat", id: s.problem
%td= s.problem.full_name
%td= s.language.pretty_name
%td #{s.submitted_at.strftime('%Y-%m-%d %H:%M')} (#{time_ago_in_words(s.submitted_at)} ago)
diff --git a/lib/grader_script.rb b/lib/grader_script.rb
--- a/lib/grader_script.rb
+++ b/lib/grader_script.rb
@@ -29,8 +29,8 @@
end
def self.start_grader(env)
- GraderScript.call_grader "#{env} queue &"
- GraderScript.call_grader "#{env} test_request &"
+ GraderScript.call_grader "#{env} queue --err-log &"
+ GraderScript.call_grader "#{env} test_request -err-log &"
end
def self.call_import_problem(problem_name,