Description:
Merge pull request #20 from nattee/master
feature merge
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r719:19bd5319581a - - 95 files changed: 1275 inserted, 462 deleted
new file 100644 |
@@ -0,0 +1,3 | |||||
|
|
1 | + # Place all the behaviors and hooks related to the matching controller here. | ||
|
|
2 | + # All this logic will automatically be available in application.js. | ||
|
|
3 | + # You can use CoffeeScript in this file: http://coffeescript.org/ |
new file 100644 |
new file 100644 |
@@ -0,0 +1,3 | |||||
|
|
1 | + // Place all the styles related to the tags controller here. | ||
|
|
2 | + // They will automatically be included in application.css. | ||
|
|
3 | + // You can use Sass (SCSS) here: http://sass-lang.com/ |
new file 100644 |
@@ -0,0 +1,104 | |||||
|
|
1 | + class GroupsController < ApplicationController | ||
|
|
2 | + before_action :set_group, only: [:show, :edit, :update, :destroy, | ||
|
|
3 | + :add_user, :remove_user,:remove_all_user, | ||
|
|
4 | + :add_problem, :remove_problem,:remove_all_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), flash: {success: "User #{user.login} was removed from the group #{@group.name}"} | ||
|
|
56 | + end | ||
|
|
57 | + | ||
|
|
58 | + def remove_all_user | ||
|
|
59 | + @group.users.clear | ||
|
|
60 | + redirect_to group_path(@group), alert: 'All users removed' | ||
|
|
61 | + end | ||
|
|
62 | + | ||
|
|
63 | + def remove_all_problem | ||
|
|
64 | + @group.problems.clear | ||
|
|
65 | + redirect_to group_path(@group), alert: 'All problems removed' | ||
|
|
66 | + end | ||
|
|
67 | + | ||
|
|
68 | + def add_user | ||
|
|
69 | + user = User.find(params[:user_id]) | ||
|
|
70 | + begin | ||
|
|
71 | + @group.users << user | ||
|
|
72 | + redirect_to group_path(@group), flash: { success: "User #{user.login} was add to the group #{@group.name}"} | ||
|
|
73 | + rescue => e | ||
|
|
74 | + redirect_to group_path(@group), alert: e.message | ||
|
|
75 | + end | ||
|
|
76 | + end | ||
|
|
77 | + | ||
|
|
78 | + def remove_problem | ||
|
|
79 | + problem = Problem.find(params[:problem_id]) | ||
|
|
80 | + @group.problems.delete(problem) | ||
|
|
81 | + redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was removed from the group #{@group.name}" } | ||
|
|
82 | + end | ||
|
|
83 | + | ||
|
|
84 | + def add_problem | ||
|
|
85 | + problem = Problem.find(params[:problem_id]) | ||
|
|
86 | + begin | ||
|
|
87 | + @group.problems << problem | ||
|
|
88 | + redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was add to the group #{@group.name}" } | ||
|
|
89 | + rescue => e | ||
|
|
90 | + redirect_to group_path(@group), alert: e.message | ||
|
|
91 | + end | ||
|
|
92 | + end | ||
|
|
93 | + | ||
|
|
94 | + private | ||
|
|
95 | + # Use callbacks to share common setup or constraints between actions. | ||
|
|
96 | + def set_group | ||
|
|
97 | + @group = Group.find(params[:id]) | ||
|
|
98 | + end | ||
|
|
99 | + | ||
|
|
100 | + # Only allow a trusted parameter "white list" through. | ||
|
|
101 | + def group_params | ||
|
|
102 | + params.require(:group).permit(:name, :description) | ||
|
|
103 | + end | ||
|
|
104 | + end |
@@ -0,0 +1,60 | |||||
|
|
1 | + class TagsController < ApplicationController | ||
|
|
2 | + before_action :set_tag, only: [:show, :edit, :update, :destroy] | ||
|
|
3 | + | ||
|
|
4 | + # GET /tags | ||
|
|
5 | + def index | ||
|
|
6 | + @tags = Tag.all | ||
|
|
7 | + end | ||
|
|
8 | + | ||
|
|
9 | + # GET /tags/1 | ||
|
|
10 | + def show | ||
|
|
11 | + end | ||
|
|
12 | + | ||
|
|
13 | + # GET /tags/new | ||
|
|
14 | + def new | ||
|
|
15 | + @tag = Tag.new | ||
|
|
16 | + end | ||
|
|
17 | + | ||
|
|
18 | + # GET /tags/1/edit | ||
|
|
19 | + def edit | ||
|
|
20 | + end | ||
|
|
21 | + | ||
|
|
22 | + # POST /tags | ||
|
|
23 | + def create | ||
|
|
24 | + @tag = Tag.new(tag_params) | ||
|
|
25 | + | ||
|
|
26 | + if @tag.save | ||
|
|
27 | + redirect_to @tag, notice: 'Tag was successfully created.' | ||
|
|
28 | + else | ||
|
|
29 | + render :new | ||
|
|
30 | + end | ||
|
|
31 | + end | ||
|
|
32 | + | ||
|
|
33 | + # PATCH/PUT /tags/1 | ||
|
|
34 | + def update | ||
|
|
35 | + if @tag.update(tag_params) | ||
|
|
36 | + redirect_to @tag, notice: 'Tag was successfully updated.' | ||
|
|
37 | + else | ||
|
|
38 | + render :edit | ||
|
|
39 | + end | ||
|
|
40 | + end | ||
|
|
41 | + | ||
|
|
42 | + # DELETE /tags/1 | ||
|
|
43 | + def destroy | ||
|
|
44 | + #remove any association | ||
|
|
45 | + ProblemTag.where(tag_id: @tag.id).destroy_all | ||
|
|
46 | + @tag.destroy | ||
|
|
47 | + redirect_to tags_url, notice: 'Tag was successfully destroyed.' | ||
|
|
48 | + end | ||
|
|
49 | + | ||
|
|
50 | + private | ||
|
|
51 | + # Use callbacks to share common setup or constraints between actions. | ||
|
|
52 | + def set_tag | ||
|
|
53 | + @tag = Tag.find(params[:id]) | ||
|
|
54 | + end | ||
|
|
55 | + | ||
|
|
56 | + # Only allow a trusted parameter "white list" through. | ||
|
|
57 | + def tag_params | ||
|
|
58 | + params.require(:tag).permit(:name, :description, :public) | ||
|
|
59 | + end | ||
|
|
60 | + end |
@@ -0,0 +1,13 | |||||
|
|
1 | + class Group < ActiveRecord::Base | ||
|
|
2 | + has_many :groups_problems, class_name: GroupProblem | ||
|
|
3 | + has_many :problems, :through => :groups_problems | ||
|
|
4 | + | ||
|
|
5 | + has_many :groups_users, class_name: GroupUser | ||
|
|
6 | + has_many :users, :through => :groups_users | ||
|
|
7 | + | ||
|
|
8 | + #has_and_belongs_to_many :problems | ||
|
|
9 | + #has_and_belongs_to_many :users | ||
|
|
10 | + | ||
|
|
11 | + | ||
|
|
12 | + end | ||
|
|
13 | + |
@@ -0,0 +1,7 | |||||
|
|
1 | + class GroupProblem < ActiveRecord::Base | ||
|
|
2 | + self.table_name = 'groups_problems' | ||
|
|
3 | + | ||
|
|
4 | + belongs_to :problem | ||
|
|
5 | + belongs_to :group | ||
|
|
6 | + validates_uniqueness_of :problem_id, scope: :group_id, message: ->(object, data) { "'#{Problem.find(data[:value]).full_name}' is already in the group" } | ||
|
|
7 | + end |
@@ -0,0 +1,7 | |||||
|
|
1 | + class GroupUser < ActiveRecord::Base | ||
|
|
2 | + self.table_name = 'groups_users' | ||
|
|
3 | + | ||
|
|
4 | + belongs_to :user | ||
|
|
5 | + belongs_to :group | ||
|
|
6 | + validates_uniqueness_of :user_id, scope: :group_id, message: ->(object, data) { "'#{User.find(data[:value]).full_name}' is already in the group" } | ||
|
|
7 | + end |
@@ -0,0 +1,8 | |||||
|
|
1 | + class ProblemTag < ActiveRecord::Base | ||
|
|
2 | + self.table_name = 'problems_tags' | ||
|
|
3 | + | ||
|
|
4 | + belongs_to :problem | ||
|
|
5 | + belongs_to :tag | ||
|
|
6 | + | ||
|
|
7 | + validates_uniqueness_of :problem_id, scope: :tag_id, message: ->(object, data) { "'#{Problem.find(data[:value]).full_name}' is already has this tag" } | ||
|
|
8 | + end |
@@ -0,0 +1,4 | |||||
|
|
1 | + class Tag < ActiveRecord::Base | ||
|
|
2 | + has_many :problems_tags, class_name: ProblemTag | ||
|
|
3 | + has_many :problems, through: :problems_tags | ||
|
|
4 | + end |
@@ -0,0 +1,20 | |||||
|
|
1 | + %h1 Editing contest | ||
|
|
2 | + = form_for(@contest) do |f| | ||
|
|
3 | + = f.error_messages | ||
|
|
4 | + %table | ||
|
|
5 | + %tr | ||
|
|
6 | + %td= f.label :name | ||
|
|
7 | + %td= f.text_field :name | ||
|
|
8 | + %tr | ||
|
|
9 | + %td= f.label :title | ||
|
|
10 | + %td= f.text_field :title | ||
|
|
11 | + %tr | ||
|
|
12 | + %td | ||
|
|
13 | + %td | ||
|
|
14 | + = f.check_box :enabled | ||
|
|
15 | + = f.label :enabled | ||
|
|
16 | + %p | ||
|
|
17 | + = f.submit 'Update' | ||
|
|
18 | + = link_to 'Show', @contest | ||
|
|
19 | + | | ||
|
|
20 | + = link_to 'Back', contests_path |
@@ -0,0 +1,27 | |||||
|
|
1 | + %h1 Listing contests | ||
|
|
2 | + .infobox | ||
|
|
3 | + %b Go back to: | ||
|
|
4 | + [#{link_to 'contest management', :controller => 'contest_management', :action => 'index'}] | ||
|
|
5 | + %p= link_to 'New contest', new_contest_path, class: 'btn btn-success' | ||
|
|
6 | + %table.table.table-striped | ||
|
|
7 | + %tr | ||
|
|
8 | + %th Name | ||
|
|
9 | + %th Title | ||
|
|
10 | + %th Enabled | ||
|
|
11 | + %th | ||
|
|
12 | + %th | ||
|
|
13 | + %th | ||
|
|
14 | + | ||
|
|
15 | + - @contests.each do |contest| | ||
|
|
16 | + - @contest = contest | ||
|
|
17 | + %tr | ||
|
|
18 | + -#%td= in_place_editor_field :contest, :name, {}, :rows => 1 | ||
|
|
19 | + -#%td= in_place_editor_field :contest, :title, {}, :rows => 1 | ||
|
|
20 | + -#%td= in_place_editor_field :contest, :enabled, {}, :rows => 1 | ||
|
|
21 | + %td= best_in_place @contest, :name | ||
|
|
22 | + %td= best_in_place @contest, :title | ||
|
|
23 | + %td= best_in_place @contest, :enabled | ||
|
|
24 | + %td= link_to 'Show', contest | ||
|
|
25 | + %td= link_to 'Edit', edit_contest_path(contest) | ||
|
|
26 | + %td= link_to 'Destroy', contest, :confirm => 'Are you sure?', :method => :delete | ||
|
|
27 | + %br/ |
@@ -0,0 +1,18 | |||||
|
|
1 | + %h1 New contest | ||
|
|
2 | + = form_for(@contest) do |f| | ||
|
|
3 | + = f.error_messages | ||
|
|
4 | + %p | ||
|
|
5 | + = f.label :name | ||
|
|
6 | + %br/ | ||
|
|
7 | + = f.text_field :name | ||
|
|
8 | + %p | ||
|
|
9 | + = f.label :title | ||
|
|
10 | + %br/ | ||
|
|
11 | + = f.text_field :title | ||
|
|
12 | + %p | ||
|
|
13 | + = f.label :enabled | ||
|
|
14 | + %br/ | ||
|
|
15 | + = f.check_box :enabled | ||
|
|
16 | + %p | ||
|
|
17 | + = f.submit 'Create' | ||
|
|
18 | + = link_to 'Back', contests_path |
@@ -0,0 +1,11 | |||||
|
|
1 | + %h1 | ||
|
|
2 | + Contest: #{h @contest.title} | ||
|
|
3 | + .infobox | ||
|
|
4 | + %b Go back to: | ||
|
|
5 | + [#{link_to 'contest management', :controller => 'contest_management', :action => 'index'}] | ||
|
|
6 | + %p | ||
|
|
7 | + %b Enabled: | ||
|
|
8 | + = h @contest.enabled | ||
|
|
9 | + = link_to 'Edit', edit_contest_path(@contest) | ||
|
|
10 | + | | ||
|
|
11 | + = link_to 'Back', contests_path |
@@ -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,22 | |||||
|
|
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 | + | ||
|
|
13 | + %tbody | ||
|
|
14 | + - @groups.each do |group| | ||
|
|
15 | + %tr | ||
|
|
16 | + %td= group.name | ||
|
|
17 | + %td= group.description | ||
|
|
18 | + %td= link_to 'View', group, class: 'btn btn-default' | ||
|
|
19 | + %td= link_to 'Destroy', group, :method => :delete, :data => { :confirm => 'Are you sure?' }, class: 'btn btn-danger' | ||
|
|
20 | + | ||
|
|
21 | + %br | ||
|
|
22 | + |
@@ -0,0 +1,73 | |||||
|
|
1 | + %p | ||
|
|
2 | + %b Name: | ||
|
|
3 | + = @group.name | ||
|
|
4 | + %p | ||
|
|
5 | + %b Description: | ||
|
|
6 | + = @group.description | ||
|
|
7 | + | ||
|
|
8 | + %br | ||
|
|
9 | + = link_to 'Edit', edit_group_path(@group) | ||
|
|
10 | + \| | ||
|
|
11 | + = link_to 'Back', groups_path | ||
|
|
12 | + | ||
|
|
13 | + .row | ||
|
|
14 | + .col-md-12 | ||
|
|
15 | + %h1 Group details | ||
|
|
16 | + .row | ||
|
|
17 | + .col-md-6 | ||
|
|
18 | + .panel.panel-default | ||
|
|
19 | + .panel-heading | ||
|
|
20 | + .panel-title Users in this group | ||
|
|
21 | + .panel-body | ||
|
|
22 | + =form_tag add_user_group_path(@group), class: 'form-inline' do | ||
|
|
23 | + .form-group | ||
|
|
24 | + =label_tag :user_id, "User" | ||
|
|
25 | + =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2' | ||
|
|
26 | + =submit_tag "Add",class: 'btn btn-primary' | ||
|
|
27 | + | ||
|
|
28 | + | ||
|
|
29 | + %table.table.table-hover | ||
|
|
30 | + %thead | ||
|
|
31 | + %tr | ||
|
|
32 | + %th Login | ||
|
|
33 | + %th Full name | ||
|
|
34 | + %th Remark | ||
|
|
35 | + %th= link_to 'Remove All', remove_all_user_group_path(@group), method: :delete, :data => { :confirm => "Remove ALL USERS from group?" }, class: 'btn btn-danger btn-sm' | ||
|
|
36 | + | ||
|
|
37 | + %tbody | ||
|
|
38 | + - @group.users.each do |user| | ||
|
|
39 | + %tr | ||
|
|
40 | + %td= user.login | ||
|
|
41 | + %td= user.full_name | ||
|
|
42 | + %td= user.remark | ||
|
|
43 | + %td= link_to 'Remove', remove_user_group_path(@group,user), :method => :delete, :data => { :confirm => "Remove #{user.full_name}?" }, class: 'btn btn-danger btn-sm' | ||
|
|
44 | + .col-md-6 | ||
|
|
45 | + .panel.panel-default | ||
|
|
46 | + .panel-heading | ||
|
|
47 | + .panel-title Problems | ||
|
|
48 | + .panel-body | ||
|
|
49 | + | ||
|
|
50 | + =form_tag add_problem_group_path(@group), class: 'form-inline' do | ||
|
|
51 | + .form-group | ||
|
|
52 | + =label_tag :problem_id, "Problem" | ||
|
|
53 | + =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2' | ||
|
|
54 | + =submit_tag "Add",class: 'btn btn-primary' | ||
|
|
55 | + | ||
|
|
56 | + | ||
|
|
57 | + %table.table.table-hover | ||
|
|
58 | + %thead | ||
|
|
59 | + %tr | ||
|
|
60 | + %th name | ||
|
|
61 | + %th Full name | ||
|
|
62 | + %th Full score | ||
|
|
63 | + %th= link_to 'Remove All', remove_all_problem_group_path(@group), method: :delete, :data => { :confirm => "Remove ALL PROBLEMS from group?" }, class: 'btn btn-danger btn-sm' | ||
|
|
64 | + | ||
|
|
65 | + %tbody | ||
|
|
66 | + - @group.problems.each do |problem| | ||
|
|
67 | + %tr | ||
|
|
68 | + %td= problem.name | ||
|
|
69 | + %td= problem.full_name | ||
|
|
70 | + %td= problem.full_score | ||
|
|
71 | + %td= link_to 'Remove', remove_problem_group_path(@group,problem), :method => :delete, :data => { :confirm => "Remove #{problem.full_name}?" }, class: 'btn btn-danger btn-sm' | ||
|
|
72 | + | ||
|
|
73 | + |
@@ -0,0 +1,22 | |||||
|
|
1 | + = form_for @tag do |f| | ||
|
|
2 | + - if @tag.errors.any? | ||
|
|
3 | + #error_explanation | ||
|
|
4 | + %h2= "#{pluralize(@tag.errors.count, "error")} prohibited this tag from being saved:" | ||
|
|
5 | + %ul | ||
|
|
6 | + - @tag.errors.full_messages.each do |msg| | ||
|
|
7 | + %li= msg | ||
|
|
8 | + | ||
|
|
9 | + .row | ||
|
|
10 | + .col-md-6 | ||
|
|
11 | + .form-group.field | ||
|
|
12 | + = f.label :name | ||
|
|
13 | + = f.text_field :name, class: 'form-control' | ||
|
|
14 | + .form-group.field | ||
|
|
15 | + = f.label :description | ||
|
|
16 | + = f.text_area :description, class: 'form-control' | ||
|
|
17 | + .form-group.field | ||
|
|
18 | + = f.label :public | ||
|
|
19 | + = f.text_field :public, class: 'form-control' | ||
|
|
20 | + .actions | ||
|
|
21 | + = f.submit 'Save', class: 'btn btn-primary' | ||
|
|
22 | + .col-md-6 |
@@ -0,0 +1,7 | |||||
|
|
1 | + %h1 Editing tag | ||
|
|
2 | + | ||
|
|
3 | + = render 'form' | ||
|
|
4 | + | ||
|
|
5 | + = link_to 'Show', @tag | ||
|
|
6 | + \| | ||
|
|
7 | + = link_to 'Back', tags_path |
@@ -0,0 +1,26 | |||||
|
|
1 | + %h1 Tags | ||
|
|
2 | + | ||
|
|
3 | + = link_to 'New Tag', new_tag_path, class: 'btn btn-success' | ||
|
|
4 | + | ||
|
|
5 | + %table.table.table-hover | ||
|
|
6 | + %thead | ||
|
|
7 | + %tr | ||
|
|
8 | + %th Name | ||
|
|
9 | + %th Description | ||
|
|
10 | + %th Public | ||
|
|
11 | + %th | ||
|
|
12 | + %th | ||
|
|
13 | + %th | ||
|
|
14 | + | ||
|
|
15 | + %tbody | ||
|
|
16 | + - @tags.each do |tag| | ||
|
|
17 | + %tr | ||
|
|
18 | + %td= tag.name | ||
|
|
19 | + %td= tag.description | ||
|
|
20 | + %td= tag.public | ||
|
|
21 | + %td= link_to 'Show', tag | ||
|
|
22 | + %td= link_to 'Edit', edit_tag_path(tag) | ||
|
|
23 | + %td= link_to 'Destroy', tag, :method => :delete, :data => { :confirm => 'Are you sure?' } | ||
|
|
24 | + | ||
|
|
25 | + %br | ||
|
|
26 | + |
@@ -0,0 +1,15 | |||||
|
|
1 | + %p#notice= notice | ||
|
|
2 | + | ||
|
|
3 | + %p | ||
|
|
4 | + %b Name: | ||
|
|
5 | + = @tag.name | ||
|
|
6 | + %p | ||
|
|
7 | + %b Description: | ||
|
|
8 | + = @tag.description | ||
|
|
9 | + %p | ||
|
|
10 | + %b Public: | ||
|
|
11 | + = @tag.public | ||
|
|
12 | + | ||
|
|
13 | + = link_to 'Edit', edit_tag_path(@tag) | ||
|
|
14 | + \| | ||
|
|
15 | + = link_to 'Back', tags_path |
@@ -0,0 +1,38 | |||||
|
|
1 | + = error_messages_for 'user' | ||
|
|
2 | + / [form:user] | ||
|
|
3 | + .form-group | ||
|
|
4 | + %label.col-md-2.control-label{for: :login} Login | ||
|
|
5 | + .col-md-4 | ||
|
|
6 | + = text_field 'user', 'login', class: 'form-control' | ||
|
|
7 | + .col-md-6 | ||
|
|
8 | + .form-group | ||
|
|
9 | + %label.col-md-2.control-label{for: :full_name} Full name | ||
|
|
10 | + .col-md-4 | ||
|
|
11 | + = text_field 'user', 'full_name', class: 'form-control' | ||
|
|
12 | + .col-md-6 | ||
|
|
13 | + .form-group | ||
|
|
14 | + %label.col-md-2.control-label{for: :password} Password | ||
|
|
15 | + .col-md-4 | ||
|
|
16 | + = password_field 'user', 'password', class: 'form-control' | ||
|
|
17 | + .col-md-6 | ||
|
|
18 | + .form-group | ||
|
|
19 | + %label.col-md-2.control-label{for: :password_confirmation} Password (confirm) | ||
|
|
20 | + .col-md-4 | ||
|
|
21 | + = password_field 'user', 'password_confirmation', class: 'form-control' | ||
|
|
22 | + .col-md-6 | ||
|
|
23 | + .form-group | ||
|
|
24 | + %label.col-md-2.control-label{for: :email} E-mail | ||
|
|
25 | + .col-md-4 | ||
|
|
26 | + = email_field 'user', 'email', class: 'form-control' | ||
|
|
27 | + .col-md-6 | ||
|
|
28 | + .form-group | ||
|
|
29 | + %label.col-md-2.control-label{for: :alias} Alias | ||
|
|
30 | + .col-md-4 | ||
|
|
31 | + = text_field 'user', 'alias', class: 'form-control' | ||
|
|
32 | + .col-md-6 | ||
|
|
33 | + .form-group | ||
|
|
34 | + %label.col-md-2.control-label{for: :remark} Remark | ||
|
|
35 | + .col-md-4 | ||
|
|
36 | + = text_field 'user', 'remark', class: 'form-control' | ||
|
|
37 | + .col-md-6 | ||
|
|
38 | + / [eoform:user] |
@@ -0,0 +1,7 | |||||
|
|
1 | + %h1 New user | ||
|
|
2 | + = form_tag( {action: 'create'}, { class: 'form-horizontal'}) do | ||
|
|
3 | + = render :partial => 'form' | ||
|
|
4 | + .form-group | ||
|
|
5 | + .col-md-offset-2.col-md-10 | ||
|
|
6 | + = submit_tag "Create", class: 'btn btn-primary' | ||
|
|
7 | + = link_to 'Back', :action => 'index' |
@@ -0,0 +1,14 | |||||
|
|
1 | + %h1 User information | ||
|
|
2 | + - for column in User.content_columns | ||
|
|
3 | + %p | ||
|
|
4 | + %b | ||
|
|
5 | + = column.human_name | ||
|
|
6 | + \: | ||
|
|
7 | + = h @user.send(column.name) | ||
|
|
8 | + %p | ||
|
|
9 | + %strong Group | ||
|
|
10 | + \: | ||
|
|
11 | + = @user.groups.map{ |x| link_to(x.name,group_path(x)).html_safe}.join(', ').html_safe | ||
|
|
12 | + = link_to 'Edit', :action => 'edit', :id => @user | ||
|
|
13 | + | | ||
|
|
14 | + = link_to 'Back', :action => 'index' |
@@ -0,0 +1,23 | |||||
|
|
1 | + # Be sure to restart your server when you modify this file. | ||
|
|
2 | + | ||
|
|
3 | + # Version of your assets, change this if you want to expire all your assets. | ||
|
|
4 | + Rails.application.config.assets.version = '1.0' | ||
|
|
5 | + | ||
|
|
6 | + # Add additional assets to the asset load path. | ||
|
|
7 | + # Rails.application.config.assets.paths << Emoji.images_path | ||
|
|
8 | + # Add Yarn node_modules folder to the asset load path. | ||
|
|
9 | + Rails.application.config.assets.paths << Rails.root.join('node_modules') | ||
|
|
10 | + Rails.application.config.assets.paths << Rails.root.join('vendor/assets/fonts') | ||
|
|
11 | + | ||
|
|
12 | + # Precompile additional assets. | ||
|
|
13 | + # application.js, application.css, and all non-JS/CSS in the app/assets | ||
|
|
14 | + # folder are already added. | ||
|
|
15 | + # Rails.application.config.assets.precompile += %w( admin.js admin.css ) | ||
|
|
16 | + | ||
|
|
17 | + Rails.application.config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] | ||
|
|
18 | + Rails.application.config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] | ||
|
|
19 | + %w( announcements submissions configurations contests contest_management graders heartbeat | ||
|
|
20 | + login main messages problems report site sites sources tasks groups | ||
|
|
21 | + test user_admin users tags testcases).each do |controller| | ||
|
|
22 | + Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"] | ||
|
|
23 | + end |
@@ -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,11 | |||||
|
|
1 | + class CreateTags < ActiveRecord::Migration | ||
|
|
2 | + def change | ||
|
|
3 | + create_table :tags do |t| | ||
|
|
4 | + t.string :name, null: false | ||
|
|
5 | + t.text :description | ||
|
|
6 | + t.boolean :public | ||
|
|
7 | + | ||
|
|
8 | + t.timestamps null: false | ||
|
|
9 | + end | ||
|
|
10 | + end | ||
|
|
11 | + end |
@@ -0,0 +1,10 | |||||
|
|
1 | + class CreateProblemTags < ActiveRecord::Migration | ||
|
|
2 | + def change | ||
|
|
3 | + create_table :problems_tags do |t| | ||
|
|
4 | + t.references :problem, index: true, foreign_key: true | ||
|
|
5 | + t.references :tag, index: true, foreign_key: true | ||
|
|
6 | + | ||
|
|
7 | + t.index [:problem_id,:tag_id], unique: true | ||
|
|
8 | + end | ||
|
|
9 | + end | ||
|
|
10 | + 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 |
@@ -0,0 +1,49 | |||||
|
|
1 | + require 'test_helper' | ||
|
|
2 | + | ||
|
|
3 | + class TagsControllerTest < ActionController::TestCase | ||
|
|
4 | + setup do | ||
|
|
5 | + @tag = tags(:one) | ||
|
|
6 | + end | ||
|
|
7 | + | ||
|
|
8 | + test "should get index" do | ||
|
|
9 | + get :index | ||
|
|
10 | + assert_response :success | ||
|
|
11 | + assert_not_nil assigns(:tags) | ||
|
|
12 | + end | ||
|
|
13 | + | ||
|
|
14 | + test "should get new" do | ||
|
|
15 | + get :new | ||
|
|
16 | + assert_response :success | ||
|
|
17 | + end | ||
|
|
18 | + | ||
|
|
19 | + test "should create tag" do | ||
|
|
20 | + assert_difference('Tag.count') do | ||
|
|
21 | + post :create, tag: { description: @tag.description, name: @tag.name, public: @tag.public } | ||
|
|
22 | + end | ||
|
|
23 | + | ||
|
|
24 | + assert_redirected_to tag_path(assigns(:tag)) | ||
|
|
25 | + end | ||
|
|
26 | + | ||
|
|
27 | + test "should show tag" do | ||
|
|
28 | + get :show, id: @tag | ||
|
|
29 | + assert_response :success | ||
|
|
30 | + end | ||
|
|
31 | + | ||
|
|
32 | + test "should get edit" do | ||
|
|
33 | + get :edit, id: @tag | ||
|
|
34 | + assert_response :success | ||
|
|
35 | + end | ||
|
|
36 | + | ||
|
|
37 | + test "should update tag" do | ||
|
|
38 | + patch :update, id: @tag, tag: { description: @tag.description, name: @tag.name, public: @tag.public } | ||
|
|
39 | + assert_redirected_to tag_path(assigns(:tag)) | ||
|
|
40 | + end | ||
|
|
41 | + | ||
|
|
42 | + test "should destroy tag" do | ||
|
|
43 | + assert_difference('Tag.count', -1) do | ||
|
|
44 | + delete :destroy, id: @tag | ||
|
|
45 | + end | ||
|
|
46 | + | ||
|
|
47 | + assert_redirected_to tags_path | ||
|
|
48 | + end | ||
|
|
49 | + end |
@@ -0,0 +1,9 | |||||
|
|
1 | + # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html | ||
|
|
2 | + | ||
|
|
3 | + one: | ||
|
|
4 | + problem_id: | ||
|
|
5 | + tag_id: | ||
|
|
6 | + | ||
|
|
7 | + two: | ||
|
|
8 | + problem_id: | ||
|
|
9 | + tag_id: |
@@ -0,0 +1,11 | |||||
|
|
1 | + # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html | ||
|
|
2 | + | ||
|
|
3 | + one: | ||
|
|
4 | + name: MyString | ||
|
|
5 | + description: MyString | ||
|
|
6 | + public: | ||
|
|
7 | + | ||
|
|
8 | + two: | ||
|
|
9 | + name: MyString | ||
|
|
10 | + description: MyString | ||
|
|
11 | + public: |
@@ -0,0 +1,7 | |||||
|
|
1 | + require 'test_helper' | ||
|
|
2 | + | ||
|
|
3 | + class ProblemTagTest < ActiveSupport::TestCase | ||
|
|
4 | + # test "the truth" do | ||
|
|
5 | + # assert true | ||
|
|
6 | + # end | ||
|
|
7 | + end |
@@ -0,0 +1,7 | |||||
|
|
1 | + require 'test_helper' | ||
|
|
2 | + | ||
|
|
3 | + class TagTest < ActiveSupport::TestCase | ||
|
|
4 | + # test "the truth" do | ||
|
|
5 | + # assert true | ||
|
|
6 | + # end | ||
|
|
7 | + end |
@@ -59,15 +59,16 | |||||
|
59 | #syntax highlighter |
|
59 | #syntax highlighter |
|
60 | gem 'rouge' |
|
60 | gem 'rouge' |
|
61 |
|
61 | ||
|
62 |
- # |
|
62 | + #bootstrap add-ons |
|
63 | gem 'bootstrap-sass', '~> 3.2.0' |
|
63 | gem 'bootstrap-sass', '~> 3.2.0' |
|
64 | gem 'bootstrap-switch-rails' |
|
64 | gem 'bootstrap-switch-rails' |
|
65 | gem 'bootstrap-toggle-rails' |
|
65 | gem 'bootstrap-toggle-rails' |
|
66 | gem 'autoprefixer-rails' |
|
66 | gem 'autoprefixer-rails' |
|
67 | - |
|
||
|
68 | - #bootstrap sortable |
|
||
|
69 | gem 'momentjs-rails' |
|
67 | gem 'momentjs-rails' |
|
70 | gem 'rails_bootstrap_sortable' |
|
68 | gem 'rails_bootstrap_sortable' |
|
|
69 | + gem 'bootstrap-datepicker-rails' | ||
|
|
70 | + gem 'bootstrap3-datetimepicker-rails' | ||
|
|
71 | + gem 'jquery-datatables-rails' | ||
|
71 |
|
72 | ||
|
72 | #----------- user interface ----------------- |
|
73 | #----------- user interface ----------------- |
|
73 | #select 2 |
|
74 | #select 2 |
@@ -61,10 +61,14 | |||||
|
61 | best_in_place (3.0.3) |
|
61 | best_in_place (3.0.3) |
|
62 | actionpack (>= 3.2) |
|
62 | actionpack (>= 3.2) |
|
63 | railties (>= 3.2) |
|
63 | railties (>= 3.2) |
|
|
64 | + bootstrap-datepicker-rails (1.7.1.1) | ||
|
|
65 | + railties (>= 3.0) | ||
|
64 | bootstrap-sass (3.2.0.2) |
|
66 | bootstrap-sass (3.2.0.2) |
|
65 | sass (~> 3.2) |
|
67 | sass (~> 3.2) |
|
66 | bootstrap-switch-rails (3.3.3) |
|
68 | bootstrap-switch-rails (3.3.3) |
|
67 | bootstrap-toggle-rails (2.2.1.0) |
|
69 | bootstrap-toggle-rails (2.2.1.0) |
|
|
70 | + bootstrap3-datetimepicker-rails (4.17.47) | ||
|
|
71 | + momentjs-rails (>= 2.8.1) | ||
|
68 | builder (3.2.2) |
|
72 | builder (3.2.2) |
|
69 | coffee-rails (4.2.1) |
|
73 | coffee-rails (4.2.1) |
|
70 | coffee-script (>= 2.2.0) |
|
74 | coffee-script (>= 2.2.0) |
@@ -97,6 +101,11 | |||||
|
97 | i18n (0.7.0) |
|
101 | i18n (0.7.0) |
|
98 | in_place_editing (1.2.0) |
|
102 | in_place_editing (1.2.0) |
|
99 | jquery-countdown-rails (2.0.2) |
|
103 | jquery-countdown-rails (2.0.2) |
|
|
104 | + jquery-datatables-rails (3.4.0) | ||
|
|
105 | + actionpack (>= 3.1) | ||
|
|
106 | + jquery-rails | ||
|
|
107 | + railties (>= 3.1) | ||
|
|
108 | + sass-rails | ||
|
100 | jquery-rails (4.2.1) |
|
109 | jquery-rails (4.2.1) |
|
101 | rails-dom-testing (>= 1, < 3) |
|
110 | rails-dom-testing (>= 1, < 3) |
|
102 | railties (>= 4.2.0) |
|
111 | railties (>= 4.2.0) |
@@ -201,9 +210,11 | |||||
|
201 | activerecord-session_store |
|
210 | activerecord-session_store |
|
202 | autoprefixer-rails |
|
211 | autoprefixer-rails |
|
203 | best_in_place (~> 3.0.1) |
|
212 | best_in_place (~> 3.0.1) |
|
|
213 | + bootstrap-datepicker-rails | ||
|
204 | bootstrap-sass (~> 3.2.0) |
|
214 | bootstrap-sass (~> 3.2.0) |
|
205 | bootstrap-switch-rails |
|
215 | bootstrap-switch-rails |
|
206 | bootstrap-toggle-rails |
|
216 | bootstrap-toggle-rails |
|
|
217 | + bootstrap3-datetimepicker-rails | ||
|
207 | coffee-rails |
|
218 | coffee-rails |
|
208 | dynamic_form |
|
219 | dynamic_form |
|
209 | fuzzy-string-match |
|
220 | fuzzy-string-match |
@@ -211,6 +222,7 | |||||
|
211 | haml-rails |
|
222 | haml-rails |
|
212 | in_place_editing |
|
223 | in_place_editing |
|
213 | jquery-countdown-rails |
|
224 | jquery-countdown-rails |
|
|
225 | + jquery-datatables-rails | ||
|
214 | jquery-rails |
|
226 | jquery-rails |
|
215 | jquery-tablesorter |
|
227 | jquery-tablesorter |
|
216 | jquery-timepicker-addon-rails |
|
228 | jquery-timepicker-addon-rails |
@@ -232,4 +244,4 | |||||
|
232 | yaml_db |
|
244 | yaml_db |
|
233 |
|
245 | ||
|
234 | BUNDLED WITH |
|
246 | BUNDLED WITH |
|
235 |
- 1.1 |
|
247 | + 1.15.4 |
@@ -12,10 +12,14 | |||||
|
12 | // |
|
12 | // |
|
13 | //= require jquery |
|
13 | //= require jquery |
|
14 | //= require jquery_ujs |
|
14 | //= require jquery_ujs |
|
|
15 | + //= require dataTables/jquery.dataTables | ||
|
|
16 | + //= require dataTables/bootstrap/3/jquery.dataTables.bootstrap | ||
|
15 | //= require jquery-ui |
|
17 | //= require jquery-ui |
|
16 | //= require bootstrap-sprockets |
|
18 | //= require bootstrap-sprockets |
|
17 | //= require moment |
|
19 | //= require moment |
|
|
20 | + //= require moment/th | ||
|
18 | //= require bootstrap-sortable |
|
21 | //= require bootstrap-sortable |
|
|
22 | + //= require bootstrap-datetimepicker | ||
|
19 | //= require select2 |
|
23 | //= require select2 |
|
20 | //= require ace-rails-ap |
|
24 | //= require ace-rails-ap |
|
21 | //= require ace/mode-c_cpp |
|
25 | //= require ace/mode-c_cpp |
@@ -32,6 +36,8 | |||||
|
32 | //= require best_in_place |
|
36 | //= require best_in_place |
|
33 | //= require best_in_place.jquery-ui |
|
37 | //= require best_in_place.jquery-ui |
|
34 | //= require brython |
|
38 | //= require brython |
|
|
39 | + //= require bootstrap-datepicker | ||
|
|
40 | + //= require bootstrap-datetimepicker | ||
|
35 |
|
41 | ||
|
36 | // since this is after blank line, it is not downloaded |
|
42 | // since this is after blank line, it is not downloaded |
|
37 | //x= require prototype |
|
43 | //x= require prototype |
@@ -33,6 +33,9 | |||||
|
33 | //@import bootstrap3-switch |
|
33 | //@import bootstrap3-switch |
|
34 | @import "bootstrap-toggle"; |
|
34 | @import "bootstrap-toggle"; |
|
35 | @import "bootstrap-sortable"; |
|
35 | @import "bootstrap-sortable"; |
|
|
36 | + @import "bootstrap-datepicker3"; | ||
|
|
37 | + @import "bootstrap-datetimepicker"; | ||
|
|
38 | + @import "dataTables/bootstrap/3/jquery.dataTables.bootstrap"; | ||
|
36 |
|
39 | ||
|
37 | //bootstrap navbar color (from) |
|
40 | //bootstrap navbar color (from) |
|
38 | $bgDefault: #19197b; |
|
41 | $bgDefault: #19197b; |
@@ -546,3 +549,9 | |||||
|
546 | margin-top: 5px; |
|
549 | margin-top: 5px; |
|
547 | margin-bottom: 5px; |
|
550 | margin-bottom: 5px; |
|
548 | } |
|
551 | } |
|
|
552 | + | ||
|
|
553 | + | ||
|
|
554 | + | ||
|
|
555 | + .grader-comment { | ||
|
|
556 | + word-wrap: break-word; | ||
|
|
557 | + } |
@@ -39,13 +39,10 | |||||
|
39 |
|
39 | ||
|
40 | def testcase_authorization |
|
40 | def testcase_authorization |
|
41 | #admin always has privileged |
|
41 | #admin always has privileged |
|
42 | - puts "haha" |
|
||
|
43 | if @current_user.admin? |
|
42 | if @current_user.admin? |
|
44 | return true |
|
43 | return true |
|
45 | end |
|
44 | end |
|
46 |
|
45 | ||
|
47 | - puts "hehe" |
|
||
|
48 | - puts GraderConfiguration["right.view_testcase"] |
|
||
|
49 | unauthorized_redirect unless GraderConfiguration["right.view_testcase"] |
|
46 | unauthorized_redirect unless GraderConfiguration["right.view_testcase"] |
|
50 | end |
|
47 | end |
|
51 |
|
48 | ||
@@ -61,27 +58,28 | |||||
|
61 | return false |
|
58 | return false |
|
62 | end |
|
59 | end |
|
63 |
|
60 | ||
|
|
61 | + | ||
|
64 | # check if run in single user mode |
|
62 | # check if run in single user mode |
|
65 | if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY] |
|
63 | if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY] |
|
66 | - user = User.find_by_id(session[:user_id]) |
|
64 | + if @current_user==nil or (not @current_user.admin?) |
|
67 | - if user==nil or (not user.admin?) |
|
||
|
68 | flash[:notice] = 'You cannot log in at this time' |
|
65 | flash[:notice] = 'You cannot log in at this time' |
|
69 | redirect_to :controller => 'main', :action => 'login' |
|
66 | redirect_to :controller => 'main', :action => 'login' |
|
70 | return false |
|
67 | return false |
|
71 | end |
|
68 | end |
|
72 | - unless user.enabled? |
|
||
|
73 | - flash[:notice] = 'Your account is disabled' |
|
||
|
74 | - redirect_to :controller => 'main', :action => 'login' |
|
||
|
75 | - return false |
|
||
|
76 | - end |
|
||
|
77 | return true |
|
69 | return true |
|
78 | end |
|
70 | end |
|
79 |
|
71 | ||
|
|
72 | + # check if the user is enabled | ||
|
|
73 | + unless @current_user.enabled? or @current_user.admin? | ||
|
|
74 | + flash[:notice] = 'Your account is disabled' | ||
|
|
75 | + redirect_to :controller => 'main', :action => 'login' | ||
|
|
76 | + return false | ||
|
|
77 | + end | ||
|
|
78 | + | ||
|
80 | if GraderConfiguration.multicontests? |
|
79 | if GraderConfiguration.multicontests? |
|
81 | - user = User.find(session[:user_id]) |
|
80 | + return true if @current_user.admin? |
|
82 | - return true if user.admin? |
|
||
|
83 | begin |
|
81 | begin |
|
84 | - if user.contest_stat(true).forced_logout |
|
82 | + if @current_user.contest_stat(true).forced_logout |
|
85 | flash[:notice] = 'You have been automatically logged out.' |
|
83 | flash[:notice] = 'You have been automatically logged out.' |
|
86 | redirect_to :controller => 'main', :action => 'index' |
|
84 | redirect_to :controller => 'main', :action => 'index' |
|
87 | end |
|
85 | end |
@@ -1,22 +1,6 | |||||
|
1 | class GradersController < ApplicationController |
|
1 | class GradersController < ApplicationController |
|
2 |
|
2 | ||
|
3 |
- before_filter :admin_authorization |
|
3 | + before_filter :admin_authorization |
|
4 | - before_filter(only: [:submission]) { |
|
||
|
5 | - #check if authenticated |
|
||
|
6 | - return false unless authenticate |
|
||
|
7 | - |
|
||
|
8 | - #admin always has privileged |
|
||
|
9 | - if @current_user.admin? |
|
||
|
10 | - return true |
|
||
|
11 | - end |
|
||
|
12 | - |
|
||
|
13 | - if GraderConfiguration["right.user_view_submission"] and Submission.find(params[:id]).problem.available? |
|
||
|
14 | - return true |
|
||
|
15 | - else |
|
||
|
16 | - unauthorized_redirect |
|
||
|
17 | - return false |
|
||
|
18 | - end |
|
||
|
19 | - } |
|
||
|
20 |
|
4 | ||
|
21 | verify :method => :post, :only => ['clear_all', |
|
5 | verify :method => :post, :only => ['clear_all', |
|
22 | 'start_exam', |
|
6 | 'start_exam', |
@@ -77,25 +61,6 | |||||
|
77 | @task = Task.find(params[:id]) |
|
61 | @task = Task.find(params[:id]) |
|
78 | end |
|
62 | end |
|
79 |
|
63 | ||
|
80 | - def submission |
|
||
|
81 | - @submission = Submission.find(params[:id]) |
|
||
|
82 | - formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', line_numbers: true ) |
|
||
|
83 | - lexer = case @submission.language.name |
|
||
|
84 | - when "c" then Rouge::Lexers::C.new |
|
||
|
85 | - when "cpp" then Rouge::Lexers::Cpp.new |
|
||
|
86 | - when "pas" then Rouge::Lexers::Pas.new |
|
||
|
87 | - when "ruby" then Rouge::Lexers::Ruby.new |
|
||
|
88 | - when "python" then Rouge::Lexers::Python.new |
|
||
|
89 | - when "java" then Rouge::Lexers::Java.new |
|
||
|
90 | - when "php" then Rouge::Lexers::PHP.new |
|
||
|
91 | - end |
|
||
|
92 | - @formatted_code = formatter.format(lexer.lex(@submission.source)) |
|
||
|
93 | - @css_style = Rouge::Themes::ThankfulEyes.render(scope: '.highlight') |
|
||
|
94 | - |
|
||
|
95 | - user = User.find(session[:user_id]) |
|
||
|
96 | - SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin? |
|
||
|
97 | - |
|
||
|
98 | - end |
|
||
|
99 |
|
64 | ||
|
100 | # various grader controls |
|
65 | # various grader controls |
|
101 |
|
66 |
@@ -7,32 +7,38 | |||||
|
7 | end |
|
7 | end |
|
8 |
|
8 | ||
|
9 | def login |
|
9 | def login |
|
10 | - if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree]) |
|
10 | + user = User.authenticate(params[:login], params[:password]) |
|
|
11 | + unless user | ||
|
|
12 | + flash[:notice] = 'Wrong password' | ||
|
|
13 | + redirect_to :controller => 'main', :action => 'login' | ||
|
|
14 | + return | ||
|
|
15 | + end | ||
|
|
16 | + | ||
|
|
17 | + if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree]) and !user.admin? | ||
|
11 | flash[:notice] = 'You must accept the agreement before logging in' |
|
18 | flash[:notice] = 'You must accept the agreement before logging in' |
|
12 | redirect_to :controller => 'main', :action => 'login' |
|
19 | redirect_to :controller => 'main', :action => 'login' |
|
13 | - elsif user = User.authenticate(params[:login], params[:password]) |
|
20 | + return |
|
14 | - session[:user_id] = user.id |
|
21 | + end |
|
15 | - session[:admin] = user.admin? |
|
22 | + |
|
|
23 | + #process logging in | ||
|
|
24 | + session[:user_id] = user.id | ||
|
|
25 | + session[:admin] = user.admin? | ||
|
16 |
|
26 | ||
|
17 |
- |
|
27 | + # clear forced logout flag for multicontests contest change |
|
18 |
- |
|
28 | + if GraderConfiguration.multicontests? |
|
19 |
- |
|
29 | + contest_stat = user.contest_stat |
|
20 |
- |
|
30 | + if contest_stat.respond_to? :forced_logout |
|
21 |
- |
|
31 | + if contest_stat.forced_logout |
|
22 |
- |
|
32 | + contest_stat.forced_logout = false |
|
23 |
- |
|
33 | + contest_stat.save |
|
24 | - end |
|
||
|
25 | end |
|
34 | end |
|
26 | end |
|
35 | end |
|
27 | - |
|
36 | + end |
|
28 | - #save login information |
|
||
|
29 | - Login.create(user_id: user.id, ip_address: request.remote_ip) |
|
||
|
30 |
|
37 | ||
|
31 | - redirect_to :controller => 'main', :action => 'list' |
|
38 | + #save login information |
|
32 | - else |
|
39 | + Login.create(user_id: user.id, ip_address: request.remote_ip) |
|
33 | - flash[:notice] = 'Wrong password' |
|
40 | + |
|
34 |
- |
|
41 | + redirect_to :controller => 'main', :action => 'list' |
|
35 | - end |
|
||
|
36 | end |
|
42 | end |
|
37 |
|
43 | ||
|
38 | def site_login |
|
44 | def site_login |
@@ -86,7 +86,7 | |||||
|
86 | render :action => 'list' and return |
|
86 | render :action => 'list' and return |
|
87 | end |
|
87 | end |
|
88 |
|
88 | ||
|
89 | - if @submission.valid? |
|
89 | + if @submission.valid?(@current_user) |
|
90 | if @submission.save == false |
|
90 | if @submission.save == false |
|
91 | flash[:notice] = 'Error saving your submission' |
|
91 | flash[:notice] = 'Error saving your submission' |
|
92 | elsif Task.create(:submission_id => @submission.id, |
|
92 | elsif Task.create(:submission_id => @submission.id, |
@@ -97,7 +97,7 | |||||
|
97 | prepare_list_information |
|
97 | prepare_list_information |
|
98 | render :action => 'list' and return |
|
98 | render :action => 'list' and return |
|
99 | end |
|
99 | end |
|
100 | - redirect_to :action => 'list' |
|
100 | + redirect_to edit_submission_path(@submission) |
|
101 | end |
|
101 | end |
|
102 |
|
102 | ||
|
103 | def source |
|
103 | def source |
@@ -165,7 +165,7 | |||||
|
165 | redirect_to :controller => 'main', :action => 'list' |
|
165 | redirect_to :controller => 'main', :action => 'list' |
|
166 | return |
|
166 | return |
|
167 | end |
|
167 | end |
|
168 | - @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id) |
|
168 | + @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id) |
|
169 |
|
169 | ||
|
170 | #stat summary |
|
170 | #stat summary |
|
171 | range =65 |
|
171 | range =65 |
@@ -195,7 +195,26 | |||||
|
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 | + ok = [] | ||
|
|
201 | + failed = [] | ||
|
|
202 | + get_problems_from_params.each do |p| | ||
|
|
203 | + begin | ||
|
|
204 | + group.problems << p | ||
|
|
205 | + ok << p.full_name | ||
|
|
206 | + rescue => e | ||
|
|
207 | + failed << p.full_name | ||
|
|
208 | + end | ||
|
|
209 | + end | ||
|
|
210 | + flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0 | ||
|
|
211 | + flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0 | ||
|
|
212 | + elsif params.has_key? 'add_tags' | ||
|
|
213 | + get_problems_from_params.each do |p| | ||
|
|
214 | + p.tag_ids += params[:tag_ids] | ||
|
|
215 | + end | ||
|
198 | end |
|
216 | end |
|
|
217 | + | ||
|
199 | redirect_to :action => 'manage' |
|
218 | redirect_to :action => 'manage' |
|
200 | end |
|
219 | end |
|
201 |
|
220 | ||
@@ -243,10 +262,7 | |||||
|
243 |
|
262 | ||
|
244 | def change_date_added |
|
263 | def change_date_added |
|
245 | problems = get_problems_from_params |
|
264 | problems = get_problems_from_params |
|
246 |
- |
|
265 | + date = Date.parse(params[:date_added]) |
|
247 | - month = params[:date_added][:month].to_i |
|
||
|
248 | - day = params[:date_added][:day].to_i |
|
||
|
249 | - date = Date.new(year,month,day) |
|
||
|
250 | problems.each do |p| |
|
266 | problems.each do |p| |
|
251 | p.date_added = date |
|
267 | p.date_added = date |
|
252 | p.save |
|
268 | p.save |
@@ -288,7 +304,7 | |||||
|
288 | private |
|
304 | private |
|
289 |
|
305 | ||
|
290 | def problem_params |
|
306 | def problem_params |
|
291 | - params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description) |
|
307 | + params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[]) |
|
292 | end |
|
308 | end |
|
293 |
|
309 | ||
|
294 | end |
|
310 | end |
@@ -52,6 +52,8 | |||||
|
52 | #set up range from param |
|
52 | #set up range from param |
|
53 | @since_id = params.fetch(:from_id, 0).to_i |
|
53 | @since_id = params.fetch(:from_id, 0).to_i |
|
54 | @until_id = params.fetch(:to_id, 0).to_i |
|
54 | @until_id = params.fetch(:to_id, 0).to_i |
|
|
55 | + @since_id = nil if @since_id == 0 | ||
|
|
56 | + @until_id = nil if @until_id == 0 | ||
|
55 |
|
57 | ||
|
56 | #calculate the routine |
|
58 | #calculate the routine |
|
57 | @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id) |
|
59 | @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id) |
@@ -1,6 +1,6 | |||||
|
1 | class SubmissionsController < ApplicationController |
|
1 | class SubmissionsController < ApplicationController |
|
2 | before_action :authenticate |
|
2 | before_action :authenticate |
|
3 |
- before_action :submission_authorization, only: [:show, |
|
3 | + before_action :submission_authorization, only: [:show, :download, :edit] |
|
4 | before_action :admin_authorization, only: [:rejudge] |
|
4 | before_action :admin_authorization, only: [:rejudge] |
|
5 |
|
5 | ||
|
6 | # GET /submissions |
|
6 | # GET /submissions |
@@ -51,7 +51,15 | |||||
|
51 | #on-site new submission on specific problem |
|
51 | #on-site new submission on specific problem |
|
52 | def direct_edit_problem |
|
52 | def direct_edit_problem |
|
53 | @problem = Problem.find(params[:problem_id]) |
|
53 | @problem = Problem.find(params[:problem_id]) |
|
|
54 | + unless @current_user.can_view_problem?(@problem) | ||
|
|
55 | + unauthorized_redirect | ||
|
|
56 | + return | ||
|
|
57 | + end | ||
|
54 | @source = '' |
|
58 | @source = '' |
|
|
59 | + if (params[:view_latest]) | ||
|
|
60 | + sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id) | ||
|
|
61 | + @source = @submission.source.to_s if @submission and @submission.source | ||
|
|
62 | + end | ||
|
55 | render 'edit' |
|
63 | render 'edit' |
|
56 | end |
|
64 | end |
|
57 |
|
65 | ||
@@ -94,8 +102,7 | |||||
|
94 | end |
|
102 | end |
|
95 |
|
103 | ||
|
96 | sub = Submission.find(params[:id]) |
|
104 | sub = Submission.find(params[:id]) |
|
97 | - if sub.problem.available? |
|
105 | + if @current_user.available_problems.include? sub.problem |
|
98 | - puts "sub = #{sub.user.id}, current = #{@current_user.id}" |
|
||
|
99 | return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user |
|
106 | return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user |
|
100 | end |
|
107 | end |
|
101 |
|
108 |
@@ -26,7 +26,7 | |||||
|
26 | # this has problem-level access control |
|
26 | # this has problem-level access control |
|
27 | def download |
|
27 | def download |
|
28 | problem = Problem.find(params[:id]) |
|
28 | problem = Problem.find(params[:id]) |
|
29 |
- |
|
29 | + unless @current_user.can_view_problem? problem |
|
30 | redirect_to :action => 'index' and return |
|
30 | redirect_to :action => 'index' and return |
|
31 | end |
|
31 | end |
|
32 |
|
32 |
@@ -24,6 +24,7 | |||||
|
24 | @users = User.paginate :page => params[:page] |
|
24 | @users = User.paginate :page => params[:page] |
|
25 | @paginated = true |
|
25 | @paginated = true |
|
26 | end |
|
26 | end |
|
|
27 | + @users = User.all | ||
|
27 | @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at'] |
|
28 | @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at'] |
|
28 | @contests = Contest.enabled |
|
29 | @contests = Contest.enabled |
|
29 | end |
|
30 | end |
@@ -228,6 +229,7 | |||||
|
228 | end |
|
229 | end |
|
229 | end |
|
230 | end |
|
230 |
|
231 | ||
|
|
232 | + | ||
|
231 | # contest management |
|
233 | # contest management |
|
232 |
|
234 | ||
|
233 | def contests |
|
235 | def contests |
@@ -411,7 +413,7 | |||||
|
411 | def bulk_manage |
|
413 | def bulk_manage |
|
412 |
|
414 | ||
|
413 | begin |
|
415 | begin |
|
414 | - @users = User.where('login REGEXP ?',params[:regex]) if params[:regex] |
|
416 | + @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex] |
|
415 | @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised |
|
417 | @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised |
|
416 | rescue Exception |
|
418 | rescue Exception |
|
417 | flash[:error] = 'Regular Expression is malformed' |
|
419 | flash[:error] = 'Regular Expression is malformed' |
@@ -423,6 +425,8 | |||||
|
423 | @action[:set_enable] = params[:enabled] |
|
425 | @action[:set_enable] = params[:enabled] |
|
424 | @action[:enabled] = params[:enable] == "1" |
|
426 | @action[:enabled] = params[:enable] == "1" |
|
425 | @action[:gen_password] = params[:gen_password] |
|
427 | @action[:gen_password] = params[:gen_password] |
|
|
428 | + @action[:add_group] = params[:add_group] | ||
|
|
429 | + @action[:group_name] = params[:group_name] | ||
|
426 | end |
|
430 | end |
|
427 |
|
431 | ||
|
428 | if params[:commit] == "Perform" |
|
432 | if params[:commit] == "Perform" |
@@ -437,6 +441,21 | |||||
|
437 | u.save |
|
441 | u.save |
|
438 | end |
|
442 | end |
|
439 | end |
|
443 | end |
|
|
444 | + if @action[:add_group] and @action[:group_name] | ||
|
|
445 | + @group = Group.find(@action[:group_name]) | ||
|
|
446 | + ok = [] | ||
|
|
447 | + failed = [] | ||
|
|
448 | + @users.each do |user| | ||
|
|
449 | + begin | ||
|
|
450 | + @group.users << user | ||
|
|
451 | + ok << user.login | ||
|
|
452 | + rescue => e | ||
|
|
453 | + failed << user.login | ||
|
|
454 | + end | ||
|
|
455 | + end | ||
|
|
456 | + flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0 | ||
|
|
457 | + flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0 | ||
|
|
458 | + end | ||
|
440 | end |
|
459 | end |
|
441 | end |
|
460 | end |
|
442 |
|
461 |
@@ -85,11 +85,10 | |||||
|
85 | end |
|
85 | end |
|
86 |
|
86 | ||
|
87 | def format_short_time(time) |
|
87 | def format_short_time(time) |
|
88 |
- now = Time.now |
|
88 | + now = Time.zone.now |
|
89 | st = '' |
|
89 | st = '' |
|
90 | - if (time.yday != now.yday) or |
|
90 | + if (time.yday != now.yday) or (time.year != now.year) |
|
91 | - (time.year != now.year) |
|
91 | + st = time.strftime("%d/%m/%y ") |
|
92 | - st = time.strftime("%x ") |
|
||
|
93 | end |
|
92 | end |
|
94 | st + time.strftime("%X") |
|
93 | st + time.strftime("%X") |
|
95 | end |
|
94 | end |
@@ -100,6 +99,10 | |||||
|
100 | return Time.at(d).gmtime.strftime("%X") |
|
99 | return Time.at(d).gmtime.strftime("%X") |
|
101 | end |
|
100 | end |
|
102 |
|
101 | ||
|
|
102 | + def format_full_time_ago(time) | ||
|
|
103 | + st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')' | ||
|
|
104 | + end | ||
|
|
105 | + | ||
|
103 | def read_textfile(fname,max_size=2048) |
|
106 | def read_textfile(fname,max_size=2048) |
|
104 | begin |
|
107 | begin |
|
105 | File.open(fname).read(max_size) |
|
108 | File.open(fname).read(max_size) |
@@ -200,7 +203,7 | |||||
|
200 | BOOTSTRAP_FLASH_MSG = { |
|
203 | BOOTSTRAP_FLASH_MSG = { |
|
201 | success: 'alert-success', |
|
204 | success: 'alert-success', |
|
202 | error: 'alert-danger', |
|
205 | error: 'alert-danger', |
|
203 |
- alert: 'alert- |
|
206 | + alert: 'alert-danger', |
|
204 | notice: 'alert-info' |
|
207 | notice: 'alert-info' |
|
205 | } |
|
208 | } |
|
206 |
|
209 |
@@ -12,6 +12,7 | |||||
|
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 |
@@ -119,6 +120,10 | |||||
|
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] |
@@ -2,6 +2,14 | |||||
|
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 | + | ||
|
|
6 | + #has_and_belongs_to_many :groups | ||
|
|
7 | + has_many :groups_problems, class_name: GroupProblem | ||
|
|
8 | + has_many :groups, :through => :groups_problems | ||
|
|
9 | + | ||
|
|
10 | + has_many :problems_tags, class_name: ProblemTag | ||
|
|
11 | + has_many :tags, through: :problems_tags | ||
|
|
12 | + | ||
|
5 | has_many :test_pairs, :dependent => :delete_all |
|
13 | has_many :test_pairs, :dependent => :delete_all |
|
6 | has_many :testcases, :dependent => :destroy |
|
14 | has_many :testcases, :dependent => :destroy |
|
7 |
|
15 |
@@ -34,8 +34,8 | |||||
|
34 |
|
34 | ||
|
35 | def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id) |
|
35 | def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id) |
|
36 | records = Submission.where(problem_id: problem_id,user_id: user_id) |
|
36 | records = Submission.where(problem_id: problem_id,user_id: user_id) |
|
37 | - records = records.where('id >= ?',since_id) if since_id > 0 |
|
37 | + records = records.where('id >= ?',since_id) if since_id and since_id > 0 |
|
38 | - records = records.where('id <= ?',until_id) if until_id > 0 |
|
38 | + records = records.where('id <= ?',until_id) if until_id and until_id > 0 |
|
39 | records.all |
|
39 | records.all |
|
40 | end |
|
40 | end |
|
41 |
|
41 | ||
@@ -137,7 +137,7 | |||||
|
137 |
|
137 | ||
|
138 | # for output_only tasks |
|
138 | # for output_only tasks |
|
139 | return if self.problem!=nil and self.problem.output_only |
|
139 | return if self.problem!=nil and self.problem.output_only |
|
140 | - |
|
140 | + |
|
141 | if self.language==nil |
|
141 | if self.language==nil |
|
142 | errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil |
|
142 | errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil |
|
143 | end |
|
143 | end |
@@ -147,8 +147,12 | |||||
|
147 | return if self.source==nil |
|
147 | return if self.source==nil |
|
148 | if self.problem==nil |
|
148 | if self.problem==nil |
|
149 | errors.add('problem',"must be specified.") |
|
149 | errors.add('problem',"must be specified.") |
|
150 | - elsif (!self.problem.available) and (self.new_record?) |
|
150 | + else |
|
151 | - errors.add('problem',"must be valid.") |
|
151 | + #admin always have right |
|
|
152 | + return if self.user.admin? | ||
|
|
153 | + | ||
|
|
154 | + #check if user has the right to submit the problem | ||
|
|
155 | + errors.add('problem',"must be valid.") if (!self.user.available_problems.include?(self.problem)) and (self.new_record?) | ||
|
152 | end |
|
156 | end |
|
153 | end |
|
157 | end |
|
154 |
|
158 |
@@ -8,6 +8,10 | |||||
|
8 |
|
8 | ||
|
9 | has_and_belongs_to_many :roles |
|
9 | has_and_belongs_to_many :roles |
|
10 |
|
10 | ||
|
|
11 | + #has_and_belongs_to_many :groups | ||
|
|
12 | + has_many :groups_users, class_name: GroupUser | ||
|
|
13 | + has_many :groups, :through => :groups_users | ||
|
|
14 | + | ||
|
11 | has_many :test_requests, -> {order(submitted_at: DESC)} |
|
15 | has_many :test_requests, -> {order(submitted_at: DESC)} |
|
12 |
|
16 | ||
|
13 | has_many :messages, -> { order(created_at: DESC) }, |
|
17 | has_many :messages, -> { order(created_at: DESC) }, |
@@ -240,9 +244,14 | |||||
|
240 | return true |
|
244 | return true |
|
241 | end |
|
245 | end |
|
242 |
|
246 | ||
|
|
247 | + #get a list of available problem | ||
|
243 | def available_problems |
|
248 | def available_problems |
|
244 | if not GraderConfiguration.multicontests? |
|
249 | if not GraderConfiguration.multicontests? |
|
245 | - return Problem.available_problems |
|
250 | + if GraderConfiguration.use_problem_group? |
|
|
251 | + return available_problems_in_group | ||
|
|
252 | + else | ||
|
|
253 | + return Problem.available_problems | ||
|
|
254 | + end | ||
|
246 | else |
|
255 | else |
|
247 | contest_problems = [] |
|
256 | contest_problems = [] |
|
248 | pin = {} |
|
257 | pin = {} |
@@ -259,12 +268,32 | |||||
|
259 | end |
|
268 | end |
|
260 | end |
|
269 | end |
|
261 |
|
270 | ||
|
|
271 | + def available_problems_in_group | ||
|
|
272 | + problem = [] | ||
|
|
273 | + self.groups.each do |group| | ||
|
|
274 | + group.problems.where(available: true).each { |p| problem << p } | ||
|
|
275 | + end | ||
|
|
276 | + problem.uniq! | ||
|
|
277 | + if problem | ||
|
|
278 | + problem.sort! do |a,b| | ||
|
|
279 | + case | ||
|
|
280 | + when a.date_added < b.date_added | ||
|
|
281 | + 1 | ||
|
|
282 | + when a.date_added > b.date_added | ||
|
|
283 | + -1 | ||
|
|
284 | + else | ||
|
|
285 | + a.name <=> b.name | ||
|
|
286 | + end | ||
|
|
287 | + end | ||
|
|
288 | + return problem | ||
|
|
289 | + else | ||
|
|
290 | + return [] | ||
|
|
291 | + end | ||
|
|
292 | + end | ||
|
|
293 | + | ||
|
262 | def can_view_problem?(problem) |
|
294 | def can_view_problem?(problem) |
|
263 | - if not GraderConfiguration.multicontests? |
|
295 | + return true if admin? |
|
264 | - return problem.available |
|
296 | + return available_problems.include? problem |
|
265 | - else |
|
||
|
266 | - return problem_in_user_contests? problem |
|
||
|
267 | - end |
|
||
|
268 | end |
|
297 | end |
|
269 |
|
298 | ||
|
270 | def self.clear_last_login |
|
299 | def self.clear_last_login |
@@ -15,7 +15,7 | |||||
|
15 |
|
15 | ||
|
16 | <p> |
|
16 | <p> |
|
17 | <b>Body:</b> |
|
17 | <b>Body:</b> |
|
18 | - <%=h @announcement.body %> |
|
18 | + <%=h markdown(@announcement.body) %> |
|
19 | </p> |
|
19 | </p> |
|
20 |
|
20 | ||
|
21 | <p> |
|
21 | <p> |
@@ -1,13 +1,15 | |||||
|
1 | - |
|
||
|
2 |
|
|
1 | - if submission.nil? |
|
3 | = "-" |
|
2 | = "-" |
|
4 | - else |
|
3 | - else |
|
|
4 | + %strong= "Submission ID:" | ||
|
|
5 | + = submission.id | ||
|
|
6 | + %br | ||
|
5 | - unless submission.graded_at |
|
7 | - unless submission.graded_at |
|
6 | - = t 'main.submitted_at' |
|
8 | + %strong= t 'main.submitted_at:' |
|
7 |
- = format_ |
|
9 | + = format_full_time_ago(submission.submitted_at.localtime) |
|
8 | - else |
|
10 | - else |
|
9 | - %strong= t 'main.graded_at' |
|
11 | + %strong= t 'main.graded_at:' |
|
10 |
- = |
|
12 | + = format_full_time_ago(submission.graded_at.localtime) |
|
11 | %br |
|
13 | %br |
|
12 | - if GraderConfiguration['ui.show_score'] |
|
14 | - if GraderConfiguration['ui.show_score'] |
|
13 | %strong=t 'main.score' |
|
15 | %strong=t 'main.score' |
@@ -17,10 +19,10 | |||||
|
17 | = submission.grader_comment |
|
19 | = submission.grader_comment |
|
18 | = "]" |
|
20 | = "]" |
|
19 | %br |
|
21 | %br |
|
20 |
- |
|
22 | + %strong View: |
|
21 |
- |
|
23 | + - if GraderConfiguration.show_grading_result |
|
22 |
- |
|
24 | + = link_to '[detailed result]', :action => 'result', :id => submission.id |
|
23 |
- |
|
25 | + = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'} if submission.graded_at |
|
24 | = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info' |
|
26 | = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info' |
|
25 | = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' |
|
27 | = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' |
|
26 |
|
28 |
@@ -9,7 +9,7 | |||||
|
9 | %br/ |
|
9 | %br/ |
|
10 | = "Submission: #{@task.submission_id}" |
|
10 | = "Submission: #{@task.submission_id}" |
|
11 | - if @task.submission !=nil |
|
11 | - if @task.submission !=nil |
|
12 |
- = link_to '[view submission]', |
|
12 | + = link_to '[view submission]', submission_path( @task.submission.id ) |
|
13 | %br/ |
|
13 | %br/ |
|
14 | = "Submitted at: #{format_short_time(@task.created_at)}" |
|
14 | = "Submitted at: #{format_short_time(@task.created_at)}" |
|
15 | %br/ |
|
15 | %br/ |
@@ -46,7 +46,9 | |||||
|
46 | %ul.dropdown-menu |
|
46 | %ul.dropdown-menu |
|
47 | = add_menu( 'Announcements', 'announcements', 'index') |
|
47 | = add_menu( 'Announcements', 'announcements', 'index') |
|
48 | = add_menu( 'Problems', 'problems', 'index') |
|
48 | = add_menu( 'Problems', 'problems', 'index') |
|
|
49 | + = add_menu( 'Tags', 'tags', 'index') | ||
|
49 | = add_menu( 'Users', 'user_admin', 'index') |
|
50 | = add_menu( 'Users', 'user_admin', 'index') |
|
|
51 | + = add_menu( 'User Groups', 'groups', 'index') | ||
|
50 | = add_menu( 'Graders', 'graders', 'list') |
|
52 | = add_menu( 'Graders', 'graders', 'list') |
|
51 | = add_menu( 'Message ', 'messages', 'console') |
|
53 | = add_menu( 'Message ', 'messages', 'console') |
|
52 | %li.divider{role: 'separator'} |
|
54 | %li.divider{role: 'separator'} |
@@ -12,22 +12,26 | |||||
|
12 | %hr/ |
|
12 | %hr/ |
|
13 |
|
13 | ||
|
14 | %div{ :style => "border: solid 1px gray; padding: 4px; background: #eeeeff;"} |
|
14 | %div{ :style => "border: solid 1px gray; padding: 4px; background: #eeeeff;"} |
|
15 | - = form_tag login_login_path do |
|
15 | + = form_tag login_login_path, {class: 'form-horizontal'} do |
|
16 | - %table |
|
16 | + .form-group |
|
17 | - %tr |
|
17 | + =label_tag :login, "Login",class: 'col-sm-3 control-label' |
|
18 | - %td{:align => "right"} |
|
18 | + .col-sm-9 |
|
19 | - ="#{t 'login_label'}:" |
|
19 | + =text_field_tag :login, nil, class: 'form-control' |
|
20 | - %td= text_field_tag 'login' |
|
20 | + .form-group |
|
21 | - %tr |
|
21 | + =label_tag :password, "Password", class: 'col-sm-3 control-label' |
|
22 | - %td{:align => "right"} |
|
22 | + .col-sm-9 |
|
23 | - ="#{t 'password_label'}:" |
|
23 | + =password_field_tag :password, nil, class: 'form-control' |
|
24 | - %td= password_field_tag |
|
24 | + - unless GraderConfiguration['right.bypass_agreement'] |
|
25 | - - unless GraderConfiguration['right.bypass_agreement'] |
|
25 | + .form-group |
|
26 | - %tr |
|
26 | + .col-sm-offset-3.col-sm-9 |
|
27 | - %td{:align => "right"}= check_box_tag 'accept_agree' |
|
27 | + .checkbox |
|
28 | - %td ยอมรับข้อตกลงการใช้งาน |
|
28 | + %label |
|
29 | - |
|
29 | + = check_box_tag 'accept_agree' |
|
30 | - = submit_tag t('login.login_submit') |
|
30 | + ยอมรับข้อตกลงการใช้งาน |
|
|
31 | + | ||
|
|
32 | + .form-group | ||
|
|
33 | + .col-sm-offset-3.col-sm-9 | ||
|
|
34 | + = submit_tag t('login.login_submit'), class: 'btn btn-primary' | ||
|
31 | %br/ |
|
35 | %br/ |
|
32 |
|
36 | ||
|
33 | - if GraderConfiguration['system.online_registration'] |
|
37 | - if GraderConfiguration['system.online_registration'] |
@@ -11,7 +11,7 | |||||
|
11 | = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem |
|
11 | = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem |
|
12 | %td |
|
12 | %td |
|
13 | = @prob_submissions[problem.id][:count] |
|
13 | = @prob_submissions[problem.id][:count] |
|
14 | - = link_to "[subs]", main_submission_path(problem.id) |
|
14 | + -#= link_to "[subs]", main_submission_path(problem.id) |
|
15 | %td |
|
15 | %td |
|
16 | = render :partial => 'submission_short', |
|
16 | = render :partial => 'submission_short', |
|
17 | :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name, :problem_id => problem.id } |
|
17 | :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name, :problem_id => problem.id } |
@@ -13,7 +13,7 | |||||
|
13 | %strong=t 'main.score' |
|
13 | %strong=t 'main.score' |
|
14 | = "#{(submission.points*100/submission.problem.full_score).to_i} " |
|
14 | = "#{(submission.points*100/submission.problem.full_score).to_i} " |
|
15 | = " [" |
|
15 | = " [" |
|
16 | - %tt |
|
16 | + %tt.grader-comment |
|
17 | = submission.grader_comment |
|
17 | = submission.grader_comment |
|
18 | = "]" |
|
18 | = "]" |
|
19 | %br |
|
19 | %br |
@@ -1,12 +1,11 | |||||
|
1 | %h1= GraderConfiguration['ui.front.title'] |
|
1 | %h1= GraderConfiguration['ui.front.title'] |
|
2 |
|
2 | ||
|
3 | - %table |
|
3 | + .row |
|
4 | - %tr |
|
4 | + .col-md-6 |
|
5 | - %td |
|
5 | + - if @announcements.length!=0 |
|
6 | - - if @announcements.length!=0 |
|
6 | + .announcementbox{:style => 'margin-top: 0px'} |
|
7 | - .announcementbox{:style => 'margin-top: 0px'} |
|
7 | + %span{:class => 'title'} |
|
8 | - %span{:class => 'title'} |
|
8 | + Announcements |
|
9 | - Announcements |
|
9 | + = render :partial => 'announcement', :collection => @announcements |
|
10 | - = render :partial => 'announcement', :collection => @announcements |
|
10 | + .col-md-4{style: "padding-left: 20px;"} |
|
11 | - %td{:style => 'vertical-align: top; width: 40%; padding-left: 20px;'} |
|
11 | + = render :partial => 'login_box' |
|
12 | - = render :partial => 'login_box' |
|
@@ -12,6 +12,9 | |||||
|
12 | %label{:for => "problem_full_score"} Full score |
|
12 | %label{:for => "problem_full_score"} Full score |
|
13 | = text_field 'problem', 'full_score', class: 'form-control' |
|
13 | = text_field 'problem', 'full_score', class: 'form-control' |
|
14 | .form-group |
|
14 | .form-group |
|
|
15 | + %label{:for => "problem_full_score"} Tags | ||
|
|
16 | + = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'}) | ||
|
|
17 | + .form-group | ||
|
15 | %label{:for => "problem_date_added"} Date added |
|
18 | %label{:for => "problem_date_added"} Date added |
|
16 | = date_select 'problem', 'date_added', class: 'form-control' |
|
19 | = date_select 'problem', 'date_added', class: 'form-control' |
|
17 | - # TODO: these should be put in model Problem, but I can't think of |
|
20 | - # TODO: these should be put in model Problem, but I can't think of |
@@ -1,10 +1,10 | |||||
|
1 | - content_for :head do |
|
1 | - content_for :head do |
|
2 | = stylesheet_link_tag 'problems' |
|
2 | = stylesheet_link_tag 'problems' |
|
3 |
- %h1 |
|
3 | + %h1 Problems |
|
4 | %p |
|
4 | %p |
|
5 |
- = link_to ' |
|
5 | + = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm' |
|
6 |
- = link_to ' |
|
6 | + = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm' |
|
7 |
- = link_to ' |
|
7 | + = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm' |
|
8 | = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm' |
|
8 | = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm' |
|
9 | = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm' |
|
9 | = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm' |
|
10 | .submitbox |
|
10 | .submitbox |
@@ -21,6 +21,10 | |||||
|
21 | %th Name |
|
21 | %th Name |
|
22 | %th Full name |
|
22 | %th Full name |
|
23 | %th.text-right Full score |
|
23 | %th.text-right Full score |
|
|
24 | + %th Tags | ||
|
|
25 | + %th | ||
|
|
26 | + Submit | ||
|
|
27 | + %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?] | ||
|
24 | %th Date added |
|
28 | %th Date added |
|
25 | %th.text-center |
|
29 | %th.text-center |
|
26 | Avail? |
|
30 | Avail? |
@@ -37,8 +41,15 | |||||
|
37 | %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"} |
|
41 | %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"} |
|
38 | - @problem=problem |
|
42 | - @problem=problem |
|
39 | %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1 |
|
43 | %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1 |
|
40 | - %td= problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1 |
|
44 | + %td |
|
|
45 | + = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1 | ||
|
|
46 | + = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem | ||
|
41 | %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1 |
|
47 | %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1 |
|
|
48 | + %td | ||
|
|
49 | + - problem.tags.each do |t| | ||
|
|
50 | + - #%button.btn.btn-default.btn-xs= t.name | ||
|
|
51 | + %span.label.label-default= t.name | ||
|
|
52 | + %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary' | ||
|
42 | %td= problem.date_added |
|
53 | %td= problem.date_added |
|
43 | %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}") |
|
54 | %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}") |
|
44 | %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}") |
|
55 | %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}") |
@@ -36,50 +36,83 | |||||
|
36 |
|
36 | ||
|
37 | %h1 Manage problems |
|
37 | %h1 Manage problems |
|
38 |
|
38 | ||
|
39 |
- %p= link_to '[Back to problem list]', |
|
39 | + %p= link_to '[Back to problem list]', problems_path |
|
40 |
|
40 | ||
|
41 | = form_tag :action=>'do_manage' do |
|
41 | = form_tag :action=>'do_manage' do |
|
42 | - .submitbox |
|
42 | + .panel.panel-primary |
|
43 | - What do you want to do to the selected problem? |
|
43 | + .panel-heading |
|
44 | - %br/ |
|
44 | + Action |
|
45 | - (You can shift-click to select a range of problems) |
|
45 | + .panel-body |
|
46 | - %ul |
|
46 | + .submit-box |
|
47 | - %li |
|
47 | + What do you want to do to the selected problem? |
|
48 | - Change date added to |
|
48 | + %br/ |
|
49 | - = select_date Date.current, :prefix => 'date_added' |
|
49 | + (You can shift-click to select a range of problems) |
|
50 | - |
|
50 | + %ul.form-inline |
|
51 | - = submit_tag 'Change', :name => 'change_date_added' |
|
51 | + %li |
|
52 | - %li |
|
52 | + Change "Date added" to |
|
53 | - Set available to |
|
53 | + .input-group.date |
|
54 | - = submit_tag 'True', :name => 'enable_problem' |
|
54 | + = text_field_tag :date_added, class: 'form-control' |
|
55 | - = submit_tag 'False', :name => 'disable_problem' |
|
55 | + %span.input-group-addon |
|
|
56 | + %span.glyphicon.glyphicon-calendar | ||
|
|
57 | + -# = select_date Date.current, :prefix => 'date_added' | ||
|
|
58 | + | ||
|
|
59 | + = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-primary btn-sm' | ||
|
|
60 | + %li | ||
|
|
61 | + Set "Available" to | ||
|
|
62 | + = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-primary btn-sm' | ||
|
|
63 | + = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-primary btn-sm' | ||
|
56 |
|
64 | ||
|
57 | - - if GraderConfiguration.multicontests? |
|
65 | + - if GraderConfiguration.multicontests? |
|
58 | - %li |
|
66 | + %li |
|
59 | - Add to |
|
67 | + Add selected problems to contest |
|
60 | - = select("contest","id",Contest.all.collect {|c| [c.title, c.id]}) |
|
68 | + = select("contest","id",Contest.all.collect {|c| [c.title, c.id]}) |
|
61 | - = submit_tag 'Add', :name => 'add_to_contest' |
|
69 | + = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-primary btn-sm' |
|
|
70 | + %li | ||
|
|
71 | + Add selected problems to user group | ||
|
|
72 | + = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2' | ||
|
|
73 | + = submit_tag 'Add', name: 'add_group', class: 'btn btn-primary' | ||
|
|
74 | + %li | ||
|
|
75 | + Add the following tags to the selected problems | ||
|
|
76 | + = select_tag "tag_ids", options_from_collection_for_select( Tag.all, 'id','name'), id: 'tags_name',class: 'select2', multiple: true, data: {placeholder: 'Select tags by clicking', width: "200px"} | ||
|
|
77 | + = submit_tag 'Add', name: 'add_tags', class: 'btn btn-primary' | ||
|
62 |
|
78 | ||
|
63 | - %table |
|
79 | + %table.table.table-hover.datatable |
|
64 | - %tr{style: "text-align: left;"} |
|
80 | + %thead |
|
65 | - %th= check_box_tag 'select_all' |
|
81 | + %tr{style: "text-align: left;"} |
|
66 | - %th Name |
|
82 | + %th= check_box_tag 'select_all' |
|
67 |
- %th |
|
83 | + %th Name |
|
68 |
- %th |
|
84 | + %th Full name |
|
69 |
- %th |
|
85 | + %th Tags |
|
70 | - - if GraderConfiguration.multicontests? |
|
86 | + %th Available |
|
71 |
- %th |
|
87 | + %th Date added |
|
|
88 | + - if GraderConfiguration.multicontests? | ||
|
|
89 | + %th Contests | ||
|
72 |
|
90 | ||
|
73 | - - num = 0 |
|
91 | + %tbody |
|
74 | - - for problem in @problems |
|
92 | + - num = 0 |
|
75 | - - num += 1 |
|
93 | + - for problem in @problems |
|
76 | - %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"} |
|
94 | + - num += 1 |
|
77 | - %td= check_box_tag "prob-#{problem.id}-#{num}" |
|
95 | + %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"} |
|
78 | - %td= problem.name |
|
96 | + %td= check_box_tag "prob-#{problem.id}-#{num}" |
|
79 |
- %td= problem. |
|
97 | + %td= problem.name |
|
80 |
- %td= problem. |
|
98 | + %td= problem.full_name |
|
81 | - %td= problem.date_added |
|
||
|
82 | - - if GraderConfiguration.multicontests? |
|
||
|
83 | %td |
|
99 | %td |
|
84 |
- - problem. |
|
100 | + - problem.tags.each do |t| |
|
85 | - = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])" |
|
101 | + %span.label.label-default= t.name |
|
|
102 | + %td= problem.available | ||
|
|
103 | + %td= problem.date_added | ||
|
|
104 | + - if GraderConfiguration.multicontests? | ||
|
|
105 | + %td | ||
|
|
106 | + - problem.contests.each do |contest| | ||
|
|
107 | + = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])" | ||
|
|
108 | + | ||
|
|
109 | + :javascript | ||
|
|
110 | + $('.input-group.date').datetimepicker({ | ||
|
|
111 | + format: 'DD/MMM/YYYY', | ||
|
|
112 | + showTodayButton: true, | ||
|
|
113 | + widgetPositioning: {horizontal: 'auto', vertical: 'bottom'}, | ||
|
|
114 | + | ||
|
|
115 | + }); | ||
|
|
116 | + $('.datatable').DataTable({ | ||
|
|
117 | + paging: false | ||
|
|
118 | + }); |
@@ -25,13 +25,14 | |||||
|
25 |
|
25 | ||
|
26 | %h2 Submissions |
|
26 | %h2 Submissions |
|
27 | - if @submissions and @submissions.count > 0 |
|
27 | - if @submissions and @submissions.count > 0 |
|
28 | - %table.info#main_table |
|
28 | + %table#main_table.table.table-condensed.table-striped |
|
29 | %thead |
|
29 | %thead |
|
30 |
- %tr |
|
30 | + %tr |
|
31 | %th ID |
|
31 | %th ID |
|
32 | %th Login |
|
32 | %th Login |
|
33 | %th Name |
|
33 | %th Name |
|
34 | %th Submitted_at |
|
34 | %th Submitted_at |
|
|
35 | + %th language | ||
|
35 | %th Points |
|
36 | %th Points |
|
36 | %th comment |
|
37 | %th comment |
|
37 | %th IP |
|
38 | %th IP |
@@ -40,14 +41,19 | |||||
|
40 | - @submissions.each do |sub| |
|
41 | - @submissions.each do |sub| |
|
41 | - next unless sub.user |
|
42 | - next unless sub.user |
|
42 | - row_odd,curr = !row_odd, sub.user if curr != sub.user |
|
43 | - row_odd,curr = !row_odd, sub.user if curr != sub.user |
|
43 | - %tr{class: row_odd ? "info-odd" : "info-even"} |
|
44 | + %tr |
|
44 | %td= link_to sub.id, submission_path(sub) |
|
45 | %td= link_to sub.id, submission_path(sub) |
|
45 | %td= link_to sub.user.login, stat_user_path(sub.user) |
|
46 | %td= link_to sub.user.login, stat_user_path(sub.user) |
|
46 | %td= sub.user.full_name |
|
47 | %td= sub.user.full_name |
|
47 |
- %td |
|
48 | + %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago" |
|
|
49 | + %td= sub.language.name | ||
|
48 | %td= sub.points |
|
50 | %td= sub.points |
|
49 | %td.fix-width= sub.grader_comment |
|
51 | %td.fix-width= sub.grader_comment |
|
50 | %td= sub.ip_address |
|
52 | %td= sub.ip_address |
|
51 | - else |
|
53 | - else |
|
52 | No submission |
|
54 | No submission |
|
53 |
|
55 | ||
|
|
56 | + :javascript | ||
|
|
57 | + $("#main_table").DataTable({ | ||
|
|
58 | + paging: false | ||
|
|
59 | + }); |
@@ -12,6 +12,9 | |||||
|
12 | %th.text-right Total |
|
12 | %th.text-right Total |
|
13 | %th.text-right Passed |
|
13 | %th.text-right Passed |
|
14 | %tbody |
|
14 | %tbody |
|
|
15 | + - sum = Array.new(@scorearray[0].count,0) | ||
|
|
16 | + - nonzero = Array.new(@scorearray[0].count,0) | ||
|
|
17 | + - full = Array.new(@scorearray[0].count,0) | ||
|
15 | - @scorearray.each do |sc| |
|
18 | - @scorearray.each do |sc| |
|
16 | %tr |
|
19 | %tr |
|
17 | - total,num_passed = 0,0 |
|
20 | - total,num_passed = 0,0 |
@@ -27,8 +30,40 | |||||
|
27 | %td.text-right= sc[i][0] |
|
30 | %td.text-right= sc[i][0] |
|
28 | - total += sc[i][0] |
|
31 | - total += sc[i][0] |
|
29 | - num_passed += 1 if sc[i][1] |
|
32 | - num_passed += 1 if sc[i][1] |
|
|
33 | + - sum[i] += sc[i][0] | ||
|
|
34 | + - nonzero[i] += 1 if sc[i][0] > 0 | ||
|
|
35 | + - full[i] += 1 if sc[i][1] | ||
|
30 | %td.text-right= total |
|
36 | %td.text-right= total |
|
31 | %td.text-right= num_passed |
|
37 | %td.text-right= num_passed |
|
|
38 | + %tfoot | ||
|
|
39 | + %tr | ||
|
|
40 | + %td Summation | ||
|
|
41 | + %td | ||
|
|
42 | + %td | ||
|
|
43 | + - sum.each.with_index do |s,i| | ||
|
|
44 | + - next if i == 0 | ||
|
|
45 | + %td.text-right= number_with_delimiter(s) | ||
|
|
46 | + %td | ||
|
|
47 | + %td | ||
|
|
48 | + %tr | ||
|
|
49 | + %td partial solver | ||
|
|
50 | + %td | ||
|
|
51 | + %td | ||
|
|
52 | + - nonzero.each.with_index do |s,i| | ||
|
|
53 | + - next if i == 0 | ||
|
|
54 | + %td.text-right= number_with_delimiter(s) | ||
|
|
55 | + %td | ||
|
|
56 | + %td | ||
|
|
57 | + %tr | ||
|
|
58 | + %td Full solver | ||
|
|
59 | + %td | ||
|
|
60 | + %td | ||
|
|
61 | + - full.each.with_index do |s,i| | ||
|
|
62 | + - next if i == 0 | ||
|
|
63 | + %td.text-right= number_with_delimiter(s) | ||
|
|
64 | + %td | ||
|
|
65 | + %td | ||
|
|
66 | + | ||
|
32 |
|
67 | ||
|
33 | :javascript |
|
68 | :javascript |
|
34 | $.bootstrapSortable(true,'reversed') |
|
69 | $.bootstrapSortable(true,'reversed') |
@@ -33,11 +33,11 | |||||
|
33 | .panel-body |
|
33 | .panel-body |
|
34 | .radio |
|
34 | .radio |
|
35 | %label |
|
35 | %label |
|
36 |
- = radio_button_tag 'users', 'all', |
|
36 | + = radio_button_tag 'users', 'all', (params[:users] == "all") |
|
37 | All users |
|
37 | All users |
|
38 | .radio |
|
38 | .radio |
|
39 | %label |
|
39 | %label |
|
40 | - = radio_button_tag 'users', 'enabled' |
|
40 | + = radio_button_tag 'users', 'enabled', (params[:users] == "enabled") |
|
41 | Only enabled users |
|
41 | Only enabled users |
|
42 | .row |
|
42 | .row |
|
43 | .col-md-12 |
|
43 | .col-md-12 |
@@ -1,24 +1,36 | |||||
|
1 | %h1 Editing site |
|
1 | %h1 Editing site |
|
2 | = error_messages_for :site |
|
2 | = error_messages_for :site |
|
3 | = form_for(@site) do |f| |
|
3 | = form_for(@site) do |f| |
|
4 | - %p |
|
4 | + .row |
|
5 | - %b Name |
|
5 | + .col-md-4 |
|
6 | - %br/ |
|
6 | + .form-group.field |
|
7 | - = f.text_field :name |
|
7 | + = f.label :name, "Name" |
|
8 | - %p |
|
8 | + = f.text_field :name, class: 'form-control' |
|
9 | - %b Password |
|
9 | + .form-group.field |
|
10 | - %br/ |
|
10 | + = f.label :password, "Password" |
|
11 | - = f.text_field :password |
|
11 | + = f.text_field :password, class: 'form-control' |
|
12 | - %p |
|
12 | + .form-group.field |
|
13 | - %b Started |
|
13 | + = f.label :started, "Started" |
|
14 | - %br/ |
|
14 | + = f.check_box :started, class: 'form-control' |
|
15 | - = f.check_box :started |
|
15 | + .form-group.field |
|
16 | - %p |
|
16 | + = f.label :start_time, "Start time" |
|
17 | - %b Start time |
|
17 | + -# = f.datetime_select :start_time, :include_blank => true |
|
18 | - %br/ |
|
18 | + .input-group.date |
|
19 | - = f.datetime_select :start_time, :include_blank => true |
|
19 | + = f.text_field :start_time, class:'form-control' , value: (@site.start_time ? @site.start_time.strftime('%d/%b/%Y %H:%M') : '') |
|
20 | - %p |
|
20 | + %span.input-group-addon |
|
21 | - = f.submit "Update" |
|
21 | + %span.glyphicon.glyphicon-calendar |
|
|
22 | + .actions | ||
|
|
23 | + = f.submit "Update", class: 'btn btn-primary' | ||
|
|
24 | + .col-md-8 | ||
|
|
25 | + | ||
|
22 | = link_to 'Show', @site |
|
26 | = link_to 'Show', @site |
|
23 | | |
|
27 | | |
|
24 | = link_to 'Back', sites_path |
|
28 | = link_to 'Back', sites_path |
|
|
29 | + | ||
|
|
30 | + | ||
|
|
31 | + :javascript | ||
|
|
32 | + $('.input-group.date').datetimepicker({ | ||
|
|
33 | + format: 'DD/MMM/YYYY HH:mm', | ||
|
|
34 | + showTodayButton: true, | ||
|
|
35 | + }); | ||
|
|
36 | + |
@@ -11,6 +11,7 | |||||
|
11 | .col-md-8 |
|
11 | .col-md-8 |
|
12 | %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'} |
|
12 | %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'} |
|
13 | .col-md-4 |
|
13 | .col-md-4 |
|
|
14 | + - # submission form | ||
|
14 | = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do |
|
15 | = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do |
|
15 |
|
16 | ||
|
16 | = hidden_field_tag 'editor_text', @source |
|
17 | = hidden_field_tag 'editor_text', @source |
@@ -25,14 +26,16 | |||||
|
25 | .form-group |
|
26 | .form-group |
|
26 | = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit', |
|
27 | = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit', |
|
27 | data: {confirm: "Submitting this source code for task #{@problem.long_name}?"} |
|
28 | data: {confirm: "Submitting this source code for task #{@problem.long_name}?"} |
|
28 | - .panel.panel-info |
|
29 | + - # latest submission status |
|
|
30 | + .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"} | ||
|
29 | .panel-heading |
|
31 | .panel-heading |
|
30 | Latest Submission Status |
|
32 | Latest Submission Status |
|
31 | = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission |
|
33 | = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission |
|
32 | .panel-body |
|
34 | .panel-body |
|
33 | - - if @submission |
|
35 | + %div#latest_status |
|
34 |
- |
|
36 | + - if @submission |
|
35 | - :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id } |
|
37 | + = render :partial => 'submission_short', |
|
|
38 | + :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id } | ||
|
36 | .row |
|
39 | .row |
|
37 | .col-md-12 |
|
40 | .col-md-12 |
|
38 | %h2 Console |
|
41 | %h2 Console |
@@ -1,2 +1,2 | |||||
|
1 | :plain |
|
1 | :plain |
|
2 | - $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}") |
|
2 | + $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name, problem_id: @problem.id}})}") |
@@ -46,6 +46,15 | |||||
|
46 | %label.checkbox-inline |
|
46 | %label.checkbox-inline |
|
47 | = check_box_tag "gen_password", true, params[:gen_password] |
|
47 | = check_box_tag "gen_password", true, params[:gen_password] |
|
48 | Generate new random password |
|
48 | Generate new random password |
|
|
49 | + .row.form-group | ||
|
|
50 | + .col-md-4 | ||
|
|
51 | + %label.checkbox-inline | ||
|
|
52 | + = check_box_tag "add_group", true, params[:add_group] | ||
|
|
53 | + Add users to group | ||
|
|
54 | + %label.col-md-3.control-label.text-right Group name | ||
|
|
55 | + .col-md-5 | ||
|
|
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 | + | ||
|
49 |
|
58 | ||
|
50 | .row |
|
59 | .row |
|
51 | .col-md-12 |
|
60 | .col-md-12 |
@@ -1,9 +1,11 | |||||
|
1 | %h1 Editing user |
|
1 | %h1 Editing user |
|
2 |
|
2 | ||
|
3 | - = form_tag :action => 'update', :id => @user do |
|
3 | + = form_tag( {:action => 'update', :id => @user}, {class: 'form-horizontal'}) do |
|
4 | = error_messages_for 'user' |
|
4 | = error_messages_for 'user' |
|
5 | = render partial: "form" |
|
5 | = render partial: "form" |
|
6 | - = submit_tag "Edit" |
|
6 | + .form-group |
|
|
7 | + .col-md-offset-2.col-md-4 | ||
|
|
8 | + = submit_tag "Edit", class: 'btn btn-primary' | ||
|
7 |
|
9 | ||
|
8 |
|
10 | ||
|
9 | = link_to 'Show', :action => 'show', :id => @user |
|
11 | = link_to 'Show', :action => 'show', :id => @user |
@@ -1,4 +1,4 | |||||
|
1 |
- %h1 |
|
1 | + %h1 Users |
|
2 |
|
2 | ||
|
3 | .panel.panel-primary |
|
3 | .panel.panel-primary |
|
4 | .panel-title.panel-heading |
|
4 | .panel-title.panel-heading |
@@ -41,8 +41,8 | |||||
|
41 | %p |
|
41 | %p |
|
42 | = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '} |
|
42 | = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '} |
|
43 | = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '} |
|
43 | = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '} |
|
|
44 | + = link_to 'Bulk Manage', bulk_manage_user_admin_path , { class: 'btn btn-default btn-info'} | ||
|
44 | = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '} |
|
45 | = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '} |
|
45 | - = link_to 'Bulk Manage', bulk_manage_user_admin_path , { class: 'btn btn-default '} |
|
||
|
46 | = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '} |
|
46 | = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '} |
|
47 | = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '} |
|
47 | = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '} |
|
48 | = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '} |
|
48 | = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '} |
@@ -56,17 +56,17 | |||||
|
56 | = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id |
|
56 | = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id |
|
57 | = link_to "[no contest]", :action => 'contests', :id => 'none' |
|
57 | = link_to "[no contest]", :action => 'contests', :id => 'none' |
|
58 |
|
58 | ||
|
59 | - Total #{@user_count} users | |
|
59 | + -# Total #{@user_count} users | |
|
60 | - - if !@paginated |
|
60 | + -# - if !@paginated |
|
61 |
- |
|
61 | + -# Display all users. |
|
62 |
- |
|
62 | + -# \#{link_to '[show in pages]', :action => 'index', :page => '1'} |
|
63 | - - else |
|
63 | + -# - else |
|
64 |
- |
|
64 | + -# Display in pages. |
|
65 |
- |
|
65 | + -# \#{link_to '[display all]', :action => 'index', :page => 'all'} | |
|
66 |
- |
|
66 | + -# \#{will_paginate @users, :container => false} |
|
67 |
|
67 | ||
|
68 |
|
68 | ||
|
69 | - %table.table.table-hover.table-condense |
|
69 | + %table.table.table-hover.table-condense.datatable |
|
70 | %thead |
|
70 | %thead |
|
71 | %th Login |
|
71 | %th Login |
|
72 | %th Full name |
|
72 | %th Full name |
@@ -95,7 +95,12 | |||||
|
95 | %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block' |
|
95 | %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block' |
|
96 | %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block' |
|
96 | %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block' |
|
97 | %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block' |
|
97 | %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block' |
|
98 |
- %td= link_to 'Destroy', |
|
98 | + %td= link_to 'Destroy', user_admin_destroy_path(user), data: {confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-danger btn-xs btn-block' |
|
99 | %br/ |
|
99 | %br/ |
|
100 | = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '} |
|
100 | = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '} |
|
101 | = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '} |
|
101 | = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '} |
|
|
102 | + | ||
|
|
103 | + :javascript | ||
|
|
104 | + $('.datatable').DataTable({ | ||
|
|
105 | + 'pageLength': 50 | ||
|
|
106 | + }); |
@@ -36,7 +36,7 | |||||
|
36 | =render partial: 'application/bar_graph', locals: {histogram: @histogram, param: {bar_width: 7}} |
|
36 | =render partial: 'application/bar_graph', locals: {histogram: @histogram, param: {bar_width: 7}} |
|
37 |
|
37 | ||
|
38 |
|
38 | ||
|
39 | - %table.tablesorter-cafe#submission_table |
|
39 | + %table#submission_table.table.table-striped |
|
40 | %thead |
|
40 | %thead |
|
41 | %tr |
|
41 | %tr |
|
42 | %th ID |
|
42 | %th ID |
@@ -64,3 +64,7 | |||||
|
64 |
|
64 | ||
|
65 |
|
65 | ||
|
66 |
|
66 | ||
|
|
67 | + :javascript | ||
|
|
68 | + $("#submission_table").DataTable({ | ||
|
|
69 | + paging: false | ||
|
|
70 | + }); |
@@ -65,7 +65,7 | |||||
|
65 | config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
65 | config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
66 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
66 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
67 | login main messages problems report site sites sources tasks |
|
67 | login main messages problems report site sites sources tasks |
|
68 | - test user_admin users ).each do |controller| |
|
68 | + test user_admin users testcases).each do |controller| |
|
69 | config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
69 | config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
70 | end |
|
70 | end |
|
71 | end |
|
71 | end |
@@ -1,4 +1,5 | |||||
|
1 | CafeGrader::Application.routes.draw do |
|
1 | CafeGrader::Application.routes.draw do |
|
|
2 | + resources :tags | ||
|
2 | get "sources/direct_edit" |
|
3 | get "sources/direct_edit" |
|
3 |
|
4 | ||
|
4 | root :to => 'main#login' |
|
5 | root :to => 'main#login' |
@@ -29,7 +30,20 | |||||
|
29 | get 'import' |
|
30 | get 'import' |
|
30 | get 'manage' |
|
31 | get 'manage' |
|
31 | end |
|
32 | end |
|
|
33 | + end | ||
|
32 |
|
34 | ||
|
|
35 | + resources :groups do | ||
|
|
36 | + member do | ||
|
|
37 | + post 'add_user', to: 'groups#add_user', as: 'add_user' | ||
|
|
38 | + delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user' | ||
|
|
39 | + delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user' | ||
|
|
40 | + post 'add_problem', to: 'groups#add_problem', as: 'add_problem' | ||
|
|
41 | + delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem' | ||
|
|
42 | + delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem' | ||
|
|
43 | + end | ||
|
|
44 | + collection do | ||
|
|
45 | + | ||
|
|
46 | + end | ||
|
33 | end |
|
47 | end |
|
34 |
|
48 | ||
|
35 | resources :testcases, only: [] do |
|
49 | resources :testcases, only: [] do |
@@ -59,7 +73,7 | |||||
|
59 | end |
|
73 | end |
|
60 | collection do |
|
74 | collection do |
|
61 | get 'prob/:problem_id', to: 'submissions#index', as: 'problem' |
|
75 | get 'prob/:problem_id', to: 'submissions#index', as: 'problem' |
|
62 | - get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem' |
|
76 | + get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem' |
|
63 | get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status' |
|
77 | get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status' |
|
64 | end |
|
78 | end |
|
65 | end |
|
79 | end |
@@ -72,6 +86,8 | |||||
|
72 |
|
86 | ||
|
73 | #user admin |
|
87 | #user admin |
|
74 | get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin' |
|
88 | get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin' |
|
|
89 | + post 'user_admin', to: 'user_admin#create' | ||
|
|
90 | + delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy' | ||
|
75 |
|
91 | ||
|
76 | #report |
|
92 | #report |
|
77 | get 'report/current_score', to: 'report#current_score', as: 'report_current_score' |
|
93 | get 'report/current_score', to: 'report#current_score', as: 'report_current_score' |
@@ -11,7 +11,7 | |||||
|
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: 20170 |
|
14 | + ActiveRecord::Schema.define(version: 20170914150742) 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 |
@@ -79,6 +79,25 | |||||
|
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 |
@@ -127,6 +146,15 | |||||
|
127 | t.boolean "view_testcase" |
|
146 | t.boolean "view_testcase" |
|
128 | end |
|
147 | end |
|
129 |
|
148 | ||
|
|
149 | + create_table "problems_tags", force: :cascade do |t| | ||
|
|
150 | + t.integer "problem_id", limit: 4 | ||
|
|
151 | + t.integer "tag_id", limit: 4 | ||
|
|
152 | + end | ||
|
|
153 | + | ||
|
|
154 | + add_index "problems_tags", ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true, using: :btree | ||
|
|
155 | + add_index "problems_tags", ["problem_id"], name: "index_problems_tags_on_problem_id", using: :btree | ||
|
|
156 | + add_index "problems_tags", ["tag_id"], name: "index_problems_tags_on_tag_id", using: :btree | ||
|
|
157 | + | ||
|
130 | create_table "rights", force: :cascade do |t| |
|
158 | create_table "rights", force: :cascade do |t| |
|
131 | t.string "name", limit: 255 |
|
159 | t.string "name", limit: 255 |
|
132 | t.string "controller", limit: 255 |
|
160 | t.string "controller", limit: 255 |
@@ -200,6 +228,14 | |||||
|
200 | add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true, using: :btree |
|
228 | add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true, using: :btree |
|
201 | add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id", using: :btree |
|
229 | add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id", using: :btree |
|
202 |
|
230 | ||
|
|
231 | + create_table "tags", force: :cascade do |t| | ||
|
|
232 | + t.string "name", limit: 255, null: false | ||
|
|
233 | + t.text "description", limit: 65535 | ||
|
|
234 | + t.boolean "public" | ||
|
|
235 | + t.datetime "created_at", null: false | ||
|
|
236 | + t.datetime "updated_at", null: false | ||
|
|
237 | + end | ||
|
|
238 | + | ||
|
203 | create_table "tasks", force: :cascade do |t| |
|
239 | create_table "tasks", force: :cascade do |t| |
|
204 | t.integer "submission_id", limit: 4 |
|
240 | t.integer "submission_id", limit: 4 |
|
205 | t.datetime "created_at" |
|
241 | t.datetime "created_at" |
@@ -280,4 +316,6 | |||||
|
280 |
|
316 | ||
|
281 | add_index "users", ["login"], name: "index_users_on_login", unique: true, using: :btree |
|
317 | add_index "users", ["login"], name: "index_users_on_login", unique: true, using: :btree |
|
282 |
|
318 | ||
|
|
319 | + add_foreign_key "problems_tags", "problems" | ||
|
|
320 | + add_foreign_key "problems_tags", "tags" | ||
|
283 | end |
|
321 | end |
@@ -163,7 +163,16 | |||||
|
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 |
@@ -33,6 +33,7 | |||||
|
33 | GraderScript.call_grader "#{env} test_request -err-log &" |
|
33 | GraderScript.call_grader "#{env} test_request -err-log &" |
|
34 | end |
|
34 | end |
|
35 |
|
35 | ||
|
|
36 | + #call the import problem script | ||
|
36 | def self.call_import_problem(problem_name, |
|
37 | def self.call_import_problem(problem_name, |
|
37 | problem_dir, |
|
38 | problem_dir, |
|
38 | time_limit=1, |
|
39 | time_limit=1, |
@@ -8,8 +8,9 | |||||
|
8 | @problem = problem |
|
8 | @problem = problem |
|
9 | end |
|
9 | end |
|
10 |
|
10 | ||
|
11 | - def import_from_file(tempfile, |
|
11 | + #Create or update problem according to the parameter |
|
12 | - time_limit, |
|
12 | + def import_from_file(tempfile, |
|
|
13 | + time_limit, | ||
|
13 | memory_limit, |
|
14 | memory_limit, |
|
14 | checker_name='text', |
|
15 | checker_name='text', |
|
15 | import_to_db=false) |
|
16 | import_to_db=false) |
@@ -52,6 +53,7 | |||||
|
52 | return filename.slice(i..len) |
|
53 | return filename.slice(i..len) |
|
53 | end |
|
54 | end |
|
54 |
|
55 | ||
|
|
56 | + # extract an archive file located at +tempfile+ to the +raw_dir+ | ||
|
55 | def extract(tempfile) |
|
57 | def extract(tempfile) |
|
56 | testdata_filename = save_testdata_file(tempfile) |
|
58 | testdata_filename = save_testdata_file(tempfile) |
|
57 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
59 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
You need to be logged in to leave comments.
Login now