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

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

@@ -0,0 +1,6
1 + %h1 New Problem
2 +
3 + = render 'form', problem: @problem
4 + .row.my-3
5 + .col-md-4
6 + = link_to 'Back', problems_path, class: 'btn btn-secondary'
@@ -0,0 +1,57
1 + # This migration comes from active_storage (originally 20170806125915)
2 + class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
3 + def change
4 + # Use Active Record's configured type for primary and foreign keys
5 + primary_key_type, foreign_key_type = primary_and_foreign_key_types
6 +
7 + create_table :active_storage_blobs, id: primary_key_type do |t|
8 + t.string :key, null: false
9 + t.string :filename, null: false
10 + t.string :content_type
11 + t.text :metadata
12 + t.string :service_name, null: false
13 + t.bigint :byte_size, null: false
14 + t.string :checksum
15 +
16 + if connection.supports_datetime_with_precision?
17 + t.datetime :created_at, precision: 6, null: false
18 + else
19 + t.datetime :created_at, null: false
20 + end
21 +
22 + t.index [ :key ], unique: true
23 + end
24 +
25 + create_table :active_storage_attachments, id: primary_key_type do |t|
26 + t.string :name, null: false
27 + t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28 + t.references :blob, null: false, type: foreign_key_type
29 +
30 + if connection.supports_datetime_with_precision?
31 + t.datetime :created_at, precision: 6, null: false
32 + else
33 + t.datetime :created_at, null: false
34 + end
35 +
36 + t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37 + t.foreign_key :active_storage_blobs, column: :blob_id
38 + end
39 +
40 + create_table :active_storage_variant_records, id: primary_key_type do |t|
41 + t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42 + t.string :variation_digest, null: false
43 +
44 + t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45 + t.foreign_key :active_storage_blobs, column: :blob_id
46 + end
47 + end
48 +
49 + private
50 + def primary_and_foreign_key_types
51 + config = Rails.configuration.generators
52 + setting = config.options[config.orm][:primary_key_type]
53 + primary_key_type = setting || :primary_key
54 + foreign_key_type = setting || :bigint
55 + [primary_key_type, foreign_key_type]
56 + end
57 + end
@@ -0,0 +1,6
1 + class AddDescriptionToProblems < ActiveRecord::Migration[7.0]
2 + def change
3 + add_column :problems, :description, :text
4 + add_column :problems, :markdown, :boolean
5 + end
6 + end
@@ -0,0 +1,21
1 + Problem.all.each do |p|
2 + next unless p.description_filename
3 + basename, ext = p.description_filename.split('.')
4 + filename = "#{Problem.download_file_basedir}/#{p.id}/#{basename}.#{ext}"
5 +
6 + if File.exists? filename
7 + p.statement.attach io: File.open(filename), filename: "#{basename}.#{ext}"
8 + puts "#{p.id}: OK"
9 + else
10 + puts "#{p.id}: #{p.name} #{filename} ERROR"
11 + end
12 +
13 + d = Description.where(id: p.description_id).first
14 + if d
15 + p.description = d.body
16 + p.markdown = d.markdowned
17 + end
18 + p.save
19 +
20 +
21 + end
@@ -26,12 +26,13
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/*
@@ -68,25 +68,26
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 -----------------------
@@ -1,12 +1,21
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)
@@ -238,27 +247,24
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)
@@ -306,25 +312,25
306 312 listen (>= 3.0.5, < 3.2)
307 313 mail
308 314 material_icons
309 315 minitest-reporters
310 316 momentjs-rails
311 317 mysql2
312 318 puma
313 319 rails (~> 7.0)
314 320 rdiscount
315 321 rouge
316 322 sassc-rails
317 323 selenium-webdriver
318 - simple_form
324 + simple_form!
319 325 spring
320 326 spring-watcher-listen (~> 2.0.0)
321 327 sprockets-rails
322 328 sqlite3
323 329 web-console (>= 3.3.0)
324 330 webdrivers
325 331
326 332 RUBY VERSION
327 333 ruby 3.1.2p20
328 334
329 335 BUNDLED WITH
330 336 2.3.22
@@ -50,28 +50,24
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 }
@@ -1,43 +1,50
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)
@@ -1,167 +1,143
1 1 class ProblemsController < ApplicationController
2 2
3 + include ActiveStorage::SetCurrent
4 +
3 5 before_action :admin_authorization, except: [:stat]
6 + before_action :set_problem, only: [:show, :edit, :update, :destroy, :get_statement, :toggle, :toggle_test, :toggle_view_testcase, :stat]
4 7 before_action only: [:stat] do
5 8 authorization_by_roles(['admin','ta'])
6 9 end
7 10
11 +
8 12 def index
9 13 @problems = Problem.order(date_added: :desc)
10 14 end
11 15
12 16
13 17 def show
14 - @problem = Problem.find(params[:id])
18 + end
19 +
20 + #get statement download link
21 + def get_statement
22 + unless @current_user.can_view_problem? @problem
23 + redirect_to list_main_path, error: 'You are not authorized to access this file'
24 + return
25 + end
26 +
27 + if params[:ext]=='pdf'
28 + content_type = 'application/pdf'
29 + else
30 + content_type = 'application/octet-stream'
31 + end
32 +
33 + filename = @problem.statement.filename.to_s
34 + data =@problem.statement.download
35 +
36 + send_data data, stream: false, disposition: 'inline', filename: filename, type: content_type
15 37 end
16 38
17 39 def new
18 40 @problem = Problem.new
19 - @description = nil
20 41 end
21 42
22 43 def create
23 44 @problem = Problem.new(problem_params)
24 - @description = Description.new(description_params)
25 - if @description.body!=''
26 - if !@description.save
27 - render :action => new and return
28 - end
29 - else
30 - @description = nil
31 - end
32 - @problem.description = @description
33 45 if @problem.save
34 - flash[:notice] = 'Problem was successfully created.'
35 - redirect_to action: :index
46 + redirect_to action: :index, notice: 'Problem was successfully created.'
36 47 else
37 48 render :action => 'new'
38 49 end
39 50 end
40 51
41 52 def quick_create
42 53 @problem = Problem.new(problem_params)
43 54 @problem.full_name = @problem.name if @problem.full_name == ''
44 55 @problem.full_score = 100
45 56 @problem.available = false
46 57 @problem.test_allowed = true
47 58 @problem.output_only = false
48 59 @problem.date_added = Time.new
49 60 if @problem.save
50 61 flash[:notice] = 'Problem was successfully created.'
51 62 redirect_to action: :index
52 63 else
53 64 flash[:notice] = 'Error saving problem'
54 65 redirect_to action: :index
55 66 end
56 67 end
57 68
58 69 def edit
59 - @problem = Problem.find(params[:id])
60 70 @description = @problem.description
61 71 end
62 72
63 73 def update
64 - @problem = Problem.find(params[:id])
65 - @description = @problem.description
66 - if @description.nil? and params[:description][:body]!=''
67 - @description = Description.new(description_params)
68 - if !@description.save
69 - flash[:notice] = 'Error saving description'
70 - render :action => 'edit' and return
71 - end
72 - @problem.description = @description
73 - elsif @description
74 - if !@description.update(description_params)
75 - flash[:notice] = 'Error saving description'
76 - render :action => 'edit' and return
77 - end
78 - end
79 - if params[:file] and params[:file].content_type != 'application/pdf'
80 - flash[:notice] = 'Error: Uploaded file is not PDF'
81 - render :action => 'edit' and return
74 + if problem_params[:statement] && problem_params[:statement].content_type != 'application/pdf'
75 + flash[:error] = 'Error: Uploaded file is not PDF'
76 + render :action => 'edit'
77 + return
82 78 end
83 79 if @problem.update(problem_params)
84 80 flash[:notice] = 'Problem was successfully updated.'
85 - unless params[:file] == nil or params[:file] == ''
86 - flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
87 - out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
88 - if not FileTest.exists? out_dirname
89 - Dir.mkdir out_dirname
90 - end
91 -
92 - out_filename = "#{out_dirname}/#{@problem.name}.pdf"
93 - if FileTest.exists? out_filename
94 - File.delete out_filename
95 - end
96 -
97 - File.open(out_filename,"wb") do |file|
98 - file.write(params[:file].read)
99 - end
100 - @problem.description_filename = "#{@problem.name}.pdf"
81 + flash[:notice] += 'A new statement PDF is uploaded' if problem_params[:statement]
101 82 @problem.save
102 - end
103 - redirect_to :action => 'show', :id => @problem
83 + redirect_to edit_problem_path(@problem)
104 84 else
105 85 render :action => 'edit'
106 86 end
107 87 end
108 88
109 89 def destroy
110 - p = Problem.find(params[:id]).destroy
90 + @problem.destroy
111 91 redirect_to action: :index
112 92 end
113 93
114 94 def toggle
115 - @problem = Problem.find(params[:id])
116 95 @problem.update(available: !(@problem.available) )
117 96 respond_to do |format|
118 97 format.js { }
119 98 end
120 99 end
121 100
122 101 def toggle_test
123 - @problem = Problem.find(params[:id])
124 102 @problem.update(test_allowed: !(@problem.test_allowed?) )
125 103 respond_to do |format|
126 104 format.js { }
127 105 end
128 106 end
129 107
130 108 def toggle_view_testcase
131 - @problem = Problem.find(params[:id])
132 109 @problem.update(view_testcase: !(@problem.view_testcase?) )
133 110 respond_to do |format|
134 111 format.js { }
135 112 end
136 113 end
137 114
138 115 def turn_all_off
139 116 Problem.available.all.each do |problem|
140 117 problem.available = false
141 118 problem.save
142 119 end
143 120 redirect_to action: :index
144 121 end
145 122
146 123 def turn_all_on
147 124 Problem.where.not(available: true).each do |problem|
148 125 problem.available = true
149 126 problem.save
150 127 end
151 128 redirect_to action: :index
152 129 end
153 130
154 131 def stat
155 - @problem = Problem.find(params[:id])
156 132 unless @problem.available or session[:admin]
157 133 redirect_to :controller => 'main', :action => 'list'
158 134 return
159 135 end
160 136 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
161 137
162 138 #stat summary
163 139 range =65
164 140 @histogram = { data: Array.new(range,0), summary: {} }
165 141 user = Hash.new(0)
166 142 @submissions.find_each do |sub|
167 143 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
@@ -286,21 +262,26
286 262 name, id, order = k.split('-')
287 263 problems << Problem.find(id)
288 264 end
289 265 end
290 266 problems
291 267 end
292 268
293 269 def get_problems_stat
294 270 end
295 271
296 272 private
297 273
274 + def set_problem
275 + @problem = Problem.find(params[:id])
276 + end
277 +
298 278 def problem_params
299 - params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
279 + params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available,
280 + :test_allowed, :output_only, :url, :description, :statement, :description, tag_ids:[])
300 281 end
301 282
302 283 def description_params
303 284 params.require(:description).permit(:body, :markdowned)
304 285 end
305 286
306 287 end
@@ -200,13 +200,24
200 200
201 201 BOOTSTRAP_FLASH_MSG = {
202 202 success: 'alert-success',
203 203 error: 'alert-danger',
204 204 alert: 'alert-danger',
205 205 notice: 'alert-info'
206 206 }
207 207
208 208 def bootstrap_class_for(flash_type)
209 209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
210 210 end
211 211
212 + def active_class_when(options = {},cname = @active_controller, aname = @active_action)
213 + class_name = ' active '
214 + ok = true
215 + options.each do |k,v|
216 + ok = false if k == :controller && v.to_s != cname
217 + ok = false if k == :action && v.to_s != aname
212 218 end
219 + return class_name if ok && options.size > 0
220 + return ''
221 + end
222 +
223 + end
@@ -1,38 +1,42
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
@@ -2,86 +2,92
2 2 %nav.navbar.fixed-top.navbar-dark.bg-primary.navbar-expand-lg
3 3 .container-fluid
4 4 %a.navbar-brand{href: list_main_path}
5 5 %span.mi.mi-bs home
6 6 MAIN
7 7 %button.navbar-toggler.collapsed{ type: :button, 'data-bs': {toggle: 'collapse', target: '#navbar-collapse'} }
8 8 %span.navbar-toggler-icon
9 9 .collapse.navbar-collapse#navbar-collapse
10 10 %ul.navbar-nav.me-auto.mb-2.mb-lg-0
11 11 / submission
12 12 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
13 13 %li.nav-item.dropdown.mx-2
14 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {expanded:"false"}, role: "button"}
14 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {expanded:"false"}, role: "button"}
15 15 = "#{I18n.t 'menu.submissions'}"
16 16 %ul.dropdown-menu
17 - %li= link_to 'View', submissions_path, class:'dropdown-item'
17 + %li= link_to 'View', submissions_path, class: 'dropdown-item '+active_class_when(controller: :submissions)
18 18 %li= link_to 'Self Test', test_index_path, class:'dropdown-item'
19 19 / hall of fame
20 20 - if GraderConfiguration['right.user_hall_of_fame']
21 - %li= link_to "#{I18n.t 'menu.hall_of_fame'}", problem_hof_report_path, class: 'nav-link mx-2'
21 + %li= link_to "#{I18n.t 'menu.hall_of_fame'}", problem_hof_report_path, class: 'nav-link mx-2'+active_class_when(controller: :report, action: :problem_hof)
22 22 / display MODE button (with countdown in contest mode)
23 23 - if GraderConfiguration.analysis_mode?
24 24 %div.btn.btn-success#countdown= "ANALYSIS MODE"
25 25 - elsif GraderConfiguration.time_limit_mode?
26 26 - if @current_user.contest_finished?
27 27 %div.btn.btn-danger#countdown= "Contest is over"
28 28 - elsif !@current_user.contest_started?
29 29 %div.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
30 30 - else
31 31 %div.btn.btn-primary#countdown asdf
32 32 :javascript
33 33 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
34 34 / admin section
35 35 - if (@current_user!=nil) and (session[:admin])
36 36 / management
37 37 %li.nav-item.dropdown.mx-2
38 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
38 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
39 39 Manage
40 40 %ul.dropdown-menu
41 - %li= link_to 'Announcements', announcements_path, class: 'dropdown-item'
42 - %li= link_to 'Problems', problems_path, class: 'dropdown-item'
43 - %li= link_to 'Tags', tags_path, class: 'dropdown-item'
44 - %li= link_to 'Users', user_admin_index_path, class: 'dropdown-item'
45 - %li= link_to 'User Groups', groups_path, class: 'dropdown-item'
46 - %li= link_to 'Graders', graders_list_path, class: 'dropdown-item'
47 - %li= link_to 'Message ', console_messages_path, class: 'dropdown-item'
41 + %li= link_to 'Announcements', announcements_path, class: 'dropdown-item'+active_class_when(controller: :announcements)
42 + %li= link_to 'Problems', problems_path, class: 'dropdown-item'+active_class_when(controller: :problems)
43 + %li= link_to 'Tags', tags_path, class: 'dropdown-item'+active_class_when(controller: :tags)
44 + %li= link_to 'Users', user_admin_index_path, class: 'dropdown-item'+active_class_when(controller: :user_admin)
45 + %li= link_to 'User Groups', groups_path, class: 'dropdown-item'+active_class_when(controller: :groups)
46 + %li= link_to 'Graders', graders_list_path, class: 'dropdown-item'+active_class_when(controller: :graders)
47 + %li= link_to 'Message ', console_messages_path, class: 'dropdown-item'+active_class_when(controller: :messages)
48 48 %li
49 49 %hr.dropdown-divider
50 - %li= link_to 'System config', grader_configuration_index_path, class: 'dropdown-item'
50 + %li= link_to 'System config', grader_configuration_index_path, class: 'dropdown-item'+active_class_when(controller: :grader_configuration)
51 51 %li
52 52 %hr.dropdown-divider
53 - %li= link_to 'Sites', sites_path, class: 'dropdown-item'
54 - %li= link_to 'Contests', contest_management_index_path, class: 'dropdown-item'
53 + %li= link_to 'Sites', sites_path, class: 'dropdown-item'+active_class_when(controller: :sites)
54 + %li= link_to 'Contests', contest_management_index_path, class: 'dropdown-item'+active_class_when(controller: :contest_management)
55 55 -#
56 56 / report
57 57 %li.nav-item.dropdown.mx-2
58 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
58 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
59 59 Report
60 60 %ul.dropdown-menu
61 - %li= link_to 'Current Score', current_score_report_path, class: 'dropdown-item'
62 - %li= link_to 'Score Report', max_score_report_path, class: 'dropdown-item'
63 - %li= link_to 'Submission Report', submission_report_path, class: 'dropdown-item'
64 - %li= link_to 'Login Report', login_report_path, class: 'dropdown-item'
61 + %li= link_to 'Current Score', current_score_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :current_score)
62 + %li= link_to 'Score Report', max_score_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :max_score)
63 + %li= link_to 'Submission Report', submission_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :submission)
64 + %li= link_to 'Login Report', login_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :login)
65 65 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
66 66 =link_to "#{ungraded} backlogs!",
67 67 graders_list_path,
68 68 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
69 69 / announcement
70 70 - @nav_announcement.each do |ann|
71 71 %p.navbar-text
72 72 = ann.body.html_safe
73 73 %ul.navbar-nav
74 74 %li.nav-item
75 75 %a.nav-link{href: help_main_path}
76 76 %span.mi.mi-bs.md-18 help
77 77 %li.nav-item
78 78 %a.nav-link{href: messages_path}
79 79 %span.mi.mi-bs.md-18 chat
80 80 - if GraderConfiguration['system.user_setting_enabled']
81 81 %li.nav-item
82 82 %a.nav-link{href: profile_users_path}
83 83 %span.mi.mi-bs.md-18 settings
84 84 %li.nav-item
85 85 %a.nav-link{href: login_main_path}
86 86 %span.mi.mi-bs.md-18 exit_to_app
87 87 = @current_user.full_name
88 + :javascript
89 + $('.active-with-children').each( (index,obj) => {
90 + if ($(obj).siblings('.dropdown-menu').has('.active').length > 0) {
91 + $(obj).addClass('active')
92 + }
93 + } )
@@ -1,13 +1,36
1 + = simple_form_for problem do |form|
2 + .row
3 + .col-md-6
4 + = form.input :name
5 + = form.input :full_name
6 + = form.input :full_score
7 + = form.input :tag_ids, collection: Tag.all, class: 'select2'
8 + = form.input :date_added
9 + = form.input :available
10 + = form.input :test_allowed
11 + = form.input :output_only
12 + = form.input :description, as: :text
13 + = form.input :markdown
14 + = form.input :url
15 + = form.input :statement
16 + %p
17 + - if @problem.statement.attached?
18 + %a{href: get_statement_problem_path(@problem)} [Download current Statement]
19 + - else
20 + no statement attached to this problem
21 + = form.submit :submit, class: 'btn btn-primary'
22 + -#
1 23 = error_messages_for 'problem'
24 +
2 25 / [form:problem]
3 26 .form-group
4 27 %label{:for => "problem_name"} Name
5 28 = text_field 'problem', 'name', class: 'form-control'
6 29 %small
7 30 Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead.
8 31 .form-group
9 32 %label{:for => "problem_full_name"} Full name
10 33 = text_field 'problem', 'full_name', class: 'form-control'
11 34 .form-group
12 35 %label{:for => "problem_full_score"} Full score
13 36 = text_field 'problem', 'full_score', class: 'form-control'
@@ -1,14 +1,6
1 - .container-fluid
2 - = form_for @problem,url:{action: 'update'},html: {multipart: true} do
3 - .row
4 - .col-md-6
5 - %h1 Editing problem
6 - = render :partial => 'form'
7 - .row
1 + %h1 Editing Problem
2 +
3 + = render 'form', problem: @problem
4 + .row.my-3
8 5 .col-md-4
9 - = submit_tag 'Edit', class: 'btn btn-primary btn-block'
10 - .col-md-4
11 - = link_to 'Show', {:action => 'show', :id => @problem}, class: 'btn btn-default btn-block'
12 - .col-md-4
13 - = link_to 'Back', problems_path, class: 'btn btn-default btn-block'
14 - .div{style: 'height: 5em'}
6 + = link_to 'Back', problems_path, class: 'btn btn-secondary'
@@ -28,38 +28,40
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'
@@ -103,25 +103,25
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.
@@ -1,19 +1,17
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
@@ -34,407 +32,341
34 32 config.include_default_input_wrapper_class = false
35 33
36 34 # CSS class to add for error notification helper.
37 35 config.error_notification_class = 'alert alert-danger'
38 36
39 37 # Method used to tidy up errors. Specify any Rails Array method.
40 38 # :first lists the first message for each field.
41 39 # :to_sentence to list all errors for each field.
42 40 config.error_method = :to_sentence
43 41
44 42 # add validation classes to `input_field`
45 43 config.input_field_error_class = 'is-invalid'
46 - config.input_field_valid_class = 'is-valid'
44 + config.input_field_valid_class = 'is-valid-xxx'
47 45
48 46
49 47 # vertical forms
50 48 #
51 49 # vertical default_wrapper
52 - config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
50 + config.wrappers :vertical_form, class: 'mb-3' do |b|
53 51 b.use :html5
54 52 b.use :placeholder
55 53 b.optional :maxlength
56 54 b.optional :minlength
57 55 b.optional :pattern
58 56 b.optional :min_max
59 57 b.optional :readonly
60 - b.use :label
61 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
62 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
63 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
58 + b.use :label, class: 'form-label'
59 + b.use :input, class: 'form-control', error_class: 'is-invalid'
60 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
61 + b.use :hint, wrap_with: { class: 'form-text' }
64 62 end
65 63
66 64 # vertical input for boolean
67 - config.wrappers :vertical_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
65 + config.wrappers :vertical_boolean, tag: 'fieldset', class: 'mb-3' do |b|
68 66 b.use :html5
69 67 b.optional :readonly
70 - b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
71 - bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
68 + b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
69 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
72 70 bb.use :label, class: 'form-check-label'
73 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
74 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
71 + bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
72 + bb.use :hint, wrap_with: { class: 'form-text' }
75 73 end
76 74 end
77 75
78 76 # vertical input for radio buttons and check boxes
79 - config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
77 + config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
80 78 b.use :html5
81 79 b.optional :readonly
82 80 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
83 81 ba.use :label_text
84 82 end
85 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
86 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
87 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
83 + b.use :input, class: 'form-check-input', error_class: 'is-invalid'
84 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
85 + b.use :hint, wrap_with: { class: 'form-text' }
88 86 end
89 87
90 88 # vertical input for inline radio buttons and check boxes
91 - config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
89 + config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
92 90 b.use :html5
93 91 b.optional :readonly
94 92 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
95 93 ba.use :label_text
96 94 end
97 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
98 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
99 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
95 + b.use :input, class: 'form-check-input', error_class: 'is-invalid'
96 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
97 + b.use :hint, wrap_with: { class: 'form-text' }
100 98 end
101 99
102 100 # vertical file input
103 - config.wrappers :vertical_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
101 + config.wrappers :vertical_file, class: 'mb-3' do |b|
104 102 b.use :html5
105 103 b.use :placeholder
106 104 b.optional :maxlength
107 105 b.optional :minlength
108 106 b.optional :readonly
109 - b.use :label
110 - b.use :input, class: 'form-control-file', error_class: 'is-invalid', valid_class: 'is-valid'
111 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
112 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
107 + b.use :label, class: 'form-label'
108 + b.use :input, class: 'form-control', error_class: 'is-invalid'
109 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
110 + b.use :hint, wrap_with: { class: 'form-text' }
111 + end
112 +
113 + # vertical select input
114 + config.wrappers :vertical_select, class: 'mb-3' do |b|
115 + b.use :html5
116 + b.optional :readonly
117 + b.use :label, class: 'form-label'
118 + b.use :input, class: 'form-select', error_class: 'is-invalid'
119 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
120 + b.use :hint, wrap_with: { class: 'form-text' }
113 121 end
114 122
115 123 # vertical multi select
116 - config.wrappers :vertical_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
124 + config.wrappers :vertical_multi_select, class: 'mb-3' do |b|
117 125 b.use :html5
118 126 b.optional :readonly
119 - b.use :label
120 - b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
121 - ba.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
127 + b.use :label, class: 'form-label'
128 + b.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
129 + ba.use :input, class: 'form-select mx-1', error_class: 'is-invalid'
122 130 end
123 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
124 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
131 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
132 + b.use :hint, wrap_with: { class: 'form-text' }
125 133 end
126 134
127 135 # vertical range input
128 - config.wrappers :vertical_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
136 + config.wrappers :vertical_range, class: 'mb-3' do |b|
129 137 b.use :html5
130 138 b.use :placeholder
131 139 b.optional :readonly
132 140 b.optional :step
133 - b.use :label
134 - b.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
135 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
136 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
141 + b.use :label, class: 'form-label'
142 + b.use :input, class: 'form-range', error_class: 'is-invalid'
143 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
144 + b.use :hint, wrap_with: { class: 'form-text' }
137 145 end
138 146
139 147
140 148 # horizontal forms
141 149 #
142 150 # horizontal default_wrapper
143 - config.wrappers :horizontal_form, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
151 + config.wrappers :horizontal_form, class: 'row mb-3' do |b|
144 152 b.use :html5
145 153 b.use :placeholder
146 154 b.optional :maxlength
147 155 b.optional :minlength
148 156 b.optional :pattern
149 157 b.optional :min_max
150 158 b.optional :readonly
151 159 b.use :label, class: 'col-sm-3 col-form-label'
152 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
153 - ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
154 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
155 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
160 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
161 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
162 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
163 + ba.use :hint, wrap_with: { class: 'form-text' }
156 164 end
157 165 end
158 166
159 167 # horizontal input for boolean
160 - config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
168 + config.wrappers :horizontal_boolean, class: 'row mb-3' do |b|
161 169 b.use :html5
162 170 b.optional :readonly
163 - b.wrapper tag: 'label', class: 'col-sm-3' do |ba|
164 - ba.use :label_text
165 - end
166 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |wr|
167 - wr.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
168 - bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
171 + b.wrapper :grid_wrapper, class: 'col-sm-9 offset-sm-3' do |wr|
172 + wr.wrapper :form_check_wrapper, class: 'form-check' do |bb|
173 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
169 174 bb.use :label, class: 'form-check-label'
170 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
171 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
175 + bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
176 + bb.use :hint, wrap_with: { class: 'form-text' }
172 177 end
173 178 end
174 179 end
175 180
176 181 # horizontal input for radio buttons and check boxes
177 - config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
182 + config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
178 183 b.use :html5
179 184 b.optional :readonly
180 185 b.use :label, class: 'col-sm-3 col-form-label pt-0'
181 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
182 - ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
183 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
184 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
186 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
187 + ba.use :input, class: 'form-check-input', error_class: 'is-invalid'
188 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
189 + ba.use :hint, wrap_with: { class: 'form-text' }
185 190 end
186 191 end
187 192
188 193 # horizontal input for inline radio buttons and check boxes
189 - config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
194 + config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
190 195 b.use :html5
191 196 b.optional :readonly
192 197 b.use :label, class: 'col-sm-3 col-form-label pt-0'
193 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
194 - ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
195 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
196 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
198 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
199 + ba.use :input, class: 'form-check-input', error_class: 'is-invalid'
200 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
201 + ba.use :hint, wrap_with: { class: 'form-text' }
197 202 end
198 203 end
199 204
200 205 # horizontal file input
201 - config.wrappers :horizontal_file, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
206 + config.wrappers :horizontal_file, class: 'row mb-3' do |b|
202 207 b.use :html5
203 208 b.use :placeholder
204 209 b.optional :maxlength
205 210 b.optional :minlength
206 211 b.optional :readonly
207 212 b.use :label, class: 'col-sm-3 col-form-label'
208 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
209 - ba.use :input, error_class: 'is-invalid', valid_class: 'is-valid'
210 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
211 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
213 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
214 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
215 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
216 + ba.use :hint, wrap_with: { class: 'form-text' }
217 + end
218 + end
219 +
220 + # horizontal select input
221 + config.wrappers :horizontal_select, class: 'row mb-3' do |b|
222 + b.use :html5
223 + b.optional :readonly
224 + b.use :label, class: 'col-sm-3 col-form-label'
225 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
226 + ba.use :input, class: 'form-select', error_class: 'is-invalid'
227 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
228 + ba.use :hint, wrap_with: { class: 'form-text' }
212 229 end
213 230 end
214 231
215 232 # horizontal multi select
216 - config.wrappers :horizontal_multi_select, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
233 + config.wrappers :horizontal_multi_select, class: 'row mb-3' do |b|
217 234 b.use :html5
218 235 b.optional :readonly
219 236 b.use :label, class: 'col-sm-3 col-form-label'
220 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
221 - ba.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
222 - bb.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
237 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
238 + ba.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
239 + bb.use :input, class: 'form-select mx-1', error_class: 'is-invalid'
223 240 end
224 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
225 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
241 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
242 + ba.use :hint, wrap_with: { class: 'form-text' }
226 243 end
227 244 end
228 245
229 246 # horizontal range input
230 - config.wrappers :horizontal_range, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
247 + config.wrappers :horizontal_range, class: 'row mb-3' do |b|
231 248 b.use :html5
232 249 b.use :placeholder
233 250 b.optional :readonly
234 251 b.optional :step
235 - b.use :label, class: 'col-sm-3 col-form-label'
236 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
237 - ba.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
238 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
239 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
252 + b.use :label, class: 'col-sm-3 col-form-label pt-0'
253 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
254 + ba.use :input, class: 'form-range', error_class: 'is-invalid'
255 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
256 + ba.use :hint, wrap_with: { class: 'form-text' }
240 257 end
241 258 end
242 259
243 260
244 261 # inline forms
245 262 #
246 263 # inline default_wrapper
247 - config.wrappers :inline_form, tag: 'span', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
264 + config.wrappers :inline_form, class: 'col-12' do |b|
248 265 b.use :html5
249 266 b.use :placeholder
250 267 b.optional :maxlength
251 268 b.optional :minlength
252 269 b.optional :pattern
253 270 b.optional :min_max
254 271 b.optional :readonly
255 - b.use :label, class: 'sr-only'
272 + b.use :label, class: 'visually-hidden'
256 273
257 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
258 - b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
259 - b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
274 + b.use :input, class: 'form-control', error_class: 'is-invalid'
275 + b.use :error, wrap_with: { class: 'invalid-feedback' }
276 + b.optional :hint, wrap_with: { class: 'form-text' }
260 277 end
261 278
262 279 # inline input for boolean
263 - config.wrappers :inline_boolean, tag: 'span', class: 'form-check mb-2 mr-sm-2', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
280 + config.wrappers :inline_boolean, class: 'col-12' do |b|
264 281 b.use :html5
265 282 b.optional :readonly
266 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
267 - b.use :label, class: 'form-check-label'
268 - b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
269 - b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
283 + b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
284 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
285 + bb.use :label, class: 'form-check-label'
286 + bb.use :error, wrap_with: { class: 'invalid-feedback' }
287 + bb.optional :hint, wrap_with: { class: 'form-text' }
288 + end
270 289 end
271 290
272 291
273 292 # bootstrap custom forms
274 293 #
275 - # custom input for boolean
276 - config.wrappers :custom_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
277 - b.use :html5
278 - b.optional :readonly
279 - b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-checkbox' do |bb|
280 - bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
281 - bb.use :label, class: 'custom-control-label'
282 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
283 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
284 - end
285 - end
286 -
287 294 # custom input switch for boolean
288 - config.wrappers :custom_boolean_switch, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
289 - b.use :html5
290 - b.optional :readonly
291 - b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-switch' do |bb|
292 - bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
293 - bb.use :label, class: 'custom-control-label'
294 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
295 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
296 - end
297 - end
298 -
299 - # custom input for radio buttons and check boxes
300 - config.wrappers :custom_collection, item_wrapper_class: 'custom-control', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
301 - b.use :html5
302 - b.optional :readonly
303 - b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
304 - ba.use :label_text
305 - end
306 - b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
307 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
308 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
309 - end
310 -
311 - # custom input for inline radio buttons and check boxes
312 - config.wrappers :custom_collection_inline, item_wrapper_class: 'custom-control custom-control-inline', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
295 + config.wrappers :custom_boolean_switch, class: 'mb-3' do |b|
313 296 b.use :html5
314 297 b.optional :readonly
315 - b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
316 - ba.use :label_text
317 - end
318 - b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
319 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
320 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
321 - end
322 -
323 - # custom file input
324 - config.wrappers :custom_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
325 - b.use :html5
326 - b.use :placeholder
327 - b.optional :maxlength
328 - b.optional :minlength
329 - b.optional :readonly
330 - b.use :label
331 - b.wrapper :custom_file_wrapper, tag: 'div', class: 'custom-file' do |ba|
332 - ba.use :input, class: 'custom-file-input', error_class: 'is-invalid', valid_class: 'is-valid'
333 - ba.use :label, class: 'custom-file-label'
334 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
335 - end
336 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
298 + b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check form-switch' do |bb|
299 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
300 + bb.use :label, class: 'form-check-label'
301 + bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
302 + bb.use :hint, wrap_with: { class: 'form-text' }
337 303 end
338 -
339 - # custom multi select
340 - config.wrappers :custom_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
341 - b.use :html5
342 - b.optional :readonly
343 - b.use :label
344 - b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
345 - ba.use :input, class: 'custom-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
346 - end
347 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
348 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
349 - end
350 -
351 - # custom range input
352 - config.wrappers :custom_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
353 - b.use :html5
354 - b.use :placeholder
355 - b.optional :readonly
356 - b.optional :step
357 - b.use :label
358 - b.use :input, class: 'custom-range', error_class: 'is-invalid', valid_class: 'is-valid'
359 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
360 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
361 304 end
362 305
363 306
364 307 # Input Group - custom component
365 - # see example app and config at https://github.com/rafaelfranca/simple_form-bootstrap
366 - # config.wrappers :input_group, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
367 - # b.use :html5
368 - # b.use :placeholder
369 - # b.optional :maxlength
370 - # b.optional :minlength
371 - # b.optional :pattern
372 - # b.optional :min_max
373 - # b.optional :readonly
374 - # b.use :label
375 - # b.wrapper :input_group_tag, tag: 'div', class: 'input-group' do |ba|
376 - # ba.optional :prepend
377 - # ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
378 - # ba.optional :append
379 - # end
380 - # b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
381 - # b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
382 - # end
383 -
384 -
385 - # Floating Labels form
386 - #
387 - # floating labels default_wrapper
388 - config.wrappers :floating_labels_form, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
308 + # see example app and config at https://github.com/heartcombo/simple_form-bootstrap
309 + config.wrappers :input_group, class: 'mb-3' do |b|
389 310 b.use :html5
390 311 b.use :placeholder
391 312 b.optional :maxlength
392 313 b.optional :minlength
393 314 b.optional :pattern
394 315 b.optional :min_max
395 316 b.optional :readonly
396 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
317 + b.use :label, class: 'form-label'
318 + b.wrapper :input_group_tag, class: 'input-group' do |ba|
319 + ba.optional :prepend
320 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
321 + ba.optional :append
322 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
323 + end
324 + b.use :hint, wrap_with: { class: 'form-text' }
325 + end
326 +
327 +
328 + # Floating Labels form
329 + #
330 + # floating labels default_wrapper
331 + config.wrappers :floating_labels_form, class: 'form-floating mb-3' do |b|
332 + b.use :html5
333 + b.use :placeholder
334 + b.optional :maxlength
335 + b.optional :minlength
336 + b.optional :pattern
337 + b.optional :min_max
338 + b.optional :readonly
339 + b.use :input, class: 'form-control', error_class: 'is-invalid'
397 340 b.use :label
398 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
399 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
341 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
342 + b.use :hint, wrap_with: { class: 'form-text' }
400 343 end
401 344
402 345 # custom multi select
403 - config.wrappers :floating_labels_select, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
346 + config.wrappers :floating_labels_select, class: 'form-floating mb-3' do |b|
404 347 b.use :html5
405 348 b.optional :readonly
406 - b.use :input, class: 'custom-select', error_class: 'is-invalid', valid_class: 'is-valid'
349 + b.use :input, class: 'form-select', error_class: 'is-invalid'
407 350 b.use :label
408 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
409 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
351 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
352 + b.use :hint, wrap_with: { class: 'form-text' }
410 353 end
411 354
412 355
413 356 # The default wrapper to be used by the FormBuilder.
414 357 config.default_wrapper = :vertical_form
415 358
416 359 # Custom wrappers for input types. This should be a hash containing an input
417 360 # type as key and the wrapper that will be used for all inputs with specified type.
418 361 config.wrapper_mappings = {
419 362 boolean: :vertical_boolean,
420 363 check_boxes: :vertical_collection,
421 364 date: :vertical_multi_select,
422 365 datetime: :vertical_multi_select,
423 366 file: :vertical_file,
424 367 radio_buttons: :vertical_collection,
425 368 range: :vertical_range,
426 - time: :vertical_multi_select
369 + time: :vertical_multi_select,
370 + select: :vertical_select
427 371 }
428 -
429 - # enable custom form wrappers
430 - # config.wrapper_mappings = {
431 - # boolean: :custom_boolean,
432 - # check_boxes: :custom_collection,
433 - # date: :custom_multi_select,
434 - # datetime: :custom_multi_select,
435 - # file: :custom_file,
436 - # radio_buttons: :custom_collection,
437 - # range: :custom_range,
438 - # time: :custom_multi_select
439 - # }
440 372 end
@@ -25,24 +25,25
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
@@ -1,79 +1,107
1 1 # This file is auto-generated from the current state of the database. Instead
2 2 # of editing this file, please use the migrations feature of Active Record to
3 3 # incrementally modify your database, and then regenerate this schema definition.
4 4 #
5 5 # This file is the source Rails uses to define your schema when running `bin/rails
6 6 # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 7 # be faster and is potentially less error prone than running all of your
8 8 # migrations from scratch. Old migrations may fail to apply correctly if those
9 9 # migrations use external dependencies or application code.
10 10 #
11 11 # It's strongly recommended that you check this file into your version control system.
12 12
13 - ActiveRecord::Schema[7.0].define(version: 2022_02_04_080936) do
14 - create_table "announcements", id: :integer, charset: "utf8", force: :cascade do |t|
13 + ActiveRecord::Schema[7.0].define(version: 2022_09_27_074644) do
14 + create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
15 + t.string "name", null: false
16 + t.string "record_type", null: false
17 + t.bigint "record_id", null: false
18 + t.bigint "blob_id", null: false
19 + t.datetime "created_at", null: false
20 + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
21 + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
22 + end
23 +
24 + create_table "active_storage_blobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
25 + t.string "key", null: false
26 + t.string "filename", null: false
27 + t.string "content_type"
28 + t.text "metadata"
29 + t.string "service_name", null: false
30 + t.bigint "byte_size", null: false
31 + t.string "checksum"
32 + t.datetime "created_at", null: false
33 + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
34 + end
35 +
36 + create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
37 + t.bigint "blob_id", null: false
38 + t.string "variation_digest", null: false
39 + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
40 + end
41 +
42 + create_table "announcements", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
15 43 t.string "author"
16 - t.text "body", size: :medium
44 + t.text "body"
17 45 t.boolean "published"
18 46 t.datetime "created_at", precision: nil, null: false
19 47 t.datetime "updated_at", precision: nil, null: false
20 48 t.boolean "frontpage", default: false
21 49 t.boolean "contest_only", default: false
22 50 t.string "title"
23 51 t.string "notes"
24 52 t.boolean "on_nav_bar", default: false
25 53 end
26 54
27 - create_table "contests", id: :integer, charset: "utf8", force: :cascade do |t|
55 + create_table "contests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
28 56 t.string "title"
29 57 t.boolean "enabled"
30 58 t.datetime "created_at", precision: nil, null: false
31 59 t.datetime "updated_at", precision: nil, null: false
32 60 t.string "name"
33 61 end
34 62
35 - create_table "contests_problems", id: false, charset: "utf8", force: :cascade do |t|
63 + create_table "contests_problems", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
36 64 t.integer "contest_id"
37 65 t.integer "problem_id"
38 66 end
39 67
40 - create_table "contests_users", id: false, charset: "utf8", force: :cascade do |t|
68 + create_table "contests_users", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
41 69 t.integer "contest_id"
42 70 t.integer "user_id"
43 71 end
44 72
45 - create_table "countries", id: :integer, charset: "utf8", force: :cascade do |t|
73 + create_table "countries", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
46 74 t.string "name"
47 75 t.datetime "created_at", precision: nil, null: false
48 76 t.datetime "updated_at", precision: nil, null: false
49 77 end
50 78
51 - create_table "descriptions", id: :integer, charset: "utf8", force: :cascade do |t|
52 - t.text "body", size: :medium
79 + create_table "descriptions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
80 + t.text "body"
53 81 t.boolean "markdowned"
54 82 t.datetime "created_at", precision: nil, null: false
55 83 t.datetime "updated_at", precision: nil, null: false
56 84 end
57 85
58 - create_table "grader_configurations", id: :integer, charset: "utf8", force: :cascade do |t|
86 + create_table "grader_configurations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
59 87 t.string "key"
60 88 t.string "value_type"
61 89 t.string "value"
62 90 t.datetime "created_at", precision: nil, null: false
63 91 t.datetime "updated_at", precision: nil, null: false
64 - t.text "description", size: :medium
92 + t.text "description"
65 93 end
66 94
67 - create_table "grader_processes", id: :integer, charset: "utf8", force: :cascade do |t|
95 + create_table "grader_processes", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
68 96 t.string "host"
69 97 t.integer "pid"
70 98 t.string "mode"
71 99 t.boolean "active"
72 100 t.datetime "created_at", precision: nil, null: false
73 101 t.datetime "updated_at", precision: nil, null: false
74 102 t.integer "task_id"
75 103 t.string "task_type"
76 104 t.boolean "terminated"
77 105 t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
78 106 end
79 107
@@ -86,240 +114,233
86 114 create_table "groups_problems", id: false, charset: "latin1", force: :cascade do |t|
87 115 t.integer "problem_id", null: false
88 116 t.integer "group_id", null: false
89 117 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
90 118 end
91 119
92 120 create_table "groups_users", charset: "latin1", force: :cascade do |t|
93 121 t.integer "group_id", null: false
94 122 t.integer "user_id", null: false
95 123 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
96 124 end
97 125
98 - create_table "heart_beats", id: :integer, charset: "latin1", force: :cascade do |t|
126 + create_table "heart_beats", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
99 127 t.integer "user_id"
100 128 t.string "ip_address"
101 129 t.datetime "created_at", precision: nil, null: false
102 130 t.datetime "updated_at", precision: nil, null: false
103 131 t.string "status"
104 132 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
105 133 end
106 134
107 - create_table "languages", id: :integer, charset: "utf8", force: :cascade do |t|
135 + create_table "languages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
108 136 t.string "name", limit: 10
109 137 t.string "pretty_name"
110 138 t.string "ext", limit: 10
111 139 t.string "common_ext"
112 140 end
113 141
114 - create_table "logins", id: :integer, charset: "latin1", force: :cascade do |t|
142 + create_table "logins", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
115 143 t.integer "user_id"
116 144 t.string "ip_address"
117 145 t.datetime "created_at", precision: nil, null: false
118 146 t.datetime "updated_at", precision: nil, null: false
119 147 t.index ["user_id"], name: "index_logins_on_user_id"
120 148 end
121 149
122 - create_table "messages", id: :integer, charset: "utf8", force: :cascade do |t|
150 + create_table "messages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
123 151 t.integer "sender_id"
124 152 t.integer "receiver_id"
125 153 t.integer "replying_message_id"
126 - t.text "body", size: :medium
154 + t.text "body"
127 155 t.boolean "replied"
128 156 t.datetime "created_at", precision: nil, null: false
129 157 t.datetime "updated_at", precision: nil, null: false
130 158 end
131 159
132 - create_table "problems", id: :bigint, default: nil, charset: "utf8", force: :cascade do |t|
133 - t.string "name", limit: 100
160 + create_table "problems", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
161 + t.string "name", limit: 30
134 162 t.string "full_name"
135 163 t.integer "full_score"
136 164 t.date "date_added"
137 165 t.boolean "available"
138 166 t.string "url"
139 167 t.integer "description_id"
140 168 t.boolean "test_allowed"
141 169 t.boolean "output_only"
142 170 t.string "description_filename"
143 171 t.boolean "view_testcase"
144 172 t.integer "difficulty"
173 + t.text "description"
174 + t.boolean "markdown"
145 175 end
146 176
147 - create_table "problems_tags", id: :bigint, default: nil, charset: "latin1", force: :cascade do |t|
148 - t.bigint "problem_id"
177 + create_table "problems_tags", id: :integer, charset: "latin1", force: :cascade do |t|
178 + t.integer "problem_id"
149 179 t.integer "tag_id"
150 180 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
151 181 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
152 182 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
153 183 end
154 184
155 - create_table "rights", id: :integer, charset: "utf8", force: :cascade do |t|
185 + create_table "rights", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
156 186 t.string "name"
157 187 t.string "controller"
158 188 t.string "action"
159 189 end
160 190
161 - create_table "rights_roles", id: false, charset: "utf8", force: :cascade do |t|
191 + create_table "rights_roles", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
162 192 t.integer "right_id"
163 193 t.integer "role_id"
164 194 t.index ["role_id"], name: "index_rights_roles_on_role_id"
165 195 end
166 196
167 - create_table "roles", id: :integer, charset: "utf8", force: :cascade do |t|
197 + create_table "roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
168 198 t.string "name"
169 199 end
170 200
171 - create_table "roles_users", id: false, charset: "utf8", force: :cascade do |t|
201 + create_table "roles_users", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
172 202 t.integer "role_id"
173 203 t.integer "user_id"
174 204 t.index ["user_id"], name: "index_roles_users_on_user_id"
175 205 end
176 206
177 - create_table "sessions", id: :integer, charset: "utf8", force: :cascade do |t|
207 + create_table "sessions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
178 208 t.string "session_id"
179 - t.text "data", size: :medium
209 + t.text "data"
180 210 t.datetime "updated_at", precision: nil
181 211 t.index ["session_id"], name: "index_sessions_on_session_id"
182 212 t.index ["updated_at"], name: "index_sessions_on_updated_at"
183 213 end
184 214
185 - create_table "sites", id: :integer, charset: "utf8", force: :cascade do |t|
215 + create_table "sites", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
186 216 t.string "name"
187 217 t.boolean "started"
188 218 t.datetime "start_time", precision: nil
189 219 t.datetime "created_at", precision: nil, null: false
190 220 t.datetime "updated_at", precision: nil, null: false
191 221 t.integer "country_id"
192 222 t.string "password"
193 223 end
194 224
195 - create_table "solutions", charset: "latin1", force: :cascade do |t|
196 - t.string "solution"
197 - t.bigint "problem_id"
198 - t.bigint "submission_id"
199 - t.integer "type"
200 - t.index ["problem_id"], name: "index_solutions_on_problem_id"
201 - t.index ["submission_id"], name: "index_solutions_on_submission_id"
202 - end
203 -
204 - create_table "submission_view_logs", id: :integer, charset: "latin1", force: :cascade do |t|
225 + create_table "submission_view_logs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
205 226 t.integer "user_id"
206 227 t.integer "submission_id"
207 228 t.datetime "created_at", precision: nil, null: false
208 229 t.datetime "updated_at", precision: nil, null: false
209 230 end
210 231
211 - create_table "submissions", id: :bigint, default: nil, charset: "utf8", force: :cascade do |t|
232 + create_table "submissions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
212 233 t.integer "user_id"
213 234 t.integer "problem_id"
214 235 t.integer "language_id"
215 236 t.text "source", size: :medium
216 237 t.binary "binary"
217 238 t.datetime "submitted_at", precision: nil
218 239 t.datetime "compiled_at", precision: nil
219 - t.text "compiler_message", size: :medium
240 + t.text "compiler_message"
220 241 t.datetime "graded_at", precision: nil
221 242 t.integer "points"
222 - t.text "grader_comment", size: :medium
243 + t.text "grader_comment"
223 244 t.integer "number"
224 245 t.string "source_filename"
225 246 t.float "max_runtime"
226 247 t.integer "peak_memory"
227 248 t.integer "effective_code_length"
228 249 t.string "ip_address"
229 250 t.integer "tag", default: 0
230 251 t.index ["submitted_at"], name: "index_submissions_on_submitted_at"
231 252 t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
232 253 t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
233 254 end
234 255
235 256 create_table "tags", id: :integer, charset: "latin1", force: :cascade do |t|
236 257 t.string "name", null: false
237 258 t.text "description"
238 259 t.boolean "public"
239 260 t.datetime "created_at", precision: nil, null: false
240 261 t.datetime "updated_at", precision: nil, null: false
241 262 end
242 263
243 - create_table "tasks", id: :integer, charset: "utf8", force: :cascade do |t|
264 + create_table "tasks", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
244 265 t.integer "submission_id"
245 266 t.datetime "created_at", precision: nil
246 267 t.integer "status"
247 268 t.datetime "updated_at", precision: nil
248 269 t.index ["status"], name: "index_tasks_on_status"
249 270 t.index ["submission_id"], name: "index_tasks_on_submission_id"
250 271 end
251 272
252 - create_table "test_pairs", id: :integer, charset: "utf8", force: :cascade do |t|
273 + create_table "test_pairs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
253 274 t.integer "problem_id"
254 - t.text "input", size: :long
255 - t.text "solution", size: :long
275 + t.text "input", size: :medium
276 + t.text "solution", size: :medium
256 277 t.datetime "created_at", precision: nil, null: false
257 278 t.datetime "updated_at", precision: nil, null: false
258 279 end
259 280
260 - create_table "test_requests", id: :integer, charset: "utf8", force: :cascade do |t|
281 + create_table "test_requests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
261 282 t.integer "user_id"
262 283 t.integer "problem_id"
263 284 t.integer "submission_id"
264 285 t.string "input_file_name"
265 286 t.string "output_file_name"
266 287 t.string "running_stat"
267 288 t.integer "status"
268 289 t.datetime "updated_at", precision: nil, null: false
269 290 t.datetime "submitted_at", precision: nil
270 291 t.datetime "compiled_at", precision: nil
271 - t.text "compiler_message", size: :medium
292 + t.text "compiler_message"
272 293 t.datetime "graded_at", precision: nil
273 294 t.string "grader_comment"
274 295 t.datetime "created_at", precision: nil, null: false
275 296 t.float "running_time"
276 297 t.string "exit_status"
277 298 t.integer "memory_usage"
278 299 t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
279 300 end
280 301
281 302 create_table "testcases", id: :integer, charset: "latin1", force: :cascade do |t|
282 303 t.integer "problem_id"
283 304 t.integer "num"
284 305 t.integer "group"
285 306 t.integer "score"
286 307 t.text "input", size: :long
287 308 t.text "sol", size: :long
288 - t.datetime "created_at", precision: nil, null: false
289 - t.datetime "updated_at", precision: nil, null: false
309 + t.datetime "created_at", precision: nil
310 + t.datetime "updated_at", precision: nil
290 311 t.index ["problem_id"], name: "index_testcases_on_problem_id"
291 312 end
292 313
293 - create_table "user_contest_stats", id: :integer, charset: "utf8", force: :cascade do |t|
314 + create_table "user_contest_stats", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
294 315 t.integer "user_id"
295 316 t.datetime "started_at", precision: nil
296 317 t.datetime "created_at", precision: nil, null: false
297 318 t.datetime "updated_at", precision: nil, null: false
298 319 t.boolean "forced_logout"
299 320 end
300 321
301 - create_table "users", id: :integer, charset: "utf8", force: :cascade do |t|
322 + create_table "users", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
302 323 t.string "login", limit: 50
303 324 t.string "full_name"
304 325 t.string "hashed_password"
305 326 t.string "salt", limit: 5
306 327 t.string "alias"
307 328 t.string "email"
308 329 t.integer "site_id"
309 330 t.integer "country_id"
310 331 t.boolean "activated", default: false
311 332 t.datetime "created_at", precision: nil
312 333 t.datetime "updated_at", precision: nil
313 - t.string "section"
314 334 t.boolean "enabled", default: true
315 335 t.string "remark"
316 336 t.string "last_ip"
337 + t.string "section"
317 338 t.integer "default_language"
318 339 t.index ["login"], name: "index_users_on_login", unique: true
319 340 end
320 341
342 + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
343 + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
321 344 add_foreign_key "problems_tags", "problems"
322 345 add_foreign_key "problems_tags", "tags"
323 - add_foreign_key "solutions", "problems"
324 - add_foreign_key "solutions", "submissions"
325 346 end
deleted file
You need to be logged in to leave comments. Login now