Description:
update master
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r661:50e8e6077bb9 - - 146 files changed: 1465 inserted, 2374 deleted

@@ -0,0 +1,32
1 + class TestcasesController < ApplicationController
2 + before_action :set_testcase, only: [:download_input,:download_sol]
3 + before_action :testcase_authorization
4 +
5 + def download_input
6 + send_data @testcase.input, type: 'text/plain', filename: "#{@testcase.problem.name}.#{@testcase.num}.in"
7 + end
8 +
9 + def download_sol
10 + send_data @testcase.sol, type: 'text/plain', filename: "#{@testcase.problem.name}.#{@testcase.num}.sol"
11 + end
12 +
13 + def show_problem
14 + @problem = Problem.includes(:testcases).find(params[:problem_id])
15 + unless @current_user.admin? or @problem.view_testcase
16 + flash[:error] = 'You cannot view the testcase of this problem'
17 + redirect_to :controller => 'main', :action => 'list'
18 + end
19 + end
20 +
21 +
22 + private
23 + # Use callbacks to share common setup or constraints between actions.
24 + def set_testcase
25 + @testcase = Testcase.find(params[:id])
26 + end
27 +
28 + # Only allow a trusted parameter "white list" through.
29 + def testcase_params
30 + params[:testcase]
31 + end
32 + end
@@ -0,0 +1,2
1 + module TestcasesHelper
2 + end
@@ -0,0 +1,2
1 + = render partial: 'toggle_button',
2 + locals: {button_id: "#problem-view-testcase-#{@problem.id}",button_on: @problem.view_testcase?}
@@ -0,0 +1,4
1 + :plain
2 + $("#compiler_msg").html("#{j @submission.compiler_message}");
3 + $("#compiler").modal();
4 +
@@ -0,0 +1,2
1 + :plain
2 + $("body").prepend("<div class=\"alert alert-info\"> Submission #{@submission.id}'s task status has been changed to \"#{@task.status_str}\". It will be re-judged soon. </div>")
@@ -0,0 +1,25
1 + %h1 Test cases
2 + %h2= @problem.long_name
3 +
4 + /navbar
5 + %ul.nav.nav-pills{role: :tablist}
6 + - @problem.testcases.each.with_index do |tc,id|
7 + %li{role: :presentation, class: ('active' if id == 0)}
8 + %a{href:"#tc#{tc.id}", role: 'tab', data: {toggle: 'tab'}}= tc.num
9 +
10 + /actual data
11 + .tab-content
12 + - @problem.testcases.each.with_index do |tc,id|
13 + .tab-pane{id: "tc#{tc.id}",class: ('active' if id == 0)}
14 + .row
15 + .col-md-6
16 + %h3 Input
17 + = link_to "Download",download_input_testcase_path(tc),class: 'btn btn-info btn-sm'
18 + .col-md-6
19 + %h3 Output
20 + = link_to "Download",download_sol_testcase_path(tc),class: 'btn btn-info btn-sm'
21 + .row
22 + .col-md-6
23 + %textarea{ rows: 25,readonly: true,style: "width:100%;resize=none;overflow-y: scroll;"}= tc.input
24 + .col-md-6
25 + %textarea{ rows: 25,readonly: true,style: "width:100%;resize=none;overflow-y: scroll;"}= tc.sol
@@ -0,0 +1,77
1 + %h1 Bulk Manage User
2 +
3 + = form_tag bulk_manage_user_admin_path
4 + .row
5 + .col-md-6
6 + .panel.panel-primary
7 + .panel-title.panel-heading
8 + Filter User
9 + .panel-body
10 + Filtering users whose login match the following MySQL regex
11 + .form-group
12 + = label_tag "regex", 'Regex Pattern'
13 + = text_field_tag "regex", params[:regex], class: 'form-control'
14 + %p
15 + Example
16 + %ul
17 + %li
18 + %code root
19 + matches every user whose login contains "root"
20 + %li
21 + %code ^56
22 + matches every user whose login starts with "56"
23 + %li
24 + %code 21$
25 + matches every user whose login ends with "21"
26 + .col-md-6
27 + .panel.panel-primary
28 + .panel-title.panel-heading
29 + Action
30 + .panel-body
31 + .row.form-group
32 + .col-md-6
33 + %label.checkbox-inline
34 + = check_box_tag "enabled", true, params[:enabled]
35 + Change "Enabled" to
36 + .col-md-3
37 + %label.radio-inline
38 + = radio_button_tag "enable", 1, params[:enable] == '1', id: 'enable-yes'
39 + Yes
40 + .col-md-3
41 + %label.radio-inline
42 + = radio_button_tag "enable", 0, params[:enable] == '0', id: 'enable-no'
43 + No
44 + .row.form-group
45 + .col-md-6
46 + %label.checkbox-inline
47 + = check_box_tag "gen_password", true, params[:gen_password]
48 + Generate new random password
49 +
50 + .row
51 + .col-md-12
52 + = submit_tag "Preview Result", class: 'btn btn-default'
53 + - if @users
54 + .row
55 + .col-md-4
56 + - if @action
57 + %h2 Confirmation
58 + - if @action[:set_enable]
59 + .alert.alert-info The following users will be set #{(@action[:enabled] ? 'enable' : 'disable')}.
60 + - if @action[:gen_password]
61 + .alert.alert-info The password of the following users will be randomly generated.
62 + .row
63 + .col-md-4
64 + = submit_tag "Perform", class: 'btn btn-primary'
65 + .row
66 + .col-md-12
67 + The pattern matches #{@users.count} following users.
68 + %br
69 + - @users.each do |user|
70 + = user.login
71 + = ' '
72 + = user.full_name
73 + = ' '
74 + = "(#{user.remark})" if user.remark
75 + %br
76 +
77 +
@@ -0,0 +1,8
1 + development:
2 + secret_key_base: '444f426d08add8e2d7cbd76e2057e521e06091231eb4d5472af6ba5654ea1124ce6a636f549be6827ce09561c314181226ad840d44e4677e1077942ee0dc82bd'
3 +
4 + test:
5 + secret_key_base: 'd52f411b06a79cc9f56d92e10d27e670cf0f0c3357e7caf9018ec23091b5c452ea9266c03a5c9e37b72c358702d4d460e957f90dcc553c9fc73a98adb520e781'
6 +
7 + production:
8 + secret_key_base: '7f85485d3d652fc6336dfe3cd98917d9bd7a323b32096bf7635d26b98ccd0480816cc2d12b5c10805cecf7d8fb322104e2bda71eb60dd871c5c537e56a063038'
@@ -0,0 +1,16
1 + class AddConfigViewTest < ActiveRecord::Migration
2 + def up
3 + GraderConfiguration.create key: 'right.view_testcase', value_type: 'boolean', value:'true', description:'When true, any user can view/download test data'
4 + #uglily and dirtily and shamelessly check other config and inifialize
5 + GraderConfiguration.where(key: 'right.user_hall_of_fame').first_or_create(value_type: 'boolean', value: 'false',
6 + description: 'If true, any user can access hall of fame page.')
7 + GraderConfiguration.where(key: 'right.multiple_ip_login').first_or_create(value_type: 'boolean', value: 'false',
8 + description: 'When change from true to false, a user can login from the first IP they logged into afterward.')
9 + GraderConfiguration.where(key: 'right.user_view_submission').first_or_create(value_type: 'boolean', value: 'false',
10 + description: 'If true, any user can view submissions of every one.')
11 + end
12 +
13 + def down
14 + GraderConfiguration.where(key: 'right.view_testcase').destroy_all;
15 + end
16 + end
@@ -0,0 +1,6
1 + class ChangeTestcaseSize < ActiveRecord::Migration
2 + def change
3 + change_column :testcases, :input, :text, :limit => 4294967295
4 + change_column :testcases, :sol, :text, :limit => 4294967295
5 + end
6 + end
@@ -0,0 +1,5
1 + class AddViewTestcaseToProblem < ActiveRecord::Migration
2 + def change
3 + add_column :problems, :view_testcase, :bool
4 + end
5 + end
@@ -0,0 +1,5
1 + class AddIndexToTask < ActiveRecord::Migration
2 + def change
3 + add_index :tasks, :submission_id
4 + end
5 + end
@@ -0,0 +1,9
1 + class AddHeartBeatFull < ActiveRecord::Migration
2 + def up
3 + GraderConfiguration.create key: 'right.heartbeat_response_full', value_type: 'string', value:'RESTART', description:'Heart beat response text when user got full score (set this value to the empty string to disable this feature)'
4 + end
5 +
6 + def down
7 +
8 + end
9 + end
@@ -0,0 +1,41
1 + # Original from http://snippets.dzone.com/posts/show/4468 by MichaelBoutros
2 + #
3 + # Optimized version which uses to_yaml for content creation and checks
4 + # that models are ActiveRecord::Base models before trying to fetch
5 + # them from database.
6 + namespace :db do
7 + namespace :fixtures do
8 + desc 'Dumps all models into fixtures.'
9 + task :dump => :environment do
10 + puts "rails root = #{Rails.root}"
11 + models = Dir.glob(Rails.root.to_s + '/app/models/**.rb').map do |s|
12 + Pathname.new(s).basename.to_s.gsub(/\.rb$/,'').camelize
13 + end
14 +
15 + puts "Found models: " + models.join(', ')
16 +
17 + models.each do |m|
18 + model = m.constantize
19 + next unless model.ancestors.include?(ActiveRecord::Base)
20 +
21 + puts "Dumping model: " + m
22 + entries = model.all.order(id: :asc)
23 +
24 + increment = 1
25 +
26 + model_file = Rails.root.to_s + '/test/fixtures2/' + m.underscore.pluralize + '.yml'
27 + File.open(model_file, 'w') do |f|
28 + entries.each do |a|
29 + attrs = a.attributes
30 + attrs.delete_if{|k,v| v.blank?}
31 +
32 + output = {m + '_' + increment.to_s => attrs}
33 + f << output.to_yaml.gsub(/^--- \n/,'') + "\n"
34 +
35 + increment += 1
36 + end
37 + end
38 + end
39 + end
40 + end
41 + end
@@ -0,0 +1,50
1 + require 'test_helper'
2 +
3 + class AnnouncementsControllerTest < ActionController::TestCase
4 + setup do
5 + @announcement = announcements(:one)
6 + @request.session[:user_id] = users(:admin).id
7 + end
8 +
9 + test "should get index" do
10 + get :index
11 + assert_response :success
12 + assert_not_nil assigns(:announcements)
13 + end
14 +
15 + test "should get new" do
16 + get :new
17 + assert_response :success
18 + end
19 +
20 + test "should create announcement" do
21 + assert_difference('Announcement.count') do
22 + post :create, announcement: { author: 'aa',body: 'bb', published: true, frontpage: true, title: 'test'}
23 + end
24 +
25 + assert_redirected_to announcement_path(assigns(:announcement))
26 + end
27 +
28 + test "should show announcement" do
29 + get :show, id: @announcement
30 + assert_response :success
31 + end
32 +
33 + test "should get edit" do
34 + get :edit, id: @announcement
35 + assert_response :success
36 + end
37 +
38 + test "should update announcement" do
39 + patch :update, id: @announcement, announcement: { author: 'aa',body: 'bb', published: true, frontpage: true, title: 'test'}
40 + assert_redirected_to announcement_path(assigns(:announcement))
41 + end
42 +
43 + test "should destroy announcement" do
44 + assert_difference('Announcement.count', -1) do
45 + delete :destroy, id: @announcement
46 + end
47 +
48 + assert_redirected_to announcements_path
49 + end
50 + end
@@ -0,0 +1,7
1 + require 'test_helper'
2 +
3 + class TestcasesControllerTest < ActionController::TestCase
4 + setup do
5 + @testcase = testcases(:one)
6 + end
7 + end
@@ -0,0 +1,144
1 + GraderConfiguration_1:
2 + key: system.single_user_mode
3 + value_type: boolean
4 + value: 'false'
5 + description: Only admins can log in to the system when running under single user mode.
6 +
7 + GraderConfiguration_2:
8 + key: ui.front.title
9 + value_type: string
10 + value: Grader
11 +
12 + GraderConfiguration_3:
13 + key: ui.front.welcome_message
14 + value_type: string
15 + value: Welcome!
16 +
17 + GraderConfiguration_4:
18 + key: ui.show_score
19 + value_type: boolean
20 + value: 'true'
21 +
22 + GraderConfiguration_5:
23 + key: contest.time_limit
24 + value_type: string
25 + value: unlimited
26 + description: Time limit in format hh:mm, or "unlimited" for contests with no time
27 + limits. This config is CACHED. Restart the server before the change can take
28 + effect.
29 +
30 + GraderConfiguration_6:
31 + key: system.mode
32 + value_type: string
33 + value: standard
34 + description: Current modes are "standard", "contest", "indv-contest", and "analysis".
35 +
36 +
37 + GraderConfiguration_7:
38 + key: contest.name
39 + value_type: string
40 + value: Grader
41 + description: This name will be shown on the user header bar.
42 +
43 +
44 + GraderConfiguration_8:
45 + key: contest.multisites
46 + value_type: boolean
47 + value: 'false'
48 + description: If the server is in contest mode and this option is true, on the log
49 + in of the admin a menu for site selections is shown.
50 +
51 +
52 + GraderConfiguration_9:
53 + key: right.user_hall_of_fame
54 + value_type: boolean
55 + value: 'false'
56 + description: If true, any user can access hall of fame page.
57 +
58 +
59 + GraderConfiguration_10:
60 + key: right.multiple_ip_login
61 + value_type: boolean
62 + value: 'true'
63 + description: When change from true to false, a user can login from the first IP
64 + they logged into afterward.
65 +
66 +
67 + GraderConfiguration_11:
68 + key: right.user_view_submission
69 + value_type: boolean
70 + value: 'false'
71 + description: If true, any user can view submissions of every one.
72 +
73 +
74 + GraderConfiguration_12:
75 + key: right.bypass_agreement
76 + value_type: boolean
77 + value: 'true'
78 + description: When false, a user must accept usage agreement before login
79 +
80 +
81 + GraderConfiguration_13:
82 + key: right.heartbeat_response
83 + value_type: string
84 + value: OK
85 + description: Heart beat response text
86 +
87 +
88 + GraderConfiguration_14:
89 + key: right.view_testcase
90 + value_type: boolean
91 + value: 'false'
92 + description: When true, any user can view/download test data
93 +
94 +
95 + GraderConfiguration_15:
96 + key: system.online_registration.smtp
97 + value_type: string
98 + value: smtp.somehost.com
99 +
100 +
101 + GraderConfiguration_16:
102 + key: system.online_registration.from
103 + value_type: string
104 + value: your.email@address
105 +
106 +
107 + GraderConfiguration_17:
108 + key: system.admin_email
109 + value_type: string
110 + value: admin@admin.email
111 +
112 +
113 + GraderConfiguration_18:
114 + key: system.user_setting_enabled
115 + value_type: boolean
116 + value: 'true'
117 + description: If this option is true, users can change their settings
118 +
119 +
120 + GraderConfiguration_19:
121 + key: contest.test_request.early_timeout
122 + value_type: boolean
123 + value: 'false'
124 +
125 +
126 + GraderConfiguration_20:
127 + key: system.multicontests
128 + value_type: boolean
129 + value: 'false'
130 +
131 +
132 + GraderConfiguration_21:
133 + key: contest.confirm_indv_contest_start
134 + value_type: boolean
135 + value: 'false'
136 +
137 +
138 + GraderConfiguration_22:
139 + key: contest.default_contest_name
140 + value_type: string
141 + value: none
142 + description: New user will be assigned to this contest automatically, if it exists. Set
143 + to 'none' if there is no default contest.
144 +
@@ -0,0 +1,7
1 + require 'test_helper'
2 +
3 + class AdminTaskTest < ActionDispatch::IntegrationTest
4 + # test "the truth" do
5 + # assert true
6 + # end
7 + end
@@ -0,0 +1,40
1 + require 'test_helper'
2 +
3 + class LoginTest < ActionDispatch::IntegrationTest
4 + # test "the truth" do
5 + # assert true
6 + # end
7 +
8 + test "login with invalid information" do
9 + get root_path
10 + assert_response :success
11 + post login_login_path, login: "root", password: "hahaha"
12 + assert_redirected_to root_path
13 + end
14 +
15 + test "normal user login" do
16 + get root_path
17 + assert_response :success
18 + post login_login_path, {login: "john", password: "hello" }
19 + assert_redirected_to main_list_path
20 + end
21 +
22 + test "normal user login in single_user mode" do
23 + GraderConfiguration.find_by(key: GraderConfiguration::SINGLE_USER_KEY).update_attributes(value: 'true')
24 + GraderConfiguration.reload
25 + get root_path
26 + assert_response :success
27 + post login_login_path, {login: "john", password: "hello" }
28 + follow_redirect!
29 + assert_redirected_to root_path
30 + end
31 +
32 + test "root login in in single_user mode" do
33 + GraderConfiguration.find_by(key: GraderConfiguration::SINGLE_USER_KEY).update_attributes(value: 'true')
34 + GraderConfiguration.reload
35 + get root_path
36 + assert_response :success
37 + post login_login_path, {login: "admin", password: "admin" }
38 + assert_redirected_to main_list_path
39 + end
40 + end
@@ -1,33 +1,35
1 # See http://help.github.com/ignore-files/ for more about ignoring files.
1 # See http://help.github.com/ignore-files/ for more about ignoring files.
2 #
2 #
3 # If you find yourself ignoring temporary files generated by your text editor
3 # If you find yourself ignoring temporary files generated by your text editor
4 # or operating system, you probably want to add a global ignore instead:
4 # or operating system, you probably want to add a global ignore instead:
5 # git config --global core.excludesfile ~/.gitignore_global
5 # git config --global core.excludesfile ~/.gitignore_global
6
6
7 # Ignore bundler config
7 # Ignore bundler config
8 /.bundle
8 /.bundle
9
9
10 # Ignore the default SQLite database.
10 # Ignore the default SQLite database.
11 /db/*.sqlite3
11 /db/*.sqlite3
12
12
13 # Ignore all logfiles and tempfiles.
13 # Ignore all logfiles and tempfiles.
14 /log/*.log
14 /log/*.log
15 /tmp
15 /tmp
16
16
17 *~
17 *~
18
18
19 /vendor/plugins/rails_upgrade
19 /vendor/plugins/rails_upgrade
20
20
21 #ignore public assets???
21 #ignore public assets???
22 /public/assets
22 /public/assets
23 /public
23 /public
24
24
25 /data
25 /data
26
26
27 #ignore .orig and .swp
27 #ignore .orig and .swp
28 *.orig
28 *.orig
29 *.swp
29 *.swp
30
30
31 #ignore rvm setting file
31 #ignore rvm setting file
32 .ruby-gemset
32 .ruby-gemset
33 .ruby-version
33 .ruby-version
34 +
35 + /config/secrets.yml
@@ -1,80 +1,91
1 source 'https://rubygems.org'
1 source 'https://rubygems.org'
2
2
3 - gem 'rails', '~>3.2'
3 + #rails
4 + gem 'rails', '~>4.2.0'
5 + gem 'activerecord-session_store'
4
6
5 - gem 'select2-rails'
6
7
7 # Bundle edge Rails instead:
8 # Bundle edge Rails instead:
8 # gem 'rails', :git => 'git://github.com/rails/rails.git'
9 # gem 'rails', :git => 'git://github.com/rails/rails.git'
9
10
11 + #---------------- database ---------------------
12 + #the database
10 gem 'mysql2'
13 gem 'mysql2'
14 + #for testing
15 + gem 'sqlite3'
16 + #for dumping database into yaml
17 + gem 'yaml_db'
11
18
12 # Gems used only for assets and not required
19 # Gems used only for assets and not required
13 # in production environments by default.
20 # in production environments by default.
14 - group :assets do
21 + gem 'sass-rails'
15 - gem 'sass-rails', '~> 3.2.6'
22 + gem 'coffee-rails'
16 - gem 'coffee-rails', '~> 3.2.2'
17
23
18 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
24 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
19 # gem 'therubyracer', :platforms => :ruby
25 # gem 'therubyracer', :platforms => :ruby
20
26
21 - gem 'uglifier'
27 + gem 'uglifier'
22 - end
23
28
24 - gem 'prototype-rails'
29 + gem 'haml'
30 + gem 'haml-rails'
31 + # gem 'prototype-rails'
25
32
26 # To use ActiveModel has_secure_password
33 # To use ActiveModel has_secure_password
27 # gem 'bcrypt-ruby', '~> 3.0.0'
34 # gem 'bcrypt-ruby', '~> 3.0.0'
28
35
29 # To use Jbuilder templates for JSON
36 # To use Jbuilder templates for JSON
30 # gem 'jbuilder'
37 # gem 'jbuilder'
31
38
32 # Use unicorn as the app server
39 # Use unicorn as the app server
33 # gem 'unicorn'
40 # gem 'unicorn'
34
41
35 # Deploy with Capistrano
42 # Deploy with Capistrano
36 # gem 'capistrano'
43 # gem 'capistrano'
37
44
38 # To use debugger
45 # To use debugger
39 # gem 'debugger'
46 # gem 'debugger'
40 #
47 #
41
48
42 #in-place editor
49 #in-place editor
43 gem 'best_in_place', '~> 3.0.1'
50 gem 'best_in_place', '~> 3.0.1'
44
51
45 # jquery addition
52 # jquery addition
46 gem 'jquery-rails'
53 gem 'jquery-rails'
47 - gem 'jquery-ui-sass-rails'
54 + gem 'jquery-ui-rails'
48 gem 'jquery-timepicker-addon-rails'
55 gem 'jquery-timepicker-addon-rails'
49 gem 'jquery-tablesorter'
56 gem 'jquery-tablesorter'
50 gem 'jquery-countdown-rails'
57 gem 'jquery-countdown-rails'
51
58
52 #syntax highlighter
59 #syntax highlighter
53 gem 'rouge'
60 gem 'rouge'
54
61
55 #add bootstrap
62 #add bootstrap
56 gem 'bootstrap-sass', '~> 3.2.0'
63 gem 'bootstrap-sass', '~> 3.2.0'
57 gem 'bootstrap-switch-rails'
64 gem 'bootstrap-switch-rails'
58 gem 'bootstrap-toggle-rails'
65 gem 'bootstrap-toggle-rails'
59 gem 'autoprefixer-rails'
66 gem 'autoprefixer-rails'
60
67
61 #bootstrap sortable
68 #bootstrap sortable
62 gem 'momentjs-rails'
69 gem 'momentjs-rails'
63 gem 'rails_bootstrap_sortable'
70 gem 'rails_bootstrap_sortable'
64
71
72 + #----------- user interface -----------------
73 + #select 2
74 + gem 'select2-rails'
65 #ace editor
75 #ace editor
66 gem 'ace-rails-ap'
76 gem 'ace-rails-ap'
77 + #paginator
78 + gem 'will_paginate', '~> 3.0.7'
67
79
68 - gem 'haml'
69 - gem 'haml-rails'
70 gem 'mail'
80 gem 'mail'
71 gem 'rdiscount'
81 gem 'rdiscount'
72 - gem 'test-unit'
73 - gem 'will_paginate', '~> 3.0.7'
74 gem 'dynamic_form'
82 gem 'dynamic_form'
75 gem 'in_place_editing'
83 gem 'in_place_editing'
76 gem 'verification', :git => 'https://github.com/sikachu/verification.git'
84 gem 'verification', :git => 'https://github.com/sikachu/verification.git'
77
85
78 - group :test, :development do
86 +
79 - gem 'rspec-rails', '~> 2.99.0'
87 + #---------------- testiing -----------------------
80 - end
88 + gem 'minitest-reporters'
89 +
90 + #---------------- for console --------------------
91 + gem 'fuzzy-string-match'
@@ -1,208 +1,235
1 GIT
1 GIT
2 remote: https://github.com/sikachu/verification.git
2 remote: https://github.com/sikachu/verification.git
3 - revision: 76eaf51b13276ecae54bd9cd115832595d2ff56d
3 + revision: ff31697b940d7b0e2ec65f08764215c96104e76d
4 specs:
4 specs:
5 verification (1.0.3)
5 verification (1.0.3)
6 - actionpack (>= 3.0.0, < 5.0)
6 + actionpack (>= 3.0.0, < 5.1)
7 - activesupport (>= 3.0.0, < 5.0)
7 + activesupport (>= 3.0.0, < 5.1)
8
8
9 GEM
9 GEM
10 remote: https://rubygems.org/
10 remote: https://rubygems.org/
11 specs:
11 specs:
12 - ace-rails-ap (4.0.2)
12 + RubyInline (3.12.4)
13 - actionmailer (3.2.22.5)
13 + ZenTest (~> 4.3)
14 - actionpack (= 3.2.22.5)
14 + ZenTest (4.11.1)
15 - mail (~> 2.5.4)
15 + ace-rails-ap (4.1.1)
16 - actionpack (3.2.22.5)
16 + actionmailer (4.2.7.1)
17 - activemodel (= 3.2.22.5)
17 + actionpack (= 4.2.7.1)
18 - activesupport (= 3.2.22.5)
18 + actionview (= 4.2.7.1)
19 - builder (~> 3.0.0)
19 + activejob (= 4.2.7.1)
20 + mail (~> 2.5, >= 2.5.4)
21 + rails-dom-testing (~> 1.0, >= 1.0.5)
22 + actionpack (4.2.7.1)
23 + actionview (= 4.2.7.1)
24 + activesupport (= 4.2.7.1)
25 + rack (~> 1.6)
26 + rack-test (~> 0.6.2)
27 + rails-dom-testing (~> 1.0, >= 1.0.5)
28 + rails-html-sanitizer (~> 1.0, >= 1.0.2)
29 + actionview (4.2.7.1)
30 + activesupport (= 4.2.7.1)
31 + builder (~> 3.1)
20 erubis (~> 2.7.0)
32 erubis (~> 2.7.0)
21 - journey (~> 1.0.4)
33 + rails-dom-testing (~> 1.0, >= 1.0.5)
22 - rack (~> 1.4.5)
34 + rails-html-sanitizer (~> 1.0, >= 1.0.2)
23 - rack-cache (~> 1.2)
35 + activejob (4.2.7.1)
24 - rack-test (~> 0.6.1)
36 + activesupport (= 4.2.7.1)
25 - sprockets (~> 2.2.1)
37 + globalid (>= 0.3.0)
26 - activemodel (3.2.22.5)
38 + activemodel (4.2.7.1)
27 - activesupport (= 3.2.22.5)
39 + activesupport (= 4.2.7.1)
28 - builder (~> 3.0.0)
40 + builder (~> 3.1)
29 - activerecord (3.2.22.5)
41 + activerecord (4.2.7.1)
30 - activemodel (= 3.2.22.5)
42 + activemodel (= 4.2.7.1)
31 - activesupport (= 3.2.22.5)
43 + activesupport (= 4.2.7.1)
32 - arel (~> 3.0.2)
44 + arel (~> 6.0)
33 - tzinfo (~> 0.3.29)
45 + activerecord-session_store (1.0.0)
34 - activeresource (3.2.22.5)
46 + actionpack (>= 4.0, < 5.1)
35 - activemodel (= 3.2.22.5)
47 + activerecord (>= 4.0, < 5.1)
36 - activesupport (= 3.2.22.5)
48 + multi_json (~> 1.11, >= 1.11.2)
37 - activesupport (3.2.22.5)
49 + rack (>= 1.5.2, < 3)
38 - i18n (~> 0.6, >= 0.6.4)
50 + railties (>= 4.0, < 5.1)
39 - multi_json (~> 1.0)
51 + activesupport (4.2.7.1)
40 - arel (3.0.3)
52 + i18n (~> 0.7)
41 - autoprefixer-rails (6.0.3)
53 + json (~> 1.7, >= 1.7.7)
54 + minitest (~> 5.1)
55 + thread_safe (~> 0.3, >= 0.3.4)
56 + tzinfo (~> 1.1)
57 + ansi (1.5.0)
58 + arel (6.0.4)
59 + autoprefixer-rails (6.6.0)
42 execjs
60 execjs
43 - json
44 best_in_place (3.0.3)
61 best_in_place (3.0.3)
45 actionpack (>= 3.2)
62 actionpack (>= 3.2)
46 railties (>= 3.2)
63 railties (>= 3.2)
47 bootstrap-sass (3.2.0.2)
64 bootstrap-sass (3.2.0.2)
48 sass (~> 3.2)
65 sass (~> 3.2)
49 bootstrap-switch-rails (3.3.3)
66 bootstrap-switch-rails (3.3.3)
50 bootstrap-toggle-rails (2.2.1.0)
67 bootstrap-toggle-rails (2.2.1.0)
51 - builder (3.0.4)
68 + builder (3.2.2)
52 - coffee-rails (3.2.2)
69 + coffee-rails (4.2.1)
53 coffee-script (>= 2.2.0)
70 coffee-script (>= 2.2.0)
54 - railties (~> 3.2.0)
71 + railties (>= 4.0.0, < 5.2.x)
55 - coffee-script (2.3.0)
72 + coffee-script (2.4.1)
56 coffee-script-source
73 coffee-script-source
57 execjs
74 execjs
58 - coffee-script-source (1.9.0)
75 + coffee-script-source (1.12.2)
59 - diff-lcs (1.2.5)
76 + concurrent-ruby (1.0.4)
60 dynamic_form (1.1.4)
77 dynamic_form (1.1.4)
61 erubis (2.7.0)
78 erubis (2.7.0)
62 - execjs (2.3.0)
79 + execjs (2.7.0)
63 - haml (4.0.6)
80 + fuzzy-string-match (1.0.0)
81 + RubyInline (>= 3.8.6)
82 + globalid (0.3.7)
83 + activesupport (>= 4.1.0)
84 + haml (4.0.7)
64 tilt
85 tilt
65 - haml-rails (0.4)
86 + haml-rails (0.9.0)
66 - actionpack (>= 3.1, < 4.1)
87 + actionpack (>= 4.0.1)
67 - activesupport (>= 3.1, < 4.1)
88 + activesupport (>= 4.0.1)
68 - haml (>= 3.1, < 4.1)
89 + haml (>= 4.0.6, < 5.0)
69 - railties (>= 3.1, < 4.1)
90 + html2haml (>= 1.0.1)
70 - hike (1.2.3)
91 + railties (>= 4.0.1)
92 + html2haml (2.0.0)
93 + erubis (~> 2.7.0)
94 + haml (~> 4.0.0)
95 + nokogiri (~> 1.6.0)
96 + ruby_parser (~> 3.5)
71 i18n (0.7.0)
97 i18n (0.7.0)
72 in_place_editing (1.2.0)
98 in_place_editing (1.2.0)
73 - journey (1.0.4)
74 jquery-countdown-rails (2.0.2)
99 jquery-countdown-rails (2.0.2)
75 - jquery-rails (3.1.2)
100 + jquery-rails (4.2.1)
76 - railties (>= 3.0, < 5.0)
101 + rails-dom-testing (>= 1, < 3)
102 + railties (>= 4.2.0)
77 thor (>= 0.14, < 2.0)
103 thor (>= 0.14, < 2.0)
78 - jquery-tablesorter (1.13.4)
104 + jquery-tablesorter (1.23.3)
79 - railties (>= 3.1, < 5)
105 + railties (>= 3.2, < 6)
80 jquery-timepicker-addon-rails (1.4.1)
106 jquery-timepicker-addon-rails (1.4.1)
81 railties (>= 3.1)
107 railties (>= 3.1)
82 - jquery-ui-rails (4.0.3)
108 + jquery-ui-rails (6.0.1)
83 - jquery-rails
109 + railties (>= 3.2.16)
84 - railties (>= 3.1.0)
85 - jquery-ui-sass-rails (4.0.3.0)
86 - jquery-rails
87 - jquery-ui-rails (= 4.0.3)
88 - railties (>= 3.1.0)
89 json (1.8.3)
110 json (1.8.3)
90 - mail (2.5.4)
111 + loofah (2.0.3)
91 - mime-types (~> 1.16)
112 + nokogiri (>= 1.5.9)
92 - treetop (~> 1.4.8)
113 + mail (2.6.4)
93 - mime-types (1.25.1)
114 + mime-types (>= 1.16, < 4)
94 - momentjs-rails (2.11.1)
115 + mime-types (3.1)
116 + mime-types-data (~> 3.2015)
117 + mime-types-data (3.2016.0521)
118 + mini_portile2 (2.1.0)
119 + minitest (5.10.1)
120 + minitest-reporters (1.1.13)
121 + ansi
122 + builder
123 + minitest (>= 5.0)
124 + ruby-progressbar
125 + momentjs-rails (2.15.1)
95 railties (>= 3.1)
126 railties (>= 3.1)
96 multi_json (1.12.1)
127 multi_json (1.12.1)
97 - mysql2 (0.3.20)
128 + mysql2 (0.4.5)
98 - polyglot (0.3.5)
129 + nokogiri (1.6.8.1)
99 - power_assert (0.2.2)
130 + mini_portile2 (~> 2.1.0)
100 - prototype-rails (3.2.1)
131 + rack (1.6.5)
101 - rails (~> 3.2)
102 - rack (1.4.7)
103 - rack-cache (1.6.1)
104 - rack (>= 0.4)
105 - rack-ssl (1.3.4)
106 - rack
107 rack-test (0.6.3)
132 rack-test (0.6.3)
108 rack (>= 1.0)
133 rack (>= 1.0)
109 - rails (3.2.22.5)
134 + rails (4.2.7.1)
110 - actionmailer (= 3.2.22.5)
135 + actionmailer (= 4.2.7.1)
111 - actionpack (= 3.2.22.5)
136 + actionpack (= 4.2.7.1)
112 - activerecord (= 3.2.22.5)
137 + actionview (= 4.2.7.1)
113 - activeresource (= 3.2.22.5)
138 + activejob (= 4.2.7.1)
114 - activesupport (= 3.2.22.5)
139 + activemodel (= 4.2.7.1)
115 - bundler (~> 1.0)
140 + activerecord (= 4.2.7.1)
116 - railties (= 3.2.22.5)
141 + activesupport (= 4.2.7.1)
117 - rails_bootstrap_sortable (2.0.0)
142 + bundler (>= 1.3.0, < 2.0)
118 - momentjs-rails (~> 2, >= 2.8.3)
143 + railties (= 4.2.7.1)
119 - railties (3.2.22.5)
144 + sprockets-rails
120 - actionpack (= 3.2.22.5)
145 + rails-deprecated_sanitizer (1.0.3)
121 - activesupport (= 3.2.22.5)
146 + activesupport (>= 4.2.0.alpha)
122 - rack-ssl (~> 1.3.2)
147 + rails-dom-testing (1.0.8)
148 + activesupport (>= 4.2.0.beta, < 5.0)
149 + nokogiri (~> 1.6)
150 + rails-deprecated_sanitizer (>= 1.0.1)
151 + rails-html-sanitizer (1.0.3)
152 + loofah (~> 2.0)
153 + rails_bootstrap_sortable (2.0.1)
154 + momentjs-rails (>= 2.8.3)
155 + railties (4.2.7.1)
156 + actionpack (= 4.2.7.1)
157 + activesupport (= 4.2.7.1)
123 rake (>= 0.8.7)
158 rake (>= 0.8.7)
124 - rdoc (~> 3.4)
159 + thor (>= 0.18.1, < 2.0)
125 - thor (>= 0.14.6, < 2.0)
160 + rake (12.0.0)
126 - rake (11.2.2)
161 + rdiscount (2.2.0.1)
127 - rdiscount (2.1.8)
162 + rouge (2.0.7)
128 - rdoc (3.12.2)
163 + ruby-progressbar (1.8.1)
129 - json (~> 1.4)
164 + ruby_parser (3.8.3)
130 - rouge (1.8.0)
165 + sexp_processor (~> 4.1)
131 - rspec-collection_matchers (1.1.2)
166 + sass (3.4.23)
132 - rspec-expectations (>= 2.99.0.beta1)
167 + sass-rails (5.0.6)
133 - rspec-core (2.99.2)
168 + railties (>= 4.0.0, < 6)
134 - rspec-expectations (2.99.2)
169 + sass (~> 3.1)
135 - diff-lcs (>= 1.1.3, < 2.0)
170 + sprockets (>= 2.8, < 4.0)
136 - rspec-mocks (2.99.3)
171 + sprockets-rails (>= 2.0, < 4.0)
137 - rspec-rails (2.99.0)
172 + tilt (>= 1.1, < 3)
138 - actionpack (>= 3.0)
173 + select2-rails (4.0.3)
139 - activemodel (>= 3.0)
140 - activesupport (>= 3.0)
141 - railties (>= 3.0)
142 - rspec-collection_matchers
143 - rspec-core (~> 2.99.0)
144 - rspec-expectations (~> 2.99.0)
145 - rspec-mocks (~> 2.99.0)
146 - sass (3.4.11)
147 - sass-rails (3.2.6)
148 - railties (~> 3.2.0)
149 - sass (>= 3.1.10)
150 - tilt (~> 1.3)
151 - select2-rails (4.0.1)
152 thor (~> 0.14)
174 thor (~> 0.14)
153 - sprockets (2.2.3)
175 + sexp_processor (4.7.0)
154 - hike (~> 1.2)
176 + sprockets (3.7.1)
155 - multi_json (~> 1.0)
177 + concurrent-ruby (~> 1.0)
156 - rack (~> 1.0)
178 + rack (> 1, < 3)
157 - tilt (~> 1.1, != 1.3.0)
179 + sprockets-rails (3.2.0)
158 - test-unit (3.0.9)
180 + actionpack (>= 4.0)
159 - power_assert
181 + activesupport (>= 4.0)
160 - thor (0.19.1)
182 + sprockets (>= 3.0.0)
161 - tilt (1.4.1)
183 + sqlite3 (1.3.12)
162 - treetop (1.4.15)
184 + thor (0.19.4)
163 - polyglot
185 + thread_safe (0.3.5)
164 - polyglot (>= 0.3.1)
186 + tilt (2.0.5)
165 - tzinfo (0.3.51)
187 + tzinfo (1.2.2)
166 - uglifier (2.7.0)
188 + thread_safe (~> 0.1)
167 - execjs (>= 0.3.0)
189 + uglifier (3.0.4)
168 - json (>= 1.8.0)
190 + execjs (>= 0.3.0, < 3)
169 - will_paginate (3.0.7)
191 + will_paginate (3.0.12)
192 + yaml_db (0.4.2)
193 + rails (>= 3.0, < 5.1)
194 + rake (>= 0.8.7)
170
195
171 PLATFORMS
196 PLATFORMS
172 ruby
197 ruby
173
198
174 DEPENDENCIES
199 DEPENDENCIES
175 ace-rails-ap
200 ace-rails-ap
201 + activerecord-session_store
176 autoprefixer-rails
202 autoprefixer-rails
177 best_in_place (~> 3.0.1)
203 best_in_place (~> 3.0.1)
178 bootstrap-sass (~> 3.2.0)
204 bootstrap-sass (~> 3.2.0)
179 bootstrap-switch-rails
205 bootstrap-switch-rails
180 bootstrap-toggle-rails
206 bootstrap-toggle-rails
181 - coffee-rails (~> 3.2.2)
207 + coffee-rails
182 dynamic_form
208 dynamic_form
209 + fuzzy-string-match
183 haml
210 haml
184 haml-rails
211 haml-rails
185 in_place_editing
212 in_place_editing
186 jquery-countdown-rails
213 jquery-countdown-rails
187 jquery-rails
214 jquery-rails
188 jquery-tablesorter
215 jquery-tablesorter
189 jquery-timepicker-addon-rails
216 jquery-timepicker-addon-rails
190 - jquery-ui-sass-rails
217 + jquery-ui-rails
191 mail
218 mail
219 + minitest-reporters
192 momentjs-rails
220 momentjs-rails
193 mysql2
221 mysql2
194 - prototype-rails
222 + rails (~> 4.2.0)
195 - rails (~> 3.2)
196 rails_bootstrap_sortable
223 rails_bootstrap_sortable
197 rdiscount
224 rdiscount
198 rouge
225 rouge
199 - rspec-rails (~> 2.99.0)
226 + sass-rails
200 - sass-rails (~> 3.2.6)
201 select2-rails
227 select2-rails
202 - test-unit
228 + sqlite3
203 uglifier
229 uglifier
204 verification!
230 verification!
205 will_paginate (~> 3.0.7)
231 will_paginate (~> 3.0.7)
232 + yaml_db
206
233
207 BUNDLED WITH
234 BUNDLED WITH
208 - 1.12.5
235 + 1.13.6
@@ -1,44 +1,41
1 // This is a manifest file that'll be compiled into application.js, which will include all the files
1 // This is a manifest file that'll be compiled into application.js, which will include all the files
2 // listed below.
2 // listed below.
3 //
3 //
4 // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
4 // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
5 // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 //
6 //
7 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
7 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 // the compiled file.
8 // the compiled file.
9 //
9 //
10 // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
10 // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 // GO AFTER THE REQUIRES BELOW.
11 // GO AFTER THE REQUIRES BELOW.
12 //
12 //
13 //= require jquery
13 //= require jquery
14 //= require jquery_ujs
14 //= require jquery_ujs
15 - //= require jquery.ui.all
15 + //= require jquery-ui
16 //= require bootstrap-sprockets
16 //= require bootstrap-sprockets
17 //= require moment
17 //= require moment
18 //= require bootstrap-sortable
18 //= require bootstrap-sortable
19 //= require select2
19 //= require select2
20 //= require ace-rails-ap
20 //= require ace-rails-ap
21 //= require ace/mode-c_cpp
21 //= require ace/mode-c_cpp
22 //= require ace/mode-python
22 //= require ace/mode-python
23 //= require ace/mode-ruby
23 //= require ace/mode-ruby
24 //= require ace/mode-pascal
24 //= require ace/mode-pascal
25 //= require ace/mode-javascript
25 //= require ace/mode-javascript
26 //= require ace/mode-java
26 //= require ace/mode-java
27 //= require ace/theme-merbivore
27 //= require ace/theme-merbivore
28 //= require custom
28 //= require custom
29 //= require jquery.countdown
29 //= require jquery.countdown
30 //-------------- addition from local_jquery -----------
30 //-------------- addition from local_jquery -----------
31 - //= require jquery.ui.datepicker
32 - //= require jquery.ui.slider
33 - //= require jquery-ui-timepicker-addon
34 //= require jquery-tablesorter
31 //= require jquery-tablesorter
35 //= require best_in_place
32 //= require best_in_place
36 //= require best_in_place.jquery-ui
33 //= require best_in_place.jquery-ui
37 //= require brython
34 //= require brython
38
35
39 // since this is after blank line, it is not downloaded
36 // since this is after blank line, it is not downloaded
40 //x= require prototype
37 //x= require prototype
41 //x= require prototype_ujs
38 //x= require prototype_ujs
42 //x= require effects
39 //x= require effects
43 //x= require dragdrop
40 //x= require dragdrop
44 //x= require controls
41 //x= require controls
@@ -1,14 +1,4
1 - //= require jquery
2 - //= require jquery_ujs
3 - //= require jquery.ui.all
4 - //= require jquery.ui.datepicker
5 - //= require jquery.ui.slider
6 - //= require jquery-ui-timepicker-addon
7 - //= require jquery-tablesorter
8 - //= require best_in_place
9 - //= require best_in_place.jquery-ui
10 -
11 $(document).ready(function() {
1 $(document).ready(function() {
12 /* Activating Best In Place */
2 /* Activating Best In Place */
13 jQuery(".best_in_place").best_in_place();
3 jQuery(".best_in_place").best_in_place();
14 });
4 });
@@ -1,117 +1,117
1 /* This is a manifest file that'll be compiled into application.css, which will include all the files
1 /* This is a manifest file that'll be compiled into application.css, which will include all the files
2 * listed below.
2 * listed below.
3 *
3 *
4 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
4 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
5 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
5 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
6 *
6 *
7 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
7 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
8 * compiled file so the styles you add here take precedence over styles defined in any styles
8 * compiled file so the styles you add here take precedence over styles defined in any styles
9 * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
9 * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
10 * file per style scope.
10 * file per style scope.
11 *
11 *
12 * // bootstrap says that we should not do this, but @import each file instead
12 * // bootstrap says that we should not do this, but @import each file instead
13 * # *= require_tree .
13 * # *= require_tree .
14 * # *= require_self
14 * # *= require_self
15 */
15 */
16
16
17 - @import "jquery.ui.all";
17 + @import "jquery-ui";
18 - @import "jquery.ui.core";
18 + //@import "jquery.ui.core";
19 - @import "jquery.ui.theme";
19 + //@import "jquery.ui.theme";
20 - @import "jquery.ui.datepicker";
20 + //@import "jquery.ui.datepicker";
21 - @import "jquery.ui.slider";
21 + //@import "jquery.ui.slider";
22 @import "jquery-ui-timepicker-addon";
22 @import "jquery-ui-timepicker-addon";
23 @import "jquery-tablesorter/theme.metro-dark";
23 @import "jquery-tablesorter/theme.metro-dark";
24 @import "jquery.countdown";
24 @import "jquery.countdown";
25 @import "tablesorter-theme.cafe";
25 @import "tablesorter-theme.cafe";
26
26
27 //bootstrap
27 //bootstrap
28 @import "bootstrap-sprockets";
28 @import "bootstrap-sprockets";
29 @import "bootstrap";
29 @import "bootstrap";
30 @import "select2";
30 @import "select2";
31 @import "select2-bootstrap";
31 @import "select2-bootstrap";
32
32
33 //@import bootstrap3-switch
33 //@import bootstrap3-switch
34 @import "bootstrap-toggle";
34 @import "bootstrap-toggle";
35 @import "bootstrap-sortable";
35 @import "bootstrap-sortable";
36
36
37 //bootstrap navbar color (from)
37 //bootstrap navbar color (from)
38 $bgDefault: #19197b;
38 $bgDefault: #19197b;
39 $bgHighlight: #06064b;
39 $bgHighlight: #06064b;
40 $colDefault: #8e8eb4;
40 $colDefault: #8e8eb4;
41 $colHighlight: #ffffff;
41 $colHighlight: #ffffff;
42 $dropDown: false;
42 $dropDown: false;
43
43
44 @font-face {
44 @font-face {
45 font-family: 'Glyphicons Halflings';
45 font-family: 'Glyphicons Halflings';
46 src: font-path('bootstrap/glyphicons-halflings-regular.eot');
46 src: font-path('bootstrap/glyphicons-halflings-regular.eot');
47 src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
47 src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
48 font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'),
48 font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'),
49 font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
49 font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
50 font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
50 font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
51 }
51 }
52
52
53
53
54 .navbar-default {
54 .navbar-default {
55 background-color: $bgDefault;
55 background-color: $bgDefault;
56 border-color: $bgHighlight;
56 border-color: $bgHighlight;
57
57
58 .navbar-brand {
58 .navbar-brand {
59 color: $colDefault;
59 color: $colDefault;
60
60
61 &:hover, &:focus {
61 &:hover, &:focus {
62 color: $colHighlight;
62 color: $colHighlight;
63 }
63 }
64 }
64 }
65
65
66 .navbar-text {
66 .navbar-text {
67 color: $colDefault;
67 color: $colDefault;
68 }
68 }
69
69
70 .navbar-nav {
70 .navbar-nav {
71 > li {
71 > li {
72 > a {
72 > a {
73 color: $colDefault;
73 color: $colDefault;
74
74
75 &:hover, &:focus {
75 &:hover, &:focus {
76 color: $colHighlight;
76 color: $colHighlight;
77 }
77 }
78 }
78 }
79
79
80 @if $dropDown {
80 @if $dropDown {
81 > .dropdown-menu {
81 > .dropdown-menu {
82 background-color: $bgDefault;
82 background-color: $bgDefault;
83
83
84 > li {
84 > li {
85 > a {
85 > a {
86 color: $colDefault;
86 color: $colDefault;
87
87
88 &:hover, &:focus {
88 &:hover, &:focus {
89 color: $colHighlight;
89 color: $colHighlight;
90 background-color: $bgHighlight;
90 background-color: $bgHighlight;
91 }
91 }
92 }
92 }
93
93
94 > .divider {
94 > .divider {
95 background-color: $bgHighlight;
95 background-color: $bgHighlight;
96 }
96 }
97 }
97 }
98 }
98 }
99 }
99 }
100 }
100 }
101
101
102 @if $dropDown {
102 @if $dropDown {
103 .open .dropdown-menu > .active {
103 .open .dropdown-menu > .active {
104 > a, > a:hover, > a:focus {
104 > a, > a:hover, > a:focus {
105 color: $colHighlight;
105 color: $colHighlight;
106 background-color: $bgHighlight;
106 background-color: $bgHighlight;
107 }
107 }
108 }
108 }
109 }
109 }
110
110
111 > .active {
111 > .active {
112 > a, > a:hover, > a:focus {
112 > a, > a:hover, > a:focus {
113 color: $colHighlight;
113 color: $colHighlight;
114 background-color: $bgHighlight;
114 background-color: $bgHighlight;
115 }
115 }
116 }
116 }
117
117
@@ -1,111 +1,116
1 class AnnouncementsController < ApplicationController
1 class AnnouncementsController < ApplicationController
2
2
3 before_filter :admin_authorization
3 before_filter :admin_authorization
4
4
5 in_place_edit_for :announcement, :published
5 in_place_edit_for :announcement, :published
6
6
7 # GET /announcements
7 # GET /announcements
8 # GET /announcements.xml
8 # GET /announcements.xml
9 def index
9 def index
10 - @announcements = Announcement.find(:all,
10 + @announcements = Announcement.order(created_at: :desc)
11 - :order => "created_at DESC")
12
11
13 respond_to do |format|
12 respond_to do |format|
14 format.html # index.html.erb
13 format.html # index.html.erb
15 format.xml { render :xml => @announcements }
14 format.xml { render :xml => @announcements }
16 end
15 end
17 end
16 end
18
17
19 # GET /announcements/1
18 # GET /announcements/1
20 # GET /announcements/1.xml
19 # GET /announcements/1.xml
21 def show
20 def show
22 @announcement = Announcement.find(params[:id])
21 @announcement = Announcement.find(params[:id])
23
22
24 respond_to do |format|
23 respond_to do |format|
25 format.html # show.html.erb
24 format.html # show.html.erb
26 format.xml { render :xml => @announcement }
25 format.xml { render :xml => @announcement }
27 end
26 end
28 end
27 end
29
28
30 # GET /announcements/new
29 # GET /announcements/new
31 # GET /announcements/new.xml
30 # GET /announcements/new.xml
32 def new
31 def new
33 @announcement = Announcement.new
32 @announcement = Announcement.new
34
33
35 respond_to do |format|
34 respond_to do |format|
36 format.html # new.html.erb
35 format.html # new.html.erb
37 format.xml { render :xml => @announcement }
36 format.xml { render :xml => @announcement }
38 end
37 end
39 end
38 end
40
39
41 # GET /announcements/1/edit
40 # GET /announcements/1/edit
42 def edit
41 def edit
43 @announcement = Announcement.find(params[:id])
42 @announcement = Announcement.find(params[:id])
44 end
43 end
45
44
46 # POST /announcements
45 # POST /announcements
47 # POST /announcements.xml
46 # POST /announcements.xml
48 def create
47 def create
49 - @announcement = Announcement.new(params[:announcement])
48 + @announcement = Announcement.new(announcement_params)
50
49
51 respond_to do |format|
50 respond_to do |format|
52 if @announcement.save
51 if @announcement.save
53 flash[:notice] = 'Announcement was successfully created.'
52 flash[:notice] = 'Announcement was successfully created.'
54 format.html { redirect_to(@announcement) }
53 format.html { redirect_to(@announcement) }
55 format.xml { render :xml => @announcement, :status => :created, :location => @announcement }
54 format.xml { render :xml => @announcement, :status => :created, :location => @announcement }
56 else
55 else
57 format.html { render :action => "new" }
56 format.html { render :action => "new" }
58 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
57 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
59 end
58 end
60 end
59 end
61 end
60 end
62
61
63 # PUT /announcements/1
62 # PUT /announcements/1
64 # PUT /announcements/1.xml
63 # PUT /announcements/1.xml
65 def update
64 def update
66 @announcement = Announcement.find(params[:id])
65 @announcement = Announcement.find(params[:id])
67
66
68 respond_to do |format|
67 respond_to do |format|
69 - if @announcement.update_attributes(params[:announcement])
68 + if @announcement.update_attributes(announcement_params)
70 flash[:notice] = 'Announcement was successfully updated.'
69 flash[:notice] = 'Announcement was successfully updated.'
71 format.html { redirect_to(@announcement) }
70 format.html { redirect_to(@announcement) }
72 format.js {}
71 format.js {}
73 format.xml { head :ok }
72 format.xml { head :ok }
74 else
73 else
75 format.html { render :action => "edit" }
74 format.html { render :action => "edit" }
76 format.js {}
75 format.js {}
77 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
76 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
78 end
77 end
79 end
78 end
80 end
79 end
81
80
82 def toggle
81 def toggle
83 @announcement = Announcement.find(params[:id])
82 @announcement = Announcement.find(params[:id])
84 @announcement.update_attributes( published: !@announcement.published? )
83 @announcement.update_attributes( published: !@announcement.published? )
85 respond_to do |format|
84 respond_to do |format|
86 format.js { render partial: 'toggle_button',
85 format.js { render partial: 'toggle_button',
87 locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } }
86 locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } }
88 end
87 end
89 end
88 end
90
89
91 def toggle_front
90 def toggle_front
92 @announcement = Announcement.find(params[:id])
91 @announcement = Announcement.find(params[:id])
93 @announcement.update_attributes( frontpage: !@announcement.frontpage? )
92 @announcement.update_attributes( frontpage: !@announcement.frontpage? )
94 respond_to do |format|
93 respond_to do |format|
95 format.js { render partial: 'toggle_button',
94 format.js { render partial: 'toggle_button',
96 locals: {button_id: "#announcement_toggle_front_#{@announcement.id}",button_on: @announcement.frontpage? } }
95 locals: {button_id: "#announcement_toggle_front_#{@announcement.id}",button_on: @announcement.frontpage? } }
97 end
96 end
98 end
97 end
99
98
100 # DELETE /announcements/1
99 # DELETE /announcements/1
101 # DELETE /announcements/1.xml
100 # DELETE /announcements/1.xml
102 def destroy
101 def destroy
103 @announcement = Announcement.find(params[:id])
102 @announcement = Announcement.find(params[:id])
104 @announcement.destroy
103 @announcement.destroy
105
104
106 respond_to do |format|
105 respond_to do |format|
107 format.html { redirect_to(announcements_url) }
106 format.html { redirect_to(announcements_url) }
108 format.xml { head :ok }
107 format.xml { head :ok }
109 end
108 end
110 end
109 end
110 +
111 + private
112 +
113 + def announcement_params
114 + params.require(:announcement).permit(:author, :body, :published, :frontpage, :contest_only, :title)
115 + end
111 end
116 end
@@ -1,128 +1,140
1 class ApplicationController < ActionController::Base
1 class ApplicationController < ActionController::Base
2 protect_from_forgery
2 protect_from_forgery
3
3
4 - before_filter :current_user
4 + before_filter :current_user
5
5
6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
8
8
9 #report and redirect for unauthorized activities
9 #report and redirect for unauthorized activities
10 def unauthorized_redirect
10 def unauthorized_redirect
11 flash[:notice] = 'You are not authorized to view the page you requested'
11 flash[:notice] = 'You are not authorized to view the page you requested'
12 redirect_to :controller => 'main', :action => 'login'
12 redirect_to :controller => 'main', :action => 'login'
13 end
13 end
14
14
15 # Returns the current logged-in user (if any).
15 # Returns the current logged-in user (if any).
16 def current_user
16 def current_user
17 return nil unless session[:user_id]
17 return nil unless session[:user_id]
18 @current_user ||= User.find(session[:user_id])
18 @current_user ||= User.find(session[:user_id])
19 end
19 end
20
20
21 def admin_authorization
21 def admin_authorization
22 return false unless authenticate
22 return false unless authenticate
23 - user = User.find(session[:user_id], :include => ['roles'])
23 + user = User.includes(:roles).find(session[:user_id])
24 unless user.admin?
24 unless user.admin?
25 unauthorized_redirect
25 unauthorized_redirect
26 return false
26 return false
27 end
27 end
28 return true
28 return true
29 end
29 end
30
30
31 def authorization_by_roles(allowed_roles)
31 def authorization_by_roles(allowed_roles)
32 return false unless authenticate
32 return false unless authenticate
33 user = User.find(session[:user_id])
33 user = User.find(session[:user_id])
34 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
34 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
35 unauthorized_redirect
35 unauthorized_redirect
36 return false
36 return false
37 end
37 end
38 end
38 end
39
39
40 + def testcase_authorization
41 + #admin always has privileged
42 + puts "haha"
43 + if @current_user.admin?
44 + return true
45 + end
46 +
47 + puts "hehe"
48 + puts GraderConfiguration["right.view_testcase"]
49 + unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
50 + end
51 +
40 protected
52 protected
41
53
42 def authenticate
54 def authenticate
43 unless session[:user_id]
55 unless session[:user_id]
44 flash[:notice] = 'You need to login'
56 flash[:notice] = 'You need to login'
45 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
57 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
46 flash[:notice] = 'You need to login but you cannot log in at this time'
58 flash[:notice] = 'You need to login but you cannot log in at this time'
47 end
59 end
48 redirect_to :controller => 'main', :action => 'login'
60 redirect_to :controller => 'main', :action => 'login'
49 return false
61 return false
50 end
62 end
51
63
52 # check if run in single user mode
64 # check if run in single user mode
53 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
65 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
54 user = User.find_by_id(session[:user_id])
66 user = User.find_by_id(session[:user_id])
55 if user==nil or (not user.admin?)
67 if user==nil or (not user.admin?)
56 flash[:notice] = 'You cannot log in at this time'
68 flash[:notice] = 'You cannot log in at this time'
57 redirect_to :controller => 'main', :action => 'login'
69 redirect_to :controller => 'main', :action => 'login'
58 return false
70 return false
59 end
71 end
60 unless user.enabled?
72 unless user.enabled?
61 flash[:notice] = 'Your account is disabled'
73 flash[:notice] = 'Your account is disabled'
62 redirect_to :controller => 'main', :action => 'login'
74 redirect_to :controller => 'main', :action => 'login'
63 return false
75 return false
64 end
76 end
65 return true
77 return true
66 end
78 end
67
79
68 if GraderConfiguration.multicontests?
80 if GraderConfiguration.multicontests?
69 user = User.find(session[:user_id])
81 user = User.find(session[:user_id])
70 return true if user.admin?
82 return true if user.admin?
71 begin
83 begin
72 if user.contest_stat(true).forced_logout
84 if user.contest_stat(true).forced_logout
73 flash[:notice] = 'You have been automatically logged out.'
85 flash[:notice] = 'You have been automatically logged out.'
74 redirect_to :controller => 'main', :action => 'index'
86 redirect_to :controller => 'main', :action => 'index'
75 end
87 end
76 rescue
88 rescue
77 end
89 end
78 end
90 end
79 return true
91 return true
80 end
92 end
81
93
82 def authenticate_by_ip_address
94 def authenticate_by_ip_address
83 #this assume that we have already authenticate normally
95 #this assume that we have already authenticate normally
84 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
96 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
85 user = User.find(session[:user_id])
97 user = User.find(session[:user_id])
86 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
98 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
87 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
99 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
88 redirect_to :controller => 'main', :action => 'login'
100 redirect_to :controller => 'main', :action => 'login'
89 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
101 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
90 return false
102 return false
91 end
103 end
92 unless user.last_ip
104 unless user.last_ip
93 user.last_ip = request.remote_ip
105 user.last_ip = request.remote_ip
94 user.save
106 user.save
95 end
107 end
96 end
108 end
97 return true
109 return true
98 end
110 end
99
111
100 def authorization
112 def authorization
101 return false unless authenticate
113 return false unless authenticate
102 user = User.find(session[:user_id])
114 user = User.find(session[:user_id])
103 unless user.roles.detect { |role|
115 unless user.roles.detect { |role|
104 - role.rights.detect{ |right|
116 + role.rights.detect{ |right|
105 - right.controller == self.class.controller_name and
117 + right.controller == self.class.controller_name and
106 - (right.action == 'all' or right.action == action_name)
118 + (right.action == 'all' or right.action == action_name)
107 - }
119 + }
108 }
120 }
109 flash[:notice] = 'You are not authorized to view the page you requested'
121 flash[:notice] = 'You are not authorized to view the page you requested'
110 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
122 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
111 redirect_to :controller => 'main', :action => 'login'
123 redirect_to :controller => 'main', :action => 'login'
112 return false
124 return false
113 end
125 end
114 end
126 end
115
127
116 def verify_time_limit
128 def verify_time_limit
117 return true if session[:user_id]==nil
129 return true if session[:user_id]==nil
118 user = User.find(session[:user_id], :include => :site)
130 user = User.find(session[:user_id], :include => :site)
119 return true if user==nil or user.site == nil
131 return true if user==nil or user.site == nil
120 if user.contest_finished?
132 if user.contest_finished?
121 flash[:notice] = 'Error: the contest you are participating is over.'
133 flash[:notice] = 'Error: the contest you are participating is over.'
122 redirect_to :back
134 redirect_to :back
123 return false
135 return false
124 end
136 end
125 return true
137 return true
126 end
138 end
127
139
128 end
140 end
@@ -1,29 +1,34
1 class ConfigurationsController < ApplicationController
1 class ConfigurationsController < ApplicationController
2
2
3 before_filter :authenticate
3 before_filter :authenticate
4 before_filter { |controller| controller.authorization_by_roles(['admin'])}
4 before_filter { |controller| controller.authorization_by_roles(['admin'])}
5
5
6
6
7 def index
7 def index
8 - @configurations = GraderConfiguration.find(:all,
8 + @configurations = GraderConfiguration.order(:key)
9 - :order => '`key`')
9 + @group = GraderConfiguration.pluck("grader_configurations.key").map{ |x| x[0...(x.index('.'))] }.uniq.sort
10 end
10 end
11
11
12 def reload
12 def reload
13 GraderConfiguration.reload
13 GraderConfiguration.reload
14 redirect_to :action => 'index'
14 redirect_to :action => 'index'
15 end
15 end
16
16
17 def update
17 def update
18 @config = GraderConfiguration.find(params[:id])
18 @config = GraderConfiguration.find(params[:id])
19 User.clear_last_login if @config.key == GraderConfiguration::MULTIPLE_IP_LOGIN_KEY and @config.value == 'true' and params[:grader_configuration][:value] == 'false'
19 User.clear_last_login if @config.key == GraderConfiguration::MULTIPLE_IP_LOGIN_KEY and @config.value == 'true' and params[:grader_configuration][:value] == 'false'
20 respond_to do |format|
20 respond_to do |format|
21 - if @config.update_attributes(params[:grader_configuration])
21 + if @config.update_attributes(configuration_params)
22 format.json { head :ok }
22 format.json { head :ok }
23 else
23 else
24 format.json { respond_with_bip(@config) }
24 format.json { respond_with_bip(@config) }
25 end
25 end
26 end
26 end
27 end
27 end
28
28
29 + private
30 + def configuration_params
31 + params.require(:grader_configuration).permit(:key,:value_type,:value,:description)
32 + end
33 +
29 end
34 end
@@ -1,50 +1,50
1 class ContestManagementController < ApplicationController
1 class ContestManagementController < ApplicationController
2
2
3 before_filter :admin_authorization
3 before_filter :admin_authorization
4
4
5 def index
5 def index
6 @num_contests = Contest.count()
6 @num_contests = Contest.count()
7 end
7 end
8
8
9 def user_stat
9 def user_stat
10 if not GraderConfiguration.indv_contest_mode?
10 if not GraderConfiguration.indv_contest_mode?
11 redirect_to :action => 'index' and return
11 redirect_to :action => 'index' and return
12 end
12 end
13
13
14 - @users = User.find(:all)
14 + @users = User.all
15 @start_times = {}
15 @start_times = {}
16 - UserContestStat.find(:all).each do |stat|
16 + UserContestStat.all.each do |stat|
17 @start_times[stat.user_id] = stat.started_at
17 @start_times[stat.user_id] = stat.started_at
18 end
18 end
19 end
19 end
20
20
21 def clear_stat
21 def clear_stat
22 user = User.find(params[:id])
22 user = User.find(params[:id])
23 if user.contest_stat!=nil
23 if user.contest_stat!=nil
24 user.contest_stat.destroy
24 user.contest_stat.destroy
25 end
25 end
26 redirect_to :action => 'user_stat'
26 redirect_to :action => 'user_stat'
27 end
27 end
28
28
29 def clear_all_stat
29 def clear_all_stat
30 if not GraderConfiguration.indv_contest_mode?
30 if not GraderConfiguration.indv_contest_mode?
31 redirect_to :action => 'index' and return
31 redirect_to :action => 'index' and return
32 end
32 end
33
33
34 UserContestStat.delete_all()
34 UserContestStat.delete_all()
35 flash[:notice] = 'All start time statistic cleared.'
35 flash[:notice] = 'All start time statistic cleared.'
36 redirect_to :action => 'index'
36 redirect_to :action => 'index'
37 end
37 end
38
38
39 def change_contest_mode
39 def change_contest_mode
40 if ['standard', 'contest', 'indv-contest', 'analysis'].include? params[:id]
40 if ['standard', 'contest', 'indv-contest', 'analysis'].include? params[:id]
41 config = GraderConfiguration.find_by_key('system.mode')
41 config = GraderConfiguration.find_by_key('system.mode')
42 config.value = params[:id]
42 config.value = params[:id]
43 config.save
43 config.save
44 else
44 else
45 flash[:notice] = 'Wrong contest mode value'
45 flash[:notice] = 'Wrong contest mode value'
46 end
46 end
47 redirect_to :action => 'index'
47 redirect_to :action => 'index'
48 end
48 end
49
49
50 end
50 end
@@ -1,92 +1,98
1 class ContestsController < ApplicationController
1 class ContestsController < ApplicationController
2
2
3 before_filter :admin_authorization
3 before_filter :admin_authorization
4
4
5 in_place_edit_for :contest, :title
5 in_place_edit_for :contest, :title
6 in_place_edit_for :contest, :enabled
6 in_place_edit_for :contest, :enabled
7
7
8 # GET /contests
8 # GET /contests
9 # GET /contests.xml
9 # GET /contests.xml
10 def index
10 def index
11 @contests = Contest.all
11 @contests = Contest.all
12
12
13 respond_to do |format|
13 respond_to do |format|
14 format.html # index.html.erb
14 format.html # index.html.erb
15 format.xml { render :xml => @contests }
15 format.xml { render :xml => @contests }
16 end
16 end
17 end
17 end
18
18
19 # GET /contests/1
19 # GET /contests/1
20 # GET /contests/1.xml
20 # GET /contests/1.xml
21 def show
21 def show
22 @contest = Contest.find(params[:id])
22 @contest = Contest.find(params[:id])
23
23
24 respond_to do |format|
24 respond_to do |format|
25 format.html # show.html.erb
25 format.html # show.html.erb
26 format.xml { render :xml => @contest }
26 format.xml { render :xml => @contest }
27 end
27 end
28 end
28 end
29
29
30 # GET /contests/new
30 # GET /contests/new
31 # GET /contests/new.xml
31 # GET /contests/new.xml
32 def new
32 def new
33 @contest = Contest.new
33 @contest = Contest.new
34
34
35 respond_to do |format|
35 respond_to do |format|
36 format.html # new.html.erb
36 format.html # new.html.erb
37 format.xml { render :xml => @contest }
37 format.xml { render :xml => @contest }
38 end
38 end
39 end
39 end
40
40
41 # GET /contests/1/edit
41 # GET /contests/1/edit
42 def edit
42 def edit
43 @contest = Contest.find(params[:id])
43 @contest = Contest.find(params[:id])
44 end
44 end
45
45
46 # POST /contests
46 # POST /contests
47 # POST /contests.xml
47 # POST /contests.xml
48 def create
48 def create
49 @contest = Contest.new(params[:contest])
49 @contest = Contest.new(params[:contest])
50
50
51 respond_to do |format|
51 respond_to do |format|
52 if @contest.save
52 if @contest.save
53 flash[:notice] = 'Contest was successfully created.'
53 flash[:notice] = 'Contest was successfully created.'
54 format.html { redirect_to(@contest) }
54 format.html { redirect_to(@contest) }
55 format.xml { render :xml => @contest, :status => :created, :location => @contest }
55 format.xml { render :xml => @contest, :status => :created, :location => @contest }
56 else
56 else
57 format.html { render :action => "new" }
57 format.html { render :action => "new" }
58 format.xml { render :xml => @contest.errors, :status => :unprocessable_entity }
58 format.xml { render :xml => @contest.errors, :status => :unprocessable_entity }
59 end
59 end
60 end
60 end
61 end
61 end
62
62
63 # PUT /contests/1
63 # PUT /contests/1
64 # PUT /contests/1.xml
64 # PUT /contests/1.xml
65 def update
65 def update
66 @contest = Contest.find(params[:id])
66 @contest = Contest.find(params[:id])
67
67
68 respond_to do |format|
68 respond_to do |format|
69 - if @contest.update_attributes(params[:contest])
69 + if @contest.update_attributes(contests_params)
70 flash[:notice] = 'Contest was successfully updated.'
70 flash[:notice] = 'Contest was successfully updated.'
71 format.html { redirect_to(@contest) }
71 format.html { redirect_to(@contest) }
72 format.xml { head :ok }
72 format.xml { head :ok }
73 else
73 else
74 format.html { render :action => "edit" }
74 format.html { render :action => "edit" }
75 format.xml { render :xml => @contest.errors, :status => :unprocessable_entity }
75 format.xml { render :xml => @contest.errors, :status => :unprocessable_entity }
76 end
76 end
77 end
77 end
78 end
78 end
79
79
80 # DELETE /contests/1
80 # DELETE /contests/1
81 # DELETE /contests/1.xml
81 # DELETE /contests/1.xml
82 def destroy
82 def destroy
83 @contest = Contest.find(params[:id])
83 @contest = Contest.find(params[:id])
84 @contest.destroy
84 @contest.destroy
85
85
86 respond_to do |format|
86 respond_to do |format|
87 format.html { redirect_to(contests_url) }
87 format.html { redirect_to(contests_url) }
88 format.xml { head :ok }
88 format.xml { head :ok }
89 end
89 end
90 end
90 end
91
91
92 + private
93 +
94 + def contests_params
95 + params.require(:contest).permit(:title,:enabled,:name)
96 + end
97 +
92 end
98 end
@@ -1,130 +1,128
1 class GradersController < ApplicationController
1 class GradersController < ApplicationController
2
2
3 before_filter :admin_authorization, except: [ :submission ]
3 before_filter :admin_authorization, except: [ :submission ]
4 before_filter(only: [:submission]) {
4 before_filter(only: [:submission]) {
5 #check if authenticated
5 #check if authenticated
6 return false unless authenticate
6 return false unless authenticate
7
7
8 #admin always has privileged
8 #admin always has privileged
9 if @current_user.admin?
9 if @current_user.admin?
10 return true
10 return true
11 end
11 end
12
12
13 if GraderConfiguration["right.user_view_submission"] and Submission.find(params[:id]).problem.available?
13 if GraderConfiguration["right.user_view_submission"] and Submission.find(params[:id]).problem.available?
14 return true
14 return true
15 else
15 else
16 unauthorized_redirect
16 unauthorized_redirect
17 return false
17 return false
18 end
18 end
19 }
19 }
20
20
21 verify :method => :post, :only => ['clear_all',
21 verify :method => :post, :only => ['clear_all',
22 'start_exam',
22 'start_exam',
23 'start_grading',
23 'start_grading',
24 'stop_all',
24 'stop_all',
25 'clear_terminated'],
25 'clear_terminated'],
26 :redirect_to => {:action => 'index'}
26 :redirect_to => {:action => 'index'}
27
27
28 def index
28 def index
29 redirect_to :action => 'list'
29 redirect_to :action => 'list'
30 end
30 end
31
31
32 def list
32 def list
33 @grader_processes = GraderProcess.find_running_graders
33 @grader_processes = GraderProcess.find_running_graders
34 @stalled_processes = GraderProcess.find_stalled_process
34 @stalled_processes = GraderProcess.find_stalled_process
35
35
36 @terminated_processes = GraderProcess.find_terminated_graders
36 @terminated_processes = GraderProcess.find_terminated_graders
37
37
38 - @last_task = Task.find(:first,
38 + @last_task = Task.last
39 - :order => 'created_at DESC')
39 + @last_test_request = TestRequest.last
40 - @last_test_request = TestRequest.find(:first,
41 - :order => 'created_at DESC')
42 @submission = Submission.order("id desc").limit(20)
40 @submission = Submission.order("id desc").limit(20)
43 @backlog_submission = Submission.where('graded_at is null')
41 @backlog_submission = Submission.where('graded_at is null')
44 end
42 end
45
43
46 def clear
44 def clear
47 grader_proc = GraderProcess.find(params[:id])
45 grader_proc = GraderProcess.find(params[:id])
48 grader_proc.destroy if grader_proc!=nil
46 grader_proc.destroy if grader_proc!=nil
49 redirect_to :action => 'list'
47 redirect_to :action => 'list'
50 end
48 end
51
49
52 def clear_terminated
50 def clear_terminated
53 GraderProcess.find_terminated_graders.each do |p|
51 GraderProcess.find_terminated_graders.each do |p|
54 p.destroy
52 p.destroy
55 end
53 end
56 redirect_to :action => 'list'
54 redirect_to :action => 'list'
57 end
55 end
58
56
59 def clear_all
57 def clear_all
60 - GraderProcess.find(:all).each do |p|
58 + GraderProcess.all.each do |p|
61 p.destroy
59 p.destroy
62 end
60 end
63 redirect_to :action => 'list'
61 redirect_to :action => 'list'
64 end
62 end
65
63
66 def view
64 def view
67 if params[:type]=='Task'
65 if params[:type]=='Task'
68 redirect_to :action => 'task', :id => params[:id]
66 redirect_to :action => 'task', :id => params[:id]
69 else
67 else
70 redirect_to :action => 'test_request', :id => params[:id]
68 redirect_to :action => 'test_request', :id => params[:id]
71 end
69 end
72 end
70 end
73
71
74 def test_request
72 def test_request
75 @test_request = TestRequest.find(params[:id])
73 @test_request = TestRequest.find(params[:id])
76 end
74 end
77
75
78 def task
76 def task
79 @task = Task.find(params[:id])
77 @task = Task.find(params[:id])
80 end
78 end
81
79
82 def submission
80 def submission
83 @submission = Submission.find(params[:id])
81 @submission = Submission.find(params[:id])
84 formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', line_numbers: true )
82 formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', line_numbers: true )
85 lexer = case @submission.language.name
83 lexer = case @submission.language.name
86 when "c" then Rouge::Lexers::C.new
84 when "c" then Rouge::Lexers::C.new
87 when "cpp" then Rouge::Lexers::Cpp.new
85 when "cpp" then Rouge::Lexers::Cpp.new
88 when "pas" then Rouge::Lexers::Pas.new
86 when "pas" then Rouge::Lexers::Pas.new
89 when "ruby" then Rouge::Lexers::Ruby.new
87 when "ruby" then Rouge::Lexers::Ruby.new
90 when "python" then Rouge::Lexers::Python.new
88 when "python" then Rouge::Lexers::Python.new
91 when "java" then Rouge::Lexers::Java.new
89 when "java" then Rouge::Lexers::Java.new
92 when "php" then Rouge::Lexers::PHP.new
90 when "php" then Rouge::Lexers::PHP.new
93 end
91 end
94 @formatted_code = formatter.format(lexer.lex(@submission.source))
92 @formatted_code = formatter.format(lexer.lex(@submission.source))
95 @css_style = Rouge::Themes::ThankfulEyes.render(scope: '.highlight')
93 @css_style = Rouge::Themes::ThankfulEyes.render(scope: '.highlight')
96
94
97 user = User.find(session[:user_id])
95 user = User.find(session[:user_id])
98 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
96 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
99
97
100 end
98 end
101
99
102 # various grader controls
100 # various grader controls
103
101
104 def stop
102 def stop
105 grader_proc = GraderProcess.find(params[:id])
103 grader_proc = GraderProcess.find(params[:id])
106 GraderScript.stop_grader(grader_proc.pid)
104 GraderScript.stop_grader(grader_proc.pid)
107 flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.'
105 flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.'
108 redirect_to :action => 'list'
106 redirect_to :action => 'list'
109 end
107 end
110
108
111 def stop_all
109 def stop_all
112 GraderScript.stop_graders(GraderProcess.find_running_graders +
110 GraderScript.stop_graders(GraderProcess.find_running_graders +
113 GraderProcess.find_stalled_process)
111 GraderProcess.find_stalled_process)
114 flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.'
112 flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.'
115 redirect_to :action => 'list'
113 redirect_to :action => 'list'
116 end
114 end
117
115
118 def start_grading
116 def start_grading
119 GraderScript.start_grader('grading')
117 GraderScript.start_grader('grading')
120 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
118 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
121 redirect_to :action => 'list'
119 redirect_to :action => 'list'
122 end
120 end
123
121
124 def start_exam
122 def start_exam
125 GraderScript.start_grader('exam')
123 GraderScript.start_grader('exam')
126 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
124 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
127 redirect_to :action => 'list'
125 redirect_to :action => 'list'
128 end
126 end
129
127
130 end
128 end
@@ -1,31 +1,46
1 class HeartbeatController < ApplicationController
1 class HeartbeatController < ApplicationController
2 before_filter :admin_authorization, :only => ['index']
2 before_filter :admin_authorization, :only => ['index']
3
3
4 def edit
4 def edit
5 - @user = User.find_by_login(params[:id])
5 + #@user = User.find_by_login(params[:id])
6 - unless @user
6 + #unless @user
7 - render text: "LOGIN_NOT_FOUND"
7 + # render text: "LOGIN_NOT_FOUND"
8 - return
8 + # return
9 - end
9 + #end
10
10
11 #hb = HeartBeat.where(user_id: @user.id, ip_address: request.remote_ip).first
11 #hb = HeartBeat.where(user_id: @user.id, ip_address: request.remote_ip).first
12 #puts "status = #{params[:status]}"
12 #puts "status = #{params[:status]}"
13 #if hb
13 #if hb
14 # if params[:status]
14 # if params[:status]
15 # hb.status = params[:status]
15 # hb.status = params[:status]
16 # hb.save
16 # hb.save
17 # end
17 # end
18 # hb.touch
18 # hb.touch
19 #else
19 #else
20 # HeartBeat.creae(user_id: @user.id, ip_address: request.remote_ip)
20 # HeartBeat.creae(user_id: @user.id, ip_address: request.remote_ip)
21 #end
21 #end
22 - HeartBeat.create(user_id: @user.id, ip_address: request.remote_ip, status: params[:status])
22 + #HeartBeat.create(user_id: @user.id, ip_address: request.remote_ip, status: params[:status])
23 +
24 + res = GraderConfiguration['right.heartbeat_response']
25 + res.strip! if res
26 + full = GraderConfiguration['right.heartbeat_response_full']
27 + full.strip! if full
23
28
24 - render text: (GraderConfiguration['right.heartbeat_response'] || 'OK')
29 + if full and full != ''
30 + l = Login.where(ip_address: request.remote_ip).last
31 + @user = l.user
32 + if @user.solve_all_available_problems?
33 + render text: (full || 'OK')
34 + else
35 + render text: (res || 'OK')
36 + end
37 + else
38 + render text: (GraderConfiguration['right.heartbeat_response'] || 'OK')
39 + end
25 end
40 end
26
41
27 def index
42 def index
28 @hb = HeartBeat.where("updated_at >= ?",Time.zone.now-2.hours).includes(:user).order(:user_id).all
43 @hb = HeartBeat.where("updated_at >= ?",Time.zone.now-2.hours).includes(:user).order(:user_id).all
29 @num = HeartBeat.where("updated_at >= ?",Time.zone.now-5.minutes).count(:user_id,distinct: true)
44 @num = HeartBeat.where("updated_at >= ?",Time.zone.now-5.minutes).count(:user_id,distinct: true)
30 end
45 end
31 end
46 end
@@ -1,318 +1,301
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 before_filter :authenticate, :except => [:index, :login]
3 before_filter :authenticate, :except => [:index, :login]
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.find_for_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?
89 if @submission.valid?
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 :action => 'list'
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 submission
128 - @user = User.find(session[:user_id])
129 - @problems = @user.available_problems
130 - if params[:id]==nil
131 - @problem = nil
132 - @submissions = nil
133 - else
134 - @problem = Problem.find_by_id(params[:id])
135 - if (@problem == nil) or (not @problem.available)
136 - redirect_to :action => 'list'
137 - flash[:notice] = 'Error: submissions for that problem are not viewable.'
138 - return
139 - end
140 - @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
141 - end
142 - end
143 -
144 def result
127 def result
145 if !GraderConfiguration.show_grading_result
128 if !GraderConfiguration.show_grading_result
146 redirect_to :action => 'list' and return
129 redirect_to :action => 'list' and return
147 end
130 end
148 @user = User.find(session[:user_id])
131 @user = User.find(session[:user_id])
149 @submission = Submission.find(params[:id])
132 @submission = Submission.find(params[:id])
150 if @submission.user!=@user
133 if @submission.user!=@user
151 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.'
152 redirect_to :action => 'list' and return
135 redirect_to :action => 'list' and return
153 end
136 end
154 prepare_grading_result(@submission)
137 prepare_grading_result(@submission)
155 end
138 end
156
139
157 def load_output
140 def load_output
158 if !GraderConfiguration.show_grading_result or params[:num]==nil
141 if !GraderConfiguration.show_grading_result or params[:num]==nil
159 redirect_to :action => 'list' and return
142 redirect_to :action => 'list' and return
160 end
143 end
161 @user = User.find(session[:user_id])
144 @user = User.find(session[:user_id])
162 @submission = Submission.find(params[:id])
145 @submission = Submission.find(params[:id])
163 if @submission.user!=@user
146 if @submission.user!=@user
164 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.'
165 redirect_to :action => 'list' and return
148 redirect_to :action => 'list' and return
166 end
149 end
167 case_num = params[:num].to_i
150 case_num = params[:num].to_i
168 out_filename = output_filename(@user.login,
151 out_filename = output_filename(@user.login,
169 @submission.problem.name,
152 @submission.problem.name,
170 @submission.id,
153 @submission.id,
171 case_num)
154 case_num)
172 if !FileTest.exists?(out_filename)
155 if !FileTest.exists?(out_filename)
173 flash[:notice] = 'Output not found.'
156 flash[:notice] = 'Output not found.'
174 redirect_to :action => 'list' and return
157 redirect_to :action => 'list' and return
175 end
158 end
176
159
177 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
160 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
178 response.headers['Content-Type'] = "application/force-download"
161 response.headers['Content-Type'] = "application/force-download"
179 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
162 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
180 response.headers["X-Sendfile"] = out_filename
163 response.headers["X-Sendfile"] = out_filename
181 response.headers['Content-length'] = File.size(out_filename)
164 response.headers['Content-length'] = File.size(out_filename)
182 render :nothing => true
165 render :nothing => true
183 else
166 else
184 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"
185 end
168 end
186 end
169 end
187
170
188 def error
171 def error
189 @user = User.find(session[:user_id])
172 @user = User.find(session[:user_id])
190 end
173 end
191
174
192 # announcement refreshing and hiding methods
175 # announcement refreshing and hiding methods
193
176
194 def announcements
177 def announcements
195 if params.has_key? 'recent'
178 if params.has_key? 'recent'
196 prepare_announcements(params[:recent])
179 prepare_announcements(params[:recent])
197 else
180 else
198 prepare_announcements
181 prepare_announcements
199 end
182 end
200 render(:partial => 'announcement',
183 render(:partial => 'announcement',
201 :collection => @announcements,
184 :collection => @announcements,
202 :locals => {:announcement_effect => true})
185 :locals => {:announcement_effect => true})
203 end
186 end
204
187
205 def confirm_contest_start
188 def confirm_contest_start
206 user = User.find(session[:user_id])
189 user = User.find(session[:user_id])
207 if request.method == 'POST'
190 if request.method == 'POST'
208 user.update_start_time
191 user.update_start_time
209 redirect_to :action => 'list'
192 redirect_to :action => 'list'
210 else
193 else
211 @contests = user.contests
194 @contests = user.contests
212 @user = user
195 @user = user
213 end
196 end
214 end
197 end
215
198
216 protected
199 protected
217
200
218 def prepare_announcements(recent=nil)
201 def prepare_announcements(recent=nil)
219 if GraderConfiguration.show_tasks_to?(@user)
202 if GraderConfiguration.show_tasks_to?(@user)
220 - @announcements = Announcement.find_published(true)
203 + @announcements = Announcement.published(true)
221 else
204 else
222 - @announcements = Announcement.find_published
205 + @announcements = Announcement.published
223 end
206 end
224 if recent!=nil
207 if recent!=nil
225 recent_id = recent.to_i
208 recent_id = recent.to_i
226 @announcements = @announcements.find_all { |a| a.id > recent_id }
209 @announcements = @announcements.find_all { |a| a.id > recent_id }
227 end
210 end
228 end
211 end
229
212
230 def prepare_list_information
213 def prepare_list_information
231 @user = User.find(session[:user_id])
214 @user = User.find(session[:user_id])
232 if not GraderConfiguration.multicontests?
215 if not GraderConfiguration.multicontests?
233 @problems = @user.available_problems
216 @problems = @user.available_problems
234 else
217 else
235 @contest_problems = @user.available_problems_group_by_contests
218 @contest_problems = @user.available_problems_group_by_contests
236 @problems = @user.available_problems
219 @problems = @user.available_problems
237 end
220 end
238 @prob_submissions = {}
221 @prob_submissions = {}
239 @problems.each do |p|
222 @problems.each do |p|
240 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
223 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
241 if sub!=nil
224 if sub!=nil
242 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
225 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
243 else
226 else
244 @prob_submissions[p.id] = { :count => 0, :submission => nil }
227 @prob_submissions[p.id] = { :count => 0, :submission => nil }
245 end
228 end
246 end
229 end
247 prepare_announcements
230 prepare_announcements
248 end
231 end
249
232
250 def check_viewability
233 def check_viewability
251 @user = User.find(session[:user_id])
234 @user = User.find(session[:user_id])
252 if (!GraderConfiguration.show_tasks_to?(@user)) and
235 if (!GraderConfiguration.show_tasks_to?(@user)) and
253 ((action_name=='submission') or (action_name=='submit'))
236 ((action_name=='submission') or (action_name=='submit'))
254 redirect_to :action => 'list' and return
237 redirect_to :action => 'list' and return
255 end
238 end
256 end
239 end
257
240
258 def prepare_grading_result(submission)
241 def prepare_grading_result(submission)
259 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
242 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
260 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
243 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
261 else
244 else
262 # guess task info from problem.full_score
245 # guess task info from problem.full_score
263 cases = submission.problem.full_score / 10
246 cases = submission.problem.full_score / 10
264 grading_info = {
247 grading_info = {
265 'testruns' => cases,
248 'testruns' => cases,
266 'testcases' => cases
249 'testcases' => cases
267 }
250 }
268 end
251 end
269 @test_runs = []
252 @test_runs = []
270 if grading_info['testruns'].is_a? Integer
253 if grading_info['testruns'].is_a? Integer
271 trun_count = grading_info['testruns']
254 trun_count = grading_info['testruns']
272 trun_count.times do |i|
255 trun_count.times do |i|
273 @test_runs << [ read_grading_result(@user.login,
256 @test_runs << [ read_grading_result(@user.login,
274 submission.problem.name,
257 submission.problem.name,
275 submission.id,
258 submission.id,
276 i+1) ]
259 i+1) ]
277 end
260 end
278 else
261 else
279 grading_info['testruns'].keys.sort.each do |num|
262 grading_info['testruns'].keys.sort.each do |num|
280 run = []
263 run = []
281 testrun = grading_info['testruns'][num]
264 testrun = grading_info['testruns'][num]
282 testrun.each do |c|
265 testrun.each do |c|
283 run << read_grading_result(@user.login,
266 run << read_grading_result(@user.login,
284 submission.problem.name,
267 submission.problem.name,
285 submission.id,
268 submission.id,
286 c)
269 c)
287 end
270 end
288 @test_runs << run
271 @test_runs << run
289 end
272 end
290 end
273 end
291 end
274 end
292
275
293 def grading_result_dir(user_name, problem_name, submission_id, case_num)
276 def grading_result_dir(user_name, problem_name, submission_id, case_num)
294 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
277 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
295 end
278 end
296
279
297 def output_filename(user_name, problem_name, submission_id, case_num)
280 def output_filename(user_name, problem_name, submission_id, case_num)
298 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
281 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
299 return "#{dir}/output.txt"
282 return "#{dir}/output.txt"
300 end
283 end
301
284
302 def read_grading_result(user_name, problem_name, submission_id, case_num)
285 def read_grading_result(user_name, problem_name, submission_id, case_num)
303 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
286 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
304 result_file_name = "#{dir}/result"
287 result_file_name = "#{dir}/result"
305 if !FileTest.exists?(result_file_name)
288 if !FileTest.exists?(result_file_name)
306 return {:num => case_num, :msg => 'program did not run'}
289 return {:num => case_num, :msg => 'program did not run'}
307 else
290 else
308 results = File.open(result_file_name).readlines
291 results = File.open(result_file_name).readlines
309 run_stat = extract_running_stat(results)
292 run_stat = extract_running_stat(results)
310 output_filename = "#{dir}/output.txt"
293 output_filename = "#{dir}/output.txt"
311 if FileTest.exists?(output_filename)
294 if FileTest.exists?(output_filename)
312 output_file = true
295 output_file = true
313 output_size = File.size(output_filename)
296 output_size = File.size(output_filename)
314 else
297 else
315 output_file = false
298 output_file = false
316 output_size = 0
299 output_size = 0
317 end
300 end
318
301
@@ -1,281 +1,294
1 class ProblemsController < ApplicationController
1 class ProblemsController < ApplicationController
2
2
3 - before_filter :authenticate, :authorization
3 + before_action :authenticate, :authorization
4 + before_action :testcase_authorization, only: [:show_testcase]
4
5
5 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_score
8 in_place_edit_for :problem, :full_score
8
9
9 def index
10 def index
10 - @problems = Problem.find(:all, :order => 'date_added DESC')
11 + @problems = Problem.order(date_added: :desc)
11 end
12 end
12
13
13 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 verify :method => :post, :only => [ :create, :quick_create,
15 verify :method => :post, :only => [ :create, :quick_create,
15 :do_manage,
16 :do_manage,
16 :do_import,
17 :do_import,
17 ],
18 ],
18 :redirect_to => { :action => :index }
19 :redirect_to => { :action => :index }
19
20
20 def show
21 def show
21 @problem = Problem.find(params[:id])
22 @problem = Problem.find(params[:id])
22 end
23 end
23
24
24 def new
25 def new
25 @problem = Problem.new
26 @problem = Problem.new
26 @description = nil
27 @description = nil
27 end
28 end
28
29
29 def create
30 def create
30 - @problem = Problem.new(params[:problem])
31 + @problem = Problem.new(problem_params)
31 @description = Description.new(params[:description])
32 @description = Description.new(params[:description])
32 if @description.body!=''
33 if @description.body!=''
33 if !@description.save
34 if !@description.save
34 render :action => new and return
35 render :action => new and return
35 end
36 end
36 else
37 else
37 @description = nil
38 @description = nil
38 end
39 end
39 @problem.description = @description
40 @problem.description = @description
40 if @problem.save
41 if @problem.save
41 flash[:notice] = 'Problem was successfully created.'
42 flash[:notice] = 'Problem was successfully created.'
42 redirect_to action: :index
43 redirect_to action: :index
43 else
44 else
44 render :action => 'new'
45 render :action => 'new'
45 end
46 end
46 end
47 end
47
48
48 def quick_create
49 def quick_create
49 - @problem = Problem.new(params[:problem])
50 + @problem = Problem.new(problem_params)
50 @problem.full_name = @problem.name if @problem.full_name == ''
51 @problem.full_name = @problem.name if @problem.full_name == ''
51 @problem.full_score = 100
52 @problem.full_score = 100
52 @problem.available = false
53 @problem.available = false
53 @problem.test_allowed = true
54 @problem.test_allowed = true
54 @problem.output_only = false
55 @problem.output_only = false
55 @problem.date_added = Time.new
56 @problem.date_added = Time.new
56 if @problem.save
57 if @problem.save
57 flash[:notice] = 'Problem was successfully created.'
58 flash[:notice] = 'Problem was successfully created.'
58 redirect_to action: :index
59 redirect_to action: :index
59 else
60 else
60 flash[:notice] = 'Error saving problem'
61 flash[:notice] = 'Error saving problem'
61 redirect_to action: :index
62 redirect_to action: :index
62 end
63 end
63 end
64 end
64
65
65 def edit
66 def edit
66 @problem = Problem.find(params[:id])
67 @problem = Problem.find(params[:id])
67 @description = @problem.description
68 @description = @problem.description
68 end
69 end
69
70
70 def update
71 def update
71 @problem = Problem.find(params[:id])
72 @problem = Problem.find(params[:id])
72 @description = @problem.description
73 @description = @problem.description
73 if @description.nil? and params[:description][:body]!=''
74 if @description.nil? and params[:description][:body]!=''
74 @description = Description.new(params[:description])
75 @description = Description.new(params[:description])
75 if !@description.save
76 if !@description.save
76 flash[:notice] = 'Error saving description'
77 flash[:notice] = 'Error saving description'
77 render :action => 'edit' and return
78 render :action => 'edit' and return
78 end
79 end
79 @problem.description = @description
80 @problem.description = @description
80 elsif @description
81 elsif @description
81 if !@description.update_attributes(params[:description])
82 if !@description.update_attributes(params[:description])
82 flash[:notice] = 'Error saving description'
83 flash[:notice] = 'Error saving description'
83 render :action => 'edit' and return
84 render :action => 'edit' and return
84 end
85 end
85 end
86 end
86 if params[:file] and params[:file].content_type != 'application/pdf'
87 if params[:file] and params[:file].content_type != 'application/pdf'
87 flash[:notice] = 'Error: Uploaded file is not PDF'
88 flash[:notice] = 'Error: Uploaded file is not PDF'
88 render :action => 'edit' and return
89 render :action => 'edit' and return
89 end
90 end
90 - if @problem.update_attributes(params[:problem])
91 + if @problem.update_attributes(problem_params)
91 flash[:notice] = 'Problem was successfully updated.'
92 flash[:notice] = 'Problem was successfully updated.'
92 unless params[:file] == nil or params[:file] == ''
93 unless params[:file] == nil or params[:file] == ''
93 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 if not FileTest.exists? out_dirname
96 if not FileTest.exists? out_dirname
96 Dir.mkdir out_dirname
97 Dir.mkdir out_dirname
97 end
98 end
98
99
99 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
100 if FileTest.exists? out_filename
101 if FileTest.exists? out_filename
101 File.delete out_filename
102 File.delete out_filename
102 end
103 end
103
104
104 File.open(out_filename,"wb") do |file|
105 File.open(out_filename,"wb") do |file|
105 file.write(params[:file].read)
106 file.write(params[:file].read)
106 end
107 end
107 @problem.description_filename = "#{@problem.name}.pdf"
108 @problem.description_filename = "#{@problem.name}.pdf"
108 @problem.save
109 @problem.save
109 end
110 end
110 redirect_to :action => 'show', :id => @problem
111 redirect_to :action => 'show', :id => @problem
111 else
112 else
112 render :action => 'edit'
113 render :action => 'edit'
113 end
114 end
114 end
115 end
115
116
116 def destroy
117 def destroy
117 p = Problem.find(params[:id]).destroy
118 p = Problem.find(params[:id]).destroy
118 redirect_to action: :index
119 redirect_to action: :index
119 end
120 end
120
121
121 def toggle
122 def toggle
122 @problem = Problem.find(params[:id])
123 @problem = Problem.find(params[:id])
123 @problem.update_attributes(available: !(@problem.available) )
124 @problem.update_attributes(available: !(@problem.available) )
124 respond_to do |format|
125 respond_to do |format|
125 format.js { }
126 format.js { }
126 end
127 end
127 end
128 end
128
129
129 def toggle_test
130 def toggle_test
130 @problem = Problem.find(params[:id])
131 @problem = Problem.find(params[:id])
131 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 respond_to do |format|
133 respond_to do |format|
133 format.js { }
134 format.js { }
134 end
135 end
135 end
136 end
136
137
138 + def toggle_view_testcase
139 + @problem = Problem.find(params[:id])
140 + @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 + respond_to do |format|
142 + format.js { }
143 + end
144 + end
145 +
137 def turn_all_off
146 def turn_all_off
138 - Problem.find(:all,
147 + Problem.available.all.each do |problem|
139 - :conditions => "available = 1").each do |problem|
140 problem.available = false
148 problem.available = false
141 problem.save
149 problem.save
142 end
150 end
143 redirect_to action: :index
151 redirect_to action: :index
144 end
152 end
145
153
146 def turn_all_on
154 def turn_all_on
147 - Problem.find(:all,
155 + Problem.where.not(available: true).each do |problem|
148 - :conditions => "available = 0").each do |problem|
149 problem.available = true
156 problem.available = true
150 problem.save
157 problem.save
151 end
158 end
152 redirect_to action: :index
159 redirect_to action: :index
153 end
160 end
154
161
155 def stat
162 def stat
156 @problem = Problem.find(params[:id])
163 @problem = Problem.find(params[:id])
157 unless @problem.available or session[:admin]
164 unless @problem.available or session[:admin]
158 redirect_to :controller => 'main', :action => 'list'
165 redirect_to :controller => 'main', :action => 'list'
159 return
166 return
160 end
167 end
161 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
168 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
162
169
163 #stat summary
170 #stat summary
164 range =65
171 range =65
165 @histogram = { data: Array.new(range,0), summary: {} }
172 @histogram = { data: Array.new(range,0), summary: {} }
166 user = Hash.new(0)
173 user = Hash.new(0)
167 @submissions.find_each do |sub|
174 @submissions.find_each do |sub|
168 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
169 @histogram[:data][d.to_i] += 1 if d < range
176 @histogram[:data][d.to_i] += 1 if d < range
170 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
171 end
178 end
172 @histogram[:summary][:max] = [@histogram[:data].max,1].max
179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
173
180
174 @summary = { attempt: user.count, solve: 0 }
181 @summary = { attempt: user.count, solve: 0 }
175 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
176 end
183 end
177
184
178 def manage
185 def manage
179 - @problems = Problem.find(:all, :order => 'date_added DESC')
186 + @problems = Problem.order(date_added: :desc)
180 end
187 end
181
188
182 def do_manage
189 def do_manage
183 if params.has_key? 'change_date_added'
190 if params.has_key? 'change_date_added'
184 change_date_added
191 change_date_added
185 elsif params.has_key? 'add_to_contest'
192 elsif params.has_key? 'add_to_contest'
186 add_to_contest
193 add_to_contest
187 elsif params.has_key? 'enable_problem'
194 elsif params.has_key? 'enable_problem'
188 set_available(true)
195 set_available(true)
189 elsif params.has_key? 'disable_problem'
196 elsif params.has_key? 'disable_problem'
190 set_available(false)
197 set_available(false)
191 end
198 end
192 redirect_to :action => 'manage'
199 redirect_to :action => 'manage'
193 end
200 end
194
201
195 def import
202 def import
196 @allow_test_pair_import = allow_test_pair_import?
203 @allow_test_pair_import = allow_test_pair_import?
197 end
204 end
198
205
199 def do_import
206 def do_import
200 old_problem = Problem.find_by_name(params[:name])
207 old_problem = Problem.find_by_name(params[:name])
201 if !allow_test_pair_import? and params.has_key? :import_to_db
208 if !allow_test_pair_import? and params.has_key? :import_to_db
202 params.delete :import_to_db
209 params.delete :import_to_db
203 end
210 end
204 @problem, import_log = Problem.create_from_import_form_params(params,
211 @problem, import_log = Problem.create_from_import_form_params(params,
205 old_problem)
212 old_problem)
206
213
207 if !@problem.errors.empty?
214 if !@problem.errors.empty?
208 render :action => 'import' and return
215 render :action => 'import' and return
209 end
216 end
210
217
211 if old_problem!=nil
218 if old_problem!=nil
212 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
219 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
213 end
220 end
214 @log = import_log
221 @log = import_log
215 end
222 end
216
223
217 def remove_contest
224 def remove_contest
218 problem = Problem.find(params[:id])
225 problem = Problem.find(params[:id])
219 contest = Contest.find(params[:contest_id])
226 contest = Contest.find(params[:contest_id])
220 if problem!=nil and contest!=nil
227 if problem!=nil and contest!=nil
221 problem.contests.delete(contest)
228 problem.contests.delete(contest)
222 end
229 end
223 redirect_to :action => 'manage'
230 redirect_to :action => 'manage'
224 end
231 end
225
232
226 ##################################
233 ##################################
227 protected
234 protected
228
235
229 def allow_test_pair_import?
236 def allow_test_pair_import?
230 if defined? ALLOW_TEST_PAIR_IMPORT
237 if defined? ALLOW_TEST_PAIR_IMPORT
231 return ALLOW_TEST_PAIR_IMPORT
238 return ALLOW_TEST_PAIR_IMPORT
232 else
239 else
233 return false
240 return false
234 end
241 end
235 end
242 end
236
243
237 def change_date_added
244 def change_date_added
238 problems = get_problems_from_params
245 problems = get_problems_from_params
239 year = params[:date_added][:year].to_i
246 year = params[:date_added][:year].to_i
240 month = params[:date_added][:month].to_i
247 month = params[:date_added][:month].to_i
241 day = params[:date_added][:day].to_i
248 day = params[:date_added][:day].to_i
242 date = Date.new(year,month,day)
249 date = Date.new(year,month,day)
243 problems.each do |p|
250 problems.each do |p|
244 p.date_added = date
251 p.date_added = date
245 p.save
252 p.save
246 end
253 end
247 end
254 end
248
255
249 def add_to_contest
256 def add_to_contest
250 problems = get_problems_from_params
257 problems = get_problems_from_params
251 contest = Contest.find(params[:contest][:id])
258 contest = Contest.find(params[:contest][:id])
252 if contest!=nil and contest.enabled
259 if contest!=nil and contest.enabled
253 problems.each do |p|
260 problems.each do |p|
254 p.contests << contest
261 p.contests << contest
255 end
262 end
256 end
263 end
257 end
264 end
258
265
259 def set_available(avail)
266 def set_available(avail)
260 problems = get_problems_from_params
267 problems = get_problems_from_params
261 problems.each do |p|
268 problems.each do |p|
262 p.available = avail
269 p.available = avail
263 p.save
270 p.save
264 end
271 end
265 end
272 end
266
273
267 def get_problems_from_params
274 def get_problems_from_params
268 problems = []
275 problems = []
269 params.keys.each do |k|
276 params.keys.each do |k|
270 if k.index('prob-')==0
277 if k.index('prob-')==0
271 name, id, order = k.split('-')
278 name, id, order = k.split('-')
272 problems << Problem.find(id)
279 problems << Problem.find(id)
273 end
280 end
274 end
281 end
275 problems
282 problems
276 end
283 end
277
284
278 def get_problems_stat
285 def get_problems_stat
279 end
286 end
280
287
288 + private
289 +
290 + def problem_params
291 + params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description)
292 + end
293 +
281 end
294 end
@@ -1,174 +1,172
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 - if GraderConfiguration["right.user_view_submission"]
12 + admin_authorization unless GraderConfiguration["right.user_view_submission"]
13 - return true;
14 - end
15 -
16 - admin_authorization
17 }
13 }
18
14
19 def max_score
15 def max_score
20 end
16 end
21
17
22 def current_score
18 def current_score
23 - @problems = Problem.find_available_problems
19 + @problems = Problem.available_problems
24 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
20 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
25 @scorearray = calculate_max_score(@problems, @users,0,0,true)
21 @scorearray = calculate_max_score(@problems, @users,0,0,true)
26
22
27 #rencer accordingly
23 #rencer accordingly
28 if params[:button] == 'download' then
24 if params[:button] == 'download' then
29 csv = gen_csv_from_scorearray(@scorearray,@problems)
25 csv = gen_csv_from_scorearray(@scorearray,@problems)
30 send_data csv, filename: 'max_score.csv'
26 send_data csv, filename: 'max_score.csv'
31 else
27 else
32 #render template: 'user_admin/user_stat'
28 #render template: 'user_admin/user_stat'
33 render 'current_score'
29 render 'current_score'
34 end
30 end
35 end
31 end
36
32
37 def show_max_score
33 def show_max_score
38 #process parameters
34 #process parameters
39 #problems
35 #problems
40 @problems = []
36 @problems = []
41 - params[:problem_id].each do |id|
37 + if params[:problem_id]
42 - next unless id.strip != ""
38 + params[:problem_id].each do |id|
43 - pid = Problem.find_by_id(id.to_i)
39 + next unless id.strip != ""
44 - @problems << pid if pid
40 + pid = Problem.find_by_id(id.to_i)
41 + @problems << pid if pid
42 + end
45 end
43 end
46
44
47 #users
45 #users
48 @users = if params[:user] == "all" then
46 @users = if params[:user] == "all" then
49 - User.find(:all, :include => [:contests, :contest_stat])
47 + User.includes(:contests).includes(:contest_stat)
50 else
48 else
51 User.includes(:contests).includes(:contest_stat).where(enabled: true)
49 User.includes(:contests).includes(:contest_stat).where(enabled: true)
52 end
50 end
53
51
54 #set up range from param
52 #set up range from param
55 - since_id = params.fetch(:min_id, 0).to_i
53 + @since_id = params.fetch(:from_id, 0).to_i
56 - until_id = params.fetch(:max_id, 0).to_i
54 + @until_id = params.fetch(:to_id, 0).to_i
57
55
58 #calculate the routine
56 #calculate the routine
59 - @scorearray = calculate_max_score(@problems, @users,since_id,until_id)
57 + @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
60
58
61 #rencer accordingly
59 #rencer accordingly
62 if params[:button] == 'download' then
60 if params[:button] == 'download' then
63 csv = gen_csv_from_scorearray(@scorearray,@problems)
61 csv = gen_csv_from_scorearray(@scorearray,@problems)
64 send_data csv, filename: 'max_score.csv'
62 send_data csv, filename: 'max_score.csv'
65 else
63 else
66 #render template: 'user_admin/user_stat'
64 #render template: 'user_admin/user_stat'
67 render 'max_score'
65 render 'max_score'
68 end
66 end
69
67
70 end
68 end
71
69
72 def score
70 def score
73 if params[:commit] == 'download csv'
71 if params[:commit] == 'download csv'
74 @problems = Problem.all
72 @problems = Problem.all
75 else
73 else
76 - @problems = Problem.find_available_problems
74 + @problems = Problem.available_problems
77 end
75 end
78 - @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
76 + @users = User.includes(:contests, :contest_stat).where(enabled: true)
79 @scorearray = Array.new
77 @scorearray = Array.new
80 @users.each do |u|
78 @users.each do |u|
81 ustat = Array.new
79 ustat = Array.new
82 ustat[0] = u
80 ustat[0] = u
83 @problems.each do |p|
81 @problems.each do |p|
84 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
82 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
85 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
83 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
86 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
84 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
87 else
85 else
88 ustat << [0,false]
86 ustat << [0,false]
89 end
87 end
90 end
88 end
91 @scorearray << ustat
89 @scorearray << ustat
92 end
90 end
93 if params[:commit] == 'download csv' then
91 if params[:commit] == 'download csv' then
94 csv = gen_csv_from_scorearray(@scorearray,@problems)
92 csv = gen_csv_from_scorearray(@scorearray,@problems)
95 send_data csv, filename: 'last_score.csv'
93 send_data csv, filename: 'last_score.csv'
96 else
94 else
97 render template: 'user_admin/user_stat'
95 render template: 'user_admin/user_stat'
98 end
96 end
99
97
100 end
98 end
101
99
102 def login_stat
100 def login_stat
103 @logins = Array.new
101 @logins = Array.new
104
102
105 date_and_time = '%Y-%m-%d %H:%M'
103 date_and_time = '%Y-%m-%d %H:%M'
106 begin
104 begin
107 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
105 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
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)
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)
109 rescue
107 rescue
110 @since_time = DateTime.new(1000,1,1)
108 @since_time = DateTime.new(1000,1,1)
111 end
109 end
112 begin
110 begin
113 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
111 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
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)
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)
115 rescue
113 rescue
116 @until_time = DateTime.new(3000,1,1)
114 @until_time = DateTime.new(3000,1,1)
117 end
115 end
118
116
119 User.all.each do |user|
117 User.all.each do |user|
120 @logins << { id: user.id,
118 @logins << { id: user.id,
121 login: user.login,
119 login: user.login,
122 full_name: user.full_name,
120 full_name: user.full_name,
123 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
121 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
124 user.id,@since_time,@until_time)
122 user.id,@since_time,@until_time)
125 .count(:id),
123 .count(:id),
126 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
124 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
127 user.id,@since_time,@until_time)
125 user.id,@since_time,@until_time)
128 .minimum(:created_at),
126 .minimum(:created_at),
129 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
127 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
130 user.id,@since_time,@until_time)
128 user.id,@since_time,@until_time)
131 .maximum(:created_at),
129 .maximum(:created_at),
132 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
130 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
133 user.id,@since_time,@until_time)
131 user.id,@since_time,@until_time)
134 .select(:ip_address).uniq
132 .select(:ip_address).uniq
135
133
136 }
134 }
137 end
135 end
138 end
136 end
139
137
140 def submission_stat
138 def submission_stat
141
139
142 date_and_time = '%Y-%m-%d %H:%M'
140 date_and_time = '%Y-%m-%d %H:%M'
143 begin
141 begin
144 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
142 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
145 rescue
143 rescue
146 @since_time = DateTime.new(1000,1,1)
144 @since_time = DateTime.new(1000,1,1)
147 end
145 end
148 begin
146 begin
149 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
147 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
150 rescue
148 rescue
151 @until_time = DateTime.new(3000,1,1)
149 @until_time = DateTime.new(3000,1,1)
152 end
150 end
153
151
154 @submissions = {}
152 @submissions = {}
155
153
156 User.find_each do |user|
154 User.find_each do |user|
157 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
155 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
158 end
156 end
159
157
160 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
158 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
161 if @submissions[s.user_id]
159 if @submissions[s.user_id]
162 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
160 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
163 a = Problem.find_by_id(s.problem_id)
161 a = Problem.find_by_id(s.problem_id)
164 @submissions[s.user_id][:sub][s.problem_id] =
162 @submissions[s.user_id][:sub][s.problem_id] =
165 { prob_name: (a ? a.full_name : '(NULL)'),
163 { prob_name: (a ? a.full_name : '(NULL)'),
166 sub_ids: [s.id] }
164 sub_ids: [s.id] }
167 else
165 else
168 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
166 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
169 end
167 end
170 @submissions[s.user_id][:count] += 1
168 @submissions[s.user_id][:count] += 1
171 end
169 end
172 end
170 end
173 end
171 end
174
172
@@ -1,62 +1,67
1 class SiteController < ApplicationController
1 class SiteController < ApplicationController
2
2
3 before_filter :site_admin_authorization, :except => 'login'
3 before_filter :site_admin_authorization, :except => 'login'
4
4
5 def login
5 def login
6 # Site administrator login
6 # Site administrator login
7 - @countries = Country.find(:all, :include => :sites)
7 + @countries = Country.includes(:sites).all
8 @country_select = @countries.collect { |c| [c.name, c.id] }
8 @country_select = @countries.collect { |c| [c.name, c.id] }
9
9
10 @country_select_with_all = [['Any',0]]
10 @country_select_with_all = [['Any',0]]
11 @countries.each do |country|
11 @countries.each do |country|
12 @country_select_with_all << [country.name, country.id]
12 @country_select_with_all << [country.name, country.id]
13 end
13 end
14
14
15 @site_select = []
15 @site_select = []
16 @countries.each do |country|
16 @countries.each do |country|
17 country.sites.each do |site|
17 country.sites.each do |site|
18 @site_select << ["#{site.name}, #{country.name}", site.id]
18 @site_select << ["#{site.name}, #{country.name}", site.id]
19 end
19 end
20 end
20 end
21
21
22 @default_site = Site.first if !GraderConfiguration['contest.multisites']
22 @default_site = Site.first if !GraderConfiguration['contest.multisites']
23
23
24 render :action => 'login', :layout => 'empty'
24 render :action => 'login', :layout => 'empty'
25 end
25 end
26
26
27 def index
27 def index
28 if @site.started
28 if @site.started
29 render :action => 'started', :layout => 'empty'
29 render :action => 'started', :layout => 'empty'
30 else
30 else
31 render :action => 'prompt', :layout => 'empty'
31 render :action => 'prompt', :layout => 'empty'
32 end
32 end
33 end
33 end
34
34
35 def start
35 def start
36 @site.started = true
36 @site.started = true
37 @site.start_time = Time.new.gmtime
37 @site.start_time = Time.new.gmtime
38 @site.save
38 @site.save
39 redirect_to :action => 'index'
39 redirect_to :action => 'index'
40 end
40 end
41
41
42 def logout
42 def logout
43 reset_session
43 reset_session
44 redirect_to :controller => 'main', :action => 'login'
44 redirect_to :controller => 'main', :action => 'login'
45 end
45 end
46
46
47 protected
47 protected
48 def site_admin_authorization
48 def site_admin_authorization
49 if session[:site_id]==nil
49 if session[:site_id]==nil
50 redirect_to :controller => 'site', :action => 'login' and return
50 redirect_to :controller => 'site', :action => 'login' and return
51 end
51 end
52 begin
52 begin
53 @site = Site.find(session[:site_id], :include => :country)
53 @site = Site.find(session[:site_id], :include => :country)
54 rescue ActiveRecord::RecordNotFound
54 rescue ActiveRecord::RecordNotFound
55 @site = nil
55 @site = nil
56 end
56 end
57 if @site==nil
57 if @site==nil
58 redirect_to :controller => 'site', :action => 'login' and return
58 redirect_to :controller => 'site', :action => 'login' and return
59 end
59 end
60 end
60 end
61
61
62 + private
63 + def site_params
64 + params.require(:site).permit()
65 + end
66 +
62 end
67 end
@@ -1,91 +1,97
1 class SitesController < ApplicationController
1 class SitesController < ApplicationController
2
2
3 before_filter :admin_authorization
3 before_filter :admin_authorization
4
4
5 # GET /sites
5 # GET /sites
6 # GET /sites.xml
6 # GET /sites.xml
7 def index
7 def index
8 - @sites = Site.find(:all, :order => 'country_id')
8 + @sites = Site.order(:country_id)
9
9
10 respond_to do |format|
10 respond_to do |format|
11 format.html # index.html.erb
11 format.html # index.html.erb
12 format.xml { render :xml => @sites }
12 format.xml { render :xml => @sites }
13 end
13 end
14 end
14 end
15
15
16 # GET /sites/1
16 # GET /sites/1
17 # GET /sites/1.xml
17 # GET /sites/1.xml
18 def show
18 def show
19 @site = Site.find(params[:id])
19 @site = Site.find(params[:id])
20
20
21 respond_to do |format|
21 respond_to do |format|
22 format.html # show.html.erb
22 format.html # show.html.erb
23 format.xml { render :xml => @site }
23 format.xml { render :xml => @site }
24 end
24 end
25 end
25 end
26
26
27 # GET /sites/new
27 # GET /sites/new
28 # GET /sites/new.xml
28 # GET /sites/new.xml
29 def new
29 def new
30 @site = Site.new
30 @site = Site.new
31
31
32 respond_to do |format|
32 respond_to do |format|
33 format.html # new.html.erb
33 format.html # new.html.erb
34 format.xml { render :xml => @site }
34 format.xml { render :xml => @site }
35 end
35 end
36 end
36 end
37
37
38 # GET /sites/1/edit
38 # GET /sites/1/edit
39 def edit
39 def edit
40 @site = Site.find(params[:id])
40 @site = Site.find(params[:id])
41 end
41 end
42
42
43 # POST /sites
43 # POST /sites
44 # POST /sites.xml
44 # POST /sites.xml
45 def create
45 def create
46 @site = Site.new(params[:site])
46 @site = Site.new(params[:site])
47 @site.clear_start_time_if_not_started
47 @site.clear_start_time_if_not_started
48
48
49 respond_to do |format|
49 respond_to do |format|
50 if @site.save
50 if @site.save
51 flash[:notice] = 'Site was successfully created.'
51 flash[:notice] = 'Site was successfully created.'
52 format.html { redirect_to(@site) }
52 format.html { redirect_to(@site) }
53 format.xml { render :xml => @site, :status => :created, :location => @site }
53 format.xml { render :xml => @site, :status => :created, :location => @site }
54 else
54 else
55 format.html { render :action => "new" }
55 format.html { render :action => "new" }
56 format.xml { render :xml => @site.errors, :status => :unprocessable_entity }
56 format.xml { render :xml => @site.errors, :status => :unprocessable_entity }
57 end
57 end
58 end
58 end
59 end
59 end
60
60
61 # PUT /sites/1
61 # PUT /sites/1
62 # PUT /sites/1.xml
62 # PUT /sites/1.xml
63 def update
63 def update
64 @site = Site.find(params[:id])
64 @site = Site.find(params[:id])
65 @site.clear_start_time_if_not_started
65 @site.clear_start_time_if_not_started
66
66
67 respond_to do |format|
67 respond_to do |format|
68 - if @site.update_attributes(params[:site])
68 + if @site.update_attributes(site_params)
69 flash[:notice] = 'Site was successfully updated.'
69 flash[:notice] = 'Site was successfully updated.'
70 format.html { redirect_to(@site) }
70 format.html { redirect_to(@site) }
71 format.xml { head :ok }
71 format.xml { head :ok }
72 else
72 else
73 format.html { render :action => "edit" }
73 format.html { render :action => "edit" }
74 format.xml { render :xml => @site.errors, :status => :unprocessable_entity }
74 format.xml { render :xml => @site.errors, :status => :unprocessable_entity }
75 end
75 end
76 end
76 end
77 end
77 end
78
78
79 # DELETE /sites/1
79 # DELETE /sites/1
80 # DELETE /sites/1.xml
80 # DELETE /sites/1.xml
81 def destroy
81 def destroy
82 @site = Site.find(params[:id])
82 @site = Site.find(params[:id])
83 @site.destroy
83 @site.destroy
84
84
85 respond_to do |format|
85 respond_to do |format|
86 format.html { redirect_to(sites_url) }
86 format.html { redirect_to(sites_url) }
87 format.xml { head :ok }
87 format.xml { head :ok }
88 end
88 end
89 end
89 end
90
90
91 + private
92 +
93 + def site_params
94 + params.require(:site).permit(:name,:started,:start_time,:country_id,:password)
95 + end
96 +
91 end
97 end
@@ -1,137 +1,108
1 class SubmissionsController < ApplicationController
1 class SubmissionsController < ApplicationController
2 - before_filter :authenticate
2 + before_action :authenticate
3 - before_filter :submission_authorization, only: [:show, :direct_edit_submission]
3 + before_action :submission_authorization, only: [:show, :direct_edit_submission, :download, :edit]
4 + before_action :admin_authorization, only: [:rejudge]
4
5
5 # GET /submissions
6 # GET /submissions
6 # GET /submissions.json
7 # GET /submissions.json
7 # Show problem selection and user's submission of that problem
8 # Show problem selection and user's submission of that problem
8 def index
9 def index
9 @user = @current_user
10 @user = @current_user
10 @problems = @user.available_problems
11 @problems = @user.available_problems
11
12
12 if params[:problem_id]==nil
13 if params[:problem_id]==nil
13 @problem = nil
14 @problem = nil
14 @submissions = nil
15 @submissions = nil
15 else
16 else
16 @problem = Problem.find_by_id(params[:problem_id])
17 @problem = Problem.find_by_id(params[:problem_id])
17 if (@problem == nil) or (not @problem.available)
18 if (@problem == nil) or (not @problem.available)
18 redirect_to main_list_path
19 redirect_to main_list_path
19 flash[:notice] = 'Error: submissions for that problem are not viewable.'
20 flash[:notice] = 'Error: submissions for that problem are not viewable.'
20 return
21 return
21 end
22 end
22 - @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
23 + @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
23 end
24 end
24 end
25 end
25
26
26 # GET /submissions/1
27 # GET /submissions/1
27 # GET /submissions/1.json
28 # GET /submissions/1.json
28 def show
29 def show
29 @submission = Submission.find(params[:id])
30 @submission = Submission.find(params[:id])
30
31
31 #log the viewing
32 #log the viewing
32 user = User.find(session[:user_id])
33 user = User.find(session[:user_id])
33 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35 +
36 + @task = @submission.task
37 + end
38 +
39 + def download
40 + @submission = Submission.find(params[:id])
41 + send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 + end
43 +
44 + def compiler_msg
45 + @submission = Submission.find(params[:id])
46 + respond_to do |format|
47 + format.js
48 + end
34 end
49 end
35
50
36 #on-site new submission on specific problem
51 #on-site new submission on specific problem
37 def direct_edit_problem
52 def direct_edit_problem
38 @problem = Problem.find(params[:problem_id])
53 @problem = Problem.find(params[:problem_id])
39 @source = ''
54 @source = ''
40 render 'edit'
55 render 'edit'
41 end
56 end
42
57
43 # GET /submissions/1/edit
58 # GET /submissions/1/edit
44 def edit
59 def edit
45 @submission = Submission.find(params[:id])
60 @submission = Submission.find(params[:id])
46 @source = @submission.source.to_s
61 @source = @submission.source.to_s
47 @problem = @submission.problem
62 @problem = @submission.problem
48 @lang_id = @submission.language.id
63 @lang_id = @submission.language.id
49 end
64 end
50
65
51
66
52 def get_latest_submission_status
67 def get_latest_submission_status
53 @problem = Problem.find(params[:pid])
68 @problem = Problem.find(params[:pid])
54 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
69 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
55 puts User.find(params[:uid]).login
70 puts User.find(params[:uid]).login
56 puts Problem.find(params[:pid]).name
71 puts Problem.find(params[:pid]).name
57 puts 'nil' unless @submission
72 puts 'nil' unless @submission
58 respond_to do |format|
73 respond_to do |format|
59 format.js
74 format.js
60 end
75 end
61 end
76 end
62
77
63 - # # GET /submissions/new
78 + # GET /submissions/:id/rejudge
64 - # # GET /submissions/new.json
79 + def rejudge
65 - # def new
80 + @submission = Submission.find(params[:id])
66 - # @submission = Submission.new
81 + @task = @submission.task
67 - #
82 + @task.status_inqueue! if @task
68 - # respond_to do |format|
83 + respond_to do |format|
69 - # format.html # new.html.erb
84 + format.js
70 - # format.json { render json: @submission }
85 + end
71 - # end
86 + end
72 - # end
73 - #
74 - #
75 - # # POST /submissions
76 - # # POST /submissions.json
77 - # def create
78 - # @submission = Submission.new(params[:submission])
79 - #
80 - # respond_to do |format|
81 - # if @submission.save
82 - # format.html { redirect_to @submission, notice: 'Submission was successfully created.' }
83 - # format.json { render json: @submission, status: :created, location: @submission }
84 - # else
85 - # format.html { render action: "new" }
86 - # format.json { render json: @submission.errors, status: :unprocessable_entity }
87 - # end
88 - # end
89 - # end
90 - #
91 - # # PUT /submissions/1
92 - # # PUT /submissions/1.json
93 - # def update
94 - # @submission = Submission.find(params[:id])
95 - #
96 - # respond_to do |format|
97 - # if @submission.update_attributes(params[:submission])
98 - # format.html { redirect_to @submission, notice: 'Submission was successfully updated.' }
99 - # format.json { head :no_content }
100 - # else
101 - # format.html { render action: "edit" }
102 - # format.json { render json: @submission.errors, status: :unprocessable_entity }
103 - # end
104 - # end
105 - # end
106 - #
107 - # # DELETE /submissions/1
108 - # # DELETE /submissions/1.json
109 - # def destroy
110 - # @submission = Submission.find(params[:id])
111 - # @submission.destroy
112 - #
113 - # respond_to do |format|
114 - # format.html { redirect_to submissions_url }
115 - # format.json { head :no_content }
116 - # end
117 - # end
118
87
119 protected
88 protected
89 +
120 def submission_authorization
90 def submission_authorization
121 #admin always has privileged
91 #admin always has privileged
122 if @current_user.admin?
92 if @current_user.admin?
123 return true
93 return true
124 end
94 end
125
95
126 sub = Submission.find(params[:id])
96 sub = Submission.find(params[:id])
127 if sub.problem.available?
97 if sub.problem.available?
128 puts "sub = #{sub.user.id}, current = #{@current_user.id}"
98 puts "sub = #{sub.user.id}, current = #{@current_user.id}"
129 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
99 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
130 end
100 end
131
101
132 #default to NO
102 #default to NO
133 unauthorized_redirect
103 unauthorized_redirect
134 return false
104 return false
135 end
105 end
106 +
136
107
137 end
108 end
@@ -1,538 +1,588
1 require 'csv'
1 require 'csv'
2
2
3 class UserAdminController < ApplicationController
3 class UserAdminController < ApplicationController
4
4
5 include MailHelperMethods
5 include MailHelperMethods
6
6
7 before_filter :admin_authorization
7 before_filter :admin_authorization
8
8
9 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
9 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 verify :method => :post, :only => [
10 verify :method => :post, :only => [
11 :create, :create_from_list,
11 :create, :create_from_list,
12 :update,
12 :update,
13 :manage_contest,
13 :manage_contest,
14 :bulk_mail
14 :bulk_mail
15 ],
15 ],
16 :redirect_to => { :action => :list }
16 :redirect_to => { :action => :list }
17
17
18 def index
18 def index
19 @user_count = User.count
19 @user_count = User.count
20 if params[:page] == 'all'
20 if params[:page] == 'all'
21 @users = User.all
21 @users = User.all
22 @paginated = false
22 @paginated = false
23 else
23 else
24 @users = User.paginate :page => params[:page]
24 @users = User.paginate :page => params[:page]
25 @paginated = true
25 @paginated = true
26 end
26 end
27 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
27 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
28 @contests = Contest.enabled
28 @contests = Contest.enabled
29 end
29 end
30
30
31 def active
31 def active
32 - sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
32 + sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
33 @users = []
33 @users = []
34 sessions.each do |session|
34 sessions.each do |session|
35 if session.data[:user_id]
35 if session.data[:user_id]
36 @users << User.find(session.data[:user_id])
36 @users << User.find(session.data[:user_id])
37 end
37 end
38 end
38 end
39 end
39 end
40
40
41 def show
41 def show
42 @user = User.find(params[:id])
42 @user = User.find(params[:id])
43 end
43 end
44
44
45 def new
45 def new
46 @user = User.new
46 @user = User.new
47 end
47 end
48
48
49 def create
49 def create
50 - @user = User.new(params[:user])
50 + @user = User.new(user_params)
51 @user.activated = true
51 @user.activated = true
52 if @user.save
52 if @user.save
53 flash[:notice] = 'User was successfully created.'
53 flash[:notice] = 'User was successfully created.'
54 redirect_to :action => 'index'
54 redirect_to :action => 'index'
55 else
55 else
56 render :action => 'new'
56 render :action => 'new'
57 end
57 end
58 end
58 end
59
59
60 def clear_last_ip
60 def clear_last_ip
61 @user = User.find(params[:id])
61 @user = User.find(params[:id])
62 @user.last_ip = nil
62 @user.last_ip = nil
63 @user.save
63 @user.save
64 redirect_to action: 'index', page: params[:page]
64 redirect_to action: 'index', page: params[:page]
65 end
65 end
66
66
67 def create_from_list
67 def create_from_list
68 lines = params[:user_list]
68 lines = params[:user_list]
69
69
70 note = []
70 note = []
71
71
72 lines.split("\n").each do |line|
72 lines.split("\n").each do |line|
73 items = line.chomp.split(',')
73 items = line.chomp.split(',')
74 if items.length>=2
74 if items.length>=2
75 login = items[0]
75 login = items[0]
76 full_name = items[1]
76 full_name = items[1]
77 + remark =''
78 + user_alias = ''
77
79
78 added_random_password = false
80 added_random_password = false
79 - if items.length>=3
81 + if items.length >= 3 and items[2].chomp(" ").length > 0;
80 password = items[2].chomp(" ")
82 password = items[2].chomp(" ")
81 - user_alias = (items.length>=4) ? items[3] : login
82 else
83 else
83 password = random_password
84 password = random_password
84 - user_alias = (items.length>=4) ? items[3] : login
85 + add_random_password=true;
85 - added_random_password = true
86 + end
87 +
88 + if items.length>= 4 and items[3].chomp(" ").length > 0;
89 + user_alias = items[3].chomp(" ")
90 + else
91 + user_alias = login
92 + end
93 +
94 + if items.length>=5
95 + remark = items[4].strip;
86 end
96 end
87
97
88 user = User.find_by_login(login)
98 user = User.find_by_login(login)
89 if (user)
99 if (user)
90 user.full_name = full_name
100 user.full_name = full_name
91 user.password = password
101 user.password = password
102 + user.remark = remark
92 else
103 else
93 user = User.new({:login => login,
104 user = User.new({:login => login,
94 :full_name => full_name,
105 :full_name => full_name,
95 :password => password,
106 :password => password,
96 :password_confirmation => password,
107 :password_confirmation => password,
97 - :alias => user_alias})
108 + :alias => user_alias,
109 + :remark => remark})
98 end
110 end
99 user.activated = true
111 user.activated = true
100 user.save
112 user.save
101
113
102 if added_random_password
114 if added_random_password
103 note << "'#{login}' (+)"
115 note << "'#{login}' (+)"
104 else
116 else
105 note << login
117 note << login
106 end
118 end
107 end
119 end
108 end
120 end
109 - flash[:notice] = 'User(s) ' + note.join(', ') +
121 + flash[:success] = 'User(s) ' + note.join(', ') +
110 ' were successfully created. ' +
122 ' were successfully created. ' +
111 '( (+) - created with random passwords.)'
123 '( (+) - created with random passwords.)'
112 redirect_to :action => 'index'
124 redirect_to :action => 'index'
113 end
125 end
114
126
115 def edit
127 def edit
116 @user = User.find(params[:id])
128 @user = User.find(params[:id])
117 end
129 end
118
130
119 def update
131 def update
120 @user = User.find(params[:id])
132 @user = User.find(params[:id])
121 - if @user.update_attributes(params[:user])
133 + if @user.update_attributes(user_params)
122 flash[:notice] = 'User was successfully updated.'
134 flash[:notice] = 'User was successfully updated.'
123 redirect_to :action => 'show', :id => @user
135 redirect_to :action => 'show', :id => @user
124 else
136 else
125 render :action => 'edit'
137 render :action => 'edit'
126 end
138 end
127 end
139 end
128
140
129 def destroy
141 def destroy
130 User.find(params[:id]).destroy
142 User.find(params[:id]).destroy
131 redirect_to :action => 'index'
143 redirect_to :action => 'index'
132 end
144 end
133
145
134 def user_stat
146 def user_stat
135 if params[:commit] == 'download csv'
147 if params[:commit] == 'download csv'
136 @problems = Problem.all
148 @problems = Problem.all
137 else
149 else
138 - @problems = Problem.find_available_problems
150 + @problems = Problem.available_problems
139 end
151 end
140 - @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
152 + @users = User.includes(:contests, :contest_stat).where(enabled: true)
141 @scorearray = Array.new
153 @scorearray = Array.new
142 @users.each do |u|
154 @users.each do |u|
143 ustat = Array.new
155 ustat = Array.new
144 ustat[0] = u
156 ustat[0] = u
145 @problems.each do |p|
157 @problems.each do |p|
146 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
158 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
147 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
159 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
148 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
160 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
149 else
161 else
150 ustat << [0,false]
162 ustat << [0,false]
151 end
163 end
152 end
164 end
153 @scorearray << ustat
165 @scorearray << ustat
154 end
166 end
155 if params[:commit] == 'download csv' then
167 if params[:commit] == 'download csv' then
156 csv = gen_csv_from_scorearray(@scorearray,@problems)
168 csv = gen_csv_from_scorearray(@scorearray,@problems)
157 send_data csv, filename: 'last_score.csv'
169 send_data csv, filename: 'last_score.csv'
158 else
170 else
159 render template: 'user_admin/user_stat'
171 render template: 'user_admin/user_stat'
160 end
172 end
161 end
173 end
162
174
163 def user_stat_max
175 def user_stat_max
164 if params[:commit] == 'download csv'
176 if params[:commit] == 'download csv'
165 @problems = Problem.all
177 @problems = Problem.all
166 else
178 else
167 - @problems = Problem.find_available_problems
179 + @problems = Problem.available_problems
168 end
180 end
169 - @users = User.find(:all, :include => [:contests, :contest_stat])
181 + @users = User.includes(:contests).includes(:contest_stat).all
170 @scorearray = Array.new
182 @scorearray = Array.new
171 #set up range from param
183 #set up range from param
172 since_id = params.fetch(:since_id, 0).to_i
184 since_id = params.fetch(:since_id, 0).to_i
173 until_id = params.fetch(:until_id, 0).to_i
185 until_id = params.fetch(:until_id, 0).to_i
174 @users.each do |u|
186 @users.each do |u|
175 ustat = Array.new
187 ustat = Array.new
176 ustat[0] = u
188 ustat[0] = u
177 @problems.each do |p|
189 @problems.each do |p|
178 max_points = 0
190 max_points = 0
179 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
191 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
180 max_points = sub.points if sub and sub.points and (sub.points > max_points)
192 max_points = sub.points if sub and sub.points and (sub.points > max_points)
181 end
193 end
182 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
194 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
183 end
195 end
184 @scorearray << ustat
196 @scorearray << ustat
185 end
197 end
186
198
187 if params[:commit] == 'download csv' then
199 if params[:commit] == 'download csv' then
188 csv = gen_csv_from_scorearray(@scorearray,@problems)
200 csv = gen_csv_from_scorearray(@scorearray,@problems)
189 send_data csv, filename: 'max_score.csv'
201 send_data csv, filename: 'max_score.csv'
190 else
202 else
191 render template: 'user_admin/user_stat'
203 render template: 'user_admin/user_stat'
192 end
204 end
193 end
205 end
194
206
195 def import
207 def import
196 if params[:file]==''
208 if params[:file]==''
197 flash[:notice] = 'Error importing no file'
209 flash[:notice] = 'Error importing no file'
198 redirect_to :action => 'index' and return
210 redirect_to :action => 'index' and return
199 end
211 end
200 import_from_file(params[:file])
212 import_from_file(params[:file])
201 end
213 end
202
214
203 def random_all_passwords
215 def random_all_passwords
204 - users = User.find(:all)
216 + users = User.all
205 @prefix = params[:prefix] || ''
217 @prefix = params[:prefix] || ''
206 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
218 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
207 @changed = false
219 @changed = false
208 if request.request_method == 'POST'
220 if request.request_method == 'POST'
209 @non_admin_users.each do |user|
221 @non_admin_users.each do |user|
210 password = random_password
222 password = random_password
211 user.password = password
223 user.password = password
212 user.password_confirmation = password
224 user.password_confirmation = password
213 user.save
225 user.save
214 end
226 end
215 @changed = true
227 @changed = true
216 end
228 end
217 end
229 end
218
230
219 # contest management
231 # contest management
220
232
221 def contests
233 def contests
222 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
234 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
223 @contests = Contest.enabled
235 @contests = Contest.enabled
224 end
236 end
225
237
226 def assign_from_list
238 def assign_from_list
227 contest_id = params[:users_contest_id]
239 contest_id = params[:users_contest_id]
228 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
240 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
229 contest = Contest.find(params[:new_contest][:id])
241 contest = Contest.find(params[:new_contest][:id])
230 if !contest
242 if !contest
231 flash[:notice] = 'Error: no contest'
243 flash[:notice] = 'Error: no contest'
232 redirect_to :action => 'contests', :id =>contest_id
244 redirect_to :action => 'contests', :id =>contest_id
233 end
245 end
234
246
235 note = []
247 note = []
236 users.each do |u|
248 users.each do |u|
237 u.contests = [contest]
249 u.contests = [contest]
238 note << u.login
250 note << u.login
239 end
251 end
240 flash[:notice] = 'User(s) ' + note.join(', ') +
252 flash[:notice] = 'User(s) ' + note.join(', ') +
241 " were successfully reassigned to #{contest.title}."
253 " were successfully reassigned to #{contest.title}."
242 redirect_to :action => 'contests', :id =>contest.id
254 redirect_to :action => 'contests', :id =>contest.id
243 end
255 end
244
256
245 def add_to_contest
257 def add_to_contest
246 user = User.find(params[:id])
258 user = User.find(params[:id])
247 contest = Contest.find(params[:contest_id])
259 contest = Contest.find(params[:contest_id])
248 if user and contest
260 if user and contest
249 user.contests << contest
261 user.contests << contest
250 end
262 end
251 redirect_to :action => 'index'
263 redirect_to :action => 'index'
252 end
264 end
253
265
254 def remove_from_contest
266 def remove_from_contest
255 user = User.find(params[:id])
267 user = User.find(params[:id])
256 contest = Contest.find(params[:contest_id])
268 contest = Contest.find(params[:contest_id])
257 if user and contest
269 if user and contest
258 user.contests.delete(contest)
270 user.contests.delete(contest)
259 end
271 end
260 redirect_to :action => 'index'
272 redirect_to :action => 'index'
261 end
273 end
262
274
263 def contest_management
275 def contest_management
264 end
276 end
265
277
266 def manage_contest
278 def manage_contest
267 contest = Contest.find(params[:contest][:id])
279 contest = Contest.find(params[:contest][:id])
268 if !contest
280 if !contest
269 flash[:notice] = 'You did not choose the contest.'
281 flash[:notice] = 'You did not choose the contest.'
270 redirect_to :action => 'contest_management' and return
282 redirect_to :action => 'contest_management' and return
271 end
283 end
272
284
273 operation = params[:operation]
285 operation = params[:operation]
274
286
275 if not ['add','remove','assign'].include? operation
287 if not ['add','remove','assign'].include? operation
276 flash[:notice] = 'You did not choose the operation to perform.'
288 flash[:notice] = 'You did not choose the operation to perform.'
277 redirect_to :action => 'contest_management' and return
289 redirect_to :action => 'contest_management' and return
278 end
290 end
279
291
280 lines = params[:login_list]
292 lines = params[:login_list]
281 if !lines or lines.blank?
293 if !lines or lines.blank?
282 flash[:notice] = 'You entered an empty list.'
294 flash[:notice] = 'You entered an empty list.'
283 redirect_to :action => 'contest_management' and return
295 redirect_to :action => 'contest_management' and return
284 end
296 end
285
297
286 note = []
298 note = []
287 users = []
299 users = []
288 lines.split("\n").each do |line|
300 lines.split("\n").each do |line|
289 user = User.find_by_login(line.chomp)
301 user = User.find_by_login(line.chomp)
290 if user
302 if user
291 if operation=='add'
303 if operation=='add'
292 if ! user.contests.include? contest
304 if ! user.contests.include? contest
293 user.contests << contest
305 user.contests << contest
294 end
306 end
295 elsif operation=='remove'
307 elsif operation=='remove'
296 user.contests.delete(contest)
308 user.contests.delete(contest)
297 else
309 else
298 user.contests = [contest]
310 user.contests = [contest]
299 end
311 end
300
312
301 if params[:reset_timer]
313 if params[:reset_timer]
302 user.contest_stat.forced_logout = true
314 user.contest_stat.forced_logout = true
303 user.contest_stat.reset_timer_and_save
315 user.contest_stat.reset_timer_and_save
304 end
316 end
305
317
306 if params[:notification_emails]
318 if params[:notification_emails]
307 send_contest_update_notification_email(user, contest)
319 send_contest_update_notification_email(user, contest)
308 end
320 end
309
321
310 note << user.login
322 note << user.login
311 users << user
323 users << user
312 end
324 end
313 end
325 end
314
326
315 if params[:reset_timer]
327 if params[:reset_timer]
316 logout_users(users)
328 logout_users(users)
317 end
329 end
318
330
319 flash[:notice] = 'User(s) ' + note.join(', ') +
331 flash[:notice] = 'User(s) ' + note.join(', ') +
320 ' were successfully modified. '
332 ' were successfully modified. '
321 redirect_to :action => 'contest_management'
333 redirect_to :action => 'contest_management'
322 end
334 end
323
335
324 # admin management
336 # admin management
325
337
326 def admin
338 def admin
327 - @admins = User.find(:all).find_all {|user| user.admin? }
339 + @admins = User.all.find_all {|user| user.admin? }
328 end
340 end
329
341
330 def grant_admin
342 def grant_admin
331 login = params[:login]
343 login = params[:login]
332 user = User.find_by_login(login)
344 user = User.find_by_login(login)
333 if user!=nil
345 if user!=nil
334 admin_role = Role.find_by_name('admin')
346 admin_role = Role.find_by_name('admin')
335 user.roles << admin_role
347 user.roles << admin_role
336 else
348 else
337 flash[:notice] = 'Unknown user'
349 flash[:notice] = 'Unknown user'
338 end
350 end
339 flash[:notice] = 'User added as admins'
351 flash[:notice] = 'User added as admins'
340 redirect_to :action => 'admin'
352 redirect_to :action => 'admin'
341 end
353 end
342
354
343 def revoke_admin
355 def revoke_admin
344 user = User.find(params[:id])
356 user = User.find(params[:id])
345 if user==nil
357 if user==nil
346 flash[:notice] = 'Unknown user'
358 flash[:notice] = 'Unknown user'
347 redirect_to :action => 'admin' and return
359 redirect_to :action => 'admin' and return
348 elsif user.login == 'root'
360 elsif user.login == 'root'
349 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
361 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
350 redirect_to :action => 'admin' and return
362 redirect_to :action => 'admin' and return
351 end
363 end
352
364
353 admin_role = Role.find_by_name('admin')
365 admin_role = Role.find_by_name('admin')
354 user.roles.delete(admin_role)
366 user.roles.delete(admin_role)
355 flash[:notice] = 'User permission revoked'
367 flash[:notice] = 'User permission revoked'
356 redirect_to :action => 'admin'
368 redirect_to :action => 'admin'
357 end
369 end
358
370
359 # mass mailing
371 # mass mailing
360
372
361 def mass_mailing
373 def mass_mailing
362 end
374 end
363
375
364 def bulk_mail
376 def bulk_mail
365 lines = params[:login_list]
377 lines = params[:login_list]
366 if !lines or lines.blank?
378 if !lines or lines.blank?
367 flash[:notice] = 'You entered an empty list.'
379 flash[:notice] = 'You entered an empty list.'
368 redirect_to :action => 'mass_mailing' and return
380 redirect_to :action => 'mass_mailing' and return
369 end
381 end
370
382
371 mail_subject = params[:subject]
383 mail_subject = params[:subject]
372 if !mail_subject or mail_subject.blank?
384 if !mail_subject or mail_subject.blank?
373 flash[:notice] = 'You entered an empty mail subject.'
385 flash[:notice] = 'You entered an empty mail subject.'
374 redirect_to :action => 'mass_mailing' and return
386 redirect_to :action => 'mass_mailing' and return
375 end
387 end
376
388
377 mail_body = params[:email_body]
389 mail_body = params[:email_body]
378 if !mail_body or mail_body.blank?
390 if !mail_body or mail_body.blank?
379 flash[:notice] = 'You entered an empty mail body.'
391 flash[:notice] = 'You entered an empty mail body.'
380 redirect_to :action => 'mass_mailing' and return
392 redirect_to :action => 'mass_mailing' and return
381 end
393 end
382
394
383 note = []
395 note = []
384 users = []
396 users = []
385 lines.split("\n").each do |line|
397 lines.split("\n").each do |line|
386 user = User.find_by_login(line.chomp)
398 user = User.find_by_login(line.chomp)
387 if user
399 if user
388 send_mail(user.email, mail_subject, mail_body)
400 send_mail(user.email, mail_subject, mail_body)
389 note << user.login
401 note << user.login
390 end
402 end
391 end
403 end
392
404
393 flash[:notice] = 'User(s) ' + note.join(', ') +
405 flash[:notice] = 'User(s) ' + note.join(', ') +
394 ' were successfully modified. '
406 ' were successfully modified. '
395 redirect_to :action => 'mass_mailing'
407 redirect_to :action => 'mass_mailing'
396 end
408 end
397
409
410 + #bulk manage
411 + def bulk_manage
412 +
413 + begin
414 + @users = User.where('login REGEXP ?',params[:regex]) if params[:regex]
415 + @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
416 + rescue Exception
417 + flash[:error] = 'Regular Expression is malformed'
418 + @users = nil
419 + end
420 +
421 + if params[:commit]
422 + @action = {}
423 + @action[:set_enable] = params[:enabled]
424 + @action[:enabled] = params[:enable] == "1"
425 + @action[:gen_password] = params[:gen_password]
426 + end
427 +
428 + if params[:commit] == "Perform"
429 + if @action[:set_enable]
430 + @users.update_all(enabled: @action[:enabled])
431 + end
432 + if @action[:gen_password]
433 + @users.each do |u|
434 + password = random_password
435 + u.password = password
436 + u.password_confirmation = password
437 + u.save
438 + end
439 + end
440 + end
441 + end
442 +
398 protected
443 protected
399
444
400 def random_password(length=5)
445 def random_password(length=5)
401 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
446 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
402 newpass = ""
447 newpass = ""
403 length.times { newpass << chars[rand(chars.size-1)] }
448 length.times { newpass << chars[rand(chars.size-1)] }
404 return newpass
449 return newpass
405 end
450 end
406
451
407 def import_from_file(f)
452 def import_from_file(f)
408 data_hash = YAML.load(f)
453 data_hash = YAML.load(f)
409 @import_log = ""
454 @import_log = ""
410
455
411 country_data = data_hash[:countries]
456 country_data = data_hash[:countries]
412 site_data = data_hash[:sites]
457 site_data = data_hash[:sites]
413 user_data = data_hash[:users]
458 user_data = data_hash[:users]
414
459
415 # import country
460 # import country
416 countries = {}
461 countries = {}
417 country_data.each_pair do |id,country|
462 country_data.each_pair do |id,country|
418 c = Country.find_by_name(country[:name])
463 c = Country.find_by_name(country[:name])
419 if c!=nil
464 if c!=nil
420 countries[id] = c
465 countries[id] = c
421 @import_log << "Found #{country[:name]}\n"
466 @import_log << "Found #{country[:name]}\n"
422 else
467 else
423 countries[id] = Country.new(:name => country[:name])
468 countries[id] = Country.new(:name => country[:name])
424 countries[id].save
469 countries[id].save
425 @import_log << "Created #{country[:name]}\n"
470 @import_log << "Created #{country[:name]}\n"
426 end
471 end
427 end
472 end
428
473
429 # import sites
474 # import sites
430 sites = {}
475 sites = {}
431 site_data.each_pair do |id,site|
476 site_data.each_pair do |id,site|
432 s = Site.find_by_name(site[:name])
477 s = Site.find_by_name(site[:name])
433 if s!=nil
478 if s!=nil
434 @import_log << "Found #{site[:name]}\n"
479 @import_log << "Found #{site[:name]}\n"
435 else
480 else
436 s = Site.new(:name => site[:name])
481 s = Site.new(:name => site[:name])
437 @import_log << "Created #{site[:name]}\n"
482 @import_log << "Created #{site[:name]}\n"
438 end
483 end
439 s.password = site[:password]
484 s.password = site[:password]
440 s.country = countries[site[:country_id]]
485 s.country = countries[site[:country_id]]
441 s.save
486 s.save
442 sites[id] = s
487 sites[id] = s
443 end
488 end
444
489
445 # import users
490 # import users
446 user_data.each_pair do |id,user|
491 user_data.each_pair do |id,user|
447 u = User.find_by_login(user[:login])
492 u = User.find_by_login(user[:login])
448 if u!=nil
493 if u!=nil
449 @import_log << "Found #{user[:login]}\n"
494 @import_log << "Found #{user[:login]}\n"
450 else
495 else
451 u = User.new(:login => user[:login])
496 u = User.new(:login => user[:login])
452 @import_log << "Created #{user[:login]}\n"
497 @import_log << "Created #{user[:login]}\n"
453 end
498 end
454 u.full_name = user[:name]
499 u.full_name = user[:name]
455 u.password = user[:password]
500 u.password = user[:password]
456 u.country = countries[user[:country_id]]
501 u.country = countries[user[:country_id]]
457 u.site = sites[user[:site_id]]
502 u.site = sites[user[:site_id]]
458 u.activated = true
503 u.activated = true
459 u.email = "empty-#{u.login}@none.com"
504 u.email = "empty-#{u.login}@none.com"
460 if not u.save
505 if not u.save
461 @import_log << "Errors\n"
506 @import_log << "Errors\n"
462 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
507 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
463 end
508 end
464 end
509 end
465
510
466 end
511 end
467
512
468 def logout_users(users)
513 def logout_users(users)
469 users.each do |user|
514 users.each do |user|
470 contest_stat = user.contest_stat(true)
515 contest_stat = user.contest_stat(true)
471 if contest_stat and !contest_stat.forced_logout
516 if contest_stat and !contest_stat.forced_logout
472 contest_stat.forced_logout = true
517 contest_stat.forced_logout = true
473 contest_stat.save
518 contest_stat.save
474 end
519 end
475 end
520 end
476 end
521 end
477
522
478 def send_contest_update_notification_email(user, contest)
523 def send_contest_update_notification_email(user, contest)
479 contest_title_name = GraderConfiguration['contest.name']
524 contest_title_name = GraderConfiguration['contest.name']
480 contest_name = contest.name
525 contest_name = contest.name
481 mail_subject = t('contest.notification.email_subject', {
526 mail_subject = t('contest.notification.email_subject', {
482 :contest_title_name => contest_title_name,
527 :contest_title_name => contest_title_name,
483 :contest_name => contest_name })
528 :contest_name => contest_name })
484 mail_body = t('contest.notification.email_body', {
529 mail_body = t('contest.notification.email_body', {
485 :full_name => user.full_name,
530 :full_name => user.full_name,
486 :contest_title_name => contest_title_name,
531 :contest_title_name => contest_title_name,
487 :contest_name => contest.name,
532 :contest_name => contest.name,
488 })
533 })
489
534
490 logger.info mail_body
535 logger.info mail_body
491 send_mail(user.email, mail_subject, mail_body)
536 send_mail(user.email, mail_subject, mail_body)
492 end
537 end
493
538
494 def find_contest_and_user_from_contest_id(id)
539 def find_contest_and_user_from_contest_id(id)
495 if id!='none'
540 if id!='none'
496 @contest = Contest.find(id)
541 @contest = Contest.find(id)
497 else
542 else
498 @contest = nil
543 @contest = nil
499 end
544 end
500 if @contest
545 if @contest
501 @users = @contest.users
546 @users = @contest.users
502 else
547 else
503 @users = User.find_users_with_no_contest
548 @users = User.find_users_with_no_contest
504 end
549 end
505 return [@contest, @users]
550 return [@contest, @users]
506 end
551 end
507
552
508 def gen_csv_from_scorearray(scorearray,problem)
553 def gen_csv_from_scorearray(scorearray,problem)
509 CSV.generate do |csv|
554 CSV.generate do |csv|
510 #add header
555 #add header
511 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
556 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
512 problem.each { |p| header << p.name }
557 problem.each { |p| header << p.name }
513 header += ['Total','Passed']
558 header += ['Total','Passed']
514 csv << header
559 csv << header
515 #add data
560 #add data
516 scorearray.each do |sc|
561 scorearray.each do |sc|
517 total = num_passed = 0
562 total = num_passed = 0
518 row = Array.new
563 row = Array.new
519 sc.each_index do |i|
564 sc.each_index do |i|
520 if i == 0
565 if i == 0
521 row << sc[i].login
566 row << sc[i].login
522 row << sc[i].full_name
567 row << sc[i].full_name
523 row << sc[i].activated
568 row << sc[i].activated
524 - row << (sc[i].try(:contest_stat).try(:started_at).nil? 'no' : 'yes')
569 + row << (sc[i].try(:contest_stat).try(:started_at).nil? ? 'no' : 'yes')
525 row << sc[i].contests.collect {|c| c.name}.join(', ')
570 row << sc[i].contests.collect {|c| c.name}.join(', ')
526 else
571 else
527 row << sc[i][0]
572 row << sc[i][0]
528 total += sc[i][0]
573 total += sc[i][0]
529 num_passed += 1 if sc[i][1]
574 num_passed += 1 if sc[i][1]
530 end
575 end
531 end
576 end
532 row << total
577 row << total
533 row << num_passed
578 row << num_passed
534 csv << row
579 csv << row
535 end
580 end
536 end
581 end
537 end
582 end
583 +
584 + private
585 + def user_params
586 + params.require(:user).permit(:login,:password,:password_confirmation,:email, :alias, :full_name,:remark)
587 + end
538 end
588 end
@@ -1,214 +1,218
1 require 'net/smtp'
1 require 'net/smtp'
2
2
3 class UsersController < ApplicationController
3 class UsersController < ApplicationController
4
4
5 include MailHelperMethods
5 include MailHelperMethods
6
6
7 before_filter :authenticate, :except => [:new,
7 before_filter :authenticate, :except => [:new,
8 :register,
8 :register,
9 :confirm,
9 :confirm,
10 :forget,
10 :forget,
11 :retrieve_password]
11 :retrieve_password]
12
12
13 before_filter :verify_online_registration, :only => [:new,
13 before_filter :verify_online_registration, :only => [:new,
14 :register,
14 :register,
15 :forget,
15 :forget,
16 :retrieve_password]
16 :retrieve_password]
17 before_filter :authenticate, :profile_authorization, only: [:profile]
17 before_filter :authenticate, :profile_authorization, only: [:profile]
18
18
19 verify :method => :post, :only => [:chg_passwd],
19 verify :method => :post, :only => [:chg_passwd],
20 :redirect_to => { :action => :index }
20 :redirect_to => { :action => :index }
21
21
22 #in_place_edit_for :user, :alias_for_editing
22 #in_place_edit_for :user, :alias_for_editing
23 #in_place_edit_for :user, :email_for_editing
23 #in_place_edit_for :user, :email_for_editing
24
24
25 def index
25 def index
26 if !GraderConfiguration['system.user_setting_enabled']
26 if !GraderConfiguration['system.user_setting_enabled']
27 redirect_to :controller => 'main', :action => 'list'
27 redirect_to :controller => 'main', :action => 'list'
28 else
28 else
29 @user = User.find(session[:user_id])
29 @user = User.find(session[:user_id])
30 end
30 end
31 end
31 end
32
32
33 def chg_passwd
33 def chg_passwd
34 user = User.find(session[:user_id])
34 user = User.find(session[:user_id])
35 user.password = params[:passwd]
35 user.password = params[:passwd]
36 user.password_confirmation = params[:passwd_verify]
36 user.password_confirmation = params[:passwd_verify]
37 if user.save
37 if user.save
38 flash[:notice] = 'password changed'
38 flash[:notice] = 'password changed'
39 else
39 else
40 flash[:notice] = 'Error: password changing failed'
40 flash[:notice] = 'Error: password changing failed'
41 end
41 end
42 redirect_to :action => 'index'
42 redirect_to :action => 'index'
43 end
43 end
44
44
45 def new
45 def new
46 @user = User.new
46 @user = User.new
47 render :action => 'new', :layout => 'empty'
47 render :action => 'new', :layout => 'empty'
48 end
48 end
49
49
50 def register
50 def register
51 if(params[:cancel])
51 if(params[:cancel])
52 redirect_to :controller => 'main', :action => 'login'
52 redirect_to :controller => 'main', :action => 'login'
53 return
53 return
54 end
54 end
55 - @user = User.new(params[:user])
55 + @user = User.new(user_params)
56 @user.password_confirmation = @user.password = User.random_password
56 @user.password_confirmation = @user.password = User.random_password
57 @user.activated = false
57 @user.activated = false
58 if (@user.valid?) and (@user.save)
58 if (@user.valid?) and (@user.save)
59 if send_confirmation_email(@user)
59 if send_confirmation_email(@user)
60 render :action => 'new_splash', :layout => 'empty'
60 render :action => 'new_splash', :layout => 'empty'
61 else
61 else
62 @admin_email = GraderConfiguration['system.admin_email']
62 @admin_email = GraderConfiguration['system.admin_email']
63 render :action => 'email_error', :layout => 'empty'
63 render :action => 'email_error', :layout => 'empty'
64 end
64 end
65 else
65 else
66 @user.errors.add(:base,"Email cannot be blank") if @user.email==''
66 @user.errors.add(:base,"Email cannot be blank") if @user.email==''
67 render :action => 'new', :layout => 'empty'
67 render :action => 'new', :layout => 'empty'
68 end
68 end
69 end
69 end
70
70
71 def confirm
71 def confirm
72 login = params[:login]
72 login = params[:login]
73 key = params[:activation]
73 key = params[:activation]
74 @user = User.find_by_login(login)
74 @user = User.find_by_login(login)
75 if (@user) and (@user.verify_activation_key(key))
75 if (@user) and (@user.verify_activation_key(key))
76 if @user.valid? # check uniquenss of email
76 if @user.valid? # check uniquenss of email
77 @user.activated = true
77 @user.activated = true
78 @user.save
78 @user.save
79 @result = :successful
79 @result = :successful
80 else
80 else
81 @result = :email_used
81 @result = :email_used
82 end
82 end
83 else
83 else
84 @result = :failed
84 @result = :failed
85 end
85 end
86 render :action => 'confirm', :layout => 'empty'
86 render :action => 'confirm', :layout => 'empty'
87 end
87 end
88
88
89 def forget
89 def forget
90 render :action => 'forget', :layout => 'empty'
90 render :action => 'forget', :layout => 'empty'
91 end
91 end
92
92
93 def retrieve_password
93 def retrieve_password
94 email = params[:email]
94 email = params[:email]
95 user = User.find_by_email(email)
95 user = User.find_by_email(email)
96 if user
96 if user
97 last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour)
97 last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour)
98 if last_updated_time > Time.now.gmtime - 5.minutes
98 if last_updated_time > Time.now.gmtime - 5.minutes
99 flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes'
99 flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes'
100 else
100 else
101 user.password = user.password_confirmation = User.random_password
101 user.password = user.password_confirmation = User.random_password
102 user.save
102 user.save
103 send_new_password_email(user)
103 send_new_password_email(user)
104 flash[:notice] = 'New password has been mailed to you.'
104 flash[:notice] = 'New password has been mailed to you.'
105 end
105 end
106 else
106 else
107 flash[:notice] = I18n.t 'registration.password_retrieval.no_email'
107 flash[:notice] = I18n.t 'registration.password_retrieval.no_email'
108 end
108 end
109 redirect_to :action => 'forget'
109 redirect_to :action => 'forget'
110 end
110 end
111
111
112 def stat
112 def stat
113 @user = User.find(params[:id])
113 @user = User.find(params[:id])
114 @submission = Submission.includes(:problem).where(user_id: params[:id])
114 @submission = Submission.includes(:problem).where(user_id: params[:id])
115
115
116 range = 120
116 range = 120
117 @histogram = { data: Array.new(range,0), summary: {} }
117 @histogram = { data: Array.new(range,0), summary: {} }
118 @summary = {count: 0, solve: 0, attempt: 0}
118 @summary = {count: 0, solve: 0, attempt: 0}
119 problem = Hash.new(0)
119 problem = Hash.new(0)
120
120
121 @submission.find_each do |sub|
121 @submission.find_each do |sub|
122 #histogram
122 #histogram
123 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
123 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
124 @histogram[:data][d.to_i] += 1 if d < range
124 @histogram[:data][d.to_i] += 1 if d < range
125
125
126 @summary[:count] += 1
126 @summary[:count] += 1
127 next unless sub.problem
127 next unless sub.problem
128 problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max
128 problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max
129 end
129 end
130
130
131 @histogram[:summary][:max] = [@histogram[:data].max,1].max
131 @histogram[:summary][:max] = [@histogram[:data].max,1].max
132 @summary[:attempt] = problem.count
132 @summary[:attempt] = problem.count
133 problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
133 problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
134 end
134 end
135
135
136 def toggle_activate
136 def toggle_activate
137 @user = User.find(params[:id])
137 @user = User.find(params[:id])
138 @user.update_attributes( activated: !@user.activated? )
138 @user.update_attributes( activated: !@user.activated? )
139 respond_to do |format|
139 respond_to do |format|
140 format.js { render partial: 'toggle_button',
140 format.js { render partial: 'toggle_button',
141 locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } }
141 locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } }
142 end
142 end
143 end
143 end
144
144
145 def toggle_enable
145 def toggle_enable
146 @user = User.find(params[:id])
146 @user = User.find(params[:id])
147 @user.update_attributes( enabled: !@user.enabled? )
147 @user.update_attributes( enabled: !@user.enabled? )
148 respond_to do |format|
148 respond_to do |format|
149 format.js { render partial: 'toggle_button',
149 format.js { render partial: 'toggle_button',
150 locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } }
150 locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } }
151 end
151 end
152 end
152 end
153
153
154 protected
154 protected
155
155
156 def verify_online_registration
156 def verify_online_registration
157 if !GraderConfiguration['system.online_registration']
157 if !GraderConfiguration['system.online_registration']
158 redirect_to :controller => 'main', :action => 'login'
158 redirect_to :controller => 'main', :action => 'login'
159 end
159 end
160 end
160 end
161
161
162 def send_confirmation_email(user)
162 def send_confirmation_email(user)
163 contest_name = GraderConfiguration['contest.name']
163 contest_name = GraderConfiguration['contest.name']
164 activation_url = url_for(:action => 'confirm',
164 activation_url = url_for(:action => 'confirm',
165 :login => user.login,
165 :login => user.login,
166 :activation => user.activation_key)
166 :activation => user.activation_key)
167 home_url = url_for(:controller => 'main', :action => 'index')
167 home_url = url_for(:controller => 'main', :action => 'index')
168 mail_subject = "[#{contest_name}] Confirmation"
168 mail_subject = "[#{contest_name}] Confirmation"
169 mail_body = t('registration.email_body', {
169 mail_body = t('registration.email_body', {
170 :full_name => user.full_name,
170 :full_name => user.full_name,
171 :contest_name => contest_name,
171 :contest_name => contest_name,
172 :login => user.login,
172 :login => user.login,
173 :password => user.password,
173 :password => user.password,
174 :activation_url => activation_url,
174 :activation_url => activation_url,
175 :admin_email => GraderConfiguration['system.admin_email']
175 :admin_email => GraderConfiguration['system.admin_email']
176 })
176 })
177
177
178 logger.info mail_body
178 logger.info mail_body
179
179
180 send_mail(user.email, mail_subject, mail_body)
180 send_mail(user.email, mail_subject, mail_body)
181 end
181 end
182
182
183 def send_new_password_email(user)
183 def send_new_password_email(user)
184 contest_name = GraderConfiguration['contest.name']
184 contest_name = GraderConfiguration['contest.name']
185 mail_subject = "[#{contest_name}] Password recovery"
185 mail_subject = "[#{contest_name}] Password recovery"
186 mail_body = t('registration.password_retrieval.email_body', {
186 mail_body = t('registration.password_retrieval.email_body', {
187 :full_name => user.full_name,
187 :full_name => user.full_name,
188 :contest_name => contest_name,
188 :contest_name => contest_name,
189 :login => user.login,
189 :login => user.login,
190 :password => user.password,
190 :password => user.password,
191 :admin_email => GraderConfiguration['system.admin_email']
191 :admin_email => GraderConfiguration['system.admin_email']
192 })
192 })
193
193
194 logger.info mail_body
194 logger.info mail_body
195
195
196 send_mail(user.email, mail_subject, mail_body)
196 send_mail(user.email, mail_subject, mail_body)
197 end
197 end
198
198
199 # allow viewing of regular user profile only when options allow so
199 # allow viewing of regular user profile only when options allow so
200 # only admins can view admins profile
200 # only admins can view admins profile
201 def profile_authorization
201 def profile_authorization
202 #if view admins' profile, allow only admin
202 #if view admins' profile, allow only admin
203 return false unless(params[:id])
203 return false unless(params[:id])
204 user = User.find(params[:id])
204 user = User.find(params[:id])
205 return false unless user
205 return false unless user
206 return admin_authorization if user.admin?
206 return admin_authorization if user.admin?
207 return true if GraderConfiguration["right.user_view_submission"]
207 return true if GraderConfiguration["right.user_view_submission"]
208
208
209 #finally, we allow only admin
209 #finally, we allow only admin
210 admin_authorization
210 admin_authorization
211 end
211 end
212 -
212 +
213 + private
214 + def user_params
215 + params.require(:user).permit(:login, :full_name, :email)
216 + end
213
217
214 end
218 end
@@ -1,199 +1,221
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.now.gmtime
89 st = ''
89 st = ''
90 if (time.yday != now.yday) or
90 if (time.yday != now.yday) or
91 (time.year != now.year)
91 (time.year != now.year)
92 st = time.strftime("%x ")
92 st = time.strftime("%x ")
93 end
93 end
94 st + time.strftime("%X")
94 st + time.strftime("%X")
95 end
95 end
96
96
97 def format_short_duration(duration)
97 def format_short_duration(duration)
98 return '' if duration==nil
98 return '' if duration==nil
99 d = duration.to_f
99 d = duration.to_f
100 return Time.at(d).gmtime.strftime("%X")
100 return Time.at(d).gmtime.strftime("%X")
101 end
101 end
102
102
103 def read_textfile(fname,max_size=2048)
103 def read_textfile(fname,max_size=2048)
104 begin
104 begin
105 File.open(fname).read(max_size)
105 File.open(fname).read(max_size)
106 rescue
106 rescue
107 nil
107 nil
108 end
108 end
109 end
109 end
110
110
111 def toggle_button(on,toggle_url,id, option={})
111 def toggle_button(on,toggle_url,id, option={})
112 btn_size = option[:size] || 'btn-xs'
112 btn_size = option[:size] || 'btn-xs'
113 link_to (on ? "Yes" : "No"), toggle_url,
113 link_to (on ? "Yes" : "No"), toggle_url,
114 {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
114 {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
115 id: id,
115 id: id,
116 data: {remote: true, method: 'get'}}
116 data: {remote: true, method: 'get'}}
117 end
117 end
118
118
119 def get_ace_mode(language)
119 def get_ace_mode(language)
120 # return ace mode string from Language
120 # return ace mode string from Language
121
121
122 case language.pretty_name
122 case language.pretty_name
123 when 'Pascal'
123 when 'Pascal'
124 'ace/mode/pascal'
124 'ace/mode/pascal'
125 when 'C++','C'
125 when 'C++','C'
126 'ace/mode/c_cpp'
126 'ace/mode/c_cpp'
127 when 'Ruby'
127 when 'Ruby'
128 'ace/mode/ruby'
128 'ace/mode/ruby'
129 when 'Python'
129 when 'Python'
130 'ace/mode/python'
130 'ace/mode/python'
131 when 'Java'
131 when 'Java'
132 'ace/mode/java'
132 'ace/mode/java'
133 else
133 else
134 'ace/mode/c_cpp'
134 'ace/mode/c_cpp'
135 end
135 end
136 end
136 end
137
137
138
138
139 def user_title_bar(user)
139 def user_title_bar(user)
140 header = ''
140 header = ''
141 time_left = ''
141 time_left = ''
142
142
143 #
143 #
144 # if the contest is over
144 # if the contest is over
145 if GraderConfiguration.time_limit_mode?
145 if GraderConfiguration.time_limit_mode?
146 if user.contest_finished?
146 if user.contest_finished?
147 header = <<CONTEST_OVER
147 header = <<CONTEST_OVER
148 <tr><td colspan="2" align="center">
148 <tr><td colspan="2" align="center">
149 <span class="contest-over-msg">THE CONTEST IS OVER</span>
149 <span class="contest-over-msg">THE CONTEST IS OVER</span>
150 </td></tr>
150 </td></tr>
151 CONTEST_OVER
151 CONTEST_OVER
152 end
152 end
153 if !user.contest_started?
153 if !user.contest_started?
154 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
154 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
155 else
155 else
156 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
156 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
157 " #{format_short_duration(user.contest_time_left)}"
157 " #{format_short_duration(user.contest_time_left)}"
158 end
158 end
159 end
159 end
160
160
161 #
161 #
162 # if the contest is in the anaysis mode
162 # if the contest is in the anaysis mode
163 if GraderConfiguration.analysis_mode?
163 if GraderConfiguration.analysis_mode?
164 header = <<ANALYSISMODE
164 header = <<ANALYSISMODE
165 <tr><td colspan="2" align="center">
165 <tr><td colspan="2" align="center">
166 <span class="contest-over-msg">ANALYSIS MODE</span>
166 <span class="contest-over-msg">ANALYSIS MODE</span>
167 </td></tr>
167 </td></tr>
168 ANALYSISMODE
168 ANALYSISMODE
169 end
169 end
170
170
171 contest_name = GraderConfiguration['contest.name']
171 contest_name = GraderConfiguration['contest.name']
172
172
173 #
173 #
174 # build real title bar
174 # build real title bar
175 result = <<TITLEBAR
175 result = <<TITLEBAR
176 <div class="title">
176 <div class="title">
177 <table>
177 <table>
178 #{header}
178 #{header}
179 <tr>
179 <tr>
180 <td class="left-col">
180 <td class="left-col">
181 #{user.full_name}<br/>
181 #{user.full_name}<br/>
182 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
182 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
183 #{time_left}
183 #{time_left}
184 <br/>
184 <br/>
185 </td>
185 </td>
186 <td class="right-col">#{contest_name}</td>
186 <td class="right-col">#{contest_name}</td>
187 </tr>
187 </tr>
188 </table>
188 </table>
189 </div>
189 </div>
190 TITLEBAR
190 TITLEBAR
191 result.html_safe
191 result.html_safe
192 end
192 end
193
193
194 def markdown(text)
194 def markdown(text)
195 markdown = RDiscount.new(text)
195 markdown = RDiscount.new(text)
196 markdown.to_html.html_safe
196 markdown.to_html.html_safe
197 end
197 end
198
198
199 +
200 + BOOTSTRAP_FLASH_MSG = {
201 + success: 'alert-success',
202 + error: 'alert-danger',
203 + alert: 'alert-block',
204 + notice: 'alert-info'
205 + }
206 +
207 + def bootstrap_class_for(flash_type)
208 + BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
209 + end
210 +
211 + def flash_messages
212 + flash.each do |msg_type, message|
213 + concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
214 + concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
215 + concat message
216 + end)
217 + end
218 + nil
219 + end
220 +
199 end
221 end
@@ -1,21 +1,15
1 class Announcement < ActiveRecord::Base
1 class Announcement < ActiveRecord::Base
2
2
3 - def self.find_published(contest_started=false)
3 + def self.published(contest_started=false)
4 if contest_started
4 if contest_started
5 - Announcement.find(:all,
5 + where(published: true).where(frontpage: false).order(created_at: :desc)
6 - :conditions => "(published = 1) AND (frontpage = 0)",
7 - :order => "created_at DESC")
8 else
6 else
9 - Announcement.find(:all,
7 + where(published: true).where(frontpage: false).where(contest_only: false).order(created_at: :desc)
10 - :conditions => "(published = 1) AND (frontpage = 0) AND (contest_only = 0)",
11 - :order => "created_at DESC")
12 end
8 end
13 end
9 end
14
10
15 - def self.find_for_frontpage
11 + def self.frontpage
16 - Announcement.find(:all,
12 + where(published: 1).where(frontpage: 1).order(created_at: :desc)
17 - :conditions => "(published = 1) AND (frontpage = 1)",
18 - :order => "created_at DESC")
19 end
13 end
20
14
21 end
15 end
@@ -1,8 +1,8
1 class Contest < ActiveRecord::Base
1 class Contest < ActiveRecord::Base
2
2
3 has_and_belongs_to_many :users
3 has_and_belongs_to_many :users
4 has_and_belongs_to_many :problems
4 has_and_belongs_to_many :problems
5
5
6 - scope :enabled, :conditions => {:enabled => true}
6 + scope :enabled, -> { where(enabled: true) }
7
7
8 end
8 end
@@ -1,177 +1,183
1 require 'yaml'
1 require 'yaml'
2
2
3 #
3 #
4 # This class also contains various login of the system.
4 # This class also contains various login of the system.
5 #
5 #
6 class GraderConfiguration < ActiveRecord::Base
6 class GraderConfiguration < ActiveRecord::Base
7
7
8 SYSTEM_MODE_CONF_KEY = 'system.mode'
8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 MULTICONTESTS_KEY = 'system.multicontests'
10 MULTICONTESTS_KEY = 'system.multicontests'
11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
13 + VIEW_TESTCASE = 'right.view_testcase'
14 + SINGLE_USER_KEY = 'system.single_user_mode'
13
15
14 cattr_accessor :config_cache
16 cattr_accessor :config_cache
15 cattr_accessor :task_grading_info_cache
17 cattr_accessor :task_grading_info_cache
16 cattr_accessor :contest_time_str
18 cattr_accessor :contest_time_str
17 cattr_accessor :contest_time
19 cattr_accessor :contest_time
18
20
19 GraderConfiguration.config_cache = nil
21 GraderConfiguration.config_cache = nil
20 GraderConfiguration.task_grading_info_cache = nil
22 GraderConfiguration.task_grading_info_cache = nil
21
23
22 def self.config_cached?
24 def self.config_cached?
23 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
25 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
24 end
26 end
25
27
26 def self.get(key)
28 def self.get(key)
27 if GraderConfiguration.config_cached?
29 if GraderConfiguration.config_cached?
28 if GraderConfiguration.config_cache == nil
30 if GraderConfiguration.config_cache == nil
29 self.read_config
31 self.read_config
30 end
32 end
31 return GraderConfiguration.config_cache[key]
33 return GraderConfiguration.config_cache[key]
32 else
34 else
33 return GraderConfiguration.read_one_key(key)
35 return GraderConfiguration.read_one_key(key)
34 end
36 end
35 end
37 end
36
38
37 def self.[](key)
39 def self.[](key)
38 self.get(key)
40 self.get(key)
39 end
41 end
40
42
41 def self.reload
43 def self.reload
42 self.read_config
44 self.read_config
43 end
45 end
44
46
45 def self.clear
47 def self.clear
46 GraderConfiguration.config_cache = nil
48 GraderConfiguration.config_cache = nil
47 end
49 end
48
50
49 #
51 #
50 # View decision
52 # View decision
51 #
53 #
52 def self.show_submitbox_to?(user)
54 def self.show_submitbox_to?(user)
53 mode = get(SYSTEM_MODE_CONF_KEY)
55 mode = get(SYSTEM_MODE_CONF_KEY)
54 return false if mode=='analysis'
56 return false if mode=='analysis'
55 if (mode=='contest')
57 if (mode=='contest')
56 return false if (user.site!=nil) and
58 return false if (user.site!=nil) and
57 ((user.site.started!=true) or (user.site.finished?))
59 ((user.site.started!=true) or (user.site.finished?))
58 end
60 end
59 return true
61 return true
60 end
62 end
61
63
62 def self.show_tasks_to?(user)
64 def self.show_tasks_to?(user)
63 if time_limit_mode?
65 if time_limit_mode?
64 return false if not user.contest_started?
66 return false if not user.contest_started?
65 end
67 end
66 return true
68 return true
67 end
69 end
68
70
69 def self.show_grading_result
71 def self.show_grading_result
70 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
72 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
71 end
73 end
72
74
75 + def self.show_testcase
76 + return get(VIEW_TESTCASE)
77 + end
78 +
73 def self.allow_test_request(user)
79 def self.allow_test_request(user)
74 mode = get(SYSTEM_MODE_CONF_KEY)
80 mode = get(SYSTEM_MODE_CONF_KEY)
75 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
81 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
76 if (mode=='contest')
82 if (mode=='contest')
77 return false if ((user.site!=nil) and
83 return false if ((user.site!=nil) and
78 ((user.site.started!=true) or
84 ((user.site.started!=true) or
79 (early_timeout and (user.site.time_left < 30.minutes))))
85 (early_timeout and (user.site.time_left < 30.minutes))))
80 end
86 end
81 return false if mode=='analysis'
87 return false if mode=='analysis'
82 return true
88 return true
83 end
89 end
84
90
85 def self.task_grading_info
91 def self.task_grading_info
86 if GraderConfiguration.task_grading_info_cache==nil
92 if GraderConfiguration.task_grading_info_cache==nil
87 read_grading_info
93 read_grading_info
88 end
94 end
89 return GraderConfiguration.task_grading_info_cache
95 return GraderConfiguration.task_grading_info_cache
90 end
96 end
91
97
92 def self.standard_mode?
98 def self.standard_mode?
93 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
99 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
94 end
100 end
95
101
96 def self.contest_mode?
102 def self.contest_mode?
97 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
103 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
98 end
104 end
99
105
100 def self.indv_contest_mode?
106 def self.indv_contest_mode?
101 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
107 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
102 end
108 end
103
109
104 def self.multicontests?
110 def self.multicontests?
105 return get(MULTICONTESTS_KEY) == true
111 return get(MULTICONTESTS_KEY) == true
106 end
112 end
107
113
108 def self.time_limit_mode?
114 def self.time_limit_mode?
109 mode = get(SYSTEM_MODE_CONF_KEY)
115 mode = get(SYSTEM_MODE_CONF_KEY)
110 return ((mode == 'contest') or (mode == 'indv-contest'))
116 return ((mode == 'contest') or (mode == 'indv-contest'))
111 end
117 end
112
118
113 def self.analysis_mode?
119 def self.analysis_mode?
114 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
120 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
115 end
121 end
116
122
117 def self.contest_time_limit
123 def self.contest_time_limit
118 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
124 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
119
125
120 if not defined? GraderConfiguration.contest_time_str
126 if not defined? GraderConfiguration.contest_time_str
121 GraderConfiguration.contest_time_str = nil
127 GraderConfiguration.contest_time_str = nil
122 end
128 end
123
129
124 if GraderConfiguration.contest_time_str != contest_time_str
130 if GraderConfiguration.contest_time_str != contest_time_str
125 GraderConfiguration.contest_time_str = contest_time_str
131 GraderConfiguration.contest_time_str = contest_time_str
126 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
132 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
127 h = tmatch[1].to_i
133 h = tmatch[1].to_i
128 m = tmatch[2].to_i
134 m = tmatch[2].to_i
129
135
130 GraderConfiguration.contest_time = h.hour + m.minute
136 GraderConfiguration.contest_time = h.hour + m.minute
131 else
137 else
132 GraderConfiguration.contest_time = nil
138 GraderConfiguration.contest_time = nil
133 end
139 end
134 end
140 end
135 return GraderConfiguration.contest_time
141 return GraderConfiguration.contest_time
136 end
142 end
137
143
138 protected
144 protected
139
145
140 def self.convert_type(val,type)
146 def self.convert_type(val,type)
141 case type
147 case type
142 when 'string'
148 when 'string'
143 return val
149 return val
144
150
145 when 'integer'
151 when 'integer'
146 return val.to_i
152 return val.to_i
147
153
148 when 'boolean'
154 when 'boolean'
149 return (val=='true')
155 return (val=='true')
150 end
156 end
151 end
157 end
152
158
153 def self.read_config
159 def self.read_config
154 GraderConfiguration.config_cache = {}
160 GraderConfiguration.config_cache = {}
155 - GraderConfiguration.find(:all).each do |conf|
161 + GraderConfiguration.all.each do |conf|
156 key = conf.key
162 key = conf.key
157 val = conf.value
163 val = conf.value
158 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
164 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
159 end
165 end
160 end
166 end
161
167
162 def self.read_one_key(key)
168 def self.read_one_key(key)
163 conf = GraderConfiguration.find_by_key(key)
169 conf = GraderConfiguration.find_by_key(key)
164 if conf
170 if conf
165 return GraderConfiguration.convert_type(conf.value,conf.value_type)
171 return GraderConfiguration.convert_type(conf.value,conf.value_type)
166 else
172 else
167 return nil
173 return nil
168 end
174 end
169 end
175 end
170
176
171 def self.read_grading_info
177 def self.read_grading_info
172 f = File.open(TASK_GRADING_INFO_FILENAME)
178 f = File.open(TASK_GRADING_INFO_FILENAME)
173 GraderConfiguration.task_grading_info_cache = YAML.load(f)
179 GraderConfiguration.task_grading_info_cache = YAML.load(f)
174 f.close
180 f.close
175 end
181 end
176
182
177 end
183 end
@@ -1,81 +1,72
1 class GraderProcess < ActiveRecord::Base
1 class GraderProcess < ActiveRecord::Base
2
2
3 def self.find_by_host_and_pid(host,pid)
3 def self.find_by_host_and_pid(host,pid)
4 - return GraderProcess.find(:first,
4 + return GraderProcess.where(host:host).where(pid: pid).first
5 - :conditions => {
6 - :host => host,
7 - :pid => pid
8 - })
9 end
5 end
10
6
11 def self.register(host,pid,mode)
7 def self.register(host,pid,mode)
12 grader = GraderProcess.find_by_host_and_pid(host,pid)
8 grader = GraderProcess.find_by_host_and_pid(host,pid)
13 if grader
9 if grader
14 grader.mode = mode
10 grader.mode = mode
15 grader.active = nil
11 grader.active = nil
16 grader.task_id = nil
12 grader.task_id = nil
17 grader.task_type = nil
13 grader.task_type = nil
18 grader.terminated = false
14 grader.terminated = false
19 grader.save
15 grader.save
20 else
16 else
21 grader = GraderProcess.create(:host => host,
17 grader = GraderProcess.create(:host => host,
22 :pid => pid,
18 :pid => pid,
23 :mode => mode,
19 :mode => mode,
24 :terminated => false)
20 :terminated => false)
25 end
21 end
26 grader
22 grader
27 end
23 end
28
24
29 def self.find_running_graders
25 def self.find_running_graders
30 - GraderProcess.find(:all,
26 + where(terminated: false)
31 - :conditions => {:terminated => 0})
32 end
27 end
33
28
34 def self.find_terminated_graders
29 def self.find_terminated_graders
35 - GraderProcess.find(:all,
30 + where(terminated: true)
36 - :conditions => "`terminated`")
37 end
31 end
38
32
39 def self.find_stalled_process
33 def self.find_stalled_process
40 - GraderProcess.find(:all,
34 + where(terminated: false).where(active: true).where("updated_at < ?",Time.now.gmtime - GraderProcess.stalled_time)
41 - :conditions => ["(`terminated` = 0) AND active AND " +
42 - "(updated_at < ?)",
43 - Time.now.gmtime - GraderProcess.stalled_time])
44 end
35 end
45
36
46 def report_active(task=nil)
37 def report_active(task=nil)
47 self.active = true
38 self.active = true
48 if task!=nil
39 if task!=nil
49 self.task_id = task.id
40 self.task_id = task.id
50 self.task_type = task.class.to_s
41 self.task_type = task.class.to_s
51 else
42 else
52 self.task_id = nil
43 self.task_id = nil
53 self.task_type = nil
44 self.task_type = nil
54 end
45 end
55 self.save
46 self.save
56 end
47 end
57
48
58 def report_inactive(task=nil)
49 def report_inactive(task=nil)
59 self.active = false
50 self.active = false
60 if task!=nil
51 if task!=nil
61 self.task_id = task.id
52 self.task_id = task.id
62 self.task_type = task.class.to_s
53 self.task_type = task.class.to_s
63 else
54 else
64 self.task_id = nil
55 self.task_id = nil
65 self.task_type = nil
56 self.task_type = nil
66 end
57 end
67 self.save
58 self.save
68 end
59 end
69
60
70 def terminate
61 def terminate
71 self.terminated = true
62 self.terminated = true
72 self.save
63 self.save
73 end
64 end
74
65
75 protected
66 protected
76
67
77 def self.stalled_time()
68 def self.stalled_time()
78 return 1.minute
69 return 1.minute
79 end
70 end
80
71
81 end
72 end
@@ -1,24 +1,24
1 class Language < ActiveRecord::Base
1 class Language < ActiveRecord::Base
2
2
3 @@languages_by_ext = {}
3 @@languages_by_ext = {}
4
4
5 def self.cache_ext_hash
5 def self.cache_ext_hash
6 @@languages_by_ext = {}
6 @@languages_by_ext = {}
7 - Language.find(:all).each do |language|
7 + Language.all.each do |language|
8 language.common_ext.split(',').each do |ext|
8 language.common_ext.split(',').each do |ext|
9 @@languages_by_ext[ext] = language
9 @@languages_by_ext[ext] = language
10 end
10 end
11 end
11 end
12 end
12 end
13
13
14 def self.find_by_extension(ext)
14 def self.find_by_extension(ext)
15 if @@languages_by_ext.length == 0
15 if @@languages_by_ext.length == 0
16 Language.cache_ext_hash
16 Language.cache_ext_hash
17 end
17 end
18 if @@languages_by_ext.has_key? ext
18 if @@languages_by_ext.has_key? ext
19 return @@languages_by_ext[ext]
19 return @@languages_by_ext[ext]
20 else
20 else
21 return nil
21 return nil
22 end
22 end
23 end
23 end
24 end
24 end
@@ -1,5 +1,4
1 class Login < ActiveRecord::Base
1 class Login < ActiveRecord::Base
2 belongs_to :user
2 belongs_to :user
3
3
4 - attr_accessible :ip_address, :logged_in_at, :user_id
5 end
4 end
@@ -1,60 +1,58
1 class Message < ActiveRecord::Base
1 class Message < ActiveRecord::Base
2
2
3 belongs_to :sender, :class_name => "User"
3 belongs_to :sender, :class_name => "User"
4 belongs_to :receiver, :class_name => "User"
4 belongs_to :receiver, :class_name => "User"
5
5
6 belongs_to :replying_message, :class_name => "Message"
6 belongs_to :replying_message, :class_name => "Message"
7
7
8 # commented manually do it
8 # commented manually do it
9 #
9 #
10 #has_many :replied_messages, {
10 #has_many :replied_messages, {
11 # :class_name => "Message",
11 # :class_name => "Message",
12 # :foreign_key => "replying_message_id"
12 # :foreign_key => "replying_message_id"
13 #}
13 #}
14 #
14 #
15
15
16 attr_accessor :replied_messages
16 attr_accessor :replied_messages
17
17
18 def self.find_all_sent_by_user(user)
18 def self.find_all_sent_by_user(user)
19 messages = user.messages
19 messages = user.messages
20 replied_messages = user.replied_messages
20 replied_messages = user.replied_messages
21 Message.build_replying_message_hierarchy messages, replied_messages
21 Message.build_replying_message_hierarchy messages, replied_messages
22 return messages
22 return messages
23 end
23 end
24
24
25 def self.find_all_system_unreplied_messages
25 def self.find_all_system_unreplied_messages
26 - self.find(:all,
26 + where('ISNULL(receiver_id) ' +
27 - :conditions => 'ISNULL(receiver_id) ' +
27 + 'AND (ISNULL(replied) OR replied=0)')
28 - 'AND (ISNULL(replied) OR replied=0)',
29 - :order => 'created_at')
30 end
28 end
31
29
32 def self.build_replying_message_hierarchy(*args)
30 def self.build_replying_message_hierarchy(*args)
33 # manually build replies hierarchy (to improve efficiency)
31 # manually build replies hierarchy (to improve efficiency)
34 all_messages = {}
32 all_messages = {}
35
33
36 args.each do |collection|
34 args.each do |collection|
37 collection.each do |m|
35 collection.each do |m|
38 all_messages[m.id] = m
36 all_messages[m.id] = m
39 m.replied_messages = []
37 m.replied_messages = []
40 end
38 end
41 end
39 end
42
40
43 all_messages.each_value do |m|
41 all_messages.each_value do |m|
44 rep_id = m.replying_message_id
42 rep_id = m.replying_message_id
45 if all_messages[rep_id]!=nil
43 if all_messages[rep_id]!=nil
46 all_messages[rep_id].add_replied_message(m)
44 all_messages[rep_id].add_replied_message(m)
47 end
45 end
48 end
46 end
49 end
47 end
50
48
51 def add_replied_message(m)
49 def add_replied_message(m)
52 if @replied_messages==nil
50 if @replied_messages==nil
53 @replied_messages = [m]
51 @replied_messages = [m]
54 else
52 else
55 @replied_messages << m
53 @replied_messages << m
56 end
54 end
57 @replied_messages
55 @replied_messages
58 end
56 end
59
57
60 end
58 end
@@ -1,133 +1,136
1 class Problem < ActiveRecord::Base
1 class Problem < ActiveRecord::Base
2
2
3 belongs_to :description
3 belongs_to :description
4 has_and_belongs_to_many :contests, :uniq => true
4 has_and_belongs_to_many :contests, :uniq => true
5 has_many :test_pairs, :dependent => :delete_all
5 has_many :test_pairs, :dependent => :delete_all
6 has_many :testcases, :dependent => :destroy
6 has_many :testcases, :dependent => :destroy
7
7
8 validates_presence_of :name
8 validates_presence_of :name
9 - validates_format_of :name, :with => /^\w+$/
9 + validates_format_of :name, :with => /\A\w+\z/
10 validates_presence_of :full_name
10 validates_presence_of :full_name
11
11
12 - scope :available, :conditions => {:available => true}
12 + scope :available, -> { where(available: true) }
13
13
14 DEFAULT_TIME_LIMIT = 1
14 DEFAULT_TIME_LIMIT = 1
15 DEFAULT_MEMORY_LIMIT = 32
15 DEFAULT_MEMORY_LIMIT = 32
16
16
17 - def self.find_available_problems
17 + def self.available_problems
18 - Problem.available.all(:order => "date_added DESC, name ASC")
18 + available.order(date_added: :desc).order(:name)
19 + #Problem.available.all(:order => "date_added DESC, name ASC")
19 end
20 end
20
21
21 def self.create_from_import_form_params(params, old_problem=nil)
22 def self.create_from_import_form_params(params, old_problem=nil)
22 org_problem = old_problem || Problem.new
23 org_problem = old_problem || Problem.new
23 import_params, problem = Problem.extract_params_and_check(params,
24 import_params, problem = Problem.extract_params_and_check(params,
24 org_problem)
25 org_problem)
25
26
26 if !problem.errors.empty?
27 if !problem.errors.empty?
27 return problem, 'Error importing'
28 return problem, 'Error importing'
28 end
29 end
29
30
30 problem.full_score = 100
31 problem.full_score = 100
31 problem.date_added = Time.new
32 problem.date_added = Time.new
32 problem.test_allowed = true
33 problem.test_allowed = true
33 problem.output_only = false
34 problem.output_only = false
34 problem.available = false
35 problem.available = false
35
36
36 if not problem.save
37 if not problem.save
37 return problem, 'Error importing'
38 return problem, 'Error importing'
38 end
39 end
39
40
40 import_to_db = params.has_key? :import_to_db
41 import_to_db = params.has_key? :import_to_db
41
42
42 importer = TestdataImporter.new(problem)
43 importer = TestdataImporter.new(problem)
43
44
44 if not importer.import_from_file(import_params[:file],
45 if not importer.import_from_file(import_params[:file],
45 import_params[:time_limit],
46 import_params[:time_limit],
46 import_params[:memory_limit],
47 import_params[:memory_limit],
47 import_params[:checker_name],
48 import_params[:checker_name],
48 import_to_db)
49 import_to_db)
49 problem.errors.add(:base,'Import error.')
50 problem.errors.add(:base,'Import error.')
50 end
51 end
51
52
52 return problem, importer.log_msg
53 return problem, importer.log_msg
53 end
54 end
54
55
55 def self.download_file_basedir
56 def self.download_file_basedir
56 return "#{Rails.root}/data/tasks"
57 return "#{Rails.root}/data/tasks"
57 end
58 end
58
59
59 def get_submission_stat
60 def get_submission_stat
60 result = Hash.new
61 result = Hash.new
61 #total number of submission
62 #total number of submission
62 result[:total_sub] = Submission.where(problem_id: self.id).count
63 result[:total_sub] = Submission.where(problem_id: self.id).count
63 - result[:attempted_user] = Submission.where(problem_id: self.id).group_by(:user_id)
64 + result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
65 + result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
66 + return result
64 end
67 end
65
68
66 def long_name
69 def long_name
67 "[#{name}] #{full_name}"
70 "[#{name}] #{full_name}"
68 end
71 end
69
72
70 protected
73 protected
71
74
72 def self.to_i_or_default(st, default)
75 def self.to_i_or_default(st, default)
73 if st!=''
76 if st!=''
74 result = st.to_i
77 result = st.to_i
75 end
78 end
76 result ||= default
79 result ||= default
77 end
80 end
78
81
79 def self.to_f_or_default(st, default)
82 def self.to_f_or_default(st, default)
80 if st!=''
83 if st!=''
81 result = st.to_f
84 result = st.to_f
82 end
85 end
83 result ||= default
86 result ||= default
84 end
87 end
85
88
86 def self.extract_params_and_check(params, problem)
89 def self.extract_params_and_check(params, problem)
87 time_limit = Problem.to_f_or_default(params[:time_limit],
90 time_limit = Problem.to_f_or_default(params[:time_limit],
88 DEFAULT_TIME_LIMIT)
91 DEFAULT_TIME_LIMIT)
89 memory_limit = Problem.to_i_or_default(params[:memory_limit],
92 memory_limit = Problem.to_i_or_default(params[:memory_limit],
90 DEFAULT_MEMORY_LIMIT)
93 DEFAULT_MEMORY_LIMIT)
91
94
92 if time_limit<=0 or time_limit >60
95 if time_limit<=0 or time_limit >60
93 problem.errors.add(:base,'Time limit out of range.')
96 problem.errors.add(:base,'Time limit out of range.')
94 end
97 end
95
98
96 if memory_limit==0 and params[:memory_limit]!='0'
99 if memory_limit==0 and params[:memory_limit]!='0'
97 problem.errors.add(:base,'Memory limit format errors.')
100 problem.errors.add(:base,'Memory limit format errors.')
98 elsif memory_limit<=0 or memory_limit >512
101 elsif memory_limit<=0 or memory_limit >512
99 problem.errors.add(:base,'Memory limit out of range.')
102 problem.errors.add(:base,'Memory limit out of range.')
100 end
103 end
101
104
102 if params[:file]==nil or params[:file]==''
105 if params[:file]==nil or params[:file]==''
103 problem.errors.add(:base,'No testdata file.')
106 problem.errors.add(:base,'No testdata file.')
104 end
107 end
105
108
106 checker_name = 'text'
109 checker_name = 'text'
107 if ['text','float'].include? params[:checker]
110 if ['text','float'].include? params[:checker]
108 checker_name = params[:checker]
111 checker_name = params[:checker]
109 end
112 end
110
113
111 file = params[:file]
114 file = params[:file]
112
115
113 if !problem.errors.empty?
116 if !problem.errors.empty?
114 return nil, problem
117 return nil, problem
115 end
118 end
116
119
117 problem.name = params[:name]
120 problem.name = params[:name]
118 if params[:full_name]!=''
121 if params[:full_name]!=''
119 problem.full_name = params[:full_name]
122 problem.full_name = params[:full_name]
120 else
123 else
121 problem.full_name = params[:name]
124 problem.full_name = params[:name]
122 end
125 end
123
126
124 return [{
127 return [{
125 :time_limit => time_limit,
128 :time_limit => time_limit,
126 :memory_limit => memory_limit,
129 :memory_limit => memory_limit,
127 :file => file,
130 :file => file,
128 :checker_name => checker_name
131 :checker_name => checker_name
129 },
132 },
130 problem]
133 problem]
131 end
134 end
132
135
133 end
136 end
@@ -1,164 +1,153
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
17 +
16 before_save :assign_latest_number_if_new_recond
18 before_save :assign_latest_number_if_new_recond
17
19
18 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)
19 - last_sub = find(:first,
21 + where("user_id = ? AND problem_id = ?",user_id,problem_id).last
20 - :conditions => {:user_id => user_id,
21 - :problem_id => problem_id},
22 - :order => 'number DESC')
23 - return last_sub
24 end
22 end
25
23
26 def self.find_all_last_by_problem(problem_id)
24 def self.find_all_last_by_problem(problem_id)
27 # 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
28 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
29 "WHERE id = " +
27 "WHERE id = " +
30 "(SELECT MAX(id) FROM submissions AS subs " +
28 "(SELECT MAX(id) FROM submissions AS subs " +
31 "WHERE subs.user_id = submissions.user_id AND " +
29 "WHERE subs.user_id = submissions.user_id AND " +
32 "problem_id = " + problem_id.to_s + " " +
30 "problem_id = " + problem_id.to_s + " " +
33 "GROUP BY user_id) " +
31 "GROUP BY user_id) " +
34 "ORDER BY user_id")
32 "ORDER BY user_id")
35 end
33 end
36
34
37 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)
38 records = Submission.where(problem_id: problem_id,user_id: user_id)
36 records = Submission.where(problem_id: problem_id,user_id: user_id)
39 records = records.where('id >= ?',since_id) if since_id > 0
37 records = records.where('id >= ?',since_id) if since_id > 0
40 records = records.where('id <= ?',until_id) if until_id > 0
38 records = records.where('id <= ?',until_id) if until_id > 0
41 records.all
39 records.all
42 end
40 end
43
41
44 def self.find_last_for_all_available_problems(user_id)
42 def self.find_last_for_all_available_problems(user_id)
45 submissions = Array.new
43 submissions = Array.new
46 - problems = Problem.find_available_problems
44 + problems = Problem.available_problems
47 problems.each do |problem|
45 problems.each do |problem|
48 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)
49 submissions << sub if sub!=nil
47 submissions << sub if sub!=nil
50 end
48 end
51 submissions
49 submissions
52 end
50 end
53
51
54 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)
55 - Submission.find(:first,
53 + where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
56 - :conditions => {
57 - :user_id => user_id,
58 - :problem_id => problem_id,
59 - :number => number
60 - })
61 end
54 end
62
55
63 def self.find_all_by_user_problem(user_id, problem_id)
56 def self.find_all_by_user_problem(user_id, problem_id)
64 - Submission.find(:all,
57 + where("user_id = ? AND problem_id = ?",user_id,problem_id)
65 - :conditions => {
66 - :user_id => user_id,
67 - :problem_id => problem_id,
68 - })
69 end
58 end
70
59
71 def download_filename
60 def download_filename
72 if self.problem.output_only
61 if self.problem.output_only
73 return self.source_filename
62 return self.source_filename
74 else
63 else
75 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
76 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
77 end
66 end
78 end
67 end
79
68
80 protected
69 protected
81
70
82 def self.find_option_in_source(option, source)
71 def self.find_option_in_source(option, source)
83 if source==nil
72 if source==nil
84 return nil
73 return nil
85 end
74 end
86 i = 0
75 i = 0
87 source.each_line do |s|
76 source.each_line do |s|
88 if s =~ option
77 if s =~ option
89 words = s.split
78 words = s.split
90 return words[1]
79 return words[1]
91 end
80 end
92 i = i + 1
81 i = i + 1
93 if i==10
82 if i==10
94 return nil
83 return nil
95 end
84 end
96 end
85 end
97 return nil
86 return nil
98 end
87 end
99
88
100 def self.find_language_in_source(source, source_filename="")
89 def self.find_language_in_source(source, source_filename="")
101 langopt = find_option_in_source(/^LANG:/,source)
90 langopt = find_option_in_source(/^LANG:/,source)
102 if langopt
91 if langopt
103 return (Language.find_by_name(langopt) ||
92 return (Language.find_by_name(langopt) ||
104 Language.find_by_pretty_name(langopt))
93 Language.find_by_pretty_name(langopt))
105 else
94 else
106 if source_filename
95 if source_filename
107 return Language.find_by_extension(source_filename.split('.').last)
96 return Language.find_by_extension(source_filename.split('.').last)
108 else
97 else
109 return nil
98 return nil
110 end
99 end
111 end
100 end
112 end
101 end
113
102
114 def self.find_problem_in_source(source, source_filename="")
103 def self.find_problem_in_source(source, source_filename="")
115 prob_opt = find_option_in_source(/^TASK:/,source)
104 prob_opt = find_option_in_source(/^TASK:/,source)
116 if problem = Problem.find_by_name(prob_opt)
105 if problem = Problem.find_by_name(prob_opt)
117 return problem
106 return problem
118 else
107 else
119 if source_filename
108 if source_filename
120 return Problem.find_by_name(source_filename.split('.').first)
109 return Problem.find_by_name(source_filename.split('.').first)
121 else
110 else
122 return nil
111 return nil
123 end
112 end
124 end
113 end
125 end
114 end
126
115
127 def assign_problem
116 def assign_problem
128 if self.problem_id!=-1
117 if self.problem_id!=-1
129 begin
118 begin
130 self.problem = Problem.find(self.problem_id)
119 self.problem = Problem.find(self.problem_id)
131 rescue ActiveRecord::RecordNotFound
120 rescue ActiveRecord::RecordNotFound
132 self.problem = nil
121 self.problem = nil
133 end
122 end
134 else
123 else
135 self.problem = Submission.find_problem_in_source(self.source,
124 self.problem = Submission.find_problem_in_source(self.source,
136 self.source_filename)
125 self.source_filename)
137 end
126 end
138 end
127 end
139
128
140 def assign_language
129 def assign_language
141 self.language = Submission.find_language_in_source(self.source,
130 self.language = Submission.find_language_in_source(self.source,
142 self.source_filename)
131 self.source_filename)
143 end
132 end
144
133
145 # validation codes
134 # validation codes
146 def must_specify_language
135 def must_specify_language
147 return if self.source==nil
136 return if self.source==nil
148
137
149 # for output_only tasks
138 # for output_only tasks
150 return if self.problem!=nil and self.problem.output_only
139 return if self.problem!=nil and self.problem.output_only
151
140
152 if self.language==nil
141 if self.language==nil
153 errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
142 errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
154 end
143 end
155 end
144 end
156
145
157 def must_have_valid_problem
146 def must_have_valid_problem
158 return if self.source==nil
147 return if self.source==nil
159 if self.problem==nil
148 if self.problem==nil
160 errors.add('problem',"must be specified.")
149 errors.add('problem',"must be specified.")
161 elsif (!self.problem.available) and (self.new_record?)
150 elsif (!self.problem.available) and (self.new_record?)
162 errors.add('problem',"must be valid.")
151 errors.add('problem',"must be valid.")
163 end
152 end
164 end
153 end
@@ -1,3 +1,3
1 class SubmissionViewLog < ActiveRecord::Base
1 class SubmissionViewLog < ActiveRecord::Base
2 - attr_accessible :submission_id, :user_id
2 + #attr_accessible :submission_id, :user_id
3 end
3 end
@@ -1,68 +1,65
1 class Task < ActiveRecord::Base
1 class Task < ActiveRecord::Base
2
2
3 belongs_to :submission
3 belongs_to :submission
4
4
5 STATUS_GRADING = 0
5 STATUS_GRADING = 0
6 STATUS_INQUEUE = 1
6 STATUS_INQUEUE = 1
7 STATUS_COMPLETE = 2
7 STATUS_COMPLETE = 2
8
8
9 def status_inqueue
9 def status_inqueue
10 self.status = Task::STATUS_INQUEUE
10 self.status = Task::STATUS_INQUEUE
11 end
11 end
12
12
13 def status_inqueue!
13 def status_inqueue!
14 status_inqueue
14 status_inqueue
15 self.save
15 self.save
16 end
16 end
17
17
18 def status_grading
18 def status_grading
19 self.status = Task::STATUS_GRADING
19 self.status = Task::STATUS_GRADING
20 end
20 end
21
21
22 def status_grading!
22 def status_grading!
23 status_grading
23 status_grading
24 self.save
24 self.save
25 end
25 end
26
26
27 def status_complete
27 def status_complete
28 self.status = Task::STATUS_COMPLETE
28 self.status = Task::STATUS_COMPLETE
29 end
29 end
30
30
31 def status_complete!
31 def status_complete!
32 status_complete
32 status_complete
33 self.save
33 self.save
34 end
34 end
35
35
36 def status_str
36 def status_str
37 case self.status
37 case self.status
38 when Task::STATUS_INQUEUE
38 when Task::STATUS_INQUEUE
39 "inqueue"
39 "inqueue"
40 when Task::STATUS_GRADING
40 when Task::STATUS_GRADING
41 "grading"
41 "grading"
42 when Task::STATUS_COMPLETE
42 when Task::STATUS_COMPLETE
43 "complete"
43 "complete"
44 end
44 end
45 end
45 end
46
46
47 def self.get_inqueue_and_change_status(status)
47 def self.get_inqueue_and_change_status(status)
48 task = nil
48 task = nil
49 begin
49 begin
50 Task.transaction do
50 Task.transaction do
51 - task = Task.find(:first,
51 + task = Task.where(status: Task::STATUS_INQUEUE).lock(true).first
52 - :order => "created_at",
53 - :conditions => {:status=> Task::STATUS_INQUEUE},
54 - :lock => true)
55 if task!=nil
52 if task!=nil
56 task.status = status
53 task.status = status
57 task.save!
54 task.save!
58 end
55 end
59 end
56 end
60
57
61 rescue
58 rescue
62 task = nil
59 task = nil
63
60
64 end
61 end
65 task
62 task
66 end
63 end
67
64
68 end
65 end
@@ -1,139 +1,136
1 #
1 #
2 # A TestRequest is a composition of submission with user's testdata.
2 # A TestRequest is a composition of submission with user's testdata.
3 #
3 #
4 # Note about TestRequest#problem: Usually, A TestRequest has to be
4 # Note about TestRequest#problem: Usually, A TestRequest has to be
5 # associated with a problem, so that execution environment can be
5 # associated with a problem, so that execution environment can be
6 # determined. However, to be more flexible, we have to ensure that
6 # determined. However, to be more flexible, we have to ensure that
7 # it works as well with problem=nil. In this case, we shall provide
7 # it works as well with problem=nil. In this case, we shall provide
8 # a "default" execution environment for it. This can be done
8 # a "default" execution environment for it. This can be done
9 # seamlessly by using TestRequest#problem_name or
9 # seamlessly by using TestRequest#problem_name or
10 # TestRequest#name_of(problem) when retrieving the name of the
10 # TestRequest#name_of(problem) when retrieving the name of the
11 # problem: #name_of would return problem.name when problem!=nil and
11 # problem: #name_of would return problem.name when problem!=nil and
12 # it would return "default" when problem=nil, #problem_name just
12 # it would return "default" when problem=nil, #problem_name just
13 # call #name_of.
13 # call #name_of.
14 #
14 #
15
15
16 require 'fileutils'
16 require 'fileutils'
17
17
18 class TestRequest < Task
18 class TestRequest < Task
19 -
19 + self.table_name = "test_requests"
20 - set_table_name "test_requests"
21
20
22 belongs_to :user
21 belongs_to :user
23 belongs_to :problem
22 belongs_to :problem
24 belongs_to :submission
23 belongs_to :submission
25
24
26 validates_presence_of :submission
25 validates_presence_of :submission
27 validate :must_have_valid_problem
26 validate :must_have_valid_problem
28
27
29 def problem_name
28 def problem_name
30 TestRequest.name_of(self.problem)
29 TestRequest.name_of(self.problem)
31 end
30 end
32
31
33 def language
32 def language
34 self.submission.language
33 self.submission.language
35 end
34 end
36
35
37 def self.get_inqueue_and_change_status(status)
36 def self.get_inqueue_and_change_status(status)
38 # since there will be only one grader grading TestRequest
37 # since there will be only one grader grading TestRequest
39 # we do not need locking (hopefully)
38 # we do not need locking (hopefully)
40
39
41 - test_request = TestRequest.find(:first,
40 + test_request = TestRequest.where(status: Task::STATUS_INQUEUE).first
42 - :order => "created_at",
43 - :conditions => {:status=> Task::STATUS_INQUEUE})
44 if test_request!=nil
41 if test_request!=nil
45 test_request.status = status
42 test_request.status = status
46 test_request.save!
43 test_request.save!
47 end
44 end
48
45
49 test_request
46 test_request
50 end
47 end
51
48
52 # interfacing with form
49 # interfacing with form
53 def self.new_from_form_params(user,params)
50 def self.new_from_form_params(user,params)
54 test_request = TestRequest.new
51 test_request = TestRequest.new
55 test_request.user = user
52 test_request.user = user
56 begin
53 begin
57 problem = Problem.find(params[:problem_id])
54 problem = Problem.find(params[:problem_id])
58 rescue ActiveRecord::RecordNotFound
55 rescue ActiveRecord::RecordNotFound
59 problem = nil
56 problem = nil
60 end
57 end
61 test_request.problem = problem
58 test_request.problem = problem
62 if problem!=nil
59 if problem!=nil
63 test_request.submission =
60 test_request.submission =
64 Submission.find_by_user_problem_number(user.id,
61 Submission.find_by_user_problem_number(user.id,
65 problem.id,
62 problem.id,
66 params[:submission_number])
63 params[:submission_number])
67 else
64 else
68 test_request.submission = nil
65 test_request.submission = nil
69 end
66 end
70
67
71 # checks if the user submits any input file
68 # checks if the user submits any input file
72 if params[:input_file]==nil or params[:input_file]==""
69 if params[:input_file]==nil or params[:input_file]==""
73 test_request.errors.add(:base,"No input submitted.")
70 test_request.errors.add(:base,"No input submitted.")
74 test_request.input_file_name = nil
71 test_request.input_file_name = nil
75 else
72 else
76 test_request.input_file_name = save_input_file(params[:input_file], user, problem)
73 test_request.input_file_name = save_input_file(params[:input_file], user, problem)
77 if test_request.input_file_name == nil
74 if test_request.input_file_name == nil
78 test_request.errors.adds(:base,"No input submitted.")
75 test_request.errors.adds(:base,"No input submitted.")
79 end
76 end
80 if params[:additional_file]!=nil and params[:additional_file]!=""
77 if params[:additional_file]!=nil and params[:additional_file]!=""
81 save_additional_file(params[:additional_file],
78 save_additional_file(params[:additional_file],
82 "#{test_request.input_file_name}.files")
79 "#{test_request.input_file_name}.files")
83 end
80 end
84 end
81 end
85 test_request.submitted_at = Time.new.gmtime
82 test_request.submitted_at = Time.new.gmtime
86 test_request.status_inqueue
83 test_request.status_inqueue
87 test_request
84 test_request
88 end
85 end
89
86
90 protected
87 protected
91
88
92 def self.name_of(problem)
89 def self.name_of(problem)
93 if problem!=nil
90 if problem!=nil
94 problem.name
91 problem.name
95 else
92 else
96 "default"
93 "default"
97 end
94 end
98 end
95 end
99
96
100 def self.random_input_file_name(user,problem)
97 def self.random_input_file_name(user,problem)
101 problem_name = TestRequest.name_of(problem)
98 problem_name = TestRequest.name_of(problem)
102 begin
99 begin
103 tmpname = TEST_REQUEST_INPUT_FILE_DIR + "/#{user.login}/#{problem_name}/#{rand(10000)}"
100 tmpname = TEST_REQUEST_INPUT_FILE_DIR + "/#{user.login}/#{problem_name}/#{rand(10000)}"
104 end while File.exists?(tmpname)
101 end while File.exists?(tmpname)
105 tmpname
102 tmpname
106 end
103 end
107
104
108 def self.save_input_file(tempfile, user, problem)
105 def self.save_input_file(tempfile, user, problem)
109 new_file_name = random_input_file_name(user,problem)
106 new_file_name = random_input_file_name(user,problem)
110 dirname = File.dirname(new_file_name)
107 dirname = File.dirname(new_file_name)
111 FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname)
108 FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname)
112
109
113 # when the user did not submit any file
110 # when the user did not submit any file
114 return nil if tempfile==""
111 return nil if tempfile==""
115
112
116 if tempfile.instance_of?(Tempfile)
113 if tempfile.instance_of?(Tempfile)
117 tempfile.close
114 tempfile.close
118 FileUtils.move(tempfile.path,new_file_name)
115 FileUtils.move(tempfile.path,new_file_name)
119 else
116 else
120 File.open(new_file_name, "wb") do |f|
117 File.open(new_file_name, "wb") do |f|
121 f.write(tempfile.read)
118 f.write(tempfile.read)
122 end
119 end
123 end
120 end
124 new_file_name
121 new_file_name
125 end
122 end
126
123
127 def self.save_additional_file(tempfile,dir)
124 def self.save_additional_file(tempfile,dir)
128 new_file_name = "#{dir}/#{tempfile.original_filename}"
125 new_file_name = "#{dir}/#{tempfile.original_filename}"
129 dirname = File.dirname(new_file_name)
126 dirname = File.dirname(new_file_name)
130 FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname)
127 FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname)
131
128
132 # when the user did not submit any file
129 # when the user did not submit any file
133 return nil if tempfile==""
130 return nil if tempfile==""
134
131
135 if tempfile.instance_of?(Tempfile)
132 if tempfile.instance_of?(Tempfile)
136 tempfile.close
133 tempfile.close
137 FileUtils.move(tempfile.path,new_file_name)
134 FileUtils.move(tempfile.path,new_file_name)
138 else
135 else
139 File.open(new_file_name, "wb") do |f|
136 File.open(new_file_name, "wb") do |f|
@@ -1,4 +1,4
1 class Testcase < ActiveRecord::Base
1 class Testcase < ActiveRecord::Base
2 belongs_to :problem
2 belongs_to :problem
3 - attr_accessible :group, :input, :num, :score, :sol
3 + #attr_accessible :group, :input, :num, :score, :sol
4 end
4 end
@@ -1,333 +1,340
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10
10
11 - has_many :test_requests, :order => "submitted_at DESC"
11 + has_many :test_requests, -> {order(submitted_at: DESC)}
12
12
13 - has_many :messages,
13 + has_many :messages, -> { order(created_at: DESC) },
14 :class_name => "Message",
14 :class_name => "Message",
15 - :foreign_key => "sender_id",
15 + :foreign_key => "sender_id"
16 - :order => 'created_at DESC'
17
16
18 - has_many :replied_messages,
17 + has_many :replied_messages, -> { order(created_at: DESC) },
19 :class_name => "Message",
18 :class_name => "Message",
20 - :foreign_key => "receiver_id",
19 + :foreign_key => "receiver_id"
21 - :order => 'created_at DESC'
22
20
23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
21 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
24
22
25 belongs_to :site
23 belongs_to :site
26 belongs_to :country
24 belongs_to :country
27
25
28 - has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
26 + has_and_belongs_to_many :contests, -> { order(:name); uniq}
29
27
30 - scope :activated_users, :conditions => {:activated => true}
28 + scope :activated_users, -> {where activated: true}
31
29
32 validates_presence_of :login
30 validates_presence_of :login
33 validates_uniqueness_of :login
31 validates_uniqueness_of :login
34 - validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
32 + validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
35 validates_length_of :login, :within => 3..30
33 validates_length_of :login, :within => 3..30
36
34
37 validates_presence_of :full_name
35 validates_presence_of :full_name
38 validates_length_of :full_name, :minimum => 1
36 validates_length_of :full_name, :minimum => 1
39
37
40 validates_presence_of :password, :if => :password_required?
38 validates_presence_of :password, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
39 validates_length_of :password, :within => 4..20, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
40 validates_confirmation_of :password, :if => :password_required?
43
41
44 validates_format_of :email,
42 validates_format_of :email,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
43 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
46 :if => :email_validation?
44 :if => :email_validation?
47 validate :uniqueness_of_email_from_activated_users,
45 validate :uniqueness_of_email_from_activated_users,
48 :if => :email_validation?
46 :if => :email_validation?
49 validate :enough_time_interval_between_same_email_registrations,
47 validate :enough_time_interval_between_same_email_registrations,
50 :if => :email_validation?
48 :if => :email_validation?
51
49
52 # these are for ytopc
50 # these are for ytopc
53 # disable for now
51 # disable for now
54 #validates_presence_of :province
52 #validates_presence_of :province
55
53
56 attr_accessor :password
54 attr_accessor :password
57
55
58 before_save :encrypt_new_password
56 before_save :encrypt_new_password
59 before_save :assign_default_site
57 before_save :assign_default_site
60 before_save :assign_default_contest
58 before_save :assign_default_contest
61
59
62 # this is for will_paginate
60 # this is for will_paginate
63 cattr_reader :per_page
61 cattr_reader :per_page
64 @@per_page = 50
62 @@per_page = 50
65
63
66 def self.authenticate(login, password)
64 def self.authenticate(login, password)
67 user = find_by_login(login)
65 user = find_by_login(login)
68 if user
66 if user
69 return user if user.authenticated?(password)
67 return user if user.authenticated?(password)
70 end
68 end
71 end
69 end
72
70
73 def authenticated?(password)
71 def authenticated?(password)
74 if self.activated
72 if self.activated
75 hashed_password == User.encrypt(password,self.salt)
73 hashed_password == User.encrypt(password,self.salt)
76 else
74 else
77 false
75 false
78 end
76 end
79 end
77 end
80
78
81 def admin?
79 def admin?
82 self.roles.detect {|r| r.name == 'admin' }
80 self.roles.detect {|r| r.name == 'admin' }
83 end
81 end
84
82
85 def email_for_editing
83 def email_for_editing
86 if self.email==nil
84 if self.email==nil
87 "(unknown)"
85 "(unknown)"
88 elsif self.email==''
86 elsif self.email==''
89 "(blank)"
87 "(blank)"
90 else
88 else
91 self.email
89 self.email
92 end
90 end
93 end
91 end
94
92
95 def email_for_editing=(e)
93 def email_for_editing=(e)
96 self.email=e
94 self.email=e
97 end
95 end
98
96
99 def alias_for_editing
97 def alias_for_editing
100 if self.alias==nil
98 if self.alias==nil
101 "(unknown)"
99 "(unknown)"
102 elsif self.alias==''
100 elsif self.alias==''
103 "(blank)"
101 "(blank)"
104 else
102 else
105 self.alias
103 self.alias
106 end
104 end
107 end
105 end
108
106
109 def alias_for_editing=(e)
107 def alias_for_editing=(e)
110 self.alias=e
108 self.alias=e
111 end
109 end
112
110
113 def activation_key
111 def activation_key
114 if self.hashed_password==nil
112 if self.hashed_password==nil
115 encrypt_new_password
113 encrypt_new_password
116 end
114 end
117 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
115 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
118 end
116 end
119
117
120 def verify_activation_key(key)
118 def verify_activation_key(key)
121 key == activation_key
119 key == activation_key
122 end
120 end
123
121
124 def self.random_password(length=5)
122 def self.random_password(length=5)
125 chars = 'abcdefghjkmnopqrstuvwxyz'
123 chars = 'abcdefghjkmnopqrstuvwxyz'
126 password = ''
124 password = ''
127 length.times { password << chars[rand(chars.length - 1)] }
125 length.times { password << chars[rand(chars.length - 1)] }
128 password
126 password
129 end
127 end
130
128
131 def self.find_non_admin_with_prefix(prefix='')
129 def self.find_non_admin_with_prefix(prefix='')
132 - users = User.find(:all)
130 + users = User.all
133 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
131 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
134 end
132 end
135
133
136 # Contest information
134 # Contest information
137
135
138 def self.find_users_with_no_contest()
136 def self.find_users_with_no_contest()
139 - users = User.find(:all)
137 + users = User.all
140 return users.find_all { |u| u.contests.length == 0 }
138 return users.find_all { |u| u.contests.length == 0 }
141 end
139 end
142
140
143
141
144 def contest_time_left
142 def contest_time_left
145 if GraderConfiguration.contest_mode?
143 if GraderConfiguration.contest_mode?
146 return nil if site==nil
144 return nil if site==nil
147 return site.time_left
145 return site.time_left
148 elsif GraderConfiguration.indv_contest_mode?
146 elsif GraderConfiguration.indv_contest_mode?
149 time_limit = GraderConfiguration.contest_time_limit
147 time_limit = GraderConfiguration.contest_time_limit
150 if time_limit == nil
148 if time_limit == nil
151 return nil
149 return nil
152 end
150 end
153 if contest_stat==nil or contest_stat.started_at==nil
151 if contest_stat==nil or contest_stat.started_at==nil
154 return (Time.now.gmtime + time_limit) - Time.now.gmtime
152 return (Time.now.gmtime + time_limit) - Time.now.gmtime
155 else
153 else
156 finish_time = contest_stat.started_at + time_limit
154 finish_time = contest_stat.started_at + time_limit
157 current_time = Time.now.gmtime
155 current_time = Time.now.gmtime
158 if current_time > finish_time
156 if current_time > finish_time
159 return 0
157 return 0
160 else
158 else
161 return finish_time - current_time
159 return finish_time - current_time
162 end
160 end
163 end
161 end
164 else
162 else
165 return nil
163 return nil
166 end
164 end
167 end
165 end
168
166
169 def contest_finished?
167 def contest_finished?
170 if GraderConfiguration.contest_mode?
168 if GraderConfiguration.contest_mode?
171 return false if site==nil
169 return false if site==nil
172 return site.finished?
170 return site.finished?
173 elsif GraderConfiguration.indv_contest_mode?
171 elsif GraderConfiguration.indv_contest_mode?
174 return false if self.contest_stat(true)==nil
172 return false if self.contest_stat(true)==nil
175 return contest_time_left == 0
173 return contest_time_left == 0
176 else
174 else
177 return false
175 return false
178 end
176 end
179 end
177 end
180
178
181 def contest_started?
179 def contest_started?
182 if GraderConfiguration.indv_contest_mode?
180 if GraderConfiguration.indv_contest_mode?
183 stat = self.contest_stat
181 stat = self.contest_stat
184 return ((stat != nil) and (stat.started_at != nil))
182 return ((stat != nil) and (stat.started_at != nil))
185 elsif GraderConfiguration.contest_mode?
183 elsif GraderConfiguration.contest_mode?
186 return true if site==nil
184 return true if site==nil
187 return site.started
185 return site.started
188 else
186 else
189 return true
187 return true
190 end
188 end
191 end
189 end
192
190
193 def update_start_time
191 def update_start_time
194 stat = self.contest_stat
192 stat = self.contest_stat
195 if stat.nil? or stat.started_at.nil?
193 if stat.nil? or stat.started_at.nil?
196 stat ||= UserContestStat.new(:user => self)
194 stat ||= UserContestStat.new(:user => self)
197 stat.started_at = Time.now.gmtime
195 stat.started_at = Time.now.gmtime
198 stat.save
196 stat.save
199 end
197 end
200 end
198 end
201
199
202 def problem_in_user_contests?(problem)
200 def problem_in_user_contests?(problem)
203 problem_contests = problem.contests.all
201 problem_contests = problem.contests.all
204
202
205 if problem_contests.length == 0 # this is public contest
203 if problem_contests.length == 0 # this is public contest
206 return true
204 return true
207 end
205 end
208
206
209 contests.each do |contest|
207 contests.each do |contest|
210 if problem_contests.find {|c| c.id == contest.id }
208 if problem_contests.find {|c| c.id == contest.id }
211 return true
209 return true
212 end
210 end
213 end
211 end
214 return false
212 return false
215 end
213 end
216
214
217 def available_problems_group_by_contests
215 def available_problems_group_by_contests
218 contest_problems = []
216 contest_problems = []
219 pin = {}
217 pin = {}
220 contests.enabled.each do |contest|
218 contests.enabled.each do |contest|
221 available_problems = contest.problems.available
219 available_problems = contest.problems.available
222 contest_problems << {
220 contest_problems << {
223 :contest => contest,
221 :contest => contest,
224 :problems => available_problems
222 :problems => available_problems
225 }
223 }
226 available_problems.each {|p| pin[p.id] = true}
224 available_problems.each {|p| pin[p.id] = true}
227 end
225 end
228 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
226 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
229 contest_problems << {
227 contest_problems << {
230 :contest => nil,
228 :contest => nil,
231 :problems => other_avaiable_problems
229 :problems => other_avaiable_problems
232 }
230 }
233 return contest_problems
231 return contest_problems
234 end
232 end
235
233
234 + def solve_all_available_problems?
235 + available_problems.each do |p|
236 + u = self
237 + sub = Submission.find_last_by_user_and_problem(u.id,p.id)
238 + return false if !p or !sub or sub.points < p.full_score
239 + end
240 + return true
241 + end
242 +
236 def available_problems
243 def available_problems
237 if not GraderConfiguration.multicontests?
244 if not GraderConfiguration.multicontests?
238 - return Problem.find_available_problems
245 + return Problem.available_problems
239 else
246 else
240 contest_problems = []
247 contest_problems = []
241 pin = {}
248 pin = {}
242 contests.enabled.each do |contest|
249 contests.enabled.each do |contest|
243 contest.problems.available.each do |problem|
250 contest.problems.available.each do |problem|
244 if not pin.has_key? problem.id
251 if not pin.has_key? problem.id
245 contest_problems << problem
252 contest_problems << problem
246 end
253 end
247 pin[problem.id] = true
254 pin[problem.id] = true
248 end
255 end
249 end
256 end
250 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
257 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
251 return contest_problems + other_avaiable_problems
258 return contest_problems + other_avaiable_problems
252 end
259 end
253 end
260 end
254
261
255 def can_view_problem?(problem)
262 def can_view_problem?(problem)
256 if not GraderConfiguration.multicontests?
263 if not GraderConfiguration.multicontests?
257 return problem.available
264 return problem.available
258 else
265 else
259 return problem_in_user_contests? problem
266 return problem_in_user_contests? problem
260 end
267 end
261 end
268 end
262
269
263 def self.clear_last_login
270 def self.clear_last_login
264 User.update_all(:last_ip => nil)
271 User.update_all(:last_ip => nil)
265 end
272 end
266
273
267 protected
274 protected
268 def encrypt_new_password
275 def encrypt_new_password
269 return if password.blank?
276 return if password.blank?
270 self.salt = (10+rand(90)).to_s
277 self.salt = (10+rand(90)).to_s
271 self.hashed_password = User.encrypt(self.password,self.salt)
278 self.hashed_password = User.encrypt(self.password,self.salt)
272 end
279 end
273
280
274 def assign_default_site
281 def assign_default_site
275 # have to catch error when migrating (because self.site is not available).
282 # have to catch error when migrating (because self.site is not available).
276 begin
283 begin
277 if self.site==nil
284 if self.site==nil
278 self.site = Site.find_by_name('default')
285 self.site = Site.find_by_name('default')
279 if self.site==nil
286 if self.site==nil
280 self.site = Site.find(1) # when 'default has be renamed'
287 self.site = Site.find(1) # when 'default has be renamed'
281 end
288 end
282 end
289 end
283 rescue
290 rescue
284 end
291 end
285 end
292 end
286
293
287 def assign_default_contest
294 def assign_default_contest
288 # have to catch error when migrating (because self.site is not available).
295 # have to catch error when migrating (because self.site is not available).
289 begin
296 begin
290 if self.contests.length == 0
297 if self.contests.length == 0
291 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
298 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
292 if default_contest
299 if default_contest
293 self.contests = [default_contest]
300 self.contests = [default_contest]
294 end
301 end
295 end
302 end
296 rescue
303 rescue
297 end
304 end
298 end
305 end
299
306
300 def password_required?
307 def password_required?
301 self.hashed_password.blank? || !self.password.blank?
308 self.hashed_password.blank? || !self.password.blank?
302 end
309 end
303
310
304 def self.encrypt(string,salt)
311 def self.encrypt(string,salt)
305 Digest::SHA1.hexdigest(salt + string)
312 Digest::SHA1.hexdigest(salt + string)
306 end
313 end
307
314
308 def uniqueness_of_email_from_activated_users
315 def uniqueness_of_email_from_activated_users
309 user = User.activated_users.find_by_email(self.email)
316 user = User.activated_users.find_by_email(self.email)
310 if user and (user.login != self.login)
317 if user and (user.login != self.login)
311 self.errors.add(:base,"Email has already been taken")
318 self.errors.add(:base,"Email has already been taken")
312 end
319 end
313 end
320 end
314
321
315 def enough_time_interval_between_same_email_registrations
322 def enough_time_interval_between_same_email_registrations
316 return if !self.new_record?
323 return if !self.new_record?
317 return if self.activated
324 return if self.activated
318 open_user = User.find_by_email(self.email,
325 open_user = User.find_by_email(self.email,
319 :order => 'created_at DESC')
326 :order => 'created_at DESC')
320 if open_user and open_user.created_at and
327 if open_user and open_user.created_at and
321 (open_user.created_at > Time.now.gmtime - 5.minutes)
328 (open_user.created_at > Time.now.gmtime - 5.minutes)
322 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
329 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
323 end
330 end
324 end
331 end
325
332
326 def email_validation?
333 def email_validation?
327 begin
334 begin
328 return VALIDATE_USER_EMAILS
335 return VALIDATE_USER_EMAILS
329 rescue
336 rescue
330 return false
337 return false
331 end
338 end
332 end
339 end
333 end
340 end
@@ -1,26 +1,26
1
1
2 %tr
2 %tr
3 %td{:align => "center"}
3 %td{:align => "center"}
4 - = submission_counter+1
4 + = submission.number
5 - %td{:align => "center"}
5 + %td.text-right
6 = link_to "##{submission.id}", submission_path(submission.id)
6 = link_to "##{submission.id}", submission_path(submission.id)
7 %td
7 %td
8 = l submission.submitted_at, format: :long
8 = l submission.submitted_at, format: :long
9 = "( #{time_ago_in_words(submission.submitted_at)} ago)"
9 = "( #{time_ago_in_words(submission.submitted_at)} ago)"
10 %td
10 %td
11 = submission.source_filename
11 = submission.source_filename
12 = " (#{submission.language.pretty_name}) "
12 = " (#{submission.language.pretty_name}) "
13 = link_to('[load]',{:action => 'source', :id => submission.id})
13 = link_to('[load]',{:action => 'source', :id => submission.id})
14 %td
14 %td
15 - if submission.graded_at
15 - if submission.graded_at
16 = "Graded at #{format_short_time(submission.graded_at)}."
16 = "Graded at #{format_short_time(submission.graded_at)}."
17 %br/
17 %br/
18 = "Score: #{(submission.points*100/submission.problem.full_score).to_i} " if GraderConfiguration['ui.show_score']
18 = "Score: #{(submission.points*100/submission.problem.full_score).to_i} " if GraderConfiguration['ui.show_score']
19 = " ["
19 = " ["
20 %tt
20 %tt
21 = submission.grader_comment
21 = submission.grader_comment
22 = "]"
22 = "]"
23 %td
23 %td
24 = render :partial => 'compiler_message', :locals => {:compiler_message => submission.compiler_message }
24 = render :partial => 'compiler_message', :locals => {:compiler_message => submission.compiler_message }
25 %td
25 %td
26 = link_to 'Edit', edit_submission_path(submission.id), class: 'btn btn-success'
26 = link_to 'Edit', edit_submission_path(submission.id), class: 'btn btn-success'
@@ -1,26 +1,26
1
1
2 - if submission.nil?
2 - if submission.nil?
3 = "-"
3 = "-"
4 - else
4 - else
5 - - if submission.graded_at.nil?
5 + - unless submission.graded_at
6 = t 'main.submitted_at'
6 = t 'main.submitted_at'
7 = format_short_time(submission.submitted_at.localtime)
7 = format_short_time(submission.submitted_at.localtime)
8 - else
8 - else
9 - = t 'main.graded_at'
9 + %strong= t 'main.graded_at'
10 - = "#{format_short_time(submission.graded_at.localtime)}, "
10 + = "#{format_short_time(submission.graded_at.localtime)} "
11 + %br
11 - if GraderConfiguration['ui.show_score']
12 - if GraderConfiguration['ui.show_score']
12 - = t 'main.score'
13 + %strong=t 'main.score'
13 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 = " ["
15 = " ["
15 %tt
16 %tt
16 = submission.grader_comment
17 = submission.grader_comment
17 = "]"
18 = "]"
19 + %br
20 + %strong View:
18 - if GraderConfiguration.show_grading_result
21 - if GraderConfiguration.show_grading_result
19 - = " | "
20 = link_to '[detailed result]', :action => 'result', :id => submission.id
22 = link_to '[detailed result]', :action => 'result', :id => submission.id
21 - = " | "
23 + = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
22 - = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true})
24 + = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info'
23 - = " | "
25 + = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
24 - = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id})
26 +
25 - //= " | "
26 - //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id)
@@ -1,34 +1,37
1 /- content_for :header do
1 /- content_for :header do
2 / = javascript_include_tag 'local_jquery'
2 / = javascript_include_tag 'local_jquery'
3
3
4 %h1 System configuration
4 %h1 System configuration
5
5
6 - %table.info
6 + - @group.each do |g|
7 - %tr.info-head
7 + %h2= g
8 - %th Key
8 + %table.table.table-striped
9 - %th Type
9 + %thead
10 - %th Value
10 + %th{style: 'width: 25%'} Key
11 - %th Description
11 + %th{style: 'width: 10%'}Type
12 - - @configurations.each do |conf|
12 + %th{style: 'width: 15%'} Value
13 - - @grader_configuration = conf
13 + %th Description
14 - %tr{:class => cycle("info-odd", "info-even")}
14 + - @configurations.each do |conf|
15 - %td
15 + - next if conf.key[0...(conf.key.index('.'))] != g
16 - /= in_place_editor_field :grader_configuration, :key, {}, :rows=>1
16 + - @grader_configuration = conf
17 - = @grader_configuration.key
17 + %tr
18 - %td
18 + %td
19 - /= in_place_editor_field :grader_configuration, :value_type, {}, :rows=>1
19 + /= in_place_editor_field :grader_configuration, :key, {}, :rows=>1
20 - = @grader_configuration.value_type
20 + = @grader_configuration.key
21 - %td
21 + %td
22 - = best_in_place @grader_configuration, :value, ok_button: "ok", cancel_button: "cancel"
22 + /= in_place_editor_field :grader_configuration, :value_type, {}, :rows=>1
23 - %td= conf.description
23 + = @grader_configuration.value_type
24 + %td
25 + = best_in_place @grader_configuration, :value, ok_button: "ok", cancel_button: "cancel"
26 + %td= conf.description
24
27
25 - if GraderConfiguration.config_cached?
28 - if GraderConfiguration.config_cached?
26 %br/
29 %br/
27 Your config is saved, but it does not automatically take effect.
30 Your config is saved, but it does not automatically take effect.
28 %br/
31 %br/
29 If you have one mongrel process running, you can
32 If you have one mongrel process running, you can
30 = link_to '[click]', :action => 'reload'
33 = link_to '[click]', :action => 'reload'
31 here to reload.
34 here to reload.
32 %br/
35 %br/
33 If you have more than one process running, you should restart
36 If you have more than one process running, you should restart
34 them manually.
37 them manually.
@@ -1,82 +1,82
1 - content_for :head do
1 - content_for :head do
2 <meta http-equiv ="refresh" content="60"/>
2 <meta http-equiv ="refresh" content="60"/>
3
3
4 %h1 Grader information
4 %h1 Grader information
5
5
6 %p
6 %p
7 = link_to 'Refresh', { :action => 'list' }, class: 'btn btn-info'
7 = link_to 'Refresh', { :action => 'list' }, class: 'btn btn-info'
8
8
9 .panel.panel-primary
9 .panel.panel-primary
10 .panel-heading
10 .panel-heading
11 Grader control:
11 Grader control:
12 .panel-body
12 .panel-body
13 =link_to 'Start Graders in grading env', { action: 'start_grading'}, class: 'btn btn-default', method: 'post'
13 =link_to 'Start Graders in grading env', { action: 'start_grading'}, class: 'btn btn-default', method: 'post'
14 =link_to 'Start Graders in exam env', { action: 'start_exam'}, class: 'btn btn-default', method: 'post'
14 =link_to 'Start Graders in exam env', { action: 'start_exam'}, class: 'btn btn-default', method: 'post'
15 =link_to 'Stop all running Graders', { action: 'stop_all'}, class: 'btn btn-default', method: 'post'
15 =link_to 'Stop all running Graders', { action: 'stop_all'}, class: 'btn btn-default', method: 'post'
16 =link_to 'Clear all data', { action: 'clear_all'}, class: 'btn btn-default', method: 'post'
16 =link_to 'Clear all data', { action: 'clear_all'}, class: 'btn btn-default', method: 'post'
17
17
18 .row
18 .row
19 .col-md-6
19 .col-md-6
20 - if @last_task
20 - if @last_task
21 Last task:
21 Last task:
22 = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task'
22 = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task'
23
23
24 %br/
24 %br/
25
25
26 - if @last_test_request
26 - if @last_test_request
27 Last test_request:
27 Last test_request:
28 = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest'
28 = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest'
29
29
30 %h2 Current graders
30 %h2 Current graders
31
31
32 = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes}
32 = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes}
33
33
34 %h2 Stalled graders
34 %h2 Stalled graders
35
35
36 = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes}
36 = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes}
37
37
38 %h2 Terminated graders
38 %h2 Terminated graders
39
39
40 %p= link_to 'Clear data for terminated graders', { action: 'clear_terminated'}, class: 'btn btn-default', method: 'post'
40 %p= link_to 'Clear data for terminated graders', { action: 'clear_terminated'}, class: 'btn btn-default', method: 'post'
41
41
42 = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes}
42 = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes}
43 .col-md-6
43 .col-md-6
44 %h2 Last 20 submissions
44 %h2 Last 20 submissions
45 %table.table.table-striped.table-condensed
45 %table.table.table-striped.table-condensed
46 %thead
46 %thead
47 %th ID
47 %th ID
48 %th User
48 %th User
49 %th Problem
49 %th Problem
50 %th Submitted
50 %th Submitted
51 %th Graded
51 %th Graded
52 %th Result
52 %th Result
53 %tbody
53 %tbody
54 - @submission.each do |sub|
54 - @submission.each do |sub|
55 %tr.inactive
55 %tr.inactive
56 %td= link_to sub.id, submission_path(sub.id)
56 %td= link_to sub.id, submission_path(sub.id)
57 - %td= ("" unless sub.user) || link_to sub.try(:user).try(:full_name), stat_user_path(sub.user.id)
57 + %td= ("" unless sub.user) || link_to(sub.try(:user).try(:full_name), stat_user_path(sub.user.id))
58 - %td= ("" unless sub.problem) || link_to sub.try(:problem).try(:full_name), stat_problem_path(sub.problem.id)
58 + %td= ("" unless sub.problem) || link_to(sub.try(:problem).try(:full_name), stat_problem_path(sub.problem.id))
59 %td= "#{time_ago_in_words(sub.submitted_at)} ago"
59 %td= "#{time_ago_in_words(sub.submitted_at)} ago"
60 %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
60 %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
61 %td= sub.grader_comment
61 %td= sub.grader_comment
62 %h2 Ungraded submission
62 %h2 Ungraded submission
63 %table.table.table-striped.table-condensed
63 %table.table.table-striped.table-condensed
64 %thead
64 %thead
65 %th ID
65 %th ID
66 %th User
66 %th User
67 %th Problem
67 %th Problem
68 %th Submitted
68 %th Submitted
69 %th Graded
69 %th Graded
70 %th Result
70 %th Result
71 %tbody
71 %tbody
72 - @backlog_submission.each do |sub|
72 - @backlog_submission.each do |sub|
73 %tr.inactive
73 %tr.inactive
74 %td= link_to sub.id, submission_path(sub.id)
74 %td= link_to sub.id, submission_path(sub.id)
75 - %td= ("" unless sub.user) || link_to sub.try(:user).try(:full_name), stat_user_path(sub.user.id)
75 + %td= ("" unless sub.user) || link_to( sub.try(:user).try(:full_name), stat_user_path(sub.user.id))
76 - %td= ("" unless sub.problem) || link_to sub.try(:problem).try(:full_name), stat_problem_path(sub.problem.id)
76 + %td= ("" unless sub.problem) || link_to( sub.try(:problem).try(:full_name), stat_problem_path(sub.problem.id))
77 %td= "#{time_ago_in_words(sub.submitted_at)} ago"
77 %td= "#{time_ago_in_words(sub.submitted_at)} ago"
78 %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
78 %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
79 %td= sub.grader_comment
79 %td= sub.grader_comment
80
80
81
81
82
82
@@ -1,84 +1,93
1 %header.navbar.navbar-default.navbar-fixed-top
1 %header.navbar.navbar-default.navbar-fixed-top
2 %nav
2 %nav
3 .container-fluid
3 .container-fluid
4 .navbar-header
4 .navbar-header
5 + %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} }
6 + %span.sr-only Togggle Navigation
7 + %span.icon-bar
8 + %span.icon-bar
9 + %span.icon-bar
5 %a.navbar-brand{href: main_list_path}
10 %a.navbar-brand{href: main_list_path}
6 %span.glyphicon.glyphicon-home
11 %span.glyphicon.glyphicon-home
7 MAIN
12 MAIN
8 - .collapse.navbar-collapse
13 + .collapse.navbar-collapse#navbar-collapse
9 %ul.nav.navbar-nav
14 %ul.nav.navbar-nav
15 + / submission
10 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
16 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
11 - //= add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list')
12 %li.dropdown
17 %li.dropdown
13 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
18 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
14 = "#{I18n.t 'menu.submissions'}"
19 = "#{I18n.t 'menu.submissions'}"
15 %span.caret
20 %span.caret
16 %ul.dropdown-menu
21 %ul.dropdown-menu
17 - = add_menu("View", 'main', 'submission')
22 + = add_menu("View", 'submissions', 'index')
18 = add_menu("Self Test", 'test', 'index')
23 = add_menu("Self Test", 'test', 'index')
24 + / hall of fame
19 - if GraderConfiguration['right.user_hall_of_fame']
25 - if GraderConfiguration['right.user_hall_of_fame']
20 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
26 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
21 / display MODE button (with countdown in contest mode)
27 / display MODE button (with countdown in contest mode)
22 - if GraderConfiguration.analysis_mode?
28 - if GraderConfiguration.analysis_mode?
23 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
29 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
24 - elsif GraderConfiguration.time_limit_mode?
30 - elsif GraderConfiguration.time_limit_mode?
25 - if @current_user.contest_finished?
31 - if @current_user.contest_finished?
26 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
32 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
27 - elsif !@current_user.contest_started?
33 - elsif !@current_user.contest_started?
28 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
34 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
29 - else
35 - else
30 %div.navbar-btn.btn.btn-primary#countdown asdf
36 %div.navbar-btn.btn.btn-primary#countdown asdf
31 :javascript
37 :javascript
32 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
38 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
33 / admin section
39 / admin section
34 - if (@current_user!=nil) and (session[:admin])
40 - if (@current_user!=nil) and (session[:admin])
41 + / management
35 %li.dropdown
42 %li.dropdown
36 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
43 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
37 Manage
44 Manage
38 %span.caret
45 %span.caret
39 %ul.dropdown-menu
46 %ul.dropdown-menu
40 = add_menu( 'Announcements', 'announcements', 'index')
47 = add_menu( 'Announcements', 'announcements', 'index')
41 = add_menu( 'Problems', 'problems', 'index')
48 = add_menu( 'Problems', 'problems', 'index')
42 = add_menu( 'Users', 'user_admin', 'index')
49 = add_menu( 'Users', 'user_admin', 'index')
43 = add_menu( 'Graders', 'graders', 'list')
50 = add_menu( 'Graders', 'graders', 'list')
44 = add_menu( 'Message ', 'messages', 'console')
51 = add_menu( 'Message ', 'messages', 'console')
45 %li.divider{role: 'separator'}
52 %li.divider{role: 'separator'}
46 = add_menu( 'System config', 'configurations', 'index')
53 = add_menu( 'System config', 'configurations', 'index')
47 %li.divider{role: 'separator'}
54 %li.divider{role: 'separator'}
48 = add_menu( 'Sites', 'sites', 'index')
55 = add_menu( 'Sites', 'sites', 'index')
49 = add_menu( 'Contests', 'contest_management', 'index')
56 = add_menu( 'Contests', 'contest_management', 'index')
57 + / report
50 %li.dropdown
58 %li.dropdown
51 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
59 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
52 Report
60 Report
53 %span.caret
61 %span.caret
54 %ul.dropdown-menu
62 %ul.dropdown-menu
55 - = add_menu( 'Results', 'report', 'current_score')
63 + = add_menu( 'Current Score', 'report', 'current_score')
64 + = add_menu( 'Score Report', 'report', 'max_score')
56 = add_menu( 'Report', 'report', 'multiple_login')
65 = add_menu( 'Report', 'report', 'multiple_login')
57 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
66 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
58 =link_to "#{ungraded} backlogs!",
67 =link_to "#{ungraded} backlogs!",
59 grader_list_path,
68 grader_list_path,
60 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
69 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
61
70
62 %ul.nav.navbar-nav.navbar-right
71 %ul.nav.navbar-nav.navbar-right
63 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
72 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
64 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
73 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
65 - if GraderConfiguration['system.user_setting_enabled']
74 - if GraderConfiguration['system.user_setting_enabled']
66 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
75 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
67 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
76 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
68
77
69 /
78 /
70 - if (@current_user!=nil) and (session[:admin])
79 - if (@current_user!=nil) and (session[:admin])
71 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
80 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
72 .container-fluid
81 .container-fluid
73 .collapse.navbar-collapse
82 .collapse.navbar-collapse
74 %ul.nav.navbar-nav
83 %ul.nav.navbar-nav
75 = add_menu( '[Announcements]', 'announcements', 'index')
84 = add_menu( '[Announcements]', 'announcements', 'index')
76 = add_menu( '[Msg console]', 'messages', 'console')
85 = add_menu( '[Msg console]', 'messages', 'console')
77 = add_menu( '[Problems]', 'problems', 'index')
86 = add_menu( '[Problems]', 'problems', 'index')
78 = add_menu( '[Users]', 'user_admin', 'index')
87 = add_menu( '[Users]', 'user_admin', 'index')
79 = add_menu( '[Results]', 'user_admin', 'user_stat')
88 = add_menu( '[Results]', 'user_admin', 'user_stat')
80 = add_menu( '[Report]', 'report', 'multiple_login')
89 = add_menu( '[Report]', 'report', 'multiple_login')
81 = add_menu( '[Graders]', 'graders', 'list')
90 = add_menu( '[Graders]', 'graders', 'list')
82 = add_menu( '[Contests]', 'contest_management', 'index')
91 = add_menu( '[Contests]', 'contest_management', 'index')
83 = add_menu( '[Sites]', 'sites', 'index')
92 = add_menu( '[Sites]', 'sites', 'index')
84 = add_menu( '[System config]', 'configurations', 'index')
93 = add_menu( '[System config]', 'configurations', 'index')
@@ -1,15 +1,16
1 <!DOCTYPE html>
1 <!DOCTYPE html>
2 %html
2 %html
3 %head
3 %head
4 %title= GraderConfiguration['contest.name']
4 %title= GraderConfiguration['contest.name']
5 = stylesheet_link_tag "application", params[:controller], :media => "all"
5 = stylesheet_link_tag "application", params[:controller], :media => "all"
6 = javascript_include_tag "application", params[:controller]
6 = javascript_include_tag "application", params[:controller]
7 = csrf_meta_tags
7 = csrf_meta_tags
8 = content_for :header
8 = content_for :header
9 = yield :head
9 = yield :head
10
10
11 %body
11 %body
12 = render 'layouts/header'
12 = render 'layouts/header'
13
13
14 - = content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil
14 + /= content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil
15 + = flash_messages
15 = yield
16 = yield
@@ -1,39 +1,39
1 %b= GraderConfiguration['ui.front.welcome_message']
1 %b= GraderConfiguration['ui.front.welcome_message']
2 %br/
2 %br/
3
3
4 - if !@hidelogin
4 - if !@hidelogin
5 =t 'login.message'
5 =t 'login.message'
6 %br/
6 %br/
7 %br/
7 %br/
8
8
9 - if flash[:notice]
9 - if flash[:notice]
10 %hr/
10 %hr/
11 %b= flash[:notice]
11 %b= flash[:notice]
12 %hr/
12 %hr/
13
13
14 %div{ :style => "border: solid 1px gray; padding: 4px; background: #eeeeff;"}
14 %div{ :style => "border: solid 1px gray; padding: 4px; background: #eeeeff;"}
15 - = form_tag :controller => 'login', :action => 'login' do
15 + = form_tag login_login_path do
16 %table
16 %table
17 %tr
17 %tr
18 %td{:align => "right"}
18 %td{:align => "right"}
19 ="#{t 'login_label'}:"
19 ="#{t 'login_label'}:"
20 %td= text_field_tag 'login'
20 %td= text_field_tag 'login'
21 %tr
21 %tr
22 %td{:align => "right"}
22 %td{:align => "right"}
23 ="#{t 'password_label'}:"
23 ="#{t 'password_label'}:"
24 %td= password_field_tag
24 %td= password_field_tag
25 - unless GraderConfiguration['right.bypass_agreement']
25 - unless GraderConfiguration['right.bypass_agreement']
26 %tr
26 %tr
27 %td{:align => "right"}= check_box_tag 'accept_agree'
27 %td{:align => "right"}= check_box_tag 'accept_agree'
28 %td ยอมรับข้อตกลงการใช้งาน
28 %td ยอมรับข้อตกลงการใช้งาน
29
29
30 = submit_tag t('login.login_submit')
30 = submit_tag t('login.login_submit')
31 %br/
31 %br/
32
32
33 - if GraderConfiguration['system.online_registration']
33 - if GraderConfiguration['system.online_registration']
34 =t 'login.participation'
34 =t 'login.participation'
35 %b
35 %b
36 = "#{t 'login.please'} "
36 = "#{t 'login.please'} "
37 = link_to "#{t 'login.register'}", :controller => :users, :action => :new
37 = link_to "#{t 'login.register'}", :controller => :users, :action => :new
38 %br/
38 %br/
39 = link_to "#{t 'login.forget_password'}", :controller => :users, :action => :forget
39 = link_to "#{t 'login.forget_password'}", :controller => :users, :action => :forget
@@ -1,18 +1,22
1 %tr
1 %tr
2 %td
2 %td
3 - = "#{problem.name}"
3 + - if @current_user and @current_user.admin?
4 + = link_to problem.name, stat_problem_path(problem)
5 + - else
6 + = "#{problem.name}"
4 %td
7 %td
5 = "#{problem.full_name}"
8 = "#{problem.full_name}"
9 +
6 %br
10 %br
7 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
11 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
8 %td
12 %td
9 = @prob_submissions[problem.id][:count]
13 = @prob_submissions[problem.id][:count]
10 = link_to "[subs]", main_submission_path(problem.id)
14 = link_to "[subs]", main_submission_path(problem.id)
11 %td
15 %td
12 = render :partial => 'submission_short',
16 = render :partial => 'submission_short',
13 :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name, :problem_id => problem.id }
17 :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name, :problem_id => problem.id }
14 %td
18 %td
15 - if @prob_submissions[problem.id][:submission]
19 - if @prob_submissions[problem.id][:submission]
16 = link_to 'Edit', edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success'
20 = link_to 'Edit', edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success'
17 - else
21 - else
18 = link_to 'New', direct_edit_problem_submissions_path(problem.id), class: 'btn btn-success'
22 = link_to 'New', direct_edit_problem_submissions_path(problem.id), class: 'btn btn-success'
@@ -1,20 +1,20
1 = form_tag({:action => 'submit'}, :multipart => true, class: 'form') do
1 = form_tag({:action => 'submit'}, :multipart => true, class: 'form') do
2 - if @submission and @submission.errors.any?
2 - if @submission and @submission.errors.any?
3 #error_explanation
3 #error_explanation
4 .alert.alert-danger
4 .alert.alert-danger
5 %h3= "#{pluralize(@submission.errors.count, "error")} prohibited this user from being saved:"
5 %h3= "#{pluralize(@submission.errors.count, "error")} prohibited this user from being saved:"
6 %ul
6 %ul
7 - @submission.errors.full_messages.each do |msg|
7 - @submission.errors.full_messages.each do |msg|
8 %li= msg
8 %li= msg
9 .form-group
9 .form-group
10 = label_tag :submission, 'Problem:'
10 = label_tag :submission, 'Problem:'
11 - = select 'submission', 'problem_id', [[(t 'main.specified_in_header'),'-1']] + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]}, {:selected => '-1'}, { class: 'select2 form-control' }
11 + = select 'submission', 'problem_id', [[(t 'main.specified_in_header'),'-1']] + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]}, {:selected => '-1'}, { class: 'select2 form-control', style: "width: 100%" }
12 .form-group
12 .form-group
13 = label_tag :file, 'File:'
13 = label_tag :file, 'File:'
14 .input-group
14 .input-group
15 %span.input-group-btn
15 %span.input-group-btn
16 %span.btn.btn-default.btn-file
16 %span.btn.btn-default.btn-file
17 Browse
17 Browse
18 = file_field_tag 'file'
18 = file_field_tag 'file'
19 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
19 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
20 = submit_tag 'Submit', class: 'btn btn-default'
20 = submit_tag 'Submit', class: 'btn btn-default'
@@ -1,26 +1,29
1
1
2 - if submission.nil?
2 - if submission.nil?
3 = "-"
3 = "-"
4 - else
4 - else
5 - unless submission.graded_at
5 - unless submission.graded_at
6 - =t 'main.submitted_at'
6 + = t 'main.submitted_at'
7 = format_short_time(submission.submitted_at.localtime)
7 = format_short_time(submission.submitted_at.localtime)
8 - else
8 - else
9 %strong= t 'main.graded_at'
9 %strong= t 'main.graded_at'
10 = "#{format_short_time(submission.graded_at.localtime)} "
10 = "#{format_short_time(submission.graded_at.localtime)} "
11 %br
11 %br
12 - if GraderConfiguration['ui.show_score']
12 - if GraderConfiguration['ui.show_score']
13 - =t 'main.score'
13 + %strong=t 'main.score'
14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
15 = " ["
15 = " ["
16 %tt
16 %tt
17 = submission.grader_comment
17 = submission.grader_comment
18 = "]"
18 = "]"
19 %br
19 %br
20 %strong View:
20 %strong View:
21 - if GraderConfiguration.show_grading_result
21 - if GraderConfiguration.show_grading_result
22 = link_to '[detailed result]', :action => 'result', :id => submission.id
22 = 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'}
23 + /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
24 + = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'}
24 = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info'
25 = link_to "#{t 'main.src_link'}",{:action => 'source', :id => 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'
26 = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
27 + - if GraderConfiguration.show_testcase
28 + = link_to "testcases", show_problem_testcases_path(problem_id), class: 'btn btn-xs btn-info'
26
29
@@ -1,52 +1,64
1 - content_for :head do
1 - content_for :head do
2 = javascript_include_tag "announcement_refresh"
2 = javascript_include_tag "announcement_refresh"
3
3
4 = user_title_bar(@user)
4 = user_title_bar(@user)
5
5
6 - if (GraderConfiguration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true)
6 - if (GraderConfiguration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true)
7 %p=t 'main.start_soon'
7 %p=t 'main.start_soon'
8
8
9 .row
9 .row
10 .col-md-7
10 .col-md-7
11 - if GraderConfiguration.show_submitbox_to?(@user)
11 - if GraderConfiguration.show_submitbox_to?(@user)
12 .panel.panel-primary
12 .panel.panel-primary
13 .panel-heading
13 .panel-heading
14 Submission
14 Submission
15 .panel-body
15 .panel-body
16 = render :partial => 'submission_box'
16 = render :partial => 'submission_box'
17 - if GraderConfiguration.show_tasks_to?(@user)
17 - if GraderConfiguration.show_tasks_to?(@user)
18 - if not GraderConfiguration.multicontests?
18 - if not GraderConfiguration.multicontests?
19 %table.table.table-striped.table-condensed
19 %table.table.table-striped.table-condensed
20 %thead
20 %thead
21 %tr
21 %tr
22 %th Task name
22 %th Task name
23 %th Full name
23 %th Full name
24 %th # of sub(s)
24 %th # of sub(s)
25 %th Results
25 %th Results
26 %th
26 %th
27 %tbody
27 %tbody
28 = render :partial => 'problem', :collection => @problems
28 = render :partial => 'problem', :collection => @problems
29 - else
29 - else
30 - @contest_problems.each do |cp|
30 - @contest_problems.each do |cp|
31 - if cp[:problems].length > 0
31 - if cp[:problems].length > 0
32 %h2{:class =>'contest-title'}
32 %h2{:class =>'contest-title'}
33 = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}"
33 = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}"
34 %table.info
34 %table.info
35 %tr.info-head
35 %tr.info-head
36 %th Task name
36 %th Task name
37 %th Full name
37 %th Full name
38 %th # of sub(s)
38 %th # of sub(s)
39 %th Results
39 %th Results
40 %th
40 %th
41 = render :partial => 'problem', :collection => cp[:problems]
41 = render :partial => 'problem', :collection => cp[:problems]
42 .col-md-5
42 .col-md-5
43 .panel.panel-info
43 .panel.panel-info
44 .panel-heading
44 .panel-heading
45 Announcement
45 Announcement
46 + = link_to 'Manage', announcements_path, class: 'btn btn-xs btn-default'
46 %ul.list-group
47 %ul.list-group
47 = render :partial => 'announcement', :collection => @announcements
48 = render :partial => 'announcement', :collection => @announcements
48
49
49 %script{:type => 'text/javascript'}
50 %script{:type => 'text/javascript'}
50 = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';"
51 = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';"
51 Announcement.registerRefreshEventTimer();
52 Announcement.registerRefreshEventTimer();
52
53
54 + .modal.fade#compiler{tabindex: -1,role: 'dialog'}
55 + .modal-dialog.modal-lg{role:'document'}
56 + .modal-content
57 + .modal-header
58 + %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
59 + %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
60 + %h4 Compiler message
61 + .modal-body
62 + %pre#compiler_msg
63 + .modal-footer
64 + %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
@@ -1,50 +1,54
1 - content_for :head do
1 - content_for :head do
2 = stylesheet_link_tag 'problems'
2 = stylesheet_link_tag 'problems'
3 %h1 Listing problems
3 %h1 Listing problems
4 %p
4 %p
5 = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
5 = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
6 = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
6 = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
7 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm'
7 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
10 .submitbox
10 .submitbox
11 = form_tag :action => 'quick_create' do
11 = form_tag :action => 'quick_create' do
12 %b Quick New:
12 %b Quick New:
13 %label{:for => "problem_name"} Name
13 %label{:for => "problem_name"} Name
14 = text_field 'problem', 'name'
14 = text_field 'problem', 'name'
15 |
15 |
16 %label{:for => "problem_full_name"} Full name
16 %label{:for => "problem_full_name"} Full name
17 = text_field 'problem', 'full_name'
17 = text_field 'problem', 'full_name'
18 = submit_tag "Create"
18 = submit_tag "Create"
19 %table.table.table-condense.table-hover
19 %table.table.table-condense.table-hover
20 %thead
20 %thead
21 %th Name
21 %th Name
22 %th Full name
22 %th Full name
23 %th.text-right Full score
23 %th.text-right Full score
24 %th Date added
24 %th Date added
25 %th.text-center
25 %th.text-center
26 Avail?
26 Avail?
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
28 %th.text-center
28 %th.text-center
29 + View Data?
30 + %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
31 + %th.text-center
29 Test?
32 Test?
30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
31 - if GraderConfiguration.multicontests?
34 - if GraderConfiguration.multicontests?
32 %th Contests
35 %th Contests
33 - for problem in @problems
36 - for problem in @problems
34 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
37 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
35 - @problem=problem
38 - @problem=problem
36 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
39 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
37 %td= problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
40 %td= problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
38 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
41 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
39 %td= problem.date_added
42 %td= problem.date_added
40 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
43 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
44 + %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
41 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
45 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
42 - if GraderConfiguration.multicontests?
46 - if GraderConfiguration.multicontests?
43 %td
47 %td
44 = problem.contests.collect { |c| c.name }.join(', ')
48 = problem.contests.collect { |c| c.name }.join(', ')
45 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
49 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
46 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
50 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
47 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
51 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
48 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
52 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
49 %br/
53 %br/
50 = link_to '[New problem]', :action => 'new'
54 = link_to '[New problem]', :action => 'new'
@@ -1,24 +1,24
1 <% for column in Problem.content_columns %>
1 <% for column in Problem.content_columns %>
2 <p>
2 <p>
3 <b><%= column.human_name %>:</b>
3 <b><%= column.human_name %>:</b>
4 <%=h @problem.send(column.name) %>
4 <%=h @problem.send(column.name) %>
5 </p>
5 </p>
6 <% end %>
6 <% end %>
7
7
8 <p>
8 <p>
9 <b>Description:</b><br/>
9 <b>Description:</b><br/>
10 <% if @problem.description!=nil %>
10 <% if @problem.description!=nil %>
11 <% if @problem.description.markdowned %>
11 <% if @problem.description.markdowned %>
12 <%= markdown(@problem.description.body) %>
12 <%= markdown(@problem.description.body) %>
13 <% else %>
13 <% else %>
14 <pre>
14 <pre>
15 <%= @problem.description.body %>
15 <%= @problem.description.body %>
16 </pre>
16 </pre>
17 <% end %>
17 <% end %>
18 <% else %>
18 <% else %>
19 (not available)
19 (not available)
20 <% end %>
20 <% end %>
21 </p>
21 </p>
22
22
23 <%= link_to 'Edit', :action => 'edit', :id => @problem %> |
23 <%= link_to 'Edit', :action => 'edit', :id => @problem %> |
24 - <%= link_to 'Back', :action => 'list' %>
24 + <%= link_to 'Back', problems_path %>
@@ -1,34 +1,34
1 %table.table.sortable.table-striped.table-bordered.table-condensed
1 %table.table.sortable.table-striped.table-bordered.table-condensed
2 %thead
2 %thead
3 %tr
3 %tr
4 %th Login
4 %th Login
5 %th Name
5 %th Name
6 - %th Activated?
6 + / %th Activated?
7 - %th Logged_in
7 + / %th Logged_in
8 - %th Contest(s)
8 + / %th Contest(s)
9 %th Remark
9 %th Remark
10 - @problems.each do |p|
10 - @problems.each do |p|
11 - %th.text-right= p.name
11 + %th.text-right= p.name.gsub('_',' ')
12 %th.text-right Total
12 %th.text-right Total
13 %th.text-right Passed
13 %th.text-right Passed
14 %tbody
14 %tbody
15 - @scorearray.each do |sc|
15 - @scorearray.each do |sc|
16 %tr
16 %tr
17 - total,num_passed = 0,0
17 - total,num_passed = 0,0
18 - sc.each_index do |i|
18 - sc.each_index do |i|
19 - if i == 0
19 - if i == 0
20 - %td= link_to sc[i].login, controller: 'users', action: 'profile', id: sc[i]
20 + %td= link_to sc[i].login, stat_user_path(sc[i])
21 %td= sc[i].full_name
21 %td= sc[i].full_name
22 - %td= sc[i].activated
22 + / %td= sc[i].activated
23 - %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
23 + / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
24 - %td= sc[i].contests.collect {|c| c.name}.join(', ')
24 + / %td= sc[i].contests.collect {|c| c.name}.join(', ')
25 %td= sc[i].remark
25 %td= sc[i].remark
26 - else
26 - else
27 %td.text-right= sc[i][0]
27 %td.text-right= sc[i][0]
28 - total += sc[i][0]
28 - total += sc[i][0]
29 - num_passed += 1 if sc[i][1]
29 - num_passed += 1 if sc[i][1]
30 %td.text-right= total
30 %td.text-right= total
31 %td.text-right= num_passed
31 %td.text-right= num_passed
32
32
33 :javascript
33 :javascript
34 $.bootstrapSortable(true,'reversed')
34 $.bootstrapSortable(true,'reversed')
@@ -1,136 +1,136
1 :css
1 :css
2 .hof_user { color: orangered; font-style: italic; }
2 .hof_user { color: orangered; font-style: italic; }
3 .hof_language { color: green; font-style: italic; }
3 .hof_language { color: green; font-style: italic; }
4 .hof_value { color: deeppink;font-style: italic; }
4 .hof_value { color: deeppink;font-style: italic; }
5 .info_param { font-weight: bold;text-align: right; }
5 .info_param { font-weight: bold;text-align: right; }
6 .tooltip {
6 .tooltip {
7 font-family: Verdana,sans-serif;
7 font-family: Verdana,sans-serif;
8 font-weight: normal;
8 font-weight: normal;
9 text-align: left;
9 text-align: left;
10 font-size: 1.0em;
10 font-size: 1.0em;
11 color: black;
11 color: black;
12 line-height: 1.1;
12 line-height: 1.1;
13 display: none;
13 display: none;
14 min-width: 20em;
14 min-width: 20em;
15 position: absolute;
15 position: absolute;
16 left: 25px;
16 left: 25px;
17 bottom: 5px;
17 bottom: 5px;
18 border: 1px solid;
18 border: 1px solid;
19 padding: 5px;
19 padding: 5px;
20 background-color: #FFF;
20 background-color: #FFF;
21 word-wrap: break-word;
21 word-wrap: break-word;
22 z-index: 9999;
22 z-index: 9999;
23 overflow: auto;
23 overflow: auto;
24 }
24 }
25
25
26
26
27 .container
27 .container
28 .row
28 .row
29 .col-md-4
29 .col-md-4
30 %h2 Overall Stat
30 %h2 Overall Stat
31 %table.table.table-hover
31 %table.table.table-hover
32 %thead
32 %thead
33 %tr
33 %tr
34 %th
34 %th
35 %th
35 %th
36 %tbody
36 %tbody
37 %tr
37 %tr
38 %td.info_param Submissions
38 %td.info_param Submissions
39 %td= @summary[:count]
39 %td= @summary[:count]
40 %tr
40 %tr
41 %td.info_param Solved/Attempted User
41 %td.info_param Solved/Attempted User
42 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
42 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
43 - if @best
43 - if @best
44 %tr
44 %tr
45 %td.info_param Best Runtime
45 %td.info_param Best Runtime
46 %td
46 %td
47 - by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
47 + by #{link_to @best[:runtime][:user], stat_user_path(@best[:runtime][:user_id])}
48 %br
48 %br
49 using <span class="text-success">#{@best[:runtime][:lang]}</span>
49 using <span class="text-success">#{@best[:runtime][:lang]}</span>
50 %br
50 %br
51 with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
51 with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
52 %br
52 %br
53 at submission
53 at submission
54 = link_to "#" + @best[:runtime][:sub_id].to_s, submission_path(@best[:runtime][:sub_id])
54 = link_to "#" + @best[:runtime][:sub_id].to_s, submission_path(@best[:runtime][:sub_id])
55
55
56 %tr
56 %tr
57 %td.info_param
57 %td.info_param
58 Best Memory Usage
58 Best Memory Usage
59 %sup{ id: "xmem_remark",
59 %sup{ id: "xmem_remark",
60 style: "position:relative; color: blue;",
60 style: "position:relative; color: blue;",
61 data: {toggle: 'tooltip', placement: 'top', animation: 'false', delay: 20},
61 data: {toggle: 'tooltip', placement: 'top', animation: 'false', delay: 20},
62 title: "This counts only for submission with 100% score. Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)"}
62 title: "This counts only for submission with 100% score. Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)"}
63 [?]
63 [?]
64 %td
64 %td
65 - by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
65 + by #{link_to @best[:memory][:user], stat_user_path(@best[:memory][:user_id])}
66 %br
66 %br
67 using <span class="text-success">#{@best[:memory][:lang]}</span>
67 using <span class="text-success">#{@best[:memory][:lang]}</span>
68 %br
68 %br
69 with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
69 with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
70 %br
70 %br
71 at submission
71 at submission
72 = link_to "#" + @best[:memory][:sub_id].to_s, submission_path(@best[:memory][:sub_id])
72 = link_to "#" + @best[:memory][:sub_id].to_s, submission_path(@best[:memory][:sub_id])
73
73
74 %tr
74 %tr
75 %td.info_param Shortest Code
75 %td.info_param Shortest Code
76 %td
76 %td
77 - by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]}
77 + by #{link_to @best[:length][:user], stat_user_path(@best[:length][:user_id])}
78 %br
78 %br
79 using <span class="text-success">#{@best[:length][:lang]}</span>
79 using <span class="text-success">#{@best[:length][:lang]}</span>
80 %br
80 %br
81 with <span class="text-success">#{@best[:length][:value]} bytes</span>
81 with <span class="text-success">#{@best[:length][:value]} bytes</span>
82 %br
82 %br
83 at submission
83 at submission
84 = link_to "#" + @best[:length][:sub_id].to_s, submission_path(@best[:length][:sub_id])
84 = link_to "#" + @best[:length][:sub_id].to_s, submission_path(@best[:length][:sub_id])
85
85
86 %tr
86 %tr
87 %td.info_param First solver
87 %td.info_param First solver
88 %td
88 %td
89 - if @best[:first][:user] != '(NULL)'
89 - if @best[:first][:user] != '(NULL)'
90 - #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver
90 + #{link_to @best[:first][:user], stat_user_path(@best[:first][:user_id])} is the first solver
91 %br
91 %br
92 using <span class="text-success">#{@best[:first][:lang]}</span>
92 using <span class="text-success">#{@best[:first][:lang]}</span>
93 %br
93 %br
94 on <span class="text-success">#{@best[:first][:value]}</span>
94 on <span class="text-success">#{@best[:first][:value]}</span>
95 %br
95 %br
96 at submission
96 at submission
97 = link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
97 = link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
98 - else
98 - else
99 no first solver
99 no first solver
100 .col-md-8
100 .col-md-8
101 - if @best
101 - if @best
102 %h2 By Language
102 %h2 By Language
103 %table.table.table-hover
103 %table.table.table-hover
104 %thead
104 %thead
105 %tr
105 %tr
106 %th Language
106 %th Language
107 %th Best runtime (ms)
107 %th Best runtime (ms)
108 %th Best memory (kbytes)
108 %th Best memory (kbytes)
109 %th Shortest Code (bytes)
109 %th Shortest Code (bytes)
110 %th First solver
110 %th First solver
111 %tbody
111 %tbody
112 - @by_lang.each do |lang,value|
112 - @by_lang.each do |lang,value|
113 %tr
113 %tr
114 %td= lang
114 %td= lang
115 %td
115 %td
116 - = link_to value[:runtime][:user], controller: 'users', action: 'profile', id: value[:runtime][:user_id]
116 + = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
117 %br
117 %br
118 = "#{(value[:runtime][:value] * 1000).to_i} @"
118 = "#{(value[:runtime][:value] * 1000).to_i} @"
119 = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
119 = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
120 %td
120 %td
121 - = link_to value[:memory][:user], controller: 'users', action: 'profile', id: value[:memory][:user_id]
121 + = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
122 %br
122 %br
123 = "#{number_with_delimiter(value[:memory][:value])} @"
123 = "#{number_with_delimiter(value[:memory][:value])} @"
124 = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
124 = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
125 %td
125 %td
126 - = link_to value[:length][:user], controller: 'users', action: 'profile', id: value[:length][:user_id]
126 + = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
127 %br
127 %br
128 = "#{value[:length][:value]} @"
128 = "#{value[:length][:value]} @"
129 = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
129 = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
130 %td
130 %td
131 - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
131 - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
132 - = link_to value[:first][:user], controller: 'users', action: 'profile', id: value[:first][:user_id]
132 + = link_to value[:first][:user], stat_user_path(value[:first][:user_id])
133 %br
133 %br
134 = "#{value[:first][:value]} @"
134 = "#{value[:first][:value]} @"
135 = link_to "#" + value[:first][:sub_id].to_s, submission_path( value[:first][:sub_id])
135 = link_to "#" + value[:first][:sub_id].to_s, submission_path( value[:first][:sub_id])
136
136
@@ -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', nil, 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', nil, 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', true
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'
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,139 +1,140
1 %h2 Live submit
1 %h2 Live submit
2 %br
2 %br
3
3
4 - %textarea#text_haha{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 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
14 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15
15
16 = hidden_field_tag 'editor_text', @source
16 = hidden_field_tag 'editor_text', @source
17 = hidden_field_tag 'submission[problem_id]', @problem.id
17 = hidden_field_tag 'submission[problem_id]', @problem.id
18 .form-group
18 .form-group
19 = label_tag "Task:"
19 = label_tag "Task:"
20 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
20 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
21
21
22 .form-group
22 .form-group
23 = label_tag 'Language'
23 = label_tag 'Language'
24 = 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"
24 = 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 .form-group
25 .form-group
26 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
26 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
27 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
27 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
28 .panel.panel-info
28 .panel.panel-info
29 .panel-heading
29 .panel-heading
30 Latest Submission Status
30 Latest Submission Status
31 + = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
31 .panel-body
32 .panel-body
32 - if @submission
33 - if @submission
33 = render :partial => 'submission_short',
34 = render :partial => 'submission_short',
34 - :locals => {:submission => @submission, :problem_name => @problem.name }
35 + :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
35 .row
36 .row
36 .col-md-12
37 .col-md-12
37 %h2 Console
38 %h2 Console
38 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
39 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
39
40
40 :javascript
41 :javascript
41 $(document).ready(function() {
42 $(document).ready(function() {
42 e = ace.edit("editor")
43 e = ace.edit("editor")
43 - e.setValue($("#text_haha").val());
44 + e.setValue($("#text_sourcecode").val());
44 e.gotoLine(1);
45 e.gotoLine(1);
45 $("#language_id").trigger('change');
46 $("#language_id").trigger('change');
46 brython();
47 brython();
47 });
48 });
48
49
49
50
50 %script#__main__{type:'text/python3'}
51 %script#__main__{type:'text/python3'}
51 :plain
52 :plain
52 import sys
53 import sys
53 import traceback
54 import traceback
54
55
55 from browser import document as doc
56 from browser import document as doc
56 from browser import window, alert, console
57 from browser import window, alert, console
57
58
58 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
59 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
59 for supporting Python development. See www.python.org for more information."""
60 for supporting Python development. See www.python.org for more information."""
60
61
61 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
62 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
62 All Rights Reserved.
63 All Rights Reserved.
63
64
64 Copyright (c) 2001-2013 Python Software Foundation.
65 Copyright (c) 2001-2013 Python Software Foundation.
65 All Rights Reserved.
66 All Rights Reserved.
66
67
67 Copyright (c) 2000 BeOpen.com.
68 Copyright (c) 2000 BeOpen.com.
68 All Rights Reserved.
69 All Rights Reserved.
69
70
70 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
71 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
71 All Rights Reserved.
72 All Rights Reserved.
72
73
73 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
74 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
74 All Rights Reserved."""
75 All Rights Reserved."""
75
76
76 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
77 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
77 All rights reserved.
78 All rights reserved.
78
79
79 Redistribution and use in source and binary forms, with or without
80 Redistribution and use in source and binary forms, with or without
80 modification, are permitted provided that the following conditions are met:
81 modification, are permitted provided that the following conditions are met:
81
82
82 Redistributions of source code must retain the above copyright notice, this
83 Redistributions of source code must retain the above copyright notice, this
83 list of conditions and the following disclaimer. Redistributions in binary
84 list of conditions and the following disclaimer. Redistributions in binary
84 form must reproduce the above copyright notice, this list of conditions and
85 form must reproduce the above copyright notice, this list of conditions and
85 the following disclaimer in the documentation and/or other materials provided
86 the following disclaimer in the documentation and/or other materials provided
86 with the distribution.
87 with the distribution.
87 Neither the name of the <ORGANIZATION> nor the names of its contributors may
88 Neither the name of the <ORGANIZATION> nor the names of its contributors may
88 be used to endorse or promote products derived from this software without
89 be used to endorse or promote products derived from this software without
89 specific prior written permission.
90 specific prior written permission.
90
91
91 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
92 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
92 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
94 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
94 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
95 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
95 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
96 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
96 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
97 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
97 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
98 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
98 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
99 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
99 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
100 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
100 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
101 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
101 POSSIBILITY OF SUCH DAMAGE.
102 POSSIBILITY OF SUCH DAMAGE.
102 """
103 """
103
104
104 def credits():
105 def credits():
105 print(_credits)
106 print(_credits)
106 credits.__repr__ = lambda:_credits
107 credits.__repr__ = lambda:_credits
107
108
108 def copyright():
109 def copyright():
109 print(_copyright)
110 print(_copyright)
110 copyright.__repr__ = lambda:_copyright
111 copyright.__repr__ = lambda:_copyright
111
112
112 def license():
113 def license():
113 print(_license)
114 print(_license)
114 license.__repr__ = lambda:_license
115 license.__repr__ = lambda:_license
115
116
116 def write(data):
117 def write(data):
117 doc['console'].value += str(data)
118 doc['console'].value += str(data)
118
119
119
120
120 sys.stdout.write = sys.stderr.write = write
121 sys.stdout.write = sys.stderr.write = write
121 history = []
122 history = []
122 current = 0
123 current = 0
123 _status = "main" # or "block" if typing inside a block
124 _status = "main" # or "block" if typing inside a block
124
125
125 # execution namespace
126 # execution namespace
126 editor_ns = {'credits':credits,
127 editor_ns = {'credits':credits,
127 'copyright':copyright,
128 'copyright':copyright,
128 'license':license,
129 'license':license,
129 '__name__':'__main__'}
130 '__name__':'__main__'}
130
131
131 def cursorToEnd(*args):
132 def cursorToEnd(*args):
132 pos = len(doc['console'].value)
133 pos = len(doc['console'].value)
133 doc['console'].setSelectionRange(pos, pos)
134 doc['console'].setSelectionRange(pos, pos)
134 doc['console'].scrollTop = doc['console'].scrollHeight
135 doc['console'].scrollTop = doc['console'].scrollHeight
135
136
136 def get_col(area):
137 def get_col(area):
137 # returns the column num of cursor
138 # returns the column num of cursor
138 sel = doc['console'].selectionStart
139 sel = doc['console'].selectionStart
139 lines = doc['console'].value.split('\n')
140 lines = doc['console'].value.split('\n')
@@ -1,2 +1,2
1 - :javascript
1 + :plain
2 $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}")
2 $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}")
@@ -1,29 +1,29
1 .panel.panel-info
1 .panel.panel-info
2 .panel-heading
2 .panel-heading
3 Select Problems
3 Select Problems
4 .panel-body
4 .panel-body
5 .form-inline
5 .form-inline
6 = select 'submission',
6 = select 'submission',
7 'problem_id',
7 'problem_id',
8 @problems.collect {|p| ["[#{p.name}] #{p.full_name}", problem_submissions_url(p.id)]},
8 @problems.collect {|p| ["[#{p.name}] #{p.full_name}", problem_submissions_url(p.id)]},
9 { selected: (@problem ? problem_submissions_url(@problem) : -1) },
9 { selected: (@problem ? problem_submissions_url(@problem) : -1) },
10 { class: 'select2 form-control'}
10 { class: 'select2 form-control'}
11 %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: '#submission_problem_id'}} Go
11 %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: '#submission_problem_id'}} Go
12
12
13 - if @problem!=nil
13 - if @problem!=nil
14 %h2= "Task: #{@problem.full_name} (#{@problem.name})"
14 %h2= "Task: #{@problem.full_name} (#{@problem.name})"
15
15
16 - if @submissions!=nil
16 - if @submissions!=nil
17 - if @submissions.length>0
17 - if @submissions.length>0
18 %table.table
18 %table.table
19 %thead
19 %thead
20 %th No.
20 %th No.
21 - %th #
21 + %th.text-right #
22 %th At
22 %th At
23 %th Source
23 %th Source
24 %th Result
24 %th Result
25 %th{:width => "300px"} Compiler message
25 %th{:width => "300px"} Compiler message
26 %th
26 %th
27 = render :partial => 'submission', :collection => @submissions
27 = render :partial => 'submission', :collection => @submissions
28 - else
28 - else
29 No submission
29 No submission
@@ -1,89 +1,112
1 %h1= "Submission: #{@submission.id}"
1 %h1= "Submission: #{@submission.id}"
2
2
3 %textarea#data{style: "display:none;"}
3 %textarea#data{style: "display:none;"}
4 :preserve
4 :preserve
5 #{@submission.source}
5 #{@submission.source}
6
6
7 //%div.highlight{:style => "border: 1px solid black;"}
7 //%div.highlight{:style => "border: 1px solid black;"}
8 //=@formatted_code.html_safe
8 //=@formatted_code.html_safe
9 +
10 +
9 .containter
11 .containter
10 .row
12 .row
11 .col-md-7
13 .col-md-7
12 %h2 Source Code
14 %h2 Source Code
13 .col-md-5
15 .col-md-5
14 %h2 Stat
16 %h2 Stat
15 .row
17 .row
16 .col-md-7
18 .col-md-7
17 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
18 :javascript
20 :javascript
19 e = ace.edit("editor")
21 e = ace.edit("editor")
20 e.setOptions({ maxLines: Infinity })
22 e.setOptions({ maxLines: Infinity })
21 e.setValue($("#data").text())
23 e.setValue($("#data").text())
22 e.gotoLine(1)
24 e.gotoLine(1)
23 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
24 e.setReadOnly(true)
26 e.setReadOnly(true)
25 .col-md-5
27 .col-md-5
26 %table.table.table-striped
28 %table.table.table-striped
27 %tr
29 %tr
28 %td.text-right
30 %td.text-right
29 %strong User
31 %strong User
30 %td
32 %td
31 - if @submission.user
33 - if @submission.user
32 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
34 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
33 = @submission.user.full_name
35 = @submission.user.full_name
34 - else
36 - else
35 = "(n/a)"
37 = "(n/a)"
36 %tr
38 %tr
37 %td.text-right
39 %td.text-right
38 %strong Task
40 %strong Task
39 %td
41 %td
40 - if @submission.problem!=nil
42 - if @submission.problem!=nil
41 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
43 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
42 = @submission.problem.full_name
44 = @submission.problem.full_name
43 - else
45 - else
44 = "(n/a)"
46 = "(n/a)"
45 %tr
47 %tr
46 %td.text-right
48 %td.text-right
47 %strong Tries
49 %strong Tries
48 %td= @submission.number
50 %td= @submission.number
49 %tr
51 %tr
50 %td.text-right
52 %td.text-right
51 %strong Language
53 %strong Language
52 %td= @submission.language.pretty_name
54 %td= @submission.language.pretty_name
53 %tr
55 %tr
54 %td.text-right
56 %td.text-right
55 %strong Submitted
57 %strong Submitted
56 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
58 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
57 %tr
59 %tr
58 %td.text-right
60 %td.text-right
59 %strong Graded
61 %strong Graded
60 - if @submission.graded_at
62 - if @submission.graded_at
61 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
63 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
62 - else
64 - else
63 %td -
65 %td -
64 %tr
66 %tr
65 %td.text-right
67 %td.text-right
66 %strong Points
68 %strong Points
67 - %td #{@submission.points}/#{@submission.problem.full_score}
69 + %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
68 %tr
70 %tr
69 %td.text-right
71 %td.text-right
70 %strong Comment
72 %strong Comment
71 %td #{@submission.grader_comment}
73 %td #{@submission.grader_comment}
72 %tr
74 %tr
73 %td.text-right
75 %td.text-right
74 %strong Runtime (s)
76 %strong Runtime (s)
75 %td #{@submission.max_runtime}
77 %td #{@submission.max_runtime}
76 %tr
78 %tr
77 %td.text-right
79 %td.text-right
78 %strong Memory (kb)
80 %strong Memory (kb)
79 %td #{@submission.peak_memory}
81 %td #{@submission.peak_memory}
80 %tr
82 %tr
81 %td.text-right
83 %td.text-right
82 %strong Compiler result
84 %strong Compiler result
83 %td
85 %td
84 - %pre= @submission.compiler_message
86 + %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
87 + view
85 - if session[:admin]
88 - if session[:admin]
86 %tr
89 %tr
87 %td.text-right
90 %td.text-right
88 %strong IP
91 %strong IP
89 %td #{@submission.ip_address}
92 %td #{@submission.ip_address}
93 + %tr
94 + %td.text-right
95 + %strong Grading Task Status
96 + %td
97 + = @task.status_str if @task
98 + - if session[:admin]
99 + = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
100 +
101 +
102 + .modal.fade#compiler{tabindex: -1,role: 'dialog'}
103 + .modal-dialog.modal-lg{role:'document'}
104 + .modal-content
105 + .modal-header
106 + %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
107 + %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
108 + %h4 Compiler message
109 + .modal-body
110 + %pre#compiler_msg= @submission.compiler_message
111 + .modal-footer
112 + %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
@@ -1,100 +1,101
1 %h1 Listing users
1 %h1 Listing users
2
2
3 .panel.panel-primary
3 .panel.panel-primary
4 .panel-title.panel-heading
4 .panel-title.panel-heading
5 Quick Add
5 Quick Add
6 .panel-body
6 .panel-body
7 = form_tag( {method: 'post'}, {class: 'form-inline'}) do
7 = form_tag( {method: 'post'}, {class: 'form-inline'}) do
8 .form-group
8 .form-group
9 = label_tag 'user_login', 'Login'
9 = label_tag 'user_login', 'Login'
10 = text_field 'user', 'login', :size => 10,class: 'form-control'
10 = text_field 'user', 'login', :size => 10,class: 'form-control'
11 .form-group
11 .form-group
12 = label_tag 'user_full_name', 'Full Name'
12 = label_tag 'user_full_name', 'Full Name'
13 = text_field 'user', 'full_name', :size => 10,class: 'form-control'
13 = text_field 'user', 'full_name', :size => 10,class: 'form-control'
14 .form-group
14 .form-group
15 = label_tag 'user_password', 'Password'
15 = label_tag 'user_password', 'Password'
16 = text_field 'user', 'password', :size => 10,class: 'form-control'
16 = text_field 'user', 'password', :size => 10,class: 'form-control'
17 .form-group
17 .form-group
18 = label_tag 'user_password_confirmation', 'Confirm'
18 = label_tag 'user_password_confirmation', 'Confirm'
19 = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control'
19 = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control'
20 .form-group
20 .form-group
21 = label_tag 'user_email', 'email'
21 = label_tag 'user_email', 'email'
22 = text_field 'user', 'email', :size => 10,class: 'form-control'
22 = text_field 'user', 'email', :size => 10,class: 'form-control'
23 =submit_tag "Create", class: 'btn btn-primary'
23 =submit_tag "Create", class: 'btn btn-primary'
24
24
25 .panel.panel-primary
25 .panel.panel-primary
26 .panel-title.panel-heading
26 .panel-title.panel-heading
27 Import from site management
27 Import from site management
28 .panel-body
28 .panel-body
29 = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
29 = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
30 .form-group
30 .form-group
31 = label_tag :file, 'File:'
31 = label_tag :file, 'File:'
32 .input-group
32 .input-group
33 %span.input-group-btn
33 %span.input-group-btn
34 %span.btn.btn-default.btn-file
34 %span.btn.btn-default.btn-file
35 Browse
35 Browse
36 = file_field_tag 'file'
36 = file_field_tag 'file'
37 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
37 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
38 = submit_tag 'Submit', class: 'btn btn-default'
38 = submit_tag 'Submit', class: 'btn btn-default'
39
39
40
40
41 %p
41 %p
42 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
42 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
43 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
43 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
44 = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
44 = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
45 + = link_to 'Bulk Manage', bulk_manage_user_admin_path , { class: 'btn btn-default '}
45 = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
46 = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
46 = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
47 = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
47 = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
48 = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
48
49
49 - if GraderConfiguration.multicontests?
50 - if GraderConfiguration.multicontests?
50 %br/
51 %br/
51 %b Multi-contest:
52 %b Multi-contest:
52 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
53 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
53 View users in:
54 View users in:
54 - @contests.each do |contest|
55 - @contests.each do |contest|
55 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
56 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
56 = link_to "[no contest]", :action => 'contests', :id => 'none'
57 = link_to "[no contest]", :action => 'contests', :id => 'none'
57
58
58 Total #{@user_count} users |
59 Total #{@user_count} users |
59 - if !@paginated
60 - if !@paginated
60 Display all users.
61 Display all users.
61 \#{link_to '[show in pages]', :action => 'index', :page => '1'}
62 \#{link_to '[show in pages]', :action => 'index', :page => '1'}
62 - else
63 - else
63 Display in pages.
64 Display in pages.
64 \#{link_to '[display all]', :action => 'index', :page => 'all'} |
65 \#{link_to '[display all]', :action => 'index', :page => 'all'} |
65 \#{will_paginate @users, :container => false}
66 \#{will_paginate @users, :container => false}
66
67
67
68
68 %table.table.table-hover.table-condense
69 %table.table.table-hover.table-condense
69 %thead
70 %thead
70 %th Login
71 %th Login
71 %th Full name
72 %th Full name
72 %th email
73 %th email
73 %th Remark
74 %th Remark
74 %th
75 %th
75 Activated
76 Activated
76 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
77 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
77 %th
78 %th
78 Enabled
79 Enabled
79 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
80 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
80 %th Last IP
81 %th Last IP
81 %th
82 %th
82 %th
83 %th
83 %th
84 %th
84 %th
85 %th
85 - for user in @users
86 - for user in @users
86 %tr
87 %tr
87 - %td= link_to user.login, controller: :users, :action => 'profile', :id => user
88 + %td= link_to user.login, stat_user_path(user)
88 %td= user.full_name
89 %td= user.full_name
89 %td= user.email
90 %td= user.email
90 %td= user.remark
91 %td= user.remark
91 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
92 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
92 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
93 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
93 %td= user.last_ip
94 %td= user.last_ip
94 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
95 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
95 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
96 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
96 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
98 %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
98 %br/
99 %br/
99 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
100 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
100 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
101 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
@@ -1,8 +1,9
1 <h1>Adding list of users</h1>
1 <h1>Adding list of users</h1>
2
2
3 <%= form_tag :action => 'create_from_list' do %>
3 <%= form_tag :action => 'create_from_list' do %>
4 - <%= submit_tag 'create users' %><br/>
4 + <%= submit_tag 'create users',class: 'btn btn-success'%><br/>
5 - List of user information in this format: <tt>user_id,name(,passwd(,alias))</tt><br/>
5 + List of user information in this format: <tt>user_id,name(,passwd(,alias(,remark)))</tt><br/>
6 - Note that <tt>passwd</tt> and <tt>alias</tt> is optional.<br/>
6 + Note that <tt>passwd, alias</tt> and <tt> remark </tt>is optional.<br />
7 + When <tt>passwd</tt> or <tt>alias</tt> is empty, the original value will be used instead.<br/>
7 <%= text_area_tag 'user_list', nil, :rows => 50, :cols => 80 %>
8 <%= text_area_tag 'user_list', nil, :rows => 50, :cols => 80 %>
8 <% end %>
9 <% end %>
@@ -1,40 +1,42
1 CafeGrader::Application.configure do
1 CafeGrader::Application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
2 # Settings specified here will take precedence over those in config/application.rb
3
3
4 # In the development environment your application's code is reloaded on
4 # In the development environment your application's code is reloaded on
5 # every request. This slows down response time but is perfect for development
5 # every request. This slows down response time but is perfect for development
6 # since you don't have to restart the web server when you make code changes.
6 # since you don't have to restart the web server when you make code changes.
7 config.cache_classes = false
7 config.cache_classes = false
8
8
9 - # Log error messages when you accidentally call methods on nil.
9 + # Log error messages when you accidentally call methods on nil. //DEPRICATED
10 - config.whiny_nils = true
10 + # config.whiny_nils = true // DEPRICATED
11
11
12 # Show full error reports and disable caching
12 # Show full error reports and disable caching
13 config.consider_all_requests_local = true
13 config.consider_all_requests_local = true
14 config.action_controller.perform_caching = false
14 config.action_controller.perform_caching = false
15
15
16 # Don't care if the mailer can't send
16 # Don't care if the mailer can't send
17 config.action_mailer.raise_delivery_errors = false
17 config.action_mailer.raise_delivery_errors = false
18
18
19 # Print deprecation notices to the Rails logger
19 # Print deprecation notices to the Rails logger
20 config.active_support.deprecation = :log
20 config.active_support.deprecation = :log
21
21
22 # Only use best-standards-support built into browsers
22 # Only use best-standards-support built into browsers
23 config.action_dispatch.best_standards_support = :builtin
23 config.action_dispatch.best_standards_support = :builtin
24
24
25 # Raise exception on mass assignment protection for Active Record models
25 # Raise exception on mass assignment protection for Active Record models
26 - config.active_record.mass_assignment_sanitizer = :strict
26 + # config.active_record.mass_assignment_sanitizer = :strict //DEPRICATED
27
27
28 - # Log the query plan for queries taking more than this (works
28 + # Log the query plan for queries taking more than this (works // DEPRICATED
29 - # with SQLite, MySQL, and PostgreSQL)
29 + # with SQLite, MySQL, and PostgreSQL) // DEPRICATED
30 - config.active_record.auto_explain_threshold_in_seconds = 0.5
30 + # config.active_record.auto_explain_threshold_in_seconds = 0.5 // DEPRICATED
31
31
32 # Do not compress assets
32 # Do not compress assets
33 config.assets.compress = false
33 config.assets.compress = false
34
34
35 # Expands the lines which load the assets
35 # Expands the lines which load the assets
36 config.assets.debug = true
36 config.assets.debug = true
37
37
38 # Prevents assets from rendering twice
38 # Prevents assets from rendering twice
39 - config.serve_static_assets = true
39 + config.serve_static_files = true
40 +
41 + config.eager_load = false
40 end
42 end
@@ -1,67 +1,69
1 CafeGrader::Application.configure do
1 CafeGrader::Application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
2 # Settings specified here will take precedence over those in config/application.rb
3
3
4 # Code is not reloaded between requests
4 # Code is not reloaded between requests
5 config.cache_classes = true
5 config.cache_classes = true
6
6
7 # Full error reports are disabled and caching is turned on
7 # Full error reports are disabled and caching is turned on
8 config.consider_all_requests_local = false
8 config.consider_all_requests_local = false
9 config.action_controller.perform_caching = true
9 config.action_controller.perform_caching = true
10
10
11 # Disable Rails's static asset server (Apache or nginx will already do this)
11 # Disable Rails's static asset server (Apache or nginx will already do this)
12 - config.serve_static_assets = false
12 + config.serve_static_files = false
13
13
14 # Compress JavaScripts and CSS
14 # Compress JavaScripts and CSS
15 config.assets.compress = true
15 config.assets.compress = true
16
16
17 # Don't fallback to assets pipeline if a precompiled asset is missed
17 # Don't fallback to assets pipeline if a precompiled asset is missed
18 config.assets.compile = false
18 config.assets.compile = false
19
19
20 # Generate digests for assets URLs
20 # Generate digests for assets URLs
21 config.assets.digest = true
21 config.assets.digest = true
22
22
23 # Defaults to nil and saved in location specified by config.assets.prefix
23 # Defaults to nil and saved in location specified by config.assets.prefix
24 # config.assets.manifest = YOUR_PATH
24 # config.assets.manifest = YOUR_PATH
25
25
26 # Specifies the header that your server uses for sending files
26 # Specifies the header that your server uses for sending files
27 # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
27 # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
28 # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29
29
30 # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
30 # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 # config.force_ssl = true
31 # config.force_ssl = true
32
32
33 # See everything in the log (default is :info)
33 # See everything in the log (default is :info)
34 # config.log_level = :debug
34 # config.log_level = :debug
35
35
36 # Prepend all log lines with the following tags
36 # Prepend all log lines with the following tags
37 # config.log_tags = [ :subdomain, :uuid ]
37 # config.log_tags = [ :subdomain, :uuid ]
38
38
39 # Use a different logger for distributed setups
39 # Use a different logger for distributed setups
40 # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
40 # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41
41
42 # Use a different cache store in production
42 # Use a different cache store in production
43 # config.cache_store = :mem_cache_store
43 # config.cache_store = :mem_cache_store
44
44
45 # Enable serving of images, stylesheets, and JavaScripts from an asset server
45 # Enable serving of images, stylesheets, and JavaScripts from an asset server
46 # config.action_controller.asset_host = "http://assets.example.com"
46 # config.action_controller.asset_host = "http://assets.example.com"
47
47
48 # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
48 # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
49 # config.assets.precompile += %w( search.js )
49 # config.assets.precompile += %w( search.js )
50
50
51 # Disable delivery errors, bad email addresses will be ignored
51 # Disable delivery errors, bad email addresses will be ignored
52 # config.action_mailer.raise_delivery_errors = false
52 # config.action_mailer.raise_delivery_errors = false
53
53
54 # Enable threaded mode
54 # Enable threaded mode
55 # config.threadsafe!
55 # config.threadsafe!
56
56
57 # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
57 # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 # the I18n.default_locale when a translation can not be found)
58 # the I18n.default_locale when a translation can not be found)
59 config.i18n.fallbacks = true
59 config.i18n.fallbacks = true
60
60
61 # Send deprecation notices to registered listeners
61 # Send deprecation notices to registered listeners
62 config.active_support.deprecation = :notify
62 config.active_support.deprecation = :notify
63
63
64 # Log the query plan for queries taking more than this (works
64 # Log the query plan for queries taking more than this (works
65 # with SQLite, MySQL, and PostgreSQL)
65 # with SQLite, MySQL, and PostgreSQL)
66 # config.active_record.auto_explain_threshold_in_seconds = 0.5
66 # config.active_record.auto_explain_threshold_in_seconds = 0.5
67 +
68 + config.eager_load = true
67 end
69 end
@@ -1,37 +1,43
1 CafeGrader::Application.configure do
1 CafeGrader::Application.configure do
2 # Settings specified here will take precedence over those in config/application.rb
2 # Settings specified here will take precedence over those in config/application.rb
3
3
4 # The test environment is used exclusively to run your application's
4 # The test environment is used exclusively to run your application's
5 # test suite. You never need to work with it otherwise. Remember that
5 # test suite. You never need to work with it otherwise. Remember that
6 # your test database is "scratch space" for the test suite and is wiped
6 # your test database is "scratch space" for the test suite and is wiped
7 # and recreated between test runs. Don't rely on the data there!
7 # and recreated between test runs. Don't rely on the data there!
8 config.cache_classes = true
8 config.cache_classes = true
9
9
10 # Configure static asset server for tests with Cache-Control for performance
10 # Configure static asset server for tests with Cache-Control for performance
11 - config.serve_static_assets = true
11 + config.serve_static_files = true
12 config.static_cache_control = "public, max-age=3600"
12 config.static_cache_control = "public, max-age=3600"
13
13
14 # Log error messages when you accidentally call methods on nil
14 # Log error messages when you accidentally call methods on nil
15 config.whiny_nils = true
15 config.whiny_nils = true
16
16
17 # Show full error reports and disable caching
17 # Show full error reports and disable caching
18 config.consider_all_requests_local = true
18 config.consider_all_requests_local = true
19 config.action_controller.perform_caching = false
19 config.action_controller.perform_caching = false
20
20
21 # Raise exceptions instead of rendering exception templates
21 # Raise exceptions instead of rendering exception templates
22 config.action_dispatch.show_exceptions = false
22 config.action_dispatch.show_exceptions = false
23
23
24 # Disable request forgery protection in test environment
24 # Disable request forgery protection in test environment
25 config.action_controller.allow_forgery_protection = false
25 config.action_controller.allow_forgery_protection = false
26
26
27 # Tell Action Mailer not to deliver emails to the real world.
27 # Tell Action Mailer not to deliver emails to the real world.
28 # The :test delivery method accumulates sent emails in the
28 # The :test delivery method accumulates sent emails in the
29 # ActionMailer::Base.deliveries array.
29 # ActionMailer::Base.deliveries array.
30 config.action_mailer.delivery_method = :test
30 config.action_mailer.delivery_method = :test
31
31
32 # Raise exception on mass assignment protection for Active Record models
32 # Raise exception on mass assignment protection for Active Record models
33 - config.active_record.mass_assignment_sanitizer = :strict
33 + #config.active_record.mass_assignment_sanitizer = :strict // DEPRICATED
34
34
35 # Print deprecation notices to the stderr
35 # Print deprecation notices to the stderr
36 config.active_support.deprecation = :stderr
36 config.active_support.deprecation = :stderr
37 +
38 + config.eager_load = false
39 +
40 + #test order
41 + config.active_support.test_order = :sorted
42 +
37 end
43 end
@@ -1,6 +1,5
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 # Add new mime types for use in respond_to blocks:
3 # Add new mime types for use in respond_to blocks:
4 # Mime::Type.register "text/richtext", :rtf
4 # Mime::Type.register "text/richtext", :rtf
5 # Mime::Type.register_alias "text/html", :iphone
5 # Mime::Type.register_alias "text/html", :iphone
6 - Mime::Type.register 'application/pdf', :pdf
@@ -1,73 +1,98
1 CafeGrader::Application.routes.draw do
1 CafeGrader::Application.routes.draw do
2 get "sources/direct_edit"
2 get "sources/direct_edit"
3
3
4 root :to => 'main#login'
4 root :to => 'main#login'
5
5
6 + #logins
7 + get 'login/login', to: 'login#login'
8 +
6 resources :contests
9 resources :contests
7
10
8 resources :sites
11 resources :sites
9
12
10 resources :announcements do
13 resources :announcements do
11 member do
14 member do
12 get 'toggle','toggle_front'
15 get 'toggle','toggle_front'
13 end
16 end
14 end
17 end
15
18
16 resources :problems do
19 resources :problems do
17 member do
20 member do
18 get 'toggle'
21 get 'toggle'
19 get 'toggle_test'
22 get 'toggle_test'
23 + get 'toggle_view_testcase'
20 get 'stat'
24 get 'stat'
21 end
25 end
22 collection do
26 collection do
23 get 'turn_all_off'
27 get 'turn_all_off'
24 get 'turn_all_on'
28 get 'turn_all_on'
25 get 'import'
29 get 'import'
26 get 'manage'
30 get 'manage'
27 end
31 end
32 +
33 + end
34 +
35 + resources :testcases, only: [] do
36 + member do
37 + get 'download_input'
38 + get 'download_sol'
39 + end
40 + collection do
41 + get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
42 + end
28 end
43 end
29
44
30 resources :grader_configuration, controller: 'configurations'
45 resources :grader_configuration, controller: 'configurations'
31
46
32 resources :users do
47 resources :users do
33 member do
48 member do
34 get 'toggle_activate', 'toggle_enable'
49 get 'toggle_activate', 'toggle_enable'
35 get 'stat'
50 get 'stat'
36 end
51 end
37 end
52 end
38
53
39 resources :submissions do
54 resources :submissions do
55 + member do
56 + get 'download'
57 + get 'compiler_msg'
58 + get 'rejudge'
59 + end
40 collection do
60 collection do
41 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
61 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
42 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
62 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
43 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
63 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
44 end
64 end
45 end
65 end
46
66
47 - match 'tasks/view/:file.:ext' => 'tasks#view'
67 +
48 - match 'tasks/download/:id/:file.:ext' => 'tasks#download'
49 - match 'heartbeat/:id/edit' => 'heartbeat#edit'
50
68
51 #main
69 #main
52 get "main/list"
70 get "main/list"
53 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
71 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
54
72
73 + #user admin
74 + get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
75 +
55 #report
76 #report
56 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
77 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
57 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
78 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
58 get "report/login"
79 get "report/login"
59 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
80 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
60 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
81 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
61
82
83 +
84 + #
85 + get 'tasks/view/:file.:ext' => 'tasks#view'
86 + get 'tasks/download/:id/:file.:ext' => 'tasks#download'
87 + get 'heartbeat/:id/edit' => 'heartbeat#edit'
88 +
62 #grader
89 #grader
63 get 'graders/list', to: 'graders#list', as: 'grader_list'
90 get 'graders/list', to: 'graders#list', as: 'grader_list'
64 -
65
91
66 - match 'heartbeat/:id/edit' => 'heartbeat#edit'
67
92
68 # See how all your routes lay out with "rake routes"
93 # See how all your routes lay out with "rake routes"
69
94
70 # This is a legacy wild controller route that's not recommended for RESTful applications.
95 # This is a legacy wild controller route that's not recommended for RESTful applications.
71 # Note: This route will make all actions in every controller accessible via GET requests.
96 # Note: This route will make all actions in every controller accessible via GET requests.
72 - match ':controller(/:action(/:id))(.:format)'
97 + match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
73 end
98 end
@@ -1,280 +1,283
1 # encoding: UTF-8
1 # encoding: UTF-8
2 # This file is auto-generated from the current state of the database. Instead
2 # This file is auto-generated from the current state of the database. Instead
3 # of editing this file, please use the migrations feature of Active Record to
3 # of editing this file, please use the migrations feature of Active Record to
4 # incrementally modify your database, and then regenerate this schema definition.
4 # incrementally modify your database, and then regenerate this schema definition.
5 #
5 #
6 # Note that this schema.rb definition is the authoritative source for your
6 # Note that this schema.rb definition is the authoritative source for your
7 # database schema. If you need to create the application database on another
7 # database schema. If you need to create the application database on another
8 # system, you should be using db:schema:load, not running all the migrations
8 # system, you should be using db:schema:load, not running all the migrations
9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 # you'll amass, the slower it'll run and the greater likelihood for issues).
10 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 #
11 #
12 - # It's strongly recommended to check this file into your version control system.
12 + # It's strongly recommended that you check this file into your version control system.
13
13
14 - ActiveRecord::Schema.define(:version => 20161014091417) do
14 + ActiveRecord::Schema.define(version: 20170427070345) do
15
15
16 - create_table "announcements", :force => true do |t|
16 + create_table "announcements", force: :cascade do |t|
17 - t.string "author"
17 + t.string "author", limit: 255
18 - t.text "body"
18 + t.text "body", limit: 65535
19 t.boolean "published"
19 t.boolean "published"
20 - t.datetime "created_at", :null => false
20 + t.datetime "created_at", null: false
21 - t.datetime "updated_at", :null => false
21 + t.datetime "updated_at", null: false
22 - t.boolean "frontpage", :default => false
22 + t.boolean "frontpage", default: false
23 - t.boolean "contest_only", :default => false
23 + t.boolean "contest_only", default: false
24 - t.string "title"
24 + t.string "title", limit: 255
25 - t.string "notes"
25 + t.string "notes", limit: 255
26 end
26 end
27
27
28 - create_table "contests", :force => true do |t|
28 + create_table "contests", force: :cascade do |t|
29 - t.string "title"
29 + t.string "title", limit: 255
30 t.boolean "enabled"
30 t.boolean "enabled"
31 - t.datetime "created_at", :null => false
31 + t.datetime "created_at", null: false
32 - t.datetime "updated_at", :null => false
32 + t.datetime "updated_at", null: false
33 - t.string "name"
33 + t.string "name", limit: 255
34 end
34 end
35
35
36 - create_table "contests_problems", :id => false, :force => true do |t|
36 + create_table "contests_problems", id: false, force: :cascade do |t|
37 - t.integer "contest_id"
37 + t.integer "contest_id", limit: 4
38 - t.integer "problem_id"
38 + t.integer "problem_id", limit: 4
39 end
39 end
40
40
41 - create_table "contests_users", :id => false, :force => true do |t|
41 + create_table "contests_users", id: false, force: :cascade do |t|
42 - t.integer "contest_id"
42 + t.integer "contest_id", limit: 4
43 - t.integer "user_id"
43 + t.integer "user_id", limit: 4
44 end
44 end
45
45
46 - create_table "countries", :force => true do |t|
46 + create_table "countries", force: :cascade do |t|
47 - t.string "name"
47 + t.string "name", limit: 255
48 - t.datetime "created_at", :null => false
48 + t.datetime "created_at", null: false
49 - t.datetime "updated_at", :null => false
49 + t.datetime "updated_at", null: false
50 end
50 end
51
51
52 - create_table "descriptions", :force => true do |t|
52 + create_table "descriptions", force: :cascade do |t|
53 - t.text "body"
53 + t.text "body", limit: 65535
54 t.boolean "markdowned"
54 t.boolean "markdowned"
55 - t.datetime "created_at", :null => false
55 + t.datetime "created_at", null: false
56 - t.datetime "updated_at", :null => false
56 + t.datetime "updated_at", null: false
57 end
57 end
58
58
59 - create_table "grader_configurations", :force => true do |t|
59 + create_table "grader_configurations", force: :cascade do |t|
60 - t.string "key"
60 + t.string "key", limit: 255
61 - t.string "value_type"
61 + t.string "value_type", limit: 255
62 - t.string "value"
62 + t.string "value", limit: 255
63 - t.datetime "created_at", :null => false
63 + t.datetime "created_at", null: false
64 - t.datetime "updated_at", :null => false
64 + t.datetime "updated_at", null: false
65 - t.text "description"
65 + t.text "description", limit: 65535
66 end
66 end
67
67
68 - create_table "grader_processes", :force => true do |t|
68 + create_table "grader_processes", force: :cascade do |t|
69 - t.string "host"
69 + t.string "host", limit: 255
70 - t.integer "pid"
70 + t.integer "pid", limit: 4
71 - t.string "mode"
71 + t.string "mode", limit: 255
72 t.boolean "active"
72 t.boolean "active"
73 - t.datetime "created_at", :null => false
73 + t.datetime "created_at", null: false
74 - t.datetime "updated_at", :null => false
74 + t.datetime "updated_at", null: false
75 - t.integer "task_id"
75 + t.integer "task_id", limit: 4
76 - t.string "task_type"
76 + t.string "task_type", limit: 255
77 t.boolean "terminated"
77 t.boolean "terminated"
78 end
78 end
79
79
80 - add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
80 + add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree
81
81
82 - create_table "heart_beats", :force => true do |t|
82 + create_table "heart_beats", force: :cascade do |t|
83 - t.integer "user_id"
83 + t.integer "user_id", limit: 4
84 - t.string "ip_address"
84 + t.string "ip_address", limit: 255
85 - t.datetime "created_at", :null => false
85 + t.datetime "created_at", null: false
86 - t.datetime "updated_at", :null => false
86 + t.datetime "updated_at", null: false
87 - t.string "status"
87 + t.string "status", limit: 255
88 end
88 end
89
89
90 - add_index "heart_beats", ["updated_at"], :name => "index_heart_beats_on_updated_at"
90 + add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree
91
91
92 - create_table "languages", :force => true do |t|
92 + create_table "languages", force: :cascade do |t|
93 - t.string "name", :limit => 10
93 + t.string "name", limit: 10
94 - t.string "pretty_name"
94 + t.string "pretty_name", limit: 255
95 - t.string "ext", :limit => 10
95 + t.string "ext", limit: 10
96 - t.string "common_ext"
96 + t.string "common_ext", limit: 255
97 end
97 end
98
98
99 - create_table "logins", :force => true do |t|
99 + create_table "logins", force: :cascade do |t|
100 - t.integer "user_id"
100 + t.integer "user_id", limit: 4
101 - t.string "ip_address"
101 + t.string "ip_address", limit: 255
102 - t.datetime "created_at", :null => false
102 + t.datetime "created_at", null: false
103 - t.datetime "updated_at", :null => false
103 + t.datetime "updated_at", null: false
104 end
104 end
105
105
106 - create_table "messages", :force => true do |t|
106 + create_table "messages", force: :cascade do |t|
107 - t.integer "sender_id"
107 + t.integer "sender_id", limit: 4
108 - t.integer "receiver_id"
108 + t.integer "receiver_id", limit: 4
109 - t.integer "replying_message_id"
109 + t.integer "replying_message_id", limit: 4
110 - t.text "body"
110 + t.text "body", limit: 65535
111 t.boolean "replied"
111 t.boolean "replied"
112 - t.datetime "created_at", :null => false
112 + t.datetime "created_at", null: false
113 - t.datetime "updated_at", :null => false
113 + t.datetime "updated_at", null: false
114 end
114 end
115
115
116 - create_table "problems", :force => true do |t|
116 + create_table "problems", force: :cascade do |t|
117 - t.string "name", :limit => 30
117 + t.string "name", limit: 30
118 - t.string "full_name"
118 + t.string "full_name", limit: 255
119 - t.integer "full_score"
119 + t.integer "full_score", limit: 4
120 t.date "date_added"
120 t.date "date_added"
121 t.boolean "available"
121 t.boolean "available"
122 - t.string "url"
122 + t.string "url", limit: 255
123 - t.integer "description_id"
123 + t.integer "description_id", limit: 4
124 t.boolean "test_allowed"
124 t.boolean "test_allowed"
125 t.boolean "output_only"
125 t.boolean "output_only"
126 - t.string "description_filename"
126 + t.string "description_filename", limit: 255
127 + t.boolean "view_testcase"
127 end
128 end
128
129
129 - create_table "rights", :force => true do |t|
130 + create_table "rights", force: :cascade do |t|
130 - t.string "name"
131 + t.string "name", limit: 255
131 - t.string "controller"
132 + t.string "controller", limit: 255
132 - t.string "action"
133 + t.string "action", limit: 255
133 end
134 end
134
135
135 - create_table "rights_roles", :id => false, :force => true do |t|
136 + create_table "rights_roles", id: false, force: :cascade do |t|
136 - t.integer "right_id"
137 + t.integer "right_id", limit: 4
137 - t.integer "role_id"
138 + t.integer "role_id", limit: 4
138 end
139 end
139
140
140 - add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
141 + add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree
141
142
142 - create_table "roles", :force => true do |t|
143 + create_table "roles", force: :cascade do |t|
143 - t.string "name"
144 + t.string "name", limit: 255
144 end
145 end
145
146
146 - create_table "roles_users", :id => false, :force => true do |t|
147 + create_table "roles_users", id: false, force: :cascade do |t|
147 - t.integer "role_id"
148 + t.integer "role_id", limit: 4
148 - t.integer "user_id"
149 + t.integer "user_id", limit: 4
149 end
150 end
150
151
151 - add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
152 + add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree
152
153
153 - create_table "sessions", :force => true do |t|
154 + create_table "sessions", force: :cascade do |t|
154 - t.string "session_id"
155 + t.string "session_id", limit: 255
155 - t.text "data"
156 + t.text "data", limit: 65535
156 t.datetime "updated_at"
157 t.datetime "updated_at"
157 end
158 end
158
159
159 - add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
160 + add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree
160 - add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
161 + add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree
161
162
162 - create_table "sites", :force => true do |t|
163 + create_table "sites", force: :cascade do |t|
163 - t.string "name"
164 + t.string "name", limit: 255
164 t.boolean "started"
165 t.boolean "started"
165 t.datetime "start_time"
166 t.datetime "start_time"
166 - t.datetime "created_at", :null => false
167 + t.datetime "created_at", null: false
167 - t.datetime "updated_at", :null => false
168 + t.datetime "updated_at", null: false
168 - t.integer "country_id"
169 + t.integer "country_id", limit: 4
169 - t.string "password"
170 + t.string "password", limit: 255
170 end
171 end
171
172
172 - create_table "submission_view_logs", :force => true do |t|
173 + create_table "submission_view_logs", force: :cascade do |t|
173 - t.integer "user_id"
174 + t.integer "user_id", limit: 4
174 - t.integer "submission_id"
175 + t.integer "submission_id", limit: 4
175 - t.datetime "created_at", :null => false
176 + t.datetime "created_at", null: false
176 - t.datetime "updated_at", :null => false
177 + t.datetime "updated_at", null: false
177 end
178 end
178
179
179 - create_table "submissions", :force => true do |t|
180 + create_table "submissions", force: :cascade do |t|
180 - t.integer "user_id"
181 + t.integer "user_id", limit: 4
181 - t.integer "problem_id"
182 + t.integer "problem_id", limit: 4
182 - t.integer "language_id"
183 + t.integer "language_id", limit: 4
183 - t.text "source"
184 + t.text "source", limit: 65535
184 - t.binary "binary"
185 + t.binary "binary", limit: 65535
185 t.datetime "submitted_at"
186 t.datetime "submitted_at"
186 t.datetime "compiled_at"
187 t.datetime "compiled_at"
187 - t.text "compiler_message"
188 + t.text "compiler_message", limit: 65535
188 t.datetime "graded_at"
189 t.datetime "graded_at"
189 - t.integer "points"
190 + t.integer "points", limit: 4
190 - t.text "grader_comment"
191 + t.text "grader_comment", limit: 65535
191 - t.integer "number"
192 + t.integer "number", limit: 4
192 - t.string "source_filename"
193 + t.string "source_filename", limit: 255
193 - t.float "max_runtime"
194 + t.float "max_runtime", limit: 24
194 - t.integer "peak_memory"
195 + t.integer "peak_memory", limit: 4
195 - t.integer "effective_code_length"
196 + t.integer "effective_code_length", limit: 4
196 - t.string "ip_address"
197 + t.string "ip_address", limit: 255
197 end
198 end
198
199
199 - add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
200 + add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true, using: :btree
200 - add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
201 + add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id", using: :btree
201
202
202 - create_table "tasks", :force => true do |t|
203 + create_table "tasks", force: :cascade do |t|
203 - t.integer "submission_id"
204 + t.integer "submission_id", limit: 4
204 t.datetime "created_at"
205 t.datetime "created_at"
205 - t.integer "status"
206 + t.integer "status", limit: 4
206 t.datetime "updated_at"
207 t.datetime "updated_at"
207 end
208 end
208
209
209 - create_table "test_pairs", :force => true do |t|
210 + add_index "tasks", ["submission_id"], name: "index_tasks_on_submission_id", using: :btree
210 - t.integer "problem_id"
211 +
211 - t.text "input", :limit => 16777215
212 + create_table "test_pairs", force: :cascade do |t|
212 - t.text "solution", :limit => 16777215
213 + t.integer "problem_id", limit: 4
213 - t.datetime "created_at", :null => false
214 + t.text "input", limit: 16777215
214 - t.datetime "updated_at", :null => false
215 + t.text "solution", limit: 16777215
216 + t.datetime "created_at", null: false
217 + t.datetime "updated_at", null: false
215 end
218 end
216
219
217 - create_table "test_requests", :force => true do |t|
220 + create_table "test_requests", force: :cascade do |t|
218 - t.integer "user_id"
221 + t.integer "user_id", limit: 4
219 - t.integer "problem_id"
222 + t.integer "problem_id", limit: 4
220 - t.integer "submission_id"
223 + t.integer "submission_id", limit: 4
221 - t.string "input_file_name"
224 + t.string "input_file_name", limit: 255
222 - t.string "output_file_name"
225 + t.string "output_file_name", limit: 255
223 - t.string "running_stat"
226 + t.string "running_stat", limit: 255
224 - t.integer "status"
227 + t.integer "status", limit: 4
225 - t.datetime "updated_at", :null => false
228 + t.datetime "updated_at", null: false
226 t.datetime "submitted_at"
229 t.datetime "submitted_at"
227 t.datetime "compiled_at"
230 t.datetime "compiled_at"
228 - t.text "compiler_message"
231 + t.text "compiler_message", limit: 65535
229 t.datetime "graded_at"
232 t.datetime "graded_at"
230 - t.string "grader_comment"
233 + t.string "grader_comment", limit: 255
231 - t.datetime "created_at", :null => false
234 + t.datetime "created_at", null: false
232 - t.float "running_time"
235 + t.float "running_time", limit: 24
233 - t.string "exit_status"
236 + t.string "exit_status", limit: 255
234 - t.integer "memory_usage"
237 + t.integer "memory_usage", limit: 4
235 end
238 end
236
239
237 - add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
240 + add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id", using: :btree
238
241
239 - create_table "testcases", :force => true do |t|
242 + create_table "testcases", force: :cascade do |t|
240 - t.integer "problem_id"
243 + t.integer "problem_id", limit: 4
241 - t.integer "num"
244 + t.integer "num", limit: 4
242 - t.integer "group"
245 + t.integer "group", limit: 4
243 - t.integer "score"
246 + t.integer "score", limit: 4
244 - t.text "input"
247 + t.text "input", limit: 4294967295
245 - t.text "sol"
248 + t.text "sol", limit: 4294967295
246 - t.datetime "created_at", :null => false
249 + t.datetime "created_at"
247 - t.datetime "updated_at", :null => false
250 + t.datetime "updated_at"
248 end
251 end
249
252
250 - add_index "testcases", ["problem_id"], :name => "index_testcases_on_problem_id"
253 + add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id", using: :btree
251
254
252 - create_table "user_contest_stats", :force => true do |t|
255 + create_table "user_contest_stats", force: :cascade do |t|
253 - t.integer "user_id"
256 + t.integer "user_id", limit: 4
254 t.datetime "started_at"
257 t.datetime "started_at"
255 - t.datetime "created_at", :null => false
258 + t.datetime "created_at", null: false
256 - t.datetime "updated_at", :null => false
259 + t.datetime "updated_at", null: false
257 t.boolean "forced_logout"
260 t.boolean "forced_logout"
258 end
261 end
259
262
260 - create_table "users", :force => true do |t|
263 + create_table "users", force: :cascade do |t|
261 - t.string "login", :limit => 50
264 + t.string "login", limit: 50
262 - t.string "full_name"
265 + t.string "full_name", limit: 255
263 - t.string "hashed_password"
266 + t.string "hashed_password", limit: 255
264 - t.string "salt", :limit => 5
267 + t.string "salt", limit: 5
265 - t.string "alias"
268 + t.string "alias", limit: 255
266 - t.string "email"
269 + t.string "email", limit: 255
267 - t.integer "site_id"
270 + t.integer "site_id", limit: 4
268 - t.integer "country_id"
271 + t.integer "country_id", limit: 4
269 - t.boolean "activated", :default => false
272 + t.boolean "activated", default: false
270 t.datetime "created_at"
273 t.datetime "created_at"
271 t.datetime "updated_at"
274 t.datetime "updated_at"
272 - t.boolean "enabled", :default => true
275 + t.boolean "enabled", default: true
273 - t.string "remark"
276 + t.string "remark", limit: 255
274 - t.string "last_ip"
277 + t.string "last_ip", limit: 255
275 - t.string "section"
278 + t.string "section", limit: 255
276 end
279 end
277
280
278 - add_index "users", ["login"], :name => "index_users_on_login", :unique => true
281 + add_index "users", ["login"], name: "index_users_on_login", unique: true, using: :btree
279
282
280 end
283 end
@@ -1,172 +1,200
1 CONFIGURATIONS =
1 CONFIGURATIONS =
2 [
2 [
3 {
3 {
4 :key => 'system.single_user_mode',
4 :key => 'system.single_user_mode',
5 :value_type => 'boolean',
5 :value_type => 'boolean',
6 :default_value => 'false',
6 :default_value => 'false',
7 :description => 'Only admins can log in to the system when running under single user mode.'
7 :description => 'Only admins can log in to the system when running under single user mode.'
8 },
8 },
9
9
10 {
10 {
11 :key => 'ui.front.title',
11 :key => 'ui.front.title',
12 :value_type => 'string',
12 :value_type => 'string',
13 :default_value => 'Grader'
13 :default_value => 'Grader'
14 },
14 },
15
15
16 {
16 {
17 :key => 'ui.front.welcome_message',
17 :key => 'ui.front.welcome_message',
18 :value_type => 'string',
18 :value_type => 'string',
19 :default_value => 'Welcome!'
19 :default_value => 'Welcome!'
20 },
20 },
21
21
22 {
22 {
23 :key => 'ui.show_score',
23 :key => 'ui.show_score',
24 :value_type => 'boolean',
24 :value_type => 'boolean',
25 :default_value => 'true'
25 :default_value => 'true'
26 },
26 },
27
27
28 {
28 {
29 :key => 'contest.time_limit',
29 :key => 'contest.time_limit',
30 :value_type => 'string',
30 :value_type => 'string',
31 :default_value => 'unlimited',
31 :default_value => 'unlimited',
32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
33 },
33 },
34
34
35 {
35 {
36 :key => 'system.mode',
36 :key => 'system.mode',
37 :value_type => 'string',
37 :value_type => 'string',
38 :default_value => 'standard',
38 :default_value => 'standard',
39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 },
40 },
41
41
42 {
42 {
43 :key => 'contest.name',
43 :key => 'contest.name',
44 :value_type => 'string',
44 :value_type => 'string',
45 :default_value => 'Grader',
45 :default_value => 'Grader',
46 :description => 'This name will be shown on the user header bar.'
46 :description => 'This name will be shown on the user header bar.'
47 },
47 },
48
48
49 {
49 {
50 :key => 'contest.multisites',
50 :key => 'contest.multisites',
51 :value_type => 'boolean',
51 :value_type => 'boolean',
52 :default_value => 'false',
52 :default_value => 'false',
53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
54 },
54 },
55
55
56 + #---------------------------- right --------------------------------
56 {
57 {
57 :key => 'right.user_hall_of_fame',
58 :key => 'right.user_hall_of_fame',
58 :value_type => 'boolean',
59 :value_type => 'boolean',
59 :default_value => 'false',
60 :default_value => 'false',
60 :description => 'If true, any user can access hall of fame page.'
61 :description => 'If true, any user can access hall of fame page.'
61 },
62 },
62
63
63 {
64 {
64 :key => 'right.multiple_ip_login',
65 :key => 'right.multiple_ip_login',
65 :value_type => 'boolean',
66 :value_type => 'boolean',
66 :default_value => 'true',
67 :default_value => 'true',
67 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
68 },
69 },
69
70
70 {
71 {
71 :key => 'right.user_view_submission',
72 :key => 'right.user_view_submission',
72 :value_type => 'boolean',
73 :value_type => 'boolean',
73 :default_value => 'false',
74 :default_value => 'false',
74 :description => 'If true, any user can view submissions of every one.'
75 :description => 'If true, any user can view submissions of every one.'
75 },
76 },
76
77
78 + {
79 + :key => 'right.bypass_agreement',
80 + :value_type => 'boolean',
81 + :default_value => 'true',
82 + :description => 'When false, a user must accept usage agreement before login'
83 + },
84 +
85 + {
86 + :key => 'right.heartbeat_response',
87 + :value_type => 'string',
88 + :default_value => 'OK',
89 + :description => 'Heart beat response text'
90 + },
91 +
92 + {
93 + :key => 'right.heartbeat_response_full',
94 + :value_type => 'string',
95 + :default_value => 'OK',
96 + :description => 'Heart beat response text when user got full score (set this value to the empty string to disable this feature)'
97 + },
98 +
99 + {
100 + :key => 'right.view_testcase',
101 + :value_type => 'boolean',
102 + :default_value => 'false',
103 + :description => 'When true, any user can view/download test data'
104 + },
77 # If Configuration['system.online_registration'] is true, the
105 # If Configuration['system.online_registration'] is true, the
78 # system allows online registration, and will use these
106 # system allows online registration, and will use these
79 # information for sending confirmation emails.
107 # information for sending confirmation emails.
80 {
108 {
81 :key => 'system.online_registration.smtp',
109 :key => 'system.online_registration.smtp',
82 :value_type => 'string',
110 :value_type => 'string',
83 :default_value => 'smtp.somehost.com'
111 :default_value => 'smtp.somehost.com'
84 },
112 },
85
113
86 {
114 {
87 :key => 'system.online_registration.from',
115 :key => 'system.online_registration.from',
88 :value_type => 'string',
116 :value_type => 'string',
89 :default_value => 'your.email@address'
117 :default_value => 'your.email@address'
90 },
118 },
91
119
92 {
120 {
93 :key => 'system.admin_email',
121 :key => 'system.admin_email',
94 :value_type => 'string',
122 :value_type => 'string',
95 :default_value => 'admin@admin.email'
123 :default_value => 'admin@admin.email'
96 },
124 },
97
125
98 {
126 {
99 :key => 'system.user_setting_enabled',
127 :key => 'system.user_setting_enabled',
100 :value_type => 'boolean',
128 :value_type => 'boolean',
101 :default_value => 'true',
129 :default_value => 'true',
102 :description => 'If this option is true, users can change their settings'
130 :description => 'If this option is true, users can change their settings'
103 },
131 },
104
132
105 {
133 {
106 :key => 'system.user_setting_enabled',
134 :key => 'system.user_setting_enabled',
107 :value_type => 'boolean',
135 :value_type => 'boolean',
108 :default_value => 'true',
136 :default_value => 'true',
109 :description => 'If this option is true, users can change their settings'
137 :description => 'If this option is true, users can change their settings'
110 },
138 },
111
139
112 # If Configuration['contest.test_request.early_timeout'] is true
140 # If Configuration['contest.test_request.early_timeout'] is true
113 # the user will not be able to use test request at 30 minutes
141 # the user will not be able to use test request at 30 minutes
114 # before the contest ends.
142 # before the contest ends.
115 {
143 {
116 :key => 'contest.test_request.early_timeout',
144 :key => 'contest.test_request.early_timeout',
117 :value_type => 'boolean',
145 :value_type => 'boolean',
118 :default_value => 'false'
146 :default_value => 'false'
119 },
147 },
120
148
121 {
149 {
122 :key => 'system.multicontests',
150 :key => 'system.multicontests',
123 :value_type => 'boolean',
151 :value_type => 'boolean',
124 :default_value => 'false'
152 :default_value => 'false'
125 },
153 },
126
154
127 {
155 {
128 :key => 'contest.confirm_indv_contest_start',
156 :key => 'contest.confirm_indv_contest_start',
129 :value_type => 'boolean',
157 :value_type => 'boolean',
130 :default_value => 'false'
158 :default_value => 'false'
131 },
159 },
132
160
133 {
161 {
134 :key => 'contest.default_contest_name',
162 :key => 'contest.default_contest_name',
135 :value_type => 'string',
163 :value_type => 'string',
136 :default_value => 'none',
164 :default_value => 'none',
137 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
165 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
138 }
166 }
139
167
140 ]
168 ]
141
169
142
170
143 def create_configuration_key(key,
171 def create_configuration_key(key,
144 value_type,
172 value_type,
145 default_value,
173 default_value,
146 description='')
174 description='')
147 conf = (GraderConfiguration.find_by_key(key) ||
175 conf = (GraderConfiguration.find_by_key(key) ||
148 GraderConfiguration.new(:key => key,
176 GraderConfiguration.new(:key => key,
149 :value_type => value_type,
177 :value_type => value_type,
150 :value => default_value))
178 :value => default_value))
151 conf.description = description
179 conf.description = description
152 conf.save
180 conf.save
153 end
181 end
154
182
155 def seed_config
183 def seed_config
156 CONFIGURATIONS.each do |conf|
184 CONFIGURATIONS.each do |conf|
157 if conf.has_key? :description
185 if conf.has_key? :description
158 desc = conf[:description]
186 desc = conf[:description]
159 else
187 else
160 desc = ''
188 desc = ''
161 end
189 end
162 create_configuration_key(conf[:key],
190 create_configuration_key(conf[:key],
163 conf[:value_type],
191 conf[:value_type],
164 conf[:default_value],
192 conf[:default_value],
165 desc)
193 desc)
166 end
194 end
167 end
195 end
168
196
169 def seed_roles
197 def seed_roles
170 return if Role.find_by_name('admin')
198 return if Role.find_by_name('admin')
171
199
172 role = Role.create(:name => 'admin')
200 role = Role.create(:name => 'admin')
@@ -1,58 +1,73
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 def self.call_import_problem(problem_name,
36 def self.call_import_problem(problem_name,
37 problem_dir,
37 problem_dir,
38 time_limit=1,
38 time_limit=1,
39 memory_limit=32,
39 memory_limit=32,
40 checker_name='text')
40 checker_name='text')
41 if GraderScript.grader_control_enabled?
41 if GraderScript.grader_control_enabled?
42 cur_dir = `pwd`.chomp
42 cur_dir = `pwd`.chomp
43 Dir.chdir(GRADER_ROOT_DIR)
43 Dir.chdir(GRADER_ROOT_DIR)
44
44
45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
47 " -t #{time_limit} -m #{memory_limit}"
47 " -t #{time_limit} -m #{memory_limit}"
48
48
49 output = `#{cmd}`
49 output = `#{cmd}`
50
50
51 Dir.chdir(cur_dir)
51 Dir.chdir(cur_dir)
52 -
52 +
53 return "import CMD: #{cmd}\n" + output
53 return "import CMD: #{cmd}\n" + output
54 end
54 end
55 return ''
55 return ''
56 end
56 end
57
57
58 + def self.call_import_testcase(problem_name)
59 + if GraderScript.grader_control_enabled?
60 + cur_dir = `pwd`.chomp
61 + Dir.chdir(GRADER_ROOT_DIR)
62 +
63 + script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase")
64 + cmd = "#{script_name} #{problem_name}"
65 +
66 + output = `#{cmd}`
67 +
68 + Dir.chdir(cur_dir)
69 + return "Testcase import result:\n" + output
70 + end
71 + end
72 +
58 end
73 end
@@ -1,136 +1,139
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 def import_from_file(tempfile,
11 def import_from_file(tempfile,
12 time_limit,
12 time_limit,
13 memory_limit,
13 memory_limit,
14 checker_name='text',
14 checker_name='text',
15 import_to_db=false)
15 import_to_db=false)
16
16
17 dirname = extract(tempfile)
17 dirname = extract(tempfile)
18 return false if not dirname
18 return false if not dirname
19 if not import_to_db
19 if not import_to_db
20 @log_msg = GraderScript.call_import_problem(@problem.name,
20 @log_msg = GraderScript.call_import_problem(@problem.name,
21 dirname,
21 dirname,
22 time_limit,
22 time_limit,
23 memory_limit,
23 memory_limit,
24 checker_name)
24 checker_name)
25 else
25 else
26 # Import test data to test pairs.
26 # Import test data to test pairs.
27
27
28 @problem.test_pairs.clear
28 @problem.test_pairs.clear
29 if import_test_pairs(dirname)
29 if import_test_pairs(dirname)
30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
32 else
32 else
33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
34 end
34 end
35 end
35 end
36
36
37 @log_msg << import_problem_description(dirname)
37 @log_msg << import_problem_description(dirname)
38 @log_msg << import_problem_pdf(dirname)
38 @log_msg << import_problem_pdf(dirname)
39 @log_msg << import_full_score(dirname)
39 @log_msg << import_full_score(dirname)
40
40
41 + #import test data
42 + @log_msg << GraderScript.call_import_testcase(@problem.name)
43 +
41 return true
44 return true
42 end
45 end
43
46
44 protected
47 protected
45
48
46 def self.long_ext(filename)
49 def self.long_ext(filename)
47 i = filename.index('.')
50 i = filename.index('.')
48 len = filename.length
51 len = filename.length
49 return filename.slice(i..len)
52 return filename.slice(i..len)
50 end
53 end
51
54
52 def extract(tempfile)
55 def extract(tempfile)
53 testdata_filename = save_testdata_file(tempfile)
56 testdata_filename = save_testdata_file(tempfile)
54 ext = TestdataImporter.long_ext(tempfile.original_filename)
57 ext = TestdataImporter.long_ext(tempfile.original_filename)
55
58
56 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
59 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
57 if File.exists? extract_dir
60 if File.exists? extract_dir
58 backup_count = 0
61 backup_count = 0
59 begin
62 begin
60 backup_count += 1
63 backup_count += 1
61 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
64 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
62 end while File.exists? backup_dirname
65 end while File.exists? backup_dirname
63 File.rename(extract_dir, backup_dirname)
66 File.rename(extract_dir, backup_dirname)
64 end
67 end
65 Dir.mkdir extract_dir
68 Dir.mkdir extract_dir
66
69
67 if ext=='.tar.gz' or ext=='.tgz'
70 if ext=='.tar.gz' or ext=='.tgz'
68 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
71 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
69 elsif ext=='.tar'
72 elsif ext=='.tar'
70 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
73 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
71 elsif ext=='.zip'
74 elsif ext=='.zip'
72 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
75 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
73 else
76 else
74 return nil
77 return nil
75 end
78 end
76
79
77 system(cmd)
80 system(cmd)
78
81
79 files = Dir["#{extract_dir}/**/*1*.in"]
82 files = Dir["#{extract_dir}/**/*1*.in"]
80 return nil if files.length==0
83 return nil if files.length==0
81
84
82 File.delete(testdata_filename)
85 File.delete(testdata_filename)
83
86
84 return File.dirname(files[0])
87 return File.dirname(files[0])
85 end
88 end
86
89
87 def save_testdata_file(tempfile)
90 def save_testdata_file(tempfile)
88 ext = TestdataImporter.long_ext(tempfile.original_filename)
91 ext = TestdataImporter.long_ext(tempfile.original_filename)
89 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
92 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
90
93
91 return nil if tempfile==""
94 return nil if tempfile==""
92
95
93 if tempfile.instance_of?(Tempfile)
96 if tempfile.instance_of?(Tempfile)
94 tempfile.close
97 tempfile.close
95 FileUtils.move(tempfile.path,testdata_filename)
98 FileUtils.move(tempfile.path,testdata_filename)
96 else
99 else
97 File.open(testdata_filename, "wb") do |f|
100 File.open(testdata_filename, "wb") do |f|
98 f.write(tempfile.read)
101 f.write(tempfile.read)
99 end
102 end
100 end
103 end
101
104
102 return testdata_filename
105 return testdata_filename
103 end
106 end
104
107
105 def import_test_pairs(dirname)
108 def import_test_pairs(dirname)
106 test_num = 1
109 test_num = 1
107 while FileTest.exists? "#{dirname}/#{test_num}.in"
110 while FileTest.exists? "#{dirname}/#{test_num}.in"
108 in_filename = "#{dirname}/#{test_num}.in"
111 in_filename = "#{dirname}/#{test_num}.in"
109 sol_filename = "#{dirname}/#{test_num}.sol"
112 sol_filename = "#{dirname}/#{test_num}.sol"
110
113
111 break if not FileTest.exists? sol_filename
114 break if not FileTest.exists? sol_filename
112
115
113 test_pair = TestPair.new(:input => open(in_filename).read,
116 test_pair = TestPair.new(:input => open(in_filename).read,
114 :solution => open(sol_filename).read,
117 :solution => open(sol_filename).read,
115 :problem => @problem)
118 :problem => @problem)
116 break if not test_pair.save
119 break if not test_pair.save
117
120
118 test_num += 1
121 test_num += 1
119 end
122 end
120 return test_num > 1
123 return test_num > 1
121 end
124 end
122
125
123 def import_problem_description(dirname)
126 def import_problem_description(dirname)
124 html_files = Dir["#{dirname}/*.html"]
127 html_files = Dir["#{dirname}/*.html"]
125 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
128 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
126 if (html_files.length != 0) or (markdown_files.length != 0)
129 if (html_files.length != 0) or (markdown_files.length != 0)
127 description = @problem.description || Description.new
130 description = @problem.description || Description.new
128
131
129 if html_files.length != 0
132 if html_files.length != 0
130 filename = html_files[0]
133 filename = html_files[0]
131 description.markdowned = false
134 description.markdowned = false
132 else
135 else
133 filename = markdown_files[0]
136 filename = markdown_files[0]
134 description.markdowned = true
137 description.markdowned = true
135 end
138 end
136
139
@@ -1,18 +1,18
1 ENV["RAILS_ENV"] = "test"
1 ENV["RAILS_ENV"] = "test"
2 require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
2 require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
3
3
4 def clear_all_tasks
4 def clear_all_tasks
5 - Task.find(:all).each do |task|
5 + Task.all.each do |task|
6 task.destroy
6 task.destroy
7 end
7 end
8 end
8 end
9
9
10
10
11 clear_all_tasks
11 clear_all_tasks
12
12
13 (1..1000).each do |i|
13 (1..1000).each do |i|
14 Task.create(:id => i,
14 Task.create(:id => i,
15 :submission_id => i,
15 :submission_id => i,
16 :status => Task::STATUS_INQUEUE)
16 :status => Task::STATUS_INQUEUE)
17 end
17 end
18
18
@@ -1,14 +1,13
1 ENV["RAILS_ENV"] = "test"
1 ENV["RAILS_ENV"] = "test"
2 require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
2 require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
3
3
4 def clear_all_tasks
4 def clear_all_tasks
5 - Task.find(:all).each do |task|
5 + Task.all.each do |task|
6 task.destroy
6 task.destroy
7 end
7 end
8 end
8 end
9
9
10 - puts Task.find(:all,
10 + puts Task.where(status: Task::STATUS_COMPLETE).length
11 - :conditions => {:status => Task::STATUS_COMPLETE}).length
12
11
13 clear_all_tasks
12 clear_all_tasks
14
13
@@ -1,5 +1,36
1 - # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
1 + Language_1:
2 - one:
2 + name: c
3 - id: 1
3 + pretty_name: C
4 - two:
4 + ext: c
5 - id: 2
5 + common_ext: c
6 +
7 + Language_2:
8 + name: cpp
9 + pretty_name: C++
10 + ext: cpp
11 + common_ext: cpp,cc
12 +
13 + Language_3:
14 + name: pas
15 + pretty_name: Pascal
16 + ext: pas
17 + common_ext: pas
18 +
19 + Language_4:
20 + name: ruby
21 + pretty_name: Ruby
22 + ext: rb
23 + common_ext: rb
24 +
25 + Language_5:
26 + name: python
27 + pretty_name: Python
28 + ext: py
29 + common_ext: py
30 +
31 + Language_6:
32 + name: java
33 + pretty_name: Java
34 + ext: java
35 + common_ext: java
36 +
@@ -1,38 +1,38
1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
2
3 <%
3 <%
4 User.public_class_method :encrypt
4 User.public_class_method :encrypt
5
5
6 salt = "abc"
6 salt = "abc"
7 %>
7 %>
8
8
9 john:
9 john:
10 login: john
10 login: john
11 full_name: john
11 full_name: john
12 hashed_password: <%= User.encrypt("hello",salt) %>
12 hashed_password: <%= User.encrypt("hello",salt) %>
13 salt: <%= salt %>
13 salt: <%= salt %>
14 activated: true
14 activated: true
15
15
16 - mary:
16 + admin:
17 - login: mary
17 + login: admin
18 - full_name: mary
18 + full_name: admin
19 - hashed_password: <%= User.encrypt("goodbye",salt) %>
19 + hashed_password: <%= User.encrypt("admin",salt) %>
20 salt: <%= salt %>
20 salt: <%= salt %>
21 roles: admin
21 roles: admin
22 activated: true
22 activated: true
23
23
24 james:
24 james:
25 login: james
25 login: james
26 full_name: James
26 full_name: James
27 hashed_password: <%= User.encrypt("morning",salt) %>
27 hashed_password: <%= User.encrypt("morning",salt) %>
28 salt: <%= salt %>
28 salt: <%= salt %>
29 contests: contest_a
29 contests: contest_a
30 activated: true
30 activated: true
31
31
32 jack:
32 jack:
33 login: jack
33 login: jack
34 full_name: Jack
34 full_name: Jack
35 hashed_password: <%= User.encrypt("morning",salt) %>
35 hashed_password: <%= User.encrypt("morning",salt) %>
36 salt: <%= salt %>
36 salt: <%= salt %>
37 contests: contest_a, contest_b
37 contests: contest_a, contest_b
38 activated: true
38 activated: true
@@ -1,16 +1,27
1 ENV["RAILS_ENV"] = "test"
1 ENV["RAILS_ENV"] = "test"
2 require File.expand_path('../../config/environment', __FILE__)
2 require File.expand_path('../../config/environment', __FILE__)
3 require 'rails/test_help'
3 require 'rails/test_help'
4
4
5 + #reporter for beautiful result
6 + require "minitest/reporters"
7 + Minitest::Reporters.use!
8 +
9 + module SignInHelper
10 + def sign_in_as(user,password)
11 + post login_login_path, {login: user, password: password }
12 + end
13 + end
14 +
5 class ActiveSupport::TestCase
15 class ActiveSupport::TestCase
16 + include SignInHelper
6 # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
17 # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
7 #
18 #
8 # Note: You'll currently still have to declare fixtures explicitly in integration tests
19 # Note: You'll currently still have to declare fixtures explicitly in integration tests
9 # -- they do not yet inherit this setting
20 # -- they do not yet inherit this setting
10 fixtures :all
21 fixtures :all
11
22
12 # Add more helper methods to be used by all tests here...
23 # Add more helper methods to be used by all tests here...
13
24
14 self.use_transactional_fixtures = true
25 self.use_transactional_fixtures = true
15 self.use_instantiated_fixtures = false
26 self.use_instantiated_fixtures = false
16 end
27 end
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
You need to be logged in to leave comments. Login now