Description:
modernize problem
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r876:8cd1c60e1ef0 - - The requested commit is too big and content was truncated. 22 files changed. Show full diff

@@ -0,0 +1,6
1 + %h1 New Problem
2 +
3 + = render 'form', problem: @problem
4 + .row.my-3
5 + .col-md-4
6 + = link_to 'Back', problems_path, class: 'btn btn-secondary'
@@ -0,0 +1,57
1 + # This migration comes from active_storage (originally 20170806125915)
2 + class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
3 + def change
4 + # Use Active Record's configured type for primary and foreign keys
5 + primary_key_type, foreign_key_type = primary_and_foreign_key_types
6 +
7 + create_table :active_storage_blobs, id: primary_key_type do |t|
8 + t.string :key, null: false
9 + t.string :filename, null: false
10 + t.string :content_type
11 + t.text :metadata
12 + t.string :service_name, null: false
13 + t.bigint :byte_size, null: false
14 + t.string :checksum
15 +
16 + if connection.supports_datetime_with_precision?
17 + t.datetime :created_at, precision: 6, null: false
18 + else
19 + t.datetime :created_at, null: false
20 + end
21 +
22 + t.index [ :key ], unique: true
23 + end
24 +
25 + create_table :active_storage_attachments, id: primary_key_type do |t|
26 + t.string :name, null: false
27 + t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28 + t.references :blob, null: false, type: foreign_key_type
29 +
30 + if connection.supports_datetime_with_precision?
31 + t.datetime :created_at, precision: 6, null: false
32 + else
33 + t.datetime :created_at, null: false
34 + end
35 +
36 + t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37 + t.foreign_key :active_storage_blobs, column: :blob_id
38 + end
39 +
40 + create_table :active_storage_variant_records, id: primary_key_type do |t|
41 + t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42 + t.string :variation_digest, null: false
43 +
44 + t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45 + t.foreign_key :active_storage_blobs, column: :blob_id
46 + end
47 + end
48 +
49 + private
50 + def primary_and_foreign_key_types
51 + config = Rails.configuration.generators
52 + setting = config.options[config.orm][:primary_key_type]
53 + primary_key_type = setting || :primary_key
54 + foreign_key_type = setting || :bigint
55 + [primary_key_type, foreign_key_type]
56 + end
57 + end
@@ -0,0 +1,6
1 + class AddDescriptionToProblems < ActiveRecord::Migration[7.0]
2 + def change
3 + add_column :problems, :description, :text
4 + add_column :problems, :markdown, :boolean
5 + end
6 + end
@@ -0,0 +1,21
1 + Problem.all.each do |p|
2 + next unless p.description_filename
3 + basename, ext = p.description_filename.split('.')
4 + filename = "#{Problem.download_file_basedir}/#{p.id}/#{basename}.#{ext}"
5 +
6 + if File.exists? filename
7 + p.statement.attach io: File.open(filename), filename: "#{basename}.#{ext}"
8 + puts "#{p.id}: OK"
9 + else
10 + puts "#{p.id}: #{p.name} #{filename} ERROR"
11 + end
12 +
13 + d = Description.where(id: p.description_id).first
14 + if d
15 + p.description = d.body
16 + p.markdown = d.markdowned
17 + end
18 + p.save
19 +
20 +
21 + end
@@ -1,37 +1,38
1 # See http://help.github.com/ignore-files/ for more about ignoring files.
1 # See http://help.github.com/ignore-files/ for more about ignoring files.
2 #
2 #
3 # If you find yourself ignoring temporary files generated by your text editor
3 # If you find yourself ignoring temporary files generated by your text editor
4 # or operating system, you probably want to add a global ignore instead:
4 # or operating system, you probably want to add a global ignore instead:
5 # git config --global core.excludesfile ~/.gitignore_global
5 # git config --global core.excludesfile ~/.gitignore_global
6
6
7 # Ignore bundler config
7 # Ignore bundler config
8 /.bundle
8 /.bundle
9
9
10 # Ignore the default SQLite database.
10 # Ignore the default SQLite database.
11 /db/*.sqlite3
11 /db/*.sqlite3
12
12
13 # Ignore all logfiles and tempfiles.
13 # Ignore all logfiles and tempfiles.
14 /log/*.log
14 /log/*.log
15 /tmp
15 /tmp
16
16
17 *~
17 *~
18
18
19 /vendor/plugins/rails_upgrade
19 /vendor/plugins/rails_upgrade
20
20
21 #ignore public assets???
21 #ignore public assets???
22 /public/assets
22 /public/assets
23 /public
23 /public
24
24
25 /data
25 /data
26
26
27 #ignore .orig and .swp
27 #ignore .orig and .swp
28 *.orig
28 *.orig
29 *.swp
29 *.swp
30
30
31 #ignore rvm setting file
31 #ignore rvm setting file
32 #.ruby-gemset
32 #.ruby-gemset
33 #.ruby-version
33 #.ruby-version
34
34
35 /config/secrets.yml
35 /config/secrets.yml
36
36
37 /.byebug_history
37 /.byebug_history
38 + /storage/*
@@ -1,123 +1,124
1 source 'https://rubygems.org'
1 source 'https://rubygems.org'
2 git_source(:github) { |repo| "https://github.com/#{repo}.git" }
2 git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
4 ruby '3.1.2'
4 ruby '3.1.2'
5
5
6 #rails
6 #rails
7 gem 'rails', '~>7.0'
7 gem 'rails', '~>7.0'
8
8
9 # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
9 # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
10 gem "sprockets-rails"
10 gem "sprockets-rails"
11
11
12 gem 'puma'
12 gem 'puma'
13
13
14 # Reduces boot times through caching; required in config/boot.rb
14 # Reduces boot times through caching; required in config/boot.rb
15 gem 'bootsnap', require: false
15 gem 'bootsnap', require: false
16
16
17 # Bundle edge Rails instead:
17 # Bundle edge Rails instead:
18 # gem 'rails', :git => 'git://github.com/rails/rails.git'
18 # gem 'rails', :git => 'git://github.com/rails/rails.git'
19
19
20 #---------------- database ---------------------
20 #---------------- database ---------------------
21 #the database
21 #the database
22 gem 'mysql2'
22 gem 'mysql2'
23 #for testing
23 #for testing
24 gem 'sqlite3'
24 gem 'sqlite3'
25 #gem 'rails-controller-testing'
25 #gem 'rails-controller-testing'
26 #for dumping database into yaml
26 #for dumping database into yaml
27 #gem 'yaml_db'
27 #gem 'yaml_db'
28
28
29
29
30 #------------- assset pipeline -----------------
30 #------------- assset pipeline -----------------
31 # Gems used only for assets and not required
31 # Gems used only for assets and not required
32 # in production environments by default.
32 # in production environments by default.
33 #sass-rails is depricated
33 #sass-rails is depricated
34 #gem 'sass-rails'
34 #gem 'sass-rails'
35 gem 'sassc-rails'
35 gem 'sassc-rails'
36 gem 'coffee-rails'
36 gem 'coffee-rails'
37 gem 'material_icons'
37 gem 'material_icons'
38
38
39 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
39 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
40 # gem 'therubyracer', :platforms => :ruby
40 # gem 'therubyracer', :platforms => :ruby
41
41
42 gem "importmap-rails", "~> 1.1"
42 gem "importmap-rails", "~> 1.1"
43 # gem 'uglifier'
43 # gem 'uglifier'
44
44
45 gem 'haml'
45 gem 'haml'
46 gem 'haml-rails'
46 gem 'haml-rails'
47
47
48 # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
48 # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
49 #gem 'turbolinks', '~> 5'
49 #gem 'turbolinks', '~> 5'
50 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
50 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
51 gem 'jbuilder'
51 gem 'jbuilder'
52
52
53
53
54 #in-place editor
54 #in-place editor
55 gem 'best_in_place', git: "https://github.com/mmotherwell/best_in_place"
55 gem 'best_in_place', git: "https://github.com/mmotherwell/best_in_place"
56
56
57 # jquery addition
57 # jquery addition
58 gem 'jquery-rails'
58 gem 'jquery-rails'
59 #gem 'jquery-ui-rails'
59 #gem 'jquery-ui-rails'
60 #gem 'jquery-timepicker-addon-rails'
60 #gem 'jquery-timepicker-addon-rails'
61 #gem 'jquery-tablesorter'
61 #gem 'jquery-tablesorter'
62 #gem 'jquery-countdown-rails'
62 #gem 'jquery-countdown-rails'
63
63
64 #syntax highlighter
64 #syntax highlighter
65 gem 'rouge'
65 gem 'rouge'
66
66
67 #bootstrap add-ons
67 #bootstrap add-ons
68 #gem 'bootstrap-sass', '~> 3.4.1'
68 #gem 'bootstrap-sass', '~> 3.4.1'
69 gem 'bootstrap', '~> 5.2'
69 gem 'bootstrap', '~> 5.2'
70 #gem 'bootstrap-switch-rails'
70 #gem 'bootstrap-switch-rails'
71 #gem 'bootstrap-toggle-rails'
71 #gem 'bootstrap-toggle-rails'
72 #gem 'autoprefixer-rails'
72 #gem 'autoprefixer-rails'
73 gem 'momentjs-rails'
73 gem 'momentjs-rails'
74 #gem 'rails_bootstrap_sortable'
74 #gem 'rails_bootstrap_sortable'
75 #gem 'bootstrap-datepicker-rails'
75 #gem 'bootstrap-datepicker-rails'
76 #gem 'bootstrap3-datetimepicker-rails', '~> 4.17.47'
76 #gem 'bootstrap3-datetimepicker-rails', '~> 4.17.47'
77 #gem 'jquery-datatables-rails'
77 #gem 'jquery-datatables-rails'
78
78
79 #----------- user interface -----------------
79 #----------- user interface -----------------
80 - gem 'simple_form'
80 + gem 'simple_form', git: 'https://github.com/heartcombo/simple_form', ref: '31fe255'
81 +
81 #select 2
82 #select 2
82 #gem 'select2-rails'
83 #gem 'select2-rails'
83 #ace editor
84 #ace editor
84 gem 'ace-rails-ap'
85 gem 'ace-rails-ap'
85 #paginator
86 #paginator
86 #gem 'will_paginate', '~> 3.0.7'
87 #gem 'will_paginate', '~> 3.0.7'
87
88
88 gem 'mail'
89 gem 'mail'
89 gem 'rdiscount' #markdown
90 gem 'rdiscount' #markdown
90
91
91
92
92 #---------------- testiing -----------------------
93 #---------------- testiing -----------------------
93 gem 'minitest-reporters'
94 gem 'minitest-reporters'
94
95
95 #---------------- for console --------------------
96 #---------------- for console --------------------
96 gem 'fuzzy-string-match'
97 gem 'fuzzy-string-match'
97
98
98
99
99 group :development, :test do
100 group :development, :test do
100 # Call 'byebug' anywhere in the code to stop execution and get a debugger console
101 # Call 'byebug' anywhere in the code to stop execution and get a debugger console
101 gem 'byebug'
102 gem 'byebug'
102 end
103 end
103
104
104 group :development do
105 group :development do
105 # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
106 # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
106 gem 'web-console', '>= 3.3.0'
107 gem 'web-console', '>= 3.3.0'
107 gem 'listen', '>= 3.0.5', '< 3.2'
108 gem 'listen', '>= 3.0.5', '< 3.2'
108 # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
109 # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
109 gem 'spring'
110 gem 'spring'
110 gem 'spring-watcher-listen', '~> 2.0.0'
111 gem 'spring-watcher-listen', '~> 2.0.0'
111
112
112 # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
113 # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
113 # gem "rack-mini-profiler"
114 # gem "rack-mini-profiler"
114 end
115 end
115
116
116 group :test do
117 group :test do
117 # Adds support for Capybara system testing and selenium driver
118 # Adds support for Capybara system testing and selenium driver
118 gem 'capybara'
119 gem 'capybara'
119 gem 'selenium-webdriver'
120 gem 'selenium-webdriver'
120 gem 'webdrivers'
121 gem 'webdrivers'
121 end
122 end
122
123
123
124
@@ -1,96 +1,105
1 + GIT
2 + remote: https://github.com/heartcombo/simple_form
3 + revision: 31fe25504771bd6cd425b585a4e0ed652fba4521
4 + ref: 31fe255
5 + specs:
6 + simple_form (5.1.0)
7 + actionpack (>= 5.2)
8 + activemodel (>= 5.2)
9 +
1 GIT
10 GIT
2 remote: https://github.com/mmotherwell/best_in_place
11 remote: https://github.com/mmotherwell/best_in_place
3 revision: 88eb3052623a9a6cd346864d2aca05021c2f80d0
12 revision: 88eb3052623a9a6cd346864d2aca05021c2f80d0
4 specs:
13 specs:
5 best_in_place (3.1.1)
14 best_in_place (3.1.1)
6 actionpack (>= 3.2)
15 actionpack (>= 3.2)
7 railties (>= 3.2)
16 railties (>= 3.2)
8
17
9 GEM
18 GEM
10 remote: https://rubygems.org/
19 remote: https://rubygems.org/
11 specs:
20 specs:
12 RubyInline (3.12.6)
21 RubyInline (3.12.6)
13 ZenTest (~> 4.3)
22 ZenTest (~> 4.3)
14 ZenTest (4.12.1)
23 ZenTest (4.12.1)
15 ace-rails-ap (4.4)
24 ace-rails-ap (4.4)
16 actioncable (7.0.4)
25 actioncable (7.0.4)
17 actionpack (= 7.0.4)
26 actionpack (= 7.0.4)
18 activesupport (= 7.0.4)
27 activesupport (= 7.0.4)
19 nio4r (~> 2.0)
28 nio4r (~> 2.0)
20 websocket-driver (>= 0.6.1)
29 websocket-driver (>= 0.6.1)
21 actionmailbox (7.0.4)
30 actionmailbox (7.0.4)
22 actionpack (= 7.0.4)
31 actionpack (= 7.0.4)
23 activejob (= 7.0.4)
32 activejob (= 7.0.4)
24 activerecord (= 7.0.4)
33 activerecord (= 7.0.4)
25 activestorage (= 7.0.4)
34 activestorage (= 7.0.4)
26 activesupport (= 7.0.4)
35 activesupport (= 7.0.4)
27 mail (>= 2.7.1)
36 mail (>= 2.7.1)
28 net-imap
37 net-imap
29 net-pop
38 net-pop
30 net-smtp
39 net-smtp
31 actionmailer (7.0.4)
40 actionmailer (7.0.4)
32 actionpack (= 7.0.4)
41 actionpack (= 7.0.4)
33 actionview (= 7.0.4)
42 actionview (= 7.0.4)
34 activejob (= 7.0.4)
43 activejob (= 7.0.4)
35 activesupport (= 7.0.4)
44 activesupport (= 7.0.4)
36 mail (~> 2.5, >= 2.5.4)
45 mail (~> 2.5, >= 2.5.4)
37 net-imap
46 net-imap
38 net-pop
47 net-pop
39 net-smtp
48 net-smtp
40 rails-dom-testing (~> 2.0)
49 rails-dom-testing (~> 2.0)
41 actionpack (7.0.4)
50 actionpack (7.0.4)
42 actionview (= 7.0.4)
51 actionview (= 7.0.4)
43 activesupport (= 7.0.4)
52 activesupport (= 7.0.4)
44 rack (~> 2.0, >= 2.2.0)
53 rack (~> 2.0, >= 2.2.0)
45 rack-test (>= 0.6.3)
54 rack-test (>= 0.6.3)
46 rails-dom-testing (~> 2.0)
55 rails-dom-testing (~> 2.0)
47 rails-html-sanitizer (~> 1.0, >= 1.2.0)
56 rails-html-sanitizer (~> 1.0, >= 1.2.0)
48 actiontext (7.0.4)
57 actiontext (7.0.4)
49 actionpack (= 7.0.4)
58 actionpack (= 7.0.4)
50 activerecord (= 7.0.4)
59 activerecord (= 7.0.4)
51 activestorage (= 7.0.4)
60 activestorage (= 7.0.4)
52 activesupport (= 7.0.4)
61 activesupport (= 7.0.4)
53 globalid (>= 0.6.0)
62 globalid (>= 0.6.0)
54 nokogiri (>= 1.8.5)
63 nokogiri (>= 1.8.5)
55 actionview (7.0.4)
64 actionview (7.0.4)
56 activesupport (= 7.0.4)
65 activesupport (= 7.0.4)
57 builder (~> 3.1)
66 builder (~> 3.1)
58 erubi (~> 1.4)
67 erubi (~> 1.4)
59 rails-dom-testing (~> 2.0)
68 rails-dom-testing (~> 2.0)
60 rails-html-sanitizer (~> 1.1, >= 1.2.0)
69 rails-html-sanitizer (~> 1.1, >= 1.2.0)
61 activejob (7.0.4)
70 activejob (7.0.4)
62 activesupport (= 7.0.4)
71 activesupport (= 7.0.4)
63 globalid (>= 0.3.6)
72 globalid (>= 0.3.6)
64 activemodel (7.0.4)
73 activemodel (7.0.4)
65 activesupport (= 7.0.4)
74 activesupport (= 7.0.4)
66 activerecord (7.0.4)
75 activerecord (7.0.4)
67 activemodel (= 7.0.4)
76 activemodel (= 7.0.4)
68 activesupport (= 7.0.4)
77 activesupport (= 7.0.4)
69 activestorage (7.0.4)
78 activestorage (7.0.4)
70 actionpack (= 7.0.4)
79 actionpack (= 7.0.4)
71 activejob (= 7.0.4)
80 activejob (= 7.0.4)
72 activerecord (= 7.0.4)
81 activerecord (= 7.0.4)
73 activesupport (= 7.0.4)
82 activesupport (= 7.0.4)
74 marcel (~> 1.0)
83 marcel (~> 1.0)
75 mini_mime (>= 1.1.0)
84 mini_mime (>= 1.1.0)
76 activesupport (7.0.4)
85 activesupport (7.0.4)
77 concurrent-ruby (~> 1.0, >= 1.0.2)
86 concurrent-ruby (~> 1.0, >= 1.0.2)
78 i18n (>= 1.6, < 2)
87 i18n (>= 1.6, < 2)
79 minitest (>= 5.1)
88 minitest (>= 5.1)
80 tzinfo (~> 2.0)
89 tzinfo (~> 2.0)
81 addressable (2.8.1)
90 addressable (2.8.1)
82 public_suffix (>= 2.0.2, < 6.0)
91 public_suffix (>= 2.0.2, < 6.0)
83 ansi (1.5.0)
92 ansi (1.5.0)
84 autoprefixer-rails (10.4.7.0)
93 autoprefixer-rails (10.4.7.0)
85 execjs (~> 2)
94 execjs (~> 2)
86 bindex (0.8.1)
95 bindex (0.8.1)
87 bootsnap (1.13.0)
96 bootsnap (1.13.0)
88 msgpack (~> 1.2)
97 msgpack (~> 1.2)
89 bootstrap (5.2.1)
98 bootstrap (5.2.1)
90 autoprefixer-rails (>= 9.1.0)
99 autoprefixer-rails (>= 9.1.0)
91 popper_js (>= 2.11.6, < 3)
100 popper_js (>= 2.11.6, < 3)
92 sassc-rails (>= 2.0.0)
101 sassc-rails (>= 2.0.0)
93 builder (3.2.4)
102 builder (3.2.4)
94 byebug (11.1.3)
103 byebug (11.1.3)
95 capybara (3.37.1)
104 capybara (3.37.1)
96 addressable
105 addressable
@@ -154,177 +163,174
154 nokogiri (>= 1.5.9)
163 nokogiri (>= 1.5.9)
155 mail (2.7.1)
164 mail (2.7.1)
156 mini_mime (>= 0.1.1)
165 mini_mime (>= 0.1.1)
157 marcel (1.0.2)
166 marcel (1.0.2)
158 material_icons (2.2.1)
167 material_icons (2.2.1)
159 railties (>= 3.2)
168 railties (>= 3.2)
160 matrix (0.4.2)
169 matrix (0.4.2)
161 method_source (1.0.0)
170 method_source (1.0.0)
162 mini_mime (1.1.2)
171 mini_mime (1.1.2)
163 minitest (5.16.3)
172 minitest (5.16.3)
164 minitest-reporters (1.5.0)
173 minitest-reporters (1.5.0)
165 ansi
174 ansi
166 builder
175 builder
167 minitest (>= 5.0)
176 minitest (>= 5.0)
168 ruby-progressbar
177 ruby-progressbar
169 momentjs-rails (2.29.4.1)
178 momentjs-rails (2.29.4.1)
170 railties (>= 3.1)
179 railties (>= 3.1)
171 msgpack (1.5.6)
180 msgpack (1.5.6)
172 mysql2 (0.5.4)
181 mysql2 (0.5.4)
173 net-imap (0.2.3)
182 net-imap (0.2.3)
174 digest
183 digest
175 net-protocol
184 net-protocol
176 strscan
185 strscan
177 net-pop (0.1.1)
186 net-pop (0.1.1)
178 digest
187 digest
179 net-protocol
188 net-protocol
180 timeout
189 timeout
181 net-protocol (0.1.3)
190 net-protocol (0.1.3)
182 timeout
191 timeout
183 net-smtp (0.3.1)
192 net-smtp (0.3.1)
184 digest
193 digest
185 net-protocol
194 net-protocol
186 timeout
195 timeout
187 nio4r (2.5.8)
196 nio4r (2.5.8)
188 nokogiri (1.13.8-x86_64-linux)
197 nokogiri (1.13.8-x86_64-linux)
189 racc (~> 1.4)
198 racc (~> 1.4)
190 popper_js (2.11.6)
199 popper_js (2.11.6)
191 public_suffix (5.0.0)
200 public_suffix (5.0.0)
192 puma (5.6.5)
201 puma (5.6.5)
193 nio4r (~> 2.0)
202 nio4r (~> 2.0)
194 racc (1.6.0)
203 racc (1.6.0)
195 rack (2.2.4)
204 rack (2.2.4)
196 rack-test (2.0.2)
205 rack-test (2.0.2)
197 rack (>= 1.3)
206 rack (>= 1.3)
198 rails (7.0.4)
207 rails (7.0.4)
199 actioncable (= 7.0.4)
208 actioncable (= 7.0.4)
200 actionmailbox (= 7.0.4)
209 actionmailbox (= 7.0.4)
201 actionmailer (= 7.0.4)
210 actionmailer (= 7.0.4)
202 actionpack (= 7.0.4)
211 actionpack (= 7.0.4)
203 actiontext (= 7.0.4)
212 actiontext (= 7.0.4)
204 actionview (= 7.0.4)
213 actionview (= 7.0.4)
205 activejob (= 7.0.4)
214 activejob (= 7.0.4)
206 activemodel (= 7.0.4)
215 activemodel (= 7.0.4)
207 activerecord (= 7.0.4)
216 activerecord (= 7.0.4)
208 activestorage (= 7.0.4)
217 activestorage (= 7.0.4)
209 activesupport (= 7.0.4)
218 activesupport (= 7.0.4)
210 bundler (>= 1.15.0)
219 bundler (>= 1.15.0)
211 railties (= 7.0.4)
220 railties (= 7.0.4)
212 rails-dom-testing (2.0.3)
221 rails-dom-testing (2.0.3)
213 activesupport (>= 4.2.0)
222 activesupport (>= 4.2.0)
214 nokogiri (>= 1.6)
223 nokogiri (>= 1.6)
215 rails-html-sanitizer (1.4.3)
224 rails-html-sanitizer (1.4.3)
216 loofah (~> 2.3)
225 loofah (~> 2.3)
217 railties (7.0.4)
226 railties (7.0.4)
218 actionpack (= 7.0.4)
227 actionpack (= 7.0.4)
219 activesupport (= 7.0.4)
228 activesupport (= 7.0.4)
220 method_source
229 method_source
221 rake (>= 12.2)
230 rake (>= 12.2)
222 thor (~> 1.0)
231 thor (~> 1.0)
223 zeitwerk (~> 2.5)
232 zeitwerk (~> 2.5)
224 rake (13.0.6)
233 rake (13.0.6)
225 rb-fsevent (0.11.2)
234 rb-fsevent (0.11.2)
226 rb-inotify (0.10.1)
235 rb-inotify (0.10.1)
227 ffi (~> 1.0)
236 ffi (~> 1.0)
228 rdiscount (2.2.0.2)
237 rdiscount (2.2.0.2)
229 regexp_parser (2.5.0)
238 regexp_parser (2.5.0)
230 rexml (3.2.5)
239 rexml (3.2.5)
231 rouge (4.0.0)
240 rouge (4.0.0)
232 ruby-progressbar (1.11.0)
241 ruby-progressbar (1.11.0)
233 ruby_parser (3.19.1)
242 ruby_parser (3.19.1)
234 sexp_processor (~> 4.16)
243 sexp_processor (~> 4.16)
235 rubyzip (2.3.2)
244 rubyzip (2.3.2)
236 sassc (2.4.0)
245 sassc (2.4.0)
237 ffi (~> 1.9)
246 ffi (~> 1.9)
238 sassc-rails (2.1.2)
247 sassc-rails (2.1.2)
239 railties (>= 4.0.0)
248 railties (>= 4.0.0)
240 sassc (>= 2.0)
249 sassc (>= 2.0)
241 sprockets (> 3.0)
250 sprockets (> 3.0)
242 sprockets-rails
251 sprockets-rails
243 tilt
252 tilt
244 selenium-webdriver (4.4.0)
253 selenium-webdriver (4.4.0)
245 childprocess (>= 0.5, < 5.0)
254 childprocess (>= 0.5, < 5.0)
246 rexml (~> 3.2, >= 3.2.5)
255 rexml (~> 3.2, >= 3.2.5)
247 rubyzip (>= 1.2.2, < 3.0)
256 rubyzip (>= 1.2.2, < 3.0)
248 websocket (~> 1.0)
257 websocket (~> 1.0)
249 sexp_processor (4.16.1)
258 sexp_processor (4.16.1)
250 - simple_form (5.1.0)
251 - actionpack (>= 5.2)
252 - activemodel (>= 5.2)
253 spring (2.1.1)
259 spring (2.1.1)
254 spring-watcher-listen (2.0.1)
260 spring-watcher-listen (2.0.1)
255 listen (>= 2.7, < 4.0)
261 listen (>= 2.7, < 4.0)
256 spring (>= 1.2, < 3.0)
262 spring (>= 1.2, < 3.0)
257 sprockets (4.1.1)
263 sprockets (4.1.1)
258 concurrent-ruby (~> 1.0)
264 concurrent-ruby (~> 1.0)
259 rack (> 1, < 3)
265 rack (> 1, < 3)
260 sprockets-rails (3.4.2)
266 sprockets-rails (3.4.2)
261 actionpack (>= 5.2)
267 actionpack (>= 5.2)
262 activesupport (>= 5.2)
268 activesupport (>= 5.2)
263 sprockets (>= 3.0.0)
269 sprockets (>= 3.0.0)
264 sqlite3 (1.5.0-x86_64-linux)
270 sqlite3 (1.5.0-x86_64-linux)
265 strscan (3.0.4)
271 strscan (3.0.4)
266 temple (0.8.2)
272 temple (0.8.2)
267 thor (1.2.1)
273 thor (1.2.1)
268 tilt (2.0.11)
274 tilt (2.0.11)
269 timeout (0.3.0)
275 timeout (0.3.0)
270 tzinfo (2.0.5)
276 tzinfo (2.0.5)
271 concurrent-ruby (~> 1.0)
277 concurrent-ruby (~> 1.0)
272 web-console (4.2.0)
278 web-console (4.2.0)
273 actionview (>= 6.0.0)
279 actionview (>= 6.0.0)
274 activemodel (>= 6.0.0)
280 activemodel (>= 6.0.0)
275 bindex (>= 0.4.0)
281 bindex (>= 0.4.0)
276 railties (>= 6.0.0)
282 railties (>= 6.0.0)
277 webdrivers (5.1.0)
283 webdrivers (5.1.0)
278 nokogiri (~> 1.6)
284 nokogiri (~> 1.6)
279 rubyzip (>= 1.3.0)
285 rubyzip (>= 1.3.0)
280 selenium-webdriver (~> 4.0)
286 selenium-webdriver (~> 4.0)
281 websocket (1.2.9)
287 websocket (1.2.9)
282 websocket-driver (0.7.5)
288 websocket-driver (0.7.5)
283 websocket-extensions (>= 0.1.0)
289 websocket-extensions (>= 0.1.0)
284 websocket-extensions (0.1.5)
290 websocket-extensions (0.1.5)
285 xpath (3.2.0)
291 xpath (3.2.0)
286 nokogiri (~> 1.8)
292 nokogiri (~> 1.8)
287 zeitwerk (2.6.0)
293 zeitwerk (2.6.0)
288
294
289 PLATFORMS
295 PLATFORMS
290 x86_64-linux
296 x86_64-linux
291
297
292 DEPENDENCIES
298 DEPENDENCIES
293 ace-rails-ap
299 ace-rails-ap
294 best_in_place!
300 best_in_place!
295 bootsnap
301 bootsnap
296 bootstrap (~> 5.2)
302 bootstrap (~> 5.2)
297 byebug
303 byebug
298 capybara
304 capybara
299 coffee-rails
305 coffee-rails
300 fuzzy-string-match
306 fuzzy-string-match
301 haml
307 haml
302 haml-rails
308 haml-rails
303 importmap-rails (~> 1.1)
309 importmap-rails (~> 1.1)
304 jbuilder
310 jbuilder
305 jquery-rails
311 jquery-rails
306 listen (>= 3.0.5, < 3.2)
312 listen (>= 3.0.5, < 3.2)
307 mail
313 mail
308 material_icons
314 material_icons
309 minitest-reporters
315 minitest-reporters
310 momentjs-rails
316 momentjs-rails
311 mysql2
317 mysql2
312 puma
318 puma
313 rails (~> 7.0)
319 rails (~> 7.0)
314 rdiscount
320 rdiscount
315 rouge
321 rouge
316 sassc-rails
322 sassc-rails
317 selenium-webdriver
323 selenium-webdriver
318 - simple_form
324 + simple_form!
319 spring
325 spring
320 spring-watcher-listen (~> 2.0.0)
326 spring-watcher-listen (~> 2.0.0)
321 sprockets-rails
327 sprockets-rails
322 sqlite3
328 sqlite3
323 web-console (>= 3.3.0)
329 web-console (>= 3.3.0)
324 webdrivers
330 webdrivers
325
331
326 RUBY VERSION
332 RUBY VERSION
327 ruby 3.1.2p20
333 ruby 3.1.2p20
328
334
329 BUNDLED WITH
335 BUNDLED WITH
330 2.3.22
336 2.3.22
@@ -1,161 +1,157
1
1
2 .secondnavbar {
2 .secondnavbar {
3 top: 50px;
3 top: 50px;
4 }
4 }
5
5
6
6
7 //for google material design
7 //for google material design
8 .mi-bs {
8 .mi-bs {
9 vertical-align: middle;
9 vertical-align: middle;
10 position: relative;
10 position: relative;
11 top: -3px;
11 top: -3px;
12 }
12 }
13
13
14 // --------------- bootstrap file upload ----------------------
14 // --------------- bootstrap file upload ----------------------
15 .btn-file {
15 .btn-file {
16 position: relative;
16 position: relative;
17 overflow: hidden;
17 overflow: hidden;
18 }
18 }
19
19
20 .btn-file input[type=file] {
20 .btn-file input[type=file] {
21 position: absolute;
21 position: absolute;
22 top: 0;
22 top: 0;
23 right: 0;
23 right: 0;
24 min-width: 100%;
24 min-width: 100%;
25 min-height: 100%;
25 min-height: 100%;
26 font-size: 100px;
26 font-size: 100px;
27 text-align: right;
27 text-align: right;
28 filter: alpha(opacity = 0);
28 filter: alpha(opacity = 0);
29 opacity: 0;
29 opacity: 0;
30 outline: none;
30 outline: none;
31 background: white;
31 background: white;
32 cursor: inherit;
32 cursor: inherit;
33 display: block;
33 display: block;
34 }
34 }
35
35
36 body {
36 body {
37 //font-size: 13px
37 //font-size: 13px
38 font-family: 'Noto Sans Thai', Tahoma, "sans-serif";
38 font-family: 'Noto Sans Thai', Tahoma, "sans-serif";
39 margin: 10px;
39 margin: 10px;
40 padding: 10px;
40 padding: 10px;
41 padding-top: 60px;
41 padding-top: 60px;
42 }
42 }
43
43
44 // ------------------ bootstrap sortable --------------------
44 // ------------------ bootstrap sortable --------------------
45 table.sortable th {
45 table.sortable th {
46 padding-right: 20px !important;
46 padding-right: 20px !important;
47
47
48 span.sign {
48 span.sign {
49 right: (-15px) !important;
49 right: (-15px) !important;
50 }
50 }
51
51
52 &.text-right {
52 &.text-right {
53 padding-left: 20px !important;
53 padding-left: 20px !important;
54 padding-right: 8px !important;
54 padding-right: 8px !important;
55
55
56 &:after, span.sign {
56 &:after, span.sign {
57 left: (-15px) !important;
57 left: (-15px) !important;
58 }
58 }
59 }
59 }
60 }
60 }
61
61
62 - input {
63 - font-family: Tahoma, "sans-serif";
64 - }
65 -
66 h1 {
62 h1 {
67 color: #334488;
63 color: #334488;
68 }
64 }
69
65
70 h2 {
66 h2 {
71 color: #5566bb;
67 color: #5566bb;
72 }
68 }
73
69
74 hr {
70 hr {
75 border-top: 1px solid #dddddd;
71 border-top: 1px solid #dddddd;
76 border-bottom: 1px solid #eeeeee;
72 border-bottom: 1px solid #eeeeee;
77 }
73 }
78
74
79 //#a
75 //#a
80 // color: #6666cc
76 // color: #6666cc
81 // text-decoration: none
77 // text-decoration: none
82 //
78 //
83 // &:link, &:visited
79 // &:link, &:visited
84 // color: #6666cc
80 // color: #6666cc
85 // text-decoration: none
81 // text-decoration: none
86 //
82 //
87 // &:hover, &:focus
83 // &:hover, &:focus
88 // color: #111166
84 // color: #111166
89 // text-decoration: none
85 // text-decoration: none
90
86
91 div {
87 div {
92 &.userbar {
88 &.userbar {
93 line-height: 1.5em;
89 line-height: 1.5em;
94 text-align: right;
90 text-align: right;
95 font-size: 12px;
91 font-size: 12px;
96 }
92 }
97
93
98 &.title {
94 &.title {
99 padding: 10px 0px;
95 padding: 10px 0px;
100 line-height: 1.5em;
96 line-height: 1.5em;
101 font-size: 13px;
97 font-size: 13px;
102
98
103 span.contest-over-msg {
99 span.contest-over-msg {
104 font-size: 15px;
100 font-size: 15px;
105 color: red;
101 color: red;
106 }
102 }
107
103
108 table {
104 table {
109 width: 100%;
105 width: 100%;
110 font-weight: bold;
106 font-weight: bold;
111 }
107 }
112
108
113 td {
109 td {
114 &.left-col {
110 &.left-col {
115 text-align: left;
111 text-align: left;
116 vertical-align: top;
112 vertical-align: top;
117 color: #444444;
113 color: #444444;
118 }
114 }
119
115
120 &.right-col {
116 &.right-col {
121 text-align: right;
117 text-align: right;
122 vertical-align: top;
118 vertical-align: top;
123 font-size: 18px;
119 font-size: 18px;
124 color: #116699;
120 color: #116699;
125 }
121 }
126 }
122 }
127 }
123 }
128 }
124 }
129
125
130 table.info {
126 table.info {
131 margin: 10px 0;
127 margin: 10px 0;
132 border: 1px solid #666666;
128 border: 1px solid #666666;
133 border-collapse: collapse;
129 border-collapse: collapse;
134 font-size: 12px;
130 font-size: 12px;
135
131
136 th {
132 th {
137 border: 1px solid #666666;
133 border: 1px solid #666666;
138 line-height: 1.5em;
134 line-height: 1.5em;
139 padding: 0 0.5em;
135 padding: 0 0.5em;
140 }
136 }
141
137
142 td {
138 td {
143 border-left: 1px solid #666666;
139 border-left: 1px solid #666666;
144 border-right: 1px solid #666666;
140 border-right: 1px solid #666666;
145 line-height: 1.5em;
141 line-height: 1.5em;
146 padding: 0 0.5em;
142 padding: 0 0.5em;
147 }
143 }
148 }
144 }
149
145
150 tr {
146 tr {
151 &.info-head {
147 &.info-head {
152 background: #777777;
148 background: #777777;
153 color: white;
149 color: white;
154 }
150 }
155
151
156 &.info-odd {
152 &.info-odd {
157 background: #eeeeee;
153 background: #eeeeee;
158 }
154 }
159
155
160 &.info-even {
156 &.info-even {
161 background: #fcfcfc;
157 background: #fcfcfc;
@@ -1,127 +1,134
1 require 'ipaddr'
1 require 'ipaddr'
2 require "securerandom"
2 require "securerandom"
3
3
4 class ApplicationController < ActionController::Base
4 class ApplicationController < ActionController::Base
5 protect_from_forgery
5 protect_from_forgery
6
6
7 before_action :current_user
7 before_action :current_user
8 before_action :nav_announcement
8 before_action :nav_announcement
9 before_action :unique_visitor_id
9 before_action :unique_visitor_id
10 + before_action :active_controller_action
10
11
11 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
12 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
12 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
13 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
13 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
14 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
14 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
15 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
15
16
16 #report and redirect for unauthorized activities
17 #report and redirect for unauthorized activities
17 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
18 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
18 flash[:notice] = notice
19 flash[:notice] = notice
19 redirect_to login_main_path
20 redirect_to login_main_path
20 end
21 end
21
22
22 # Returns the current logged-in user (if any).
23 # Returns the current logged-in user (if any).
23 def current_user
24 def current_user
24 return nil unless session[:user_id]
25 return nil unless session[:user_id]
25 @current_user ||= User.find(session[:user_id])
26 @current_user ||= User.find(session[:user_id])
26 end
27 end
27
28
28 def nav_announcement
29 def nav_announcement
29 @nav_announcement = Announcement.where(on_nav_bar: true)
30 @nav_announcement = Announcement.where(on_nav_bar: true)
30 end
31 end
31
32
33 + def active_controller_action
34 + #so that we can override this value inside each action
35 + @active_controller = controller_name
36 + @active_action = action_name
37 + end
38 +
32 def admin_authorization
39 def admin_authorization
33 return false unless check_valid_login
40 return false unless check_valid_login
34 user = User.includes(:roles).find(session[:user_id])
41 user = User.includes(:roles).find(session[:user_id])
35 unless user.admin?
42 unless user.admin?
36 unauthorized_redirect
43 unauthorized_redirect
37 return false
44 return false
38 end
45 end
39 return true
46 return true
40 end
47 end
41
48
42 #admin always count as every roles
49 #admin always count as every roles
43 def role_authorization(roles)
50 def role_authorization(roles)
44 return false unless check_valid_login
51 return false unless check_valid_login
45 user = User.find(session[:user_id])
52 user = User.find(session[:user_id])
46 return true if user.admin?
53 return true if user.admin?
47 roles.each do |r|
54 roles.each do |r|
48 return true if user.has_role?(r)
55 return true if user.has_role?(r)
49 end
56 end
50 unauthorized_redirect
57 unauthorized_redirect
51 end
58 end
52
59
53 def authorization_by_roles(allowed_roles)
60 def authorization_by_roles(allowed_roles)
54 return false unless check_valid_login
61 return false unless check_valid_login
55 unless @current_user.roles.detect { |role| allowed_roles.member?(role.name) }
62 unless @current_user.roles.detect { |role| allowed_roles.member?(role.name) }
56 unauthorized_redirect
63 unauthorized_redirect
57 return false
64 return false
58 end
65 end
59 end
66 end
60
67
61 def testcase_authorization
68 def testcase_authorization
62 #admin always has privileged
69 #admin always has privileged
63 if @current_user.admin?
70 if @current_user.admin?
64 return true
71 return true
65 end
72 end
66
73
67 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
74 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
68 end
75 end
69
76
70 def unique_visitor_id
77 def unique_visitor_id
71 unless cookies.encrypted[:uuid]
78 unless cookies.encrypted[:uuid]
72 value = SecureRandom.uuid
79 value = SecureRandom.uuid
73 cookies.encrypted[:uuid] = { value: value, expires: 20.year }
80 cookies.encrypted[:uuid] = { value: value, expires: 20.year }
74 end
81 end
75 end
82 end
76
83
77 protected
84 protected
78
85
79 #redirect to root (and also force logout)
86 #redirect to root (and also force logout)
80 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
87 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
81 def check_valid_login
88 def check_valid_login
82 #check if logged in
89 #check if logged in
83 unless session[:user_id]
90 unless session[:user_id]
84 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
91 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
85 unauthorized_redirect('You need to login but you cannot log in at this time')
92 unauthorized_redirect('You need to login but you cannot log in at this time')
86 else
93 else
87 unauthorized_redirect('You need to login')
94 unauthorized_redirect('You need to login')
88 end
95 end
89 return false
96 return false
90 end
97 end
91
98
92 # check if run in single user mode
99 # check if run in single user mode
93 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
100 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
94 if @current_user==nil || (!@current_user.admin?)
101 if @current_user==nil || (!@current_user.admin?)
95 unauthorized_redirect('You cannot log in at this time')
102 unauthorized_redirect('You cannot log in at this time')
96 return false
103 return false
97 end
104 end
98 end
105 end
99
106
100 # check if the user is enabled
107 # check if the user is enabled
101 unless @current_user.enabled? || @current_user.admin?
108 unless @current_user.enabled? || @current_user.admin?
102 unauthorized_redirect 'Your account is disabled'
109 unauthorized_redirect 'Your account is disabled'
103 return false
110 return false
104 end
111 end
105
112
106 # check if user ip is allowed
113 # check if user ip is allowed
107 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
114 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
108 unless is_request_ip_allowed?
115 unless is_request_ip_allowed?
109 unauthorized_redirect 'Your IP is not allowed to login at this time.'
116 unauthorized_redirect 'Your IP is not allowed to login at this time.'
110 return false
117 return false
111 end
118 end
112 end
119 end
113
120
114 if GraderConfiguration.multicontests?
121 if GraderConfiguration.multicontests?
115 return true if @current_user.admin?
122 return true if @current_user.admin?
116 begin
123 begin
117 if @current_user.contest_stat(true).forced_logout
124 if @current_user.contest_stat(true).forced_logout
118 flash[:notice] = 'You have been automatically logged out.'
125 flash[:notice] = 'You have been automatically logged out.'
119 redirect_to :controller => 'main', :action => 'index'
126 redirect_to :controller => 'main', :action => 'index'
120 end
127 end
121 rescue
128 rescue
122 end
129 end
123 end
130 end
124 return true
131 return true
125 end
132 end
126
133
127 #redirect to root (and also force logout)
134 #redirect to root (and also force logout)
@@ -1,306 +1,287
1 class ProblemsController < ApplicationController
1 class ProblemsController < ApplicationController
2
2
3 + include ActiveStorage::SetCurrent
4 +
3 before_action :admin_authorization, except: [:stat]
5 before_action :admin_authorization, except: [:stat]
6 + before_action :set_problem, only: [:show, :edit, :update, :destroy, :get_statement, :toggle, :toggle_test, :toggle_view_testcase, :stat]
4 before_action only: [:stat] do
7 before_action only: [:stat] do
5 authorization_by_roles(['admin','ta'])
8 authorization_by_roles(['admin','ta'])
6 end
9 end
7
10
11 +
8 def index
12 def index
9 @problems = Problem.order(date_added: :desc)
13 @problems = Problem.order(date_added: :desc)
10 end
14 end
11
15
12
16
13 def show
17 def show
14 - @problem = Problem.find(params[:id])
18 + end
19 +
20 + #get statement download link
21 + def get_statement
22 + unless @current_user.can_view_problem? @problem
23 + redirect_to list_main_path, error: 'You are not authorized to access this file'
24 + return
25 + end
26 +
27 + if params[:ext]=='pdf'
28 + content_type = 'application/pdf'
29 + else
30 + content_type = 'application/octet-stream'
31 + end
32 +
33 + filename = @problem.statement.filename.to_s
34 + data =@problem.statement.download
35 +
36 + send_data data, stream: false, disposition: 'inline', filename: filename, type: content_type
15 end
37 end
16
38
17 def new
39 def new
18 @problem = Problem.new
40 @problem = Problem.new
19 - @description = nil
20 end
41 end
21
42
22 def create
43 def create
23 @problem = Problem.new(problem_params)
44 @problem = Problem.new(problem_params)
24 - @description = Description.new(description_params)
25 - if @description.body!=''
26 - if !@description.save
27 - render :action => new and return
28 - end
29 - else
30 - @description = nil
31 - end
32 - @problem.description = @description
33 if @problem.save
45 if @problem.save
34 - flash[:notice] = 'Problem was successfully created.'
46 + redirect_to action: :index, notice: 'Problem was successfully created.'
35 - redirect_to action: :index
36 else
47 else
37 render :action => 'new'
48 render :action => 'new'
38 end
49 end
39 end
50 end
40
51
41 def quick_create
52 def quick_create
42 @problem = Problem.new(problem_params)
53 @problem = Problem.new(problem_params)
43 @problem.full_name = @problem.name if @problem.full_name == ''
54 @problem.full_name = @problem.name if @problem.full_name == ''
44 @problem.full_score = 100
55 @problem.full_score = 100
45 @problem.available = false
56 @problem.available = false
46 @problem.test_allowed = true
57 @problem.test_allowed = true
47 @problem.output_only = false
58 @problem.output_only = false
48 @problem.date_added = Time.new
59 @problem.date_added = Time.new
49 if @problem.save
60 if @problem.save
50 flash[:notice] = 'Problem was successfully created.'
61 flash[:notice] = 'Problem was successfully created.'
51 redirect_to action: :index
62 redirect_to action: :index
52 else
63 else
53 flash[:notice] = 'Error saving problem'
64 flash[:notice] = 'Error saving problem'
54 redirect_to action: :index
65 redirect_to action: :index
55 end
66 end
56 end
67 end
57
68
58 def edit
69 def edit
59 - @problem = Problem.find(params[:id])
60 @description = @problem.description
70 @description = @problem.description
61 end
71 end
62
72
63 def update
73 def update
64 - @problem = Problem.find(params[:id])
74 + if problem_params[:statement] && problem_params[:statement].content_type != 'application/pdf'
65 - @description = @problem.description
75 + flash[:error] = 'Error: Uploaded file is not PDF'
66 - if @description.nil? and params[:description][:body]!=''
76 + render :action => 'edit'
67 - @description = Description.new(description_params)
77 + return
68 - if !@description.save
69 - flash[:notice] = 'Error saving description'
70 - render :action => 'edit' and return
71 - end
72 - @problem.description = @description
73 - elsif @description
74 - if !@description.update(description_params)
75 - flash[:notice] = 'Error saving description'
76 - render :action => 'edit' and return
77 - end
78 - end
79 - if params[:file] and params[:file].content_type != 'application/pdf'
80 - flash[:notice] = 'Error: Uploaded file is not PDF'
81 - render :action => 'edit' and return
82 end
78 end
83 if @problem.update(problem_params)
79 if @problem.update(problem_params)
84 flash[:notice] = 'Problem was successfully updated.'
80 flash[:notice] = 'Problem was successfully updated. '
85 - unless params[:file] == nil or params[:file] == ''
81 + flash[:notice] += 'A new statement PDF is uploaded' if problem_params[:statement]
86 - flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
87 - out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
88 - if not FileTest.exists? out_dirname
89 - Dir.mkdir out_dirname
90 - end
91 -
92 - out_filename = "#{out_dirname}/#{@problem.name}.pdf"
93 - if FileTest.exists? out_filename
94 - File.delete out_filename
95 - end
96 -
97 - File.open(out_filename,"wb") do |file|
98 - file.write(params[:file].read)
99 - end
100 - @problem.description_filename = "#{@problem.name}.pdf"
101 @problem.save
82 @problem.save
102 - end
83 + redirect_to edit_problem_path(@problem)
103 - redirect_to :action => 'show', :id => @problem
104 else
84 else
105 render :action => 'edit'
85 render :action => 'edit'
106 end
86 end
107 end
87 end
108
88
109 def destroy
89 def destroy
110 - p = Problem.find(params[:id]).destroy
90 + @problem.destroy
111 redirect_to action: :index
91 redirect_to action: :index
112 end
92 end
113
93
114 def toggle
94 def toggle
115 - @problem = Problem.find(params[:id])
116 @problem.update(available: !(@problem.available) )
95 @problem.update(available: !(@problem.available) )
117 respond_to do |format|
96 respond_to do |format|
118 format.js { }
97 format.js { }
119 end
98 end
120 end
99 end
121
100
122 def toggle_test
101 def toggle_test
123 - @problem = Problem.find(params[:id])
124 @problem.update(test_allowed: !(@problem.test_allowed?) )
102 @problem.update(test_allowed: !(@problem.test_allowed?) )
125 respond_to do |format|
103 respond_to do |format|
126 format.js { }
104 format.js { }
127 end
105 end
128 end
106 end
129
107
130 def toggle_view_testcase
108 def toggle_view_testcase
131 - @problem = Problem.find(params[:id])
132 @problem.update(view_testcase: !(@problem.view_testcase?) )
109 @problem.update(view_testcase: !(@problem.view_testcase?) )
133 respond_to do |format|
110 respond_to do |format|
134 format.js { }
111 format.js { }
135 end
112 end
136 end
113 end
137
114
138 def turn_all_off
115 def turn_all_off
139 Problem.available.all.each do |problem|
116 Problem.available.all.each do |problem|
140 problem.available = false
117 problem.available = false
141 problem.save
118 problem.save
142 end
119 end
143 redirect_to action: :index
120 redirect_to action: :index
144 end
121 end
145
122
146 def turn_all_on
123 def turn_all_on
147 Problem.where.not(available: true).each do |problem|
124 Problem.where.not(available: true).each do |problem|
148 problem.available = true
125 problem.available = true
149 problem.save
126 problem.save
150 end
127 end
151 redirect_to action: :index
128 redirect_to action: :index
152 end
129 end
153
130
154 def stat
131 def stat
155 - @problem = Problem.find(params[:id])
156 unless @problem.available or session[:admin]
132 unless @problem.available or session[:admin]
157 redirect_to :controller => 'main', :action => 'list'
133 redirect_to :controller => 'main', :action => 'list'
158 return
134 return
159 end
135 end
160 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
136 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
161
137
162 #stat summary
138 #stat summary
163 range =65
139 range =65
164 @histogram = { data: Array.new(range,0), summary: {} }
140 @histogram = { data: Array.new(range,0), summary: {} }
165 user = Hash.new(0)
141 user = Hash.new(0)
166 @submissions.find_each do |sub|
142 @submissions.find_each do |sub|
167 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
143 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
168 @histogram[:data][d.to_i] += 1 if d < range
144 @histogram[:data][d.to_i] += 1 if d < range
169 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
145 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
170 end
146 end
171 @histogram[:summary][:max] = [@histogram[:data].max,1].max
147 @histogram[:summary][:max] = [@histogram[:data].max,1].max
172
148
173 @summary = { attempt: user.count, solve: 0 }
149 @summary = { attempt: user.count, solve: 0 }
174 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
150 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
175 end
151 end
176
152
177 def manage
153 def manage
178 @problems = Problem.order(date_added: :desc)
154 @problems = Problem.order(date_added: :desc)
179 end
155 end
180
156
181 def do_manage
157 def do_manage
182 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
158 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
183 change_date_added
159 change_date_added
184 elsif params.has_key? 'add_to_contest'
160 elsif params.has_key? 'add_to_contest'
185 add_to_contest
161 add_to_contest
186 elsif params.has_key? 'enable_problem'
162 elsif params.has_key? 'enable_problem'
187 set_available(true)
163 set_available(true)
188 elsif params.has_key? 'disable_problem'
164 elsif params.has_key? 'disable_problem'
189 set_available(false)
165 set_available(false)
190 elsif params.has_key? 'add_group'
166 elsif params.has_key? 'add_group'
191 group = Group.find(params[:group_id])
167 group = Group.find(params[:group_id])
192 ok = []
168 ok = []
193 failed = []
169 failed = []
194 get_problems_from_params.each do |p|
170 get_problems_from_params.each do |p|
195 begin
171 begin
196 group.problems << p
172 group.problems << p
197 ok << p.full_name
173 ok << p.full_name
198 rescue => e
174 rescue => e
199 failed << p.full_name
175 failed << p.full_name
200 end
176 end
201 end
177 end
202 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
178 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
203 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
179 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
204 elsif params.has_key? 'add_tags'
180 elsif params.has_key? 'add_tags'
205 get_problems_from_params.each do |p|
181 get_problems_from_params.each do |p|
206 p.tag_ids += params[:tag_ids]
182 p.tag_ids += params[:tag_ids]
207 end
183 end
208 end
184 end
209
185
210 redirect_to :action => 'manage'
186 redirect_to :action => 'manage'
211 end
187 end
212
188
213 def import
189 def import
214 @allow_test_pair_import = allow_test_pair_import?
190 @allow_test_pair_import = allow_test_pair_import?
215 end
191 end
216
192
217 def do_import
193 def do_import
218 old_problem = Problem.find_by_name(params[:name])
194 old_problem = Problem.find_by_name(params[:name])
219 if !allow_test_pair_import? and params.has_key? :import_to_db
195 if !allow_test_pair_import? and params.has_key? :import_to_db
220 params.delete :import_to_db
196 params.delete :import_to_db
221 end
197 end
222 @problem, import_log = Problem.create_from_import_form_params(params,
198 @problem, import_log = Problem.create_from_import_form_params(params,
223 old_problem)
199 old_problem)
224
200
225 if !@problem.errors.empty?
201 if !@problem.errors.empty?
226 render :action => 'import' and return
202 render :action => 'import' and return
227 end
203 end
228
204
229 if old_problem!=nil
205 if old_problem!=nil
230 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
206 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
231 end
207 end
232 @log = import_log
208 @log = import_log
233 end
209 end
234
210
235 def remove_contest
211 def remove_contest
236 problem = Problem.find(params[:id])
212 problem = Problem.find(params[:id])
237 contest = Contest.find(params[:contest_id])
213 contest = Contest.find(params[:contest_id])
238 if problem!=nil and contest!=nil
214 if problem!=nil and contest!=nil
239 problem.contests.delete(contest)
215 problem.contests.delete(contest)
240 end
216 end
241 redirect_to :action => 'manage'
217 redirect_to :action => 'manage'
242 end
218 end
243
219
244 ##################################
220 ##################################
245 protected
221 protected
246
222
247 def allow_test_pair_import?
223 def allow_test_pair_import?
248 if defined? ALLOW_TEST_PAIR_IMPORT
224 if defined? ALLOW_TEST_PAIR_IMPORT
249 return ALLOW_TEST_PAIR_IMPORT
225 return ALLOW_TEST_PAIR_IMPORT
250 else
226 else
251 return false
227 return false
252 end
228 end
253 end
229 end
254
230
255 def change_date_added
231 def change_date_added
256 problems = get_problems_from_params
232 problems = get_problems_from_params
257 date = Date.parse(params[:date_added])
233 date = Date.parse(params[:date_added])
258 problems.each do |p|
234 problems.each do |p|
259 p.date_added = date
235 p.date_added = date
260 p.save
236 p.save
261 end
237 end
262 end
238 end
263
239
264 def add_to_contest
240 def add_to_contest
265 problems = get_problems_from_params
241 problems = get_problems_from_params
266 contest = Contest.find(params[:contest][:id])
242 contest = Contest.find(params[:contest][:id])
267 if contest!=nil and contest.enabled
243 if contest!=nil and contest.enabled
268 problems.each do |p|
244 problems.each do |p|
269 p.contests << contest
245 p.contests << contest
270 end
246 end
271 end
247 end
272 end
248 end
273
249
274 def set_available(avail)
250 def set_available(avail)
275 problems = get_problems_from_params
251 problems = get_problems_from_params
276 problems.each do |p|
252 problems.each do |p|
277 p.available = avail
253 p.available = avail
278 p.save
254 p.save
279 end
255 end
280 end
256 end
281
257
282 def get_problems_from_params
258 def get_problems_from_params
283 problems = []
259 problems = []
284 params.keys.each do |k|
260 params.keys.each do |k|
285 if k.index('prob-')==0
261 if k.index('prob-')==0
286 name, id, order = k.split('-')
262 name, id, order = k.split('-')
287 problems << Problem.find(id)
263 problems << Problem.find(id)
288 end
264 end
289 end
265 end
290 problems
266 problems
291 end
267 end
292
268
293 def get_problems_stat
269 def get_problems_stat
294 end
270 end
295
271
296 private
272 private
297
273
274 + def set_problem
275 + @problem = Problem.find(params[:id])
276 + end
277 +
298 def problem_params
278 def problem_params
299 - params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
279 + params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available,
280 + :test_allowed, :output_only, :url, :description, :statement, :description, tag_ids:[])
300 end
281 end
301
282
302 def description_params
283 def description_params
303 params.require(:description).permit(:body, :markdowned)
284 params.require(:description).permit(:body, :markdowned)
304 end
285 end
305
286
306 end
287 end
@@ -116,97 +116,108
116 btn_block = option[:block] || 'btn-block'
116 btn_block = option[:block] || 'btn-block'
117 link_to (on ? "Yes" : "No"), toggle_url,
117 link_to (on ? "Yes" : "No"), toggle_url,
118 {class: "btn #{btn_block} #{btn_size} btn-#{on ? 'success' : 'outline-secondary'} ajax-toggle",
118 {class: "btn #{btn_block} #{btn_size} btn-#{on ? 'success' : 'outline-secondary'} ajax-toggle",
119 id: id,
119 id: id,
120 data: {remote: true, method: 'get'}}
120 data: {remote: true, method: 'get'}}
121 end
121 end
122
122
123 def get_ace_mode(language)
123 def get_ace_mode(language)
124 # return ace mode string from Language
124 # return ace mode string from Language
125
125
126 case language.pretty_name
126 case language.pretty_name
127 when 'Pascal'
127 when 'Pascal'
128 'ace/mode/pascal'
128 'ace/mode/pascal'
129 when 'C++','C'
129 when 'C++','C'
130 'ace/mode/c_cpp'
130 'ace/mode/c_cpp'
131 when 'Ruby'
131 when 'Ruby'
132 'ace/mode/ruby'
132 'ace/mode/ruby'
133 when 'Python'
133 when 'Python'
134 'ace/mode/python'
134 'ace/mode/python'
135 when 'Java'
135 when 'Java'
136 'ace/mode/java'
136 'ace/mode/java'
137 else
137 else
138 'ace/mode/c_cpp'
138 'ace/mode/c_cpp'
139 end
139 end
140 end
140 end
141
141
142
142
143 def user_title_bar(user)
143 def user_title_bar(user)
144 header = ''
144 header = ''
145 time_left = ''
145 time_left = ''
146
146
147 #
147 #
148 # if the contest is over
148 # if the contest is over
149 if GraderConfiguration.time_limit_mode?
149 if GraderConfiguration.time_limit_mode?
150 if user.contest_finished?
150 if user.contest_finished?
151 header = <<CONTEST_OVER
151 header = <<CONTEST_OVER
152 <tr><td colspan="2" align="center">
152 <tr><td colspan="2" align="center">
153 <span class="contest-over-msg">THE CONTEST IS OVER</span>
153 <span class="contest-over-msg">THE CONTEST IS OVER</span>
154 </td></tr>
154 </td></tr>
155 CONTEST_OVER
155 CONTEST_OVER
156 end
156 end
157 if !user.contest_started?
157 if !user.contest_started?
158 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
158 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
159 else
159 else
160 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
160 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
161 " #{format_short_duration(user.contest_time_left)}"
161 " #{format_short_duration(user.contest_time_left)}"
162 end
162 end
163 end
163 end
164
164
165 #
165 #
166 # if the contest is in the anaysis mode
166 # if the contest is in the anaysis mode
167 if GraderConfiguration.analysis_mode?
167 if GraderConfiguration.analysis_mode?
168 header = <<ANALYSISMODE
168 header = <<ANALYSISMODE
169 <tr><td colspan="2" align="center">
169 <tr><td colspan="2" align="center">
170 <span class="contest-over-msg">ANALYSIS MODE</span>
170 <span class="contest-over-msg">ANALYSIS MODE</span>
171 </td></tr>
171 </td></tr>
172 ANALYSISMODE
172 ANALYSISMODE
173 end
173 end
174
174
175 contest_name = GraderConfiguration['contest.name']
175 contest_name = GraderConfiguration['contest.name']
176
176
177 #
177 #
178 # build real title bar
178 # build real title bar
179 result = <<TITLEBAR
179 result = <<TITLEBAR
180 <div class="title">
180 <div class="title">
181 <table>
181 <table>
182 #{header}
182 #{header}
183 <tr>
183 <tr>
184 <td class="left-col">
184 <td class="left-col">
185 <br/>
185 <br/>
186 </td>
186 </td>
187 <td class="right-col">#{contest_name}</td>
187 <td class="right-col">#{contest_name}</td>
188 </tr>
188 </tr>
189 </table>
189 </table>
190 </div>
190 </div>
191 TITLEBAR
191 TITLEBAR
192 result.html_safe
192 result.html_safe
193 end
193 end
194
194
195 def markdown(text)
195 def markdown(text)
196 markdown = RDiscount.new(text)
196 markdown = RDiscount.new(text)
197 markdown.to_html.html_safe
197 markdown.to_html.html_safe
198 end
198 end
199
199
200
200
201 BOOTSTRAP_FLASH_MSG = {
201 BOOTSTRAP_FLASH_MSG = {
202 success: 'alert-success',
202 success: 'alert-success',
203 error: 'alert-danger',
203 error: 'alert-danger',
204 alert: 'alert-danger',
204 alert: 'alert-danger',
205 notice: 'alert-info'
205 notice: 'alert-info'
206 }
206 }
207
207
208 def bootstrap_class_for(flash_type)
208 def bootstrap_class_for(flash_type)
209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
210 end
210 end
211
211
212 + def active_class_when(options = {},cname = @active_controller, aname = @active_action)
213 + class_name = ' active '
214 + ok = true
215 + options.each do |k,v|
216 + ok = false if k == :controller && v.to_s != cname
217 + ok = false if k == :action && v.to_s != aname
212 end
218 end
219 + return class_name if ok && options.size > 0
220 + return ''
221 + end
222 +
223 + end
@@ -1,122 +1,126
1 class Problem < ApplicationRecord
1 class Problem < ApplicationRecord
2
2
3 - belongs_to :description
3 + #belongs_to :description
4 +
4 has_and_belongs_to_many :contests, :uniq => true
5 has_and_belongs_to_many :contests, :uniq => true
5
6
6 #has_and_belongs_to_many :groups
7 #has_and_belongs_to_many :groups
7 has_many :groups_problems, class_name: 'GroupProblem'
8 has_many :groups_problems, class_name: 'GroupProblem'
8 has_many :groups, :through => :groups_problems
9 has_many :groups, :through => :groups_problems
9
10
10 has_many :problems_tags, class_name: 'ProblemTag'
11 has_many :problems_tags, class_name: 'ProblemTag'
11 has_many :tags, through: :problems_tags
12 has_many :tags, through: :problems_tags
12
13
13 has_many :test_pairs, :dependent => :delete_all
14 has_many :test_pairs, :dependent => :delete_all
14 has_many :testcases, :dependent => :destroy
15 has_many :testcases, :dependent => :destroy
15
16
16 has_many :submissions
17 has_many :submissions
17
18
18 validates_presence_of :name
19 validates_presence_of :name
19 validates_format_of :name, :with => /\A\w+\z/
20 validates_format_of :name, :with => /\A\w+\z/
20 validates_presence_of :full_name
21 validates_presence_of :full_name
21
22
22 scope :available, -> { where(available: true) }
23 scope :available, -> { where(available: true) }
23
24
24 DEFAULT_TIME_LIMIT = 1
25 DEFAULT_TIME_LIMIT = 1
25 DEFAULT_MEMORY_LIMIT = 32
26 DEFAULT_MEMORY_LIMIT = 32
26
27
28 + has_one_attached :statement
29 + has_many_attached :attachments
30 +
27 def get_jschart_history
31 def get_jschart_history
28 start = 4.month.ago.beginning_of_day
32 start = 4.month.ago.beginning_of_day
29 start_date = start.to_date
33 start_date = start.to_date
30 count = Submission.where(problem: self).where('submitted_at >= ?', start).group('DATE(submitted_at)').count
34 count = Submission.where(problem: self).where('submitted_at >= ?', start).group('DATE(submitted_at)').count
31 i = 0
35 i = 0
32 label = []
36 label = []
33 value = []
37 value = []
34 while (start_date + i < Time.zone.now.to_date)
38 while (start_date + i < Time.zone.now.to_date)
35 if (start_date+i).day == 1
39 if (start_date+i).day == 1
36 #label << (start_date+i).strftime("%d %b %Y")
40 #label << (start_date+i).strftime("%d %b %Y")
37 #label << (start_date+i).strftime("%d")
41 #label << (start_date+i).strftime("%d")
38 else
42 else
39 #label << ' '
43 #label << ' '
40 #label << (start_date+i).strftime("%d")
44 #label << (start_date+i).strftime("%d")
41 end
45 end
42 label << (start_date+i).strftime("%d-%b")
46 label << (start_date+i).strftime("%d-%b")
43 value << (count[start_date+i] || 0)
47 value << (count[start_date+i] || 0)
44 i+=1
48 i+=1
45 end
49 end
46 return {labels: label,datasets: [label:'sub',data: value, backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgb(75, 192, 192)']}
50 return {labels: label,datasets: [label:'sub',data: value, backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgb(75, 192, 192)']}
47 end
51 end
48
52
49 def self.available_problems
53 def self.available_problems
50 available.order(date_added: :desc).order(:name)
54 available.order(date_added: :desc).order(:name)
51 #Problem.available.all(:order => "date_added DESC, name ASC")
55 #Problem.available.all(:order => "date_added DESC, name ASC")
52 end
56 end
53
57
54 def self.create_from_import_form_params(params, old_problem=nil)
58 def self.create_from_import_form_params(params, old_problem=nil)
55 org_problem = old_problem || Problem.new
59 org_problem = old_problem || Problem.new
56 import_params, problem = Problem.extract_params_and_check(params,
60 import_params, problem = Problem.extract_params_and_check(params,
57 org_problem)
61 org_problem)
58
62
59 if !problem.errors.empty?
63 if !problem.errors.empty?
60 return problem, 'Error importing'
64 return problem, 'Error importing'
61 end
65 end
62
66
63 problem.full_score = 100
67 problem.full_score = 100
64 problem.date_added = Time.new
68 problem.date_added = Time.new
65 problem.test_allowed = true
69 problem.test_allowed = true
66 problem.output_only = false
70 problem.output_only = false
67 problem.available = false
71 problem.available = false
68
72
69 if not problem.save
73 if not problem.save
70 return problem, 'Error importing'
74 return problem, 'Error importing'
71 end
75 end
72
76
73 import_to_db = params.has_key? :import_to_db
77 import_to_db = params.has_key? :import_to_db
74
78
75 importer = TestdataImporter.new(problem)
79 importer = TestdataImporter.new(problem)
76
80
77 if not importer.import_from_file(import_params[:file],
81 if not importer.import_from_file(import_params[:file],
78 import_params[:time_limit],
82 import_params[:time_limit],
79 import_params[:memory_limit],
83 import_params[:memory_limit],
80 import_params[:checker_name],
84 import_params[:checker_name],
81 import_to_db)
85 import_to_db)
82 problem.errors.add(:base,'Import error.')
86 problem.errors.add(:base,'Import error.')
83 end
87 end
84
88
85 return problem, importer.log_msg
89 return problem, importer.log_msg
86 end
90 end
87
91
88 def self.download_file_basedir
92 def self.download_file_basedir
89 return "#{Rails.root}/data/tasks"
93 return "#{Rails.root}/data/tasks"
90 end
94 end
91
95
92 def get_submission_stat
96 def get_submission_stat
93 result = Hash.new
97 result = Hash.new
94 #total number of submission
98 #total number of submission
95 result[:total_sub] = Submission.where(problem_id: self.id).count
99 result[:total_sub] = Submission.where(problem_id: self.id).count
96 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
100 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
97 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
101 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
98 return result
102 return result
99 end
103 end
100
104
101 def long_name
105 def long_name
102 "[#{name}] #{full_name}"
106 "[#{name}] #{full_name}"
103 end
107 end
104
108
105 protected
109 protected
106
110
107 def self.to_i_or_default(st, default)
111 def self.to_i_or_default(st, default)
108 if st!=''
112 if st!=''
109 result = st.to_i
113 result = st.to_i
110 end
114 end
111 result ||= default
115 result ||= default
112 end
116 end
113
117
114 def self.to_f_or_default(st, default)
118 def self.to_f_or_default(st, default)
115 if st!=''
119 if st!=''
116 result = st.to_f
120 result = st.to_f
117 end
121 end
118 result ||= default
122 result ||= default
119 end
123 end
120
124
121 def self.extract_params_and_check(params, problem)
125 def self.extract_params_and_check(params, problem)
122 time_limit = Problem.to_f_or_default(params[:time_limit],
126 time_limit = Problem.to_f_or_default(params[:time_limit],
@@ -1,87 +1,93
1 %header
1 %header
2 %nav.navbar.fixed-top.navbar-dark.bg-primary.navbar-expand-lg
2 %nav.navbar.fixed-top.navbar-dark.bg-primary.navbar-expand-lg
3 .container-fluid
3 .container-fluid
4 %a.navbar-brand{href: list_main_path}
4 %a.navbar-brand{href: list_main_path}
5 %span.mi.mi-bs home
5 %span.mi.mi-bs home
6 MAIN
6 MAIN
7 %button.navbar-toggler.collapsed{ type: :button, 'data-bs': {toggle: 'collapse', target: '#navbar-collapse'} }
7 %button.navbar-toggler.collapsed{ type: :button, 'data-bs': {toggle: 'collapse', target: '#navbar-collapse'} }
8 %span.navbar-toggler-icon
8 %span.navbar-toggler-icon
9 .collapse.navbar-collapse#navbar-collapse
9 .collapse.navbar-collapse#navbar-collapse
10 %ul.navbar-nav.me-auto.mb-2.mb-lg-0
10 %ul.navbar-nav.me-auto.mb-2.mb-lg-0
11 / submission
11 / submission
12 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
12 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
13 %li.nav-item.dropdown.mx-2
13 %li.nav-item.dropdown.mx-2
14 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {expanded:"false"}, role: "button"}
14 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {expanded:"false"}, role: "button"}
15 = "#{I18n.t 'menu.submissions'}"
15 = "#{I18n.t 'menu.submissions'}"
16 %ul.dropdown-menu
16 %ul.dropdown-menu
17 - %li= link_to 'View', submissions_path, class:'dropdown-item'
17 + %li= link_to 'View', submissions_path, class: 'dropdown-item '+active_class_when(controller: :submissions)
18 %li= link_to 'Self Test', test_index_path, class:'dropdown-item'
18 %li= link_to 'Self Test', test_index_path, class:'dropdown-item'
19 / hall of fame
19 / hall of fame
20 - if GraderConfiguration['right.user_hall_of_fame']
20 - if GraderConfiguration['right.user_hall_of_fame']
21 - %li= link_to "#{I18n.t 'menu.hall_of_fame'}", problem_hof_report_path, class: 'nav-link mx-2'
21 + %li= link_to "#{I18n.t 'menu.hall_of_fame'}", problem_hof_report_path, class: 'nav-link mx-2'+active_class_when(controller: :report, action: :problem_hof)
22 / display MODE button (with countdown in contest mode)
22 / display MODE button (with countdown in contest mode)
23 - if GraderConfiguration.analysis_mode?
23 - if GraderConfiguration.analysis_mode?
24 %div.btn.btn-success#countdown= "ANALYSIS MODE"
24 %div.btn.btn-success#countdown= "ANALYSIS MODE"
25 - elsif GraderConfiguration.time_limit_mode?
25 - elsif GraderConfiguration.time_limit_mode?
26 - if @current_user.contest_finished?
26 - if @current_user.contest_finished?
27 %div.btn.btn-danger#countdown= "Contest is over"
27 %div.btn.btn-danger#countdown= "Contest is over"
28 - elsif !@current_user.contest_started?
28 - elsif !@current_user.contest_started?
29 %div.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
29 %div.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
30 - else
30 - else
31 %div.btn.btn-primary#countdown asdf
31 %div.btn.btn-primary#countdown asdf
32 :javascript
32 :javascript
33 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
33 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
34 / admin section
34 / admin section
35 - if (@current_user!=nil) and (session[:admin])
35 - if (@current_user!=nil) and (session[:admin])
36 / management
36 / management
37 %li.nav-item.dropdown.mx-2
37 %li.nav-item.dropdown.mx-2
38 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
38 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
39 Manage
39 Manage
40 %ul.dropdown-menu
40 %ul.dropdown-menu
41 - %li= link_to 'Announcements', announcements_path, class: 'dropdown-item'
41 + %li= link_to 'Announcements', announcements_path, class: 'dropdown-item'+active_class_when(controller: :announcements)
42 - %li= link_to 'Problems', problems_path, class: 'dropdown-item'
42 + %li= link_to 'Problems', problems_path, class: 'dropdown-item'+active_class_when(controller: :problems)
43 - %li= link_to 'Tags', tags_path, class: 'dropdown-item'
43 + %li= link_to 'Tags', tags_path, class: 'dropdown-item'+active_class_when(controller: :tags)
44 - %li= link_to 'Users', user_admin_index_path, class: 'dropdown-item'
44 + %li= link_to 'Users', user_admin_index_path, class: 'dropdown-item'+active_class_when(controller: :user_admin)
45 - %li= link_to 'User Groups', groups_path, class: 'dropdown-item'
45 + %li= link_to 'User Groups', groups_path, class: 'dropdown-item'+active_class_when(controller: :groups)
46 - %li= link_to 'Graders', graders_list_path, class: 'dropdown-item'
46 + %li= link_to 'Graders', graders_list_path, class: 'dropdown-item'+active_class_when(controller: :graders)
47 - %li= link_to 'Message ', console_messages_path, class: 'dropdown-item'
47 + %li= link_to 'Message ', console_messages_path, class: 'dropdown-item'+active_class_when(controller: :messages)
48 %li
48 %li
49 %hr.dropdown-divider
49 %hr.dropdown-divider
50 - %li= link_to 'System config', grader_configuration_index_path, class: 'dropdown-item'
50 + %li= link_to 'System config', grader_configuration_index_path, class: 'dropdown-item'+active_class_when(controller: :grader_configuration)
51 %li
51 %li
52 %hr.dropdown-divider
52 %hr.dropdown-divider
53 - %li= link_to 'Sites', sites_path, class: 'dropdown-item'
53 + %li= link_to 'Sites', sites_path, class: 'dropdown-item'+active_class_when(controller: :sites)
54 - %li= link_to 'Contests', contest_management_index_path, class: 'dropdown-item'
54 + %li= link_to 'Contests', contest_management_index_path, class: 'dropdown-item'+active_class_when(controller: :contest_management)
55 -#
55 -#
56 / report
56 / report
57 %li.nav-item.dropdown.mx-2
57 %li.nav-item.dropdown.mx-2
58 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
58 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
59 Report
59 Report
60 %ul.dropdown-menu
60 %ul.dropdown-menu
61 - %li= link_to 'Current Score', current_score_report_path, class: 'dropdown-item'
61 + %li= link_to 'Current Score', current_score_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :current_score)
62 - %li= link_to 'Score Report', max_score_report_path, class: 'dropdown-item'
62 + %li= link_to 'Score Report', max_score_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :max_score)
63 - %li= link_to 'Submission Report', submission_report_path, class: 'dropdown-item'
63 + %li= link_to 'Submission Report', submission_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :submission)
64 - %li= link_to 'Login Report', login_report_path, class: 'dropdown-item'
64 + %li= link_to 'Login Report', login_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :login)
65 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
65 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
66 =link_to "#{ungraded} backlogs!",
66 =link_to "#{ungraded} backlogs!",
67 graders_list_path,
67 graders_list_path,
68 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
68 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
69 / announcement
69 / announcement
70 - @nav_announcement.each do |ann|
70 - @nav_announcement.each do |ann|
71 %p.navbar-text
71 %p.navbar-text
72 = ann.body.html_safe
72 = ann.body.html_safe
73 %ul.navbar-nav
73 %ul.navbar-nav
74 %li.nav-item
74 %li.nav-item
75 %a.nav-link{href: help_main_path}
75 %a.nav-link{href: help_main_path}
76 %span.mi.mi-bs.md-18 help
76 %span.mi.mi-bs.md-18 help
77 %li.nav-item
77 %li.nav-item
78 %a.nav-link{href: messages_path}
78 %a.nav-link{href: messages_path}
79 %span.mi.mi-bs.md-18 chat
79 %span.mi.mi-bs.md-18 chat
80 - if GraderConfiguration['system.user_setting_enabled']
80 - if GraderConfiguration['system.user_setting_enabled']
81 %li.nav-item
81 %li.nav-item
82 %a.nav-link{href: profile_users_path}
82 %a.nav-link{href: profile_users_path}
83 %span.mi.mi-bs.md-18 settings
83 %span.mi.mi-bs.md-18 settings
84 %li.nav-item
84 %li.nav-item
85 %a.nav-link{href: login_main_path}
85 %a.nav-link{href: login_main_path}
86 %span.mi.mi-bs.md-18 exit_to_app
86 %span.mi.mi-bs.md-18 exit_to_app
87 = @current_user.full_name
87 = @current_user.full_name
88 + :javascript
89 + $('.active-with-children').each( (index,obj) => {
90 + if ($(obj).siblings('.dropdown-menu').has('.active').length > 0) {
91 + $(obj).addClass('active')
92 + }
93 + } )
@@ -1,55 +1,78
1 + = simple_form_for problem do |form|
2 + .row
3 + .col-md-6
4 + = form.input :name
5 + = form.input :full_name
6 + = form.input :full_score
7 + = form.input :tag_ids, collection: Tag.all, class: 'select2'
8 + = form.input :date_added
9 + = form.input :available
10 + = form.input :test_allowed
11 + = form.input :output_only
12 + = form.input :description, as: :text
13 + = form.input :markdown
14 + = form.input :url
15 + = form.input :statement
16 + %p
17 + - if @problem.statement.attached?
18 + %a{href: get_statement_problem_path(@problem)} [Download current Statement]
19 + - else
20 + no statement attached to this problem
21 + = form.submit :submit, class: 'btn btn-primary'
22 + -#
1 = error_messages_for 'problem'
23 = error_messages_for 'problem'
24 +
2 / [form:problem]
25 / [form:problem]
3 .form-group
26 .form-group
4 %label{:for => "problem_name"} Name
27 %label{:for => "problem_name"} Name
5 = text_field 'problem', 'name', class: 'form-control'
28 = text_field 'problem', 'name', class: 'form-control'
6 %small
29 %small
7 Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead.
30 Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead.
8 .form-group
31 .form-group
9 %label{:for => "problem_full_name"} Full name
32 %label{:for => "problem_full_name"} Full name
10 = text_field 'problem', 'full_name', class: 'form-control'
33 = text_field 'problem', 'full_name', class: 'form-control'
11 .form-group
34 .form-group
12 %label{:for => "problem_full_score"} Full score
35 %label{:for => "problem_full_score"} Full score
13 = text_field 'problem', 'full_score', class: 'form-control'
36 = text_field 'problem', 'full_score', class: 'form-control'
14 .form-group
37 .form-group
15 %label{:for => "problem_full_score"} Tags
38 %label{:for => "problem_full_score"} Tags
16 = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'})
39 = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'})
17 .form-group
40 .form-group
18 %label{:for => "problem_date_added"} Date added
41 %label{:for => "problem_date_added"} Date added
19 = date_select 'problem', 'date_added', class: 'form-control'
42 = date_select 'problem', 'date_added', class: 'form-control'
20 - # TODO: these should be put in model Problem, but I can't think of
43 - # TODO: these should be put in model Problem, but I can't think of
21 - # nice default values for them. These values look fine only
44 - # nice default values for them. These values look fine only
22 - # in this case (of lazily adding new problems).
45 - # in this case (of lazily adding new problems).
23 - @problem.available = true if @problem!=nil and @problem.available==nil
46 - @problem.available = true if @problem!=nil and @problem.available==nil
24 - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil
47 - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil
25 - @problem.output_only = false if @problem!=nil and @problem.output_only==nil
48 - @problem.output_only = false if @problem!=nil and @problem.output_only==nil
26 .checkbox
49 .checkbox
27 %label{:for => "problem_available"}
50 %label{:for => "problem_available"}
28 = check_box :problem, :available
51 = check_box :problem, :available
29 Available?
52 Available?
30 .checkbox
53 .checkbox
31 %label{:for => "problem_test_allowed"}
54 %label{:for => "problem_test_allowed"}
32 = check_box :problem, :test_allowed
55 = check_box :problem, :test_allowed
33 Test allowed?
56 Test allowed?
34 .checkbox
57 .checkbox
35 %label{:for => "problem_output_only"}
58 %label{:for => "problem_output_only"}
36 = check_box :problem, :output_only
59 = check_box :problem, :output_only
37 Output only?
60 Output only?
38 = error_messages_for 'description'
61 = error_messages_for 'description'
39 .form-group
62 .form-group
40 %label{:for => "description_body"} Description
63 %label{:for => "description_body"} Description
41 %br/
64 %br/
42 = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control'
65 = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control'
43 .form-group
66 .form-group
44 %label{:for => "description_markdowned"} Markdowned?
67 %label{:for => "description_markdowned"} Markdowned?
45 = select "description", |
68 = select "description", |
46 "markdowned", |
69 "markdowned", |
47 [['True',true],['False',false]], |
70 [['True',true],['False',false]], |
48 {:selected => (@description) ? @description.markdowned : false } |
71 {:selected => (@description) ? @description.markdowned : false } |
49 .form-group
72 .form-group
50 %label{:for => "problem_url"} URL
73 %label{:for => "problem_url"} URL
51 %br/
74 %br/
52 = text_field 'problem', 'url',class: 'form-control'
75 = text_field 'problem', 'url',class: 'form-control'
53 %p
76 %p
54 Task PDF #{file_field_tag 'file'}
77 Task PDF #{file_field_tag 'file'}
55 / [eoform:problem]
78 / [eoform:problem]
@@ -1,14 +1,6
1 - .container-fluid
1 + %h1 Editing Problem
2 - = form_for @problem,url:{action: 'update'},html: {multipart: true} do
2 +
3 - .row
3 + = render 'form', problem: @problem
4 - .col-md-6
4 + .row.my-3
5 - %h1 Editing problem
6 - = render :partial => 'form'
7 - .row
8 .col-md-4
5 .col-md-4
9 - = submit_tag 'Edit', class: 'btn btn-primary btn-block'
6 + = link_to 'Back', problems_path, class: 'btn btn-secondary'
10 - .col-md-4
11 - = link_to 'Show', {:action => 'show', :id => @problem}, class: 'btn btn-default btn-block'
12 - .col-md-4
13 - = link_to 'Back', problems_path, class: 'btn btn-default btn-block'
14 - .div{style: 'height: 5em'}
@@ -1,65 +1,67
1 - content_for :head do
1 - content_for :head do
2 = stylesheet_link_tag 'problems'
2 = stylesheet_link_tag 'problems'
3 %h1 Problems
3 %h1 Problems
4 %p
4 %p
5 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
5 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
6 = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
6 = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
7 = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm'
7 = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-secondary btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-secondary btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-secondary btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-secondary btn-sm'
10 .submitbox
10 .submitbox
11 = form_tag action: 'quick_create', controller: 'problems' do
11 = form_tag action: 'quick_create', controller: 'problems' do
12 %b Quick New:
12 %b Quick New:
13 %label{:for => "problem_name"} Name
13 %label{:for => "problem_name"} Name
14 = text_field 'problem', 'name'
14 = text_field 'problem', 'name'
15 |
15 |
16 %label{:for => "problem_full_name"} Full name
16 %label{:for => "problem_full_name"} Full name
17 = text_field 'problem', 'full_name'
17 = text_field 'problem', 'full_name'
18 = submit_tag "Create"
18 = submit_tag "Create"
19 %table.table.table-condense.table-hover
19 %table.table.table-condense.table-hover
20 %thead
20 %thead
21 %th Name
21 %th Name
22 %th Full name
22 %th Full name
23 %th.text-right Full score
23 %th.text-right Full score
24 %th Tags
24 %th Tags
25 %th
25 %th
26 Submit
26 Submit
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
28 %th Date added
28 %th Date added
29 %th.text-center
29 %th.text-center
30 Avail?
30 Avail?
31 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
31 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
32 %th.text-center
32 %th.text-center
33 View Data?
33 View Data?
34 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
34 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
35 %th.text-center
35 %th.text-center
36 Test?
36 Test?
37 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
37 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
38 - if GraderConfiguration.multicontests?
38 - if GraderConfiguration.multicontests?
39 %th Contests
39 %th Contests
40 + %th.text-center
41 + %th.text-center
42 + %th.text-center
40 - for problem in @problems
43 - for problem in @problems
41 %tr{:class => "#{(problem.available) ? "bg-success bg-opacity-25" : "bg-opacity-25"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
44 %tr{:class => "#{(problem.available) ? "bg-success bg-opacity-25" : "bg-opacity-25"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
42 - @problem=problem
45 - @problem=problem
43 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
46 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
44 %td
47 %td
45 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
48 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
46 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
49 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
47 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
50 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
48 %td
51 %td
49 - problem.tags.each do |t|
52 - problem.tags.each do |t|
50 - #%button.btn.btn-default.btn-sm= t.name
53 - #%button.btn.btn-default.btn-sm= t.name
51 %span.label.label-default= t.name
54 %span.label.label-default= t.name
52 %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-sm btn-primary'
55 %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-sm btn-primary'
53 %td= problem.date_added
56 %td= problem.date_added
54 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
57 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
55 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
58 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
56 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
59 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
57 - if GraderConfiguration.multicontests?
60 - if GraderConfiguration.multicontests?
58 %td
61 %td
59 = problem.contests.collect { |c| c.name }.join(', ')
62 = problem.contests.collect { |c| c.name }.join(', ')
60 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-sm btn-block'
63 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-sm btn-block'
61 - %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-sm btn-block'
62 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-sm btn-block'
64 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-sm btn-block'
63 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-sm btn-block'
65 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-sm btn-block'
64 %br/
66 %br/
65 = link_to '[New problem]', :action => 'new'
67 = link_to '[New problem]', :action => 'new'
@@ -19,158 +19,158
19 # Any of these extensions can be disabled for a
19 # Any of these extensions can be disabled for a
20 # given input by passing: `f.input EXTENSION_NAME => false`.
20 # given input by passing: `f.input EXTENSION_NAME => false`.
21 # You can make any of these extensions optional by
21 # You can make any of these extensions optional by
22 # renaming `b.use` to `b.optional`.
22 # renaming `b.use` to `b.optional`.
23
23
24 # Determines whether to use HTML5 (:email, :url, ...)
24 # Determines whether to use HTML5 (:email, :url, ...)
25 # and required attributes
25 # and required attributes
26 b.use :html5
26 b.use :html5
27
27
28 # Calculates placeholders automatically from I18n
28 # Calculates placeholders automatically from I18n
29 # You can also pass a string as f.input placeholder: "Placeholder"
29 # You can also pass a string as f.input placeholder: "Placeholder"
30 b.use :placeholder
30 b.use :placeholder
31
31
32 ## Optional extensions
32 ## Optional extensions
33 # They are disabled unless you pass `f.input EXTENSION_NAME => true`
33 # They are disabled unless you pass `f.input EXTENSION_NAME => true`
34 # to the input. If so, they will retrieve the values from the model
34 # to the input. If so, they will retrieve the values from the model
35 # if any exists. If you want to enable any of those
35 # if any exists. If you want to enable any of those
36 # extensions by default, you can change `b.optional` to `b.use`.
36 # extensions by default, you can change `b.optional` to `b.use`.
37
37
38 # Calculates maxlength from length validations for string inputs
38 # Calculates maxlength from length validations for string inputs
39 # and/or database column lengths
39 # and/or database column lengths
40 b.optional :maxlength
40 b.optional :maxlength
41
41
42 # Calculate minlength from length validations for string inputs
42 # Calculate minlength from length validations for string inputs
43 b.optional :minlength
43 b.optional :minlength
44
44
45 # Calculates pattern from format validations for string inputs
45 # Calculates pattern from format validations for string inputs
46 b.optional :pattern
46 b.optional :pattern
47
47
48 # Calculates min and max from length validations for numeric inputs
48 # Calculates min and max from length validations for numeric inputs
49 b.optional :min_max
49 b.optional :min_max
50
50
51 # Calculates readonly automatically from readonly attributes
51 # Calculates readonly automatically from readonly attributes
52 b.optional :readonly
52 b.optional :readonly
53
53
54 ## Inputs
54 ## Inputs
55 # b.use :input, class: 'input', error_class: 'is-invalid', valid_class: 'is-valid'
55 # b.use :input, class: 'input', error_class: 'is-invalid', valid_class: 'is-valid'
56 b.use :label_input
56 b.use :label_input
57 b.use :hint, wrap_with: { tag: :span, class: :hint }
57 b.use :hint, wrap_with: { tag: :span, class: :hint }
58 b.use :error, wrap_with: { tag: :span, class: :error }
58 b.use :error, wrap_with: { tag: :span, class: :error }
59
59
60 ## full_messages_for
60 ## full_messages_for
61 # If you want to display the full error message for the attribute, you can
61 # If you want to display the full error message for the attribute, you can
62 # use the component :full_error, like:
62 # use the component :full_error, like:
63 #
63 #
64 # b.use :full_error, wrap_with: { tag: :span, class: :error }
64 # b.use :full_error, wrap_with: { tag: :span, class: :error }
65 end
65 end
66
66
67 # The default wrapper to be used by the FormBuilder.
67 # The default wrapper to be used by the FormBuilder.
68 config.default_wrapper = :default
68 config.default_wrapper = :default
69
69
70 # Define the way to render check boxes / radio buttons with labels.
70 # Define the way to render check boxes / radio buttons with labels.
71 # Defaults to :nested for bootstrap config.
71 # Defaults to :nested for bootstrap config.
72 # inline: input + label
72 # inline: input + label
73 # nested: label > input
73 # nested: label > input
74 config.boolean_style = :nested
74 config.boolean_style = :nested
75
75
76 # Default class for buttons
76 # Default class for buttons
77 config.button_class = 'btn'
77 config.button_class = 'btn'
78
78
79 # Method used to tidy up errors. Specify any Rails Array method.
79 # Method used to tidy up errors. Specify any Rails Array method.
80 # :first lists the first message for each field.
80 # :first lists the first message for each field.
81 # Use :to_sentence to list all errors for each field.
81 # Use :to_sentence to list all errors for each field.
82 # config.error_method = :first
82 # config.error_method = :first
83
83
84 # Default tag used for error notification helper.
84 # Default tag used for error notification helper.
85 config.error_notification_tag = :div
85 config.error_notification_tag = :div
86
86
87 # CSS class to add for error notification helper.
87 # CSS class to add for error notification helper.
88 config.error_notification_class = 'error_notification'
88 config.error_notification_class = 'error_notification'
89
89
90 # Series of attempts to detect a default label method for collection.
90 # Series of attempts to detect a default label method for collection.
91 # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
91 # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
92
92
93 # Series of attempts to detect a default value method for collection.
93 # Series of attempts to detect a default value method for collection.
94 # config.collection_value_methods = [ :id, :to_s ]
94 # config.collection_value_methods = [ :id, :to_s ]
95
95
96 # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
96 # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
97 # config.collection_wrapper_tag = nil
97 # config.collection_wrapper_tag = nil
98
98
99 # You can define the class to use on all collection wrappers. Defaulting to none.
99 # You can define the class to use on all collection wrappers. Defaulting to none.
100 # config.collection_wrapper_class = nil
100 # config.collection_wrapper_class = nil
101
101
102 # You can wrap each item in a collection of radio/check boxes with a tag,
102 # You can wrap each item in a collection of radio/check boxes with a tag,
103 # defaulting to :span.
103 # defaulting to :span.
104 # config.item_wrapper_tag = :span
104 # config.item_wrapper_tag = :span
105
105
106 # You can define a class to use in all item wrappers. Defaulting to none.
106 # You can define a class to use in all item wrappers. Defaulting to none.
107 # config.item_wrapper_class = nil
107 # config.item_wrapper_class = nil
108
108
109 # How the label text should be generated altogether with the required text.
109 # How the label text should be generated altogether with the required text.
110 # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
110 # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
111
111
112 # You can define the class to use on all labels. Default is nil.
112 # You can define the class to use on all labels. Default is nil.
113 # config.label_class = nil
113 # config.label_class = nil
114
114
115 - # You can define the default class to be used on forms. Can be overriden
115 + # You can define the default class to be used on forms. Can be overridden
116 # with `html: { :class }`. Defaulting to none.
116 # with `html: { :class }`. Defaulting to none.
117 # config.default_form_class = nil
117 # config.default_form_class = nil
118
118
119 # You can define which elements should obtain additional classes
119 # You can define which elements should obtain additional classes
120 # config.generate_additional_classes_for = [:wrapper, :label, :input]
120 # config.generate_additional_classes_for = [:wrapper, :label, :input]
121
121
122 # Whether attributes are required by default (or not). Default is true.
122 # Whether attributes are required by default (or not). Default is true.
123 # config.required_by_default = true
123 # config.required_by_default = true
124
124
125 # Tell browsers whether to use the native HTML5 validations (novalidate form option).
125 # Tell browsers whether to use the native HTML5 validations (novalidate form option).
126 # These validations are enabled in SimpleForm's internal config but disabled by default
126 # These validations are enabled in SimpleForm's internal config but disabled by default
127 # in this configuration, which is recommended due to some quirks from different browsers.
127 # in this configuration, which is recommended due to some quirks from different browsers.
128 # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
128 # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
129 # change this configuration to true.
129 # change this configuration to true.
130 config.browser_validations = false
130 config.browser_validations = false
131
131
132 # Custom mappings for input types. This should be a hash containing a regexp
132 # Custom mappings for input types. This should be a hash containing a regexp
133 # to match as key, and the input type that will be used when the field name
133 # to match as key, and the input type that will be used when the field name
134 # matches the regexp as value.
134 # matches the regexp as value.
135 # config.input_mappings = { /count/ => :integer }
135 # config.input_mappings = { /count/ => :integer }
136
136
137 # Custom wrappers for input types. This should be a hash containing an input
137 # Custom wrappers for input types. This should be a hash containing an input
138 # type as key and the wrapper that will be used for all inputs with specified type.
138 # type as key and the wrapper that will be used for all inputs with specified type.
139 # config.wrapper_mappings = { string: :prepend }
139 # config.wrapper_mappings = { string: :prepend }
140
140
141 # Namespaces where SimpleForm should look for custom input classes that
141 # Namespaces where SimpleForm should look for custom input classes that
142 # override default inputs.
142 # override default inputs.
143 # config.custom_inputs_namespaces << "CustomInputs"
143 # config.custom_inputs_namespaces << "CustomInputs"
144
144
145 # Default priority for time_zone inputs.
145 # Default priority for time_zone inputs.
146 # config.time_zone_priority = nil
146 # config.time_zone_priority = nil
147
147
148 # Default priority for country inputs.
148 # Default priority for country inputs.
149 # config.country_priority = nil
149 # config.country_priority = nil
150
150
151 # When false, do not use translations for labels.
151 # When false, do not use translations for labels.
152 # config.translate_labels = true
152 # config.translate_labels = true
153
153
154 # Automatically discover new inputs in Rails' autoload path.
154 # Automatically discover new inputs in Rails' autoload path.
155 # config.inputs_discovery = true
155 # config.inputs_discovery = true
156
156
157 # Cache SimpleForm inputs discovery
157 # Cache SimpleForm inputs discovery
158 # config.cache_discovery = !Rails.env.development?
158 # config.cache_discovery = !Rails.env.development?
159
159
160 # Default class for inputs
160 # Default class for inputs
161 # config.input_class = nil
161 # config.input_class = nil
162
162
163 # Define the default class of the input wrapper of the boolean input.
163 # Define the default class of the input wrapper of the boolean input.
164 config.boolean_label_class = 'checkbox'
164 config.boolean_label_class = 'checkbox'
165
165
166 # Defines if the default input wrapper class should be included in radio
166 # Defines if the default input wrapper class should be included in radio
167 # collection wrappers.
167 # collection wrappers.
168 # config.include_default_input_wrapper_class = true
168 # config.include_default_input_wrapper_class = true
169
169
170 # Defines which i18n scope will be used in Simple Form.
170 # Defines which i18n scope will be used in Simple Form.
171 # config.i18n_scope = 'simple_form'
171 # config.i18n_scope = 'simple_form'
172
172
173 # Defines validation classes to the input_field. By default it's nil.
173 # Defines validation classes to the input_field. By default it's nil.
174 # config.input_field_valid_class = 'is-valid'
174 # config.input_field_valid_class = 'is-valid'
175 # config.input_field_error_class = 'is-invalid'
175 # config.input_field_error_class = 'is-invalid'
176 end
176 end
@@ -1,440 +1,372
1 # frozen_string_literal: true
1 # frozen_string_literal: true
2
2
3 - # Please do not make direct changes to this file!
3 + # These defaults are defined and maintained by the community at
4 - # This generator is maintained by the community around simple_form-bootstrap:
4 + # https://github.com/heartcombo/simple_form-bootstrap
5 - # https://github.com/rafaelfranca/simple_form-bootstrap
5 + # Please submit feedback, changes and tests only there.
6 - # All future development, tests, and organization should happen there.
7 - # Background history: https://github.com/heartcombo/simple_form/issues/1561
8
6
9 # Uncomment this and change the path if necessary to include your own
7 # Uncomment this and change the path if necessary to include your own
10 # components.
8 # components.
11 # See https://github.com/heartcombo/simple_form#custom-components
9 # See https://github.com/heartcombo/simple_form#custom-components
12 # to know more about custom components.
10 # to know more about custom components.
13 # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
11 # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
14
12
15 # Use this setup block to configure all options available in SimpleForm.
13 # Use this setup block to configure all options available in SimpleForm.
16 SimpleForm.setup do |config|
14 SimpleForm.setup do |config|
17 # Default class for buttons
15 # Default class for buttons
18 config.button_class = 'btn'
16 config.button_class = 'btn'
19
17
20 # Define the default class of the input wrapper of the boolean input.
18 # Define the default class of the input wrapper of the boolean input.
21 config.boolean_label_class = 'form-check-label'
19 config.boolean_label_class = 'form-check-label'
22
20
23 # How the label text should be generated altogether with the required text.
21 # How the label text should be generated altogether with the required text.
24 config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }
22 config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }
25
23
26 # Define the way to render check boxes / radio buttons with labels.
24 # Define the way to render check boxes / radio buttons with labels.
27 config.boolean_style = :inline
25 config.boolean_style = :inline
28
26
29 # You can wrap each item in a collection of radio/check boxes with a tag
27 # You can wrap each item in a collection of radio/check boxes with a tag
30 config.item_wrapper_tag = :div
28 config.item_wrapper_tag = :div
31
29
32 # Defines if the default input wrapper class should be included in radio
30 # Defines if the default input wrapper class should be included in radio
33 # collection wrappers.
31 # collection wrappers.
34 config.include_default_input_wrapper_class = false
32 config.include_default_input_wrapper_class = false
35
33
36 # CSS class to add for error notification helper.
34 # CSS class to add for error notification helper.
37 config.error_notification_class = 'alert alert-danger'
35 config.error_notification_class = 'alert alert-danger'
38
36
39 # Method used to tidy up errors. Specify any Rails Array method.
37 # Method used to tidy up errors. Specify any Rails Array method.
40 # :first lists the first message for each field.
38 # :first lists the first message for each field.
41 # :to_sentence to list all errors for each field.
39 # :to_sentence to list all errors for each field.
42 config.error_method = :to_sentence
40 config.error_method = :to_sentence
43
41
44 # add validation classes to `input_field`
42 # add validation classes to `input_field`
45 config.input_field_error_class = 'is-invalid'
43 config.input_field_error_class = 'is-invalid'
46 - config.input_field_valid_class = 'is-valid'
44 + config.input_field_valid_class = 'is-valid-xxx'
47
45
48
46
49 # vertical forms
47 # vertical forms
50 #
48 #
51 # vertical default_wrapper
49 # vertical default_wrapper
52 - config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
50 + config.wrappers :vertical_form, class: 'mb-3' do |b|
53 b.use :html5
51 b.use :html5
54 b.use :placeholder
52 b.use :placeholder
55 b.optional :maxlength
53 b.optional :maxlength
56 b.optional :minlength
54 b.optional :minlength
57 b.optional :pattern
55 b.optional :pattern
58 b.optional :min_max
56 b.optional :min_max
59 b.optional :readonly
57 b.optional :readonly
60 - b.use :label
58 + b.use :label, class: 'form-label'
61 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
59 + b.use :input, class: 'form-control', error_class: 'is-invalid'
62 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
60 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
63 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
61 + b.use :hint, wrap_with: { class: 'form-text' }
64 end
62 end
65
63
66 # vertical input for boolean
64 # vertical input for boolean
67 - config.wrappers :vertical_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
65 + config.wrappers :vertical_boolean, tag: 'fieldset', class: 'mb-3' do |b|
68 b.use :html5
66 b.use :html5
69 b.optional :readonly
67 b.optional :readonly
70 - b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
68 + b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
71 - bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
69 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
72 bb.use :label, class: 'form-check-label'
70 bb.use :label, class: 'form-check-label'
73 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
71 + bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
74 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
72 + bb.use :hint, wrap_with: { class: 'form-text' }
75 end
73 end
76 end
74 end
77
75
78 # vertical input for radio buttons and check boxes
76 # vertical input for radio buttons and check boxes
79 - config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
77 + config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
80 b.use :html5
78 b.use :html5
81 b.optional :readonly
79 b.optional :readonly
82 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
80 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
83 ba.use :label_text
81 ba.use :label_text
84 end
82 end
85 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
83 + b.use :input, class: 'form-check-input', error_class: 'is-invalid'
86 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
84 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
87 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
85 + b.use :hint, wrap_with: { class: 'form-text' }
88 end
86 end
89
87
90 # vertical input for inline radio buttons and check boxes
88 # vertical input for inline radio buttons and check boxes
91 - config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
89 + config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
92 b.use :html5
90 b.use :html5
93 b.optional :readonly
91 b.optional :readonly
94 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
92 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
95 ba.use :label_text
93 ba.use :label_text
96 end
94 end
97 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
95 + b.use :input, class: 'form-check-input', error_class: 'is-invalid'
98 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
96 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
99 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
97 + b.use :hint, wrap_with: { class: 'form-text' }
100 end
98 end
101
99
102 # vertical file input
100 # vertical file input
103 - config.wrappers :vertical_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
101 + config.wrappers :vertical_file, class: 'mb-3' do |b|
104 b.use :html5
102 b.use :html5
105 b.use :placeholder
103 b.use :placeholder
106 b.optional :maxlength
104 b.optional :maxlength
107 b.optional :minlength
105 b.optional :minlength
108 b.optional :readonly
106 b.optional :readonly
109 - b.use :label
107 + b.use :label, class: 'form-label'
110 - b.use :input, class: 'form-control-file', error_class: 'is-invalid', valid_class: 'is-valid'
108 + b.use :input, class: 'form-control', error_class: 'is-invalid'
111 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
109 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
112 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
110 + b.use :hint, wrap_with: { class: 'form-text' }
111 + end
112 +
113 + # vertical select input
114 + config.wrappers :vertical_select, class: 'mb-3' do |b|
115 + b.use :html5
116 + b.optional :readonly
117 + b.use :label, class: 'form-label'
118 + b.use :input, class: 'form-select', error_class: 'is-invalid'
119 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
120 + b.use :hint, wrap_with: { class: 'form-text' }
113 end
121 end
114
122
115 # vertical multi select
123 # vertical multi select
116 - config.wrappers :vertical_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
124 + config.wrappers :vertical_multi_select, class: 'mb-3' do |b|
117 b.use :html5
125 b.use :html5
118 b.optional :readonly
126 b.optional :readonly
119 - b.use :label
127 + b.use :label, class: 'form-label'
120 - b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
128 + b.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
121 - ba.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
129 + ba.use :input, class: 'form-select mx-1', error_class: 'is-invalid'
122 end
130 end
123 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
131 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
124 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
132 + b.use :hint, wrap_with: { class: 'form-text' }
125 end
133 end
126
134
127 # vertical range input
135 # vertical range input
128 - config.wrappers :vertical_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
136 + config.wrappers :vertical_range, class: 'mb-3' do |b|
129 b.use :html5
137 b.use :html5
130 b.use :placeholder
138 b.use :placeholder
131 b.optional :readonly
139 b.optional :readonly
132 b.optional :step
140 b.optional :step
133 - b.use :label
141 + b.use :label, class: 'form-label'
134 - b.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
142 + b.use :input, class: 'form-range', error_class: 'is-invalid'
135 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
143 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
136 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
144 + b.use :hint, wrap_with: { class: 'form-text' }
137 end
145 end
138
146
139
147
140 # horizontal forms
148 # horizontal forms
141 #
149 #
142 # horizontal default_wrapper
150 # horizontal default_wrapper
143 - config.wrappers :horizontal_form, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
151 + config.wrappers :horizontal_form, class: 'row mb-3' do |b|
144 b.use :html5
152 b.use :html5
145 b.use :placeholder
153 b.use :placeholder
146 b.optional :maxlength
154 b.optional :maxlength
147 b.optional :minlength
155 b.optional :minlength
148 b.optional :pattern
156 b.optional :pattern
149 b.optional :min_max
157 b.optional :min_max
150 b.optional :readonly
158 b.optional :readonly
151 b.use :label, class: 'col-sm-3 col-form-label'
159 b.use :label, class: 'col-sm-3 col-form-label'
152 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
160 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
153 - ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
161 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
154 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
162 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
155 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
163 + ba.use :hint, wrap_with: { class: 'form-text' }
156 end
164 end
157 end
165 end
158
166
159 # horizontal input for boolean
167 # horizontal input for boolean
160 - config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
168 + config.wrappers :horizontal_boolean, class: 'row mb-3' do |b|
161 b.use :html5
169 b.use :html5
162 b.optional :readonly
170 b.optional :readonly
163 - b.wrapper tag: 'label', class: 'col-sm-3' do |ba|
171 + b.wrapper :grid_wrapper, class: 'col-sm-9 offset-sm-3' do |wr|
164 - ba.use :label_text
172 + wr.wrapper :form_check_wrapper, class: 'form-check' do |bb|
165 - end
173 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
166 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |wr|
167 - wr.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
168 - bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
169 bb.use :label, class: 'form-check-label'
174 bb.use :label, class: 'form-check-label'
170 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
175 + bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
171 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
176 + bb.use :hint, wrap_with: { class: 'form-text' }
172 end
177 end
173 end
178 end
174 end
179 end
175
180
176 # horizontal input for radio buttons and check boxes
181 # horizontal input for radio buttons and check boxes
177 - config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
182 + config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
178 b.use :html5
183 b.use :html5
179 b.optional :readonly
184 b.optional :readonly
180 b.use :label, class: 'col-sm-3 col-form-label pt-0'
185 b.use :label, class: 'col-sm-3 col-form-label pt-0'
181 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
186 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
182 - ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
187 + ba.use :input, class: 'form-check-input', error_class: 'is-invalid'
183 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
188 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
184 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
189 + ba.use :hint, wrap_with: { class: 'form-text' }
185 end
190 end
186 end
191 end
187
192
188 # horizontal input for inline radio buttons and check boxes
193 # horizontal input for inline radio buttons and check boxes
189 - config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
194 + config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
190 b.use :html5
195 b.use :html5
191 b.optional :readonly
196 b.optional :readonly
192 b.use :label, class: 'col-sm-3 col-form-label pt-0'
197 b.use :label, class: 'col-sm-3 col-form-label pt-0'
193 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
198 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
194 - ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
199 + ba.use :input, class: 'form-check-input', error_class: 'is-invalid'
195 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
200 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
196 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
201 + ba.use :hint, wrap_with: { class: 'form-text' }
197 end
202 end
198 end
203 end
199
204
200 # horizontal file input
205 # horizontal file input
201 - config.wrappers :horizontal_file, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
206 + config.wrappers :horizontal_file, class: 'row mb-3' do |b|
202 b.use :html5
207 b.use :html5
203 b.use :placeholder
208 b.use :placeholder
204 b.optional :maxlength
209 b.optional :maxlength
205 b.optional :minlength
210 b.optional :minlength
206 b.optional :readonly
211 b.optional :readonly
207 b.use :label, class: 'col-sm-3 col-form-label'
212 b.use :label, class: 'col-sm-3 col-form-label'
208 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
213 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
209 - ba.use :input, error_class: 'is-invalid', valid_class: 'is-valid'
214 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
210 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
215 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
211 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
216 + ba.use :hint, wrap_with: { class: 'form-text' }
217 + end
218 + end
219 +
220 + # horizontal select input
221 + config.wrappers :horizontal_select, class: 'row mb-3' do |b|
222 + b.use :html5
223 + b.optional :readonly
224 + b.use :label, class: 'col-sm-3 col-form-label'
225 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
226 + ba.use :input, class: 'form-select', error_class: 'is-invalid'
227 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
228 + ba.use :hint, wrap_with: { class: 'form-text' }
212 end
229 end
213 end
230 end
214
231
215 # horizontal multi select
232 # horizontal multi select
216 - config.wrappers :horizontal_multi_select, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
233 + config.wrappers :horizontal_multi_select, class: 'row mb-3' do |b|
217 b.use :html5
234 b.use :html5
218 b.optional :readonly
235 b.optional :readonly
219 b.use :label, class: 'col-sm-3 col-form-label'
236 b.use :label, class: 'col-sm-3 col-form-label'
220 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
237 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
221 - ba.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
238 + ba.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
222 - bb.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
239 + bb.use :input, class: 'form-select mx-1', error_class: 'is-invalid'
223 end
240 end
224 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
241 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
225 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
242 + ba.use :hint, wrap_with: { class: 'form-text' }
226 end
243 end
227 end
244 end
228
245
229 # horizontal range input
246 # horizontal range input
230 - config.wrappers :horizontal_range, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
247 + config.wrappers :horizontal_range, class: 'row mb-3' do |b|
231 b.use :html5
248 b.use :html5
232 b.use :placeholder
249 b.use :placeholder
233 b.optional :readonly
250 b.optional :readonly
234 b.optional :step
251 b.optional :step
235 - b.use :label, class: 'col-sm-3 col-form-label'
252 + b.use :label, class: 'col-sm-3 col-form-label pt-0'
236 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
253 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
237 - ba.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
254 + ba.use :input, class: 'form-range', error_class: 'is-invalid'
238 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
255 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
239 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
256 + ba.use :hint, wrap_with: { class: 'form-text' }
240 end
257 end
241 end
258 end
242
259
243
260
244 # inline forms
261 # inline forms
245 #
262 #
246 # inline default_wrapper
263 # inline default_wrapper
247 - config.wrappers :inline_form, tag: 'span', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
264 + config.wrappers :inline_form, class: 'col-12' do |b|
248 b.use :html5
265 b.use :html5
249 b.use :placeholder
266 b.use :placeholder
250 b.optional :maxlength
267 b.optional :maxlength
251 b.optional :minlength
268 b.optional :minlength
252 b.optional :pattern
269 b.optional :pattern
253 b.optional :min_max
270 b.optional :min_max
254 b.optional :readonly
271 b.optional :readonly
255 - b.use :label, class: 'sr-only'
272 + b.use :label, class: 'visually-hidden'
256
273
257 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
274 + b.use :input, class: 'form-control', error_class: 'is-invalid'
258 - b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
275 + b.use :error, wrap_with: { class: 'invalid-feedback' }
259 - b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
276 + b.optional :hint, wrap_with: { class: 'form-text' }
260 end
277 end
261
278
262 # inline input for boolean
279 # inline input for boolean
263 - config.wrappers :inline_boolean, tag: 'span', class: 'form-check mb-2 mr-sm-2', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
280 + config.wrappers :inline_boolean, class: 'col-12' do |b|
264 b.use :html5
281 b.use :html5
265 b.optional :readonly
282 b.optional :readonly
266 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
283 + b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
267 - b.use :label, class: 'form-check-label'
284 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
268 - b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
285 + bb.use :label, class: 'form-check-label'
269 - b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
286 + bb.use :error, wrap_with: { class: 'invalid-feedback' }
287 + bb.optional :hint, wrap_with: { class: 'form-text' }
288 + end
270 end
289 end
271
290
272
291
273 # bootstrap custom forms
292 # bootstrap custom forms
274 #
293 #
275 - # custom input for boolean
276 - config.wrappers :custom_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
277 - b.use :html5
278 - b.optional :readonly
279 - b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-checkbox' do |bb|
280 - bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
281 - bb.use :label, class: 'custom-control-label'
282 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
283 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
284 - end
285 - end
286 -
287 # custom input switch for boolean
294 # custom input switch for boolean
288 - config.wrappers :custom_boolean_switch, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
295 + config.wrappers :custom_boolean_switch, class: 'mb-3' do |b|
289 - b.use :html5
290 - b.optional :readonly
291 - b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-switch' do |bb|
292 - bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
293 - bb.use :label, class: 'custom-control-label'
294 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
295 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
296 - end
297 - end
298 -
299 - # custom input for radio buttons and check boxes
300 - config.wrappers :custom_collection, item_wrapper_class: 'custom-control', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
301 - b.use :html5
302 - b.optional :readonly
303 - b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
304 - ba.use :label_text
305 - end
306 - b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
307 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
308 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
309 - end
310 -
311 - # custom input for inline radio buttons and check boxes
312 - config.wrappers :custom_collection_inline, item_wrapper_class: 'custom-control custom-control-inline', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
313 b.use :html5
296 b.use :html5
314 b.optional :readonly
297 b.optional :readonly
315 - b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
298 + b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check form-switch' do |bb|
316 - ba.use :label_text
299 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
317 - end
300 + bb.use :label, class: 'form-check-label'
318 - b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
301 + bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
319 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
302 + bb.use :hint, wrap_with: { class: 'form-text' }
320 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
321 - end
322 -
323 - # custom file input
324 - config.wrappers :custom_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
325 - b.use :html5
326 - b.use :placeholder
327 - b.optional :maxlength
328 - b.optional :minlength
329 - b.optional :readonly
330 - b.use :label
331 - b.wrapper :custom_file_wrapper, tag: 'div', class: 'custom-file' do |ba|
332 - ba.use :input, class: 'custom-file-input', error_class: 'is-invalid', valid_class: 'is-valid'
333 - ba.use :label, class: 'custom-file-label'
334 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
335 - end
336 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
337 end
303 end
338 -
339 - # custom multi select
340 - config.wrappers :custom_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
341 - b.use :html5
342 - b.optional :readonly
343 - b.use :label
344 - b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
345 - ba.use :input, class: 'custom-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
346 - end
347 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
348 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
349 - end
350 -
351 - # custom range input
352 - config.wrappers :custom_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
353 - b.use :html5
354 - b.use :placeholder
355 - b.optional :readonly
356 - b.optional :step
357 - b.use :label
358 - b.use :input, class: 'custom-range', error_class: 'is-invalid', valid_class: 'is-valid'
359 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
360 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
361 end
304 end
362
305
363
306
364 # Input Group - custom component
307 # Input Group - custom component
365 - # see example app and config at https://github.com/rafaelfranca/simple_form-bootstrap
308 + # see example app and config at https://github.com/heartcombo/simple_form-bootstrap
366 - # config.wrappers :input_group, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
309 + config.wrappers :input_group, class: 'mb-3' do |b|
367 - # b.use :html5
368 - # b.use :placeholder
369 - # b.optional :maxlength
370 - # b.optional :minlength
371 - # b.optional :pattern
372 - # b.optional :min_max
373 - # b.optional :readonly
374 - # b.use :label
375 - # b.wrapper :input_group_tag, tag: 'div', class: 'input-group' do |ba|
376 - # ba.optional :prepend
377 - # ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
378 - # ba.optional :append
379 - # end
380 - # b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
381 - # b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
382 - # end
383 -
384 -
385 - # Floating Labels form
386 - #
387 - # floating labels default_wrapper
388 - config.wrappers :floating_labels_form, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
389 b.use :html5
310 b.use :html5
390 b.use :placeholder
311 b.use :placeholder
391 b.optional :maxlength
312 b.optional :maxlength
392 b.optional :minlength
313 b.optional :minlength
393 b.optional :pattern
314 b.optional :pattern
394 b.optional :min_max
315 b.optional :min_max
395 b.optional :readonly
316 b.optional :readonly
396 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
317 + b.use :label, class: 'form-label'
318 + b.wrapper :input_group_tag, class: 'input-group' do |ba|
319 + ba.optional :prepend
320 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
321 + ba.optional :append
322 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
323 + end
324 + b.use :hint, wrap_with: { class: 'form-text' }
325 + end
326 +
327 +
328 + # Floating Labels form
329 + #
330 + # floating labels default_wrapper
331 + config.wrappers :floating_labels_form, class: 'form-floating mb-3' do |b|
332 + b.use :html5
333 + b.use :placeholder
334 + b.optional :maxlength
335 + b.optional :minlength
336 + b.optional :pattern
337 + b.optional :min_max
338 + b.optional :readonly
339 + b.use :input, class: 'form-control', error_class: 'is-invalid'
397 b.use :label
340 b.use :label
398 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
341 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
399 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
342 + b.use :hint, wrap_with: { class: 'form-text' }
400 end
343 end
401
344
402 # custom multi select
345 # custom multi select
403 - config.wrappers :floating_labels_select, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
346 + config.wrappers :floating_labels_select, class: 'form-floating mb-3' do |b|
404 b.use :html5
347 b.use :html5
405 b.optional :readonly
348 b.optional :readonly
406 - b.use :input, class: 'custom-select', error_class: 'is-invalid', valid_class: 'is-valid'
349 + b.use :input, class: 'form-select', error_class: 'is-invalid'
407 b.use :label
350 b.use :label
408 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
351 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
409 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
352 + b.use :hint, wrap_with: { class: 'form-text' }
410 end
353 end
411
354
412
355
413 # The default wrapper to be used by the FormBuilder.
356 # The default wrapper to be used by the FormBuilder.
414 config.default_wrapper = :vertical_form
357 config.default_wrapper = :vertical_form
415
358
416 # Custom wrappers for input types. This should be a hash containing an input
359 # Custom wrappers for input types. This should be a hash containing an input
417 # type as key and the wrapper that will be used for all inputs with specified type.
360 # type as key and the wrapper that will be used for all inputs with specified type.
418 config.wrapper_mappings = {
361 config.wrapper_mappings = {
419 boolean: :vertical_boolean,
362 boolean: :vertical_boolean,
420 check_boxes: :vertical_collection,
363 check_boxes: :vertical_collection,
421 date: :vertical_multi_select,
364 date: :vertical_multi_select,
422 datetime: :vertical_multi_select,
365 datetime: :vertical_multi_select,
423 file: :vertical_file,
366 file: :vertical_file,
424 radio_buttons: :vertical_collection,
367 radio_buttons: :vertical_collection,
425 range: :vertical_range,
368 range: :vertical_range,
426 - time: :vertical_multi_select
369 + time: :vertical_multi_select,
370 + select: :vertical_select
427 }
371 }
428 -
429 - # enable custom form wrappers
430 - # config.wrapper_mappings = {
431 - # boolean: :custom_boolean,
432 - # check_boxes: :custom_collection,
433 - # date: :custom_multi_select,
434 - # datetime: :custom_multi_select,
435 - # file: :custom_file,
436 - # radio_buttons: :custom_collection,
437 - # range: :custom_range,
438 - # time: :custom_multi_select
439 - # }
440 end
372 end
@@ -1,132 +1,133
1 Rails.application.routes.draw do
1 Rails.application.routes.draw do
2 resources :tags
2 resources :tags
3 get "sources/direct_edit"
3 get "sources/direct_edit"
4
4
5 root :to => 'main#login'
5 root :to => 'main#login'
6
6
7 #logins
7 #logins
8 match 'login/login', to: 'login#login', via: [:get,:post]
8 match 'login/login', to: 'login#login', via: [:get,:post]
9
9
10 resources :contests
10 resources :contests
11 resources :sites
11 resources :sites
12 resources :test
12 resources :test
13
13
14 resources :messages do
14 resources :messages do
15 member do
15 member do
16 get 'hide'
16 get 'hide'
17 post 'reply'
17 post 'reply'
18 end
18 end
19 collection do
19 collection do
20 get 'console'
20 get 'console'
21 get 'list_all'
21 get 'list_all'
22 end
22 end
23 end
23 end
24
24
25 resources :announcements do
25 resources :announcements do
26 member do
26 member do
27 get 'toggle','toggle_front'
27 get 'toggle','toggle_front'
28 end
28 end
29 end
29 end
30
30
31 resources :problems do
31 resources :problems do
32 member do
32 member do
33 get 'toggle'
33 get 'toggle'
34 get 'toggle_test'
34 get 'toggle_test'
35 get 'toggle_view_testcase'
35 get 'toggle_view_testcase'
36 get 'stat'
36 get 'stat'
37 + get 'get_statement'
37 end
38 end
38 collection do
39 collection do
39 get 'turn_all_off'
40 get 'turn_all_off'
40 get 'turn_all_on'
41 get 'turn_all_on'
41 get 'import'
42 get 'import'
42 get 'manage'
43 get 'manage'
43 get 'quick_create'
44 get 'quick_create'
44 post 'do_manage'
45 post 'do_manage'
45 post 'do_import'
46 post 'do_import'
46 end
47 end
47 end
48 end
48
49
49 resources :groups do
50 resources :groups do
50 member do
51 member do
51 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 get 'toggle'
58 get 'toggle'
58 end
59 end
59 collection do
60 collection do
60
61
61 end
62 end
62 end
63 end
63
64
64 resources :testcases, only: [] do
65 resources :testcases, only: [] do
65 member do
66 member do
66 get 'download_input'
67 get 'download_input'
67 get 'download_sol'
68 get 'download_sol'
68 end
69 end
69 collection do
70 collection do
70 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
71 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
71 end
72 end
72 end
73 end
73
74
74 resources :grader_configuration, controller: 'configurations' do
75 resources :grader_configuration, controller: 'configurations' do
75 collection do
76 collection do
76 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
77 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
77 end
78 end
78 end
79 end
79
80
80 resources :users do
81 resources :users do
81 member do
82 member do
82 get 'toggle_activate', 'toggle_enable'
83 get 'toggle_activate', 'toggle_enable'
83 get 'stat'
84 get 'stat'
84 end
85 end
85 collection do
86 collection do
86 get 'profile'
87 get 'profile'
87 post 'chg_passwd'
88 post 'chg_passwd'
88 post 'chg_default_language'
89 post 'chg_default_language'
89 end
90 end
90 end
91 end
91
92
92 resources :submissions do
93 resources :submissions do
93 member do
94 member do
94 get 'download'
95 get 'download'
95 get 'compiler_msg'
96 get 'compiler_msg'
96 get 'rejudge'
97 get 'rejudge'
97 get 'set_tag'
98 get 'set_tag'
98 end
99 end
99 collection do
100 collection do
100 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
101 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
101 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
102 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
102 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
103 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
103 end
104 end
104 end
105 end
105
106
106
107
107 #user admin
108 #user admin
108 resources :user_admin do
109 resources :user_admin do
109 collection do
110 collection do
110 match 'bulk_manage', via: [:get, :post]
111 match 'bulk_manage', via: [:get, :post]
111 get 'bulk_mail'
112 get 'bulk_mail'
112 get 'user_stat'
113 get 'user_stat'
113 get 'import'
114 get 'import'
114 get 'new_list'
115 get 'new_list'
115 get 'admin'
116 get 'admin'
116 get 'active'
117 get 'active'
117 get 'mass_mailing'
118 get 'mass_mailing'
118 match 'modify_role', via: [:get, :post]
119 match 'modify_role', via: [:get, :post]
119 match 'create_from_list', via: [:get, :post]
120 match 'create_from_list', via: [:get, :post]
120 match 'random_all_passwords', via: [:get, :post]
121 match 'random_all_passwords', via: [:get, :post]
121 end
122 end
122 member do
123 member do
123 get 'clear_last_ip'
124 get 'clear_last_ip'
124 end
125 end
125 end
126 end
126
127
127 resources :contest_management, only: [:index] do
128 resources :contest_management, only: [:index] do
128 collection do
129 collection do
129 get 'user_stat'
130 get 'user_stat'
130 get 'clear_stat'
131 get 'clear_stat'
131 get 'clear_all_stat'
132 get 'clear_all_stat'
132 get 'change_contest_mode'
133 get 'change_contest_mode'
@@ -1,325 +1,346
1 # This file is auto-generated from the current state of the database. Instead
1 # This file is auto-generated from the current state of the database. Instead
2 # of editing this file, please use the migrations feature of Active Record to
2 # of editing this file, please use the migrations feature of Active Record to
3 # incrementally modify your database, and then regenerate this schema definition.
3 # incrementally modify your database, and then regenerate this schema definition.
4 #
4 #
5 # This file is the source Rails uses to define your schema when running `bin/rails
5 # This file is the source Rails uses to define your schema when running `bin/rails
6 # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
6 # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 # be faster and is potentially less error prone than running all of your
7 # be faster and is potentially less error prone than running all of your
8 # migrations from scratch. Old migrations may fail to apply correctly if those
8 # migrations from scratch. Old migrations may fail to apply correctly if those
9 # migrations use external dependencies or application code.
9 # migrations use external dependencies or application code.
10 #
10 #
11 # It's strongly recommended that you check this file into your version control system.
11 # It's strongly recommended that you check this file into your version control system.
12
12
13 - ActiveRecord::Schema[7.0].define(version: 2022_02_04_080936) do
13 + ActiveRecord::Schema[7.0].define(version: 2022_09_27_074644) do
14 - create_table "announcements", id: :integer, charset: "utf8", force: :cascade do |t|
14 + create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
15 + t.string "name", null: false
16 + t.string "record_type", null: false
17 + t.bigint "record_id", null: false
18 + t.bigint "blob_id", null: false
19 + t.datetime "created_at", null: false
20 + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
21 + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
22 + end
23 +
24 + create_table "active_storage_blobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
25 + t.string "key", null: false
26 + t.string "filename", null: false
27 + t.string "content_type"
28 + t.text "metadata"
29 + t.string "service_name", null: false
30 + t.bigint "byte_size", null: false
31 + t.string "checksum"
32 + t.datetime "created_at", null: false
33 + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
34 + end
35 +
36 + create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
37 + t.bigint "blob_id", null: false
38 + t.string "variation_digest", null: false
39 + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
40 + end
41 +
42 + create_table "announcements", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
15 t.string "author"
43 t.string "author"
16 - t.text "body", size: :medium
44 + t.text "body"
17 t.boolean "published"
45 t.boolean "published"
18 t.datetime "created_at", precision: nil, null: false
46 t.datetime "created_at", precision: nil, null: false
19 t.datetime "updated_at", precision: nil, null: false
47 t.datetime "updated_at", precision: nil, null: false
20 t.boolean "frontpage", default: false
48 t.boolean "frontpage", default: false
21 t.boolean "contest_only", default: false
49 t.boolean "contest_only", default: false
22 t.string "title"
50 t.string "title"
23 t.string "notes"
51 t.string "notes"
24 t.boolean "on_nav_bar", default: false
52 t.boolean "on_nav_bar", default: false
25 end
53 end
26
54
27 - create_table "contests", id: :integer, charset: "utf8", force: :cascade do |t|
55 + create_table "contests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
28 t.string "title"
56 t.string "title"
29 t.boolean "enabled"
57 t.boolean "enabled"
30 t.datetime "created_at", precision: nil, null: false
58 t.datetime "created_at", precision: nil, null: false
31 t.datetime "updated_at", precision: nil, null: false
59 t.datetime "updated_at", precision: nil, null: false
32 t.string "name"
60 t.string "name"
33 end
61 end
34
62
35 - create_table "contests_problems", id: false, charset: "utf8", force: :cascade do |t|
63 + create_table "contests_problems", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
36 t.integer "contest_id"
64 t.integer "contest_id"
37 t.integer "problem_id"
65 t.integer "problem_id"
38 end
66 end
39
67
40 - create_table "contests_users", id: false, charset: "utf8", force: :cascade do |t|
68 + create_table "contests_users", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
41 t.integer "contest_id"
69 t.integer "contest_id"
42 t.integer "user_id"
70 t.integer "user_id"
43 end
71 end
44
72
45 - create_table "countries", id: :integer, charset: "utf8", force: :cascade do |t|
73 + create_table "countries", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
46 t.string "name"
74 t.string "name"
47 t.datetime "created_at", precision: nil, null: false
75 t.datetime "created_at", precision: nil, null: false
48 t.datetime "updated_at", precision: nil, null: false
76 t.datetime "updated_at", precision: nil, null: false
49 end
77 end
50
78
51 - create_table "descriptions", id: :integer, charset: "utf8", force: :cascade do |t|
79 + create_table "descriptions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
52 - t.text "body", size: :medium
80 + t.text "body"
53 t.boolean "markdowned"
81 t.boolean "markdowned"
54 t.datetime "created_at", precision: nil, null: false
82 t.datetime "created_at", precision: nil, null: false
55 t.datetime "updated_at", precision: nil, null: false
83 t.datetime "updated_at", precision: nil, null: false
56 end
84 end
57
85
58 - create_table "grader_configurations", id: :integer, charset: "utf8", force: :cascade do |t|
86 + create_table "grader_configurations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
59 t.string "key"
87 t.string "key"
60 t.string "value_type"
88 t.string "value_type"
61 t.string "value"
89 t.string "value"
62 t.datetime "created_at", precision: nil, null: false
90 t.datetime "created_at", precision: nil, null: false
63 t.datetime "updated_at", precision: nil, null: false
91 t.datetime "updated_at", precision: nil, null: false
64 - t.text "description", size: :medium
92 + t.text "description"
65 end
93 end
66
94
67 - create_table "grader_processes", id: :integer, charset: "utf8", force: :cascade do |t|
95 + create_table "grader_processes", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
68 t.string "host"
96 t.string "host"
69 t.integer "pid"
97 t.integer "pid"
70 t.string "mode"
98 t.string "mode"
71 t.boolean "active"
99 t.boolean "active"
72 t.datetime "created_at", precision: nil, null: false
100 t.datetime "created_at", precision: nil, null: false
73 t.datetime "updated_at", precision: nil, null: false
101 t.datetime "updated_at", precision: nil, null: false
74 t.integer "task_id"
102 t.integer "task_id"
75 t.string "task_type"
103 t.string "task_type"
76 t.boolean "terminated"
104 t.boolean "terminated"
77 t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
105 t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
78 end
106 end
79
107
80 create_table "groups", id: :integer, charset: "latin1", force: :cascade do |t|
108 create_table "groups", id: :integer, charset: "latin1", force: :cascade do |t|
81 t.string "name"
109 t.string "name"
82 t.string "description"
110 t.string "description"
83 t.boolean "enabled", default: true
111 t.boolean "enabled", default: true
84 end
112 end
85
113
86 create_table "groups_problems", id: false, charset: "latin1", force: :cascade do |t|
114 create_table "groups_problems", id: false, charset: "latin1", force: :cascade do |t|
87 t.integer "problem_id", null: false
115 t.integer "problem_id", null: false
88 t.integer "group_id", null: false
116 t.integer "group_id", null: false
89 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
117 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
90 end
118 end
91
119
92 create_table "groups_users", charset: "latin1", force: :cascade do |t|
120 create_table "groups_users", charset: "latin1", force: :cascade do |t|
93 t.integer "group_id", null: false
121 t.integer "group_id", null: false
94 t.integer "user_id", null: false
122 t.integer "user_id", null: false
95 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
123 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
96 end
124 end
97
125
98 - create_table "heart_beats", id: :integer, charset: "latin1", force: :cascade do |t|
126 + create_table "heart_beats", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
99 t.integer "user_id"
127 t.integer "user_id"
100 t.string "ip_address"
128 t.string "ip_address"
101 t.datetime "created_at", precision: nil, null: false
129 t.datetime "created_at", precision: nil, null: false
102 t.datetime "updated_at", precision: nil, null: false
130 t.datetime "updated_at", precision: nil, null: false
103 t.string "status"
131 t.string "status"
104 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
132 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
105 end
133 end
106
134
107 - create_table "languages", id: :integer, charset: "utf8", force: :cascade do |t|
135 + create_table "languages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
108 t.string "name", limit: 10
136 t.string "name", limit: 10
109 t.string "pretty_name"
137 t.string "pretty_name"
110 t.string "ext", limit: 10
138 t.string "ext", limit: 10
111 t.string "common_ext"
139 t.string "common_ext"
112 end
140 end
113
141
114 - create_table "logins", id: :integer, charset: "latin1", force: :cascade do |t|
142 + create_table "logins", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
115 t.integer "user_id"
143 t.integer "user_id"
116 t.string "ip_address"
144 t.string "ip_address"
117 t.datetime "created_at", precision: nil, null: false
145 t.datetime "created_at", precision: nil, null: false
118 t.datetime "updated_at", precision: nil, null: false
146 t.datetime "updated_at", precision: nil, null: false
119 t.index ["user_id"], name: "index_logins_on_user_id"
147 t.index ["user_id"], name: "index_logins_on_user_id"
120 end
148 end
121
149
122 - create_table "messages", id: :integer, charset: "utf8", force: :cascade do |t|
150 + create_table "messages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
123 t.integer "sender_id"
151 t.integer "sender_id"
124 t.integer "receiver_id"
152 t.integer "receiver_id"
125 t.integer "replying_message_id"
153 t.integer "replying_message_id"
126 - t.text "body", size: :medium
154 + t.text "body"
127 t.boolean "replied"
155 t.boolean "replied"
128 t.datetime "created_at", precision: nil, null: false
156 t.datetime "created_at", precision: nil, null: false
129 t.datetime "updated_at", precision: nil, null: false
157 t.datetime "updated_at", precision: nil, null: false
130 end
158 end
131
159
132 - create_table "problems", id: :bigint, default: nil, charset: "utf8", force: :cascade do |t|
160 + create_table "problems", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
133 - t.string "name", limit: 100
161 + t.string "name", limit: 30
134 t.string "full_name"
162 t.string "full_name"
135 t.integer "full_score"
163 t.integer "full_score"
136 t.date "date_added"
164 t.date "date_added"
137 t.boolean "available"
165 t.boolean "available"
138 t.string "url"
166 t.string "url"
139 t.integer "description_id"
167 t.integer "description_id"
140 t.boolean "test_allowed"
168 t.boolean "test_allowed"
141 t.boolean "output_only"
169 t.boolean "output_only"
142 t.string "description_filename"
170 t.string "description_filename"
143 t.boolean "view_testcase"
171 t.boolean "view_testcase"
144 t.integer "difficulty"
172 t.integer "difficulty"
173 + t.text "description"
174 + t.boolean "markdown"
145 end
175 end
146
176
147 - create_table "problems_tags", id: :bigint, default: nil, charset: "latin1", force: :cascade do |t|
177 + create_table "problems_tags", id: :integer, charset: "latin1", force: :cascade do |t|
148 - t.bigint "problem_id"
178 + t.integer "problem_id"
149 t.integer "tag_id"
179 t.integer "tag_id"
150 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
180 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
151 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
181 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
152 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
182 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
153 end
183 end
154
184
155 - create_table "rights", id: :integer, charset: "utf8", force: :cascade do |t|
185 + create_table "rights", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
156 t.string "name"
186 t.string "name"
157 t.string "controller"
187 t.string "controller"
158 t.string "action"
188 t.string "action"
159 end
189 end
160
190
161 - create_table "rights_roles", id: false, charset: "utf8", force: :cascade do |t|
191 + create_table "rights_roles", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
162 t.integer "right_id"
192 t.integer "right_id"
163 t.integer "role_id"
193 t.integer "role_id"
164 t.index ["role_id"], name: "index_rights_roles_on_role_id"
194 t.index ["role_id"], name: "index_rights_roles_on_role_id"
165 end
195 end
166
196
167 - create_table "roles", id: :integer, charset: "utf8", force: :cascade do |t|
197 + create_table "roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
168 t.string "name"
198 t.string "name"
169 end
199 end
170
200
171 - create_table "roles_users", id: false, charset: "utf8", force: :cascade do |t|
201 + create_table "roles_users", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
172 t.integer "role_id"
202 t.integer "role_id"
173 t.integer "user_id"
203 t.integer "user_id"
174 t.index ["user_id"], name: "index_roles_users_on_user_id"
204 t.index ["user_id"], name: "index_roles_users_on_user_id"
175 end
205 end
176
206
177 - create_table "sessions", id: :integer, charset: "utf8", force: :cascade do |t|
207 + create_table "sessions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
178 t.string "session_id"
208 t.string "session_id"
179 - t.text "data", size: :medium
209 + t.text "data"
180 t.datetime "updated_at", precision: nil
210 t.datetime "updated_at", precision: nil
181 t.index ["session_id"], name: "index_sessions_on_session_id"
211 t.index ["session_id"], name: "index_sessions_on_session_id"
182 t.index ["updated_at"], name: "index_sessions_on_updated_at"
212 t.index ["updated_at"], name: "index_sessions_on_updated_at"
183 end
213 end
184
214
185 - create_table "sites", id: :integer, charset: "utf8", force: :cascade do |t|
215 + create_table "sites", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
186 t.string "name"
216 t.string "name"
187 t.boolean "started"
217 t.boolean "started"
188 t.datetime "start_time", precision: nil
218 t.datetime "start_time", precision: nil
189 t.datetime "created_at", precision: nil, null: false
219 t.datetime "created_at", precision: nil, null: false
190 t.datetime "updated_at", precision: nil, null: false
220 t.datetime "updated_at", precision: nil, null: false
191 t.integer "country_id"
221 t.integer "country_id"
192 t.string "password"
222 t.string "password"
193 end
223 end
194
224
195 - create_table "solutions", charset: "latin1", force: :cascade do |t|
225 + create_table "submission_view_logs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
196 - t.string "solution"
197 - t.bigint "problem_id"
198 - t.bigint "submission_id"
199 - t.integer "type"
200 - t.index ["problem_id"], name: "index_solutions_on_problem_id"
201 - t.index ["submission_id"], name: "index_solutions_on_submission_id"
202 - end
203 -
204 - create_table "submission_view_logs", id: :integer, charset: "latin1", force: :cascade do |t|
205 t.integer "user_id"
226 t.integer "user_id"
206 t.integer "submission_id"
227 t.integer "submission_id"
207 t.datetime "created_at", precision: nil, null: false
228 t.datetime "created_at", precision: nil, null: false
208 t.datetime "updated_at", precision: nil, null: false
229 t.datetime "updated_at", precision: nil, null: false
209 end
230 end
210
231
211 - create_table "submissions", id: :bigint, default: nil, charset: "utf8", force: :cascade do |t|
232 + create_table "submissions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
212 t.integer "user_id"
233 t.integer "user_id"
213 t.integer "problem_id"
234 t.integer "problem_id"
214 t.integer "language_id"
235 t.integer "language_id"
215 t.text "source", size: :medium
236 t.text "source", size: :medium
216 t.binary "binary"
237 t.binary "binary"
217 t.datetime "submitted_at", precision: nil
238 t.datetime "submitted_at", precision: nil
218 t.datetime "compiled_at", precision: nil
239 t.datetime "compiled_at", precision: nil
219 - t.text "compiler_message", size: :medium
240 + t.text "compiler_message"
220 t.datetime "graded_at", precision: nil
241 t.datetime "graded_at", precision: nil
221 t.integer "points"
242 t.integer "points"
222 - t.text "grader_comment", size: :medium
243 + t.text "grader_comment"
223 t.integer "number"
244 t.integer "number"
224 t.string "source_filename"
245 t.string "source_filename"
225 t.float "max_runtime"
246 t.float "max_runtime"
226 t.integer "peak_memory"
247 t.integer "peak_memory"
227 t.integer "effective_code_length"
248 t.integer "effective_code_length"
228 t.string "ip_address"
249 t.string "ip_address"
229 t.integer "tag", default: 0
250 t.integer "tag", default: 0
230 t.index ["submitted_at"], name: "index_submissions_on_submitted_at"
251 t.index ["submitted_at"], name: "index_submissions_on_submitted_at"
231 t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
252 t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
232 t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
253 t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
233 end
254 end
234
255
235 create_table "tags", id: :integer, charset: "latin1", force: :cascade do |t|
256 create_table "tags", id: :integer, charset: "latin1", force: :cascade do |t|
236 t.string "name", null: false
257 t.string "name", null: false
237 t.text "description"
258 t.text "description"
238 t.boolean "public"
259 t.boolean "public"
239 t.datetime "created_at", precision: nil, null: false
260 t.datetime "created_at", precision: nil, null: false
240 t.datetime "updated_at", precision: nil, null: false
261 t.datetime "updated_at", precision: nil, null: false
241 end
262 end
242
263
243 - create_table "tasks", id: :integer, charset: "utf8", force: :cascade do |t|
264 + create_table "tasks", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
244 t.integer "submission_id"
265 t.integer "submission_id"
245 t.datetime "created_at", precision: nil
266 t.datetime "created_at", precision: nil
246 t.integer "status"
267 t.integer "status"
247 t.datetime "updated_at", precision: nil
268 t.datetime "updated_at", precision: nil
248 t.index ["status"], name: "index_tasks_on_status"
269 t.index ["status"], name: "index_tasks_on_status"
249 t.index ["submission_id"], name: "index_tasks_on_submission_id"
270 t.index ["submission_id"], name: "index_tasks_on_submission_id"
250 end
271 end
251
272
252 - create_table "test_pairs", id: :integer, charset: "utf8", force: :cascade do |t|
273 + create_table "test_pairs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
253 t.integer "problem_id"
274 t.integer "problem_id"
254 - t.text "input", size: :long
275 + t.text "input", size: :medium
255 - t.text "solution", size: :long
276 + t.text "solution", size: :medium
256 t.datetime "created_at", precision: nil, null: false
277 t.datetime "created_at", precision: nil, null: false
257 t.datetime "updated_at", precision: nil, null: false
278 t.datetime "updated_at", precision: nil, null: false
258 end
279 end
259
280
260 - create_table "test_requests", id: :integer, charset: "utf8", force: :cascade do |t|
281 + create_table "test_requests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
261 t.integer "user_id"
282 t.integer "user_id"
262 t.integer "problem_id"
283 t.integer "problem_id"
263 t.integer "submission_id"
284 t.integer "submission_id"
264 t.string "input_file_name"
285 t.string "input_file_name"
265 t.string "output_file_name"
286 t.string "output_file_name"
266 t.string "running_stat"
287 t.string "running_stat"
267 t.integer "status"
288 t.integer "status"
268 t.datetime "updated_at", precision: nil, null: false
289 t.datetime "updated_at", precision: nil, null: false
269 t.datetime "submitted_at", precision: nil
290 t.datetime "submitted_at", precision: nil
270 t.datetime "compiled_at", precision: nil
291 t.datetime "compiled_at", precision: nil
271 - t.text "compiler_message", size: :medium
292 + t.text "compiler_message"
272 t.datetime "graded_at", precision: nil
293 t.datetime "graded_at", precision: nil
273 t.string "grader_comment"
294 t.string "grader_comment"
274 t.datetime "created_at", precision: nil, null: false
295 t.datetime "created_at", precision: nil, null: false
275 t.float "running_time"
296 t.float "running_time"
276 t.string "exit_status"
297 t.string "exit_status"
277 t.integer "memory_usage"
298 t.integer "memory_usage"
278 t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
299 t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
279 end
300 end
280
301
281 create_table "testcases", id: :integer, charset: "latin1", force: :cascade do |t|
302 create_table "testcases", id: :integer, charset: "latin1", force: :cascade do |t|
282 t.integer "problem_id"
303 t.integer "problem_id"
283 t.integer "num"
304 t.integer "num"
284 t.integer "group"
305 t.integer "group"
285 t.integer "score"
306 t.integer "score"
286 t.text "input", size: :long
307 t.text "input", size: :long
287 t.text "sol", size: :long
308 t.text "sol", size: :long
288 - t.datetime "created_at", precision: nil, null: false
309 + t.datetime "created_at", precision: nil
289 - t.datetime "updated_at", precision: nil, null: false
310 + t.datetime "updated_at", precision: nil
290 t.index ["problem_id"], name: "index_testcases_on_problem_id"
311 t.index ["problem_id"], name: "index_testcases_on_problem_id"
291 end
312 end
292
313
293 - create_table "user_contest_stats", id: :integer, charset: "utf8", force: :cascade do |t|
314 + create_table "user_contest_stats", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
294 t.integer "user_id"
315 t.integer "user_id"
295 t.datetime "started_at", precision: nil
316 t.datetime "started_at", precision: nil
296 t.datetime "created_at", precision: nil, null: false
317 t.datetime "created_at", precision: nil, null: false
297 t.datetime "updated_at", precision: nil, null: false
318 t.datetime "updated_at", precision: nil, null: false
298 t.boolean "forced_logout"
319 t.boolean "forced_logout"
299 end
320 end
300
321
301 - create_table "users", id: :integer, charset: "utf8", force: :cascade do |t|
322 + create_table "users", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
302 t.string "login", limit: 50
323 t.string "login", limit: 50
303 t.string "full_name"
324 t.string "full_name"
304 t.string "hashed_password"
325 t.string "hashed_password"
305 t.string "salt", limit: 5
326 t.string "salt", limit: 5
306 t.string "alias"
327 t.string "alias"
307 t.string "email"
328 t.string "email"
308 t.integer "site_id"
329 t.integer "site_id"
309 t.integer "country_id"
330 t.integer "country_id"
310 t.boolean "activated", default: false
331 t.boolean "activated", default: false
311 t.datetime "created_at", precision: nil
332 t.datetime "created_at", precision: nil
312 t.datetime "updated_at", precision: nil
333 t.datetime "updated_at", precision: nil
313 - t.string "section"
314 t.boolean "enabled", default: true
334 t.boolean "enabled", default: true
315 t.string "remark"
335 t.string "remark"
316 t.string "last_ip"
336 t.string "last_ip"
337 + t.string "section"
317 t.integer "default_language"
338 t.integer "default_language"
318 t.index ["login"], name: "index_users_on_login", unique: true
339 t.index ["login"], name: "index_users_on_login", unique: true
319 end
340 end
320
341
342 + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
343 + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
321 add_foreign_key "problems_tags", "problems"
344 add_foreign_key "problems_tags", "problems"
322 add_foreign_key "problems_tags", "tags"
345 add_foreign_key "problems_tags", "tags"
323 - add_foreign_key "solutions", "problems"
324 - add_foreign_key "solutions", "submissions"
325 end
346 end
deleted file
You need to be logged in to leave comments. Login now