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