Description:
modernize problem
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
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 |
@@ -14,24 +14,25 | |||
|
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/* |
@@ -56,49 +56,50 | |||
|
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 |
@@ -1,24 +1,33 | |||
|
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) |
@@ -226,51 +235,48 | |||
|
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) |
@@ -294,37 +300,37 | |||
|
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 | |
|
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 |
@@ -38,52 +38,48 | |||
|
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 |
@@ -1,55 +1,62 | |||
|
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) } |
@@ -1,179 +1,155 | |||
|
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 |
- |
|
|
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 | - 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" | |
|
101 | - @problem.save | |
|
102 | - end | |
|
103 | - redirect_to :action => 'show', :id => @problem | |
|
80 | + flash[:notice] = 'Problem was successfully updated. ' | |
|
81 | + flash[:notice] += 'A new statement PDF is uploaded' if problem_params[:statement] | |
|
82 | + @problem.save | |
|
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 |
|
|
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 |
@@ -274,33 +250,38 | |||
|
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, |
|
|
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 |
@@ -188,25 +188,36 | |||
|
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 | |
|
218 | + end | |
|
219 | + return class_name if ok && options.size > 0 | |
|
220 | + return '' | |
|
221 | + end | |
|
222 | + | |
|
212 | 223 | end |
@@ -1,50 +1,54 | |||
|
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) |
@@ -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'}, |
|
|
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,55 +1,78 | |||
|
1 | - = error_messages_for 'problem' | |
|
2 | - / [form:problem] | |
|
3 | - .form-group | |
|
4 | - %label{:for => "problem_name"} Name | |
|
5 | - = text_field 'problem', 'name', class: 'form-control' | |
|
6 | - %small | |
|
7 | - Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead. | |
|
8 | - .form-group | |
|
9 | - %label{:for => "problem_full_name"} Full name | |
|
10 | - = text_field 'problem', 'full_name', class: 'form-control' | |
|
11 | - .form-group | |
|
12 | - %label{:for => "problem_full_score"} Full score | |
|
13 | - = text_field 'problem', 'full_score', class: 'form-control' | |
|
14 | - .form-group | |
|
15 | - %label{:for => "problem_full_score"} Tags | |
|
16 | - = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'}) | |
|
17 | - .form-group | |
|
18 | - %label{:for => "problem_date_added"} Date added | |
|
19 | - = date_select 'problem', 'date_added', class: 'form-control' | |
|
20 | - - # TODO: these should be put in model Problem, but I can't think of | |
|
21 | - - # nice default values for them. These values look fine only | |
|
22 | - - # in this case (of lazily adding new problems). | |
|
23 | - - @problem.available = true if @problem!=nil and @problem.available==nil | |
|
24 | - - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil | |
|
25 | - - @problem.output_only = false if @problem!=nil and @problem.output_only==nil | |
|
26 | - .checkbox | |
|
27 |
- %label{:for => "problem_ |
|
|
28 | - = check_box :problem, :available | |
|
29 | - Available? | |
|
30 | - .checkbox | |
|
31 | - %label{:for => "problem_test_allowed"} | |
|
32 | - = check_box :problem, :test_allowed | |
|
33 | - Test allowed? | |
|
34 | - .checkbox | |
|
35 |
- %label{:for => "problem_ |
|
|
36 | - = check_box :problem, :output_only | |
|
37 | - Output only? | |
|
38 | - = error_messages_for 'description' | |
|
39 | - .form-group | |
|
40 | - %label{:for => "description_body"} Description | |
|
41 | - %br/ | |
|
42 | - = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' | |
|
43 | - .form-group | |
|
44 | - %label{:for => "description_markdowned"} Markdowned? | |
|
45 | - = select "description", | | |
|
46 | - "markdowned", | | |
|
47 | - [['True',true],['False',false]], | | |
|
48 | - {:selected => (@description) ? @description.markdowned : false } | | |
|
49 | - .form-group | |
|
50 |
- %label{:for => "problem_ |
|
|
51 | - %br/ | |
|
52 | - = text_field 'problem', 'url',class: 'form-control' | |
|
53 | - %p | |
|
54 | - Task PDF #{file_field_tag 'file'} | |
|
55 | - / [eoform:problem] | |
|
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 | + -# | |
|
23 | + = error_messages_for 'problem' | |
|
24 | + | |
|
25 | + / [form:problem] | |
|
26 | + .form-group | |
|
27 | + %label{:for => "problem_name"} Name | |
|
28 | + = text_field 'problem', 'name', class: 'form-control' | |
|
29 | + %small | |
|
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. | |
|
31 | + .form-group | |
|
32 | + %label{:for => "problem_full_name"} Full name | |
|
33 | + = text_field 'problem', 'full_name', class: 'form-control' | |
|
34 | + .form-group | |
|
35 | + %label{:for => "problem_full_score"} Full score | |
|
36 | + = text_field 'problem', 'full_score', class: 'form-control' | |
|
37 | + .form-group | |
|
38 | + %label{:for => "problem_full_score"} Tags | |
|
39 | + = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'}) | |
|
40 | + .form-group | |
|
41 | + %label{:for => "problem_date_added"} Date added | |
|
42 | + = date_select 'problem', 'date_added', class: 'form-control' | |
|
43 | + - # TODO: these should be put in model Problem, but I can't think of | |
|
44 | + - # nice default values for them. These values look fine only | |
|
45 | + - # in this case (of lazily adding new problems). | |
|
46 | + - @problem.available = true if @problem!=nil and @problem.available==nil | |
|
47 | + - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil | |
|
48 | + - @problem.output_only = false if @problem!=nil and @problem.output_only==nil | |
|
49 | + .checkbox | |
|
50 | + %label{:for => "problem_available"} | |
|
51 | + = check_box :problem, :available | |
|
52 | + Available? | |
|
53 | + .checkbox | |
|
54 | + %label{:for => "problem_test_allowed"} | |
|
55 | + = check_box :problem, :test_allowed | |
|
56 | + Test allowed? | |
|
57 | + .checkbox | |
|
58 | + %label{:for => "problem_output_only"} | |
|
59 | + = check_box :problem, :output_only | |
|
60 | + Output only? | |
|
61 | + = error_messages_for 'description' | |
|
62 | + .form-group | |
|
63 | + %label{:for => "description_body"} Description | |
|
64 | + %br/ | |
|
65 | + = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' | |
|
66 | + .form-group | |
|
67 | + %label{:for => "description_markdowned"} Markdowned? | |
|
68 | + = select "description", | | |
|
69 | + "markdowned", | | |
|
70 | + [['True',true],['False',false]], | | |
|
71 | + {:selected => (@description) ? @description.markdowned : false } | | |
|
72 | + .form-group | |
|
73 | + %label{:for => "problem_url"} URL | |
|
74 | + %br/ | |
|
75 | + = text_field 'problem', 'url',class: 'form-control' | |
|
76 | + %p | |
|
77 | + Task PDF #{file_field_tag 'file'} | |
|
78 | + / [eoform:problem] |
@@ -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 | |
|
8 | - .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'} | |
|
1 | + %h1 Editing 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' |
@@ -16,50 +16,52 | |||
|
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' |
@@ -91,49 +91,49 | |||
|
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 } |
@@ -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' |
|
|
62 |
- b.use :full_error, wrap_with: { |
|
|
63 |
- b.use :hint, wrap_with: { |
|
|
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: ' |
|
|
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 |
|
|
71 |
- bb.use :input, class: 'form-check-input', error_class: 'is-invalid' |
|
|
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: { |
|
|
74 |
- bb.use :hint, wrap_with: { |
|
|
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: ' |
|
|
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' |
|
|
86 |
- b.use :full_error, wrap_with: { |
|
|
87 |
- b.use :hint, wrap_with: { |
|
|
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: ' |
|
|
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' |
|
|
98 |
- b.use :full_error, wrap_with: { |
|
|
99 |
- b.use :hint, wrap_with: { |
|
|
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 |
|
|
111 |
- b.use :full_error, wrap_with: { |
|
|
112 |
- b.use :hint, wrap_with: { |
|
|
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 |
|
|
121 |
- ba.use :input, class: 'form-c |
|
|
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: { |
|
|
124 |
- b.use :hint, wrap_with: { |
|
|
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- |
|
|
135 |
- b.use :full_error, wrap_with: { |
|
|
136 |
- b.use :hint, wrap_with: { |
|
|
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 |
|
|
153 |
- ba.use :input, class: 'form-control', error_class: 'is-invalid' |
|
|
154 |
- ba.use :full_error, wrap_with: { |
|
|
155 |
- ba.use :hint, wrap_with: { |
|
|
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 |
|
|
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: { |
|
|
171 |
- bb.use :hint, wrap_with: { |
|
|
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', |
|
|
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 |
|
|
182 |
- ba.use :input, class: 'form-check-input', error_class: 'is-invalid' |
|
|
183 |
- ba.use :full_error, wrap_with: { |
|
|
184 |
- ba.use :hint, wrap_with: { |
|
|
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', |
|
|
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 |
|
|
194 |
- ba.use :input, class: 'form-check-input', error_class: 'is-invalid' |
|
|
195 |
- ba.use :full_error, wrap_with: { |
|
|
196 |
- ba.use :hint, wrap_with: { |
|
|
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 |
|
|
209 |
- ba.use :input, error_class: 'is-invalid' |
|
|
210 |
- ba.use :full_error, wrap_with: { |
|
|
211 |
- ba.use :hint, wrap_with: { |
|
|
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 |
|
|
221 |
- ba.wrapper |
|
|
222 |
- bb.use :input, class: 'form-c |
|
|
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: { |
|
|
225 |
- ba.use :hint, wrap_with: { |
|
|
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 |
|
|
237 |
- ba.use :input, class: 'form- |
|
|
238 |
- ba.use :full_error, wrap_with: { |
|
|
239 |
- ba.use :hint, wrap_with: { |
|
|
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: ' |
|
|
272 | + b.use :label, class: 'visually-hidden' | |
|
256 | 273 | |
|
257 |
- b.use :input, class: 'form-control', error_class: 'is-invalid' |
|
|
258 |
- b.use :error, wrap_with: { |
|
|
259 |
- b.optional :hint, wrap_with: { |
|
|
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 : |
|
|
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 : |
|
|
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' } | |
|
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' } | |
|
335 | 303 | end |
|
336 | - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' } | |
|
337 | - 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/ |
|
|
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: { |
|
|
399 |
- b.use :hint, wrap_with: { |
|
|
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, |
|
|
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: ' |
|
|
349 | + b.use :input, class: 'form-select', error_class: 'is-invalid' | |
|
407 | 350 | b.use :label |
|
408 |
- b.use :full_error, wrap_with: { |
|
|
409 |
- b.use :hint, wrap_with: { |
|
|
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 |
@@ -13,48 +13,49 | |||
|
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 |
@@ -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 |
|
|
14 |
- create_table "a |
|
|
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" |
|
|
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" |
|
|
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" |
|
|
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: " |
|
|
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: " |
|
|
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" |
|
|
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: : |
|
|
133 |
- t.string "name", limit: |
|
|
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: : |
|
|
148 |
- t. |
|
|
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" |
|
|
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 "s |
|
|
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: : |
|
|
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" |
|
|
240 | + t.text "compiler_message" | |
|
220 | 241 | t.datetime "graded_at", precision: nil |
|
221 | 242 | t.integer "points" |
|
222 |
- t.text "grader_comment" |
|
|
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: : |
|
|
255 |
- t.text "solution", size: : |
|
|
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" |
|
|
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 |
|
|
289 |
- t.datetime "updated_at", precision: nil |
|
|
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 |
modified file |
deleted file |
You need to be logged in to leave comments.
Login now