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

r672:d41325ea3a15 - - 20 files changed: 365 inserted, 10 deleted

@@ -0,0 +1,86
1 + class GroupsController < ApplicationController
2 + before_action :set_group, only: [:show, :edit, :update, :destroy,
3 + :add_user, :remove_user,
4 + :add_problem, :remove_problem,
5 + ]
6 + before_action :authenticate, :admin_authorization
7 +
8 + # GET /groups
9 + def index
10 + @groups = Group.all
11 + end
12 +
13 + # GET /groups/1
14 + def show
15 + end
16 +
17 + # GET /groups/new
18 + def new
19 + @group = Group.new
20 + end
21 +
22 + # GET /groups/1/edit
23 + def edit
24 + end
25 +
26 + # POST /groups
27 + def create
28 + @group = Group.new(group_params)
29 +
30 + if @group.save
31 + redirect_to @group, notice: 'Group was successfully created.'
32 + else
33 + render :new
34 + end
35 + end
36 +
37 + # PATCH/PUT /groups/1
38 + def update
39 + if @group.update(group_params)
40 + redirect_to @group, notice: 'Group was successfully updated.'
41 + else
42 + render :edit
43 + end
44 + end
45 +
46 + # DELETE /groups/1
47 + def destroy
48 + @group.destroy
49 + redirect_to groups_url, notice: 'Group was successfully destroyed.'
50 + end
51 +
52 + def remove_user
53 + user = User.find(params[:user_id])
54 + @group.users.delete(user)
55 + redirect_to group_path(@group), notice: "User #{user.login} was removed from the group #{@group.name}"
56 + end
57 +
58 + def add_user
59 + user = User.find(params[:user_id])
60 + @group.users << user
61 + redirect_to group_path(@group), notice: "User #{user.login} was add to the group #{@group.name}"
62 + end
63 +
64 + def remove_problem
65 + problem = Problem.find(params[:problem_id])
66 + @group.problems.delete(problem)
67 + redirect_to group_path(@group), notice: "Problem #{problem.name} was removed from the group #{@group.name}"
68 + end
69 +
70 + def add_problem
71 + problem = Problem.find(params[:problem_id])
72 + @group.problems << problem
73 + redirect_to group_path(@group), notice: "Problem #{problem.name} was add to the group #{@group.name}"
74 + end
75 +
76 + private
77 + # Use callbacks to share common setup or constraints between actions.
78 + def set_group
79 + @group = Group.find(params[:id])
80 + end
81 +
82 + # Only allow a trusted parameter "white list" through.
83 + def group_params
84 + params.require(:group).permit(:name, :description)
85 + end
86 + end
@@ -0,0 +1,2
1 + module GroupsHelper
2 + end
@@ -0,0 +1,5
1 + class Group < ActiveRecord::Base
2 + has_and_belongs_to_many :problems
3 + has_and_belongs_to_many :users
4 + end
5 +
@@ -0,0 +1,16
1 + = form_for @group do |f|
2 + - if @group.errors.any?
3 + #error_explanation
4 + %h2= "#{pluralize(@group.errors.count, "error")} prohibited this group from being saved:"
5 + %ul
6 + - @group.errors.full_messages.each do |msg|
7 + %li= msg
8 +
9 + .form-group.field
10 + = f.label :name
11 + = f.text_field :name, class: 'form-control'
12 + .form-group.field
13 + = f.label :description
14 + = f.text_field :description, class: 'form-control'
15 + .form-group.actions
16 + = f.submit 'Save', class: 'btn btn-primary'
@@ -0,0 +1,7
1 + %h1 Editing group
2 +
3 + = render 'form'
4 +
5 + = link_to 'Show', @group
6 + \|
7 + = link_to 'Back', groups_path
@@ -0,0 +1,24
1 + %h1 Groups
2 +
3 + %p
4 + = link_to 'New Group', new_group_path, class: 'btn btn-primary'
5 + %table.table.table-hover
6 + %thead
7 + %tr
8 + %th Name
9 + %th Description
10 + %th
11 + %th
12 + %th
13 +
14 + %tbody
15 + - @groups.each do |group|
16 + %tr
17 + %td= group.name
18 + %td= group.description
19 + %td= link_to 'Show', group, class: 'btn btn-default'
20 + %td= link_to 'Edit', edit_group_path(group), class: 'btn btn-default'
21 + %td= link_to 'Destroy', group, :method => :delete, :data => { :confirm => 'Are you sure?' }, class: 'btn btn-danger'
22 +
23 + %br
24 +
@@ -0,0 +1,5
1 + %h1 New group
2 +
3 + = render 'form'
4 +
5 + = link_to 'Back', groups_path
@@ -0,0 +1,67
1 + %p#notice= notice
2 +
3 + %p
4 + %b Name:
5 + = @group.name
6 + %p
7 + %b Description:
8 + = @group.description
9 +
10 + %br
11 + = link_to 'Edit', edit_group_path(@group)
12 + \|
13 + = link_to 'Back', groups_path
14 +
15 + .row
16 + .col-md-6
17 + %h1 Users in this group
18 +
19 + =form_tag add_user_group_path(@group), class: 'form-inline' do
20 + .form-group
21 + =label_tag :user_id, "User"
22 + =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2'
23 + =submit_tag "Add",class: 'btn btn-primary'
24 +
25 +
26 + %table.table.table-hover
27 + %thead
28 + %tr
29 + %th Login
30 + %th Full name
31 + %th Remark
32 + %th
33 +
34 + %tbody
35 + - @group.users.each do |user|
36 + %tr
37 + %td= user.login
38 + %td= user.full_name
39 + %td= user.remark
40 + %td= link_to 'Remove', remove_user_group_path(@group,user), :method => :delete, :data => { :confirm => "Remove #{user.full_name}?" }, class: 'btn btn-danger'
41 + .col-md-6
42 + %h1 Problems in this group
43 +
44 + =form_tag add_problem_group_path(@group), class: 'form-inline' do
45 + .form-group
46 + =label_tag :problem_id, "Problem"
47 + =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2'
48 + =submit_tag "Add",class: 'btn btn-primary'
49 +
50 +
51 + %table.table.table-hover
52 + %thead
53 + %tr
54 + %th name
55 + %th Full name
56 + %th Full score
57 + %th
58 +
59 + %tbody
60 + - @group.problems.each do |problem|
61 + %tr
62 + %td= problem.name
63 + %td= problem.full_name
64 + %td= problem.full_score
65 + %td= link_to 'Remove', remove_problem_group_path(@group,problem), :method => :delete, :data => { :confirm => "Remove #{problem.full_name}?" }, class: 'btn btn-danger'
66 +
67 +
@@ -0,0 +1,30
1 + class CreateGroups < ActiveRecord::Migration
2 +
3 + def change
4 + create_table :groups do |t|
5 + t.string :name
6 + t.string :description
7 + end
8 +
9 + create_join_table :groups, :users do |t|
10 + # t.index [:group_id, :user_id]
11 + t.index [:user_id, :group_id]
12 + end
13 +
14 + create_join_table :problems, :groups do |t|
15 + # t.index [:problem_id, :group_id]
16 + t.index [:group_id, :problem_id]
17 + end
18 +
19 + reversible do |change|
20 + change.up do
21 + GraderConfiguration.where(key: 'system.use_problem_group').first_or_create(value_type: 'boolean', value: 'false',
22 + description: 'If true, available problem to the user will be only ones associated with the group of the user');
23 + end
24 +
25 + change.down do
26 + GraderConfiguration.where(key: 'system.use_problem_group').destroy_all
27 + end
28 + end
29 + end
30 + end
@@ -0,0 +1,49
1 + require 'test_helper'
2 +
3 + class GroupsControllerTest < ActionController::TestCase
4 + setup do
5 + @group = groups(:one)
6 + end
7 +
8 + test "should get index" do
9 + get :index
10 + assert_response :success
11 + assert_not_nil assigns(:groups)
12 + end
13 +
14 + test "should get new" do
15 + get :new
16 + assert_response :success
17 + end
18 +
19 + test "should create group" do
20 + assert_difference('Group.count') do
21 + post :create, group: { description: @group.description, name: @group.name }
22 + end
23 +
24 + assert_redirected_to group_path(assigns(:group))
25 + end
26 +
27 + test "should show group" do
28 + get :show, id: @group
29 + assert_response :success
30 + end
31 +
32 + test "should get edit" do
33 + get :edit, id: @group
34 + assert_response :success
35 + end
36 +
37 + test "should update group" do
38 + patch :update, id: @group, group: { description: @group.description, name: @group.name }
39 + assert_redirected_to group_path(assigns(:group))
40 + end
41 +
42 + test "should destroy group" do
43 + assert_difference('Group.count', -1) do
44 + delete :destroy, id: @group
45 + end
46 +
47 + assert_redirected_to groups_path
48 + end
49 + end
@@ -192,12 +192,17
192 elsif params.has_key? 'add_to_contest'
192 elsif params.has_key? 'add_to_contest'
193 add_to_contest
193 add_to_contest
194 elsif params.has_key? 'enable_problem'
194 elsif params.has_key? 'enable_problem'
195 set_available(true)
195 set_available(true)
196 elsif params.has_key? 'disable_problem'
196 elsif params.has_key? 'disable_problem'
197 set_available(false)
197 set_available(false)
198 + elsif params.has_key? 'add_group'
199 + group = Group.find(params[:group_id])
200 + get_problems_from_params.each do |p|
201 + group.problems << p
202 + end
198 end
203 end
199 redirect_to :action => 'manage'
204 redirect_to :action => 'manage'
200 end
205 end
201
206
202 def import
207 def import
203 @allow_test_pair_import = allow_test_pair_import?
208 @allow_test_pair_import = allow_test_pair_import?
@@ -437,16 +437,15
437 password = random_password
437 password = random_password
438 u.password = password
438 u.password = password
439 u.password_confirmation = password
439 u.password_confirmation = password
440 u.save
440 u.save
441 end
441 end
442 end
442 end
443 - if @action[:add_group]
443 + if @action[:add_group] and @action[:group_name]
444 - @uses.each do |u|
444 + @group = Group.find(@action[:group_name])
445 -
445 + @users.each { |user| @group.users << user }
446 - end
447 end
446 end
448 end
447 end
449 end
448 end
450
449
451 protected
450 protected
452
451
@@ -9,12 +9,13
9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 MULTICONTESTS_KEY = 'system.multicontests'
10 MULTICONTESTS_KEY = 'system.multicontests'
11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
13 VIEW_TESTCASE = 'right.view_testcase'
13 VIEW_TESTCASE = 'right.view_testcase'
14 SINGLE_USER_KEY = 'system.single_user_mode'
14 SINGLE_USER_KEY = 'system.single_user_mode'
15 + SYSTEM_USE_PROBLEM_GROUP = 'system.use_problem_group'
15
16
16 cattr_accessor :config_cache
17 cattr_accessor :config_cache
17 cattr_accessor :task_grading_info_cache
18 cattr_accessor :task_grading_info_cache
18 cattr_accessor :contest_time_str
19 cattr_accessor :contest_time_str
19 cattr_accessor :contest_time
20 cattr_accessor :contest_time
20
21
@@ -116,12 +117,16
116 return ((mode == 'contest') or (mode == 'indv-contest'))
117 return ((mode == 'contest') or (mode == 'indv-contest'))
117 end
118 end
118
119
119 def self.analysis_mode?
120 def self.analysis_mode?
120 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
121 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
121 end
122 end
123 +
124 + def self.use_problem_group?
125 + return get(SYSTEM_USE_PROBLEM_GROUP)
126 + end
122
127
123 def self.contest_time_limit
128 def self.contest_time_limit
124 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
129 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
125
130
126 if not defined? GraderConfiguration.contest_time_str
131 if not defined? GraderConfiguration.contest_time_str
127 GraderConfiguration.contest_time_str = nil
132 GraderConfiguration.contest_time_str = nil
@@ -1,10 +1,11
1 class Problem < ActiveRecord::Base
1 class Problem < ActiveRecord::Base
2
2
3 belongs_to :description
3 belongs_to :description
4 has_and_belongs_to_many :contests, :uniq => true
4 has_and_belongs_to_many :contests, :uniq => true
5 + has_and_belongs_to_many :groups
5 has_many :test_pairs, :dependent => :delete_all
6 has_many :test_pairs, :dependent => :delete_all
6 has_many :testcases, :dependent => :destroy
7 has_many :testcases, :dependent => :destroy
7
8
8 validates_presence_of :name
9 validates_presence_of :name
9 validates_format_of :name, :with => /\A\w+\z/
10 validates_format_of :name, :with => /\A\w+\z/
10 validates_presence_of :full_name
11 validates_presence_of :full_name
@@ -4,12 +4,13
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10 + has_and_belongs_to_many :groups
10
11
11 has_many :test_requests, -> {order(submitted_at: DESC)}
12 has_many :test_requests, -> {order(submitted_at: DESC)}
12
13
13 has_many :messages, -> { order(created_at: DESC) },
14 has_many :messages, -> { order(created_at: DESC) },
14 :class_name => "Message",
15 :class_name => "Message",
15 :foreign_key => "sender_id"
16 :foreign_key => "sender_id"
@@ -287,13 +288,17
287 end
288 end
288 return true
289 return true
289 end
290 end
290
291
291 def available_problems
292 def available_problems
292 if not GraderConfiguration.multicontests?
293 if not GraderConfiguration.multicontests?
293 - return Problem.available_problems
294 + if GraderConfiguration.use_problem_group?
295 + return available_problems_in_group
296 + else
297 + return Problem.available_problems
298 + end
294 else
299 else
295 contest_problems = []
300 contest_problems = []
296 pin = {}
301 pin = {}
297 contests.enabled.each do |contest|
302 contests.enabled.each do |contest|
298 contest.problems.available.each do |problem|
303 contest.problems.available.each do |problem|
299 if not pin.has_key? problem.id
304 if not pin.has_key? problem.id
@@ -304,12 +309,20
304 end
309 end
305 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
310 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
306 return contest_problems + other_avaiable_problems
311 return contest_problems + other_avaiable_problems
307 end
312 end
308 end
313 end
309
314
315 + def available_problems_in_group
316 + problem = []
317 + self.groups.each do |group|
318 + group.problems.where(available: true).each { |p| problem << p }
319 + end
320 + return problem.uniq
321 + end
322 +
310 def can_view_problem?(problem)
323 def can_view_problem?(problem)
311 if not GraderConfiguration.multicontests?
324 if not GraderConfiguration.multicontests?
312 return problem.available
325 return problem.available
313 else
326 else
314 return problem_in_user_contests? problem
327 return problem_in_user_contests? problem
315 end
328 end
@@ -36,13 +36,13
36
36
37 %h1 Manage problems
37 %h1 Manage problems
38
38
39 %p= link_to '[Back to problem list]', :action => 'list'
39 %p= link_to '[Back to problem list]', :action => 'list'
40
40
41 = form_tag :action=>'do_manage' do
41 = form_tag :action=>'do_manage' do
42 - .submitbox
42 + .submitbox.panel
43 What do you want to do to the selected problem?
43 What do you want to do to the selected problem?
44 %br/
44 %br/
45 (You can shift-click to select a range of problems)
45 (You can shift-click to select a range of problems)
46 %ul
46 %ul
47 %li
47 %li
48 Change date added to
48 Change date added to
@@ -56,14 +56,19
56
56
57 - if GraderConfiguration.multicontests?
57 - if GraderConfiguration.multicontests?
58 %li
58 %li
59 Add to
59 Add to
60 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
60 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
61 = submit_tag 'Add', :name => 'add_to_contest'
61 = submit_tag 'Add', :name => 'add_to_contest'
62 + %li
63 + Add problems to group
64 + = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
65 + = submit_tag 'Add', name: 'add_group'
66 +
62
67
63 - %table
68 + %table.table.table-hover
64 %tr{style: "text-align: left;"}
69 %tr{style: "text-align: left;"}
65 %th= check_box_tag 'select_all'
70 %th= check_box_tag 'select_all'
66 %th Name
71 %th Name
67 %th Full name
72 %th Full name
68 %th Available
73 %th Available
69 %th Date added
74 %th Date added
@@ -50,13 +50,13
50 .col-md-4
50 .col-md-4
51 %label.checkbox-inline
51 %label.checkbox-inline
52 = check_box_tag "add_group", true, params[:add_group]
52 = check_box_tag "add_group", true, params[:add_group]
53 Add users to group
53 Add users to group
54 %label.col-md-3.control-label.text-right Group name
54 %label.col-md-3.control-label.text-right Group name
55 .col-md-5
55 .col-md-5
56 - = text_field_tag "group_name", params[:group_name], id: 'group_name',class: 'form-control select2'
56 + = select_tag "group_name", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'form-control select2'
57
57
58
58
59 .row
59 .row
60 .col-md-12
60 .col-md-12
61 = submit_tag "Preview Result", class: 'btn btn-default'
61 = submit_tag "Preview Result", class: 'btn btn-default'
62 - if @users
62 - if @users
@@ -26,13 +26,21
26 collection do
26 collection do
27 get 'turn_all_off'
27 get 'turn_all_off'
28 get 'turn_all_on'
28 get 'turn_all_on'
29 get 'import'
29 get 'import'
30 get 'manage'
30 get 'manage'
31 end
31 end
32 + end
32
33
34 + resources :groups do
35 + member do
36 + post 'add_user', to: 'groups#add_user', as: 'add_user'
37 + delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
38 + post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
39 + delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
40 + end
33 end
41 end
34
42
35 resources :testcases, only: [] do
43 resources :testcases, only: [] do
36 member do
44 member do
37 get 'download_input'
45 get 'download_input'
38 get 'download_sol'
46 get 'download_sol'
@@ -8,13 +8,13
8 # system, you should be using db:schema:load, not running all the migrations
8 # system, you should be using db:schema:load, not running all the migrations
9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 # you'll amass, the slower it'll run and the greater likelihood for issues).
10 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 #
11 #
12 # It's strongly recommended that you check this file into your version control system.
12 # It's strongly recommended that you check this file into your version control system.
13
13
14 - ActiveRecord::Schema.define(version: 20170427070345) do
14 + ActiveRecord::Schema.define(version: 20170911091143) do
15
15
16 create_table "announcements", force: :cascade do |t|
16 create_table "announcements", force: :cascade do |t|
17 t.string "author", limit: 255
17 t.string "author", limit: 255
18 t.text "body", limit: 65535
18 t.text "body", limit: 65535
19 t.boolean "published"
19 t.boolean "published"
20 t.datetime "created_at", null: false
20 t.datetime "created_at", null: false
@@ -76,12 +76,31
76 t.string "task_type", limit: 255
76 t.string "task_type", limit: 255
77 t.boolean "terminated"
77 t.boolean "terminated"
78 end
78 end
79
79
80 add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree
80 add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree
81
81
82 + create_table "groups", force: :cascade do |t|
83 + t.string "name", limit: 255
84 + t.string "description", limit: 255
85 + end
86 +
87 + create_table "groups_problems", id: false, force: :cascade do |t|
88 + t.integer "problem_id", limit: 4, null: false
89 + t.integer "group_id", limit: 4, null: false
90 + end
91 +
92 + add_index "groups_problems", ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id", using: :btree
93 +
94 + create_table "groups_users", id: false, force: :cascade do |t|
95 + t.integer "group_id", limit: 4, null: false
96 + t.integer "user_id", limit: 4, null: false
97 + end
98 +
99 + add_index "groups_users", ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id", using: :btree
100 +
82 create_table "heart_beats", force: :cascade do |t|
101 create_table "heart_beats", force: :cascade do |t|
83 t.integer "user_id", limit: 4
102 t.integer "user_id", limit: 4
84 t.string "ip_address", limit: 255
103 t.string "ip_address", limit: 255
85 t.datetime "created_at", null: false
104 t.datetime "created_at", null: false
86 t.datetime "updated_at", null: false
105 t.datetime "updated_at", null: false
87 t.string "status", limit: 255
106 t.string "status", limit: 255
@@ -160,13 +160,22
160
160
161 {
161 {
162 :key => 'contest.default_contest_name',
162 :key => 'contest.default_contest_name',
163 :value_type => 'string',
163 :value_type => 'string',
164 :default_value => 'none',
164 :default_value => 'none',
165 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
165 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
166 - }
166 + },
167 +
168 + {
169 + :key => 'system.use_problem_group',
170 + :value_type => 'boolean',
171 + :default_value => 'false',
172 + :description => "If true, available problem to the user will be only ones associated with the group of the user."
173 + },
174 +
175 +
167
176
168 ]
177 ]
169
178
170
179
171 def create_configuration_key(key,
180 def create_configuration_key(key,
172 value_type,
181 value_type,
You need to be logged in to leave comments. Login now