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 |
@@ -14,78 +14,79 | |||||
|
14 | #for testing |
|
14 | #for testing |
|
15 | gem 'sqlite3' |
|
15 | gem 'sqlite3' |
|
16 | #for dumping database into yaml |
|
16 | #for dumping database into yaml |
|
17 | gem 'yaml_db' |
|
17 | gem 'yaml_db' |
|
18 |
|
18 | ||
|
19 | # Gems used only for assets and not required |
|
19 | # Gems used only for assets and not required |
|
20 | # in production environments by default. |
|
20 | # in production environments by default. |
|
21 | gem 'sass-rails' |
|
21 | gem 'sass-rails' |
|
22 | gem 'coffee-rails' |
|
22 | gem 'coffee-rails' |
|
23 |
|
23 | ||
|
24 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes |
|
24 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes |
|
25 | # gem 'therubyracer', :platforms => :ruby |
|
25 | # gem 'therubyracer', :platforms => :ruby |
|
26 |
|
26 | ||
|
27 | gem 'uglifier' |
|
27 | gem 'uglifier' |
|
28 |
|
28 | ||
|
29 | gem 'haml' |
|
29 | gem 'haml' |
|
30 | gem 'haml-rails' |
|
30 | gem 'haml-rails' |
|
31 | # gem 'prototype-rails' |
|
31 | # gem 'prototype-rails' |
|
32 |
|
32 | ||
|
33 | # To use ActiveModel has_secure_password |
|
33 | # To use ActiveModel has_secure_password |
|
34 | # gem 'bcrypt-ruby', '~> 3.0.0' |
|
34 | # gem 'bcrypt-ruby', '~> 3.0.0' |
|
35 |
|
35 | ||
|
36 | # To use Jbuilder templates for JSON |
|
36 | # To use Jbuilder templates for JSON |
|
37 | # gem 'jbuilder' |
|
37 | # gem 'jbuilder' |
|
38 |
|
38 | ||
|
39 | # Use unicorn as the app server |
|
39 | # Use unicorn as the app server |
|
40 | # gem 'unicorn' |
|
40 | # gem 'unicorn' |
|
41 |
|
41 | ||
|
42 | # Deploy with Capistrano |
|
42 | # Deploy with Capistrano |
|
43 | # gem 'capistrano' |
|
43 | # gem 'capistrano' |
|
44 |
|
44 | ||
|
45 | # To use debugger |
|
45 | # To use debugger |
|
46 | # gem 'debugger' |
|
46 | # gem 'debugger' |
|
47 | # |
|
47 | # |
|
48 |
|
48 | ||
|
49 | #in-place editor |
|
49 | #in-place editor |
|
50 | gem 'best_in_place', '~> 3.0.1' |
|
50 | gem 'best_in_place', '~> 3.0.1' |
|
51 |
|
51 | ||
|
52 | # jquery addition |
|
52 | # jquery addition |
|
53 | gem 'jquery-rails' |
|
53 | gem 'jquery-rails' |
|
54 | gem 'jquery-ui-rails' |
|
54 | gem 'jquery-ui-rails' |
|
55 | gem 'jquery-timepicker-addon-rails' |
|
55 | gem 'jquery-timepicker-addon-rails' |
|
56 | gem 'jquery-tablesorter' |
|
56 | gem 'jquery-tablesorter' |
|
57 | gem 'jquery-countdown-rails' |
|
57 | gem 'jquery-countdown-rails' |
|
58 |
|
58 | ||
|
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 |
|
74 | gem 'select2-rails' |
|
75 | gem 'select2-rails' |
|
75 | #ace editor |
|
76 | #ace editor |
|
76 | gem 'ace-rails-ap' |
|
77 | gem 'ace-rails-ap' |
|
77 | #paginator |
|
78 | #paginator |
|
78 | gem 'will_paginate', '~> 3.0.7' |
|
79 | gem 'will_paginate', '~> 3.0.7' |
|
79 |
|
80 | ||
|
80 | gem 'mail' |
|
81 | gem 'mail' |
|
81 | gem 'rdiscount' |
|
82 | gem 'rdiscount' |
|
82 | gem 'dynamic_form' |
|
83 | gem 'dynamic_form' |
|
83 | gem 'in_place_editing' |
|
84 | gem 'in_place_editing' |
|
84 | gem 'verification', :git => 'https://github.com/sikachu/verification.git' |
|
85 | gem 'verification', :git => 'https://github.com/sikachu/verification.git' |
|
85 |
|
86 | ||
|
86 |
|
87 | ||
|
87 | #---------------- testiing ----------------------- |
|
88 | #---------------- testiing ----------------------- |
|
88 | gem 'minitest-reporters' |
|
89 | gem 'minitest-reporters' |
|
89 |
|
90 | ||
|
90 | #---------------- for console -------------------- |
|
91 | #---------------- for console -------------------- |
|
91 | gem 'fuzzy-string-match' |
|
92 | gem 'fuzzy-string-match' |
@@ -16,132 +16,141 | |||||
|
16 | actionmailer (4.2.7.1) |
|
16 | actionmailer (4.2.7.1) |
|
17 | actionpack (= 4.2.7.1) |
|
17 | actionpack (= 4.2.7.1) |
|
18 | actionview (= 4.2.7.1) |
|
18 | actionview (= 4.2.7.1) |
|
19 | activejob (= 4.2.7.1) |
|
19 | activejob (= 4.2.7.1) |
|
20 | mail (~> 2.5, >= 2.5.4) |
|
20 | mail (~> 2.5, >= 2.5.4) |
|
21 | rails-dom-testing (~> 1.0, >= 1.0.5) |
|
21 | rails-dom-testing (~> 1.0, >= 1.0.5) |
|
22 | actionpack (4.2.7.1) |
|
22 | actionpack (4.2.7.1) |
|
23 | actionview (= 4.2.7.1) |
|
23 | actionview (= 4.2.7.1) |
|
24 | activesupport (= 4.2.7.1) |
|
24 | activesupport (= 4.2.7.1) |
|
25 | rack (~> 1.6) |
|
25 | rack (~> 1.6) |
|
26 | rack-test (~> 0.6.2) |
|
26 | rack-test (~> 0.6.2) |
|
27 | rails-dom-testing (~> 1.0, >= 1.0.5) |
|
27 | rails-dom-testing (~> 1.0, >= 1.0.5) |
|
28 | rails-html-sanitizer (~> 1.0, >= 1.0.2) |
|
28 | rails-html-sanitizer (~> 1.0, >= 1.0.2) |
|
29 | actionview (4.2.7.1) |
|
29 | actionview (4.2.7.1) |
|
30 | activesupport (= 4.2.7.1) |
|
30 | activesupport (= 4.2.7.1) |
|
31 | builder (~> 3.1) |
|
31 | builder (~> 3.1) |
|
32 | erubis (~> 2.7.0) |
|
32 | erubis (~> 2.7.0) |
|
33 | rails-dom-testing (~> 1.0, >= 1.0.5) |
|
33 | rails-dom-testing (~> 1.0, >= 1.0.5) |
|
34 | rails-html-sanitizer (~> 1.0, >= 1.0.2) |
|
34 | rails-html-sanitizer (~> 1.0, >= 1.0.2) |
|
35 | activejob (4.2.7.1) |
|
35 | activejob (4.2.7.1) |
|
36 | activesupport (= 4.2.7.1) |
|
36 | activesupport (= 4.2.7.1) |
|
37 | globalid (>= 0.3.0) |
|
37 | globalid (>= 0.3.0) |
|
38 | activemodel (4.2.7.1) |
|
38 | activemodel (4.2.7.1) |
|
39 | activesupport (= 4.2.7.1) |
|
39 | activesupport (= 4.2.7.1) |
|
40 | builder (~> 3.1) |
|
40 | builder (~> 3.1) |
|
41 | activerecord (4.2.7.1) |
|
41 | activerecord (4.2.7.1) |
|
42 | activemodel (= 4.2.7.1) |
|
42 | activemodel (= 4.2.7.1) |
|
43 | activesupport (= 4.2.7.1) |
|
43 | activesupport (= 4.2.7.1) |
|
44 | arel (~> 6.0) |
|
44 | arel (~> 6.0) |
|
45 | activerecord-session_store (1.0.0) |
|
45 | activerecord-session_store (1.0.0) |
|
46 | actionpack (>= 4.0, < 5.1) |
|
46 | actionpack (>= 4.0, < 5.1) |
|
47 | activerecord (>= 4.0, < 5.1) |
|
47 | activerecord (>= 4.0, < 5.1) |
|
48 | multi_json (~> 1.11, >= 1.11.2) |
|
48 | multi_json (~> 1.11, >= 1.11.2) |
|
49 | rack (>= 1.5.2, < 3) |
|
49 | rack (>= 1.5.2, < 3) |
|
50 | railties (>= 4.0, < 5.1) |
|
50 | railties (>= 4.0, < 5.1) |
|
51 | activesupport (4.2.7.1) |
|
51 | activesupport (4.2.7.1) |
|
52 | i18n (~> 0.7) |
|
52 | i18n (~> 0.7) |
|
53 | json (~> 1.7, >= 1.7.7) |
|
53 | json (~> 1.7, >= 1.7.7) |
|
54 | minitest (~> 5.1) |
|
54 | minitest (~> 5.1) |
|
55 | thread_safe (~> 0.3, >= 0.3.4) |
|
55 | thread_safe (~> 0.3, >= 0.3.4) |
|
56 | tzinfo (~> 1.1) |
|
56 | tzinfo (~> 1.1) |
|
57 | ansi (1.5.0) |
|
57 | ansi (1.5.0) |
|
58 | arel (6.0.4) |
|
58 | arel (6.0.4) |
|
59 | autoprefixer-rails (6.6.0) |
|
59 | autoprefixer-rails (6.6.0) |
|
60 | execjs |
|
60 | execjs |
|
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) |
|
71 | railties (>= 4.0.0, < 5.2.x) |
|
75 | railties (>= 4.0.0, < 5.2.x) |
|
72 | coffee-script (2.4.1) |
|
76 | coffee-script (2.4.1) |
|
73 | coffee-script-source |
|
77 | coffee-script-source |
|
74 | execjs |
|
78 | execjs |
|
75 | coffee-script-source (1.12.2) |
|
79 | coffee-script-source (1.12.2) |
|
76 | concurrent-ruby (1.0.4) |
|
80 | concurrent-ruby (1.0.4) |
|
77 | dynamic_form (1.1.4) |
|
81 | dynamic_form (1.1.4) |
|
78 | erubis (2.7.0) |
|
82 | erubis (2.7.0) |
|
79 | execjs (2.7.0) |
|
83 | execjs (2.7.0) |
|
80 | fuzzy-string-match (1.0.0) |
|
84 | fuzzy-string-match (1.0.0) |
|
81 | RubyInline (>= 3.8.6) |
|
85 | RubyInline (>= 3.8.6) |
|
82 | globalid (0.3.7) |
|
86 | globalid (0.3.7) |
|
83 | activesupport (>= 4.1.0) |
|
87 | activesupport (>= 4.1.0) |
|
84 | haml (4.0.7) |
|
88 | haml (4.0.7) |
|
85 | tilt |
|
89 | tilt |
|
86 | haml-rails (0.9.0) |
|
90 | haml-rails (0.9.0) |
|
87 | actionpack (>= 4.0.1) |
|
91 | actionpack (>= 4.0.1) |
|
88 | activesupport (>= 4.0.1) |
|
92 | activesupport (>= 4.0.1) |
|
89 | haml (>= 4.0.6, < 5.0) |
|
93 | haml (>= 4.0.6, < 5.0) |
|
90 | html2haml (>= 1.0.1) |
|
94 | html2haml (>= 1.0.1) |
|
91 | railties (>= 4.0.1) |
|
95 | railties (>= 4.0.1) |
|
92 | html2haml (2.0.0) |
|
96 | html2haml (2.0.0) |
|
93 | erubis (~> 2.7.0) |
|
97 | erubis (~> 2.7.0) |
|
94 | haml (~> 4.0.0) |
|
98 | haml (~> 4.0.0) |
|
95 | nokogiri (~> 1.6.0) |
|
99 | nokogiri (~> 1.6.0) |
|
96 | ruby_parser (~> 3.5) |
|
100 | ruby_parser (~> 3.5) |
|
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) |
|
103 | thor (>= 0.14, < 2.0) |
|
112 | thor (>= 0.14, < 2.0) |
|
104 | jquery-tablesorter (1.23.3) |
|
113 | jquery-tablesorter (1.23.3) |
|
105 | railties (>= 3.2, < 6) |
|
114 | railties (>= 3.2, < 6) |
|
106 | jquery-timepicker-addon-rails (1.4.1) |
|
115 | jquery-timepicker-addon-rails (1.4.1) |
|
107 | railties (>= 3.1) |
|
116 | railties (>= 3.1) |
|
108 | jquery-ui-rails (6.0.1) |
|
117 | jquery-ui-rails (6.0.1) |
|
109 | railties (>= 3.2.16) |
|
118 | railties (>= 3.2.16) |
|
110 | json (1.8.3) |
|
119 | json (1.8.3) |
|
111 | loofah (2.0.3) |
|
120 | loofah (2.0.3) |
|
112 | nokogiri (>= 1.5.9) |
|
121 | nokogiri (>= 1.5.9) |
|
113 | mail (2.6.4) |
|
122 | mail (2.6.4) |
|
114 | mime-types (>= 1.16, < 4) |
|
123 | mime-types (>= 1.16, < 4) |
|
115 | mime-types (3.1) |
|
124 | mime-types (3.1) |
|
116 | mime-types-data (~> 3.2015) |
|
125 | mime-types-data (~> 3.2015) |
|
117 | mime-types-data (3.2016.0521) |
|
126 | mime-types-data (3.2016.0521) |
|
118 | mini_portile2 (2.1.0) |
|
127 | mini_portile2 (2.1.0) |
|
119 | minitest (5.10.1) |
|
128 | minitest (5.10.1) |
|
120 | minitest-reporters (1.1.13) |
|
129 | minitest-reporters (1.1.13) |
|
121 | ansi |
|
130 | ansi |
|
122 | builder |
|
131 | builder |
|
123 | minitest (>= 5.0) |
|
132 | minitest (>= 5.0) |
|
124 | ruby-progressbar |
|
133 | ruby-progressbar |
|
125 | momentjs-rails (2.15.1) |
|
134 | momentjs-rails (2.15.1) |
|
126 | railties (>= 3.1) |
|
135 | railties (>= 3.1) |
|
127 | multi_json (1.12.1) |
|
136 | multi_json (1.12.1) |
|
128 | mysql2 (0.4.5) |
|
137 | mysql2 (0.4.5) |
|
129 | nokogiri (1.6.8.1) |
|
138 | nokogiri (1.6.8.1) |
|
130 | mini_portile2 (~> 2.1.0) |
|
139 | mini_portile2 (~> 2.1.0) |
|
131 | rack (1.6.5) |
|
140 | rack (1.6.5) |
|
132 | rack-test (0.6.3) |
|
141 | rack-test (0.6.3) |
|
133 | rack (>= 1.0) |
|
142 | rack (>= 1.0) |
|
134 | rails (4.2.7.1) |
|
143 | rails (4.2.7.1) |
|
135 | actionmailer (= 4.2.7.1) |
|
144 | actionmailer (= 4.2.7.1) |
|
136 | actionpack (= 4.2.7.1) |
|
145 | actionpack (= 4.2.7.1) |
|
137 | actionview (= 4.2.7.1) |
|
146 | actionview (= 4.2.7.1) |
|
138 | activejob (= 4.2.7.1) |
|
147 | activejob (= 4.2.7.1) |
|
139 | activemodel (= 4.2.7.1) |
|
148 | activemodel (= 4.2.7.1) |
|
140 | activerecord (= 4.2.7.1) |
|
149 | activerecord (= 4.2.7.1) |
|
141 | activesupport (= 4.2.7.1) |
|
150 | activesupport (= 4.2.7.1) |
|
142 | bundler (>= 1.3.0, < 2.0) |
|
151 | bundler (>= 1.3.0, < 2.0) |
|
143 | railties (= 4.2.7.1) |
|
152 | railties (= 4.2.7.1) |
|
144 | sprockets-rails |
|
153 | sprockets-rails |
|
145 | rails-deprecated_sanitizer (1.0.3) |
|
154 | rails-deprecated_sanitizer (1.0.3) |
|
146 | activesupport (>= 4.2.0.alpha) |
|
155 | activesupport (>= 4.2.0.alpha) |
|
147 | rails-dom-testing (1.0.8) |
|
156 | rails-dom-testing (1.0.8) |
@@ -156,80 +165,83 | |||||
|
156 | actionpack (= 4.2.7.1) |
|
165 | actionpack (= 4.2.7.1) |
|
157 | activesupport (= 4.2.7.1) |
|
166 | activesupport (= 4.2.7.1) |
|
158 | rake (>= 0.8.7) |
|
167 | rake (>= 0.8.7) |
|
159 | thor (>= 0.18.1, < 2.0) |
|
168 | thor (>= 0.18.1, < 2.0) |
|
160 | rake (12.0.0) |
|
169 | rake (12.0.0) |
|
161 | rdiscount (2.2.0.1) |
|
170 | rdiscount (2.2.0.1) |
|
162 | rouge (2.0.7) |
|
171 | rouge (2.0.7) |
|
163 | ruby-progressbar (1.8.1) |
|
172 | ruby-progressbar (1.8.1) |
|
164 | ruby_parser (3.8.3) |
|
173 | ruby_parser (3.8.3) |
|
165 | sexp_processor (~> 4.1) |
|
174 | sexp_processor (~> 4.1) |
|
166 | sass (3.4.23) |
|
175 | sass (3.4.23) |
|
167 | sass-rails (5.0.6) |
|
176 | sass-rails (5.0.6) |
|
168 | railties (>= 4.0.0, < 6) |
|
177 | railties (>= 4.0.0, < 6) |
|
169 | sass (~> 3.1) |
|
178 | sass (~> 3.1) |
|
170 | sprockets (>= 2.8, < 4.0) |
|
179 | sprockets (>= 2.8, < 4.0) |
|
171 | sprockets-rails (>= 2.0, < 4.0) |
|
180 | sprockets-rails (>= 2.0, < 4.0) |
|
172 | tilt (>= 1.1, < 3) |
|
181 | tilt (>= 1.1, < 3) |
|
173 | select2-rails (4.0.3) |
|
182 | select2-rails (4.0.3) |
|
174 | thor (~> 0.14) |
|
183 | thor (~> 0.14) |
|
175 | sexp_processor (4.7.0) |
|
184 | sexp_processor (4.7.0) |
|
176 | sprockets (3.7.1) |
|
185 | sprockets (3.7.1) |
|
177 | concurrent-ruby (~> 1.0) |
|
186 | concurrent-ruby (~> 1.0) |
|
178 | rack (> 1, < 3) |
|
187 | rack (> 1, < 3) |
|
179 | sprockets-rails (3.2.0) |
|
188 | sprockets-rails (3.2.0) |
|
180 | actionpack (>= 4.0) |
|
189 | actionpack (>= 4.0) |
|
181 | activesupport (>= 4.0) |
|
190 | activesupport (>= 4.0) |
|
182 | sprockets (>= 3.0.0) |
|
191 | sprockets (>= 3.0.0) |
|
183 | sqlite3 (1.3.12) |
|
192 | sqlite3 (1.3.12) |
|
184 | thor (0.19.4) |
|
193 | thor (0.19.4) |
|
185 | thread_safe (0.3.5) |
|
194 | thread_safe (0.3.5) |
|
186 | tilt (2.0.5) |
|
195 | tilt (2.0.5) |
|
187 | tzinfo (1.2.2) |
|
196 | tzinfo (1.2.2) |
|
188 | thread_safe (~> 0.1) |
|
197 | thread_safe (~> 0.1) |
|
189 | uglifier (3.0.4) |
|
198 | uglifier (3.0.4) |
|
190 | execjs (>= 0.3.0, < 3) |
|
199 | execjs (>= 0.3.0, < 3) |
|
191 | will_paginate (3.0.12) |
|
200 | will_paginate (3.0.12) |
|
192 | yaml_db (0.4.2) |
|
201 | yaml_db (0.4.2) |
|
193 | rails (>= 3.0, < 5.1) |
|
202 | rails (>= 3.0, < 5.1) |
|
194 | rake (>= 0.8.7) |
|
203 | rake (>= 0.8.7) |
|
195 |
|
204 | ||
|
196 | PLATFORMS |
|
205 | PLATFORMS |
|
197 | ruby |
|
206 | ruby |
|
198 |
|
207 | ||
|
199 | DEPENDENCIES |
|
208 | DEPENDENCIES |
|
200 | ace-rails-ap |
|
209 | ace-rails-ap |
|
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 |
|
210 | haml |
|
221 | haml |
|
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 |
|
217 | jquery-ui-rails |
|
229 | jquery-ui-rails |
|
218 |
|
230 | ||
|
219 | minitest-reporters |
|
231 | minitest-reporters |
|
220 | momentjs-rails |
|
232 | momentjs-rails |
|
221 | mysql2 |
|
233 | mysql2 |
|
222 | rails (~> 4.2.0) |
|
234 | rails (~> 4.2.0) |
|
223 | rails_bootstrap_sortable |
|
235 | rails_bootstrap_sortable |
|
224 | rdiscount |
|
236 | rdiscount |
|
225 | rouge |
|
237 | rouge |
|
226 | sass-rails |
|
238 | sass-rails |
|
227 | select2-rails |
|
239 | select2-rails |
|
228 | sqlite3 |
|
240 | sqlite3 |
|
229 | uglifier |
|
241 | uglifier |
|
230 | verification! |
|
242 | verification! |
|
231 | will_paginate (~> 3.0.7) |
|
243 | will_paginate (~> 3.0.7) |
|
232 | yaml_db |
|
244 | yaml_db |
|
233 |
|
245 | ||
|
234 | BUNDLED WITH |
|
246 | BUNDLED WITH |
|
235 |
- 1.1 |
|
247 | + 1.15.4 |
@@ -1,41 +1,47 | |||||
|
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files |
|
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files |
|
2 | // listed below. |
|
2 | // listed below. |
|
3 | // |
|
3 | // |
|
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, |
|
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, |
|
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. |
|
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. |
|
6 | // |
|
6 | // |
|
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the |
|
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the |
|
8 | // the compiled file. |
|
8 | // the compiled file. |
|
9 | // |
|
9 | // |
|
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD |
|
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD |
|
11 | // GO AFTER THE REQUIRES BELOW. |
|
11 | // GO AFTER THE REQUIRES BELOW. |
|
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 |
|
22 | //= require ace/mode-python |
|
26 | //= require ace/mode-python |
|
23 | //= require ace/mode-ruby |
|
27 | //= require ace/mode-ruby |
|
24 | //= require ace/mode-pascal |
|
28 | //= require ace/mode-pascal |
|
25 | //= require ace/mode-javascript |
|
29 | //= require ace/mode-javascript |
|
26 | //= require ace/mode-java |
|
30 | //= require ace/mode-java |
|
27 | //= require ace/theme-merbivore |
|
31 | //= require ace/theme-merbivore |
|
28 | //= require custom |
|
32 | //= require custom |
|
29 | //= require jquery.countdown |
|
33 | //= require jquery.countdown |
|
30 | //-------------- addition from local_jquery ----------- |
|
34 | //-------------- addition from local_jquery ----------- |
|
31 | //= require jquery-tablesorter |
|
35 | //= require jquery-tablesorter |
|
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 |
|
38 | //x= require prototype_ujs |
|
44 | //x= require prototype_ujs |
|
39 | //x= require effects |
|
45 | //x= require effects |
|
40 | //x= require dragdrop |
|
46 | //x= require dragdrop |
|
41 | //x= require controls |
|
47 | //x= require controls |
@@ -1,83 +1,86 | |||||
|
1 | /* This is a manifest file that'll be compiled into application.css, which will include all the files |
|
1 | /* This is a manifest file that'll be compiled into application.css, which will include all the files |
|
2 | * listed below. |
|
2 | * listed below. |
|
3 | * |
|
3 | * |
|
4 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, |
|
4 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, |
|
5 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. |
|
5 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. |
|
6 | * |
|
6 | * |
|
7 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the |
|
7 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the |
|
8 | * compiled file so the styles you add here take precedence over styles defined in any styles |
|
8 | * compiled file so the styles you add here take precedence over styles defined in any styles |
|
9 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new |
|
9 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new |
|
10 | * file per style scope. |
|
10 | * file per style scope. |
|
11 | * |
|
11 | * |
|
12 | * // bootstrap says that we should not do this, but @import each file instead |
|
12 | * // bootstrap says that we should not do this, but @import each file instead |
|
13 | * # *= require_tree . |
|
13 | * # *= require_tree . |
|
14 | * # *= require_self |
|
14 | * # *= require_self |
|
15 | */ |
|
15 | */ |
|
16 |
|
16 | ||
|
17 | @import "jquery-ui"; |
|
17 | @import "jquery-ui"; |
|
18 | //@import "jquery.ui.core"; |
|
18 | //@import "jquery.ui.core"; |
|
19 | //@import "jquery.ui.theme"; |
|
19 | //@import "jquery.ui.theme"; |
|
20 | //@import "jquery.ui.datepicker"; |
|
20 | //@import "jquery.ui.datepicker"; |
|
21 | //@import "jquery.ui.slider"; |
|
21 | //@import "jquery.ui.slider"; |
|
22 | @import "jquery-ui-timepicker-addon"; |
|
22 | @import "jquery-ui-timepicker-addon"; |
|
23 | @import "jquery-tablesorter/theme.metro-dark"; |
|
23 | @import "jquery-tablesorter/theme.metro-dark"; |
|
24 | @import "jquery.countdown"; |
|
24 | @import "jquery.countdown"; |
|
25 | @import "tablesorter-theme.cafe"; |
|
25 | @import "tablesorter-theme.cafe"; |
|
26 |
|
26 | ||
|
27 | //bootstrap |
|
27 | //bootstrap |
|
28 | @import "bootstrap-sprockets"; |
|
28 | @import "bootstrap-sprockets"; |
|
29 | @import "bootstrap"; |
|
29 | @import "bootstrap"; |
|
30 | @import "select2"; |
|
30 | @import "select2"; |
|
31 | @import "select2-bootstrap"; |
|
31 | @import "select2-bootstrap"; |
|
32 |
|
32 | ||
|
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; |
|
39 | $bgHighlight: #06064b; |
|
42 | $bgHighlight: #06064b; |
|
40 | $colDefault: #8e8eb4; |
|
43 | $colDefault: #8e8eb4; |
|
41 | $colHighlight: #ffffff; |
|
44 | $colHighlight: #ffffff; |
|
42 | $dropDown: false; |
|
45 | $dropDown: false; |
|
43 |
|
46 | ||
|
44 | @font-face { |
|
47 | @font-face { |
|
45 | font-family: 'Glyphicons Halflings'; |
|
48 | font-family: 'Glyphicons Halflings'; |
|
46 | src: font-path('bootstrap/glyphicons-halflings-regular.eot'); |
|
49 | src: font-path('bootstrap/glyphicons-halflings-regular.eot'); |
|
47 | src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), |
|
50 | src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), |
|
48 | font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'), |
|
51 | font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'), |
|
49 | font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'), |
|
52 | font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'), |
|
50 | font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); |
|
53 | font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); |
|
51 | } |
|
54 | } |
|
52 |
|
55 | ||
|
53 |
|
56 | ||
|
54 | .navbar-default { |
|
57 | .navbar-default { |
|
55 | background-color: $bgDefault; |
|
58 | background-color: $bgDefault; |
|
56 | border-color: $bgHighlight; |
|
59 | border-color: $bgHighlight; |
|
57 |
|
60 | ||
|
58 | .navbar-brand { |
|
61 | .navbar-brand { |
|
59 | color: $colDefault; |
|
62 | color: $colDefault; |
|
60 |
|
63 | ||
|
61 | &:hover, &:focus { |
|
64 | &:hover, &:focus { |
|
62 | color: $colHighlight; |
|
65 | color: $colHighlight; |
|
63 | } |
|
66 | } |
|
64 | } |
|
67 | } |
|
65 |
|
68 | ||
|
66 | .navbar-text { |
|
69 | .navbar-text { |
|
67 | color: $colDefault; |
|
70 | color: $colDefault; |
|
68 | } |
|
71 | } |
|
69 |
|
72 | ||
|
70 | .navbar-nav { |
|
73 | .navbar-nav { |
|
71 | > li { |
|
74 | > li { |
|
72 | > a { |
|
75 | > a { |
|
73 | color: $colDefault; |
|
76 | color: $colDefault; |
|
74 |
|
77 | ||
|
75 | &:hover, &:focus { |
|
78 | &:hover, &:focus { |
|
76 | color: $colHighlight; |
|
79 | color: $colHighlight; |
|
77 | } |
|
80 | } |
|
78 | } |
|
81 | } |
|
79 |
|
82 | ||
|
80 | @if $dropDown { |
|
83 | @if $dropDown { |
|
81 | > .dropdown-menu { |
|
84 | > .dropdown-menu { |
|
82 | background-color: $bgDefault; |
|
85 | background-color: $bgDefault; |
|
83 |
|
86 | ||
@@ -501,48 +504,54 | |||||
|
501 | } |
|
504 | } |
|
502 |
|
505 | ||
|
503 | &.reply-body { |
|
506 | &.reply-body { |
|
504 | border: 2px solid #bbbbbb; |
|
507 | border: 2px solid #bbbbbb; |
|
505 | background: #fffff8; |
|
508 | background: #fffff8; |
|
506 | padding-left: 5px; |
|
509 | padding-left: 5px; |
|
507 | } |
|
510 | } |
|
508 |
|
511 | ||
|
509 | &.stat { |
|
512 | &.stat { |
|
510 | font-size: 10px; |
|
513 | font-size: 10px; |
|
511 | line-height: 1.75em; |
|
514 | line-height: 1.75em; |
|
512 | padding: 0 5px; |
|
515 | padding: 0 5px; |
|
513 | color: #333333; |
|
516 | color: #333333; |
|
514 | background: #dddddd; |
|
517 | background: #dddddd; |
|
515 | font-weight: bold; |
|
518 | font-weight: bold; |
|
516 | } |
|
519 | } |
|
517 |
|
520 | ||
|
518 | &.message div.stat { |
|
521 | &.message div.stat { |
|
519 | font-size: 10px; |
|
522 | font-size: 10px; |
|
520 | line-height: 1.75em; |
|
523 | line-height: 1.75em; |
|
521 | padding: 0 5px; |
|
524 | padding: 0 5px; |
|
522 | color: #444444; |
|
525 | color: #444444; |
|
523 | background: #bbbbbb; |
|
526 | background: #bbbbbb; |
|
524 | font-weight: bold; |
|
527 | font-weight: bold; |
|
525 | } |
|
528 | } |
|
526 | } |
|
529 | } |
|
527 | } |
|
530 | } |
|
528 |
|
531 | ||
|
529 | &.contest-title { |
|
532 | &.contest-title { |
|
530 | color: white; |
|
533 | color: white; |
|
531 | text-align: center; |
|
534 | text-align: center; |
|
532 | line-height: 2em; |
|
535 | line-height: 2em; |
|
533 | } |
|
536 | } |
|
534 |
|
537 | ||
|
535 | &.registration-desc, &.test-desc { |
|
538 | &.registration-desc, &.test-desc { |
|
536 | border: 1px dotted gray; |
|
539 | border: 1px dotted gray; |
|
537 | background: #f5f5f5; |
|
540 | background: #f5f5f5; |
|
538 | padding: 5px; |
|
541 | padding: 5px; |
|
539 | margin: 10px 0; |
|
542 | margin: 10px 0; |
|
540 | font-size: 12px; |
|
543 | font-size: 12px; |
|
541 | line-height: 1.5em; |
|
544 | line-height: 1.5em; |
|
542 | } |
|
545 | } |
|
543 | } |
|
546 | } |
|
544 |
|
547 | ||
|
545 | h2.contest-title { |
|
548 | h2.contest-title { |
|
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 | + } |
@@ -1,132 +1,130 | |||||
|
1 | class ApplicationController < ActionController::Base |
|
1 | class ApplicationController < ActionController::Base |
|
2 | protect_from_forgery |
|
2 | protect_from_forgery |
|
3 |
|
3 | ||
|
4 | before_filter :current_user |
|
4 | before_filter :current_user |
|
5 |
|
5 | ||
|
6 | SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode' |
|
6 | SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode' |
|
7 | MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login' |
|
7 | MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login' |
|
8 |
|
8 | ||
|
9 | #report and redirect for unauthorized activities |
|
9 | #report and redirect for unauthorized activities |
|
10 | def unauthorized_redirect |
|
10 | def unauthorized_redirect |
|
11 | flash[:notice] = 'You are not authorized to view the page you requested' |
|
11 | flash[:notice] = 'You are not authorized to view the page you requested' |
|
12 | redirect_to :controller => 'main', :action => 'login' |
|
12 | redirect_to :controller => 'main', :action => 'login' |
|
13 | end |
|
13 | end |
|
14 |
|
14 | ||
|
15 | # Returns the current logged-in user (if any). |
|
15 | # Returns the current logged-in user (if any). |
|
16 | def current_user |
|
16 | def current_user |
|
17 | return nil unless session[:user_id] |
|
17 | return nil unless session[:user_id] |
|
18 | @current_user ||= User.find(session[:user_id]) |
|
18 | @current_user ||= User.find(session[:user_id]) |
|
19 | end |
|
19 | end |
|
20 |
|
20 | ||
|
21 | def admin_authorization |
|
21 | def admin_authorization |
|
22 | return false unless authenticate |
|
22 | return false unless authenticate |
|
23 | user = User.includes(:roles).find(session[:user_id]) |
|
23 | user = User.includes(:roles).find(session[:user_id]) |
|
24 | unless user.admin? |
|
24 | unless user.admin? |
|
25 | unauthorized_redirect |
|
25 | unauthorized_redirect |
|
26 | return false |
|
26 | return false |
|
27 | end |
|
27 | end |
|
28 | return true |
|
28 | return true |
|
29 | end |
|
29 | end |
|
30 |
|
30 | ||
|
31 | def authorization_by_roles(allowed_roles) |
|
31 | def authorization_by_roles(allowed_roles) |
|
32 | return false unless authenticate |
|
32 | return false unless authenticate |
|
33 | user = User.find(session[:user_id]) |
|
33 | user = User.find(session[:user_id]) |
|
34 | unless user.roles.detect { |role| allowed_roles.member?(role.name) } |
|
34 | unless user.roles.detect { |role| allowed_roles.member?(role.name) } |
|
35 | unauthorized_redirect |
|
35 | unauthorized_redirect |
|
36 | return false |
|
36 | return false |
|
37 | end |
|
37 | end |
|
38 | end |
|
38 | end |
|
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 | ||
|
52 | protected |
|
49 | protected |
|
53 |
|
50 | ||
|
54 | def authenticate |
|
51 | def authenticate |
|
55 | unless session[:user_id] |
|
52 | unless session[:user_id] |
|
56 | flash[:notice] = 'You need to login' |
|
53 | flash[:notice] = 'You need to login' |
|
57 | if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY] |
|
54 | if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY] |
|
58 | flash[:notice] = 'You need to login but you cannot log in at this time' |
|
55 | flash[:notice] = 'You need to login but you cannot log in at this time' |
|
59 | end |
|
56 | end |
|
60 | redirect_to :controller => 'main', :action => 'login' |
|
57 | redirect_to :controller => 'main', :action => 'login' |
|
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 |
|
88 | rescue |
|
86 | rescue |
|
89 | end |
|
87 | end |
|
90 | end |
|
88 | end |
|
91 | return true |
|
89 | return true |
|
92 | end |
|
90 | end |
|
93 |
|
91 | ||
|
94 | def authenticate_by_ip_address |
|
92 | def authenticate_by_ip_address |
|
95 | #this assume that we have already authenticate normally |
|
93 | #this assume that we have already authenticate normally |
|
96 | unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY] |
|
94 | unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY] |
|
97 | user = User.find(session[:user_id]) |
|
95 | user = User.find(session[:user_id]) |
|
98 | if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip) |
|
96 | if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip) |
|
99 | flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}" |
|
97 | flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}" |
|
100 | redirect_to :controller => 'main', :action => 'login' |
|
98 | redirect_to :controller => 'main', :action => 'login' |
|
101 | puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}" |
|
99 | puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}" |
|
102 | return false |
|
100 | return false |
|
103 | end |
|
101 | end |
|
104 | unless user.last_ip |
|
102 | unless user.last_ip |
|
105 | user.last_ip = request.remote_ip |
|
103 | user.last_ip = request.remote_ip |
|
106 | user.save |
|
104 | user.save |
|
107 | end |
|
105 | end |
|
108 | end |
|
106 | end |
|
109 | return true |
|
107 | return true |
|
110 | end |
|
108 | end |
|
111 |
|
109 | ||
|
112 | def authorization |
|
110 | def authorization |
|
113 | return false unless authenticate |
|
111 | return false unless authenticate |
|
114 | user = User.find(session[:user_id]) |
|
112 | user = User.find(session[:user_id]) |
|
115 | unless user.roles.detect { |role| |
|
113 | unless user.roles.detect { |role| |
|
116 | role.rights.detect{ |right| |
|
114 | role.rights.detect{ |right| |
|
117 | right.controller == self.class.controller_name and |
|
115 | right.controller == self.class.controller_name and |
|
118 | (right.action == 'all' or right.action == action_name) |
|
116 | (right.action == 'all' or right.action == action_name) |
|
119 | } |
|
117 | } |
|
120 | } |
|
118 | } |
|
121 | flash[:notice] = 'You are not authorized to view the page you requested' |
|
119 | flash[:notice] = 'You are not authorized to view the page you requested' |
|
122 | #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login') |
|
120 | #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login') |
|
123 | redirect_to :controller => 'main', :action => 'login' |
|
121 | redirect_to :controller => 'main', :action => 'login' |
|
124 | return false |
|
122 | return false |
|
125 | end |
|
123 | end |
|
126 | end |
|
124 | end |
|
127 |
|
125 | ||
|
128 | def verify_time_limit |
|
126 | def verify_time_limit |
|
129 | return true if session[:user_id]==nil |
|
127 | return true if session[:user_id]==nil |
|
130 | user = User.find(session[:user_id], :include => :site) |
|
128 | user = User.find(session[:user_id], :include => :site) |
|
131 | return true if user==nil or user.site == nil |
|
129 | return true if user==nil or user.site == nil |
|
132 | if user.contest_finished? |
|
130 | if user.contest_finished? |
@@ -1,128 +1,93 | |||||
|
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', |
|
23 | 'start_grading', |
|
7 | 'start_grading', |
|
24 | 'stop_all', |
|
8 | 'stop_all', |
|
25 | 'clear_terminated'], |
|
9 | 'clear_terminated'], |
|
26 | :redirect_to => {:action => 'index'} |
|
10 | :redirect_to => {:action => 'index'} |
|
27 |
|
11 | ||
|
28 | def index |
|
12 | def index |
|
29 | redirect_to :action => 'list' |
|
13 | redirect_to :action => 'list' |
|
30 | end |
|
14 | end |
|
31 |
|
15 | ||
|
32 | def list |
|
16 | def list |
|
33 | @grader_processes = GraderProcess.find_running_graders |
|
17 | @grader_processes = GraderProcess.find_running_graders |
|
34 | @stalled_processes = GraderProcess.find_stalled_process |
|
18 | @stalled_processes = GraderProcess.find_stalled_process |
|
35 |
|
19 | ||
|
36 | @terminated_processes = GraderProcess.find_terminated_graders |
|
20 | @terminated_processes = GraderProcess.find_terminated_graders |
|
37 |
|
21 | ||
|
38 | @last_task = Task.last |
|
22 | @last_task = Task.last |
|
39 | @last_test_request = TestRequest.last |
|
23 | @last_test_request = TestRequest.last |
|
40 | @submission = Submission.order("id desc").limit(20) |
|
24 | @submission = Submission.order("id desc").limit(20) |
|
41 | @backlog_submission = Submission.where('graded_at is null') |
|
25 | @backlog_submission = Submission.where('graded_at is null') |
|
42 | end |
|
26 | end |
|
43 |
|
27 | ||
|
44 | def clear |
|
28 | def clear |
|
45 | grader_proc = GraderProcess.find(params[:id]) |
|
29 | grader_proc = GraderProcess.find(params[:id]) |
|
46 | grader_proc.destroy if grader_proc!=nil |
|
30 | grader_proc.destroy if grader_proc!=nil |
|
47 | redirect_to :action => 'list' |
|
31 | redirect_to :action => 'list' |
|
48 | end |
|
32 | end |
|
49 |
|
33 | ||
|
50 | def clear_terminated |
|
34 | def clear_terminated |
|
51 | GraderProcess.find_terminated_graders.each do |p| |
|
35 | GraderProcess.find_terminated_graders.each do |p| |
|
52 | p.destroy |
|
36 | p.destroy |
|
53 | end |
|
37 | end |
|
54 | redirect_to :action => 'list' |
|
38 | redirect_to :action => 'list' |
|
55 | end |
|
39 | end |
|
56 |
|
40 | ||
|
57 | def clear_all |
|
41 | def clear_all |
|
58 | GraderProcess.all.each do |p| |
|
42 | GraderProcess.all.each do |p| |
|
59 | p.destroy |
|
43 | p.destroy |
|
60 | end |
|
44 | end |
|
61 | redirect_to :action => 'list' |
|
45 | redirect_to :action => 'list' |
|
62 | end |
|
46 | end |
|
63 |
|
47 | ||
|
64 | def view |
|
48 | def view |
|
65 | if params[:type]=='Task' |
|
49 | if params[:type]=='Task' |
|
66 | redirect_to :action => 'task', :id => params[:id] |
|
50 | redirect_to :action => 'task', :id => params[:id] |
|
67 | else |
|
51 | else |
|
68 | redirect_to :action => 'test_request', :id => params[:id] |
|
52 | redirect_to :action => 'test_request', :id => params[:id] |
|
69 | end |
|
53 | end |
|
70 | end |
|
54 | end |
|
71 |
|
55 | ||
|
72 | def test_request |
|
56 | def test_request |
|
73 | @test_request = TestRequest.find(params[:id]) |
|
57 | @test_request = TestRequest.find(params[:id]) |
|
74 | end |
|
58 | end |
|
75 |
|
59 | ||
|
76 | def task |
|
60 | def task |
|
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 | ||
|
102 | def stop |
|
67 | def stop |
|
103 | grader_proc = GraderProcess.find(params[:id]) |
|
68 | grader_proc = GraderProcess.find(params[:id]) |
|
104 | GraderScript.stop_grader(grader_proc.pid) |
|
69 | GraderScript.stop_grader(grader_proc.pid) |
|
105 | flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.' |
|
70 | flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.' |
|
106 | redirect_to :action => 'list' |
|
71 | redirect_to :action => 'list' |
|
107 | end |
|
72 | end |
|
108 |
|
73 | ||
|
109 | def stop_all |
|
74 | def stop_all |
|
110 | GraderScript.stop_graders(GraderProcess.find_running_graders + |
|
75 | GraderScript.stop_graders(GraderProcess.find_running_graders + |
|
111 | GraderProcess.find_stalled_process) |
|
76 | GraderProcess.find_stalled_process) |
|
112 | flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.' |
|
77 | flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.' |
|
113 | redirect_to :action => 'list' |
|
78 | redirect_to :action => 'list' |
|
114 | end |
|
79 | end |
|
115 |
|
80 | ||
|
116 | def start_grading |
|
81 | def start_grading |
|
117 | GraderScript.start_grader('grading') |
|
82 | GraderScript.start_grader('grading') |
|
118 | flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request' |
|
83 | flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request' |
|
119 | redirect_to :action => 'list' |
|
84 | redirect_to :action => 'list' |
|
120 | end |
|
85 | end |
|
121 |
|
86 | ||
|
122 | def start_exam |
|
87 | def start_exam |
|
123 | GraderScript.start_grader('exam') |
|
88 | GraderScript.start_grader('exam') |
|
124 | flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request' |
|
89 | flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request' |
|
125 | redirect_to :action => 'list' |
|
90 | redirect_to :action => 'list' |
|
126 | end |
|
91 | end |
|
127 |
|
92 | ||
|
128 | end |
|
93 | end |
@@ -1,57 +1,63 | |||||
|
1 | class LoginController < ApplicationController |
|
1 | class LoginController < ApplicationController |
|
2 |
|
2 | ||
|
3 | def index |
|
3 | def index |
|
4 | # show login screen |
|
4 | # show login screen |
|
5 | reset_session |
|
5 | reset_session |
|
6 | redirect_to :controller => 'main', :action => 'login' |
|
6 | redirect_to :controller => 'main', :action => 'login' |
|
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 |
|
39 | begin |
|
45 | begin |
|
40 | site = Site.find(params[:login][:site_id]) |
|
46 | site = Site.find(params[:login][:site_id]) |
|
41 | rescue ActiveRecord::RecordNotFound |
|
47 | rescue ActiveRecord::RecordNotFound |
|
42 | site = nil |
|
48 | site = nil |
|
43 | end |
|
49 | end |
|
44 | if site==nil |
|
50 | if site==nil |
|
45 | flash[:notice] = 'Wrong site' |
|
51 | flash[:notice] = 'Wrong site' |
|
46 | redirect_to :controller => 'main', :action => 'login' and return |
|
52 | redirect_to :controller => 'main', :action => 'login' and return |
|
47 | end |
|
53 | end |
|
48 | if (site.password) and (site.password == params[:login][:password]) |
|
54 | if (site.password) and (site.password == params[:login][:password]) |
|
49 | session[:site_id] = site.id |
|
55 | session[:site_id] = site.id |
|
50 | redirect_to :controller => 'site', :action => 'index' |
|
56 | redirect_to :controller => 'site', :action => 'index' |
|
51 | else |
|
57 | else |
|
52 | flash[:notice] = 'Wrong site password' |
|
58 | flash[:notice] = 'Wrong site password' |
|
53 | redirect_to :controller => 'site', :action => 'login' |
|
59 | redirect_to :controller => 'site', :action => 'login' |
|
54 | end |
|
60 | end |
|
55 | end |
|
61 | end |
|
56 |
|
62 | ||
|
57 | end |
|
63 | end |
@@ -41,108 +41,108 | |||||
|
41 | # |
|
41 | # |
|
42 | # logger.info "PATH: #{request.path}" |
|
42 | # logger.info "PATH: #{request.path}" |
|
43 | # if GraderConfiguration['system.single_user_mode'] and |
|
43 | # if GraderConfiguration['system.single_user_mode'] and |
|
44 | # request.path!='/main/login' |
|
44 | # request.path!='/main/login' |
|
45 | # @hidelogin = true |
|
45 | # @hidelogin = true |
|
46 | # end |
|
46 | # end |
|
47 |
|
47 | ||
|
48 | @announcements = Announcement.frontpage |
|
48 | @announcements = Announcement.frontpage |
|
49 | render :action => 'login', :layout => 'empty' |
|
49 | render :action => 'login', :layout => 'empty' |
|
50 | end |
|
50 | end |
|
51 |
|
51 | ||
|
52 | def list |
|
52 | def list |
|
53 | prepare_list_information |
|
53 | prepare_list_information |
|
54 | end |
|
54 | end |
|
55 |
|
55 | ||
|
56 | def help |
|
56 | def help |
|
57 | @user = User.find(session[:user_id]) |
|
57 | @user = User.find(session[:user_id]) |
|
58 | end |
|
58 | end |
|
59 |
|
59 | ||
|
60 | def submit |
|
60 | def submit |
|
61 | user = User.find(session[:user_id]) |
|
61 | user = User.find(session[:user_id]) |
|
62 |
|
62 | ||
|
63 | @submission = Submission.new |
|
63 | @submission = Submission.new |
|
64 | @submission.problem_id = params[:submission][:problem_id] |
|
64 | @submission.problem_id = params[:submission][:problem_id] |
|
65 | @submission.user = user |
|
65 | @submission.user = user |
|
66 | @submission.language_id = 0 |
|
66 | @submission.language_id = 0 |
|
67 | if (params['file']) and (params['file']!='') |
|
67 | if (params['file']) and (params['file']!='') |
|
68 | @submission.source = File.open(params['file'].path,'r:UTF-8',&:read) |
|
68 | @submission.source = File.open(params['file'].path,'r:UTF-8',&:read) |
|
69 | @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '') |
|
69 | @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '') |
|
70 | @submission.source_filename = params['file'].original_filename |
|
70 | @submission.source_filename = params['file'].original_filename |
|
71 | end |
|
71 | end |
|
72 |
|
72 | ||
|
73 | if (params[:editor_text]) |
|
73 | if (params[:editor_text]) |
|
74 | language = Language.find_by_id(params[:language_id]) |
|
74 | language = Language.find_by_id(params[:language_id]) |
|
75 | @submission.source = params[:editor_text] |
|
75 | @submission.source = params[:editor_text] |
|
76 | @submission.source_filename = "live_edit.#{language.ext}" |
|
76 | @submission.source_filename = "live_edit.#{language.ext}" |
|
77 | @submission.language = language |
|
77 | @submission.language = language |
|
78 | end |
|
78 | end |
|
79 |
|
79 | ||
|
80 | @submission.submitted_at = Time.new.gmtime |
|
80 | @submission.submitted_at = Time.new.gmtime |
|
81 | @submission.ip_address = request.remote_ip |
|
81 | @submission.ip_address = request.remote_ip |
|
82 |
|
82 | ||
|
83 | if GraderConfiguration.time_limit_mode? and user.contest_finished? |
|
83 | if GraderConfiguration.time_limit_mode? and user.contest_finished? |
|
84 | @submission.errors.add(:base,"The contest is over.") |
|
84 | @submission.errors.add(:base,"The contest is over.") |
|
85 | prepare_list_information |
|
85 | prepare_list_information |
|
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, |
|
93 | :status => Task::STATUS_INQUEUE) == false |
|
93 | :status => Task::STATUS_INQUEUE) == false |
|
94 | flash[:notice] = 'Error adding your submission to task queue' |
|
94 | flash[:notice] = 'Error adding your submission to task queue' |
|
95 | end |
|
95 | end |
|
96 | else |
|
96 | else |
|
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 |
|
104 | submission = Submission.find(params[:id]) |
|
104 | submission = Submission.find(params[:id]) |
|
105 | if ((submission.user_id == session[:user_id]) and |
|
105 | if ((submission.user_id == session[:user_id]) and |
|
106 | (submission.problem != nil) and |
|
106 | (submission.problem != nil) and |
|
107 | (submission.problem.available)) |
|
107 | (submission.problem.available)) |
|
108 | send_data(submission.source, |
|
108 | send_data(submission.source, |
|
109 | {:filename => submission.download_filename, |
|
109 | {:filename => submission.download_filename, |
|
110 | :type => 'text/plain'}) |
|
110 | :type => 'text/plain'}) |
|
111 | else |
|
111 | else |
|
112 | flash[:notice] = 'Error viewing source' |
|
112 | flash[:notice] = 'Error viewing source' |
|
113 | redirect_to :action => 'list' |
|
113 | redirect_to :action => 'list' |
|
114 | end |
|
114 | end |
|
115 | end |
|
115 | end |
|
116 |
|
116 | ||
|
117 | def compiler_msg |
|
117 | def compiler_msg |
|
118 | @submission = Submission.find(params[:id]) |
|
118 | @submission = Submission.find(params[:id]) |
|
119 | if @submission.user_id == session[:user_id] |
|
119 | if @submission.user_id == session[:user_id] |
|
120 | render :action => 'compiler_msg', :layout => 'empty' |
|
120 | render :action => 'compiler_msg', :layout => 'empty' |
|
121 | else |
|
121 | else |
|
122 | flash[:notice] = 'Error viewing source' |
|
122 | flash[:notice] = 'Error viewing source' |
|
123 | redirect_to :action => 'list' |
|
123 | redirect_to :action => 'list' |
|
124 | end |
|
124 | end |
|
125 | end |
|
125 | end |
|
126 |
|
126 | ||
|
127 | def result |
|
127 | def result |
|
128 | if !GraderConfiguration.show_grading_result |
|
128 | if !GraderConfiguration.show_grading_result |
|
129 | redirect_to :action => 'list' and return |
|
129 | redirect_to :action => 'list' and return |
|
130 | end |
|
130 | end |
|
131 | @user = User.find(session[:user_id]) |
|
131 | @user = User.find(session[:user_id]) |
|
132 | @submission = Submission.find(params[:id]) |
|
132 | @submission = Submission.find(params[:id]) |
|
133 | if @submission.user!=@user |
|
133 | if @submission.user!=@user |
|
134 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
134 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
135 | redirect_to :action => 'list' and return |
|
135 | redirect_to :action => 'list' and return |
|
136 | end |
|
136 | end |
|
137 | prepare_grading_result(@submission) |
|
137 | prepare_grading_result(@submission) |
|
138 | end |
|
138 | end |
|
139 |
|
139 | ||
|
140 | def load_output |
|
140 | def load_output |
|
141 | if !GraderConfiguration.show_grading_result or params[:num]==nil |
|
141 | if !GraderConfiguration.show_grading_result or params[:num]==nil |
|
142 | redirect_to :action => 'list' and return |
|
142 | redirect_to :action => 'list' and return |
|
143 | end |
|
143 | end |
|
144 | @user = User.find(session[:user_id]) |
|
144 | @user = User.find(session[:user_id]) |
|
145 | @submission = Submission.find(params[:id]) |
|
145 | @submission = Submission.find(params[:id]) |
|
146 | if @submission.user!=@user |
|
146 | if @submission.user!=@user |
|
147 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
147 | flash[:notice] = 'You are not allowed to view result of other users.' |
|
148 | redirect_to :action => 'list' and return |
|
148 | redirect_to :action => 'list' and return |
@@ -120,175 +120,191 | |||||
|
120 | end |
|
120 | end |
|
121 |
|
121 | ||
|
122 | def toggle |
|
122 | def toggle |
|
123 | @problem = Problem.find(params[:id]) |
|
123 | @problem = Problem.find(params[:id]) |
|
124 | @problem.update_attributes(available: !(@problem.available) ) |
|
124 | @problem.update_attributes(available: !(@problem.available) ) |
|
125 | respond_to do |format| |
|
125 | respond_to do |format| |
|
126 | format.js { } |
|
126 | format.js { } |
|
127 | end |
|
127 | end |
|
128 | end |
|
128 | end |
|
129 |
|
129 | ||
|
130 | def toggle_test |
|
130 | def toggle_test |
|
131 | @problem = Problem.find(params[:id]) |
|
131 | @problem = Problem.find(params[:id]) |
|
132 | @problem.update_attributes(test_allowed: !(@problem.test_allowed?) ) |
|
132 | @problem.update_attributes(test_allowed: !(@problem.test_allowed?) ) |
|
133 | respond_to do |format| |
|
133 | respond_to do |format| |
|
134 | format.js { } |
|
134 | format.js { } |
|
135 | end |
|
135 | end |
|
136 | end |
|
136 | end |
|
137 |
|
137 | ||
|
138 | def toggle_view_testcase |
|
138 | def toggle_view_testcase |
|
139 | @problem = Problem.find(params[:id]) |
|
139 | @problem = Problem.find(params[:id]) |
|
140 | @problem.update_attributes(view_testcase: !(@problem.view_testcase?) ) |
|
140 | @problem.update_attributes(view_testcase: !(@problem.view_testcase?) ) |
|
141 | respond_to do |format| |
|
141 | respond_to do |format| |
|
142 | format.js { } |
|
142 | format.js { } |
|
143 | end |
|
143 | end |
|
144 | end |
|
144 | end |
|
145 |
|
145 | ||
|
146 | def turn_all_off |
|
146 | def turn_all_off |
|
147 | Problem.available.all.each do |problem| |
|
147 | Problem.available.all.each do |problem| |
|
148 | problem.available = false |
|
148 | problem.available = false |
|
149 | problem.save |
|
149 | problem.save |
|
150 | end |
|
150 | end |
|
151 | redirect_to action: :index |
|
151 | redirect_to action: :index |
|
152 | end |
|
152 | end |
|
153 |
|
153 | ||
|
154 | def turn_all_on |
|
154 | def turn_all_on |
|
155 | Problem.where.not(available: true).each do |problem| |
|
155 | Problem.where.not(available: true).each do |problem| |
|
156 | problem.available = true |
|
156 | problem.available = true |
|
157 | problem.save |
|
157 | problem.save |
|
158 | end |
|
158 | end |
|
159 | redirect_to action: :index |
|
159 | redirect_to action: :index |
|
160 | end |
|
160 | end |
|
161 |
|
161 | ||
|
162 | def stat |
|
162 | def stat |
|
163 | @problem = Problem.find(params[:id]) |
|
163 | @problem = Problem.find(params[:id]) |
|
164 | unless @problem.available or session[:admin] |
|
164 | unless @problem.available or session[:admin] |
|
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 |
|
172 | @histogram = { data: Array.new(range,0), summary: {} } |
|
172 | @histogram = { data: Array.new(range,0), summary: {} } |
|
173 | user = Hash.new(0) |
|
173 | user = Hash.new(0) |
|
174 | @submissions.find_each do |sub| |
|
174 | @submissions.find_each do |sub| |
|
175 | d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60 |
|
175 | d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60 |
|
176 | @histogram[:data][d.to_i] += 1 if d < range |
|
176 | @histogram[:data][d.to_i] += 1 if d < range |
|
177 | user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max |
|
177 | user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max |
|
178 | end |
|
178 | end |
|
179 | @histogram[:summary][:max] = [@histogram[:data].max,1].max |
|
179 | @histogram[:summary][:max] = [@histogram[:data].max,1].max |
|
180 |
|
180 | ||
|
181 | @summary = { attempt: user.count, solve: 0 } |
|
181 | @summary = { attempt: user.count, solve: 0 } |
|
182 | user.each_value { |v| @summary[:solve] += 1 if v == 1 } |
|
182 | user.each_value { |v| @summary[:solve] += 1 if v == 1 } |
|
183 | end |
|
183 | end |
|
184 |
|
184 | ||
|
185 | def manage |
|
185 | def manage |
|
186 | @problems = Problem.order(date_added: :desc) |
|
186 | @problems = Problem.order(date_added: :desc) |
|
187 | end |
|
187 | end |
|
188 |
|
188 | ||
|
189 | def do_manage |
|
189 | def do_manage |
|
190 | if params.has_key? 'change_date_added' |
|
190 | if params.has_key? 'change_date_added' |
|
191 | change_date_added |
|
191 | change_date_added |
|
192 | elsif params.has_key? 'add_to_contest' |
|
192 | elsif params.has_key? 'add_to_contest' |
|
193 | add_to_contest |
|
193 | add_to_contest |
|
194 | elsif params.has_key? 'enable_problem' |
|
194 | elsif params.has_key? 'enable_problem' |
|
195 | set_available(true) |
|
195 | set_available(true) |
|
196 | elsif params.has_key? 'disable_problem' |
|
196 | elsif params.has_key? 'disable_problem' |
|
197 | set_available(false) |
|
197 | set_available(false) |
|
|
198 | + elsif params.has_key? 'add_group' | ||
|
|
199 | + group = Group.find(params[:group_id]) | ||
|
|
200 | + 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 | ||
|
202 | def import |
|
221 | def import |
|
203 | @allow_test_pair_import = allow_test_pair_import? |
|
222 | @allow_test_pair_import = allow_test_pair_import? |
|
204 | end |
|
223 | end |
|
205 |
|
224 | ||
|
206 | def do_import |
|
225 | def do_import |
|
207 | old_problem = Problem.find_by_name(params[:name]) |
|
226 | old_problem = Problem.find_by_name(params[:name]) |
|
208 | if !allow_test_pair_import? and params.has_key? :import_to_db |
|
227 | if !allow_test_pair_import? and params.has_key? :import_to_db |
|
209 | params.delete :import_to_db |
|
228 | params.delete :import_to_db |
|
210 | end |
|
229 | end |
|
211 | @problem, import_log = Problem.create_from_import_form_params(params, |
|
230 | @problem, import_log = Problem.create_from_import_form_params(params, |
|
212 | old_problem) |
|
231 | old_problem) |
|
213 |
|
232 | ||
|
214 | if !@problem.errors.empty? |
|
233 | if !@problem.errors.empty? |
|
215 | render :action => 'import' and return |
|
234 | render :action => 'import' and return |
|
216 | end |
|
235 | end |
|
217 |
|
236 | ||
|
218 | if old_problem!=nil |
|
237 | if old_problem!=nil |
|
219 | flash[:notice] = "The test data has been replaced for problem #{@problem.name}" |
|
238 | flash[:notice] = "The test data has been replaced for problem #{@problem.name}" |
|
220 | end |
|
239 | end |
|
221 | @log = import_log |
|
240 | @log = import_log |
|
222 | end |
|
241 | end |
|
223 |
|
242 | ||
|
224 | def remove_contest |
|
243 | def remove_contest |
|
225 | problem = Problem.find(params[:id]) |
|
244 | problem = Problem.find(params[:id]) |
|
226 | contest = Contest.find(params[:contest_id]) |
|
245 | contest = Contest.find(params[:contest_id]) |
|
227 | if problem!=nil and contest!=nil |
|
246 | if problem!=nil and contest!=nil |
|
228 | problem.contests.delete(contest) |
|
247 | problem.contests.delete(contest) |
|
229 | end |
|
248 | end |
|
230 | redirect_to :action => 'manage' |
|
249 | redirect_to :action => 'manage' |
|
231 | end |
|
250 | end |
|
232 |
|
251 | ||
|
233 | ################################## |
|
252 | ################################## |
|
234 | protected |
|
253 | protected |
|
235 |
|
254 | ||
|
236 | def allow_test_pair_import? |
|
255 | def allow_test_pair_import? |
|
237 | if defined? ALLOW_TEST_PAIR_IMPORT |
|
256 | if defined? ALLOW_TEST_PAIR_IMPORT |
|
238 | return ALLOW_TEST_PAIR_IMPORT |
|
257 | return ALLOW_TEST_PAIR_IMPORT |
|
239 | else |
|
258 | else |
|
240 | return false |
|
259 | return false |
|
241 | end |
|
260 | end |
|
242 | end |
|
261 | end |
|
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 |
|
253 | end |
|
269 | end |
|
254 | end |
|
270 | end |
|
255 |
|
271 | ||
|
256 | def add_to_contest |
|
272 | def add_to_contest |
|
257 | problems = get_problems_from_params |
|
273 | problems = get_problems_from_params |
|
258 | contest = Contest.find(params[:contest][:id]) |
|
274 | contest = Contest.find(params[:contest][:id]) |
|
259 | if contest!=nil and contest.enabled |
|
275 | if contest!=nil and contest.enabled |
|
260 | problems.each do |p| |
|
276 | problems.each do |p| |
|
261 | p.contests << contest |
|
277 | p.contests << contest |
|
262 | end |
|
278 | end |
|
263 | end |
|
279 | end |
|
264 | end |
|
280 | end |
|
265 |
|
281 | ||
|
266 | def set_available(avail) |
|
282 | def set_available(avail) |
|
267 | problems = get_problems_from_params |
|
283 | problems = get_problems_from_params |
|
268 | problems.each do |p| |
|
284 | problems.each do |p| |
|
269 | p.available = avail |
|
285 | p.available = avail |
|
270 | p.save |
|
286 | p.save |
|
271 | end |
|
287 | end |
|
272 | end |
|
288 | end |
|
273 |
|
289 | ||
|
274 | def get_problems_from_params |
|
290 | def get_problems_from_params |
|
275 | problems = [] |
|
291 | problems = [] |
|
276 | params.keys.each do |k| |
|
292 | params.keys.each do |k| |
|
277 | if k.index('prob-')==0 |
|
293 | if k.index('prob-')==0 |
|
278 | name, id, order = k.split('-') |
|
294 | name, id, order = k.split('-') |
|
279 | problems << Problem.find(id) |
|
295 | problems << Problem.find(id) |
|
280 | end |
|
296 | end |
|
281 | end |
|
297 | end |
|
282 | problems |
|
298 | problems |
|
283 | end |
|
299 | end |
|
284 |
|
300 | ||
|
285 | def get_problems_stat |
|
301 | def get_problems_stat |
|
286 | end |
|
302 | end |
|
287 |
|
303 | ||
|
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 |
@@ -7,96 +7,98 | |||||
|
7 | before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score] |
|
7 | before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score] |
|
8 |
|
8 | ||
|
9 | before_filter(only: [:problem_hof]) { |c| |
|
9 | before_filter(only: [:problem_hof]) { |c| |
|
10 | return false unless authenticate |
|
10 | return false unless authenticate |
|
11 |
|
11 | ||
|
12 | admin_authorization unless GraderConfiguration["right.user_view_submission"] |
|
12 | admin_authorization unless GraderConfiguration["right.user_view_submission"] |
|
13 | } |
|
13 | } |
|
14 |
|
14 | ||
|
15 | def max_score |
|
15 | def max_score |
|
16 | end |
|
16 | end |
|
17 |
|
17 | ||
|
18 | def current_score |
|
18 | def current_score |
|
19 | @problems = Problem.available_problems |
|
19 | @problems = Problem.available_problems |
|
20 | @users = User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
20 | @users = User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
21 | @scorearray = calculate_max_score(@problems, @users,0,0,true) |
|
21 | @scorearray = calculate_max_score(@problems, @users,0,0,true) |
|
22 |
|
22 | ||
|
23 | #rencer accordingly |
|
23 | #rencer accordingly |
|
24 | if params[:button] == 'download' then |
|
24 | if params[:button] == 'download' then |
|
25 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
25 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
26 | send_data csv, filename: 'max_score.csv' |
|
26 | send_data csv, filename: 'max_score.csv' |
|
27 | else |
|
27 | else |
|
28 | #render template: 'user_admin/user_stat' |
|
28 | #render template: 'user_admin/user_stat' |
|
29 | render 'current_score' |
|
29 | render 'current_score' |
|
30 | end |
|
30 | end |
|
31 | end |
|
31 | end |
|
32 |
|
32 | ||
|
33 | def show_max_score |
|
33 | def show_max_score |
|
34 | #process parameters |
|
34 | #process parameters |
|
35 | #problems |
|
35 | #problems |
|
36 | @problems = [] |
|
36 | @problems = [] |
|
37 | if params[:problem_id] |
|
37 | if params[:problem_id] |
|
38 | params[:problem_id].each do |id| |
|
38 | params[:problem_id].each do |id| |
|
39 | next unless id.strip != "" |
|
39 | next unless id.strip != "" |
|
40 | pid = Problem.find_by_id(id.to_i) |
|
40 | pid = Problem.find_by_id(id.to_i) |
|
41 | @problems << pid if pid |
|
41 | @problems << pid if pid |
|
42 | end |
|
42 | end |
|
43 | end |
|
43 | end |
|
44 |
|
44 | ||
|
45 | #users |
|
45 | #users |
|
46 | @users = if params[:user] == "all" then |
|
46 | @users = if params[:user] == "all" then |
|
47 | User.includes(:contests).includes(:contest_stat) |
|
47 | User.includes(:contests).includes(:contest_stat) |
|
48 | else |
|
48 | else |
|
49 | User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
49 | User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
50 | end |
|
50 | end |
|
51 |
|
51 | ||
|
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) |
|
58 |
|
60 | ||
|
59 | #rencer accordingly |
|
61 | #rencer accordingly |
|
60 | if params[:button] == 'download' then |
|
62 | if params[:button] == 'download' then |
|
61 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
63 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
62 | send_data csv, filename: 'max_score.csv' |
|
64 | send_data csv, filename: 'max_score.csv' |
|
63 | else |
|
65 | else |
|
64 | #render template: 'user_admin/user_stat' |
|
66 | #render template: 'user_admin/user_stat' |
|
65 | render 'max_score' |
|
67 | render 'max_score' |
|
66 | end |
|
68 | end |
|
67 |
|
69 | ||
|
68 | end |
|
70 | end |
|
69 |
|
71 | ||
|
70 | def score |
|
72 | def score |
|
71 | if params[:commit] == 'download csv' |
|
73 | if params[:commit] == 'download csv' |
|
72 | @problems = Problem.all |
|
74 | @problems = Problem.all |
|
73 | else |
|
75 | else |
|
74 | @problems = Problem.available_problems |
|
76 | @problems = Problem.available_problems |
|
75 | end |
|
77 | end |
|
76 | @users = User.includes(:contests, :contest_stat).where(enabled: true) |
|
78 | @users = User.includes(:contests, :contest_stat).where(enabled: true) |
|
77 | @scorearray = Array.new |
|
79 | @scorearray = Array.new |
|
78 | @users.each do |u| |
|
80 | @users.each do |u| |
|
79 | ustat = Array.new |
|
81 | ustat = Array.new |
|
80 | ustat[0] = u |
|
82 | ustat[0] = u |
|
81 | @problems.each do |p| |
|
83 | @problems.each do |p| |
|
82 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
84 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
83 | if (sub!=nil) and (sub.points!=nil) and p and p.full_score |
|
85 | if (sub!=nil) and (sub.points!=nil) and p and p.full_score |
|
84 | ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)] |
|
86 | ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)] |
|
85 | else |
|
87 | else |
|
86 | ustat << [0,false] |
|
88 | ustat << [0,false] |
|
87 | end |
|
89 | end |
|
88 | end |
|
90 | end |
|
89 | @scorearray << ustat |
|
91 | @scorearray << ustat |
|
90 | end |
|
92 | end |
|
91 | if params[:commit] == 'download csv' then |
|
93 | if params[:commit] == 'download csv' then |
|
92 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
94 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
93 | send_data csv, filename: 'last_score.csv' |
|
95 | send_data csv, filename: 'last_score.csv' |
|
94 | else |
|
96 | else |
|
95 | render template: 'user_admin/user_stat' |
|
97 | render template: 'user_admin/user_stat' |
|
96 | end |
|
98 | end |
|
97 |
|
99 | ||
|
98 | end |
|
100 | end |
|
99 |
|
101 | ||
|
100 | def login_stat |
|
102 | def login_stat |
|
101 | @logins = Array.new |
|
103 | @logins = Array.new |
|
102 |
|
104 |
@@ -1,108 +1,115 | |||||
|
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 |
|
7 | # GET /submissions.json |
|
7 | # GET /submissions.json |
|
8 | # Show problem selection and user's submission of that problem |
|
8 | # Show problem selection and user's submission of that problem |
|
9 | def index |
|
9 | def index |
|
10 | @user = @current_user |
|
10 | @user = @current_user |
|
11 | @problems = @user.available_problems |
|
11 | @problems = @user.available_problems |
|
12 |
|
12 | ||
|
13 | if params[:problem_id]==nil |
|
13 | if params[:problem_id]==nil |
|
14 | @problem = nil |
|
14 | @problem = nil |
|
15 | @submissions = nil |
|
15 | @submissions = nil |
|
16 | else |
|
16 | else |
|
17 | @problem = Problem.find_by_id(params[:problem_id]) |
|
17 | @problem = Problem.find_by_id(params[:problem_id]) |
|
18 | if (@problem == nil) or (not @problem.available) |
|
18 | if (@problem == nil) or (not @problem.available) |
|
19 | redirect_to main_list_path |
|
19 | redirect_to main_list_path |
|
20 | flash[:notice] = 'Error: submissions for that problem are not viewable.' |
|
20 | flash[:notice] = 'Error: submissions for that problem are not viewable.' |
|
21 | return |
|
21 | return |
|
22 | end |
|
22 | end |
|
23 | @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc) |
|
23 | @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc) |
|
24 | end |
|
24 | end |
|
25 | end |
|
25 | end |
|
26 |
|
26 | ||
|
27 | # GET /submissions/1 |
|
27 | # GET /submissions/1 |
|
28 | # GET /submissions/1.json |
|
28 | # GET /submissions/1.json |
|
29 | def show |
|
29 | def show |
|
30 | @submission = Submission.find(params[:id]) |
|
30 | @submission = Submission.find(params[:id]) |
|
31 |
|
31 | ||
|
32 | #log the viewing |
|
32 | #log the viewing |
|
33 | user = User.find(session[:user_id]) |
|
33 | user = User.find(session[:user_id]) |
|
34 | SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin? |
|
34 | SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin? |
|
35 |
|
35 | ||
|
36 | @task = @submission.task |
|
36 | @task = @submission.task |
|
37 | end |
|
37 | end |
|
38 |
|
38 | ||
|
39 | def download |
|
39 | def download |
|
40 | @submission = Submission.find(params[:id]) |
|
40 | @submission = Submission.find(params[:id]) |
|
41 | send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'}) |
|
41 | send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'}) |
|
42 | end |
|
42 | end |
|
43 |
|
43 | ||
|
44 | def compiler_msg |
|
44 | def compiler_msg |
|
45 | @submission = Submission.find(params[:id]) |
|
45 | @submission = Submission.find(params[:id]) |
|
46 | respond_to do |format| |
|
46 | respond_to do |format| |
|
47 | format.js |
|
47 | format.js |
|
48 | end |
|
48 | end |
|
49 | end |
|
49 | end |
|
50 |
|
50 | ||
|
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 | ||
|
58 | # GET /submissions/1/edit |
|
66 | # GET /submissions/1/edit |
|
59 | def edit |
|
67 | def edit |
|
60 | @submission = Submission.find(params[:id]) |
|
68 | @submission = Submission.find(params[:id]) |
|
61 | @source = @submission.source.to_s |
|
69 | @source = @submission.source.to_s |
|
62 | @problem = @submission.problem |
|
70 | @problem = @submission.problem |
|
63 | @lang_id = @submission.language.id |
|
71 | @lang_id = @submission.language.id |
|
64 | end |
|
72 | end |
|
65 |
|
73 | ||
|
66 |
|
74 | ||
|
67 | def get_latest_submission_status |
|
75 | def get_latest_submission_status |
|
68 | @problem = Problem.find(params[:pid]) |
|
76 | @problem = Problem.find(params[:pid]) |
|
69 | @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid]) |
|
77 | @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid]) |
|
70 | puts User.find(params[:uid]).login |
|
78 | puts User.find(params[:uid]).login |
|
71 | puts Problem.find(params[:pid]).name |
|
79 | puts Problem.find(params[:pid]).name |
|
72 | puts 'nil' unless @submission |
|
80 | puts 'nil' unless @submission |
|
73 | respond_to do |format| |
|
81 | respond_to do |format| |
|
74 | format.js |
|
82 | format.js |
|
75 | end |
|
83 | end |
|
76 | end |
|
84 | end |
|
77 |
|
85 | ||
|
78 | # GET /submissions/:id/rejudge |
|
86 | # GET /submissions/:id/rejudge |
|
79 | def rejudge |
|
87 | def rejudge |
|
80 | @submission = Submission.find(params[:id]) |
|
88 | @submission = Submission.find(params[:id]) |
|
81 | @task = @submission.task |
|
89 | @task = @submission.task |
|
82 | @task.status_inqueue! if @task |
|
90 | @task.status_inqueue! if @task |
|
83 | respond_to do |format| |
|
91 | respond_to do |format| |
|
84 | format.js |
|
92 | format.js |
|
85 | end |
|
93 | end |
|
86 | end |
|
94 | end |
|
87 |
|
95 | ||
|
88 | protected |
|
96 | protected |
|
89 |
|
97 | ||
|
90 | def submission_authorization |
|
98 | def submission_authorization |
|
91 | #admin always has privileged |
|
99 | #admin always has privileged |
|
92 | if @current_user.admin? |
|
100 | if @current_user.admin? |
|
93 | return true |
|
101 | return true |
|
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 | ||
|
102 | #default to NO |
|
109 | #default to NO |
|
103 | unauthorized_redirect |
|
110 | unauthorized_redirect |
|
104 | return false |
|
111 | return false |
|
105 | end |
|
112 | end |
|
106 |
|
113 | ||
|
107 |
|
114 | ||
|
108 | end |
|
115 | end |
@@ -1,75 +1,75 | |||||
|
1 | class TasksController < ApplicationController |
|
1 | class TasksController < ApplicationController |
|
2 |
|
2 | ||
|
3 | before_filter :authenticate, :check_viewability |
|
3 | before_filter :authenticate, :check_viewability |
|
4 |
|
4 | ||
|
5 | def index |
|
5 | def index |
|
6 | redirect_to :action => 'list' |
|
6 | redirect_to :action => 'list' |
|
7 | end |
|
7 | end |
|
8 |
|
8 | ||
|
9 | def list |
|
9 | def list |
|
10 | @problems = @user.available_problems |
|
10 | @problems = @user.available_problems |
|
11 | end |
|
11 | end |
|
12 |
|
12 | ||
|
13 | # this has contest-wide access control |
|
13 | # this has contest-wide access control |
|
14 | def view |
|
14 | def view |
|
15 | base_name = params[:file] |
|
15 | base_name = params[:file] |
|
16 | base_filename = File.basename("#{base_name}.#{params[:ext]}") |
|
16 | base_filename = File.basename("#{base_name}.#{params[:ext]}") |
|
17 | filename = "#{Problem.download_file_basedir}/#{base_filename}" |
|
17 | filename = "#{Problem.download_file_basedir}/#{base_filename}" |
|
18 |
|
18 | ||
|
19 | if !FileTest.exists?(filename) |
|
19 | if !FileTest.exists?(filename) |
|
20 | redirect_to :action => 'index' and return |
|
20 | redirect_to :action => 'index' and return |
|
21 | end |
|
21 | end |
|
22 |
|
22 | ||
|
23 | send_file_to_user(filename, base_filename) |
|
23 | send_file_to_user(filename, base_filename) |
|
24 | end |
|
24 | end |
|
25 |
|
25 | ||
|
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 | ||
|
33 | base_name = params[:file] |
|
33 | base_name = params[:file] |
|
34 | base_filename = File.basename("#{base_name}.#{params[:ext]}") |
|
34 | base_filename = File.basename("#{base_name}.#{params[:ext]}") |
|
35 | filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}" |
|
35 | filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}" |
|
36 | puts "SENDING: #{filename}" |
|
36 | puts "SENDING: #{filename}" |
|
37 |
|
37 | ||
|
38 | if !FileTest.exists?(filename) |
|
38 | if !FileTest.exists?(filename) |
|
39 | redirect_to :action => 'index' and return |
|
39 | redirect_to :action => 'index' and return |
|
40 | end |
|
40 | end |
|
41 |
|
41 | ||
|
42 | puts "SENDING: #{filename}" |
|
42 | puts "SENDING: #{filename}" |
|
43 |
|
43 | ||
|
44 | send_file_to_user(filename, base_filename) |
|
44 | send_file_to_user(filename, base_filename) |
|
45 | end |
|
45 | end |
|
46 |
|
46 | ||
|
47 | protected |
|
47 | protected |
|
48 |
|
48 | ||
|
49 | def send_file_to_user(filename, base_filename) |
|
49 | def send_file_to_user(filename, base_filename) |
|
50 | if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE |
|
50 | if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE |
|
51 | response.headers['Content-Type'] = "application/force-download" |
|
51 | response.headers['Content-Type'] = "application/force-download" |
|
52 | response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\"" |
|
52 | response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\"" |
|
53 | response.headers["X-Sendfile"] = filename |
|
53 | response.headers["X-Sendfile"] = filename |
|
54 | response.headers['Content-length'] = File.size(filename) |
|
54 | response.headers['Content-length'] = File.size(filename) |
|
55 | render :nothing => true |
|
55 | render :nothing => true |
|
56 | else |
|
56 | else |
|
57 | if params[:ext]=='pdf' |
|
57 | if params[:ext]=='pdf' |
|
58 | content_type = 'application/pdf' |
|
58 | content_type = 'application/pdf' |
|
59 | else |
|
59 | else |
|
60 | content_type = 'application/octet-stream' |
|
60 | content_type = 'application/octet-stream' |
|
61 | end |
|
61 | end |
|
62 |
|
62 | ||
|
63 | send_file filename, :stream => false, :disposition => 'inline', :filename => base_filename, :type => content_type |
|
63 | send_file filename, :stream => false, :disposition => 'inline', :filename => base_filename, :type => content_type |
|
64 | end |
|
64 | end |
|
65 | end |
|
65 | end |
|
66 |
|
66 | ||
|
67 | def check_viewability |
|
67 | def check_viewability |
|
68 | @user = User.find(session[:user_id]) |
|
68 | @user = User.find(session[:user_id]) |
|
69 | if @user==nil or !GraderConfiguration.show_tasks_to?(@user) |
|
69 | if @user==nil or !GraderConfiguration.show_tasks_to?(@user) |
|
70 | redirect_to :controller => 'main', :action => 'list' |
|
70 | redirect_to :controller => 'main', :action => 'list' |
|
71 | return false |
|
71 | return false |
|
72 | end |
|
72 | end |
|
73 | end |
|
73 | end |
|
74 |
|
74 | ||
|
75 | end |
|
75 | end |
@@ -1,74 +1,75 | |||||
|
1 | require 'csv' |
|
1 | require 'csv' |
|
2 |
|
2 | ||
|
3 | class UserAdminController < ApplicationController |
|
3 | class UserAdminController < ApplicationController |
|
4 |
|
4 | ||
|
5 | include MailHelperMethods |
|
5 | include MailHelperMethods |
|
6 |
|
6 | ||
|
7 | before_filter :admin_authorization |
|
7 | before_filter :admin_authorization |
|
8 |
|
8 | ||
|
9 | # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) |
|
9 | # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) |
|
10 | verify :method => :post, :only => [ |
|
10 | verify :method => :post, :only => [ |
|
11 | :create, :create_from_list, |
|
11 | :create, :create_from_list, |
|
12 | :update, |
|
12 | :update, |
|
13 | :manage_contest, |
|
13 | :manage_contest, |
|
14 | :bulk_mail |
|
14 | :bulk_mail |
|
15 | ], |
|
15 | ], |
|
16 | :redirect_to => { :action => :list } |
|
16 | :redirect_to => { :action => :list } |
|
17 |
|
17 | ||
|
18 | def index |
|
18 | def index |
|
19 | @user_count = User.count |
|
19 | @user_count = User.count |
|
20 | if params[:page] == 'all' |
|
20 | if params[:page] == 'all' |
|
21 | @users = User.all |
|
21 | @users = User.all |
|
22 | @paginated = false |
|
22 | @paginated = false |
|
23 | else |
|
23 | else |
|
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 |
|
30 |
|
31 | ||
|
31 | def active |
|
32 | def active |
|
32 | sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago) |
|
33 | sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago) |
|
33 | @users = [] |
|
34 | @users = [] |
|
34 | sessions.each do |session| |
|
35 | sessions.each do |session| |
|
35 | if session.data[:user_id] |
|
36 | if session.data[:user_id] |
|
36 | @users << User.find(session.data[:user_id]) |
|
37 | @users << User.find(session.data[:user_id]) |
|
37 | end |
|
38 | end |
|
38 | end |
|
39 | end |
|
39 | end |
|
40 | end |
|
40 |
|
41 | ||
|
41 | def show |
|
42 | def show |
|
42 | @user = User.find(params[:id]) |
|
43 | @user = User.find(params[:id]) |
|
43 | end |
|
44 | end |
|
44 |
|
45 | ||
|
45 | def new |
|
46 | def new |
|
46 | @user = User.new |
|
47 | @user = User.new |
|
47 | end |
|
48 | end |
|
48 |
|
49 | ||
|
49 | def create |
|
50 | def create |
|
50 | @user = User.new(user_params) |
|
51 | @user = User.new(user_params) |
|
51 | @user.activated = true |
|
52 | @user.activated = true |
|
52 | if @user.save |
|
53 | if @user.save |
|
53 | flash[:notice] = 'User was successfully created.' |
|
54 | flash[:notice] = 'User was successfully created.' |
|
54 | redirect_to :action => 'index' |
|
55 | redirect_to :action => 'index' |
|
55 | else |
|
56 | else |
|
56 | render :action => 'new' |
|
57 | render :action => 'new' |
|
57 | end |
|
58 | end |
|
58 | end |
|
59 | end |
|
59 |
|
60 | ||
|
60 | def clear_last_ip |
|
61 | def clear_last_ip |
|
61 | @user = User.find(params[:id]) |
|
62 | @user = User.find(params[:id]) |
|
62 | @user.last_ip = nil |
|
63 | @user.last_ip = nil |
|
63 | @user.save |
|
64 | @user.save |
|
64 | redirect_to action: 'index', page: params[:page] |
|
65 | redirect_to action: 'index', page: params[:page] |
|
65 | end |
|
66 | end |
|
66 |
|
67 | ||
|
67 | def create_from_list |
|
68 | def create_from_list |
|
68 | lines = params[:user_list] |
|
69 | lines = params[:user_list] |
|
69 |
|
70 | ||
|
70 | note = [] |
|
71 | note = [] |
|
71 |
|
72 | ||
|
72 | lines.split("\n").each do |line| |
|
73 | lines.split("\n").each do |line| |
|
73 | items = line.chomp.split(',') |
|
74 | items = line.chomp.split(',') |
|
74 | if items.length>=2 |
|
75 | if items.length>=2 |
@@ -183,96 +184,97 | |||||
|
183 | #set up range from param |
|
184 | #set up range from param |
|
184 | since_id = params.fetch(:since_id, 0).to_i |
|
185 | since_id = params.fetch(:since_id, 0).to_i |
|
185 | until_id = params.fetch(:until_id, 0).to_i |
|
186 | until_id = params.fetch(:until_id, 0).to_i |
|
186 | @users.each do |u| |
|
187 | @users.each do |u| |
|
187 | ustat = Array.new |
|
188 | ustat = Array.new |
|
188 | ustat[0] = u |
|
189 | ustat[0] = u |
|
189 | @problems.each do |p| |
|
190 | @problems.each do |p| |
|
190 | max_points = 0 |
|
191 | max_points = 0 |
|
191 | Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub| |
|
192 | Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub| |
|
192 | max_points = sub.points if sub and sub.points and (sub.points > max_points) |
|
193 | max_points = sub.points if sub and sub.points and (sub.points > max_points) |
|
193 | end |
|
194 | end |
|
194 | ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)] |
|
195 | ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)] |
|
195 | end |
|
196 | end |
|
196 | @scorearray << ustat |
|
197 | @scorearray << ustat |
|
197 | end |
|
198 | end |
|
198 |
|
199 | ||
|
199 | if params[:commit] == 'download csv' then |
|
200 | if params[:commit] == 'download csv' then |
|
200 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
201 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
201 | send_data csv, filename: 'max_score.csv' |
|
202 | send_data csv, filename: 'max_score.csv' |
|
202 | else |
|
203 | else |
|
203 | render template: 'user_admin/user_stat' |
|
204 | render template: 'user_admin/user_stat' |
|
204 | end |
|
205 | end |
|
205 | end |
|
206 | end |
|
206 |
|
207 | ||
|
207 | def import |
|
208 | def import |
|
208 | if params[:file]=='' |
|
209 | if params[:file]=='' |
|
209 | flash[:notice] = 'Error importing no file' |
|
210 | flash[:notice] = 'Error importing no file' |
|
210 | redirect_to :action => 'index' and return |
|
211 | redirect_to :action => 'index' and return |
|
211 | end |
|
212 | end |
|
212 | import_from_file(params[:file]) |
|
213 | import_from_file(params[:file]) |
|
213 | end |
|
214 | end |
|
214 |
|
215 | ||
|
215 | def random_all_passwords |
|
216 | def random_all_passwords |
|
216 | users = User.all |
|
217 | users = User.all |
|
217 | @prefix = params[:prefix] || '' |
|
218 | @prefix = params[:prefix] || '' |
|
218 | @non_admin_users = User.find_non_admin_with_prefix(@prefix) |
|
219 | @non_admin_users = User.find_non_admin_with_prefix(@prefix) |
|
219 | @changed = false |
|
220 | @changed = false |
|
220 | if request.request_method == 'POST' |
|
221 | if request.request_method == 'POST' |
|
221 | @non_admin_users.each do |user| |
|
222 | @non_admin_users.each do |user| |
|
222 | password = random_password |
|
223 | password = random_password |
|
223 | user.password = password |
|
224 | user.password = password |
|
224 | user.password_confirmation = password |
|
225 | user.password_confirmation = password |
|
225 | user.save |
|
226 | user.save |
|
226 | end |
|
227 | end |
|
227 | @changed = true |
|
228 | @changed = true |
|
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 |
|
234 | @contest, @users = find_contest_and_user_from_contest_id(params[:id]) |
|
236 | @contest, @users = find_contest_and_user_from_contest_id(params[:id]) |
|
235 | @contests = Contest.enabled |
|
237 | @contests = Contest.enabled |
|
236 | end |
|
238 | end |
|
237 |
|
239 | ||
|
238 | def assign_from_list |
|
240 | def assign_from_list |
|
239 | contest_id = params[:users_contest_id] |
|
241 | contest_id = params[:users_contest_id] |
|
240 | org_contest, users = find_contest_and_user_from_contest_id(contest_id) |
|
242 | org_contest, users = find_contest_and_user_from_contest_id(contest_id) |
|
241 | contest = Contest.find(params[:new_contest][:id]) |
|
243 | contest = Contest.find(params[:new_contest][:id]) |
|
242 | if !contest |
|
244 | if !contest |
|
243 | flash[:notice] = 'Error: no contest' |
|
245 | flash[:notice] = 'Error: no contest' |
|
244 | redirect_to :action => 'contests', :id =>contest_id |
|
246 | redirect_to :action => 'contests', :id =>contest_id |
|
245 | end |
|
247 | end |
|
246 |
|
248 | ||
|
247 | note = [] |
|
249 | note = [] |
|
248 | users.each do |u| |
|
250 | users.each do |u| |
|
249 | u.contests = [contest] |
|
251 | u.contests = [contest] |
|
250 | note << u.login |
|
252 | note << u.login |
|
251 | end |
|
253 | end |
|
252 | flash[:notice] = 'User(s) ' + note.join(', ') + |
|
254 | flash[:notice] = 'User(s) ' + note.join(', ') + |
|
253 | " were successfully reassigned to #{contest.title}." |
|
255 | " were successfully reassigned to #{contest.title}." |
|
254 | redirect_to :action => 'contests', :id =>contest.id |
|
256 | redirect_to :action => 'contests', :id =>contest.id |
|
255 | end |
|
257 | end |
|
256 |
|
258 | ||
|
257 | def add_to_contest |
|
259 | def add_to_contest |
|
258 | user = User.find(params[:id]) |
|
260 | user = User.find(params[:id]) |
|
259 | contest = Contest.find(params[:contest_id]) |
|
261 | contest = Contest.find(params[:contest_id]) |
|
260 | if user and contest |
|
262 | if user and contest |
|
261 | user.contests << contest |
|
263 | user.contests << contest |
|
262 | end |
|
264 | end |
|
263 | redirect_to :action => 'index' |
|
265 | redirect_to :action => 'index' |
|
264 | end |
|
266 | end |
|
265 |
|
267 | ||
|
266 | def remove_from_contest |
|
268 | def remove_from_contest |
|
267 | user = User.find(params[:id]) |
|
269 | user = User.find(params[:id]) |
|
268 | contest = Contest.find(params[:contest_id]) |
|
270 | contest = Contest.find(params[:contest_id]) |
|
269 | if user and contest |
|
271 | if user and contest |
|
270 | user.contests.delete(contest) |
|
272 | user.contests.delete(contest) |
|
271 | end |
|
273 | end |
|
272 | redirect_to :action => 'index' |
|
274 | redirect_to :action => 'index' |
|
273 | end |
|
275 | end |
|
274 |
|
276 | ||
|
275 | def contest_management |
|
277 | def contest_management |
|
276 | end |
|
278 | end |
|
277 |
|
279 | ||
|
278 | def manage_contest |
|
280 | def manage_contest |
@@ -366,122 +368,139 | |||||
|
366 | user.roles.delete(admin_role) |
|
368 | user.roles.delete(admin_role) |
|
367 | flash[:notice] = 'User permission revoked' |
|
369 | flash[:notice] = 'User permission revoked' |
|
368 | redirect_to :action => 'admin' |
|
370 | redirect_to :action => 'admin' |
|
369 | end |
|
371 | end |
|
370 |
|
372 | ||
|
371 | # mass mailing |
|
373 | # mass mailing |
|
372 |
|
374 | ||
|
373 | def mass_mailing |
|
375 | def mass_mailing |
|
374 | end |
|
376 | end |
|
375 |
|
377 | ||
|
376 | def bulk_mail |
|
378 | def bulk_mail |
|
377 | lines = params[:login_list] |
|
379 | lines = params[:login_list] |
|
378 | if !lines or lines.blank? |
|
380 | if !lines or lines.blank? |
|
379 | flash[:notice] = 'You entered an empty list.' |
|
381 | flash[:notice] = 'You entered an empty list.' |
|
380 | redirect_to :action => 'mass_mailing' and return |
|
382 | redirect_to :action => 'mass_mailing' and return |
|
381 | end |
|
383 | end |
|
382 |
|
384 | ||
|
383 | mail_subject = params[:subject] |
|
385 | mail_subject = params[:subject] |
|
384 | if !mail_subject or mail_subject.blank? |
|
386 | if !mail_subject or mail_subject.blank? |
|
385 | flash[:notice] = 'You entered an empty mail subject.' |
|
387 | flash[:notice] = 'You entered an empty mail subject.' |
|
386 | redirect_to :action => 'mass_mailing' and return |
|
388 | redirect_to :action => 'mass_mailing' and return |
|
387 | end |
|
389 | end |
|
388 |
|
390 | ||
|
389 | mail_body = params[:email_body] |
|
391 | mail_body = params[:email_body] |
|
390 | if !mail_body or mail_body.blank? |
|
392 | if !mail_body or mail_body.blank? |
|
391 | flash[:notice] = 'You entered an empty mail body.' |
|
393 | flash[:notice] = 'You entered an empty mail body.' |
|
392 | redirect_to :action => 'mass_mailing' and return |
|
394 | redirect_to :action => 'mass_mailing' and return |
|
393 | end |
|
395 | end |
|
394 |
|
396 | ||
|
395 | note = [] |
|
397 | note = [] |
|
396 | users = [] |
|
398 | users = [] |
|
397 | lines.split("\n").each do |line| |
|
399 | lines.split("\n").each do |line| |
|
398 | user = User.find_by_login(line.chomp) |
|
400 | user = User.find_by_login(line.chomp) |
|
399 | if user |
|
401 | if user |
|
400 | send_mail(user.email, mail_subject, mail_body) |
|
402 | send_mail(user.email, mail_subject, mail_body) |
|
401 | note << user.login |
|
403 | note << user.login |
|
402 | end |
|
404 | end |
|
403 | end |
|
405 | end |
|
404 |
|
406 | ||
|
405 | flash[:notice] = 'User(s) ' + note.join(', ') + |
|
407 | flash[:notice] = 'User(s) ' + note.join(', ') + |
|
406 | ' were successfully modified. ' |
|
408 | ' were successfully modified. ' |
|
407 | redirect_to :action => 'mass_mailing' |
|
409 | redirect_to :action => 'mass_mailing' |
|
408 | end |
|
410 | end |
|
409 |
|
411 | ||
|
410 | #bulk manage |
|
412 | #bulk manage |
|
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' |
|
418 | @users = nil |
|
420 | @users = nil |
|
419 | end |
|
421 | end |
|
420 |
|
422 | ||
|
421 | if params[:commit] |
|
423 | if params[:commit] |
|
422 | @action = {} |
|
424 | @action = {} |
|
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" |
|
429 | if @action[:set_enable] |
|
433 | if @action[:set_enable] |
|
430 | @users.update_all(enabled: @action[:enabled]) |
|
434 | @users.update_all(enabled: @action[:enabled]) |
|
431 | end |
|
435 | end |
|
432 | if @action[:gen_password] |
|
436 | if @action[:gen_password] |
|
433 | @users.each do |u| |
|
437 | @users.each do |u| |
|
434 | password = random_password |
|
438 | password = random_password |
|
435 | u.password = password |
|
439 | u.password = password |
|
436 | u.password_confirmation = password |
|
440 | u.password_confirmation = password |
|
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 | ||
|
443 | protected |
|
462 | protected |
|
444 |
|
463 | ||
|
445 | def random_password(length=5) |
|
464 | def random_password(length=5) |
|
446 | chars = 'abcdefghijkmnopqrstuvwxyz23456789' |
|
465 | chars = 'abcdefghijkmnopqrstuvwxyz23456789' |
|
447 | newpass = "" |
|
466 | newpass = "" |
|
448 | length.times { newpass << chars[rand(chars.size-1)] } |
|
467 | length.times { newpass << chars[rand(chars.size-1)] } |
|
449 | return newpass |
|
468 | return newpass |
|
450 | end |
|
469 | end |
|
451 |
|
470 | ||
|
452 | def import_from_file(f) |
|
471 | def import_from_file(f) |
|
453 | data_hash = YAML.load(f) |
|
472 | data_hash = YAML.load(f) |
|
454 | @import_log = "" |
|
473 | @import_log = "" |
|
455 |
|
474 | ||
|
456 | country_data = data_hash[:countries] |
|
475 | country_data = data_hash[:countries] |
|
457 | site_data = data_hash[:sites] |
|
476 | site_data = data_hash[:sites] |
|
458 | user_data = data_hash[:users] |
|
477 | user_data = data_hash[:users] |
|
459 |
|
478 | ||
|
460 | # import country |
|
479 | # import country |
|
461 | countries = {} |
|
480 | countries = {} |
|
462 | country_data.each_pair do |id,country| |
|
481 | country_data.each_pair do |id,country| |
|
463 | c = Country.find_by_name(country[:name]) |
|
482 | c = Country.find_by_name(country[:name]) |
|
464 | if c!=nil |
|
483 | if c!=nil |
|
465 | countries[id] = c |
|
484 | countries[id] = c |
|
466 | @import_log << "Found #{country[:name]}\n" |
|
485 | @import_log << "Found #{country[:name]}\n" |
|
467 | else |
|
486 | else |
|
468 | countries[id] = Country.new(:name => country[:name]) |
|
487 | countries[id] = Country.new(:name => country[:name]) |
|
469 | countries[id].save |
|
488 | countries[id].save |
|
470 | @import_log << "Created #{country[:name]}\n" |
|
489 | @import_log << "Created #{country[:name]}\n" |
|
471 | end |
|
490 | end |
|
472 | end |
|
491 | end |
|
473 |
|
492 | ||
|
474 | # import sites |
|
493 | # import sites |
|
475 | sites = {} |
|
494 | sites = {} |
|
476 | site_data.each_pair do |id,site| |
|
495 | site_data.each_pair do |id,site| |
|
477 | s = Site.find_by_name(site[:name]) |
|
496 | s = Site.find_by_name(site[:name]) |
|
478 | if s!=nil |
|
497 | if s!=nil |
|
479 | @import_log << "Found #{site[:name]}\n" |
|
498 | @import_log << "Found #{site[:name]}\n" |
|
480 | else |
|
499 | else |
|
481 | s = Site.new(:name => site[:name]) |
|
500 | s = Site.new(:name => site[:name]) |
|
482 | @import_log << "Created #{site[:name]}\n" |
|
501 | @import_log << "Created #{site[:name]}\n" |
|
483 | end |
|
502 | end |
|
484 | s.password = site[:password] |
|
503 | s.password = site[:password] |
|
485 | s.country = countries[site[:country_id]] |
|
504 | s.country = countries[site[:country_id]] |
|
486 | s.save |
|
505 | s.save |
|
487 | sites[id] = s |
|
506 | sites[id] = s |
@@ -40,111 +40,114 | |||||
|
40 |
|
40 | ||
|
41 | if (user!=nil) and (session[:admin]) |
|
41 | if (user!=nil) and (session[:admin]) |
|
42 | # admin menu |
|
42 | # admin menu |
|
43 | menu_items << "<b>Administrative task:</b> " |
|
43 | menu_items << "<b>Administrative task:</b> " |
|
44 | append_to menu_items, '[Announcements]', 'announcements', 'index' |
|
44 | append_to menu_items, '[Announcements]', 'announcements', 'index' |
|
45 | append_to menu_items, '[Msg console]', 'messages', 'console' |
|
45 | append_to menu_items, '[Msg console]', 'messages', 'console' |
|
46 | append_to menu_items, '[Problems]', 'problems', 'index' |
|
46 | append_to menu_items, '[Problems]', 'problems', 'index' |
|
47 | append_to menu_items, '[Users]', 'user_admin', 'index' |
|
47 | append_to menu_items, '[Users]', 'user_admin', 'index' |
|
48 | append_to menu_items, '[Results]', 'user_admin', 'user_stat' |
|
48 | append_to menu_items, '[Results]', 'user_admin', 'user_stat' |
|
49 | append_to menu_items, '[Report]', 'report', 'multiple_login' |
|
49 | append_to menu_items, '[Report]', 'report', 'multiple_login' |
|
50 | append_to menu_items, '[Graders]', 'graders', 'list' |
|
50 | append_to menu_items, '[Graders]', 'graders', 'list' |
|
51 | append_to menu_items, '[Contests]', 'contest_management', 'index' |
|
51 | append_to menu_items, '[Contests]', 'contest_management', 'index' |
|
52 | append_to menu_items, '[Sites]', 'sites', 'index' |
|
52 | append_to menu_items, '[Sites]', 'sites', 'index' |
|
53 | append_to menu_items, '[System config]', 'configurations', 'index' |
|
53 | append_to menu_items, '[System config]', 'configurations', 'index' |
|
54 | menu_items << "<br/>" |
|
54 | menu_items << "<br/>" |
|
55 | end |
|
55 | end |
|
56 |
|
56 | ||
|
57 | # main page |
|
57 | # main page |
|
58 | append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list' |
|
58 | append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list' |
|
59 | append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list' |
|
59 | append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list' |
|
60 |
|
60 | ||
|
61 | if (user!=nil) and (GraderConfiguration.show_tasks_to?(user)) |
|
61 | if (user!=nil) and (GraderConfiguration.show_tasks_to?(user)) |
|
62 | append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list' |
|
62 | append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list' |
|
63 | append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission' |
|
63 | append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission' |
|
64 | append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index' |
|
64 | append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index' |
|
65 | end |
|
65 | end |
|
66 |
|
66 | ||
|
67 | if GraderConfiguration['right.user_hall_of_fame'] |
|
67 | if GraderConfiguration['right.user_hall_of_fame'] |
|
68 | append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof' |
|
68 | append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof' |
|
69 | end |
|
69 | end |
|
70 | append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help' |
|
70 | append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help' |
|
71 |
|
71 | ||
|
72 | if GraderConfiguration['system.user_setting_enabled'] |
|
72 | if GraderConfiguration['system.user_setting_enabled'] |
|
73 | append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index' |
|
73 | append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index' |
|
74 | end |
|
74 | end |
|
75 | append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login' |
|
75 | append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login' |
|
76 |
|
76 | ||
|
77 | menu_items.html_safe |
|
77 | menu_items.html_safe |
|
78 | end |
|
78 | end |
|
79 |
|
79 | ||
|
80 | def append_to(option,label, controller, action) |
|
80 | def append_to(option,label, controller, action) |
|
81 | option << ' ' if option!='' |
|
81 | option << ' ' if option!='' |
|
82 | option << link_to_unless_current(label, |
|
82 | option << link_to_unless_current(label, |
|
83 | :controller => controller, |
|
83 | :controller => controller, |
|
84 | :action => action) |
|
84 | :action => action) |
|
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 |
|
96 |
|
95 | ||
|
97 | def format_short_duration(duration) |
|
96 | def format_short_duration(duration) |
|
98 | return '' if duration==nil |
|
97 | return '' if duration==nil |
|
99 | d = duration.to_f |
|
98 | d = duration.to_f |
|
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) |
|
106 | rescue |
|
109 | rescue |
|
107 | nil |
|
110 | nil |
|
108 | end |
|
111 | end |
|
109 | end |
|
112 | end |
|
110 |
|
113 | ||
|
111 | def toggle_button(on,toggle_url,id, option={}) |
|
114 | def toggle_button(on,toggle_url,id, option={}) |
|
112 | btn_size = option[:size] || 'btn-xs' |
|
115 | btn_size = option[:size] || 'btn-xs' |
|
113 | link_to (on ? "Yes" : "No"), toggle_url, |
|
116 | link_to (on ? "Yes" : "No"), toggle_url, |
|
114 | {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle", |
|
117 | {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle", |
|
115 | id: id, |
|
118 | id: id, |
|
116 | data: {remote: true, method: 'get'}} |
|
119 | data: {remote: true, method: 'get'}} |
|
117 | end |
|
120 | end |
|
118 |
|
121 | ||
|
119 | def get_ace_mode(language) |
|
122 | def get_ace_mode(language) |
|
120 | # return ace mode string from Language |
|
123 | # return ace mode string from Language |
|
121 |
|
124 | ||
|
122 | case language.pretty_name |
|
125 | case language.pretty_name |
|
123 | when 'Pascal' |
|
126 | when 'Pascal' |
|
124 | 'ace/mode/pascal' |
|
127 | 'ace/mode/pascal' |
|
125 | when 'C++','C' |
|
128 | when 'C++','C' |
|
126 | 'ace/mode/c_cpp' |
|
129 | 'ace/mode/c_cpp' |
|
127 | when 'Ruby' |
|
130 | when 'Ruby' |
|
128 | 'ace/mode/ruby' |
|
131 | 'ace/mode/ruby' |
|
129 | when 'Python' |
|
132 | when 'Python' |
|
130 | 'ace/mode/python' |
|
133 | 'ace/mode/python' |
|
131 | when 'Java' |
|
134 | when 'Java' |
|
132 | 'ace/mode/java' |
|
135 | 'ace/mode/java' |
|
133 | else |
|
136 | else |
|
134 | 'ace/mode/c_cpp' |
|
137 | 'ace/mode/c_cpp' |
|
135 | end |
|
138 | end |
|
136 | end |
|
139 | end |
|
137 |
|
140 | ||
|
138 |
|
141 | ||
|
139 | def user_title_bar(user) |
|
142 | def user_title_bar(user) |
|
140 | header = '' |
|
143 | header = '' |
|
141 | time_left = '' |
|
144 | time_left = '' |
|
142 |
|
145 | ||
|
143 | # |
|
146 | # |
|
144 | # if the contest is over |
|
147 | # if the contest is over |
|
145 | if GraderConfiguration.time_limit_mode? |
|
148 | if GraderConfiguration.time_limit_mode? |
|
146 | if user.contest_finished? |
|
149 | if user.contest_finished? |
|
147 | header = <<CONTEST_OVER |
|
150 | header = <<CONTEST_OVER |
|
148 | <tr><td colspan="2" align="center"> |
|
151 | <tr><td colspan="2" align="center"> |
|
149 | <span class="contest-over-msg">THE CONTEST IS OVER</span> |
|
152 | <span class="contest-over-msg">THE CONTEST IS OVER</span> |
|
150 | </td></tr> |
|
153 | </td></tr> |
@@ -155,67 +158,67 | |||||
|
155 | else |
|
158 | else |
|
156 | time_left = " " + (t 'title_bar.remaining_time') + |
|
159 | time_left = " " + (t 'title_bar.remaining_time') + |
|
157 | " #{format_short_duration(user.contest_time_left)}" |
|
160 | " #{format_short_duration(user.contest_time_left)}" |
|
158 | end |
|
161 | end |
|
159 | end |
|
162 | end |
|
160 |
|
163 | ||
|
161 | # |
|
164 | # |
|
162 | # if the contest is in the anaysis mode |
|
165 | # if the contest is in the anaysis mode |
|
163 | if GraderConfiguration.analysis_mode? |
|
166 | if GraderConfiguration.analysis_mode? |
|
164 | header = <<ANALYSISMODE |
|
167 | header = <<ANALYSISMODE |
|
165 | <tr><td colspan="2" align="center"> |
|
168 | <tr><td colspan="2" align="center"> |
|
166 | <span class="contest-over-msg">ANALYSIS MODE</span> |
|
169 | <span class="contest-over-msg">ANALYSIS MODE</span> |
|
167 | </td></tr> |
|
170 | </td></tr> |
|
168 | ANALYSISMODE |
|
171 | ANALYSISMODE |
|
169 | end |
|
172 | end |
|
170 |
|
173 | ||
|
171 | contest_name = GraderConfiguration['contest.name'] |
|
174 | contest_name = GraderConfiguration['contest.name'] |
|
172 |
|
175 | ||
|
173 | # |
|
176 | # |
|
174 | # build real title bar |
|
177 | # build real title bar |
|
175 | result = <<TITLEBAR |
|
178 | result = <<TITLEBAR |
|
176 | <div class="title"> |
|
179 | <div class="title"> |
|
177 | <table> |
|
180 | <table> |
|
178 | #{header} |
|
181 | #{header} |
|
179 | <tr> |
|
182 | <tr> |
|
180 | <td class="left-col"> |
|
183 | <td class="left-col"> |
|
181 | #{user.full_name}<br/> |
|
184 | #{user.full_name}<br/> |
|
182 | #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)} |
|
185 | #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)} |
|
183 | #{time_left} |
|
186 | #{time_left} |
|
184 | <br/> |
|
187 | <br/> |
|
185 | </td> |
|
188 | </td> |
|
186 | <td class="right-col">#{contest_name}</td> |
|
189 | <td class="right-col">#{contest_name}</td> |
|
187 | </tr> |
|
190 | </tr> |
|
188 | </table> |
|
191 | </table> |
|
189 | </div> |
|
192 | </div> |
|
190 | TITLEBAR |
|
193 | TITLEBAR |
|
191 | result.html_safe |
|
194 | result.html_safe |
|
192 | end |
|
195 | end |
|
193 |
|
196 | ||
|
194 | def markdown(text) |
|
197 | def markdown(text) |
|
195 | markdown = RDiscount.new(text) |
|
198 | markdown = RDiscount.new(text) |
|
196 | markdown.to_html.html_safe |
|
199 | markdown.to_html.html_safe |
|
197 | end |
|
200 | end |
|
198 |
|
201 | ||
|
199 |
|
202 | ||
|
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 | ||
|
207 | def bootstrap_class_for(flash_type) |
|
210 | def bootstrap_class_for(flash_type) |
|
208 | BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s) |
|
211 | BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s) |
|
209 | end |
|
212 | end |
|
210 |
|
213 | ||
|
211 | def flash_messages |
|
214 | def flash_messages |
|
212 | flash.each do |msg_type, message| |
|
215 | flash.each do |msg_type, message| |
|
213 | concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do |
|
216 | concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do |
|
214 | concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' }) |
|
217 | concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' }) |
|
215 | concat message |
|
218 | concat message |
|
216 | end) |
|
219 | end) |
|
217 | end |
|
220 | end |
|
218 | nil |
|
221 | nil |
|
219 | end |
|
222 | end |
|
220 |
|
223 | ||
|
221 | end |
|
224 | end |
@@ -1,62 +1,63 | |||||
|
1 | require 'yaml' |
|
1 | require 'yaml' |
|
2 |
|
2 | ||
|
3 | # |
|
3 | # |
|
4 | # This class also contains various login of the system. |
|
4 | # This class also contains various login of the system. |
|
5 | # |
|
5 | # |
|
6 | class GraderConfiguration < ActiveRecord::Base |
|
6 | class GraderConfiguration < ActiveRecord::Base |
|
7 |
|
7 | ||
|
8 | SYSTEM_MODE_CONF_KEY = 'system.mode' |
|
8 | SYSTEM_MODE_CONF_KEY = 'system.mode' |
|
9 | TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout' |
|
9 | TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout' |
|
10 | MULTICONTESTS_KEY = 'system.multicontests' |
|
10 | MULTICONTESTS_KEY = 'system.multicontests' |
|
11 | CONTEST_TIME_LIMIT_KEY = 'contest.time_limit' |
|
11 | CONTEST_TIME_LIMIT_KEY = 'contest.time_limit' |
|
12 | MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login' |
|
12 | MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login' |
|
13 | VIEW_TESTCASE = 'right.view_testcase' |
|
13 | VIEW_TESTCASE = 'right.view_testcase' |
|
14 | SINGLE_USER_KEY = 'system.single_user_mode' |
|
14 | SINGLE_USER_KEY = 'system.single_user_mode' |
|
|
15 | + SYSTEM_USE_PROBLEM_GROUP = 'system.use_problem_group' | ||
|
15 |
|
16 | ||
|
16 | cattr_accessor :config_cache |
|
17 | cattr_accessor :config_cache |
|
17 | cattr_accessor :task_grading_info_cache |
|
18 | cattr_accessor :task_grading_info_cache |
|
18 | cattr_accessor :contest_time_str |
|
19 | cattr_accessor :contest_time_str |
|
19 | cattr_accessor :contest_time |
|
20 | cattr_accessor :contest_time |
|
20 |
|
21 | ||
|
21 | GraderConfiguration.config_cache = nil |
|
22 | GraderConfiguration.config_cache = nil |
|
22 | GraderConfiguration.task_grading_info_cache = nil |
|
23 | GraderConfiguration.task_grading_info_cache = nil |
|
23 |
|
24 | ||
|
24 | def self.config_cached? |
|
25 | def self.config_cached? |
|
25 | (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED) |
|
26 | (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED) |
|
26 | end |
|
27 | end |
|
27 |
|
28 | ||
|
28 | def self.get(key) |
|
29 | def self.get(key) |
|
29 | if GraderConfiguration.config_cached? |
|
30 | if GraderConfiguration.config_cached? |
|
30 | if GraderConfiguration.config_cache == nil |
|
31 | if GraderConfiguration.config_cache == nil |
|
31 | self.read_config |
|
32 | self.read_config |
|
32 | end |
|
33 | end |
|
33 | return GraderConfiguration.config_cache[key] |
|
34 | return GraderConfiguration.config_cache[key] |
|
34 | else |
|
35 | else |
|
35 | return GraderConfiguration.read_one_key(key) |
|
36 | return GraderConfiguration.read_one_key(key) |
|
36 | end |
|
37 | end |
|
37 | end |
|
38 | end |
|
38 |
|
39 | ||
|
39 | def self.[](key) |
|
40 | def self.[](key) |
|
40 | self.get(key) |
|
41 | self.get(key) |
|
41 | end |
|
42 | end |
|
42 |
|
43 | ||
|
43 | def self.reload |
|
44 | def self.reload |
|
44 | self.read_config |
|
45 | self.read_config |
|
45 | end |
|
46 | end |
|
46 |
|
47 | ||
|
47 | def self.clear |
|
48 | def self.clear |
|
48 | GraderConfiguration.config_cache = nil |
|
49 | GraderConfiguration.config_cache = nil |
|
49 | end |
|
50 | end |
|
50 |
|
51 | ||
|
51 | # |
|
52 | # |
|
52 | # View decision |
|
53 | # View decision |
|
53 | # |
|
54 | # |
|
54 | def self.show_submitbox_to?(user) |
|
55 | def self.show_submitbox_to?(user) |
|
55 | mode = get(SYSTEM_MODE_CONF_KEY) |
|
56 | mode = get(SYSTEM_MODE_CONF_KEY) |
|
56 | return false if mode=='analysis' |
|
57 | return false if mode=='analysis' |
|
57 | if (mode=='contest') |
|
58 | if (mode=='contest') |
|
58 | return false if (user.site!=nil) and |
|
59 | return false if (user.site!=nil) and |
|
59 | ((user.site.started!=true) or (user.site.finished?)) |
|
60 | ((user.site.started!=true) or (user.site.finished?)) |
|
60 | end |
|
61 | end |
|
61 | return true |
|
62 | return true |
|
62 | end |
|
63 | end |
@@ -74,96 +75,100 | |||||
|
74 |
|
75 | ||
|
75 | def self.show_testcase |
|
76 | def self.show_testcase |
|
76 | return get(VIEW_TESTCASE) |
|
77 | return get(VIEW_TESTCASE) |
|
77 | end |
|
78 | end |
|
78 |
|
79 | ||
|
79 | def self.allow_test_request(user) |
|
80 | def self.allow_test_request(user) |
|
80 | mode = get(SYSTEM_MODE_CONF_KEY) |
|
81 | mode = get(SYSTEM_MODE_CONF_KEY) |
|
81 | early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY) |
|
82 | early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY) |
|
82 | if (mode=='contest') |
|
83 | if (mode=='contest') |
|
83 | return false if ((user.site!=nil) and |
|
84 | return false if ((user.site!=nil) and |
|
84 | ((user.site.started!=true) or |
|
85 | ((user.site.started!=true) or |
|
85 | (early_timeout and (user.site.time_left < 30.minutes)))) |
|
86 | (early_timeout and (user.site.time_left < 30.minutes)))) |
|
86 | end |
|
87 | end |
|
87 | return false if mode=='analysis' |
|
88 | return false if mode=='analysis' |
|
88 | return true |
|
89 | return true |
|
89 | end |
|
90 | end |
|
90 |
|
91 | ||
|
91 | def self.task_grading_info |
|
92 | def self.task_grading_info |
|
92 | if GraderConfiguration.task_grading_info_cache==nil |
|
93 | if GraderConfiguration.task_grading_info_cache==nil |
|
93 | read_grading_info |
|
94 | read_grading_info |
|
94 | end |
|
95 | end |
|
95 | return GraderConfiguration.task_grading_info_cache |
|
96 | return GraderConfiguration.task_grading_info_cache |
|
96 | end |
|
97 | end |
|
97 |
|
98 | ||
|
98 | def self.standard_mode? |
|
99 | def self.standard_mode? |
|
99 | return get(SYSTEM_MODE_CONF_KEY) == 'standard' |
|
100 | return get(SYSTEM_MODE_CONF_KEY) == 'standard' |
|
100 | end |
|
101 | end |
|
101 |
|
102 | ||
|
102 | def self.contest_mode? |
|
103 | def self.contest_mode? |
|
103 | return get(SYSTEM_MODE_CONF_KEY) == 'contest' |
|
104 | return get(SYSTEM_MODE_CONF_KEY) == 'contest' |
|
104 | end |
|
105 | end |
|
105 |
|
106 | ||
|
106 | def self.indv_contest_mode? |
|
107 | def self.indv_contest_mode? |
|
107 | return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest' |
|
108 | return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest' |
|
108 | end |
|
109 | end |
|
109 |
|
110 | ||
|
110 | def self.multicontests? |
|
111 | def self.multicontests? |
|
111 | return get(MULTICONTESTS_KEY) == true |
|
112 | return get(MULTICONTESTS_KEY) == true |
|
112 | end |
|
113 | end |
|
113 |
|
114 | ||
|
114 | def self.time_limit_mode? |
|
115 | def self.time_limit_mode? |
|
115 | mode = get(SYSTEM_MODE_CONF_KEY) |
|
116 | mode = get(SYSTEM_MODE_CONF_KEY) |
|
116 | return ((mode == 'contest') or (mode == 'indv-contest')) |
|
117 | return ((mode == 'contest') or (mode == 'indv-contest')) |
|
117 | end |
|
118 | end |
|
118 |
|
119 | ||
|
119 | def self.analysis_mode? |
|
120 | def self.analysis_mode? |
|
120 | return get(SYSTEM_MODE_CONF_KEY) == 'analysis' |
|
121 | return get(SYSTEM_MODE_CONF_KEY) == 'analysis' |
|
121 | end |
|
122 | end |
|
|
123 | + | ||
|
|
124 | + def self.use_problem_group? | ||
|
|
125 | + return get(SYSTEM_USE_PROBLEM_GROUP) | ||
|
|
126 | + end | ||
|
122 |
|
127 | ||
|
123 | def self.contest_time_limit |
|
128 | def self.contest_time_limit |
|
124 | contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY] |
|
129 | contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY] |
|
125 |
|
130 | ||
|
126 | if not defined? GraderConfiguration.contest_time_str |
|
131 | if not defined? GraderConfiguration.contest_time_str |
|
127 | GraderConfiguration.contest_time_str = nil |
|
132 | GraderConfiguration.contest_time_str = nil |
|
128 | end |
|
133 | end |
|
129 |
|
134 | ||
|
130 | if GraderConfiguration.contest_time_str != contest_time_str |
|
135 | if GraderConfiguration.contest_time_str != contest_time_str |
|
131 | GraderConfiguration.contest_time_str = contest_time_str |
|
136 | GraderConfiguration.contest_time_str = contest_time_str |
|
132 | if tmatch = /(\d+):(\d+)/.match(contest_time_str) |
|
137 | if tmatch = /(\d+):(\d+)/.match(contest_time_str) |
|
133 | h = tmatch[1].to_i |
|
138 | h = tmatch[1].to_i |
|
134 | m = tmatch[2].to_i |
|
139 | m = tmatch[2].to_i |
|
135 |
|
140 | ||
|
136 | GraderConfiguration.contest_time = h.hour + m.minute |
|
141 | GraderConfiguration.contest_time = h.hour + m.minute |
|
137 | else |
|
142 | else |
|
138 | GraderConfiguration.contest_time = nil |
|
143 | GraderConfiguration.contest_time = nil |
|
139 | end |
|
144 | end |
|
140 | end |
|
145 | end |
|
141 | return GraderConfiguration.contest_time |
|
146 | return GraderConfiguration.contest_time |
|
142 | end |
|
147 | end |
|
143 |
|
148 | ||
|
144 | protected |
|
149 | protected |
|
145 |
|
150 | ||
|
146 | def self.convert_type(val,type) |
|
151 | def self.convert_type(val,type) |
|
147 | case type |
|
152 | case type |
|
148 | when 'string' |
|
153 | when 'string' |
|
149 | return val |
|
154 | return val |
|
150 |
|
155 | ||
|
151 | when 'integer' |
|
156 | when 'integer' |
|
152 | return val.to_i |
|
157 | return val.to_i |
|
153 |
|
158 | ||
|
154 | when 'boolean' |
|
159 | when 'boolean' |
|
155 | return (val=='true') |
|
160 | return (val=='true') |
|
156 | end |
|
161 | end |
|
157 | end |
|
162 | end |
|
158 |
|
163 | ||
|
159 | def self.read_config |
|
164 | def self.read_config |
|
160 | GraderConfiguration.config_cache = {} |
|
165 | GraderConfiguration.config_cache = {} |
|
161 | GraderConfiguration.all.each do |conf| |
|
166 | GraderConfiguration.all.each do |conf| |
|
162 | key = conf.key |
|
167 | key = conf.key |
|
163 | val = conf.value |
|
168 | val = conf.value |
|
164 | GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type) |
|
169 | GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type) |
|
165 | end |
|
170 | end |
|
166 | end |
|
171 | end |
|
167 |
|
172 | ||
|
168 | def self.read_one_key(key) |
|
173 | def self.read_one_key(key) |
|
169 | conf = GraderConfiguration.find_by_key(key) |
|
174 | conf = GraderConfiguration.find_by_key(key) |
@@ -1,52 +1,60 | |||||
|
1 | class Problem < ActiveRecord::Base |
|
1 | class Problem < ActiveRecord::Base |
|
2 |
|
2 | ||
|
3 | belongs_to :description |
|
3 | belongs_to :description |
|
4 | has_and_belongs_to_many :contests, :uniq => true |
|
4 | has_and_belongs_to_many :contests, :uniq => true |
|
|
5 | + | ||
|
|
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 | ||
|
8 | validates_presence_of :name |
|
16 | validates_presence_of :name |
|
9 | validates_format_of :name, :with => /\A\w+\z/ |
|
17 | validates_format_of :name, :with => /\A\w+\z/ |
|
10 | validates_presence_of :full_name |
|
18 | validates_presence_of :full_name |
|
11 |
|
19 | ||
|
12 | scope :available, -> { where(available: true) } |
|
20 | scope :available, -> { where(available: true) } |
|
13 |
|
21 | ||
|
14 | DEFAULT_TIME_LIMIT = 1 |
|
22 | DEFAULT_TIME_LIMIT = 1 |
|
15 | DEFAULT_MEMORY_LIMIT = 32 |
|
23 | DEFAULT_MEMORY_LIMIT = 32 |
|
16 |
|
24 | ||
|
17 | def self.available_problems |
|
25 | def self.available_problems |
|
18 | available.order(date_added: :desc).order(:name) |
|
26 | available.order(date_added: :desc).order(:name) |
|
19 | #Problem.available.all(:order => "date_added DESC, name ASC") |
|
27 | #Problem.available.all(:order => "date_added DESC, name ASC") |
|
20 | end |
|
28 | end |
|
21 |
|
29 | ||
|
22 | def self.create_from_import_form_params(params, old_problem=nil) |
|
30 | def self.create_from_import_form_params(params, old_problem=nil) |
|
23 | org_problem = old_problem || Problem.new |
|
31 | org_problem = old_problem || Problem.new |
|
24 | import_params, problem = Problem.extract_params_and_check(params, |
|
32 | import_params, problem = Problem.extract_params_and_check(params, |
|
25 | org_problem) |
|
33 | org_problem) |
|
26 |
|
34 | ||
|
27 | if !problem.errors.empty? |
|
35 | if !problem.errors.empty? |
|
28 | return problem, 'Error importing' |
|
36 | return problem, 'Error importing' |
|
29 | end |
|
37 | end |
|
30 |
|
38 | ||
|
31 | problem.full_score = 100 |
|
39 | problem.full_score = 100 |
|
32 | problem.date_added = Time.new |
|
40 | problem.date_added = Time.new |
|
33 | problem.test_allowed = true |
|
41 | problem.test_allowed = true |
|
34 | problem.output_only = false |
|
42 | problem.output_only = false |
|
35 | problem.available = false |
|
43 | problem.available = false |
|
36 |
|
44 | ||
|
37 | if not problem.save |
|
45 | if not problem.save |
|
38 | return problem, 'Error importing' |
|
46 | return problem, 'Error importing' |
|
39 | end |
|
47 | end |
|
40 |
|
48 | ||
|
41 | import_to_db = params.has_key? :import_to_db |
|
49 | import_to_db = params.has_key? :import_to_db |
|
42 |
|
50 | ||
|
43 | importer = TestdataImporter.new(problem) |
|
51 | importer = TestdataImporter.new(problem) |
|
44 |
|
52 | ||
|
45 | if not importer.import_from_file(import_params[:file], |
|
53 | if not importer.import_from_file(import_params[:file], |
|
46 | import_params[:time_limit], |
|
54 | import_params[:time_limit], |
|
47 | import_params[:memory_limit], |
|
55 | import_params[:memory_limit], |
|
48 | import_params[:checker_name], |
|
56 | import_params[:checker_name], |
|
49 | import_to_db) |
|
57 | import_to_db) |
|
50 | problem.errors.add(:base,'Import error.') |
|
58 | problem.errors.add(:base,'Import error.') |
|
51 | end |
|
59 | end |
|
52 |
|
60 |
@@ -1,86 +1,86 | |||||
|
1 | class Submission < ActiveRecord::Base |
|
1 | class Submission < ActiveRecord::Base |
|
2 |
|
2 | ||
|
3 | belongs_to :language |
|
3 | belongs_to :language |
|
4 | belongs_to :problem |
|
4 | belongs_to :problem |
|
5 | belongs_to :user |
|
5 | belongs_to :user |
|
6 |
|
6 | ||
|
7 | before_validation :assign_problem |
|
7 | before_validation :assign_problem |
|
8 | before_validation :assign_language |
|
8 | before_validation :assign_language |
|
9 |
|
9 | ||
|
10 | validates_presence_of :source |
|
10 | validates_presence_of :source |
|
11 | validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long' |
|
11 | validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long' |
|
12 | validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short' |
|
12 | validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short' |
|
13 | validate :must_have_valid_problem |
|
13 | validate :must_have_valid_problem |
|
14 | validate :must_specify_language |
|
14 | validate :must_specify_language |
|
15 |
|
15 | ||
|
16 | has_one :task |
|
16 | has_one :task |
|
17 |
|
17 | ||
|
18 | before_save :assign_latest_number_if_new_recond |
|
18 | before_save :assign_latest_number_if_new_recond |
|
19 |
|
19 | ||
|
20 | def self.find_last_by_user_and_problem(user_id, problem_id) |
|
20 | def self.find_last_by_user_and_problem(user_id, problem_id) |
|
21 | where("user_id = ? AND problem_id = ?",user_id,problem_id).last |
|
21 | where("user_id = ? AND problem_id = ?",user_id,problem_id).last |
|
22 | end |
|
22 | end |
|
23 |
|
23 | ||
|
24 | def self.find_all_last_by_problem(problem_id) |
|
24 | def self.find_all_last_by_problem(problem_id) |
|
25 | # need to put in SQL command, maybe there's a better way |
|
25 | # need to put in SQL command, maybe there's a better way |
|
26 | Submission.includes(:user).find_by_sql("SELECT * FROM submissions " + |
|
26 | Submission.includes(:user).find_by_sql("SELECT * FROM submissions " + |
|
27 | "WHERE id = " + |
|
27 | "WHERE id = " + |
|
28 | "(SELECT MAX(id) FROM submissions AS subs " + |
|
28 | "(SELECT MAX(id) FROM submissions AS subs " + |
|
29 | "WHERE subs.user_id = submissions.user_id AND " + |
|
29 | "WHERE subs.user_id = submissions.user_id AND " + |
|
30 | "problem_id = " + problem_id.to_s + " " + |
|
30 | "problem_id = " + problem_id.to_s + " " + |
|
31 | "GROUP BY user_id) " + |
|
31 | "GROUP BY user_id) " + |
|
32 | "ORDER BY user_id") |
|
32 | "ORDER BY user_id") |
|
33 | end |
|
33 | end |
|
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 | ||
|
42 | def self.find_last_for_all_available_problems(user_id) |
|
42 | def self.find_last_for_all_available_problems(user_id) |
|
43 | submissions = Array.new |
|
43 | submissions = Array.new |
|
44 | problems = Problem.available_problems |
|
44 | problems = Problem.available_problems |
|
45 | problems.each do |problem| |
|
45 | problems.each do |problem| |
|
46 | sub = Submission.find_last_by_user_and_problem(user_id, problem.id) |
|
46 | sub = Submission.find_last_by_user_and_problem(user_id, problem.id) |
|
47 | submissions << sub if sub!=nil |
|
47 | submissions << sub if sub!=nil |
|
48 | end |
|
48 | end |
|
49 | submissions |
|
49 | submissions |
|
50 | end |
|
50 | end |
|
51 |
|
51 | ||
|
52 | def self.find_by_user_problem_number(user_id, problem_id, number) |
|
52 | def self.find_by_user_problem_number(user_id, problem_id, number) |
|
53 | where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first |
|
53 | where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first |
|
54 | end |
|
54 | end |
|
55 |
|
55 | ||
|
56 | def self.find_all_by_user_problem(user_id, problem_id) |
|
56 | def self.find_all_by_user_problem(user_id, problem_id) |
|
57 | where("user_id = ? AND problem_id = ?",user_id,problem_id) |
|
57 | where("user_id = ? AND problem_id = ?",user_id,problem_id) |
|
58 | end |
|
58 | end |
|
59 |
|
59 | ||
|
60 | def download_filename |
|
60 | def download_filename |
|
61 | if self.problem.output_only |
|
61 | if self.problem.output_only |
|
62 | return self.source_filename |
|
62 | return self.source_filename |
|
63 | else |
|
63 | else |
|
64 | timestamp = self.submitted_at.localtime.strftime("%H%M%S") |
|
64 | timestamp = self.submitted_at.localtime.strftime("%H%M%S") |
|
65 | return "#{self.problem.name}-#{timestamp}.#{self.language.ext}" |
|
65 | return "#{self.problem.name}-#{timestamp}.#{self.language.ext}" |
|
66 | end |
|
66 | end |
|
67 | end |
|
67 | end |
|
68 |
|
68 | ||
|
69 | protected |
|
69 | protected |
|
70 |
|
70 | ||
|
71 | def self.find_option_in_source(option, source) |
|
71 | def self.find_option_in_source(option, source) |
|
72 | if source==nil |
|
72 | if source==nil |
|
73 | return nil |
|
73 | return nil |
|
74 | end |
|
74 | end |
|
75 | i = 0 |
|
75 | i = 0 |
|
76 | source.each_line do |s| |
|
76 | source.each_line do |s| |
|
77 | if s =~ option |
|
77 | if s =~ option |
|
78 | words = s.split |
|
78 | words = s.split |
|
79 | return words[1] |
|
79 | return words[1] |
|
80 | end |
|
80 | end |
|
81 | i = i + 1 |
|
81 | i = i + 1 |
|
82 | if i==10 |
|
82 | if i==10 |
|
83 | return nil |
|
83 | return nil |
|
84 | end |
|
84 | end |
|
85 | end |
|
85 | end |
|
86 | return nil |
|
86 | return nil |
@@ -92,71 +92,75 | |||||
|
92 | return (Language.find_by_name(langopt) || |
|
92 | return (Language.find_by_name(langopt) || |
|
93 | Language.find_by_pretty_name(langopt)) |
|
93 | Language.find_by_pretty_name(langopt)) |
|
94 | else |
|
94 | else |
|
95 | if source_filename |
|
95 | if source_filename |
|
96 | return Language.find_by_extension(source_filename.split('.').last) |
|
96 | return Language.find_by_extension(source_filename.split('.').last) |
|
97 | else |
|
97 | else |
|
98 | return nil |
|
98 | return nil |
|
99 | end |
|
99 | end |
|
100 | end |
|
100 | end |
|
101 | end |
|
101 | end |
|
102 |
|
102 | ||
|
103 | def self.find_problem_in_source(source, source_filename="") |
|
103 | def self.find_problem_in_source(source, source_filename="") |
|
104 | prob_opt = find_option_in_source(/^TASK:/,source) |
|
104 | prob_opt = find_option_in_source(/^TASK:/,source) |
|
105 | if problem = Problem.find_by_name(prob_opt) |
|
105 | if problem = Problem.find_by_name(prob_opt) |
|
106 | return problem |
|
106 | return problem |
|
107 | else |
|
107 | else |
|
108 | if source_filename |
|
108 | if source_filename |
|
109 | return Problem.find_by_name(source_filename.split('.').first) |
|
109 | return Problem.find_by_name(source_filename.split('.').first) |
|
110 | else |
|
110 | else |
|
111 | return nil |
|
111 | return nil |
|
112 | end |
|
112 | end |
|
113 | end |
|
113 | end |
|
114 | end |
|
114 | end |
|
115 |
|
115 | ||
|
116 | def assign_problem |
|
116 | def assign_problem |
|
117 | if self.problem_id!=-1 |
|
117 | if self.problem_id!=-1 |
|
118 | begin |
|
118 | begin |
|
119 | self.problem = Problem.find(self.problem_id) |
|
119 | self.problem = Problem.find(self.problem_id) |
|
120 | rescue ActiveRecord::RecordNotFound |
|
120 | rescue ActiveRecord::RecordNotFound |
|
121 | self.problem = nil |
|
121 | self.problem = nil |
|
122 | end |
|
122 | end |
|
123 | else |
|
123 | else |
|
124 | self.problem = Submission.find_problem_in_source(self.source, |
|
124 | self.problem = Submission.find_problem_in_source(self.source, |
|
125 | self.source_filename) |
|
125 | self.source_filename) |
|
126 | end |
|
126 | end |
|
127 | end |
|
127 | end |
|
128 |
|
128 | ||
|
129 | def assign_language |
|
129 | def assign_language |
|
130 | self.language = Submission.find_language_in_source(self.source, |
|
130 | self.language = Submission.find_language_in_source(self.source, |
|
131 | self.source_filename) |
|
131 | self.source_filename) |
|
132 | end |
|
132 | end |
|
133 |
|
133 | ||
|
134 | # validation codes |
|
134 | # validation codes |
|
135 | def must_specify_language |
|
135 | def must_specify_language |
|
136 | return if self.source==nil |
|
136 | return if self.source==nil |
|
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 |
|
144 | end |
|
144 | end |
|
145 |
|
145 | ||
|
146 | def must_have_valid_problem |
|
146 | def must_have_valid_problem |
|
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 | ||
|
155 | # callbacks |
|
159 | # callbacks |
|
156 | def assign_latest_number_if_new_recond |
|
160 | def assign_latest_number_if_new_recond |
|
157 | return if !self.new_record? |
|
161 | return if !self.new_record? |
|
158 | latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id) |
|
162 | latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id) |
|
159 | self.number = (latest==nil) ? 1 : latest.number + 1; |
|
163 | self.number = (latest==nil) ? 1 : latest.number + 1; |
|
160 | end |
|
164 | end |
|
161 |
|
165 | ||
|
162 | end |
|
166 | end |
@@ -1,58 +1,62 | |||||
|
1 | require 'digest/sha1' |
|
1 | require 'digest/sha1' |
|
2 | require 'net/pop' |
|
2 | require 'net/pop' |
|
3 | require 'net/https' |
|
3 | require 'net/https' |
|
4 | require 'net/http' |
|
4 | require 'net/http' |
|
5 | require 'json' |
|
5 | require 'json' |
|
6 |
|
6 | ||
|
7 | class User < ActiveRecord::Base |
|
7 | class User < ActiveRecord::Base |
|
8 |
|
8 | ||
|
9 | has_and_belongs_to_many :roles |
|
9 | has_and_belongs_to_many :roles |
|
10 |
|
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) }, |
|
14 | :class_name => "Message", |
|
18 | :class_name => "Message", |
|
15 | :foreign_key => "sender_id" |
|
19 | :foreign_key => "sender_id" |
|
16 |
|
20 | ||
|
17 | has_many :replied_messages, -> { order(created_at: DESC) }, |
|
21 | has_many :replied_messages, -> { order(created_at: DESC) }, |
|
18 | :class_name => "Message", |
|
22 | :class_name => "Message", |
|
19 | :foreign_key => "receiver_id" |
|
23 | :foreign_key => "receiver_id" |
|
20 |
|
24 | ||
|
21 | has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy |
|
25 | has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy |
|
22 |
|
26 | ||
|
23 | belongs_to :site |
|
27 | belongs_to :site |
|
24 | belongs_to :country |
|
28 | belongs_to :country |
|
25 |
|
29 | ||
|
26 | has_and_belongs_to_many :contests, -> { order(:name); uniq} |
|
30 | has_and_belongs_to_many :contests, -> { order(:name); uniq} |
|
27 |
|
31 | ||
|
28 | scope :activated_users, -> {where activated: true} |
|
32 | scope :activated_users, -> {where activated: true} |
|
29 |
|
33 | ||
|
30 | validates_presence_of :login |
|
34 | validates_presence_of :login |
|
31 | validates_uniqueness_of :login |
|
35 | validates_uniqueness_of :login |
|
32 | validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/ |
|
36 | validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/ |
|
33 | validates_length_of :login, :within => 3..30 |
|
37 | validates_length_of :login, :within => 3..30 |
|
34 |
|
38 | ||
|
35 | validates_presence_of :full_name |
|
39 | validates_presence_of :full_name |
|
36 | validates_length_of :full_name, :minimum => 1 |
|
40 | validates_length_of :full_name, :minimum => 1 |
|
37 |
|
41 | ||
|
38 | validates_presence_of :password, :if => :password_required? |
|
42 | validates_presence_of :password, :if => :password_required? |
|
39 | validates_length_of :password, :within => 4..20, :if => :password_required? |
|
43 | validates_length_of :password, :within => 4..20, :if => :password_required? |
|
40 | validates_confirmation_of :password, :if => :password_required? |
|
44 | validates_confirmation_of :password, :if => :password_required? |
|
41 |
|
45 | ||
|
42 | validates_format_of :email, |
|
46 | validates_format_of :email, |
|
43 | :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, |
|
47 | :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, |
|
44 | :if => :email_validation? |
|
48 | :if => :email_validation? |
|
45 | validate :uniqueness_of_email_from_activated_users, |
|
49 | validate :uniqueness_of_email_from_activated_users, |
|
46 | :if => :email_validation? |
|
50 | :if => :email_validation? |
|
47 | validate :enough_time_interval_between_same_email_registrations, |
|
51 | validate :enough_time_interval_between_same_email_registrations, |
|
48 | :if => :email_validation? |
|
52 | :if => :email_validation? |
|
49 |
|
53 | ||
|
50 | # these are for ytopc |
|
54 | # these are for ytopc |
|
51 | # disable for now |
|
55 | # disable for now |
|
52 | #validates_presence_of :province |
|
56 | #validates_presence_of :province |
|
53 |
|
57 | ||
|
54 | attr_accessor :password |
|
58 | attr_accessor :password |
|
55 |
|
59 | ||
|
56 | before_save :encrypt_new_password |
|
60 | before_save :encrypt_new_password |
|
57 | before_save :assign_default_site |
|
61 | before_save :assign_default_site |
|
58 | before_save :assign_default_contest |
|
62 | before_save :assign_default_contest |
@@ -195,121 +199,146 | |||||
|
195 | stat.started_at = Time.now.gmtime |
|
199 | stat.started_at = Time.now.gmtime |
|
196 | stat.save |
|
200 | stat.save |
|
197 | end |
|
201 | end |
|
198 | end |
|
202 | end |
|
199 |
|
203 | ||
|
200 | def problem_in_user_contests?(problem) |
|
204 | def problem_in_user_contests?(problem) |
|
201 | problem_contests = problem.contests.all |
|
205 | problem_contests = problem.contests.all |
|
202 |
|
206 | ||
|
203 | if problem_contests.length == 0 # this is public contest |
|
207 | if problem_contests.length == 0 # this is public contest |
|
204 | return true |
|
208 | return true |
|
205 | end |
|
209 | end |
|
206 |
|
210 | ||
|
207 | contests.each do |contest| |
|
211 | contests.each do |contest| |
|
208 | if problem_contests.find {|c| c.id == contest.id } |
|
212 | if problem_contests.find {|c| c.id == contest.id } |
|
209 | return true |
|
213 | return true |
|
210 | end |
|
214 | end |
|
211 | end |
|
215 | end |
|
212 | return false |
|
216 | return false |
|
213 | end |
|
217 | end |
|
214 |
|
218 | ||
|
215 | def available_problems_group_by_contests |
|
219 | def available_problems_group_by_contests |
|
216 | contest_problems = [] |
|
220 | contest_problems = [] |
|
217 | pin = {} |
|
221 | pin = {} |
|
218 | contests.enabled.each do |contest| |
|
222 | contests.enabled.each do |contest| |
|
219 | available_problems = contest.problems.available |
|
223 | available_problems = contest.problems.available |
|
220 | contest_problems << { |
|
224 | contest_problems << { |
|
221 | :contest => contest, |
|
225 | :contest => contest, |
|
222 | :problems => available_problems |
|
226 | :problems => available_problems |
|
223 | } |
|
227 | } |
|
224 | available_problems.each {|p| pin[p.id] = true} |
|
228 | available_problems.each {|p| pin[p.id] = true} |
|
225 | end |
|
229 | end |
|
226 | other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0} |
|
230 | other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0} |
|
227 | contest_problems << { |
|
231 | contest_problems << { |
|
228 | :contest => nil, |
|
232 | :contest => nil, |
|
229 | :problems => other_avaiable_problems |
|
233 | :problems => other_avaiable_problems |
|
230 | } |
|
234 | } |
|
231 | return contest_problems |
|
235 | return contest_problems |
|
232 | end |
|
236 | end |
|
233 |
|
237 | ||
|
234 | def solve_all_available_problems? |
|
238 | def solve_all_available_problems? |
|
235 | available_problems.each do |p| |
|
239 | available_problems.each do |p| |
|
236 | u = self |
|
240 | u = self |
|
237 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
241 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
238 | return false if !p or !sub or sub.points < p.full_score |
|
242 | return false if !p or !sub or sub.points < p.full_score |
|
239 | end |
|
243 | end |
|
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 = {} |
|
249 | contests.enabled.each do |contest| |
|
258 | contests.enabled.each do |contest| |
|
250 | contest.problems.available.each do |problem| |
|
259 | contest.problems.available.each do |problem| |
|
251 | if not pin.has_key? problem.id |
|
260 | if not pin.has_key? problem.id |
|
252 | contest_problems << problem |
|
261 | contest_problems << problem |
|
253 | end |
|
262 | end |
|
254 | pin[problem.id] = true |
|
263 | pin[problem.id] = true |
|
255 | end |
|
264 | end |
|
256 | end |
|
265 | end |
|
257 | other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0} |
|
266 | other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0} |
|
258 | return contest_problems + other_avaiable_problems |
|
267 | return contest_problems + other_avaiable_problems |
|
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 |
|
271 | User.update_all(:last_ip => nil) |
|
300 | User.update_all(:last_ip => nil) |
|
272 | end |
|
301 | end |
|
273 |
|
302 | ||
|
274 | protected |
|
303 | protected |
|
275 | def encrypt_new_password |
|
304 | def encrypt_new_password |
|
276 | return if password.blank? |
|
305 | return if password.blank? |
|
277 | self.salt = (10+rand(90)).to_s |
|
306 | self.salt = (10+rand(90)).to_s |
|
278 | self.hashed_password = User.encrypt(self.password,self.salt) |
|
307 | self.hashed_password = User.encrypt(self.password,self.salt) |
|
279 | end |
|
308 | end |
|
280 |
|
309 | ||
|
281 | def assign_default_site |
|
310 | def assign_default_site |
|
282 | # have to catch error when migrating (because self.site is not available). |
|
311 | # have to catch error when migrating (because self.site is not available). |
|
283 | begin |
|
312 | begin |
|
284 | if self.site==nil |
|
313 | if self.site==nil |
|
285 | self.site = Site.find_by_name('default') |
|
314 | self.site = Site.find_by_name('default') |
|
286 | if self.site==nil |
|
315 | if self.site==nil |
|
287 | self.site = Site.find(1) # when 'default has be renamed' |
|
316 | self.site = Site.find(1) # when 'default has be renamed' |
|
288 | end |
|
317 | end |
|
289 | end |
|
318 | end |
|
290 | rescue |
|
319 | rescue |
|
291 | end |
|
320 | end |
|
292 | end |
|
321 | end |
|
293 |
|
322 | ||
|
294 | def assign_default_contest |
|
323 | def assign_default_contest |
|
295 | # have to catch error when migrating (because self.site is not available). |
|
324 | # have to catch error when migrating (because self.site is not available). |
|
296 | begin |
|
325 | begin |
|
297 | if self.contests.length == 0 |
|
326 | if self.contests.length == 0 |
|
298 | default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name']) |
|
327 | default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name']) |
|
299 | if default_contest |
|
328 | if default_contest |
|
300 | self.contests = [default_contest] |
|
329 | self.contests = [default_contest] |
|
301 | end |
|
330 | end |
|
302 | end |
|
331 | end |
|
303 | rescue |
|
332 | rescue |
|
304 | end |
|
333 | end |
|
305 | end |
|
334 | end |
|
306 |
|
335 | ||
|
307 | def password_required? |
|
336 | def password_required? |
|
308 | self.hashed_password.blank? || !self.password.blank? |
|
337 | self.hashed_password.blank? || !self.password.blank? |
|
309 | end |
|
338 | end |
|
310 |
|
339 | ||
|
311 | def self.encrypt(string,salt) |
|
340 | def self.encrypt(string,salt) |
|
312 | Digest::SHA1.hexdigest(salt + string) |
|
341 | Digest::SHA1.hexdigest(salt + string) |
|
313 | end |
|
342 | end |
|
314 |
|
343 | ||
|
315 | def uniqueness_of_email_from_activated_users |
|
344 | def uniqueness_of_email_from_activated_users |
@@ -1,37 +1,37 | |||||
|
1 | <p> |
|
1 | <p> |
|
2 | <b>Author:</b> |
|
2 | <b>Author:</b> |
|
3 | <%=h @announcement.author %> |
|
3 | <%=h @announcement.author %> |
|
4 | </p> |
|
4 | </p> |
|
5 |
|
5 | ||
|
6 | <p> |
|
6 | <p> |
|
7 | <b>Title:</b> |
|
7 | <b>Title:</b> |
|
8 | <%=h @announcement.title %> |
|
8 | <%=h @announcement.title %> |
|
9 | </p> |
|
9 | </p> |
|
10 |
|
10 | ||
|
11 | <p> |
|
11 | <p> |
|
12 | <b>Notes:</b> |
|
12 | <b>Notes:</b> |
|
13 | <%=h @announcement.notes %> |
|
13 | <%=h @announcement.notes %> |
|
14 | </p> |
|
14 | </p> |
|
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> |
|
22 | <b>Published:</b> |
|
22 | <b>Published:</b> |
|
23 | <%=h @announcement.published %> |
|
23 | <%=h @announcement.published %> |
|
24 | </p> |
|
24 | </p> |
|
25 |
|
25 | ||
|
26 | <p> |
|
26 | <p> |
|
27 | <b>Show on front page:</b> |
|
27 | <b>Show on front page:</b> |
|
28 | <%=h @announcement.frontpage %> |
|
28 | <%=h @announcement.frontpage %> |
|
29 | </p> |
|
29 | </p> |
|
30 |
|
30 | ||
|
31 | <p> |
|
31 | <p> |
|
32 | <b>Show only in contest:</b> |
|
32 | <b>Show only in contest:</b> |
|
33 | <%=h @announcement.contest_only %> |
|
33 | <%=h @announcement.contest_only %> |
|
34 | </p> |
|
34 | </p> |
|
35 |
|
35 | ||
|
36 | <%= link_to 'Edit', edit_announcement_path(@announcement) %> | |
|
36 | <%= link_to 'Edit', edit_announcement_path(@announcement) %> | |
|
37 | <%= link_to 'Back', announcements_path %> |
|
37 | <%= link_to 'Back', announcements_path %> |
@@ -1,26 +1,28 | |||||
|
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' |
|
14 | = "#{(submission.points*100/submission.problem.full_score).to_i} " |
|
16 | = "#{(submission.points*100/submission.problem.full_score).to_i} " |
|
15 | = " [" |
|
17 | = " [" |
|
16 | %tt |
|
18 | %tt |
|
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 |
@@ -1,15 +1,15 | |||||
|
1 | %h1= "Task: #{@task.id}" |
|
1 | %h1= "Task: #{@task.id}" |
|
2 |
|
2 | ||
|
3 | %p |
|
3 | %p |
|
4 | User: |
|
4 | User: |
|
5 | = "#{@task.submission.user.login}" |
|
5 | = "#{@task.submission.user.login}" |
|
6 | %br/ |
|
6 | %br/ |
|
7 | Status: |
|
7 | Status: |
|
8 | = "#{@task.status_str} (at #{format_short_time(@task.updated_at)})" |
|
8 | = "#{@task.status_str} (at #{format_short_time(@task.updated_at)})" |
|
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/ |
@@ -1,93 +1,95 | |||||
|
1 | %header.navbar.navbar-default.navbar-fixed-top |
|
1 | %header.navbar.navbar-default.navbar-fixed-top |
|
2 | %nav |
|
2 | %nav |
|
3 | .container-fluid |
|
3 | .container-fluid |
|
4 | .navbar-header |
|
4 | .navbar-header |
|
5 | %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} } |
|
5 | %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} } |
|
6 | %span.sr-only Togggle Navigation |
|
6 | %span.sr-only Togggle Navigation |
|
7 | %span.icon-bar |
|
7 | %span.icon-bar |
|
8 | %span.icon-bar |
|
8 | %span.icon-bar |
|
9 | %span.icon-bar |
|
9 | %span.icon-bar |
|
10 | %a.navbar-brand{href: main_list_path} |
|
10 | %a.navbar-brand{href: main_list_path} |
|
11 | %span.glyphicon.glyphicon-home |
|
11 | %span.glyphicon.glyphicon-home |
|
12 | MAIN |
|
12 | MAIN |
|
13 | .collapse.navbar-collapse#navbar-collapse |
|
13 | .collapse.navbar-collapse#navbar-collapse |
|
14 | %ul.nav.navbar-nav |
|
14 | %ul.nav.navbar-nav |
|
15 | / submission |
|
15 | / submission |
|
16 | - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user)) |
|
16 | - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user)) |
|
17 | %li.dropdown |
|
17 | %li.dropdown |
|
18 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
18 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
19 | = "#{I18n.t 'menu.submissions'}" |
|
19 | = "#{I18n.t 'menu.submissions'}" |
|
20 | %span.caret |
|
20 | %span.caret |
|
21 | %ul.dropdown-menu |
|
21 | %ul.dropdown-menu |
|
22 | = add_menu("View", 'submissions', 'index') |
|
22 | = add_menu("View", 'submissions', 'index') |
|
23 | = add_menu("Self Test", 'test', 'index') |
|
23 | = add_menu("Self Test", 'test', 'index') |
|
24 | / hall of fame |
|
24 | / hall of fame |
|
25 | - if GraderConfiguration['right.user_hall_of_fame'] |
|
25 | - if GraderConfiguration['right.user_hall_of_fame'] |
|
26 | = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof') |
|
26 | = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof') |
|
27 | / display MODE button (with countdown in contest mode) |
|
27 | / display MODE button (with countdown in contest mode) |
|
28 | - if GraderConfiguration.analysis_mode? |
|
28 | - if GraderConfiguration.analysis_mode? |
|
29 | %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE" |
|
29 | %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE" |
|
30 | - elsif GraderConfiguration.time_limit_mode? |
|
30 | - elsif GraderConfiguration.time_limit_mode? |
|
31 | - if @current_user.contest_finished? |
|
31 | - if @current_user.contest_finished? |
|
32 | %div.navbar-btn.btn.btn-danger#countdown= "Contest is over" |
|
32 | %div.navbar-btn.btn.btn-danger#countdown= "Contest is over" |
|
33 | - elsif !@current_user.contest_started? |
|
33 | - elsif !@current_user.contest_started? |
|
34 | %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started') |
|
34 | %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started') |
|
35 | - else |
|
35 | - else |
|
36 | %div.navbar-btn.btn.btn-primary#countdown asdf |
|
36 | %div.navbar-btn.btn.btn-primary#countdown asdf |
|
37 | :javascript |
|
37 | :javascript |
|
38 | $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'}); |
|
38 | $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'}); |
|
39 | / admin section |
|
39 | / admin section |
|
40 | - if (@current_user!=nil) and (session[:admin]) |
|
40 | - if (@current_user!=nil) and (session[:admin]) |
|
41 | / management |
|
41 | / management |
|
42 | %li.dropdown |
|
42 | %li.dropdown |
|
43 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
43 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
44 | Manage |
|
44 | Manage |
|
45 | %span.caret |
|
45 | %span.caret |
|
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'} |
|
53 | = add_menu( 'System config', 'configurations', 'index') |
|
55 | = add_menu( 'System config', 'configurations', 'index') |
|
54 | %li.divider{role: 'separator'} |
|
56 | %li.divider{role: 'separator'} |
|
55 | = add_menu( 'Sites', 'sites', 'index') |
|
57 | = add_menu( 'Sites', 'sites', 'index') |
|
56 | = add_menu( 'Contests', 'contest_management', 'index') |
|
58 | = add_menu( 'Contests', 'contest_management', 'index') |
|
57 | / report |
|
59 | / report |
|
58 | %li.dropdown |
|
60 | %li.dropdown |
|
59 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
61 | %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} |
|
60 | Report |
|
62 | Report |
|
61 | %span.caret |
|
63 | %span.caret |
|
62 | %ul.dropdown-menu |
|
64 | %ul.dropdown-menu |
|
63 | = add_menu( 'Current Score', 'report', 'current_score') |
|
65 | = add_menu( 'Current Score', 'report', 'current_score') |
|
64 | = add_menu( 'Score Report', 'report', 'max_score') |
|
66 | = add_menu( 'Score Report', 'report', 'max_score') |
|
65 | = add_menu( 'Report', 'report', 'multiple_login') |
|
67 | = add_menu( 'Report', 'report', 'multiple_login') |
|
66 | - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0 |
|
68 | - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0 |
|
67 | =link_to "#{ungraded} backlogs!", |
|
69 | =link_to "#{ungraded} backlogs!", |
|
68 | grader_list_path, |
|
70 | grader_list_path, |
|
69 | class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission' |
|
71 | class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission' |
|
70 |
|
72 | ||
|
71 | %ul.nav.navbar-nav.navbar-right |
|
73 | %ul.nav.navbar-nav.navbar-right |
|
72 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help') |
|
74 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help') |
|
73 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}}) |
|
75 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}}) |
|
74 | - if GraderConfiguration['system.user_setting_enabled'] |
|
76 | - if GraderConfiguration['system.user_setting_enabled'] |
|
75 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}}) |
|
77 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}}) |
|
76 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}}) |
|
78 | = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}}) |
|
77 |
|
79 | ||
|
78 | / |
|
80 | / |
|
79 | - if (@current_user!=nil) and (session[:admin]) |
|
81 | - if (@current_user!=nil) and (session[:admin]) |
|
80 | %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar |
|
82 | %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar |
|
81 | .container-fluid |
|
83 | .container-fluid |
|
82 | .collapse.navbar-collapse |
|
84 | .collapse.navbar-collapse |
|
83 | %ul.nav.navbar-nav |
|
85 | %ul.nav.navbar-nav |
|
84 | = add_menu( '[Announcements]', 'announcements', 'index') |
|
86 | = add_menu( '[Announcements]', 'announcements', 'index') |
|
85 | = add_menu( '[Msg console]', 'messages', 'console') |
|
87 | = add_menu( '[Msg console]', 'messages', 'console') |
|
86 | = add_menu( '[Problems]', 'problems', 'index') |
|
88 | = add_menu( '[Problems]', 'problems', 'index') |
|
87 | = add_menu( '[Users]', 'user_admin', 'index') |
|
89 | = add_menu( '[Users]', 'user_admin', 'index') |
|
88 | = add_menu( '[Results]', 'user_admin', 'user_stat') |
|
90 | = add_menu( '[Results]', 'user_admin', 'user_stat') |
|
89 | = add_menu( '[Report]', 'report', 'multiple_login') |
|
91 | = add_menu( '[Report]', 'report', 'multiple_login') |
|
90 | = add_menu( '[Graders]', 'graders', 'list') |
|
92 | = add_menu( '[Graders]', 'graders', 'list') |
|
91 | = add_menu( '[Contests]', 'contest_management', 'index') |
|
93 | = add_menu( '[Contests]', 'contest_management', 'index') |
|
92 | = add_menu( '[Sites]', 'sites', 'index') |
|
94 | = add_menu( '[Sites]', 'sites', 'index') |
|
93 | = add_menu( '[System config]', 'configurations', 'index') |
|
95 | = add_menu( '[System config]', 'configurations', 'index') |
@@ -1,39 +1,43 | |||||
|
1 | %b= GraderConfiguration['ui.front.welcome_message'] |
|
1 | %b= GraderConfiguration['ui.front.welcome_message'] |
|
2 | %br/ |
|
2 | %br/ |
|
3 |
|
3 | ||
|
4 | - if !@hidelogin |
|
4 | - if !@hidelogin |
|
5 | =t 'login.message' |
|
5 | =t 'login.message' |
|
6 | %br/ |
|
6 | %br/ |
|
7 | %br/ |
|
7 | %br/ |
|
8 |
|
8 | ||
|
9 | - if flash[:notice] |
|
9 | - if flash[:notice] |
|
10 | %hr/ |
|
10 | %hr/ |
|
11 | %b= flash[:notice] |
|
11 | %b= flash[:notice] |
|
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'] |
|
34 | =t 'login.participation' |
|
38 | =t 'login.participation' |
|
35 | %b |
|
39 | %b |
|
36 | = "#{t 'login.please'} " |
|
40 | = "#{t 'login.please'} " |
|
37 | = link_to "#{t 'login.register'}", :controller => :users, :action => :new |
|
41 | = link_to "#{t 'login.register'}", :controller => :users, :action => :new |
|
38 | %br/ |
|
42 | %br/ |
|
39 | = link_to "#{t 'login.forget_password'}", :controller => :users, :action => :forget |
|
43 | = link_to "#{t 'login.forget_password'}", :controller => :users, :action => :forget |
@@ -1,22 +1,22 | |||||
|
1 | %tr |
|
1 | %tr |
|
2 | %td |
|
2 | %td |
|
3 | - if @current_user and @current_user.admin? |
|
3 | - if @current_user and @current_user.admin? |
|
4 | = link_to problem.name, stat_problem_path(problem) |
|
4 | = link_to problem.name, stat_problem_path(problem) |
|
5 | - else |
|
5 | - else |
|
6 | = "#{problem.name}" |
|
6 | = "#{problem.name}" |
|
7 | %td |
|
7 | %td |
|
8 | = "#{problem.full_name}" |
|
8 | = "#{problem.full_name}" |
|
9 |
|
9 | ||
|
10 | %br |
|
10 | %br |
|
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 } |
|
18 | %td |
|
18 | %td |
|
19 | - if @prob_submissions[problem.id][:submission] |
|
19 | - if @prob_submissions[problem.id][:submission] |
|
20 | = link_to 'Edit', edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success' |
|
20 | = link_to 'Edit', edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success' |
|
21 | - else |
|
21 | - else |
|
22 | = link_to 'New', direct_edit_problem_submissions_path(problem.id), class: 'btn btn-success' |
|
22 | = link_to 'New', direct_edit_problem_submissions_path(problem.id), class: 'btn btn-success' |
@@ -1,29 +1,29 | |||||
|
1 |
|
1 | ||
|
2 | - if submission.nil? |
|
2 | - if submission.nil? |
|
3 | = "-" |
|
3 | = "-" |
|
4 | - else |
|
4 | - else |
|
5 | - unless submission.graded_at |
|
5 | - unless submission.graded_at |
|
6 | = t 'main.submitted_at' |
|
6 | = t 'main.submitted_at' |
|
7 | = format_short_time(submission.submitted_at.localtime) |
|
7 | = format_short_time(submission.submitted_at.localtime) |
|
8 | - else |
|
8 | - else |
|
9 | %strong= t 'main.graded_at' |
|
9 | %strong= t 'main.graded_at' |
|
10 | = "#{format_short_time(submission.graded_at.localtime)} " |
|
10 | = "#{format_short_time(submission.graded_at.localtime)} " |
|
11 | %br |
|
11 | %br |
|
12 | - if GraderConfiguration['ui.show_score'] |
|
12 | - if GraderConfiguration['ui.show_score'] |
|
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 |
|
20 | %strong View: |
|
20 | %strong View: |
|
21 | - if GraderConfiguration.show_grading_result |
|
21 | - if GraderConfiguration.show_grading_result |
|
22 | = link_to '[detailed result]', :action => 'result', :id => submission.id |
|
22 | = link_to '[detailed result]', :action => 'result', :id => submission.id |
|
23 | /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'} |
|
23 | /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'} |
|
24 | = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'} |
|
24 | = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'} |
|
25 | = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info' |
|
25 | = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info' |
|
26 | = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' |
|
26 | = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info' |
|
27 | - if GraderConfiguration.show_testcase |
|
27 | - if GraderConfiguration.show_testcase |
|
28 | = link_to "testcases", show_problem_testcases_path(problem_id), class: 'btn btn-xs btn-info' |
|
28 | = link_to "testcases", show_problem_testcases_path(problem_id), class: 'btn btn-xs btn-info' |
|
29 |
|
29 |
@@ -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' |
|
@@ -1,52 +1,55 | |||||
|
1 | = error_messages_for 'problem' |
|
1 | = error_messages_for 'problem' |
|
2 | / [form:problem] |
|
2 | / [form:problem] |
|
3 | .form-group |
|
3 | .form-group |
|
4 | %label{:for => "problem_name"} Name |
|
4 | %label{:for => "problem_name"} Name |
|
5 | = text_field 'problem', 'name', class: 'form-control' |
|
5 | = text_field 'problem', 'name', class: 'form-control' |
|
6 | %small |
|
6 | %small |
|
7 | Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead. |
|
7 | Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead. |
|
8 | .form-group |
|
8 | .form-group |
|
9 | %label{:for => "problem_full_name"} Full name |
|
9 | %label{:for => "problem_full_name"} Full name |
|
10 | = text_field 'problem', 'full_name', class: 'form-control' |
|
10 | = text_field 'problem', 'full_name', class: 'form-control' |
|
11 | .form-group |
|
11 | .form-group |
|
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 |
|
18 | - # nice default values for them. These values look fine only |
|
21 | - # nice default values for them. These values look fine only |
|
19 | - # in this case (of lazily adding new problems). |
|
22 | - # in this case (of lazily adding new problems). |
|
20 | - @problem.available = true if @problem!=nil and @problem.available==nil |
|
23 | - @problem.available = true if @problem!=nil and @problem.available==nil |
|
21 | - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil |
|
24 | - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil |
|
22 | - @problem.output_only = false if @problem!=nil and @problem.output_only==nil |
|
25 | - @problem.output_only = false if @problem!=nil and @problem.output_only==nil |
|
23 | .checkbox |
|
26 | .checkbox |
|
24 | %label{:for => "problem_available"} |
|
27 | %label{:for => "problem_available"} |
|
25 | = check_box :problem, :available |
|
28 | = check_box :problem, :available |
|
26 | Available? |
|
29 | Available? |
|
27 | .checkbox |
|
30 | .checkbox |
|
28 | %label{:for => "problem_test_allowed"} |
|
31 | %label{:for => "problem_test_allowed"} |
|
29 | = check_box :problem, :test_allowed |
|
32 | = check_box :problem, :test_allowed |
|
30 | Test allowed? |
|
33 | Test allowed? |
|
31 | .checkbox |
|
34 | .checkbox |
|
32 | %label{:for => "problem_output_only"} |
|
35 | %label{:for => "problem_output_only"} |
|
33 | = check_box :problem, :output_only |
|
36 | = check_box :problem, :output_only |
|
34 | Output only? |
|
37 | Output only? |
|
35 | = error_messages_for 'description' |
|
38 | = error_messages_for 'description' |
|
36 | .form-group |
|
39 | .form-group |
|
37 | %label{:for => "description_body"} Description |
|
40 | %label{:for => "description_body"} Description |
|
38 | %br/ |
|
41 | %br/ |
|
39 | = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' |
|
42 | = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' |
|
40 | .form-group |
|
43 | .form-group |
|
41 | %label{:for => "description_markdowned"} Markdowned? |
|
44 | %label{:for => "description_markdowned"} Markdowned? |
|
42 | = select "description", | |
|
45 | = select "description", | |
|
43 | "markdowned", | |
|
46 | "markdowned", | |
|
44 | [['True',true],['False',false]], | |
|
47 | [['True',true],['False',false]], | |
|
45 | {:selected => (@description) ? @description.markdowned : false } | |
|
48 | {:selected => (@description) ? @description.markdowned : false } | |
|
46 | .form-group |
|
49 | .form-group |
|
47 | %label{:for => "problem_url"} URL |
|
50 | %label{:for => "problem_url"} URL |
|
48 | %br/ |
|
51 | %br/ |
|
49 | = text_field 'problem', 'url',class: 'form-control' |
|
52 | = text_field 'problem', 'url',class: 'form-control' |
|
50 | %p |
|
53 | %p |
|
51 | Task PDF #{file_field_tag 'file'} |
|
54 | Task PDF #{file_field_tag 'file'} |
|
52 | / [eoform:problem] |
|
55 | / [eoform:problem] |
@@ -1,54 +1,65 | |||||
|
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 |
|
11 | = form_tag :action => 'quick_create' do |
|
11 | = form_tag :action => 'quick_create' do |
|
12 | %b Quick New: |
|
12 | %b Quick New: |
|
13 | %label{:for => "problem_name"} Name |
|
13 | %label{:for => "problem_name"} Name |
|
14 | = text_field 'problem', 'name' |
|
14 | = text_field 'problem', 'name' |
|
15 | | |
|
15 | | |
|
16 | %label{:for => "problem_full_name"} Full name |
|
16 | %label{:for => "problem_full_name"} Full name |
|
17 | = text_field 'problem', 'full_name' |
|
17 | = text_field 'problem', 'full_name' |
|
18 | = submit_tag "Create" |
|
18 | = submit_tag "Create" |
|
19 | %table.table.table-condense.table-hover |
|
19 | %table.table.table-condense.table-hover |
|
20 | %thead |
|
20 | %thead |
|
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? |
|
27 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?] |
|
31 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?] |
|
28 | %th.text-center |
|
32 | %th.text-center |
|
29 | View Data? |
|
33 | View Data? |
|
30 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?] |
|
34 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?] |
|
31 | %th.text-center |
|
35 | %th.text-center |
|
32 | Test? |
|
36 | Test? |
|
33 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?] |
|
37 | %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?] |
|
34 | - if GraderConfiguration.multicontests? |
|
38 | - if GraderConfiguration.multicontests? |
|
35 | %th Contests |
|
39 | %th Contests |
|
36 | - for problem in @problems |
|
40 | - for problem in @problems |
|
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}") |
|
45 | %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}") |
|
56 | %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}") |
|
46 | - if GraderConfiguration.multicontests? |
|
57 | - if GraderConfiguration.multicontests? |
|
47 | %td |
|
58 | %td |
|
48 | = problem.contests.collect { |c| c.name }.join(', ') |
|
59 | = problem.contests.collect { |c| c.name }.join(', ') |
|
49 | %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block' |
|
60 | %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block' |
|
50 | %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
61 | %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
51 | %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
62 | %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block' |
|
52 | %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block' |
|
63 | %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block' |
|
53 | %br/ |
|
64 | %br/ |
|
54 | = link_to '[New problem]', :action => 'new' |
|
65 | = link_to '[New problem]', :action => 'new' |
@@ -1,85 +1,118 | |||||
|
1 | - content_for :head do |
|
1 | - content_for :head do |
|
2 | = stylesheet_link_tag 'problems' |
|
2 | = stylesheet_link_tag 'problems' |
|
3 | = javascript_include_tag 'local_jquery' |
|
3 | = javascript_include_tag 'local_jquery' |
|
4 |
|
4 | ||
|
5 | :javascript |
|
5 | :javascript |
|
6 | $(document).ready( function() { |
|
6 | $(document).ready( function() { |
|
7 | function shiftclick(start,stop,value) { |
|
7 | function shiftclick(start,stop,value) { |
|
8 | $('tr input').each( function(id,input) { |
|
8 | $('tr input').each( function(id,input) { |
|
9 | var $input=$(input); |
|
9 | var $input=$(input); |
|
10 | var iid=parseInt($input.attr('id').split('-')[2]); |
|
10 | var iid=parseInt($input.attr('id').split('-')[2]); |
|
11 | if(iid>=start&&iid<=stop){ |
|
11 | if(iid>=start&&iid<=stop){ |
|
12 | $input.prop('checked',value) |
|
12 | $input.prop('checked',value) |
|
13 | } |
|
13 | } |
|
14 | }); |
|
14 | }); |
|
15 | } |
|
15 | } |
|
16 |
|
16 | ||
|
17 | $('tr input').click( function(e) { |
|
17 | $('tr input').click( function(e) { |
|
18 | if (e.shiftKey) { |
|
18 | if (e.shiftKey) { |
|
19 | stop = parseInt($(this).attr('id').split('-')[2]); |
|
19 | stop = parseInt($(this).attr('id').split('-')[2]); |
|
20 | var orig_stop = stop |
|
20 | var orig_stop = stop |
|
21 | if (typeof start !== 'undefined') { |
|
21 | if (typeof start !== 'undefined') { |
|
22 | if (start > stop) { |
|
22 | if (start > stop) { |
|
23 | var tmp = start; |
|
23 | var tmp = start; |
|
24 | start = stop; |
|
24 | start = stop; |
|
25 | stop = tmp; |
|
25 | stop = tmp; |
|
26 | } |
|
26 | } |
|
27 | shiftclick(start,stop,$(this).is(':checked') ) |
|
27 | shiftclick(start,stop,$(this).is(':checked') ) |
|
28 | } |
|
28 | } |
|
29 | start = orig_stop |
|
29 | start = orig_stop |
|
30 | } else { |
|
30 | } else { |
|
31 | start = parseInt($(this).attr('id').split('-')[2]); |
|
31 | start = parseInt($(this).attr('id').split('-')[2]); |
|
32 | } |
|
32 | } |
|
33 | }); |
|
33 | }); |
|
34 | }); |
|
34 | }); |
|
35 |
|
35 | ||
|
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 | + }); |
@@ -1,53 +1,59 | |||||
|
1 | :css |
|
1 | :css |
|
2 | .fix-width { |
|
2 | .fix-width { |
|
3 | font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier" |
|
3 | font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier" |
|
4 | } |
|
4 | } |
|
5 |
|
5 | ||
|
6 | %h1 Problem stat: #{@problem.name} |
|
6 | %h1 Problem stat: #{@problem.name} |
|
7 | %h2 Overview |
|
7 | %h2 Overview |
|
8 |
|
8 | ||
|
9 |
|
9 | ||
|
10 | %table.info |
|
10 | %table.info |
|
11 | %thead |
|
11 | %thead |
|
12 | %tr.info-head |
|
12 | %tr.info-head |
|
13 | %th Stat |
|
13 | %th Stat |
|
14 | %th Value |
|
14 | %th Value |
|
15 | %tbody |
|
15 | %tbody |
|
16 | %tr{class: cycle('info-even','info-odd')} |
|
16 | %tr{class: cycle('info-even','info-odd')} |
|
17 | %td Submissions |
|
17 | %td Submissions |
|
18 | %td= @submissions.count |
|
18 | %td= @submissions.count |
|
19 | %tr{class: cycle('info-even','info-odd')} |
|
19 | %tr{class: cycle('info-even','info-odd')} |
|
20 | %td Solved/Attempted User |
|
20 | %td Solved/Attempted User |
|
21 | %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) |
|
21 | %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) |
|
22 |
|
22 | ||
|
23 | %h2 Submissions Count |
|
23 | %h2 Submissions Count |
|
24 | = render partial: 'application/bar_graph', locals: { histogram: @histogram } |
|
24 | = render partial: 'application/bar_graph', locals: { histogram: @histogram } |
|
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 |
|
38 | %tbody |
|
39 | %tbody |
|
39 | - row_odd,curr = true,'' |
|
40 | - row_odd,curr = true,'' |
|
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 | + }); |
@@ -1,34 +1,69 | |||||
|
1 | %table.table.sortable.table-striped.table-bordered.table-condensed |
|
1 | %table.table.sortable.table-striped.table-bordered.table-condensed |
|
2 | %thead |
|
2 | %thead |
|
3 | %tr |
|
3 | %tr |
|
4 | %th Login |
|
4 | %th Login |
|
5 | %th Name |
|
5 | %th Name |
|
6 | / %th Activated? |
|
6 | / %th Activated? |
|
7 | / %th Logged_in |
|
7 | / %th Logged_in |
|
8 | / %th Contest(s) |
|
8 | / %th Contest(s) |
|
9 | %th Remark |
|
9 | %th Remark |
|
10 | - @problems.each do |p| |
|
10 | - @problems.each do |p| |
|
11 | %th.text-right= p.name.gsub('_',' ') |
|
11 | %th.text-right= p.name.gsub('_',' ') |
|
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 |
|
18 | - sc.each_index do |i| |
|
21 | - sc.each_index do |i| |
|
19 | - if i == 0 |
|
22 | - if i == 0 |
|
20 | %td= link_to sc[i].login, stat_user_path(sc[i]) |
|
23 | %td= link_to sc[i].login, stat_user_path(sc[i]) |
|
21 | %td= sc[i].full_name |
|
24 | %td= sc[i].full_name |
|
22 | / %td= sc[i].activated |
|
25 | / %td= sc[i].activated |
|
23 | / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no' |
|
26 | / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no' |
|
24 | / %td= sc[i].contests.collect {|c| c.name}.join(', ') |
|
27 | / %td= sc[i].contests.collect {|c| c.name}.join(', ') |
|
25 | %td= sc[i].remark |
|
28 | %td= sc[i].remark |
|
26 | - else |
|
29 | - else |
|
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') |
@@ -1,49 +1,49 | |||||
|
1 | %h1 Maximum score |
|
1 | %h1 Maximum score |
|
2 |
|
2 | ||
|
3 | = form_tag report_show_max_score_path |
|
3 | = form_tag report_show_max_score_path |
|
4 | .row |
|
4 | .row |
|
5 | .col-md-4 |
|
5 | .col-md-4 |
|
6 | .panel.panel-primary |
|
6 | .panel.panel-primary |
|
7 | .panel-heading |
|
7 | .panel-heading |
|
8 | Problems |
|
8 | Problems |
|
9 | .panel-body |
|
9 | .panel-body |
|
10 | %p |
|
10 | %p |
|
11 | Select problem(s) that we wish to know the score. |
|
11 | Select problem(s) that we wish to know the score. |
|
12 | = label_tag :problem_id, "Problems" |
|
12 | = label_tag :problem_id, "Problems" |
|
13 | = select_tag 'problem_id[]', |
|
13 | = select_tag 'problem_id[]', |
|
14 | options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]), |
|
14 | options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]), |
|
15 | { class: 'select2 form-control', multiple: "true" } |
|
15 | { class: 'select2 form-control', multiple: "true" } |
|
16 | .col-md-4 |
|
16 | .col-md-4 |
|
17 | .panel.panel-primary |
|
17 | .panel.panel-primary |
|
18 | .panel-heading |
|
18 | .panel-heading |
|
19 | Submission range |
|
19 | Submission range |
|
20 | .panel-body |
|
20 | .panel-body |
|
21 | %p |
|
21 | %p |
|
22 | Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively. |
|
22 | Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively. |
|
23 | .form-group |
|
23 | .form-group |
|
24 | = label_tag :from, "Min" |
|
24 | = label_tag :from, "Min" |
|
25 | = text_field_tag 'from_id', @since_id, class: "form-control" |
|
25 | = text_field_tag 'from_id', @since_id, class: "form-control" |
|
26 | .form-group |
|
26 | .form-group |
|
27 | = label_tag :from, "Max" |
|
27 | = label_tag :from, "Max" |
|
28 | = text_field_tag 'to_id', @until_id, class: "form-control" |
|
28 | = text_field_tag 'to_id', @until_id, class: "form-control" |
|
29 | .col-md-4 |
|
29 | .col-md-4 |
|
30 | .panel.panel-primary |
|
30 | .panel.panel-primary |
|
31 | .panel-heading |
|
31 | .panel-heading |
|
32 | Users |
|
32 | Users |
|
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 |
|
44 | = button_tag 'Show', class: "btn btn-primary btn-large", value: "show" |
|
44 | = button_tag 'Show', class: "btn btn-primary btn-large", value: "show" |
|
45 | = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download" |
|
45 | = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download" |
|
46 |
|
46 | ||
|
47 | - if @scorearray |
|
47 | - if @scorearray |
|
48 | %h2 Result |
|
48 | %h2 Result |
|
49 | =render "score_table" |
|
49 | =render "score_table" |
@@ -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 | + |
@@ -1,83 +1,86 | |||||
|
1 | %h2 Live submit |
|
1 | %h2 Live submit |
|
2 | %br |
|
2 | %br |
|
3 |
|
3 | ||
|
4 | %textarea#text_sourcecode{style: "display:none"}~ @source |
|
4 | %textarea#text_sourcecode{style: "display:none"}~ @source |
|
5 | .container |
|
5 | .container |
|
6 | .row |
|
6 | .row |
|
7 | .col-md-12 |
|
7 | .col-md-12 |
|
8 | .alert.alert-info |
|
8 | .alert.alert-info |
|
9 | Write your code in the following box, choose language, and click submit button when finished |
|
9 | Write your code in the following box, choose language, and click submit button when finished |
|
10 | .row |
|
10 | .row |
|
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 |
|
17 | = hidden_field_tag 'submission[problem_id]', @problem.id |
|
18 | = hidden_field_tag 'submission[problem_id]', @problem.id |
|
18 | .form-group |
|
19 | .form-group |
|
19 | = label_tag "Task:" |
|
20 | = label_tag "Task:" |
|
20 | = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true |
|
21 | = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true |
|
21 |
|
22 | ||
|
22 | .form-group |
|
23 | .form-group |
|
23 | = label_tag 'Language' |
|
24 | = label_tag 'Language' |
|
24 | = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px" |
|
25 | = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px" |
|
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 |
|
39 | %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20} |
|
42 | %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20} |
|
40 |
|
43 | ||
|
41 | :javascript |
|
44 | :javascript |
|
42 | $(document).ready(function() { |
|
45 | $(document).ready(function() { |
|
43 | e = ace.edit("editor") |
|
46 | e = ace.edit("editor") |
|
44 | e.setValue($("#text_sourcecode").val()); |
|
47 | e.setValue($("#text_sourcecode").val()); |
|
45 | e.gotoLine(1); |
|
48 | e.gotoLine(1); |
|
46 | $("#language_id").trigger('change'); |
|
49 | $("#language_id").trigger('change'); |
|
47 | brython(); |
|
50 | brython(); |
|
48 | }); |
|
51 | }); |
|
49 |
|
52 | ||
|
50 |
|
53 | ||
|
51 | %script#__main__{type:'text/python3'} |
|
54 | %script#__main__{type:'text/python3'} |
|
52 | :plain |
|
55 | :plain |
|
53 | import sys |
|
56 | import sys |
|
54 | import traceback |
|
57 | import traceback |
|
55 |
|
58 | ||
|
56 | from browser import document as doc |
|
59 | from browser import document as doc |
|
57 | from browser import window, alert, console |
|
60 | from browser import window, alert, console |
|
58 |
|
61 | ||
|
59 | _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands |
|
62 | _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands |
|
60 | for supporting Python development. See www.python.org for more information.""" |
|
63 | for supporting Python development. See www.python.org for more information.""" |
|
61 |
|
64 | ||
|
62 | _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
65 | _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
63 | All Rights Reserved. |
|
66 | All Rights Reserved. |
|
64 |
|
67 | ||
|
65 | Copyright (c) 2001-2013 Python Software Foundation. |
|
68 | Copyright (c) 2001-2013 Python Software Foundation. |
|
66 | All Rights Reserved. |
|
69 | All Rights Reserved. |
|
67 |
|
70 | ||
|
68 | Copyright (c) 2000 BeOpen.com. |
|
71 | Copyright (c) 2000 BeOpen.com. |
|
69 | All Rights Reserved. |
|
72 | All Rights Reserved. |
|
70 |
|
73 | ||
|
71 | Copyright (c) 1995-2001 Corporation for National Research Initiatives. |
|
74 | Copyright (c) 1995-2001 Corporation for National Research Initiatives. |
|
72 | All Rights Reserved. |
|
75 | All Rights Reserved. |
|
73 |
|
76 | ||
|
74 | Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. |
|
77 | Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. |
|
75 | All Rights Reserved.""" |
|
78 | All Rights Reserved.""" |
|
76 |
|
79 | ||
|
77 | _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
80 | _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com |
|
78 | All rights reserved. |
|
81 | All rights reserved. |
|
79 |
|
82 | ||
|
80 | Redistribution and use in source and binary forms, with or without |
|
83 | Redistribution and use in source and binary forms, with or without |
|
81 | modification, are permitted provided that the following conditions are met: |
|
84 | modification, are permitted provided that the following conditions are met: |
|
82 |
|
85 | ||
|
83 | Redistributions of source code must retain the above copyright notice, this |
|
86 | Redistributions of source code must retain the above copyright notice, this |
@@ -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}})}") |
@@ -1,77 +1,86 | |||||
|
1 | %h1 Bulk Manage User |
|
1 | %h1 Bulk Manage User |
|
2 |
|
2 | ||
|
3 | = form_tag bulk_manage_user_admin_path |
|
3 | = form_tag bulk_manage_user_admin_path |
|
4 | .row |
|
4 | .row |
|
5 | .col-md-6 |
|
5 | .col-md-6 |
|
6 | .panel.panel-primary |
|
6 | .panel.panel-primary |
|
7 | .panel-title.panel-heading |
|
7 | .panel-title.panel-heading |
|
8 | Filter User |
|
8 | Filter User |
|
9 | .panel-body |
|
9 | .panel-body |
|
10 | Filtering users whose login match the following MySQL regex |
|
10 | Filtering users whose login match the following MySQL regex |
|
11 | .form-group |
|
11 | .form-group |
|
12 | = label_tag "regex", 'Regex Pattern' |
|
12 | = label_tag "regex", 'Regex Pattern' |
|
13 | = text_field_tag "regex", params[:regex], class: 'form-control' |
|
13 | = text_field_tag "regex", params[:regex], class: 'form-control' |
|
14 | %p |
|
14 | %p |
|
15 | Example |
|
15 | Example |
|
16 | %ul |
|
16 | %ul |
|
17 | %li |
|
17 | %li |
|
18 | %code root |
|
18 | %code root |
|
19 | matches every user whose login contains "root" |
|
19 | matches every user whose login contains "root" |
|
20 | %li |
|
20 | %li |
|
21 | %code ^56 |
|
21 | %code ^56 |
|
22 | matches every user whose login starts with "56" |
|
22 | matches every user whose login starts with "56" |
|
23 | %li |
|
23 | %li |
|
24 | %code 21$ |
|
24 | %code 21$ |
|
25 | matches every user whose login ends with "21" |
|
25 | matches every user whose login ends with "21" |
|
26 | .col-md-6 |
|
26 | .col-md-6 |
|
27 | .panel.panel-primary |
|
27 | .panel.panel-primary |
|
28 | .panel-title.panel-heading |
|
28 | .panel-title.panel-heading |
|
29 | Action |
|
29 | Action |
|
30 | .panel-body |
|
30 | .panel-body |
|
31 | .row.form-group |
|
31 | .row.form-group |
|
32 | .col-md-6 |
|
32 | .col-md-6 |
|
33 | %label.checkbox-inline |
|
33 | %label.checkbox-inline |
|
34 | = check_box_tag "enabled", true, params[:enabled] |
|
34 | = check_box_tag "enabled", true, params[:enabled] |
|
35 | Change "Enabled" to |
|
35 | Change "Enabled" to |
|
36 | .col-md-3 |
|
36 | .col-md-3 |
|
37 | %label.radio-inline |
|
37 | %label.radio-inline |
|
38 | = radio_button_tag "enable", 1, params[:enable] == '1', id: 'enable-yes' |
|
38 | = radio_button_tag "enable", 1, params[:enable] == '1', id: 'enable-yes' |
|
39 | Yes |
|
39 | Yes |
|
40 | .col-md-3 |
|
40 | .col-md-3 |
|
41 | %label.radio-inline |
|
41 | %label.radio-inline |
|
42 | = radio_button_tag "enable", 0, params[:enable] == '0', id: 'enable-no' |
|
42 | = radio_button_tag "enable", 0, params[:enable] == '0', id: 'enable-no' |
|
43 | No |
|
43 | No |
|
44 | .row.form-group |
|
44 | .row.form-group |
|
45 | .col-md-6 |
|
45 | .col-md-6 |
|
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 |
|
52 | = submit_tag "Preview Result", class: 'btn btn-default' |
|
61 | = submit_tag "Preview Result", class: 'btn btn-default' |
|
53 | - if @users |
|
62 | - if @users |
|
54 | .row |
|
63 | .row |
|
55 | .col-md-4 |
|
64 | .col-md-4 |
|
56 | - if @action |
|
65 | - if @action |
|
57 | %h2 Confirmation |
|
66 | %h2 Confirmation |
|
58 | - if @action[:set_enable] |
|
67 | - if @action[:set_enable] |
|
59 | .alert.alert-info The following users will be set #{(@action[:enabled] ? 'enable' : 'disable')}. |
|
68 | .alert.alert-info The following users will be set #{(@action[:enabled] ? 'enable' : 'disable')}. |
|
60 | - if @action[:gen_password] |
|
69 | - if @action[:gen_password] |
|
61 | .alert.alert-info The password of the following users will be randomly generated. |
|
70 | .alert.alert-info The password of the following users will be randomly generated. |
|
62 | .row |
|
71 | .row |
|
63 | .col-md-4 |
|
72 | .col-md-4 |
|
64 | = submit_tag "Perform", class: 'btn btn-primary' |
|
73 | = submit_tag "Perform", class: 'btn btn-primary' |
|
65 | .row |
|
74 | .row |
|
66 | .col-md-12 |
|
75 | .col-md-12 |
|
67 | The pattern matches #{@users.count} following users. |
|
76 | The pattern matches #{@users.count} following users. |
|
68 | %br |
|
77 | %br |
|
69 | - @users.each do |user| |
|
78 | - @users.each do |user| |
|
70 | = user.login |
|
79 | = user.login |
|
71 | = ' ' |
|
80 | = ' ' |
|
72 | = user.full_name |
|
81 | = user.full_name |
|
73 | = ' ' |
|
82 | = ' ' |
|
74 | = "(#{user.remark})" if user.remark |
|
83 | = "(#{user.remark})" if user.remark |
|
75 | %br |
|
84 | %br |
|
76 |
|
85 | ||
|
77 |
|
86 |
@@ -1,11 +1,13 | |||||
|
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 |
|
10 | | |
|
12 | | |
|
11 | = link_to 'Back', :action => 'list' |
|
13 | = link_to 'Back', :action => 'list' |
@@ -1,101 +1,106 | |||||
|
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 |
|
5 | Quick Add |
|
5 | Quick Add |
|
6 | .panel-body |
|
6 | .panel-body |
|
7 | = form_tag( {method: 'post'}, {class: 'form-inline'}) do |
|
7 | = form_tag( {method: 'post'}, {class: 'form-inline'}) do |
|
8 | .form-group |
|
8 | .form-group |
|
9 | = label_tag 'user_login', 'Login' |
|
9 | = label_tag 'user_login', 'Login' |
|
10 | = text_field 'user', 'login', :size => 10,class: 'form-control' |
|
10 | = text_field 'user', 'login', :size => 10,class: 'form-control' |
|
11 | .form-group |
|
11 | .form-group |
|
12 | = label_tag 'user_full_name', 'Full Name' |
|
12 | = label_tag 'user_full_name', 'Full Name' |
|
13 | = text_field 'user', 'full_name', :size => 10,class: 'form-control' |
|
13 | = text_field 'user', 'full_name', :size => 10,class: 'form-control' |
|
14 | .form-group |
|
14 | .form-group |
|
15 | = label_tag 'user_password', 'Password' |
|
15 | = label_tag 'user_password', 'Password' |
|
16 | = text_field 'user', 'password', :size => 10,class: 'form-control' |
|
16 | = text_field 'user', 'password', :size => 10,class: 'form-control' |
|
17 | .form-group |
|
17 | .form-group |
|
18 | = label_tag 'user_password_confirmation', 'Confirm' |
|
18 | = label_tag 'user_password_confirmation', 'Confirm' |
|
19 | = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control' |
|
19 | = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control' |
|
20 | .form-group |
|
20 | .form-group |
|
21 | = label_tag 'user_email', 'email' |
|
21 | = label_tag 'user_email', 'email' |
|
22 | = text_field 'user', 'email', :size => 10,class: 'form-control' |
|
22 | = text_field 'user', 'email', :size => 10,class: 'form-control' |
|
23 | =submit_tag "Create", class: 'btn btn-primary' |
|
23 | =submit_tag "Create", class: 'btn btn-primary' |
|
24 |
|
24 | ||
|
25 | .panel.panel-primary |
|
25 | .panel.panel-primary |
|
26 | .panel-title.panel-heading |
|
26 | .panel-title.panel-heading |
|
27 | Import from site management |
|
27 | Import from site management |
|
28 | .panel-body |
|
28 | .panel-body |
|
29 | = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do |
|
29 | = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do |
|
30 | .form-group |
|
30 | .form-group |
|
31 | = label_tag :file, 'File:' |
|
31 | = label_tag :file, 'File:' |
|
32 | .input-group |
|
32 | .input-group |
|
33 | %span.input-group-btn |
|
33 | %span.input-group-btn |
|
34 | %span.btn.btn-default.btn-file |
|
34 | %span.btn.btn-default.btn-file |
|
35 | Browse |
|
35 | Browse |
|
36 | = file_field_tag 'file' |
|
36 | = file_field_tag 'file' |
|
37 | = text_field_tag '' , nil, {readonly: true, class: 'form-control'} |
|
37 | = text_field_tag '' , nil, {readonly: true, class: 'form-control'} |
|
38 | = submit_tag 'Submit', class: 'btn btn-default' |
|
38 | = submit_tag 'Submit', class: 'btn btn-default' |
|
39 |
|
39 | ||
|
40 |
|
40 | ||
|
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 '} |
|
49 |
|
49 | ||
|
50 | - if GraderConfiguration.multicontests? |
|
50 | - if GraderConfiguration.multicontests? |
|
51 | %br/ |
|
51 | %br/ |
|
52 | %b Multi-contest: |
|
52 | %b Multi-contest: |
|
53 | = link_to '[Manage bulk users in contests]', :action => 'contest_management' |
|
53 | = link_to '[Manage bulk users in contests]', :action => 'contest_management' |
|
54 | View users in: |
|
54 | View users in: |
|
55 | - @contests.each do |contest| |
|
55 | - @contests.each do |contest| |
|
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 |
|
73 | %th email |
|
73 | %th email |
|
74 | %th Remark |
|
74 | %th Remark |
|
75 | %th |
|
75 | %th |
|
76 | Activated |
|
76 | Activated |
|
77 | %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?] |
|
77 | %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?] |
|
78 | %th |
|
78 | %th |
|
79 | Enabled |
|
79 | Enabled |
|
80 | %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?] |
|
80 | %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?] |
|
81 | %th Last IP |
|
81 | %th Last IP |
|
82 | %th |
|
82 | %th |
|
83 | %th |
|
83 | %th |
|
84 | %th |
|
84 | %th |
|
85 | %th |
|
85 | %th |
|
86 | - for user in @users |
|
86 | - for user in @users |
|
87 | %tr |
|
87 | %tr |
|
88 | %td= link_to user.login, stat_user_path(user) |
|
88 | %td= link_to user.login, stat_user_path(user) |
|
89 | %td= user.full_name |
|
89 | %td= user.full_name |
|
90 | %td= user.email |
|
90 | %td= user.email |
|
91 | %td= user.remark |
|
91 | %td= user.remark |
|
92 | %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}") |
|
92 | %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}") |
|
93 | %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}") |
|
93 | %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}") |
|
94 | %td= user.last_ip |
|
94 | %td= user.last_ip |
|
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 | + }); |
@@ -1,66 +1,70 | |||||
|
1 | - content_for :header do |
|
1 | - content_for :header do |
|
2 | = javascript_include_tag 'local_jquery' |
|
2 | = javascript_include_tag 'local_jquery' |
|
3 |
|
3 | ||
|
4 | :javascript |
|
4 | :javascript |
|
5 | $(function () { |
|
5 | $(function () { |
|
6 | $('#submission_table').tablesorter({widgets: ['zebra']}); |
|
6 | $('#submission_table').tablesorter({widgets: ['zebra']}); |
|
7 | }); |
|
7 | }); |
|
8 |
|
8 | ||
|
9 | :css |
|
9 | :css |
|
10 | .fix-width { |
|
10 | .fix-width { |
|
11 | font-family: Droid Sans Mono,Consolas, monospace, mono, Courier New, Courier; |
|
11 | font-family: Droid Sans Mono,Consolas, monospace, mono, Courier New, Courier; |
|
12 | } |
|
12 | } |
|
13 |
|
13 | ||
|
14 | %h1= @user.full_name |
|
14 | %h1= @user.full_name |
|
15 |
|
15 | ||
|
16 | <b>Login:</b> #{@user.login} <br/> |
|
16 | <b>Login:</b> #{@user.login} <br/> |
|
17 | <b>Full name:</b> #{@user.full_name} <br /> |
|
17 | <b>Full name:</b> #{@user.full_name} <br /> |
|
18 |
|
18 | ||
|
19 |
|
19 | ||
|
20 | %h2 Problem Stat |
|
20 | %h2 Problem Stat |
|
21 | %table.info |
|
21 | %table.info |
|
22 | %thead |
|
22 | %thead |
|
23 | %tr.info-head |
|
23 | %tr.info-head |
|
24 | %th Stat |
|
24 | %th Stat |
|
25 | %th Value |
|
25 | %th Value |
|
26 | %tbody |
|
26 | %tbody |
|
27 | %tr{class: cycle('info-even','info-odd')} |
|
27 | %tr{class: cycle('info-even','info-odd')} |
|
28 | %td.info_param Submissions |
|
28 | %td.info_param Submissions |
|
29 | %td= @summary[:count] |
|
29 | %td= @summary[:count] |
|
30 | %tr{class: cycle('info-even','info-odd')} |
|
30 | %tr{class: cycle('info-even','info-odd')} |
|
31 | %td.info_param Solved/Attempted Problem |
|
31 | %td.info_param Solved/Attempted Problem |
|
32 | %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) |
|
32 | %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) |
|
33 |
|
33 | ||
|
34 | %h2 Submission History |
|
34 | %h2 Submission History |
|
35 |
|
35 | ||
|
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 |
|
43 | %th Problem code |
|
43 | %th Problem code |
|
44 | %th Problem full name |
|
44 | %th Problem full name |
|
45 | %th Language |
|
45 | %th Language |
|
46 | %th Submitted at |
|
46 | %th Submitted at |
|
47 | %th Result |
|
47 | %th Result |
|
48 | %th Score |
|
48 | %th Score |
|
49 | - if session[:admin] |
|
49 | - if session[:admin] |
|
50 | %th IP |
|
50 | %th IP |
|
51 | %tbody |
|
51 | %tbody |
|
52 | - @submission.each do |s| |
|
52 | - @submission.each do |s| |
|
53 | - next unless s.problem |
|
53 | - next unless s.problem |
|
54 | %tr |
|
54 | %tr |
|
55 | %td= link_to s.id, submission_path(s) |
|
55 | %td= link_to s.id, submission_path(s) |
|
56 | %td= link_to s.problem.name, stat_problem_path(s.problem) |
|
56 | %td= link_to s.problem.name, stat_problem_path(s.problem) |
|
57 | %td= s.problem.full_name |
|
57 | %td= s.problem.full_name |
|
58 | %td= s.language.pretty_name |
|
58 | %td= s.language.pretty_name |
|
59 | %td #{s.submitted_at.strftime('%Y-%m-%d %H:%M')} (#{time_ago_in_words(s.submitted_at)} ago) |
|
59 | %td #{s.submitted_at.strftime('%Y-%m-%d %H:%M')} (#{time_ago_in_words(s.submitted_at)} ago) |
|
60 | %td.fix-width= s.grader_comment |
|
60 | %td.fix-width= s.grader_comment |
|
61 | %td= ( s.try(:points) ? (s.points*100/s.problem.full_score) : '' ) |
|
61 | %td= ( s.try(:points) ? (s.points*100/s.problem.full_score) : '' ) |
|
62 | - if session[:admin] |
|
62 | - if session[:admin] |
|
63 | %td= s.ip_address |
|
63 | %td= s.ip_address |
|
64 |
|
64 | ||
|
65 |
|
65 | ||
|
66 |
|
66 | ||
|
|
67 | + :javascript | ||
|
|
68 | + $("#submission_table").DataTable({ | ||
|
|
69 | + paging: false | ||
|
|
70 | + }); |
@@ -20,53 +20,53 | |||||
|
20 |
|
20 | ||
|
21 | # Only load the plugins named here, in the order given (default is alphabetical). |
|
21 | # Only load the plugins named here, in the order given (default is alphabetical). |
|
22 | # :all can be used as a placeholder for all plugins not explicitly named. |
|
22 | # :all can be used as a placeholder for all plugins not explicitly named. |
|
23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] |
|
23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] |
|
24 |
|
24 | ||
|
25 | # Activate observers that should always be running. |
|
25 | # Activate observers that should always be running. |
|
26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer |
|
26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer |
|
27 |
|
27 | ||
|
28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
|
28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
|
29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. |
|
29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. |
|
30 | config.time_zone = 'UTC' |
|
30 | config.time_zone = 'UTC' |
|
31 |
|
31 | ||
|
32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
|
32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
|
33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
|
33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
|
34 | config.i18n.default_locale = :en |
|
34 | config.i18n.default_locale = :en |
|
35 |
|
35 | ||
|
36 | # Configure the default encoding used in templates for Ruby 1.9. |
|
36 | # Configure the default encoding used in templates for Ruby 1.9. |
|
37 | config.encoding = "utf-8" |
|
37 | config.encoding = "utf-8" |
|
38 |
|
38 | ||
|
39 | # Configure sensitive parameters which will be filtered from the log file. |
|
39 | # Configure sensitive parameters which will be filtered from the log file. |
|
40 | config.filter_parameters += [:password] |
|
40 | config.filter_parameters += [:password] |
|
41 |
|
41 | ||
|
42 | # Enable escaping HTML in JSON. |
|
42 | # Enable escaping HTML in JSON. |
|
43 | config.active_support.escape_html_entities_in_json = true |
|
43 | config.active_support.escape_html_entities_in_json = true |
|
44 |
|
44 | ||
|
45 | # Use SQL instead of Active Record's schema dumper when creating the database. |
|
45 | # Use SQL instead of Active Record's schema dumper when creating the database. |
|
46 | # This is necessary if your schema can't be completely dumped by the schema dumper, |
|
46 | # This is necessary if your schema can't be completely dumped by the schema dumper, |
|
47 | # like if you have constraints or database-specific column types |
|
47 | # like if you have constraints or database-specific column types |
|
48 | # config.active_record.schema_format = :sql |
|
48 | # config.active_record.schema_format = :sql |
|
49 |
|
49 | ||
|
50 | # Enable the asset pipeline |
|
50 | # Enable the asset pipeline |
|
51 | config.assets.enabled = true |
|
51 | config.assets.enabled = true |
|
52 |
|
52 | ||
|
53 | # Version of your assets, change this if you want to expire all your assets |
|
53 | # Version of your assets, change this if you want to expire all your assets |
|
54 | config.assets.version = '1.0' |
|
54 | config.assets.version = '1.0' |
|
55 |
|
55 | ||
|
56 | # ---------------- IMPORTANT ---------------------- |
|
56 | # ---------------- IMPORTANT ---------------------- |
|
57 | # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader" |
|
57 | # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader" |
|
58 | # moreover, using the following line instead also known to works |
|
58 | # moreover, using the following line instead also known to works |
|
59 | #config.action_controller.relative_url_root = '/grader' |
|
59 | #config.action_controller.relative_url_root = '/grader' |
|
60 |
|
60 | ||
|
61 | #font path |
|
61 | #font path |
|
62 | config.assets.paths << "#{Rails}/vendor/assets/fonts" |
|
62 | config.assets.paths << "#{Rails}/vendor/assets/fonts" |
|
63 |
|
63 | ||
|
64 | config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
64 | config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
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 |
|
72 | end |
|
72 | end |
@@ -1,98 +1,114 | |||||
|
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' |
|
5 |
|
6 | ||
|
6 | #logins |
|
7 | #logins |
|
7 | get 'login/login', to: 'login#login' |
|
8 | get 'login/login', to: 'login#login' |
|
8 |
|
9 | ||
|
9 | resources :contests |
|
10 | resources :contests |
|
10 |
|
11 | ||
|
11 | resources :sites |
|
12 | resources :sites |
|
12 |
|
13 | ||
|
13 | resources :announcements do |
|
14 | resources :announcements do |
|
14 | member do |
|
15 | member do |
|
15 | get 'toggle','toggle_front' |
|
16 | get 'toggle','toggle_front' |
|
16 | end |
|
17 | end |
|
17 | end |
|
18 | end |
|
18 |
|
19 | ||
|
19 | resources :problems do |
|
20 | resources :problems do |
|
20 | member do |
|
21 | member do |
|
21 | get 'toggle' |
|
22 | get 'toggle' |
|
22 | get 'toggle_test' |
|
23 | get 'toggle_test' |
|
23 | get 'toggle_view_testcase' |
|
24 | get 'toggle_view_testcase' |
|
24 | get 'stat' |
|
25 | get 'stat' |
|
25 | end |
|
26 | end |
|
26 | collection do |
|
27 | collection do |
|
27 | get 'turn_all_off' |
|
28 | get 'turn_all_off' |
|
28 | get 'turn_all_on' |
|
29 | get 'turn_all_on' |
|
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 |
|
36 | member do |
|
50 | member do |
|
37 | get 'download_input' |
|
51 | get 'download_input' |
|
38 | get 'download_sol' |
|
52 | get 'download_sol' |
|
39 | end |
|
53 | end |
|
40 | collection do |
|
54 | collection do |
|
41 | get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem' |
|
55 | get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem' |
|
42 | end |
|
56 | end |
|
43 | end |
|
57 | end |
|
44 |
|
58 | ||
|
45 | resources :grader_configuration, controller: 'configurations' |
|
59 | resources :grader_configuration, controller: 'configurations' |
|
46 |
|
60 | ||
|
47 | resources :users do |
|
61 | resources :users do |
|
48 | member do |
|
62 | member do |
|
49 | get 'toggle_activate', 'toggle_enable' |
|
63 | get 'toggle_activate', 'toggle_enable' |
|
50 | get 'stat' |
|
64 | get 'stat' |
|
51 | end |
|
65 | end |
|
52 | end |
|
66 | end |
|
53 |
|
67 | ||
|
54 | resources :submissions do |
|
68 | resources :submissions do |
|
55 | member do |
|
69 | member do |
|
56 | get 'download' |
|
70 | get 'download' |
|
57 | get 'compiler_msg' |
|
71 | get 'compiler_msg' |
|
58 | get 'rejudge' |
|
72 | get 'rejudge' |
|
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 |
|
66 |
|
80 | ||
|
67 |
|
81 | ||
|
68 |
|
82 | ||
|
69 | #main |
|
83 | #main |
|
70 | get "main/list" |
|
84 | get "main/list" |
|
71 | get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission' |
|
85 | get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission' |
|
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' |
|
78 | get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof' |
|
94 | get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof' |
|
79 | get "report/login" |
|
95 | get "report/login" |
|
80 | get 'report/max_score', to: 'report#max_score', as: 'report_max_score' |
|
96 | get 'report/max_score', to: 'report#max_score', as: 'report_max_score' |
|
81 | post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score' |
|
97 | post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score' |
|
82 |
|
98 | ||
|
83 |
|
99 | ||
|
84 | # |
|
100 | # |
|
85 | get 'tasks/view/:file.:ext' => 'tasks#view' |
|
101 | get 'tasks/view/:file.:ext' => 'tasks#view' |
|
86 | get 'tasks/download/:id/:file.:ext' => 'tasks#download' |
|
102 | get 'tasks/download/:id/:file.:ext' => 'tasks#download' |
|
87 | get 'heartbeat/:id/edit' => 'heartbeat#edit' |
|
103 | get 'heartbeat/:id/edit' => 'heartbeat#edit' |
|
88 |
|
104 | ||
|
89 | #grader |
|
105 | #grader |
|
90 | get 'graders/list', to: 'graders#list', as: 'grader_list' |
|
106 | get 'graders/list', to: 'graders#list', as: 'grader_list' |
|
91 |
|
107 | ||
|
92 |
|
108 | ||
|
93 | # See how all your routes lay out with "rake routes" |
|
109 | # See how all your routes lay out with "rake routes" |
|
94 |
|
110 | ||
|
95 | # This is a legacy wild controller route that's not recommended for RESTful applications. |
|
111 | # This is a legacy wild controller route that's not recommended for RESTful applications. |
|
96 | # Note: This route will make all actions in every controller accessible via GET requests. |
|
112 | # Note: This route will make all actions in every controller accessible via GET requests. |
|
97 | match ':controller(/:action(/:id))(.:format)', via: [:get, :post] |
|
113 | match ':controller(/:action(/:id))(.:format)', via: [:get, :post] |
|
98 | end |
|
114 | end |
@@ -1,283 +1,321 | |||||
|
1 | # encoding: UTF-8 |
|
1 | # encoding: UTF-8 |
|
2 | # This file is auto-generated from the current state of the database. Instead |
|
2 | # This file is auto-generated from the current state of the database. Instead |
|
3 | # of editing this file, please use the migrations feature of Active Record to |
|
3 | # of editing this file, please use the migrations feature of Active Record to |
|
4 | # incrementally modify your database, and then regenerate this schema definition. |
|
4 | # incrementally modify your database, and then regenerate this schema definition. |
|
5 | # |
|
5 | # |
|
6 | # Note that this schema.rb definition is the authoritative source for your |
|
6 | # Note that this schema.rb definition is the authoritative source for your |
|
7 | # database schema. If you need to create the application database on another |
|
7 | # database schema. If you need to create the application database on another |
|
8 | # system, you should be using db:schema:load, not running all the migrations |
|
8 | # system, you should be using db:schema:load, not running all the migrations |
|
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations |
|
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations |
|
10 | # you'll amass, the slower it'll run and the greater likelihood for issues). |
|
10 | # you'll amass, the slower it'll run and the greater likelihood for issues). |
|
11 | # |
|
11 | # |
|
12 | # It's strongly recommended that you check this file into your version control system. |
|
12 | # It's strongly recommended that you check this file into your version control system. |
|
13 |
|
13 | ||
|
14 |
- ActiveRecord::Schema.define(version: 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 |
|
18 | t.text "body", limit: 65535 |
|
18 | t.text "body", limit: 65535 |
|
19 | t.boolean "published" |
|
19 | t.boolean "published" |
|
20 | t.datetime "created_at", null: false |
|
20 | t.datetime "created_at", null: false |
|
21 | t.datetime "updated_at", null: false |
|
21 | t.datetime "updated_at", null: false |
|
22 | t.boolean "frontpage", default: false |
|
22 | t.boolean "frontpage", default: false |
|
23 | t.boolean "contest_only", default: false |
|
23 | t.boolean "contest_only", default: false |
|
24 | t.string "title", limit: 255 |
|
24 | t.string "title", limit: 255 |
|
25 | t.string "notes", limit: 255 |
|
25 | t.string "notes", limit: 255 |
|
26 | end |
|
26 | end |
|
27 |
|
27 | ||
|
28 | create_table "contests", force: :cascade do |t| |
|
28 | create_table "contests", force: :cascade do |t| |
|
29 | t.string "title", limit: 255 |
|
29 | t.string "title", limit: 255 |
|
30 | t.boolean "enabled" |
|
30 | t.boolean "enabled" |
|
31 | t.datetime "created_at", null: false |
|
31 | t.datetime "created_at", null: false |
|
32 | t.datetime "updated_at", null: false |
|
32 | t.datetime "updated_at", null: false |
|
33 | t.string "name", limit: 255 |
|
33 | t.string "name", limit: 255 |
|
34 | end |
|
34 | end |
|
35 |
|
35 | ||
|
36 | create_table "contests_problems", id: false, force: :cascade do |t| |
|
36 | create_table "contests_problems", id: false, force: :cascade do |t| |
|
37 | t.integer "contest_id", limit: 4 |
|
37 | t.integer "contest_id", limit: 4 |
|
38 | t.integer "problem_id", limit: 4 |
|
38 | t.integer "problem_id", limit: 4 |
|
39 | end |
|
39 | end |
|
40 |
|
40 | ||
|
41 | create_table "contests_users", id: false, force: :cascade do |t| |
|
41 | create_table "contests_users", id: false, force: :cascade do |t| |
|
42 | t.integer "contest_id", limit: 4 |
|
42 | t.integer "contest_id", limit: 4 |
|
43 | t.integer "user_id", limit: 4 |
|
43 | t.integer "user_id", limit: 4 |
|
44 | end |
|
44 | end |
|
45 |
|
45 | ||
|
46 | create_table "countries", force: :cascade do |t| |
|
46 | create_table "countries", force: :cascade do |t| |
|
47 | t.string "name", limit: 255 |
|
47 | t.string "name", limit: 255 |
|
48 | t.datetime "created_at", null: false |
|
48 | t.datetime "created_at", null: false |
|
49 | t.datetime "updated_at", null: false |
|
49 | t.datetime "updated_at", null: false |
|
50 | end |
|
50 | end |
|
51 |
|
51 | ||
|
52 | create_table "descriptions", force: :cascade do |t| |
|
52 | create_table "descriptions", force: :cascade do |t| |
|
53 | t.text "body", limit: 65535 |
|
53 | t.text "body", limit: 65535 |
|
54 | t.boolean "markdowned" |
|
54 | t.boolean "markdowned" |
|
55 | t.datetime "created_at", null: false |
|
55 | t.datetime "created_at", null: false |
|
56 | t.datetime "updated_at", null: false |
|
56 | t.datetime "updated_at", null: false |
|
57 | end |
|
57 | end |
|
58 |
|
58 | ||
|
59 | create_table "grader_configurations", force: :cascade do |t| |
|
59 | create_table "grader_configurations", force: :cascade do |t| |
|
60 | t.string "key", limit: 255 |
|
60 | t.string "key", limit: 255 |
|
61 | t.string "value_type", limit: 255 |
|
61 | t.string "value_type", limit: 255 |
|
62 | t.string "value", limit: 255 |
|
62 | t.string "value", limit: 255 |
|
63 | t.datetime "created_at", null: false |
|
63 | t.datetime "created_at", null: false |
|
64 | t.datetime "updated_at", null: false |
|
64 | t.datetime "updated_at", null: false |
|
65 | t.text "description", limit: 65535 |
|
65 | t.text "description", limit: 65535 |
|
66 | end |
|
66 | end |
|
67 |
|
67 | ||
|
68 | create_table "grader_processes", force: :cascade do |t| |
|
68 | create_table "grader_processes", force: :cascade do |t| |
|
69 | t.string "host", limit: 255 |
|
69 | t.string "host", limit: 255 |
|
70 | t.integer "pid", limit: 4 |
|
70 | t.integer "pid", limit: 4 |
|
71 | t.string "mode", limit: 255 |
|
71 | t.string "mode", limit: 255 |
|
72 | t.boolean "active" |
|
72 | t.boolean "active" |
|
73 | t.datetime "created_at", null: false |
|
73 | t.datetime "created_at", null: false |
|
74 | t.datetime "updated_at", null: false |
|
74 | t.datetime "updated_at", null: false |
|
75 | t.integer "task_id", limit: 4 |
|
75 | t.integer "task_id", limit: 4 |
|
76 | t.string "task_type", limit: 255 |
|
76 | t.string "task_type", limit: 255 |
|
77 | t.boolean "terminated" |
|
77 | t.boolean "terminated" |
|
78 | end |
|
78 | end |
|
79 |
|
79 | ||
|
80 | add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree |
|
80 | add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree |
|
81 |
|
81 | ||
|
|
82 | + create_table "groups", force: :cascade do |t| | ||
|
|
83 | + t.string "name", limit: 255 | ||
|
|
84 | + t.string "description", limit: 255 | ||
|
|
85 | + end | ||
|
|
86 | + | ||
|
|
87 | + create_table "groups_problems", id: false, force: :cascade do |t| | ||
|
|
88 | + t.integer "problem_id", limit: 4, null: false | ||
|
|
89 | + t.integer "group_id", limit: 4, null: false | ||
|
|
90 | + end | ||
|
|
91 | + | ||
|
|
92 | + add_index "groups_problems", ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id", using: :btree | ||
|
|
93 | + | ||
|
|
94 | + create_table "groups_users", id: false, force: :cascade do |t| | ||
|
|
95 | + t.integer "group_id", limit: 4, null: false | ||
|
|
96 | + t.integer "user_id", limit: 4, null: false | ||
|
|
97 | + end | ||
|
|
98 | + | ||
|
|
99 | + add_index "groups_users", ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id", using: :btree | ||
|
|
100 | + | ||
|
82 | create_table "heart_beats", force: :cascade do |t| |
|
101 | create_table "heart_beats", force: :cascade do |t| |
|
83 | t.integer "user_id", limit: 4 |
|
102 | t.integer "user_id", limit: 4 |
|
84 | t.string "ip_address", limit: 255 |
|
103 | t.string "ip_address", limit: 255 |
|
85 | t.datetime "created_at", null: false |
|
104 | t.datetime "created_at", null: false |
|
86 | t.datetime "updated_at", null: false |
|
105 | t.datetime "updated_at", null: false |
|
87 | t.string "status", limit: 255 |
|
106 | t.string "status", limit: 255 |
|
88 | end |
|
107 | end |
|
89 |
|
108 | ||
|
90 | add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree |
|
109 | add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree |
|
91 |
|
110 | ||
|
92 | create_table "languages", force: :cascade do |t| |
|
111 | create_table "languages", force: :cascade do |t| |
|
93 | t.string "name", limit: 10 |
|
112 | t.string "name", limit: 10 |
|
94 | t.string "pretty_name", limit: 255 |
|
113 | t.string "pretty_name", limit: 255 |
|
95 | t.string "ext", limit: 10 |
|
114 | t.string "ext", limit: 10 |
|
96 | t.string "common_ext", limit: 255 |
|
115 | t.string "common_ext", limit: 255 |
|
97 | end |
|
116 | end |
|
98 |
|
117 | ||
|
99 | create_table "logins", force: :cascade do |t| |
|
118 | create_table "logins", force: :cascade do |t| |
|
100 | t.integer "user_id", limit: 4 |
|
119 | t.integer "user_id", limit: 4 |
|
101 | t.string "ip_address", limit: 255 |
|
120 | t.string "ip_address", limit: 255 |
|
102 | t.datetime "created_at", null: false |
|
121 | t.datetime "created_at", null: false |
|
103 | t.datetime "updated_at", null: false |
|
122 | t.datetime "updated_at", null: false |
|
104 | end |
|
123 | end |
|
105 |
|
124 | ||
|
106 | create_table "messages", force: :cascade do |t| |
|
125 | create_table "messages", force: :cascade do |t| |
|
107 | t.integer "sender_id", limit: 4 |
|
126 | t.integer "sender_id", limit: 4 |
|
108 | t.integer "receiver_id", limit: 4 |
|
127 | t.integer "receiver_id", limit: 4 |
|
109 | t.integer "replying_message_id", limit: 4 |
|
128 | t.integer "replying_message_id", limit: 4 |
|
110 | t.text "body", limit: 65535 |
|
129 | t.text "body", limit: 65535 |
|
111 | t.boolean "replied" |
|
130 | t.boolean "replied" |
|
112 | t.datetime "created_at", null: false |
|
131 | t.datetime "created_at", null: false |
|
113 | t.datetime "updated_at", null: false |
|
132 | t.datetime "updated_at", null: false |
|
114 | end |
|
133 | end |
|
115 |
|
134 | ||
|
116 | create_table "problems", force: :cascade do |t| |
|
135 | create_table "problems", force: :cascade do |t| |
|
117 | t.string "name", limit: 30 |
|
136 | t.string "name", limit: 30 |
|
118 | t.string "full_name", limit: 255 |
|
137 | t.string "full_name", limit: 255 |
|
119 | t.integer "full_score", limit: 4 |
|
138 | t.integer "full_score", limit: 4 |
|
120 | t.date "date_added" |
|
139 | t.date "date_added" |
|
121 | t.boolean "available" |
|
140 | t.boolean "available" |
|
122 | t.string "url", limit: 255 |
|
141 | t.string "url", limit: 255 |
|
123 | t.integer "description_id", limit: 4 |
|
142 | t.integer "description_id", limit: 4 |
|
124 | t.boolean "test_allowed" |
|
143 | t.boolean "test_allowed" |
|
125 | t.boolean "output_only" |
|
144 | t.boolean "output_only" |
|
126 | t.string "description_filename", limit: 255 |
|
145 | t.string "description_filename", limit: 255 |
|
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 |
|
133 | t.string "action", limit: 255 |
|
161 | t.string "action", limit: 255 |
|
134 | end |
|
162 | end |
|
135 |
|
163 | ||
|
136 | create_table "rights_roles", id: false, force: :cascade do |t| |
|
164 | create_table "rights_roles", id: false, force: :cascade do |t| |
|
137 | t.integer "right_id", limit: 4 |
|
165 | t.integer "right_id", limit: 4 |
|
138 | t.integer "role_id", limit: 4 |
|
166 | t.integer "role_id", limit: 4 |
|
139 | end |
|
167 | end |
|
140 |
|
168 | ||
|
141 | add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree |
|
169 | add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree |
|
142 |
|
170 | ||
|
143 | create_table "roles", force: :cascade do |t| |
|
171 | create_table "roles", force: :cascade do |t| |
|
144 | t.string "name", limit: 255 |
|
172 | t.string "name", limit: 255 |
|
145 | end |
|
173 | end |
|
146 |
|
174 | ||
|
147 | create_table "roles_users", id: false, force: :cascade do |t| |
|
175 | create_table "roles_users", id: false, force: :cascade do |t| |
|
148 | t.integer "role_id", limit: 4 |
|
176 | t.integer "role_id", limit: 4 |
|
149 | t.integer "user_id", limit: 4 |
|
177 | t.integer "user_id", limit: 4 |
|
150 | end |
|
178 | end |
|
151 |
|
179 | ||
|
152 | add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree |
|
180 | add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree |
|
153 |
|
181 | ||
|
154 | create_table "sessions", force: :cascade do |t| |
|
182 | create_table "sessions", force: :cascade do |t| |
|
155 | t.string "session_id", limit: 255 |
|
183 | t.string "session_id", limit: 255 |
|
156 | t.text "data", limit: 65535 |
|
184 | t.text "data", limit: 65535 |
|
157 | t.datetime "updated_at" |
|
185 | t.datetime "updated_at" |
|
158 | end |
|
186 | end |
|
159 |
|
187 | ||
|
160 | add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree |
|
188 | add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree |
|
161 | add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree |
|
189 | add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree |
|
162 |
|
190 | ||
|
163 | create_table "sites", force: :cascade do |t| |
|
191 | create_table "sites", force: :cascade do |t| |
|
164 | t.string "name", limit: 255 |
|
192 | t.string "name", limit: 255 |
|
165 | t.boolean "started" |
|
193 | t.boolean "started" |
|
166 | t.datetime "start_time" |
|
194 | t.datetime "start_time" |
|
167 | t.datetime "created_at", null: false |
|
195 | t.datetime "created_at", null: false |
|
168 | t.datetime "updated_at", null: false |
|
196 | t.datetime "updated_at", null: false |
|
169 | t.integer "country_id", limit: 4 |
|
197 | t.integer "country_id", limit: 4 |
|
170 | t.string "password", limit: 255 |
|
198 | t.string "password", limit: 255 |
|
171 | end |
|
199 | end |
|
172 |
|
200 | ||
|
173 | create_table "submission_view_logs", force: :cascade do |t| |
|
201 | create_table "submission_view_logs", force: :cascade do |t| |
|
174 | t.integer "user_id", limit: 4 |
|
202 | t.integer "user_id", limit: 4 |
|
175 | t.integer "submission_id", limit: 4 |
|
203 | t.integer "submission_id", limit: 4 |
|
176 | t.datetime "created_at", null: false |
|
204 | t.datetime "created_at", null: false |
|
177 | t.datetime "updated_at", null: false |
|
205 | t.datetime "updated_at", null: false |
|
178 | end |
|
206 | end |
|
179 |
|
207 | ||
|
180 | create_table "submissions", force: :cascade do |t| |
|
208 | create_table "submissions", force: :cascade do |t| |
|
181 | t.integer "user_id", limit: 4 |
|
209 | t.integer "user_id", limit: 4 |
|
182 | t.integer "problem_id", limit: 4 |
|
210 | t.integer "problem_id", limit: 4 |
|
183 | t.integer "language_id", limit: 4 |
|
211 | t.integer "language_id", limit: 4 |
|
184 | t.text "source", limit: 65535 |
|
212 | t.text "source", limit: 65535 |
|
185 | t.binary "binary", limit: 65535 |
|
213 | t.binary "binary", limit: 65535 |
|
186 | t.datetime "submitted_at" |
|
214 | t.datetime "submitted_at" |
|
187 | t.datetime "compiled_at" |
|
215 | t.datetime "compiled_at" |
|
188 | t.text "compiler_message", limit: 65535 |
|
216 | t.text "compiler_message", limit: 65535 |
|
189 | t.datetime "graded_at" |
|
217 | t.datetime "graded_at" |
|
190 | t.integer "points", limit: 4 |
|
218 | t.integer "points", limit: 4 |
|
191 | t.text "grader_comment", limit: 65535 |
|
219 | t.text "grader_comment", limit: 65535 |
|
192 | t.integer "number", limit: 4 |
|
220 | t.integer "number", limit: 4 |
|
193 | t.string "source_filename", limit: 255 |
|
221 | t.string "source_filename", limit: 255 |
|
194 | t.float "max_runtime", limit: 24 |
|
222 | t.float "max_runtime", limit: 24 |
|
195 | t.integer "peak_memory", limit: 4 |
|
223 | t.integer "peak_memory", limit: 4 |
|
196 | t.integer "effective_code_length", limit: 4 |
|
224 | t.integer "effective_code_length", limit: 4 |
|
197 | t.string "ip_address", limit: 255 |
|
225 | t.string "ip_address", limit: 255 |
|
198 | end |
|
226 | end |
|
199 |
|
227 | ||
|
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" |
|
206 | t.integer "status", limit: 4 |
|
242 | t.integer "status", limit: 4 |
|
207 | t.datetime "updated_at" |
|
243 | t.datetime "updated_at" |
|
208 | end |
|
244 | end |
|
209 |
|
245 | ||
|
210 | add_index "tasks", ["submission_id"], name: "index_tasks_on_submission_id", using: :btree |
|
246 | add_index "tasks", ["submission_id"], name: "index_tasks_on_submission_id", using: :btree |
|
211 |
|
247 | ||
|
212 | create_table "test_pairs", force: :cascade do |t| |
|
248 | create_table "test_pairs", force: :cascade do |t| |
|
213 | t.integer "problem_id", limit: 4 |
|
249 | t.integer "problem_id", limit: 4 |
|
214 | t.text "input", limit: 16777215 |
|
250 | t.text "input", limit: 16777215 |
|
215 | t.text "solution", limit: 16777215 |
|
251 | t.text "solution", limit: 16777215 |
|
216 | t.datetime "created_at", null: false |
|
252 | t.datetime "created_at", null: false |
|
217 | t.datetime "updated_at", null: false |
|
253 | t.datetime "updated_at", null: false |
|
218 | end |
|
254 | end |
|
219 |
|
255 | ||
|
220 | create_table "test_requests", force: :cascade do |t| |
|
256 | create_table "test_requests", force: :cascade do |t| |
|
221 | t.integer "user_id", limit: 4 |
|
257 | t.integer "user_id", limit: 4 |
|
222 | t.integer "problem_id", limit: 4 |
|
258 | t.integer "problem_id", limit: 4 |
|
223 | t.integer "submission_id", limit: 4 |
|
259 | t.integer "submission_id", limit: 4 |
|
224 | t.string "input_file_name", limit: 255 |
|
260 | t.string "input_file_name", limit: 255 |
|
225 | t.string "output_file_name", limit: 255 |
|
261 | t.string "output_file_name", limit: 255 |
|
226 | t.string "running_stat", limit: 255 |
|
262 | t.string "running_stat", limit: 255 |
|
227 | t.integer "status", limit: 4 |
|
263 | t.integer "status", limit: 4 |
|
228 | t.datetime "updated_at", null: false |
|
264 | t.datetime "updated_at", null: false |
|
229 | t.datetime "submitted_at" |
|
265 | t.datetime "submitted_at" |
|
230 | t.datetime "compiled_at" |
|
266 | t.datetime "compiled_at" |
|
231 | t.text "compiler_message", limit: 65535 |
|
267 | t.text "compiler_message", limit: 65535 |
|
232 | t.datetime "graded_at" |
|
268 | t.datetime "graded_at" |
|
233 | t.string "grader_comment", limit: 255 |
|
269 | t.string "grader_comment", limit: 255 |
|
234 | t.datetime "created_at", null: false |
|
270 | t.datetime "created_at", null: false |
|
235 | t.float "running_time", limit: 24 |
|
271 | t.float "running_time", limit: 24 |
|
236 | t.string "exit_status", limit: 255 |
|
272 | t.string "exit_status", limit: 255 |
|
237 | t.integer "memory_usage", limit: 4 |
|
273 | t.integer "memory_usage", limit: 4 |
|
238 | end |
|
274 | end |
|
239 |
|
275 | ||
|
240 | add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id", using: :btree |
|
276 | add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id", using: :btree |
|
241 |
|
277 | ||
|
242 | create_table "testcases", force: :cascade do |t| |
|
278 | create_table "testcases", force: :cascade do |t| |
|
243 | t.integer "problem_id", limit: 4 |
|
279 | t.integer "problem_id", limit: 4 |
|
244 | t.integer "num", limit: 4 |
|
280 | t.integer "num", limit: 4 |
|
245 | t.integer "group", limit: 4 |
|
281 | t.integer "group", limit: 4 |
|
246 | t.integer "score", limit: 4 |
|
282 | t.integer "score", limit: 4 |
|
247 | t.text "input", limit: 4294967295 |
|
283 | t.text "input", limit: 4294967295 |
|
248 | t.text "sol", limit: 4294967295 |
|
284 | t.text "sol", limit: 4294967295 |
|
249 | t.datetime "created_at" |
|
285 | t.datetime "created_at" |
|
250 | t.datetime "updated_at" |
|
286 | t.datetime "updated_at" |
|
251 | end |
|
287 | end |
|
252 |
|
288 | ||
|
253 | add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id", using: :btree |
|
289 | add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id", using: :btree |
|
254 |
|
290 | ||
|
255 | create_table "user_contest_stats", force: :cascade do |t| |
|
291 | create_table "user_contest_stats", force: :cascade do |t| |
|
256 | t.integer "user_id", limit: 4 |
|
292 | t.integer "user_id", limit: 4 |
|
257 | t.datetime "started_at" |
|
293 | t.datetime "started_at" |
|
258 | t.datetime "created_at", null: false |
|
294 | t.datetime "created_at", null: false |
|
259 | t.datetime "updated_at", null: false |
|
295 | t.datetime "updated_at", null: false |
|
260 | t.boolean "forced_logout" |
|
296 | t.boolean "forced_logout" |
|
261 | end |
|
297 | end |
|
262 |
|
298 | ||
|
263 | create_table "users", force: :cascade do |t| |
|
299 | create_table "users", force: :cascade do |t| |
|
264 | t.string "login", limit: 50 |
|
300 | t.string "login", limit: 50 |
|
265 | t.string "full_name", limit: 255 |
|
301 | t.string "full_name", limit: 255 |
|
266 | t.string "hashed_password", limit: 255 |
|
302 | t.string "hashed_password", limit: 255 |
|
267 | t.string "salt", limit: 5 |
|
303 | t.string "salt", limit: 5 |
|
268 | t.string "alias", limit: 255 |
|
304 | t.string "alias", limit: 255 |
|
269 | t.string "email", limit: 255 |
|
305 | t.string "email", limit: 255 |
|
270 | t.integer "site_id", limit: 4 |
|
306 | t.integer "site_id", limit: 4 |
|
271 | t.integer "country_id", limit: 4 |
|
307 | t.integer "country_id", limit: 4 |
|
272 | t.boolean "activated", default: false |
|
308 | t.boolean "activated", default: false |
|
273 | t.datetime "created_at" |
|
309 | t.datetime "created_at" |
|
274 | t.datetime "updated_at" |
|
310 | t.datetime "updated_at" |
|
275 | t.boolean "enabled", default: true |
|
311 | t.boolean "enabled", default: true |
|
276 | t.string "remark", limit: 255 |
|
312 | t.string "remark", limit: 255 |
|
277 | t.string "last_ip", limit: 255 |
|
313 | t.string "last_ip", limit: 255 |
|
278 | t.string "section", limit: 255 |
|
314 | t.string "section", limit: 255 |
|
279 | end |
|
315 | end |
|
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 |
@@ -118,97 +118,106 | |||||
|
118 | }, |
|
118 | }, |
|
119 |
|
119 | ||
|
120 | { |
|
120 | { |
|
121 | :key => 'system.admin_email', |
|
121 | :key => 'system.admin_email', |
|
122 | :value_type => 'string', |
|
122 | :value_type => 'string', |
|
123 | :default_value => 'admin@admin.email' |
|
123 | :default_value => 'admin@admin.email' |
|
124 | }, |
|
124 | }, |
|
125 |
|
125 | ||
|
126 | { |
|
126 | { |
|
127 | :key => 'system.user_setting_enabled', |
|
127 | :key => 'system.user_setting_enabled', |
|
128 | :value_type => 'boolean', |
|
128 | :value_type => 'boolean', |
|
129 | :default_value => 'true', |
|
129 | :default_value => 'true', |
|
130 | :description => 'If this option is true, users can change their settings' |
|
130 | :description => 'If this option is true, users can change their settings' |
|
131 | }, |
|
131 | }, |
|
132 |
|
132 | ||
|
133 | { |
|
133 | { |
|
134 | :key => 'system.user_setting_enabled', |
|
134 | :key => 'system.user_setting_enabled', |
|
135 | :value_type => 'boolean', |
|
135 | :value_type => 'boolean', |
|
136 | :default_value => 'true', |
|
136 | :default_value => 'true', |
|
137 | :description => 'If this option is true, users can change their settings' |
|
137 | :description => 'If this option is true, users can change their settings' |
|
138 | }, |
|
138 | }, |
|
139 |
|
139 | ||
|
140 | # If Configuration['contest.test_request.early_timeout'] is true |
|
140 | # If Configuration['contest.test_request.early_timeout'] is true |
|
141 | # the user will not be able to use test request at 30 minutes |
|
141 | # the user will not be able to use test request at 30 minutes |
|
142 | # before the contest ends. |
|
142 | # before the contest ends. |
|
143 | { |
|
143 | { |
|
144 | :key => 'contest.test_request.early_timeout', |
|
144 | :key => 'contest.test_request.early_timeout', |
|
145 | :value_type => 'boolean', |
|
145 | :value_type => 'boolean', |
|
146 | :default_value => 'false' |
|
146 | :default_value => 'false' |
|
147 | }, |
|
147 | }, |
|
148 |
|
148 | ||
|
149 | { |
|
149 | { |
|
150 | :key => 'system.multicontests', |
|
150 | :key => 'system.multicontests', |
|
151 | :value_type => 'boolean', |
|
151 | :value_type => 'boolean', |
|
152 | :default_value => 'false' |
|
152 | :default_value => 'false' |
|
153 | }, |
|
153 | }, |
|
154 |
|
154 | ||
|
155 | { |
|
155 | { |
|
156 | :key => 'contest.confirm_indv_contest_start', |
|
156 | :key => 'contest.confirm_indv_contest_start', |
|
157 | :value_type => 'boolean', |
|
157 | :value_type => 'boolean', |
|
158 | :default_value => 'false' |
|
158 | :default_value => 'false' |
|
159 | }, |
|
159 | }, |
|
160 |
|
160 | ||
|
161 | { |
|
161 | { |
|
162 | :key => 'contest.default_contest_name', |
|
162 | :key => 'contest.default_contest_name', |
|
163 | :value_type => 'string', |
|
163 | :value_type => 'string', |
|
164 | :default_value => 'none', |
|
164 | :default_value => 'none', |
|
165 | :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest." |
|
165 | :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest." |
|
166 | - } |
|
166 | + }, |
|
|
167 | + | ||
|
|
168 | + { | ||
|
|
169 | + :key => 'system.use_problem_group', | ||
|
|
170 | + :value_type => 'boolean', | ||
|
|
171 | + :default_value => 'false', | ||
|
|
172 | + :description => "If true, available problem to the user will be only ones associated with the group of the user." | ||
|
|
173 | + }, | ||
|
|
174 | + | ||
|
|
175 | + | ||
|
167 |
|
176 | ||
|
168 | ] |
|
177 | ] |
|
169 |
|
178 | ||
|
170 |
|
179 | ||
|
171 | def create_configuration_key(key, |
|
180 | def create_configuration_key(key, |
|
172 | value_type, |
|
181 | value_type, |
|
173 | default_value, |
|
182 | default_value, |
|
174 | description='') |
|
183 | description='') |
|
175 | conf = (GraderConfiguration.find_by_key(key) || |
|
184 | conf = (GraderConfiguration.find_by_key(key) || |
|
176 | GraderConfiguration.new(:key => key, |
|
185 | GraderConfiguration.new(:key => key, |
|
177 | :value_type => value_type, |
|
186 | :value_type => value_type, |
|
178 | :value => default_value)) |
|
187 | :value => default_value)) |
|
179 | conf.description = description |
|
188 | conf.description = description |
|
180 | conf.save |
|
189 | conf.save |
|
181 | end |
|
190 | end |
|
182 |
|
191 | ||
|
183 | def seed_config |
|
192 | def seed_config |
|
184 | CONFIGURATIONS.each do |conf| |
|
193 | CONFIGURATIONS.each do |conf| |
|
185 | if conf.has_key? :description |
|
194 | if conf.has_key? :description |
|
186 | desc = conf[:description] |
|
195 | desc = conf[:description] |
|
187 | else |
|
196 | else |
|
188 | desc = '' |
|
197 | desc = '' |
|
189 | end |
|
198 | end |
|
190 | create_configuration_key(conf[:key], |
|
199 | create_configuration_key(conf[:key], |
|
191 | conf[:value_type], |
|
200 | conf[:value_type], |
|
192 | conf[:default_value], |
|
201 | conf[:default_value], |
|
193 | desc) |
|
202 | desc) |
|
194 | end |
|
203 | end |
|
195 | end |
|
204 | end |
|
196 |
|
205 | ||
|
197 | def seed_roles |
|
206 | def seed_roles |
|
198 | return if Role.find_by_name('admin') |
|
207 | return if Role.find_by_name('admin') |
|
199 |
|
208 | ||
|
200 | role = Role.create(:name => 'admin') |
|
209 | role = Role.create(:name => 'admin') |
|
201 | user_admin_right = Right.create(:name => 'user_admin', |
|
210 | user_admin_right = Right.create(:name => 'user_admin', |
|
202 | :controller => 'user_admin', |
|
211 | :controller => 'user_admin', |
|
203 | :action => 'all') |
|
212 | :action => 'all') |
|
204 | problem_admin_right = Right.create(:name=> 'problem_admin', |
|
213 | problem_admin_right = Right.create(:name=> 'problem_admin', |
|
205 | :controller => 'problems', |
|
214 | :controller => 'problems', |
|
206 | :action => 'all') |
|
215 | :action => 'all') |
|
207 |
|
216 | ||
|
208 | graders_right = Right.create(:name => 'graders_admin', |
|
217 | graders_right = Right.create(:name => 'graders_admin', |
|
209 | :controller => 'graders', |
|
218 | :controller => 'graders', |
|
210 | :action => 'all') |
|
219 | :action => 'all') |
|
211 |
|
220 | ||
|
212 | role.rights << user_admin_right; |
|
221 | role.rights << user_admin_right; |
|
213 | role.rights << problem_admin_right; |
|
222 | role.rights << problem_admin_right; |
|
214 | role.rights << graders_right; |
|
223 | role.rights << graders_right; |
@@ -1,73 +1,74 | |||||
|
1 | module GraderScript |
|
1 | module GraderScript |
|
2 |
|
2 | ||
|
3 | def self.grader_control_enabled? |
|
3 | def self.grader_control_enabled? |
|
4 | if defined? GRADER_ROOT_DIR |
|
4 | if defined? GRADER_ROOT_DIR |
|
5 | GRADER_ROOT_DIR != '' |
|
5 | GRADER_ROOT_DIR != '' |
|
6 | else |
|
6 | else |
|
7 | false |
|
7 | false |
|
8 | end |
|
8 | end |
|
9 | end |
|
9 | end |
|
10 |
|
10 | ||
|
11 | def self.raw_dir |
|
11 | def self.raw_dir |
|
12 | File.join GRADER_ROOT_DIR, "raw" |
|
12 | File.join GRADER_ROOT_DIR, "raw" |
|
13 | end |
|
13 | end |
|
14 |
|
14 | ||
|
15 | def self.call_grader(params) |
|
15 | def self.call_grader(params) |
|
16 | if GraderScript.grader_control_enabled? |
|
16 | if GraderScript.grader_control_enabled? |
|
17 | cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params |
|
17 | cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params |
|
18 | system(cmd) |
|
18 | system(cmd) |
|
19 | end |
|
19 | end |
|
20 | end |
|
20 | end |
|
21 |
|
21 | ||
|
22 | def self.stop_grader(pid) |
|
22 | def self.stop_grader(pid) |
|
23 | GraderScript.call_grader "stop #{pid}" |
|
23 | GraderScript.call_grader "stop #{pid}" |
|
24 | end |
|
24 | end |
|
25 |
|
25 | ||
|
26 | def self.stop_graders(pids) |
|
26 | def self.stop_graders(pids) |
|
27 | pid_str = (pids.map { |process| process.pid.to_s }).join ' ' |
|
27 | pid_str = (pids.map { |process| process.pid.to_s }).join ' ' |
|
28 | GraderScript.call_grader "stop #{pid_str}" |
|
28 | GraderScript.call_grader "stop #{pid_str}" |
|
29 | end |
|
29 | end |
|
30 |
|
30 | ||
|
31 | def self.start_grader(env) |
|
31 | def self.start_grader(env) |
|
32 | GraderScript.call_grader "#{env} queue --err-log &" |
|
32 | GraderScript.call_grader "#{env} queue --err-log &" |
|
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, |
|
39 | memory_limit=32, |
|
40 | memory_limit=32, |
|
40 | checker_name='text') |
|
41 | checker_name='text') |
|
41 | if GraderScript.grader_control_enabled? |
|
42 | if GraderScript.grader_control_enabled? |
|
42 | cur_dir = `pwd`.chomp |
|
43 | cur_dir = `pwd`.chomp |
|
43 | Dir.chdir(GRADER_ROOT_DIR) |
|
44 | Dir.chdir(GRADER_ROOT_DIR) |
|
44 |
|
45 | ||
|
45 | script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem") |
|
46 | script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem") |
|
46 | cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" + |
|
47 | cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" + |
|
47 | " -t #{time_limit} -m #{memory_limit}" |
|
48 | " -t #{time_limit} -m #{memory_limit}" |
|
48 |
|
49 | ||
|
49 | output = `#{cmd}` |
|
50 | output = `#{cmd}` |
|
50 |
|
51 | ||
|
51 | Dir.chdir(cur_dir) |
|
52 | Dir.chdir(cur_dir) |
|
52 |
|
53 | ||
|
53 | return "import CMD: #{cmd}\n" + output |
|
54 | return "import CMD: #{cmd}\n" + output |
|
54 | end |
|
55 | end |
|
55 | return '' |
|
56 | return '' |
|
56 | end |
|
57 | end |
|
57 |
|
58 | ||
|
58 | def self.call_import_testcase(problem_name) |
|
59 | def self.call_import_testcase(problem_name) |
|
59 | if GraderScript.grader_control_enabled? |
|
60 | if GraderScript.grader_control_enabled? |
|
60 | cur_dir = `pwd`.chomp |
|
61 | cur_dir = `pwd`.chomp |
|
61 | Dir.chdir(GRADER_ROOT_DIR) |
|
62 | Dir.chdir(GRADER_ROOT_DIR) |
|
62 |
|
63 | ||
|
63 | script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase") |
|
64 | script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase") |
|
64 | cmd = "#{script_name} #{problem_name}" |
|
65 | cmd = "#{script_name} #{problem_name}" |
|
65 |
|
66 | ||
|
66 | output = `#{cmd}` |
|
67 | output = `#{cmd}` |
|
67 |
|
68 | ||
|
68 | Dir.chdir(cur_dir) |
|
69 | Dir.chdir(cur_dir) |
|
69 | return "Testcase import result:\n" + output |
|
70 | return "Testcase import result:\n" + output |
|
70 | end |
|
71 | end |
|
71 | end |
|
72 | end |
|
72 |
|
73 | ||
|
73 | end |
|
74 | end |
@@ -1,102 +1,104 | |||||
|
1 | require 'tmpdir' |
|
1 | require 'tmpdir' |
|
2 |
|
2 | ||
|
3 | class TestdataImporter |
|
3 | class TestdataImporter |
|
4 |
|
4 | ||
|
5 | attr :log_msg |
|
5 | attr :log_msg |
|
6 |
|
6 | ||
|
7 | def initialize(problem) |
|
7 | def initialize(problem) |
|
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) |
|
16 |
|
17 | ||
|
17 | dirname = extract(tempfile) |
|
18 | dirname = extract(tempfile) |
|
18 | return false if not dirname |
|
19 | return false if not dirname |
|
19 | if not import_to_db |
|
20 | if not import_to_db |
|
20 | @log_msg = GraderScript.call_import_problem(@problem.name, |
|
21 | @log_msg = GraderScript.call_import_problem(@problem.name, |
|
21 | dirname, |
|
22 | dirname, |
|
22 | time_limit, |
|
23 | time_limit, |
|
23 | memory_limit, |
|
24 | memory_limit, |
|
24 | checker_name) |
|
25 | checker_name) |
|
25 | else |
|
26 | else |
|
26 | # Import test data to test pairs. |
|
27 | # Import test data to test pairs. |
|
27 |
|
28 | ||
|
28 | @problem.test_pairs.clear |
|
29 | @problem.test_pairs.clear |
|
29 | if import_test_pairs(dirname) |
|
30 | if import_test_pairs(dirname) |
|
30 | test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}" |
|
31 | test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}" |
|
31 | @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)" |
|
32 | @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)" |
|
32 | else |
|
33 | else |
|
33 | @log_msg = "Importing test pair failed. (0 test pairs imported)" |
|
34 | @log_msg = "Importing test pair failed. (0 test pairs imported)" |
|
34 | end |
|
35 | end |
|
35 | end |
|
36 | end |
|
36 |
|
37 | ||
|
37 | @log_msg << import_problem_description(dirname) |
|
38 | @log_msg << import_problem_description(dirname) |
|
38 | @log_msg << import_problem_pdf(dirname) |
|
39 | @log_msg << import_problem_pdf(dirname) |
|
39 | @log_msg << import_full_score(dirname) |
|
40 | @log_msg << import_full_score(dirname) |
|
40 |
|
41 | ||
|
41 | #import test data |
|
42 | #import test data |
|
42 | @log_msg << GraderScript.call_import_testcase(@problem.name) |
|
43 | @log_msg << GraderScript.call_import_testcase(@problem.name) |
|
43 |
|
44 | ||
|
44 | return true |
|
45 | return true |
|
45 | end |
|
46 | end |
|
46 |
|
47 | ||
|
47 | protected |
|
48 | protected |
|
48 |
|
49 | ||
|
49 | def self.long_ext(filename) |
|
50 | def self.long_ext(filename) |
|
50 | i = filename.index('.') |
|
51 | i = filename.index('.') |
|
51 | len = filename.length |
|
52 | len = filename.length |
|
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) |
|
58 |
|
60 | ||
|
59 | extract_dir = File.join(GraderScript.raw_dir, @problem.name) |
|
61 | extract_dir = File.join(GraderScript.raw_dir, @problem.name) |
|
60 | if File.exists? extract_dir |
|
62 | if File.exists? extract_dir |
|
61 | backup_count = 0 |
|
63 | backup_count = 0 |
|
62 | begin |
|
64 | begin |
|
63 | backup_count += 1 |
|
65 | backup_count += 1 |
|
64 | backup_dirname = "#{extract_dir}.backup.#{backup_count}" |
|
66 | backup_dirname = "#{extract_dir}.backup.#{backup_count}" |
|
65 | end while File.exists? backup_dirname |
|
67 | end while File.exists? backup_dirname |
|
66 | File.rename(extract_dir, backup_dirname) |
|
68 | File.rename(extract_dir, backup_dirname) |
|
67 | end |
|
69 | end |
|
68 | Dir.mkdir extract_dir |
|
70 | Dir.mkdir extract_dir |
|
69 |
|
71 | ||
|
70 | if ext=='.tar.gz' or ext=='.tgz' |
|
72 | if ext=='.tar.gz' or ext=='.tgz' |
|
71 | cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}" |
|
73 | cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}" |
|
72 | elsif ext=='.tar' |
|
74 | elsif ext=='.tar' |
|
73 | cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}" |
|
75 | cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}" |
|
74 | elsif ext=='.zip' |
|
76 | elsif ext=='.zip' |
|
75 | cmd = "unzip -o #{testdata_filename} -d #{extract_dir}" |
|
77 | cmd = "unzip -o #{testdata_filename} -d #{extract_dir}" |
|
76 | else |
|
78 | else |
|
77 | return nil |
|
79 | return nil |
|
78 | end |
|
80 | end |
|
79 |
|
81 | ||
|
80 | system(cmd) |
|
82 | system(cmd) |
|
81 |
|
83 | ||
|
82 | files = Dir["#{extract_dir}/**/*1*.in"] |
|
84 | files = Dir["#{extract_dir}/**/*1*.in"] |
|
83 | return nil if files.length==0 |
|
85 | return nil if files.length==0 |
|
84 |
|
86 | ||
|
85 | File.delete(testdata_filename) |
|
87 | File.delete(testdata_filename) |
|
86 |
|
88 | ||
|
87 | return File.dirname(files[0]) |
|
89 | return File.dirname(files[0]) |
|
88 | end |
|
90 | end |
|
89 |
|
91 | ||
|
90 | def save_testdata_file(tempfile) |
|
92 | def save_testdata_file(tempfile) |
|
91 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
93 | ext = TestdataImporter.long_ext(tempfile.original_filename) |
|
92 | testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}") |
|
94 | testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}") |
|
93 |
|
95 | ||
|
94 | return nil if tempfile=="" |
|
96 | return nil if tempfile=="" |
|
95 |
|
97 | ||
|
96 | if tempfile.instance_of?(Tempfile) |
|
98 | if tempfile.instance_of?(Tempfile) |
|
97 | tempfile.close |
|
99 | tempfile.close |
|
98 | FileUtils.move(tempfile.path,testdata_filename) |
|
100 | FileUtils.move(tempfile.path,testdata_filename) |
|
99 | else |
|
101 | else |
|
100 | File.open(testdata_filename, "wb") do |f| |
|
102 | File.open(testdata_filename, "wb") do |f| |
|
101 | f.write(tempfile.read) |
|
103 | f.write(tempfile.read) |
|
102 | end |
|
104 | end |
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