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