Description:
Merge pull request #20 from nattee/master feature merge
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

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,2
1 + module GroupsHelper
2 + end
@@ -0,0 +1,2
1 + module TagsHelper
2 + 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,5
1 + %h1 New group
2 +
3 + = render 'form'
4 +
5 + = link_to 'Back', groups_path
@@ -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,5
1 + %h1 New tag
2 +
3 + = render 'form'
4 +
5 + = link_to 'Back', tags_path
@@ -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 - #add bootstrap
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 mail
230 mail
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.13.6
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, except: [ :submission ]
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 - # clear forced logout flag for multicontests contest change
27 + # clear forced logout flag for multicontests contest change
18 - if GraderConfiguration.multicontests?
28 + if GraderConfiguration.multicontests?
19 - contest_stat = user.contest_stat
29 + contest_stat = user.contest_stat
20 - if contest_stat.respond_to? :forced_logout
30 + if contest_stat.respond_to? :forced_logout
21 - if contest_stat.forced_logout
31 + if contest_stat.forced_logout
22 - contest_stat.forced_logout = false
32 + contest_stat.forced_logout = false
23 - contest_stat.save
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 - redirect_to :controller => 'main', :action => 'login'
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 - year = params[:date_added][:year].to_i
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, :direct_edit_submission, :download, :edit]
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 - if !problem or !problem.available or !@user.can_view_problem? problem
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.gmtime
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 = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
159 time_left = "&nbsp;&nbsp;" + (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-block',
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 - if submission.nil?
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_short_time(submission.submitted_at.localtime)
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 - = "#{format_short_time(submission.graded_at.localtime)} "
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 - %strong View:
22 + %strong View:
21 - - if GraderConfiguration.show_grading_result
23 + - if GraderConfiguration.show_grading_result
22 - = link_to '[detailed result]', :action => 'result', :id => submission.id
24 + = 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'}
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]', :action => 'submission', :id => @task.submission.id
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 Listing problems
3 + %h1 Problems
4 %p
4 %p
5 - = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
5 + = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
6 - = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
6 + = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
7 - = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm'
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]', :action => '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 - &nbsp;&nbsp;&nbsp;
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 + &nbsp;&nbsp;&nbsp;
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 Full name
83 + %th Name
68 - %th Available
84 + %th Full name
69 - %th Date added
85 + %th Tags
70 - - if GraderConfiguration.multicontests?
86 + %th Available
71 - %th Contests
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.full_name
97 + %td= problem.name
80 - %td= problem.available
98 + %td= problem.full_name
81 - %td= problem.date_added
82 - - if GraderConfiguration.multicontests?
83 %td
99 %td
84 - - problem.contests.each do |contest|
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.info-head
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= time_ago_in_words(sub.submitted_at) + " ago"
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', true
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 - = render :partial => 'submission_short',
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 Listing users
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 - Display all users.
61 + -# Display all users.
62 - \#{link_to '[show in pages]', :action => 'index', :page => '1'}
62 + -# \#{link_to '[show in pages]', :action => 'index', :page => '1'}
63 - - else
63 + -# - else
64 - Display in pages.
64 + -# Display in pages.
65 - \#{link_to '[display all]', :action => 'index', :page => 'all'} |
65 + -# \#{link_to '[display all]', :action => 'index', :page => 'all'} |
66 - \#{will_paginate @users, :container => false}
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', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
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: 20170427070345) do
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