Description:
merge bootstrap back to algo
Commit status:
[Not Reviewed]
References:
merge algo
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r704:4afa0e9c66d2 - - 103 files changed: 1725 inserted, 816 deleted

@@ -0,0 +1,6
1 + #js for announcement
2 + $ ->
3 + $(document).ajaxError (event, jqxhr, settings, exception) ->
4 + if jqxhr.status
5 + alert 'We\'re sorry, but something went wrong (' + jqxhr.status + ')'
6 + return
new file 100644
new file 100644
new file 100644
@@ -0,0 +1,52
1 + $(document).on 'change', '.btn-file :file', ->
2 + input = $(this)
3 + numFiles = if input.get(0).files then input.get(0).files.length else 1
4 + label = input.val().replace(/\\/g, '/').replace(/.*\//, '')
5 + input.trigger 'fileselect', [
6 + numFiles
7 + label
8 + ]
9 + return
10 +
11 +
12 + # document ready
13 +
14 + $ ->
15 + $(".select2").select2()
16 + #$(".bootstrap-switch").bootstrapSwitch()
17 + #$(".bootstrap-toggle").bootstrapToggle()
18 + $('.btn-file :file').on 'fileselect', (event, numFiles, label) ->
19 + input = $(this).parents('.input-group').find(':text')
20 + log = if numFiles > 1 then numFiles + ' files selected' else label
21 + if input.length
22 + input.val log
23 + else
24 + if log
25 + alert log
26 + return
27 + $(".go-button").on 'click', (event) ->
28 + link = $(this).attr("data-source")
29 + url = $(link).val()
30 + if url
31 + window.location.href = url
32 + return
33 + $('.ajax-toggle').on 'click', (event) ->
34 + target = $(event.target)
35 + target.removeClass 'btn-default'
36 + target.removeClass 'btn-success'
37 + target.addClass 'btn-warning'
38 + target.text '...'
39 + return
40 +
41 +
42 + #ace editor
43 + if $("#editor").length > 0
44 + e = ace.edit("editor")
45 + e.setTheme('ace/theme/merbivore')
46 + e.getSession().setTabSize(2)
47 + e.getSession().setUseSoftTabs(true)
48 +
49 + #best in place
50 + jQuery(".best_in_place").best_in_place()
51 +
52 + return
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
@@ -0,0 +1,32
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://jashkenas.github.com/coffee-script/
4 +
5 +
6 + $ ->
7 + $("#live_submit").on "click", (event) ->
8 + h = $("#editor_text")
9 + e = ace.edit("editor")
10 + h.val(e.getValue())
11 +
12 + $("#language_id").on "change", (event) ->
13 + text = $("#language_id option:selected").text()
14 + mode = 'ace/mode/c_cpp'
15 + switch text
16 + when 'Pascal' then mode = 'ace/mode/pascal'
17 + when 'C++','C' then mode = 'ace/mode/c_cpp'
18 + when 'Ruby' then mode = 'ace/mode/ruby'
19 + when 'Python' then mode = 'ace/mode/python'
20 + when 'Java' then mode = 'ace/mode/java'
21 + editor = ace.edit('editor')
22 + editor.getSession().setMode(mode)
23 +
24 + e = ace.edit("editor")
25 + e.setValue($("#text_haha").val())
26 + e.gotoLine(1)
27 + $("#language_id").trigger('change')
28 +
29 +
30 +
31 +
32 + return
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
new file 100644
@@ -0,0 +1,4
1 + // Place all the styles related to the sources controller here.
2 + // They will automatically be included in application.css.
3 + // You can use Sass (SCSS) here: http://sass-lang.com/
4 +
new file 100644
new file 100644
new file 100644
new file 100644
@@ -0,0 +1,27
1 + class SourcesController < ApplicationController
2 + before_filter :authenticate
3 +
4 + def direct_edit
5 + @problem = Problem.find(params[:pid])
6 + @source = ''
7 + end
8 +
9 + def direct_edit_submission
10 + @submission = Submission.find(params[:sid])
11 + @source = @submission.source.to_s
12 + @problem = @submission.problem
13 + @lang_id = @submission.language.id
14 + render 'direct_edit'
15 + end
16 +
17 + def get_latest_submission_status
18 + @problem = Problem.find(params[:pid])
19 + @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
20 + puts User.find(params[:uid]).login
21 + puts Problem.find(params[:pid]).name
22 + puts 'nil' unless @submission
23 + respond_to do |format|
24 + format.js
25 + end
26 + end
27 + end
@@ -0,0 +1,2
1 + module SourcesHelper
2 + end
@@ -0,0 +1,37
1 + %h1 Listing announcements
2 +
3 + = link_to '+ Add announcement', new_announcement_path, class: 'btn btn-success'
4 + %br
5 + %br
6 +
7 + %table.table.table-striped
8 + %thead
9 + %th Updated
10 + %th Announcement
11 + %th Author
12 + %th{style: 'width: 100px'} Published?
13 + %th{style: 'width: 100px'}Front?
14 + %th
15 + %th
16 + - for announcement in @announcements
17 + %tr
18 + - @announcement = announcement
19 + %td= time_ago_in_words announcement.updated_at
20 + %td
21 + - if !announcement.title.blank?
22 + %b Title:
23 + = h announcement.title
24 + %br/
25 + - if !announcement.notes.blank?
26 + %b
27 + Notes: #{h announcement.notes}
28 + %br/
29 + = h announcement.body
30 + %td= h announcement.author
31 + %td= toggle_button(announcement.published?, toggle_announcement_url(@announcement), "announcement_toggle_#{@announcement.id}", {size: 'btn-sm'})
32 + %td= toggle_button(announcement.frontpage?, toggle_front_announcement_url(@announcement), "announcement_toggle_front_#{@announcement.id}", {size: 'btn-sm'})
33 + %td= link_to 'Edit', edit_announcement_path(announcement), class: 'btn btn-block btn-sm btn-info'
34 + %td= link_to 'Destroy', announcement, :confirm => 'Are you sure?', :method => :delete, class: "btn btn-block btn-sm btn-danger"
35 + %br
36 +
37 + = link_to '+ Add announcement', new_announcement_path, class: 'btn btn-success'
@@ -0,0 +1,26
1 +
2 + - if submission.nil?
3 + = "-"
4 + - else
5 + - if submission.graded_at.nil?
6 + =t 'main.submitted_at'
7 + = format_short_time(submission.submitted_at.localtime)
8 + - else
9 + = t 'main.graded_at'
10 + = "#{format_short_time(submission.graded_at.localtime)}, "
11 + - if GraderConfiguration['ui.show_score']
12 + = t 'main.score'
13 + = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 + = " ["
15 + %tt
16 + = submission.grader_comment
17 + = "]"
18 + - if GraderConfiguration.show_grading_result
19 + = " | "
20 + = link_to '[detailed result]', :action => 'result', :id => submission.id
21 + = " | "
22 + = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true})
23 + = " | "
24 + = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id})
25 + //= " | "
26 + //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id)
@@ -0,0 +1,7
1 + :plain
2 + var t = $("#{button_id}");
3 + t.removeClass('btn-default');
4 + t.removeClass('btn-success');
5 + t.removeClass('btn-warning');
6 + t.addClass("btn-#{button_on ? 'success' : 'default'}");
7 + t.text("#{button_on ? 'Yes' : 'No'}");
@@ -0,0 +1,84
1 + %header.navbar.navbar-default.navbar-fixed-top
2 + %nav
3 + .container-fluid
4 + .navbar-header
5 + %a.navbar-brand{href: main_list_path}
6 + %span.glyphicon.glyphicon-home
7 + MAIN
8 + .collapse.navbar-collapse
9 + %ul.nav.navbar-nav
10 + - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
11 + //= add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list')
12 + %li.dropdown
13 + %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
14 + = "#{I18n.t 'menu.submissions'}"
15 + %span.caret
16 + %ul.dropdown-menu
17 + = add_menu("View", 'main', 'submission')
18 + = add_menu("Self Test", 'test', 'index')
19 + - if GraderConfiguration['right.user_hall_of_fame']
20 + = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
21 + / display MODE button (with countdown in contest mode)
22 + - if GraderConfiguration.analysis_mode?
23 + %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
24 + - elsif GraderConfiguration.time_limit_mode?
25 + - if @current_user.contest_finished?
26 + %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
27 + - elsif !@current_user.contest_started?
28 + %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
29 + - else
30 + %div.navbar-btn.btn.btn-primary#countdown asdf
31 + :javascript
32 + $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
33 + / admin section
34 + - if (@current_user!=nil) and (session[:admin])
35 + %li.dropdown
36 + %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
37 + Manage
38 + %span.caret
39 + %ul.dropdown-menu
40 + = add_menu( 'Announcements', 'announcements', 'index')
41 + = add_menu( 'Problems', 'problems', 'index')
42 + = add_menu( 'Users', 'user_admin', 'index')
43 + = add_menu( 'Graders', 'graders', 'list')
44 + = add_menu( 'Message ', 'messages', 'console')
45 + %li.divider{role: 'separator'}
46 + = add_menu( 'System config', 'configurations', 'index')
47 + %li.divider{role: 'separator'}
48 + = add_menu( 'Sites', 'sites', 'index')
49 + = add_menu( 'Contests', 'contest_management', 'index')
50 + %li.dropdown
51 + %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
52 + Report
53 + %span.caret
54 + %ul.dropdown-menu
55 + = add_menu( 'Results', 'user_admin', 'user_stat')
56 + = add_menu( 'Report', 'report', 'multiple_login')
57 + - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
58 + =link_to "#{ungraded} backlogs!",
59 + grader_list_path,
60 + class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
61 +
62 + %ul.nav.navbar-nav.navbar-right
63 + = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
64 + = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
65 + - if GraderConfiguration['system.user_setting_enabled']
66 + = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
67 + = 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'}})
68 +
69 + /
70 + - if (@current_user!=nil) and (session[:admin])
71 + %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
72 + .container-fluid
73 + .collapse.navbar-collapse
74 + %ul.nav.navbar-nav
75 + = add_menu( '[Announcements]', 'announcements', 'index')
76 + = add_menu( '[Msg console]', 'messages', 'console')
77 + = add_menu( '[Problems]', 'problems', 'index')
78 + = add_menu( '[Users]', 'user_admin', 'index')
79 + = add_menu( '[Results]', 'user_admin', 'user_stat')
80 + = add_menu( '[Report]', 'report', 'multiple_login')
81 + = add_menu( '[Graders]', 'graders', 'list')
82 + = add_menu( '[Contests]', 'contest_management', 'index')
83 + = add_menu( '[Sites]', 'sites', 'index')
84 + = add_menu( '[System config]', 'configurations', 'index')
@@ -0,0 +1,15
1 + <!DOCTYPE html>
2 + %html
3 + %head
4 + %title= GraderConfiguration['contest.name']
5 + = stylesheet_link_tag "application", params[:controller], :media => "all"
6 + = javascript_include_tag "application", params[:controller]
7 + = csrf_meta_tags
8 + = content_for :header
9 + = yield :head
10 +
11 + %body
12 + = render 'layouts/header'
13 +
14 + = content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil
15 + = yield
@@ -0,0 +1,17
1 + %tr
2 + %td
3 + = "#{problem.name}"
4 + %td
5 + = "#{problem.full_name}"
6 + = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
7 + %td
8 + = @prob_submissions[problem.id][:count]
9 + = link_to "[subs]", main_submission_path(problem.id)
10 + %td
11 + = render :partial => 'submission_short',
12 + :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name }
13 + %td
14 + - if @prob_submissions[problem.id][:submission]
15 + = link_to 'Edit', direct_edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success'
16 + - else
17 + = link_to 'New', direct_edit_path(problem.id), class: 'btn btn-success'
@@ -0,0 +1,20
1 + = form_tag({:action => 'submit'}, :multipart => true, class: 'form') do
2 + - if @submission and @submission.errors.any?
3 + #error_explanation
4 + .alert.alert-danger
5 + %h3= "#{pluralize(@submission.errors.count, "error")} prohibited this user from being saved:"
6 + %ul
7 + - @submission.errors.full_messages.each do |msg|
8 + %li= msg
9 + .form-group
10 + = label_tag :submission, 'Problem:'
11 + = select 'submission', 'problem_id', [[(t 'main.specified_in_header'),'-1']] + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]}, {:selected => '-1'}, { class: 'select2 form-control' }
12 + .form-group
13 + = label_tag :file, 'File:'
14 + .input-group
15 + %span.input-group-btn
16 + %span.btn.btn-default.btn-file
17 + Browse
18 + = file_field_tag 'file'
19 + = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
20 + = submit_tag 'Submit', class: 'btn btn-default'
@@ -0,0 +1,50
1 + - content_for :head do
2 + = stylesheet_link_tag 'problems'
3 + %h1 Listing problems
4 + %p
5 + = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
6 + = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
7 + = link_to 'Import problems', {:action => 'import'}, 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'
10 + .submitbox
11 + = form_tag :action => 'quick_create' do
12 + %b Quick New:
13 + %label{:for => "problem_name"} Name
14 + = text_field 'problem', 'name'
15 + |
16 + %label{:for => "problem_full_name"} Full name
17 + = text_field 'problem', 'full_name'
18 + = submit_tag "Create"
19 + %table.table.table-condense.table-hover
20 + %thead
21 + %th Name
22 + %th Full name
23 + %th.text-right Full score
24 + %th Date added
25 + %th.text-center
26 + Avail?
27 + %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
28 + %th.text-center
29 + Test?
30 + %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
31 + - if GraderConfiguration.multicontests?
32 + %th Contests
33 + - for problem in @problems
34 + %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
35 + - @problem=problem
36 + %td= in_place_editor_field :problem, :name, {}, :rows=>1
37 + %td= in_place_editor_field :problem, :full_name, {}, :rows=>1
38 + %td.text-right= in_place_editor_field :problem, :full_score, {}, :rows=>1
39 + %td= problem.date_added
40 + %td= toggle_button(@problem.available?, toggle_problem_url(@problem), "problem-avail-#{@problem.id}")
41 + %td= toggle_button(@problem.test_allowed?, toggle_test_problem_url(@problem), "problem-test-#{@problem.id}")
42 + - if GraderConfiguration.multicontests?
43 + %td
44 + = problem.contests.collect { |c| c.name }.join(', ')
45 + %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
46 + %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
47 + %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
48 + %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-xs btn-block'
49 + %br/
50 + = link_to '[New problem]', :action => 'new'
@@ -0,0 +1,8
1 + = render partial: 'toggle_button',
2 + locals: {button_id: "#problem-avail-#{@problem.id}",button_on: @problem.available }
3 + :plain
4 + r = $("#prob-#{@problem.id}");
5 + r.removeClass('success');
6 + r.removeClass('danger');
7 + r.addClass("#{@problem.available? ? 'success' : 'danger'}");
8 +
@@ -0,0 +1,2
1 + = render partial: 'toggle_button',
2 + locals: {button_id: "#problem-test-#{@problem.id}",button_on: @problem.test_allowed?}
@@ -0,0 +1,24
1 + %h1 Editing site
2 + = error_messages_for :site
3 + = form_for(@site) do |f|
4 + %p
5 + %b Name
6 + %br/
7 + = f.text_field :name
8 + %p
9 + %b Password
10 + %br/
11 + = f.text_field :password
12 + %p
13 + %b Started
14 + %br/
15 + = f.check_box :started
16 + %p
17 + %b Start time
18 + %br/
19 + = f.datetime_select :start_time, :include_blank => true
20 + %p
21 + = f.submit "Update"
22 + = link_to 'Show', @site
23 + |
24 + = link_to 'Back', sites_path
@@ -0,0 +1,24
1 + %h1 Listing sites
2 + %table.table.table-striped
3 + %tr
4 + %th Name
5 + %th Password
6 + %th Started
7 + %th Start time
8 + %th
9 + %th
10 + %th
11 + - for site in @sites
12 + - background = "white"
13 + - background = "grey" if (site.started==true) and (site.finished? == true)
14 + - background = "lightgreen" if (site.started==true) and (site.finished? != true)
15 + %tr{:style => "background: #{background};"}
16 + %td= h site.name
17 + %td= h site.password
18 + %td= h site.started
19 + %td= h site.start_time
20 + %td= link_to 'Show', site, class: 'btn btn-default'
21 + %td= link_to 'Edit', edit_site_path(site), class: 'btn btn-default'
22 + %td= link_to 'Destroy', site, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-default'
23 + %br/
24 + = link_to '+ New site', new_site_path, class: 'btn btn-success'
@@ -0,0 +1,22
1 + %h1 New site
2 + = error_messages_for :site
3 + = form_for(@site) do |f|
4 + %p
5 + %b Name
6 + %br/
7 + = f.text_field :name
8 + %p
9 + %b Password
10 + %br/
11 + = f.text_field :password
12 + %p
13 + %b Started
14 + %br/
15 + = f.check_box :started
16 + %p
17 + %b Start time
18 + %br/
19 + = f.datetime_select :start_time
20 + %p
21 + = f.submit "Create"
22 + = link_to 'Back', sites_path
@@ -0,0 +1,15
1 + %p
2 + %b Name:
3 + = h @site.name
4 + %p
5 + %b Password:
6 + = h @site.password
7 + %p
8 + %b Started:
9 + = h @site.started
10 + %p
11 + %b Start time:
12 + = h @site.start_time
13 + = link_to 'Edit', edit_site_path(@site)
14 + |
15 + = link_to 'Back', sites_path
@@ -0,0 +1,264
1 + %h2 Live submit
2 + %br
3 +
4 + %textarea#text_haha{style: "display:none"}~ @source
5 + .container
6 + .row
7 + .col-md-12
8 + .alert.alert-info
9 + Write your code in the following box, choose language, and click submit button when finished
10 + .row
11 + .col-md-8
12 + %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
13 + .col-md-4
14 + = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15 +
16 + = hidden_field_tag 'editor_text', @source
17 + = hidden_field_tag 'submission[problem_id]', @problem.id
18 + .form-group
19 + = label_tag "Task:"
20 + = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
21 +
22 + .form-group
23 + = 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 + .form-group
26 + = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
27 + data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
28 + .panel.panel-info
29 + .panel-heading
30 + Latest Submission Status
31 + .panel-body
32 + - if @submission
33 + = render :partial => 'submission_short',
34 + :locals => {:submission => @submission, :problem_name => @problem.name }
35 + .row
36 + .col-md-12
37 + %h2 Console
38 + %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
39 +
40 + :javascript
41 + $(document).ready(function() {
42 + brython();
43 + });
44 +
45 +
46 + %script#__main__{type:'text/python3'}
47 + :plain
48 + import sys
49 + import traceback
50 +
51 + from browser import document as doc
52 + from browser import window, alert, console
53 +
54 + _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
55 + for supporting Python development. See www.python.org for more information."""
56 +
57 + _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
58 + All Rights Reserved.
59 +
60 + Copyright (c) 2001-2013 Python Software Foundation.
61 + All Rights Reserved.
62 +
63 + Copyright (c) 2000 BeOpen.com.
64 + All Rights Reserved.
65 +
66 + Copyright (c) 1995-2001 Corporation for National Research Initiatives.
67 + All Rights Reserved.
68 +
69 + Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
70 + All Rights Reserved."""
71 +
72 + _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
73 + All rights reserved.
74 +
75 + Redistribution and use in source and binary forms, with or without
76 + modification, are permitted provided that the following conditions are met:
77 +
78 + Redistributions of source code must retain the above copyright notice, this
79 + list of conditions and the following disclaimer. Redistributions in binary
80 + form must reproduce the above copyright notice, this list of conditions and
81 + the following disclaimer in the documentation and/or other materials provided
82 + with the distribution.
83 + Neither the name of the <ORGANIZATION> nor the names of its contributors may
84 + be used to endorse or promote products derived from this software without
85 + specific prior written permission.
86 +
87 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
88 + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
89 + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
90 + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
91 + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
92 + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
93 + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
94 + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
95 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
96 + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
97 + POSSIBILITY OF SUCH DAMAGE.
98 + """
99 +
100 + def credits():
101 + print(_credits)
102 + credits.__repr__ = lambda:_credits
103 +
104 + def copyright():
105 + print(_copyright)
106 + copyright.__repr__ = lambda:_copyright
107 +
108 + def license():
109 + print(_license)
110 + license.__repr__ = lambda:_license
111 +
112 + def write(data):
113 + doc['console'].value += str(data)
114 +
115 +
116 + sys.stdout.write = sys.stderr.write = write
117 + history = []
118 + current = 0
119 + _status = "main" # or "block" if typing inside a block
120 +
121 + # execution namespace
122 + editor_ns = {'credits':credits,
123 + 'copyright':copyright,
124 + 'license':license,
125 + '__name__':'__main__'}
126 +
127 + def cursorToEnd(*args):
128 + pos = len(doc['console'].value)
129 + doc['console'].setSelectionRange(pos, pos)
130 + doc['console'].scrollTop = doc['console'].scrollHeight
131 +
132 + def get_col(area):
133 + # returns the column num of cursor
134 + sel = doc['console'].selectionStart
135 + lines = doc['console'].value.split('\n')
136 + for line in lines[:-1]:
137 + sel -= len(line) + 1
138 + return sel
139 +
140 +
141 + def myKeyPress(event):
142 + global _status, current
143 + if event.keyCode == 9: # tab key
144 + event.preventDefault()
145 + doc['console'].value += " "
146 + elif event.keyCode == 13: # return
147 + src = doc['console'].value
148 + if _status == "main":
149 + currentLine = src[src.rfind('>>>') + 4:]
150 + elif _status == "3string":
151 + currentLine = src[src.rfind('>>>') + 4:]
152 + currentLine = currentLine.replace('\n... ', '\n')
153 + else:
154 + currentLine = src[src.rfind('...') + 4:]
155 + if _status == 'main' and not currentLine.strip():
156 + doc['console'].value += '\n>>> '
157 + event.preventDefault()
158 + return
159 + doc['console'].value += '\n'
160 + history.append(currentLine)
161 + current = len(history)
162 + if _status == "main" or _status == "3string":
163 + try:
164 + _ = editor_ns['_'] = eval(currentLine, editor_ns)
165 + if _ is not None:
166 + write(repr(_)+'\n')
167 + doc['console'].value += '>>> '
168 + _status = "main"
169 + except IndentationError:
170 + doc['console'].value += '... '
171 + _status = "block"
172 + except SyntaxError as msg:
173 + if str(msg) == 'invalid syntax : triple string end not found' or \
174 + str(msg).startswith('Unbalanced bracket'):
175 + doc['console'].value += '... '
176 + _status = "3string"
177 + elif str(msg) == 'eval() argument must be an expression':
178 + try:
179 + exec(currentLine, editor_ns)
180 + except:
181 + traceback.print_exc()
182 + doc['console'].value += '>>> '
183 + _status = "main"
184 + elif str(msg) == 'decorator expects function':
185 + doc['console'].value += '... '
186 + _status = "block"
187 + else:
188 + traceback.print_exc()
189 + doc['console'].value += '>>> '
190 + _status = "main"
191 + except:
192 + traceback.print_exc()
193 + doc['console'].value += '>>> '
194 + _status = "main"
195 + elif currentLine == "": # end of block
196 + block = src[src.rfind('>>>') + 4:].splitlines()
197 + block = [block[0]] + [b[4:] for b in block[1:]]
198 + block_src = '\n'.join(block)
199 + # status must be set before executing code in globals()
200 + _status = "main"
201 + try:
202 + _ = exec(block_src, editor_ns)
203 + if _ is not None:
204 + print(repr(_))
205 + except:
206 + traceback.print_exc()
207 + doc['console'].value += '>>> '
208 + else:
209 + doc['console'].value += '... '
210 +
211 + cursorToEnd()
212 + event.preventDefault()
213 +
214 + def myKeyDown(event):
215 + global _status, current
216 + if event.keyCode == 37: # left arrow
217 + sel = get_col(doc['console'])
218 + if sel < 5:
219 + event.preventDefault()
220 + event.stopPropagation()
221 + elif event.keyCode == 36: # line start
222 + pos = doc['console'].selectionStart
223 + col = get_col(doc['console'])
224 + doc['console'].setSelectionRange(pos - col + 4, pos - col + 4)
225 + event.preventDefault()
226 + elif event.keyCode == 38: # up
227 + if current > 0:
228 + pos = doc['console'].selectionStart
229 + col = get_col(doc['console'])
230 + # remove current line
231 + doc['console'].value = doc['console'].value[:pos - col + 4]
232 + current -= 1
233 + doc['console'].value += history[current]
234 + event.preventDefault()
235 + elif event.keyCode == 40: # down
236 + if current < len(history) - 1:
237 + pos = doc['console'].selectionStart
238 + col = get_col(doc['console'])
239 + # remove current line
240 + doc['console'].value = doc['console'].value[:pos - col + 4]
241 + current += 1
242 + doc['console'].value += history[current]
243 + event.preventDefault()
244 + elif event.keyCode == 8: # backspace
245 + src = doc['console'].value
246 + lstart = src.rfind('\n')
247 + if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6):
248 + event.preventDefault()
249 + event.stopPropagation()
250 +
251 +
252 + doc['console'].bind('keypress', myKeyPress)
253 + doc['console'].bind('keydown', myKeyDown)
254 + doc['console'].bind('click', cursorToEnd)
255 + v = sys.implementation.version
256 + doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % (
257 + v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion)
258 + #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.'
259 + doc['console'].focus()
260 + cursorToEnd()
261 +
262 +
263 +
264 +
@@ -0,0 +1,2
1 + :javascript
2 + $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}")
@@ -0,0 +1,100
1 + %h1 Listing users
2 +
3 + .panel.panel-primary
4 + .panel-title.panel-heading
5 + Quick Add
6 + .panel-body
7 + = form_tag( {method: 'post'}, {class: 'form-inline'}) do
8 + .form-group
9 + = label_tag 'user_login', 'Login'
10 + = text_field 'user', 'login', :size => 10,class: 'form-control'
11 + .form-group
12 + = label_tag 'user_full_name', 'Full Name'
13 + = text_field 'user', 'full_name', :size => 10,class: 'form-control'
14 + .form-group
15 + = label_tag 'user_password', 'Password'
16 + = text_field 'user', 'password', :size => 10,class: 'form-control'
17 + .form-group
18 + = label_tag 'user_password_confirmation', 'Confirm'
19 + = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control'
20 + .form-group
21 + = label_tag 'user_email', 'email'
22 + = text_field 'user', 'email', :size => 10,class: 'form-control'
23 + =submit_tag "Create", class: 'btn btn-primary'
24 +
25 + .panel.panel-primary
26 + .panel-title.panel-heading
27 + Import from site management
28 + .panel-body
29 + = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
30 + .form-group
31 + = label_tag :file, 'File:'
32 + .input-group
33 + %span.input-group-btn
34 + %span.btn.btn-default.btn-file
35 + Browse
36 + = file_field_tag 'file'
37 + = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
38 + = submit_tag 'Submit', class: 'btn btn-default'
39 +
40 +
41 + %p
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 '}
44 + = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
45 + = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
46 + = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
47 + = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
48 +
49 + - if GraderConfiguration.multicontests?
50 + %br/
51 + %b Multi-contest:
52 + = link_to '[Manage bulk users in contests]', :action => 'contest_management'
53 + View users in:
54 + - @contests.each do |contest|
55 + = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
56 + = link_to "[no contest]", :action => 'contests', :id => 'none'
57 +
58 + Total #{@user_count} users |
59 + - if !@paginated
60 + Display all users.
61 + \#{link_to '[show in pages]', :action => 'index', :page => '1'}
62 + - else
63 + Display in pages.
64 + \#{link_to '[display all]', :action => 'index', :page => 'all'} |
65 + \#{will_paginate @users, :container => false}
66 +
67 +
68 + %table.table.table-hover.table-condense
69 + %thead
70 + %th Login
71 + %th Full name
72 + %th email
73 + %th Remark
74 + %th
75 + Activated
76 + %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
77 + %th
78 + Enabled
79 + %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
80 + %th Last IP
81 + %th
82 + %th
83 + %th
84 + %th
85 + - for user in @users
86 + %tr
87 + %td= link_to user.login, controller: :users, :action => 'profile', :id => user
88 + %td= user.full_name
89 + %td= user.email
90 + %td= user.remark
91 + %td= toggle_button(user.activated?, toggle_activate_user_url(user),"toggle_activate_user_#{user.id}")
92 + %td= toggle_button(user.enabled?, toggle_enable_user_url(user),"toggle_enable_user_#{user.id}")
93 + %td= user.last_ip
94 + %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 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
96 + %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 + %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-xs btn-block'
98 + %br/
99 + = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
100 + = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
@@ -0,0 +1,3
1 + class ActiveRecord::ConnectionAdapters::Mysql2Adapter
2 + NATIVE_DATABASE_TYPES[:primary_key] = "int(11) auto_increment PRIMARY KEY"
3 + end
@@ -0,0 +1,12
1 + require 'spec_helper'
2 +
3 + describe SourcesController do
4 +
5 + describe "GET 'direct_edit'" do
6 + it "returns http success" do
7 + get 'direct_edit'
8 + response.should be_success
9 + end
10 + end
11 +
12 + end
@@ -0,0 +1,15
1 + require 'spec_helper'
2 +
3 + # Specs in this file have access to a helper object that includes
4 + # the SourcesHelper. For example:
5 + #
6 + # describe SourcesHelper do
7 + # describe "string concat" do
8 + # it "concats two strings with spaces" do
9 + # expect(helper.concat_strings("this","that")).to eq("this that")
10 + # end
11 + # end
12 + # end
13 + describe SourcesHelper do
14 + pending "add some examples to (or delete) #{__FILE__}"
15 + end
@@ -0,0 +1,5
1 + require 'spec_helper'
2 +
3 + describe "sources/direct_edit.html.haml" do
4 + pending "add some examples to (or delete) #{__FILE__}"
5 + end
@@ -1,63 +1,80
1 1 source 'https://rubygems.org'
2 2
3 3 gem 'rails', '3.2.21'
4 4
5 + gem 'select2-rails'
6 +
5 7 # Bundle edge Rails instead:
6 8 # gem 'rails', :git => 'git://github.com/rails/rails.git'
7 9
8 10 gem 'mysql2'
9 11
10 12 # Gems used only for assets and not required
11 13 # in production environments by default.
12 14 group :assets do
13 15 gem 'sass-rails', '~> 3.2.6'
14 16 gem 'coffee-rails', '~> 3.2.2'
15 17
16 18 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
17 19 # gem 'therubyracer', :platforms => :ruby
18 20
19 21 gem 'uglifier'
20 22 end
21 23
22 24 gem 'prototype-rails'
23 25
24 26 # To use ActiveModel has_secure_password
25 27 # gem 'bcrypt-ruby', '~> 3.0.0'
26 28
27 29 # To use Jbuilder templates for JSON
28 30 # gem 'jbuilder'
29 31
30 32 # Use unicorn as the app server
31 33 # gem 'unicorn'
32 34
33 35 # Deploy with Capistrano
34 36 # gem 'capistrano'
35 37
36 38 # To use debugger
37 39 # gem 'debugger'
38 40 #
39 41
40 42 #in-place editor
41 43 gem 'best_in_place', '~> 3.0.1'
42 44
43 45 # jquery addition
44 46 gem 'jquery-rails'
45 47 gem 'jquery-ui-sass-rails'
46 48 gem 'jquery-timepicker-addon-rails'
47 49 gem 'jquery-tablesorter'
50 + gem 'jquery-countdown-rails'
48 51
49 52 #syntax highlighter
50 53 gem 'rouge'
51 54
55 + #add bootstrap
56 + gem 'bootstrap-sass', '~> 3.2.0'
57 + gem 'bootstrap-switch-rails'
58 + gem 'bootstrap-toggle-rails'
59 + gem 'autoprefixer-rails'
60 +
61 + #bootstrap sortable
62 + gem 'momentjs-rails'
63 + gem 'rails_bootstrap_sortable'
64 +
65 + #ace editor
66 + gem 'ace-rails-ap'
67 +
52 68 gem 'haml'
69 + gem 'haml-rails'
53 70 gem 'mail'
54 71 gem 'rdiscount'
55 72 gem 'test-unit'
56 73 gem 'will_paginate', '~> 3.0.7'
57 74 gem 'dynamic_form'
58 75 gem 'in_place_editing'
59 76 gem 'verification', :git => 'https://github.com/sikachu/verification.git'
60 77
61 78 group :test, :development do
62 79 gem 'rspec-rails', '~> 2.99.0'
63 80 end
@@ -1,175 +1,205
1 1 GIT
2 2 remote: https://github.com/sikachu/verification.git
3 3 revision: 76eaf51b13276ecae54bd9cd115832595d2ff56d
4 4 specs:
5 5 verification (1.0.3)
6 6 actionpack (>= 3.0.0, < 5.0)
7 7 activesupport (>= 3.0.0, < 5.0)
8 8
9 9 GEM
10 10 remote: https://rubygems.org/
11 11 specs:
12 + ace-rails-ap (4.0.2)
12 13 actionmailer (3.2.21)
13 14 actionpack (= 3.2.21)
14 15 mail (~> 2.5.4)
15 16 actionpack (3.2.21)
16 17 activemodel (= 3.2.21)
17 18 activesupport (= 3.2.21)
18 19 builder (~> 3.0.0)
19 20 erubis (~> 2.7.0)
20 21 journey (~> 1.0.4)
21 22 rack (~> 1.4.5)
22 23 rack-cache (~> 1.2)
23 24 rack-test (~> 0.6.1)
24 25 sprockets (~> 2.2.1)
25 26 activemodel (3.2.21)
26 27 activesupport (= 3.2.21)
27 28 builder (~> 3.0.0)
28 29 activerecord (3.2.21)
29 30 activemodel (= 3.2.21)
30 31 activesupport (= 3.2.21)
31 32 arel (~> 3.0.2)
32 33 tzinfo (~> 0.3.29)
33 34 activeresource (3.2.21)
34 35 activemodel (= 3.2.21)
35 36 activesupport (= 3.2.21)
36 37 activesupport (3.2.21)
37 38 i18n (~> 0.6, >= 0.6.4)
38 39 multi_json (~> 1.0)
39 40 arel (3.0.3)
41 + autoprefixer-rails (6.0.3)
42 + execjs
43 + json
40 44 best_in_place (3.0.3)
41 45 actionpack (>= 3.2)
42 46 railties (>= 3.2)
47 + bootstrap-sass (3.2.0.2)
48 + sass (~> 3.2)
49 + bootstrap-switch-rails (3.3.3)
50 + bootstrap-toggle-rails (2.2.1.0)
43 51 builder (3.0.4)
44 52 coffee-rails (3.2.2)
45 53 coffee-script (>= 2.2.0)
46 54 railties (~> 3.2.0)
47 55 coffee-script (2.3.0)
48 56 coffee-script-source
49 57 execjs
50 58 coffee-script-source (1.9.0)
51 59 diff-lcs (1.2.5)
52 60 dynamic_form (1.1.4)
53 61 erubis (2.7.0)
54 62 execjs (2.3.0)
55 63 haml (4.0.6)
56 64 tilt
65 + haml-rails (0.4)
66 + actionpack (>= 3.1, < 4.1)
67 + activesupport (>= 3.1, < 4.1)
68 + haml (>= 3.1, < 4.1)
69 + railties (>= 3.1, < 4.1)
57 70 hike (1.2.3)
58 71 i18n (0.7.0)
59 72 in_place_editing (1.2.0)
60 73 journey (1.0.4)
74 + jquery-countdown-rails (2.0.2)
61 75 jquery-rails (3.1.2)
62 76 railties (>= 3.0, < 5.0)
63 77 thor (>= 0.14, < 2.0)
64 78 jquery-tablesorter (1.13.4)
65 79 railties (>= 3.1, < 5)
66 80 jquery-timepicker-addon-rails (1.4.1)
67 81 railties (>= 3.1)
68 82 jquery-ui-rails (4.0.3)
69 83 jquery-rails
70 84 railties (>= 3.1.0)
71 85 jquery-ui-sass-rails (4.0.3.0)
72 86 jquery-rails
73 87 jquery-ui-rails (= 4.0.3)
74 88 railties (>= 3.1.0)
75 89 json (1.8.2)
76 90 mail (2.5.4)
77 91 mime-types (~> 1.16)
78 92 treetop (~> 1.4.8)
79 93 mime-types (1.25.1)
94 + momentjs-rails (2.11.1)
95 + railties (>= 3.1)
80 96 multi_json (1.10.1)
81 97 mysql2 (0.3.20)
82 98 polyglot (0.3.5)
83 99 power_assert (0.2.2)
84 100 prototype-rails (3.2.1)
85 101 rails (~> 3.2)
86 102 rack (1.4.5)
87 103 rack-cache (1.2)
88 104 rack (>= 0.4)
89 105 rack-ssl (1.3.4)
90 106 rack
91 107 rack-test (0.6.3)
92 108 rack (>= 1.0)
93 109 rails (3.2.21)
94 110 actionmailer (= 3.2.21)
95 111 actionpack (= 3.2.21)
96 112 activerecord (= 3.2.21)
97 113 activeresource (= 3.2.21)
98 114 activesupport (= 3.2.21)
99 115 bundler (~> 1.0)
100 116 railties (= 3.2.21)
117 + rails_bootstrap_sortable (2.0.0)
118 + momentjs-rails (~> 2, >= 2.8.3)
101 119 railties (3.2.21)
102 120 actionpack (= 3.2.21)
103 121 activesupport (= 3.2.21)
104 122 rack-ssl (~> 1.3.2)
105 123 rake (>= 0.8.7)
106 124 rdoc (~> 3.4)
107 125 thor (>= 0.14.6, < 2.0)
108 126 rake (10.4.2)
109 127 rdiscount (2.1.8)
110 128 rdoc (3.12.2)
111 129 json (~> 1.4)
112 130 rouge (1.8.0)
113 131 rspec-collection_matchers (1.1.2)
114 132 rspec-expectations (>= 2.99.0.beta1)
115 133 rspec-core (2.99.2)
116 134 rspec-expectations (2.99.2)
117 135 diff-lcs (>= 1.1.3, < 2.0)
118 136 rspec-mocks (2.99.3)
119 137 rspec-rails (2.99.0)
120 138 actionpack (>= 3.0)
121 139 activemodel (>= 3.0)
122 140 activesupport (>= 3.0)
123 141 railties (>= 3.0)
124 142 rspec-collection_matchers
125 143 rspec-core (~> 2.99.0)
126 144 rspec-expectations (~> 2.99.0)
127 145 rspec-mocks (~> 2.99.0)
128 146 sass (3.4.11)
129 147 sass-rails (3.2.6)
130 148 railties (~> 3.2.0)
131 149 sass (>= 3.1.10)
132 150 tilt (~> 1.3)
151 + select2-rails (4.0.1)
152 + thor (~> 0.14)
133 153 sprockets (2.2.3)
134 154 hike (~> 1.2)
135 155 multi_json (~> 1.0)
136 156 rack (~> 1.0)
137 157 tilt (~> 1.1, != 1.3.0)
138 158 test-unit (3.0.9)
139 159 power_assert
140 160 thor (0.19.1)
141 161 tilt (1.4.1)
142 162 treetop (1.4.15)
143 163 polyglot
144 164 polyglot (>= 0.3.1)
145 165 tzinfo (0.3.43)
146 166 uglifier (2.7.0)
147 167 execjs (>= 0.3.0)
148 168 json (>= 1.8.0)
149 169 will_paginate (3.0.7)
150 170
151 171 PLATFORMS
152 172 ruby
153 173
154 174 DEPENDENCIES
175 + ace-rails-ap
176 + autoprefixer-rails
155 177 best_in_place (~> 3.0.1)
178 + bootstrap-sass (~> 3.2.0)
179 + bootstrap-switch-rails
180 + bootstrap-toggle-rails
156 181 coffee-rails (~> 3.2.2)
157 182 dynamic_form
158 183 haml
184 + haml-rails
159 185 in_place_editing
186 + jquery-countdown-rails
160 187 jquery-rails
161 188 jquery-tablesorter
162 189 jquery-timepicker-addon-rails
163 190 jquery-ui-sass-rails
164 191 mail
192 + momentjs-rails
165 193 mysql2
166 194 prototype-rails
167 195 rails (= 3.2.21)
196 + rails_bootstrap_sortable
168 197 rdiscount
169 198 rouge
170 199 rspec-rails (~> 2.99.0)
171 200 sass-rails (~> 3.2.6)
201 + select2-rails
172 202 test-unit
173 203 uglifier
174 204 verification!
175 205 will_paginate (~> 3.0.7)
@@ -1,17 +1,44
1 1 // This is a manifest file that'll be compiled into application.js, which will include all the files
2 2 // listed below.
3 3 //
4 4 // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 5 // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 6 //
7 7 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 8 // the compiled file.
9 9 //
10 10 // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 11 // GO AFTER THE REQUIRES BELOW.
12 12 //
13 - //= require prototype
14 - //= require prototype_ujs
15 - //= require effects
16 - //= require dragdrop
17 - //= require controls
13 + //= require jquery
14 + //= require jquery_ujs
15 + //= require jquery.ui.all
16 + //= require bootstrap-sprockets
17 + //= require moment
18 + //= require bootstrap-sortable
19 + //= require select2
20 + //= require ace-rails-ap
21 + //= require ace/mode-c_cpp
22 + //= require ace/mode-python
23 + //= require ace/mode-ruby
24 + //= require ace/mode-pascal
25 + //= require ace/mode-javascript
26 + //= require ace/mode-java
27 + //= require ace/theme-merbivore
28 + //= require custom
29 + //= require jquery.countdown
30 + //-------------- addition from local_jquery -----------
31 + //= require jquery.ui.datepicker
32 + //= require jquery.ui.slider
33 + //= require jquery-ui-timepicker-addon
34 + //= require jquery-tablesorter
35 + //= require best_in_place
36 + //= require best_in_place.jquery-ui
37 + //= require brython
38 +
39 + // since this is after blank line, it is not downloaded
40 + //x= require prototype
41 + //x= require prototype_ujs
42 + //x= require effects
43 + //x= require dragdrop
44 + //x= require controls
@@ -1,3 +0,0
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://jashkenas.github.com/coffee-script/
@@ -1,97 +1,228
1 + /*
2 + * This is a manifest file that'll be compiled into application.css, which will include all the files
3 + * listed below.
4 + *
5 + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 + *
8 + * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 + * compiled file so the styles you add here take precedence over styles defined in any styles
10 + * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11 + * file per style scope.
12 + *
13 + // bootstrap says that we should not do this, but @import each file instead
14 + # *= require_tree .
15 + # *= require_self
16 + */
1 17
18 + @import jquery.ui.all
2 19 @import jquery.ui.core
3 20 @import jquery.ui.theme
4 21 @import jquery.ui.datepicker
5 22 @import jquery.ui.slider
6 23 @import jquery-ui-timepicker-addon
7 24 @import jquery-tablesorter/theme.metro-dark
25 + @import jquery.countdown
8 26 @import tablesorter-theme.cafe
9 27
28 + //bootstrap
29 + @import bootstrap-sprockets
30 + @import bootstrap
31 + @import select2
32 + @import select2-bootstrap
33 + //@import bootstrap3-switch
34 + @import bootstrap-toggle
35 + @import bootstrap-sortable
36 +
37 + //bootstrap navbar color (from)
38 + $bgDefault : #19197b
39 + $bgHighlight : #06064b
40 + $colDefault : #8e8eb4
41 + $colHighlight : #ffffff
42 + $dropDown : false
43 + .navbar-default
44 + background-color: $bgDefault
45 + border-color: $bgHighlight
46 + .navbar-brand
47 + color: $colDefault
48 + &:hover, &:focus
49 + color: $colHighlight
50 + .navbar-text
51 + color: $colDefault
52 + .navbar-nav
53 + > li
54 + > a
55 + color: $colDefault
56 + &:hover, &:focus
57 + color: $colHighlight
58 + @if $dropDown
59 + > .dropdown-menu
60 + background-color: $bgDefault
61 + > li
62 + > a
63 + color: $colDefault
64 + &:hover, &:focus
65 + color: $colHighlight
66 + background-color: $bgHighlight
67 + > .divider
68 + background-color: $bgHighlight
69 + @if $dropDown
70 + .open .dropdown-menu > .active
71 + > a, > a:hover, > a:focus
72 + color: $colHighlight
73 + background-color: $bgHighlight
74 + > .active
75 + > a, > a:hover, > a:focus
76 + color: $colHighlight
77 + background-color: $bgHighlight
78 + > .open
79 + > a, > a:hover, > a:focus
80 + color: $colHighlight
81 + background-color: $bgHighlight
82 + .navbar-toggle
83 + border-color: $bgHighlight
84 + &:hover, &:focus
85 + background-color: $bgHighlight
86 + .icon-bar
87 + background-color: $colDefault
88 + .navbar-collapse,
89 + .navbar-form
90 + border-color: $colDefault
91 + .navbar-link
92 + color: $colDefault
93 + &:hover
94 + color: $colHighlight
95 + @media (max-width: 767px)
96 + .navbar-default .navbar-nav .open .dropdown-menu
97 + > li > a
98 + color: $colDefault
99 + &:hover, &:focus
100 + color: $colHighlight
101 + > .active
102 + > a, > a:hover, > a:focus
103 + color: $colHighlight
104 + background-color: $bgHighlight
105 +
106 + .secondnavbar
107 + top: 50px
108 +
109 +
110 + // --------------- bootstrap file upload ----------------------
111 + .btn-file
112 + position: relative
113 + overflow: hidden
114 +
115 + .btn-file input[type=file]
116 + position: absolute
117 + top: 0
118 + right: 0
119 + min-width: 100%
120 + min-height: 100%
121 + font-size: 100px
122 + text-align: right
123 + filter: alpha(opacity=0)
124 + opacity: 0
125 + outline: none
126 + background: white
127 + cursor: inherit
128 + display: block
129 +
10 130 body
11 131 background: white image-url("topbg.jpg") repeat-x top center
12 - font-size: 13px
13 - font-family: Tahoma, "sans-serif"
132 + //font-size: 13px
133 + //font-family: Tahoma, "sans-serif"
14 134 margin: 10px
15 135 padding: 10px
136 + padding-top: 60px
16 137
138 + // ------------------ bootstrap sortable --------------------
139 + table.sortable th
140 + padding-right: 20px !important
141 + span.sign
142 + right: -15px !important
143 + &.text-right
144 + padding-left: 20px !important
145 + padding-right: 8px !important
146 + &:after, span.sign
147 + left: -15px !important
17 148
18 149 input
19 150 font-family: Tahoma, "sans-serif"
20 151
21 152
22 153 h1
23 154 font-size: 24px
24 155 color: #334488
25 156 line-height: 2em
26 157
27 158
28 159 h2
29 160 font-size: 18px
30 161 color: #5566bb
31 162 line-height: 1.5em
32 163
33 164
34 165 hr
35 166 border-top: 1px solid #dddddd
36 167 border-bottom: 1px solid #eeeeee
37 168
38 169
39 - a
40 - color: #6666cc
41 - text-decoration: none
42 -
43 - &:link, &:visited
44 - color: #6666cc
45 - text-decoration: none
46 -
47 - &:hover, &:focus
48 - color: #111166
49 - text-decoration: none
170 + //#a
171 + // color: #6666cc
172 + // text-decoration: none
173 + //
174 + // &:link, &:visited
175 + // color: #6666cc
176 + // text-decoration: none
177 + //
178 + // &:hover, &:focus
179 + // color: #111166
180 + // text-decoration: none
50 181
51 182
52 183 div
53 184 &.userbar
54 185 line-height: 1.5em
55 186 text-align: right
56 187 font-size: 12px
57 188
58 189 &.title
59 190 padding: 10px 0px
60 191 line-height: 1.5em
61 192 font-size: 13px
62 193
63 194 span.contest-over-msg
64 195 font-size: 15px
65 196 color: red
66 197
67 198 table
68 199 width: 100%
69 200 font-weight: bold
70 201
71 202 td
72 203 &.left-col
73 204 text-align: left
74 205 vertical-align: top
75 206 color: #444444
76 207
77 208 &.right-col
78 209 text-align: right
79 210 vertical-align: top
80 211 font-size: 18px
81 212 color: #116699
82 213
83 214
84 215 table.info
85 216 margin: 10px 0
86 217 border: 1px solid #666666
87 218 border-collapse: collapse
88 219 font-size: 12px
89 220
90 221 th
91 222 border: 1px solid #666666
92 223 line-height: 1.5em
93 224 padding: 0 0.5em
94 225
95 226 td
96 227 border-left: 1px solid #666666
97 228 border-right: 1px solid #666666
@@ -1,77 +1,79
1 1 /*************
2 2 Metro Dark Theme
3 3 *************/
4 4 /* overall */
5 5 .tablesorter-cafe {
6 - // font: 12px/18px 'Segoe UI Semilight', 'Open Sans', Verdana, Arial, Helvetica, sans-serif;
6 + /* font: 12px/18px 'Segoe UI Semilight', 'Open Sans', Verdana, Arial, Helvetica, sans-serif; */
7 7 color: #000;
8 8 background-color: #777;
9 9 margin: 10px 0 15px;
10 10 text-align: left;
11 11 border-collapse: collapse;
12 12 border: #555 1px solid;
13 13 }
14 14
15 15 .tablesorter-cafe tr.dark-row th, .tablesorter-cafe tr.dark-row td {
16 16 background-color: #222;
17 17 color: #fff;
18 18 text-align: left;
19 19 font-size: 14px;
20 20 }
21 21
22 22 /* header/footer */
23 23 .tablesorter-cafe caption,
24 24 .tablesorter-cafe th,
25 25 .tablesorter-cafe thead td,
26 26 .tablesorter-cafe tfoot th,
27 27 .tablesorter-cafe tfoot td {
28 + /*
28 29 //font-weight: 300;
29 30 //font-size: 15px;
31 + */
30 32 color: #fff;
31 33 background-color: #777;
32 34 padding: 2px;
33 35 border: #555 1px solid;
34 36 }
35 37
36 38 .tablesorter-cafe .header,
37 39 .tablesorter-cafe .tablesorter-header {
38 40 background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAGFBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u5jNePWAAAACHRSTlMAMxIHKwEgMWD59H4AAABSSURBVAjXY2BgYFJgAAHzYhDJ6igSAKTYBAUTgJSioKAQAwNzoaCguAFDiCAQuDIkgigxBgiA8cJAVCpQt6AgSL+JoKAzA0gjUBsQqBcBCYhFAAE/CV4zeSzxAAAAAElFTkSuQmCC);
39 41 background-position: center right;
40 42 background-repeat: no-repeat;
41 43 cursor: pointer;
42 44 white-space: normal;
43 45 }
44 46 .tablesorter-cafe .tablesorter-header-inner {
45 47 padding: 0 18px 0 4px;
46 48 }
47 49 .tablesorter-cafe thead .headerSortUp,
48 50 .tablesorter-cafe thead .tablesorter-headerSortUp,
49 51 .tablesorter-cafe thead .tablesorter-headerAsc {
50 52 background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAIVBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u5meJAOAAAACnRSTlMAMwsqXt+gIBUGxGoDMAAAAFlJREFUCNctzC0SQAAUReEzGNQ3AlHRiSRZFCVZYgeswRL8hLdK7834wj3tAlGP6y7fYHpKS6w6WwbVG0I1NZVnZPG8/DYxOYlnhUYkA06R1s9ESsxR4NIdPhkPFDFYuEnMAAAAAElFTkSuQmCC);
51 53 }
52 54 .tablesorter-cafe thead .headerSortDown,
53 55 .tablesorter-cafe thead .tablesorter-headerSortDown,
54 56 .tablesorter-cafe thead .tablesorter-headerDesc {
55 57 background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAALVBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7i0NViAAAADnRSTlMAMiweCQITTvDctZZqaTlM310AAABcSURBVAjXY2BgYEtgAAFHERDJqigUAKSYBQUNgFSioKAYAwOLIBA4MASBKFUGQxAlzAAF+94BwWuGKBC1lIFl3rt3Lx0YGCzevWsGSjK9e6cAUlT3HKyW9wADAwDRrBiDy6bKzwAAAABJRU5ErkJggg==);
56 58 }
57 59 .tablesorter-cafe thead .sorter-false {
58 60 background-image: none;
59 61 cursor: default;
60 62 padding: 4px;
61 63 }
62 64
63 65 /* tbody */
64 66 .tablesorter-cafe td {
65 67 background-color: #fff;
66 68 padding: 1px 4px;
67 69 vertical-align: top;
68 70 border-style: solid;
69 71 border-color: #666;
70 72 border-collapse: collapse;
71 73 border-width: 0px 1px;
72 74
73 75 }
74 76
75 77 /* hovered row colors */
76 78 .tablesorter-cafe tbody > tr:hover > td,
77 79 .tablesorter-cafe tbody > tr.even:hover > td,
@@ -24,68 +24,88
24 24 respond_to do |format|
25 25 format.html # show.html.erb
26 26 format.xml { render :xml => @announcement }
27 27 end
28 28 end
29 29
30 30 # GET /announcements/new
31 31 # GET /announcements/new.xml
32 32 def new
33 33 @announcement = Announcement.new
34 34
35 35 respond_to do |format|
36 36 format.html # new.html.erb
37 37 format.xml { render :xml => @announcement }
38 38 end
39 39 end
40 40
41 41 # GET /announcements/1/edit
42 42 def edit
43 43 @announcement = Announcement.find(params[:id])
44 44 end
45 45
46 46 # POST /announcements
47 47 # POST /announcements.xml
48 48 def create
49 49 @announcement = Announcement.new(params[:announcement])
50 50
51 51 respond_to do |format|
52 52 if @announcement.save
53 53 flash[:notice] = 'Announcement was successfully created.'
54 54 format.html { redirect_to(@announcement) }
55 55 format.xml { render :xml => @announcement, :status => :created, :location => @announcement }
56 56 else
57 57 format.html { render :action => "new" }
58 58 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
59 59 end
60 60 end
61 61 end
62 62
63 63 # PUT /announcements/1
64 64 # PUT /announcements/1.xml
65 65 def update
66 66 @announcement = Announcement.find(params[:id])
67 67
68 68 respond_to do |format|
69 69 if @announcement.update_attributes(params[:announcement])
70 70 flash[:notice] = 'Announcement was successfully updated.'
71 71 format.html { redirect_to(@announcement) }
72 + format.js {}
72 73 format.xml { head :ok }
73 74 else
74 75 format.html { render :action => "edit" }
76 + format.js {}
75 77 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
76 78 end
77 79 end
78 80 end
79 81
82 + def toggle
83 + @announcement = Announcement.find(params[:id])
84 + @announcement.update_attributes( published: !@announcement.published? )
85 + respond_to do |format|
86 + format.js { render partial: 'toggle_button',
87 + locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } }
88 + end
89 + end
90 +
91 + def toggle_front
92 + @announcement = Announcement.find(params[:id])
93 + @announcement.update_attributes( frontpage: !@announcement.frontpage? )
94 + respond_to do |format|
95 + format.js { render partial: 'toggle_button',
96 + locals: {button_id: "#announcement_toggle_front_#{@announcement.id}",button_on: @announcement.frontpage? } }
97 + end
98 + end
99 +
80 100 # DELETE /announcements/1
81 101 # DELETE /announcements/1.xml
82 102 def destroy
83 103 @announcement = Announcement.find(params[:id])
84 104 @announcement.destroy
85 105
86 106 respond_to do |format|
87 107 format.html { redirect_to(announcements_url) }
88 108 format.xml { head :ok }
89 109 end
90 110 end
91 111 end
@@ -1,95 +1,108
1 1 class ApplicationController < ActionController::Base
2 2 protect_from_forgery
3 3
4 + before_filter :current_user
5 +
4 6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
5 7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
6 8
9 + # Returns the current logged-in user (if any).
10 + def current_user
11 + return nil unless session[:user_id]
12 + @current_user ||= User.find(session[:user_id])
13 + end
14 +
7 15 def admin_authorization
8 16 return false unless authenticate
9 17 user = User.find(session[:user_id], :include => ['roles'])
10 18 unless user.admin?
11 19 flash[:notice] = 'You are not authorized to view the page you requested'
12 20 redirect_to :controller => 'main', :action => 'login' unless user.admin?
13 21 return false
14 22 end
15 23 return true
16 24 end
17 25
18 26 def authorization_by_roles(allowed_roles)
19 27 return false unless authenticate
20 28 user = User.find(session[:user_id])
21 29 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
22 30 flash[:notice] = 'You are not authorized to view the page you requested'
23 31 redirect_to :controller => 'main', :action => 'login'
24 32 return false
25 33 end
26 34 end
27 35
28 36 protected
29 37
30 38 def authenticate
31 39 unless session[:user_id]
32 40 flash[:notice] = 'You need to login'
33 41 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
34 42 flash[:notice] = 'You need to login but you cannot log in at this time'
35 43 end
36 44 redirect_to :controller => 'main', :action => 'login'
37 45 return false
38 46 end
39 47
40 48 # check if run in single user mode
41 49 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
42 - user = User.find(session[:user_id])
50 + user = User.find_by_id(session[:user_id])
43 51 if user==nil or (not user.admin?)
44 52 flash[:notice] = 'You cannot log in at this time'
45 53 redirect_to :controller => 'main', :action => 'login'
46 54 return false
47 55 end
56 + unless user.enabled?
57 + flash[:notice] = 'Your account is disabled'
58 + redirect_to :controller => 'main', :action => 'login'
59 + return false
60 + end
48 61 return true
49 62 end
50 63
51 64 if GraderConfiguration.multicontests?
52 65 user = User.find(session[:user_id])
53 66 return true if user.admin?
54 67 begin
55 68 if user.contest_stat(true).forced_logout
56 69 flash[:notice] = 'You have been automatically logged out.'
57 70 redirect_to :controller => 'main', :action => 'index'
58 71 end
59 72 rescue
60 73 end
61 74 end
62 75 return true
63 76 end
64 77
65 78 def authenticate_by_ip_address
66 79 #this assume that we have already authenticate normally
67 80 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
68 81 user = User.find(session[:user_id])
69 82 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
70 83 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
71 84 redirect_to :controller => 'main', :action => 'login'
72 85 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
73 86 return false
74 87 end
75 88 unless user.last_ip
76 89 user.last_ip = request.remote_ip
77 90 user.save
78 91 end
79 92 end
80 93 return true
81 94 end
82 95
83 96 def authorization
84 97 return false unless authenticate
85 98 user = User.find(session[:user_id])
86 99 unless user.roles.detect { |role|
87 100 role.rights.detect{ |right|
88 101 right.controller == self.class.controller_name and
89 102 (right.action == 'all' or right.action == action_name)
90 103 }
91 104 }
92 105 flash[:notice] = 'You are not authorized to view the page you requested'
93 106 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
94 107 redirect_to :controller => 'main', :action => 'login'
95 108 return false
@@ -24,152 +24,160
24 24 # COMMENT OUT: only need when having high load
25 25 # caches_action :index, :login
26 26
27 27 # NOTE: This method is not actually needed, 'config/routes.rb' has
28 28 # assigned action login as a default action.
29 29 def index
30 30 redirect_to :action => 'login'
31 31 end
32 32
33 33 def login
34 34 saved_notice = flash[:notice]
35 35 reset_session
36 36 flash.now[:notice] = saved_notice
37 37
38 38 # EXPERIMENT:
39 39 # Hide login if in single user mode and the url does not
40 40 # explicitly specify /login
41 41 #
42 42 # logger.info "PATH: #{request.path}"
43 43 # if GraderConfiguration['system.single_user_mode'] and
44 44 # request.path!='/main/login'
45 45 # @hidelogin = true
46 46 # end
47 47
48 48 @announcements = Announcement.find_for_frontpage
49 49 render :action => 'login', :layout => 'empty'
50 50 end
51 51
52 52 def list
53 53 prepare_list_information
54 54 end
55 55
56 56 def help
57 57 @user = User.find(session[:user_id])
58 58 end
59 59
60 60 def submit
61 61 user = User.find(session[:user_id])
62 62
63 63 @submission = Submission.new
64 64 @submission.problem_id = params[:submission][:problem_id]
65 65 @submission.user = user
66 66 @submission.language_id = 0
67 67 if (params['file']) and (params['file']!='')
68 68 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
69 69 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
70 70 @submission.source_filename = params['file'].original_filename
71 71 end
72 +
73 + if (params[:editor_text])
74 + language = Language.find_by_id(params[:language_id])
75 + @submission.source = params[:editor_text]
76 + @submission.source_filename = "live_edit.#{language.ext}"
77 + @submission.language = language
78 + end
79 +
72 80 @submission.submitted_at = Time.new.gmtime
73 81 @submission.ip_address = request.remote_ip
74 82
75 83 if GraderConfiguration.time_limit_mode? and user.contest_finished?
76 84 @submission.errors.add(:base,"The contest is over.")
77 85 prepare_list_information
78 86 render :action => 'list' and return
79 87 end
80 88
81 89 if @submission.valid?
82 90 if @submission.save == false
83 91 flash[:notice] = 'Error saving your submission'
84 92 elsif Task.create(:submission_id => @submission.id,
85 93 :status => Task::STATUS_INQUEUE) == false
86 94 flash[:notice] = 'Error adding your submission to task queue'
87 95 end
88 96 else
89 97 prepare_list_information
90 98 render :action => 'list' and return
91 99 end
92 100 redirect_to :action => 'list'
93 101 end
94 102
95 103 def source
96 104 submission = Submission.find(params[:id])
97 105 if ((submission.user_id == session[:user_id]) and
98 106 (submission.problem != nil) and
99 107 (submission.problem.available))
100 108 send_data(submission.source,
101 109 {:filename => submission.download_filename,
102 110 :type => 'text/plain'})
103 111 else
104 112 flash[:notice] = 'Error viewing source'
105 113 redirect_to :action => 'list'
106 114 end
107 115 end
108 116
109 117 def compiler_msg
110 118 @submission = Submission.find(params[:id])
111 119 if @submission.user_id == session[:user_id]
112 120 render :action => 'compiler_msg', :layout => 'empty'
113 121 else
114 122 flash[:notice] = 'Error viewing source'
115 123 redirect_to :action => 'list'
116 124 end
117 125 end
118 126
119 127 def submission
120 128 @user = User.find(session[:user_id])
121 129 @problems = @user.available_problems
122 130 if params[:id]==nil
123 131 @problem = nil
124 132 @submissions = nil
125 133 else
126 - @problem = Problem.find_by_name(params[:id])
127 - if not @problem.available
134 + @problem = Problem.find_by_id(params[:id])
135 + if (@problem == nil) or (not @problem.available)
128 136 redirect_to :action => 'list'
129 137 flash[:notice] = 'Error: submissions for that problem are not viewable.'
130 138 return
131 139 end
132 140 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
133 141 end
134 142 end
135 143
136 144 def result
137 145 if !GraderConfiguration.show_grading_result
138 146 redirect_to :action => 'list' and return
139 147 end
140 148 @user = User.find(session[:user_id])
141 149 @submission = Submission.find(params[:id])
142 150 if @submission.user!=@user
143 151 flash[:notice] = 'You are not allowed to view result of other users.'
144 152 redirect_to :action => 'list' and return
145 153 end
146 154 prepare_grading_result(@submission)
147 155 end
148 156
149 157 def load_output
150 158 if !GraderConfiguration.show_grading_result or params[:num]==nil
151 159 redirect_to :action => 'list' and return
152 160 end
153 161 @user = User.find(session[:user_id])
154 162 @submission = Submission.find(params[:id])
155 163 if @submission.user!=@user
156 164 flash[:notice] = 'You are not allowed to view result of other users.'
157 165 redirect_to :action => 'list' and return
158 166 end
159 167 case_num = params[:num].to_i
160 168 out_filename = output_filename(@user.login,
161 169 @submission.problem.name,
162 170 @submission.id,
163 171 case_num)
164 172 if !FileTest.exists?(out_filename)
165 173 flash[:notice] = 'Output not found.'
166 174 redirect_to :action => 'list' and return
167 175 end
168 176
169 177 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
170 178 response.headers['Content-Type'] = "application/force-download"
171 179 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
172 180 response.headers["X-Sendfile"] = out_filename
173 181 response.headers['Content-length'] = File.size(out_filename)
174 182 render :nothing => true
175 183 else
@@ -1,196 +1,201
1 1 class ProblemsController < ApplicationController
2 2
3 3 before_filter :authenticate, :authorization
4 4
5 5 in_place_edit_for :problem, :name
6 6 in_place_edit_for :problem, :full_name
7 7 in_place_edit_for :problem, :full_score
8 8
9 9 def index
10 - list
11 - render :action => 'list'
10 + @problems = Problem.find(:all, :order => 'date_added DESC')
12 11 end
13 12
14 13 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 14 verify :method => :post, :only => [ :destroy,
16 15 :create, :quick_create,
17 16 :do_manage,
18 17 :do_import,
19 18 :update ],
20 - :redirect_to => { :action => :list }
21 -
22 - def list
23 - @problems = Problem.find(:all, :order => 'date_added DESC')
24 - end
19 + :redirect_to => { :action => :index }
25 20
26 21 def show
27 22 @problem = Problem.find(params[:id])
28 23 end
29 24
30 25 def new
31 26 @problem = Problem.new
32 27 @description = nil
33 28 end
34 29
35 30 def create
36 31 @problem = Problem.new(params[:problem])
37 32 @description = Description.new(params[:description])
38 33 if @description.body!=''
39 34 if !@description.save
40 35 render :action => new and return
41 36 end
42 37 else
43 38 @description = nil
44 39 end
45 40 @problem.description = @description
46 41 if @problem.save
47 42 flash[:notice] = 'Problem was successfully created.'
48 - redirect_to :action => 'list'
43 + redirect_to action: :index
49 44 else
50 45 render :action => 'new'
51 46 end
52 47 end
53 48
54 49 def quick_create
55 50 @problem = Problem.new(params[:problem])
56 51 @problem.full_name = @problem.name if @problem.full_name == ''
57 52 @problem.full_score = 100
58 53 @problem.available = false
59 54 @problem.test_allowed = true
60 55 @problem.output_only = false
61 56 @problem.date_added = Time.new
62 57 if @problem.save
63 58 flash[:notice] = 'Problem was successfully created.'
64 - redirect_to :action => 'list'
59 + redirect_to action: :index
65 60 else
66 61 flash[:notice] = 'Error saving problem'
67 - redirect_to :action => 'list'
62 + redirect_to action: :index
68 63 end
69 64 end
70 65
71 66 def edit
72 67 @problem = Problem.find(params[:id])
73 68 @description = @problem.description
74 69 end
75 70
76 71 def update
77 72 @problem = Problem.find(params[:id])
78 73 @description = @problem.description
79 74 if @description == nil and params[:description][:body]!=''
80 75 @description = Description.new(params[:description])
81 76 if !@description.save
82 77 flash[:notice] = 'Error saving description'
83 78 render :action => 'edit' and return
84 79 end
85 80 @problem.description = @description
86 81 elsif @description!=nil
87 82 if !@description.update_attributes(params[:description])
88 83 flash[:notice] = 'Error saving description'
89 84 render :action => 'edit' and return
90 85 end
91 86 end
92 87 if params[:file] and params[:file].content_type != 'application/pdf'
93 88 flash[:notice] = 'Error: Uploaded file is not PDF'
94 89 render :action => 'edit' and return
95 90 end
96 91 if @problem.update_attributes(params[:problem])
97 92 flash[:notice] = 'Problem was successfully updated.'
98 93 unless params[:file] == nil or params[:file] == ''
99 94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
100 95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
101 96 if not FileTest.exists? out_dirname
102 97 Dir.mkdir out_dirname
103 98 end
104 99
105 100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
106 101 if FileTest.exists? out_filename
107 102 File.delete out_filename
108 103 end
109 104
110 105 File.open(out_filename,"wb") do |file|
111 106 file.write(params[:file].read)
112 107 end
113 108 @problem.description_filename = "#{@problem.name}.pdf"
114 109 @problem.save
115 110 end
116 111 redirect_to :action => 'show', :id => @problem
117 112 else
118 113 render :action => 'edit'
119 114 end
120 115 end
121 116
122 117 def destroy
123 118 Problem.find(params[:id]).destroy
124 - redirect_to :action => 'list'
119 + redirect_to action: :index
125 120 end
126 121
127 122 def toggle
128 123 @problem = Problem.find(params[:id])
129 - @problem.available = !(@problem.available)
130 - @problem.save
124 + @problem.update_attributes(available: !(@problem.available) )
125 + respond_to do |format|
126 + format.js { }
127 + end
128 + end
129 +
130 + def toggle_test
131 + @problem = Problem.find(params[:id])
132 + @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 + respond_to do |format|
134 + format.js { }
135 + end
131 136 end
132 137
133 138 def turn_all_off
134 139 Problem.find(:all,
135 140 :conditions => "available = 1").each do |problem|
136 141 problem.available = false
137 142 problem.save
138 143 end
139 - redirect_to :action => 'list'
144 + redirect_to action: :index
140 145 end
141 146
142 147 def turn_all_on
143 148 Problem.find(:all,
144 149 :conditions => "available = 0").each do |problem|
145 150 problem.available = true
146 151 problem.save
147 152 end
148 - redirect_to :action => 'list'
153 + redirect_to action: :index
149 154 end
150 155
151 156 def stat
152 157 @problem = Problem.find(params[:id])
153 158 unless @problem.available or session[:admin]
154 159 redirect_to :controller => 'main', :action => 'list'
155 160 return
156 161 end
157 162 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
158 163
159 164 #stat summary
160 165 range =65
161 166 @histogram = { data: Array.new(range,0), summary: {} }
162 167 user = Hash.new(0)
163 168 @submissions.find_each do |sub|
164 169 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
165 170 @histogram[:data][d.to_i] += 1 if d < range
166 171 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
167 172 end
168 173 @histogram[:summary][:max] = [@histogram[:data].max,1].max
169 174
170 175 @summary = { attempt: user.count, solve: 0 }
171 176 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
172 177 end
173 178
174 179 def manage
175 180 @problems = Problem.find(:all, :order => 'date_added DESC')
176 181 end
177 182
178 183 def do_manage
179 184 if params.has_key? 'change_date_added'
180 185 change_date_added
181 186 elsif params.has_key? 'add_to_contest'
182 187 add_to_contest
183 188 elsif params.has_key? 'enable_problem'
184 189 set_available(true)
185 190 elsif params.has_key? 'disable_problem'
186 191 set_available(false)
187 192 end
188 193 redirect_to :action => 'manage'
189 194 end
190 195
191 196 def import
192 197 @allow_test_pair_import = allow_test_pair_import?
193 198 end
194 199
195 200 def do_import
196 201 old_problem = Problem.find_by_name(params[:name])
@@ -1,129 +1,154
1 1 class ReportController < ApplicationController
2 2
3 3 before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize]
4 4
5 5 before_filter(only: [:problem_hof]) { |c|
6 6 return false unless authenticate
7 7
8 8 if GraderConfiguration["right.user_view_submission"]
9 9 return true;
10 10 end
11 11
12 12 admin_authorization
13 13 }
14 14
15 + def score
16 + if params[:commit] == 'download csv'
17 + @problems = Problem.all
18 + else
19 + @problems = Problem.find_available_problems
20 + end
21 + @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
22 + @scorearray = Array.new
23 + @users.each do |u|
24 + ustat = Array.new
25 + ustat[0] = u
26 + @problems.each do |p|
27 + sub = Submission.find_last_by_user_and_problem(u.id,p.id)
28 + if (sub!=nil) and (sub.points!=nil) and p and p.full_score
29 + ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
30 + else
31 + ustat << [0,false]
32 + end
33 + end
34 + @scorearray << ustat
35 + end
36 + if params[:commit] == 'download csv' then
37 + csv = gen_csv_from_scorearray(@scorearray,@problems)
38 + send_data csv, filename: 'last_score.csv'
39 + else
40 + render template: 'user_admin/user_stat'
41 + end
42 +
43 + end
44 +
15 45 def login_stat
16 46 @logins = Array.new
17 47
18 48 date_and_time = '%Y-%m-%d %H:%M'
19 49 begin
20 50 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
21 51 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
22 52 rescue
23 53 @since_time = DateTime.new(1000,1,1)
24 54 end
25 55 begin
26 56 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
27 57 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
28 58 rescue
29 59 @until_time = DateTime.new(3000,1,1)
30 60 end
31 61
32 62 User.all.each do |user|
33 63 @logins << { id: user.id,
34 64 login: user.login,
35 65 full_name: user.full_name,
36 66 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
37 67 user.id,@since_time,@until_time)
38 68 .count(:id),
39 69 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
40 70 user.id,@since_time,@until_time)
41 71 .minimum(:created_at),
42 72 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
43 73 user.id,@since_time,@until_time)
44 74 .maximum(:created_at),
45 75 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
46 76 user.id,@since_time,@until_time)
47 77 .select(:ip_address).uniq
48 78
49 79 }
50 80 end
51 81 end
52 82
53 83 def submission_stat
54 84
55 85 date_and_time = '%Y-%m-%d %H:%M'
56 86 begin
57 87 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
58 88 rescue
59 89 @since_time = DateTime.new(1000,1,1)
60 90 end
61 91 begin
62 92 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
63 93 rescue
64 94 @until_time = DateTime.new(3000,1,1)
65 95 end
66 96
67 97 @submissions = {}
68 98
69 99 User.find_each do |user|
70 100 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
71 101 end
72 102
73 103 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
74 104 if @submissions[s.user_id]
75 105 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
76 - a = nil
77 - begin
78 - a = Problem.find(s.problem_id)
79 - rescue
80 - a = nil
81 - end
106 + a = Problem.find_by_id(s.problem_id)
82 107 @submissions[s.user_id][:sub][s.problem_id] =
83 108 { prob_name: (a ? a.full_name : '(NULL)'),
84 109 sub_ids: [s.id] }
85 110 else
86 111 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
87 112 end
88 113 @submissions[s.user_id][:count] += 1
89 114 end
90 115 end
91 116 end
92 117
93 118 def problem_hof
94 119 # gen problem list
95 120 @user = User.find(session[:user_id])
96 121 @problems = @user.available_problems
97 122
98 123 # get selected problems or the default
99 124 if params[:id]
100 125 begin
101 126 @problem = Problem.available.find(params[:id])
102 127 rescue
103 128 redirect_to action: :problem_hof
104 129 flash[:notice] = 'Error: submissions for that problem are not viewable.'
105 130 return
106 131 end
107 132 end
108 133
109 134 return unless @problem
110 135
111 136 @by_lang = {} #aggregrate by language
112 137
113 138 range =65
114 139 @histogram = { data: Array.new(range,0), summary: {} }
115 140 @summary = {count: 0, solve: 0, attempt: 0}
116 141 user = Hash.new(0)
117 142 Submission.where(problem_id: @problem.id).find_each do |sub|
118 143 #histogram
119 144 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
120 145 @histogram[:data][d.to_i] += 1 if d < range
121 146
122 147 next unless sub.points
123 148 @summary[:count] += 1
124 149 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
125 150
126 151 lang = Language.find_by_id(sub.language_id)
127 152 next unless lang
128 153 next unless sub.points >= @problem.full_score
129 154
@@ -1,313 +1,312
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_filter :admin_authorization
8 8
9 9 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 10 verify :method => :post, :only => [ :destroy,
11 11 :create, :create_from_list,
12 12 :update,
13 13 :manage_contest,
14 14 :bulk_mail
15 15 ],
16 16 :redirect_to => { :action => :list }
17 17
18 18 def index
19 19 list
20 - render :action => 'list'
21 20 end
22 21
23 22 def list
24 23 @user_count = User.count
25 24 if params[:page] == 'all'
26 25 @users = User.all
27 26 @paginated = false
28 27 else
29 28 @users = User.paginate :page => params[:page]
30 29 @paginated = true
31 30 end
32 31 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
33 32 @contests = Contest.enabled
34 33 end
35 34
36 35 def active
37 36 sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
38 37 @users = []
39 38 sessions.each do |session|
40 39 if session.data[:user_id]
41 40 @users << User.find(session.data[:user_id])
42 41 end
43 42 end
44 43 end
45 44
46 45 def show
47 46 @user = User.find(params[:id])
48 47 end
49 48
50 49 def new
51 50 @user = User.new
52 51 end
53 52
54 53 def create
55 54 @user = User.new(params[:user])
56 55 @user.activated = true
57 56 if @user.save
58 57 flash[:notice] = 'User was successfully created.'
59 - redirect_to :action => 'list'
58 + redirect_to :action => 'index'
60 59 else
61 60 render :action => 'new'
62 61 end
63 62 end
64 63
65 64 def clear_last_ip
66 65 @user = User.find(params[:id])
67 66 @user.last_ip = nil
68 67 @user.save
69 - redirect_to action: 'list', page: params[:page]
68 + redirect_to action: 'index', page: params[:page]
70 69 end
71 70
72 71 def create_from_list
73 72 lines = params[:user_list]
74 73
75 74 note = []
76 75
77 76 lines.split("\n").each do |line|
78 77 items = line.chomp.split(',')
79 78 if items.length>=2
80 79 login = items[0]
81 80 full_name = items[1]
82 81
83 82 added_random_password = false
84 83 if items.length>=3
85 84 password = items[2].chomp(" ")
86 85 user_alias = (items.length>=4) ? items[3] : login
87 86 else
88 87 password = random_password
89 88 user_alias = (items.length>=4) ? items[3] : login
90 89 added_random_password = true
91 90 end
92 91
93 92 user = User.find_by_login(login)
94 93 if (user)
95 94 user.full_name = full_name
96 95 user.password = password
97 96 else
98 97 user = User.new({:login => login,
99 98 :full_name => full_name,
100 99 :password => password,
101 100 :password_confirmation => password,
102 101 :alias => user_alias})
103 102 end
104 103 user.activated = true
105 104 user.save
106 105
107 106 if added_random_password
108 107 note << "'#{login}' (+)"
109 108 else
110 109 note << login
111 110 end
112 111 end
113 112 end
114 113 flash[:notice] = 'User(s) ' + note.join(', ') +
115 114 ' were successfully created. ' +
116 115 '( (+) - created with random passwords.)'
117 - redirect_to :action => 'list'
116 + redirect_to :action => 'index'
118 117 end
119 118
120 119 def edit
121 120 @user = User.find(params[:id])
122 121 end
123 122
124 123 def update
125 124 @user = User.find(params[:id])
126 125 if @user.update_attributes(params[:user])
127 126 flash[:notice] = 'User was successfully updated.'
128 127 redirect_to :action => 'show', :id => @user
129 128 else
130 129 render :action => 'edit'
131 130 end
132 131 end
133 132
134 133 def destroy
135 134 User.find(params[:id]).destroy
136 - redirect_to :action => 'list'
135 + redirect_to :action => 'index'
137 136 end
138 137
139 138 def user_stat
140 139 if params[:commit] == 'download csv'
141 140 @problems = Problem.all
142 141 else
143 142 @problems = Problem.find_available_problems
144 143 end
145 144 @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
146 145 @scorearray = Array.new
147 146 @users.each do |u|
148 147 ustat = Array.new
149 148 ustat[0] = u
150 149 @problems.each do |p|
151 150 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
152 151 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
153 152 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
154 153 else
155 154 ustat << [0,false]
156 155 end
157 156 end
158 157 @scorearray << ustat
159 158 end
160 159 if params[:commit] == 'download csv' then
161 160 csv = gen_csv_from_scorearray(@scorearray,@problems)
162 161 send_data csv, filename: 'last_score.csv'
163 162 else
164 163 render template: 'user_admin/user_stat'
165 164 end
166 165 end
167 166
168 167 def user_stat_max
169 168 if params[:commit] == 'download csv'
170 169 @problems = Problem.all
171 170 else
172 171 @problems = Problem.find_available_problems
173 172 end
174 173 @users = User.find(:all, :include => [:contests, :contest_stat])
175 174 @scorearray = Array.new
176 175 #set up range from param
177 176 since_id = params.fetch(:since_id, 0).to_i
178 177 until_id = params.fetch(:until_id, 0).to_i
179 178 @users.each do |u|
180 179 ustat = Array.new
181 180 ustat[0] = u
182 181 @problems.each do |p|
183 182 max_points = 0
184 183 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
185 184 max_points = sub.points if sub and sub.points and (sub.points > max_points)
186 185 end
187 186 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
188 187 end
189 188 @scorearray << ustat
190 189 end
191 190
192 191 if params[:commit] == 'download csv' then
193 192 csv = gen_csv_from_scorearray(@scorearray,@problems)
194 193 send_data csv, filename: 'max_score.csv'
195 194 else
196 195 render template: 'user_admin/user_stat'
197 196 end
198 197 end
199 198
200 199 def import
201 200 if params[:file]==''
202 201 flash[:notice] = 'Error importing no file'
203 - redirect_to :action => 'list' and return
202 + redirect_to :action => 'index' and return
204 203 end
205 204 import_from_file(params[:file])
206 205 end
207 206
208 207 def random_all_passwords
209 208 users = User.find(:all)
210 209 @prefix = params[:prefix] || ''
211 210 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
212 211 @changed = false
213 212 if request.request_method == 'POST'
214 213 @non_admin_users.each do |user|
215 214 password = random_password
216 215 user.password = password
217 216 user.password_confirmation = password
218 217 user.save
219 218 end
220 219 @changed = true
221 220 end
222 221 end
223 222
224 223 # contest management
225 224
226 225 def contests
227 226 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
228 227 @contests = Contest.enabled
229 228 end
230 229
231 230 def assign_from_list
232 231 contest_id = params[:users_contest_id]
233 232 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
234 233 contest = Contest.find(params[:new_contest][:id])
235 234 if !contest
236 235 flash[:notice] = 'Error: no contest'
237 236 redirect_to :action => 'contests', :id =>contest_id
238 237 end
239 238
240 239 note = []
241 240 users.each do |u|
242 241 u.contests = [contest]
243 242 note << u.login
244 243 end
245 244 flash[:notice] = 'User(s) ' + note.join(', ') +
246 245 " were successfully reassigned to #{contest.title}."
247 246 redirect_to :action => 'contests', :id =>contest.id
248 247 end
249 248
250 249 def add_to_contest
251 250 user = User.find(params[:id])
252 251 contest = Contest.find(params[:contest_id])
253 252 if user and contest
254 253 user.contests << contest
255 254 end
256 - redirect_to :action => 'list'
255 + redirect_to :action => 'index'
257 256 end
258 257
259 258 def remove_from_contest
260 259 user = User.find(params[:id])
261 260 contest = Contest.find(params[:contest_id])
262 261 if user and contest
263 262 user.contests.delete(contest)
264 263 end
265 - redirect_to :action => 'list'
264 + redirect_to :action => 'index'
266 265 end
267 266
268 267 def contest_management
269 268 end
270 269
271 270 def manage_contest
272 271 contest = Contest.find(params[:contest][:id])
273 272 if !contest
274 273 flash[:notice] = 'You did not choose the contest.'
275 274 redirect_to :action => 'contest_management' and return
276 275 end
277 276
278 277 operation = params[:operation]
279 278
280 279 if not ['add','remove','assign'].include? operation
281 280 flash[:notice] = 'You did not choose the operation to perform.'
282 281 redirect_to :action => 'contest_management' and return
283 282 end
284 283
285 284 lines = params[:login_list]
286 285 if !lines or lines.blank?
287 286 flash[:notice] = 'You entered an empty list.'
288 287 redirect_to :action => 'contest_management' and return
289 288 end
290 289
291 290 note = []
292 291 users = []
293 292 lines.split("\n").each do |line|
294 293 user = User.find_by_login(line.chomp)
295 294 if user
296 295 if operation=='add'
297 296 if ! user.contests.include? contest
298 297 user.contests << contest
299 298 end
300 299 elsif operation=='remove'
301 300 user.contests.delete(contest)
302 301 else
303 302 user.contests = [contest]
304 303 end
305 304
306 305 if params[:reset_timer]
307 306 user.contest_stat.forced_logout = true
308 307 user.contest_stat.reset_timer_and_save
309 308 end
310 309
311 310 if params[:notification_emails]
312 311 send_contest_update_notification_email(user, contest)
313 312 end
@@ -88,108 +88,127
88 88
89 89 def forget
90 90 render :action => 'forget', :layout => 'empty'
91 91 end
92 92
93 93 def retrieve_password
94 94 email = params[:email]
95 95 user = User.find_by_email(email)
96 96 if user
97 97 last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour)
98 98 if last_updated_time > Time.now.gmtime - 5.minutes
99 99 flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes'
100 100 else
101 101 user.password = user.password_confirmation = User.random_password
102 102 user.save
103 103 send_new_password_email(user)
104 104 flash[:notice] = 'New password has been mailed to you.'
105 105 end
106 106 else
107 107 flash[:notice] = I18n.t 'registration.password_retrieval.no_email'
108 108 end
109 109 redirect_to :action => 'forget'
110 110 end
111 111
112 112 def profile
113 113 @user = User.find(params[:id])
114 114 @submission = Submission.includes(:problem).where(user_id: params[:id])
115 115
116 116 range = 120
117 117 @histogram = { data: Array.new(range,0), summary: {} }
118 118 @summary = {count: 0, solve: 0, attempt: 0}
119 119 problem = Hash.new(0)
120 120
121 121 @submission.find_each do |sub|
122 122 #histogram
123 123 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
124 124 @histogram[:data][d.to_i] += 1 if d < range
125 125
126 126 @summary[:count] += 1
127 127 next unless sub.problem
128 128 problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max
129 129 end
130 130
131 131 @histogram[:summary][:max] = [@histogram[:data].max,1].max
132 132 @summary[:attempt] = problem.count
133 133 problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
134 134 end
135 135
136 + def toggle_activate
137 + @user = User.find(params[:id])
138 + @user.update_attributes( activated: !@user.activated? )
139 + respond_to do |format|
140 + format.js { render partial: 'toggle_button',
141 + locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } }
142 + end
143 + end
144 +
145 + def toggle_enable
146 + @user = User.find(params[:id])
147 + @user.update_attributes( enabled: !@user.enabled? )
148 + respond_to do |format|
149 + format.js { render partial: 'toggle_button',
150 + locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } }
151 + end
152 + end
153 +
136 154 protected
137 155
138 156 def verify_online_registration
139 157 if !GraderConfiguration['system.online_registration']
140 158 redirect_to :controller => 'main', :action => 'login'
141 159 end
142 160 end
143 161
144 162 def send_confirmation_email(user)
145 163 contest_name = GraderConfiguration['contest.name']
146 164 activation_url = url_for(:action => 'confirm',
147 165 :login => user.login,
148 166 :activation => user.activation_key)
149 167 home_url = url_for(:controller => 'main', :action => 'index')
150 168 mail_subject = "[#{contest_name}] Confirmation"
151 169 mail_body = t('registration.email_body', {
152 170 :full_name => user.full_name,
153 171 :contest_name => contest_name,
154 172 :login => user.login,
155 173 :password => user.password,
156 174 :activation_url => activation_url,
157 175 :admin_email => GraderConfiguration['system.admin_email']
158 176 })
159 177
160 178 logger.info mail_body
161 179
162 180 send_mail(user.email, mail_subject, mail_body)
163 181 end
164 182
165 183 def send_new_password_email(user)
166 184 contest_name = GraderConfiguration['contest.name']
167 185 mail_subject = "[#{contest_name}] Password recovery"
168 186 mail_body = t('registration.password_retrieval.email_body', {
169 187 :full_name => user.full_name,
170 188 :contest_name => contest_name,
171 189 :login => user.login,
172 190 :password => user.password,
173 191 :admin_email => GraderConfiguration['system.admin_email']
174 192 })
175 193
176 194 logger.info mail_body
177 195
178 196 send_mail(user.email, mail_subject, mail_body)
179 197 end
180 198
181 199 # allow viewing of regular user profile only when options allow so
182 200 # only admins can view admins profile
183 201 def profile_authorization
184 202 #if view admins' profile, allow only admin
185 203 return false unless(params[:id])
186 204 user = User.find(params[:id])
187 205 return false unless user
188 206 return admin_authorization if user.admin?
189 207 return true if GraderConfiguration["right.user_view_submission"]
190 208
191 209 #finally, we allow only admin
192 210 admin_authorization
193 211 end
194 212
213 +
195 214 end
@@ -1,125 +1,185
1 1 # Methods added to this helper will be available to all templates in the application.
2 2 module ApplicationHelper
3 3
4 + def navbar_user_header
5 + left_menu = ''
6 + right_menu = ''
7 + user = User.find(session[:user_id])
8 +
9 + if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
10 + left_menu << add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list')
11 + left_menu << add_menu("#{I18n.t 'menu.submissions'}", 'main', 'submission')
12 + left_menu << add_menu("#{I18n.t 'menu.test'}", 'test', 'index')
13 + end
14 +
15 + if GraderConfiguration['right.user_hall_of_fame']
16 + left_menu << add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
17 + end
18 +
19 + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
20 + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
21 + if GraderConfiguration['system.user_setting_enabled']
22 + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
23 + end
24 + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
25 +
26 +
27 + result = content_tag(:ul,left_menu.html_safe,class: 'nav navbar-nav') + content_tag(:ul,right_menu.html_safe,class: 'nav navbar-nav navbar-right')
28 + end
29 +
30 + def add_menu(title, controller, action,html_option = {})
31 + link_option = {controller: controller, action: action}
32 + html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
33 + content_tag(:li, link_to(title,link_option),html_option)
34 + end
35 +
4 36 def user_header
5 37 menu_items = ''
6 38 user = User.find(session[:user_id])
7 39
8 40 if (user!=nil) and (session[:admin])
9 41 # admin menu
10 42 menu_items << "<b>Administrative task:</b> "
11 43 append_to menu_items, '[Announcements]', 'announcements', 'index'
12 44 append_to menu_items, '[Msg console]', 'messages', 'console'
13 45 append_to menu_items, '[Problems]', 'problems', 'index'
14 46 append_to menu_items, '[Users]', 'user_admin', 'index'
15 47 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
16 48 append_to menu_items, '[Report]', 'report', 'multiple_login'
17 49 append_to menu_items, '[Graders]', 'graders', 'list'
18 50 append_to menu_items, '[Contests]', 'contest_management', 'index'
19 51 append_to menu_items, '[Sites]', 'sites', 'index'
20 52 append_to menu_items, '[System config]', 'configurations', 'index'
21 53 menu_items << "<br/>"
22 54 end
23 55
24 56 # main page
25 57 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
26 58 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
27 59
28 60 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
29 61 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
30 62 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
31 63 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
32 64 end
33 65
34 66 if GraderConfiguration['right.user_hall_of_fame']
35 67 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
36 68 end
37 69 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
38 70
39 71 if GraderConfiguration['system.user_setting_enabled']
40 72 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
41 73 end
42 74 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
43 75
44 76 menu_items.html_safe
45 77 end
46 78
47 79 def append_to(option,label, controller, action)
48 80 option << ' ' if option!=''
49 81 option << link_to_unless_current(label,
50 82 :controller => controller,
51 83 :action => action)
52 84 end
53 85
54 86 def format_short_time(time)
55 87 now = Time.now.gmtime
56 88 st = ''
57 89 if (time.yday != now.yday) or
58 90 (time.year != now.year)
59 91 st = time.strftime("%x ")
60 92 end
61 93 st + time.strftime("%X")
62 94 end
63 95
64 96 def format_short_duration(duration)
65 97 return '' if duration==nil
66 98 d = duration.to_f
67 99 return Time.at(d).gmtime.strftime("%X")
68 100 end
69 101
70 102 def read_textfile(fname,max_size=2048)
71 103 begin
72 104 File.open(fname).read(max_size)
73 105 rescue
74 106 nil
75 107 end
76 108 end
77 109
110 + def toggle_button(on,toggle_url,id, option={})
111 + btn_size = option[:size] || 'btn-xs'
112 + link_to (on ? "Yes" : "No"), toggle_url,
113 + {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
114 + id: id,
115 + data: {remote: true, method: 'get'}}
116 + end
117 +
118 + def get_ace_mode(language)
119 + # return ace mode string from Language
120 +
121 + case language.pretty_name
122 + when 'Pascal'
123 + 'ace/mode/pascal'
124 + when 'C++','C'
125 + 'ace/mode/c_cpp'
126 + when 'Ruby'
127 + 'ace/mode/ruby'
128 + when 'Python'
129 + 'ace/mode/python'
130 + when 'Java'
131 + 'ace/mode/java'
132 + else
133 + 'ace/mode/c_cpp'
134 + end
135 + end
136 +
137 +
78 138 def user_title_bar(user)
79 139 header = ''
80 140 time_left = ''
81 141
82 142 #
83 143 # if the contest is over
84 144 if GraderConfiguration.time_limit_mode?
85 145 if user.contest_finished?
86 146 header = <<CONTEST_OVER
87 147 <tr><td colspan="2" align="center">
88 148 <span class="contest-over-msg">THE CONTEST IS OVER</span>
89 149 </td></tr>
90 150 CONTEST_OVER
91 151 end
92 152 if !user.contest_started?
93 153 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
94 154 else
95 155 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
96 156 " #{format_short_duration(user.contest_time_left)}"
97 157 end
98 158 end
99 159
100 160 #
101 161 # if the contest is in the anaysis mode
102 162 if GraderConfiguration.analysis_mode?
103 163 header = <<ANALYSISMODE
104 164 <tr><td colspan="2" align="center">
105 165 <span class="contest-over-msg">ANALYSIS MODE</span>
106 166 </td></tr>
107 167 ANALYSISMODE
108 168 end
109 169
110 170 contest_name = GraderConfiguration['contest.name']
111 171
112 172 #
113 173 # build real title bar
114 174 result = <<TITLEBAR
115 175 <div class="title">
116 176 <table>
117 177 #{header}
118 178 <tr>
119 179 <td class="left-col">
120 180 #{user.full_name}<br/>
121 181 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
122 182 #{time_left}
123 183 <br/>
124 184 </td>
125 185 <td class="right-col">#{contest_name}</td>
@@ -16,96 +16,100
16 16 def self.find_available_problems
17 17 Problem.available.all(:order => "date_added DESC, name ASC")
18 18 end
19 19
20 20 def self.create_from_import_form_params(params, old_problem=nil)
21 21 org_problem = old_problem || Problem.new
22 22 import_params, problem = Problem.extract_params_and_check(params,
23 23 org_problem)
24 24
25 25 if !problem.errors.empty?
26 26 return problem, 'Error importing'
27 27 end
28 28
29 29 problem.full_score = 100
30 30 problem.date_added = Time.new
31 31 problem.test_allowed = true
32 32 problem.output_only = false
33 33 problem.available = false
34 34
35 35 if not problem.save
36 36 return problem, 'Error importing'
37 37 end
38 38
39 39 import_to_db = params.has_key? :import_to_db
40 40
41 41 importer = TestdataImporter.new(problem)
42 42
43 43 if not importer.import_from_file(import_params[:file],
44 44 import_params[:time_limit],
45 45 import_params[:memory_limit],
46 46 import_params[:checker_name],
47 47 import_to_db)
48 48 problem.errors.add(:base,'Import error.')
49 49 end
50 50
51 51 return problem, importer.log_msg
52 52 end
53 53
54 54 def self.download_file_basedir
55 55 return "#{Rails.root}/data/tasks"
56 56 end
57 57
58 58 def get_submission_stat
59 59 result = Hash.new
60 60 #total number of submission
61 61 result[:total_sub] = Submission.where(problem_id: self.id).count
62 62 result[:attempted_user] = Submission.where(problem_id: self.id).group_by(:user_id)
63 63 end
64 +
65 + def long_name
66 + "[#{name}] #{full_name}"
67 + end
64 68
65 69 protected
66 70
67 71 def self.to_i_or_default(st, default)
68 72 if st!=''
69 73 result = st.to_i
70 74 end
71 75 result ||= default
72 76 end
73 77
74 78 def self.to_f_or_default(st, default)
75 79 if st!=''
76 80 result = st.to_f
77 81 end
78 82 result ||= default
79 83 end
80 84
81 85 def self.extract_params_and_check(params, problem)
82 86 time_limit = Problem.to_f_or_default(params[:time_limit],
83 87 DEFAULT_TIME_LIMIT)
84 88 memory_limit = Problem.to_i_or_default(params[:memory_limit],
85 89 DEFAULT_MEMORY_LIMIT)
86 90
87 91 if time_limit<=0 or time_limit >60
88 92 problem.errors.add(:base,'Time limit out of range.')
89 93 end
90 94
91 95 if memory_limit==0 and params[:memory_limit]!='0'
92 96 problem.errors.add(:base,'Memory limit format errors.')
93 97 elsif memory_limit<=0 or memory_limit >512
94 98 problem.errors.add(:base,'Memory limit out of range.')
95 99 end
96 100
97 101 if params[:file]==nil or params[:file]==''
98 102 problem.errors.add(:base,'No testdata file.')
99 103 end
100 104
101 105 checker_name = 'text'
102 106 if ['text','float'].include? params[:checker]
103 107 checker_name = params[:checker]
104 108 end
105 109
106 110 file = params[:file]
107 111
108 112 if !problem.errors.empty?
109 113 return nil, problem
110 114 end
111 115
@@ -194,97 +194,97
194 194 return site.time_left
195 195 elsif GraderConfiguration.indv_contest_mode?
196 196 time_limit = GraderConfiguration.contest_time_limit
197 197 if time_limit == nil
198 198 return nil
199 199 end
200 200 if contest_stat==nil or contest_stat.started_at==nil
201 201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
202 202 else
203 203 finish_time = contest_stat.started_at + time_limit
204 204 current_time = Time.now.gmtime
205 205 if current_time > finish_time
206 206 return 0
207 207 else
208 208 return finish_time - current_time
209 209 end
210 210 end
211 211 else
212 212 return nil
213 213 end
214 214 end
215 215
216 216 def contest_finished?
217 217 if GraderConfiguration.contest_mode?
218 218 return false if site==nil
219 219 return site.finished?
220 220 elsif GraderConfiguration.indv_contest_mode?
221 221 return false if self.contest_stat(true)==nil
222 222 return contest_time_left == 0
223 223 else
224 224 return false
225 225 end
226 226 end
227 227
228 228 def contest_started?
229 229 if GraderConfiguration.indv_contest_mode?
230 230 stat = self.contest_stat
231 231 return ((stat != nil) and (stat.started_at != nil))
232 232 elsif GraderConfiguration.contest_mode?
233 233 return true if site==nil
234 234 return site.started
235 235 else
236 236 return true
237 237 end
238 238 end
239 239
240 240 def update_start_time
241 241 stat = self.contest_stat
242 - if stat == nil or stat.started_at == nil
242 + if (stat.nil?) or (stat.started_at.nil?)
243 243 stat ||= UserContestStat.new(:user => self)
244 244 stat.started_at = Time.now.gmtime
245 245 stat.save
246 246 end
247 247 end
248 248
249 249 def problem_in_user_contests?(problem)
250 250 problem_contests = problem.contests.all
251 251
252 252 if problem_contests.length == 0 # this is public contest
253 253 return true
254 254 end
255 255
256 256 contests.each do |contest|
257 257 if problem_contests.find {|c| c.id == contest.id }
258 258 return true
259 259 end
260 260 end
261 261 return false
262 262 end
263 263
264 264 def available_problems_group_by_contests
265 265 contest_problems = []
266 266 pin = {}
267 267 contests.enabled.each do |contest|
268 268 available_problems = contest.problems.available
269 269 contest_problems << {
270 270 :contest => contest,
271 271 :problems => available_problems
272 272 }
273 273 available_problems.each {|p| pin[p.id] = true}
274 274 end
275 275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
276 276 contest_problems << {
277 277 :contest => nil,
278 278 :problems => other_avaiable_problems
279 279 }
280 280 return contest_problems
281 281 end
282 282
283 283 def available_problems
284 284 if not GraderConfiguration.multicontests?
285 285 return Problem.find_available_problems
286 286 else
287 287 contest_problems = []
288 288 pin = {}
289 289 contests.enabled.each do |contest|
290 290 contest.problems.available.each do |problem|
@@ -1,32 +1,34
1 - - content_for :header do
2 - = javascript_include_tag 'local_jquery'
1 + /- content_for :header do
2 + / = javascript_include_tag 'local_jquery'
3 3
4 4 %h1 System configuration
5 5
6 6 %table.info
7 7 %tr.info-head
8 8 %th Key
9 9 %th Type
10 10 %th Value
11 11 %th Description
12 12 - @configurations.each do |conf|
13 13 - @grader_configuration = conf
14 14 %tr{:class => cycle("info-odd", "info-even")}
15 15 %td
16 - = in_place_editor_field :grader_configuration, :key, {}, :rows=>1
16 + /= in_place_editor_field :grader_configuration, :key, {}, :rows=>1
17 + = @grader_configuration.key
17 18 %td
18 - = in_place_editor_field :grader_configuration, :value_type, {}, :rows=>1
19 + /= in_place_editor_field :grader_configuration, :value_type, {}, :rows=>1
20 + = @grader_configuration.value_type
19 21 %td
20 22 = best_in_place @grader_configuration, :value, ok_button: "ok", cancel_button: "cancel"
21 23 %td= conf.description
22 24
23 25 - if GraderConfiguration.config_cached?
24 26 %br/
25 27 Your config is saved, but it does not automatically take effect.
26 28 %br/
27 29 If you have one mongrel process running, you can
28 30 = link_to '[click]', :action => 'reload'
29 31 here to reload.
30 32 %br/
31 33 If you have more than one process running, you should restart
32 34 them manually.
@@ -1,11 +1,11
1 1
2 2 %td= grader.host
3 3 %td= grader.pid
4 4 %td= grader.mode
5 - %td= grader.updated_at.strftime("%H:%M:%S") if grader.updated_at!=nil
5 + %td= grader.updated_at.strftime("%H:%M:%S") unless grader.updated_at.nil?
6 6 %td= grader.task_type
7 7 %td
8 - - if grader.task_id==nil
8 + - if grader.task_id.nil?
9 9 idle
10 10 - else
11 11 = link_to "#{grader.task_id}", :action => 'view', :id => grader.task_id, :type => grader.task_type
@@ -1,25 +1,26
1 1 - if grader_list.length!=0
2 - %table.graders
2 + %table.table.table-striped.table-condensed
3 3 %tr
4 4 %th host
5 5 %th pid
6 6 %th mode
7 7 %th last updated
8 8 %th type
9 9 %th task
10 + %th
10 11 - grader_list.each do |grader|
11 12 - if grader.active
12 13 - c = 'active'
13 14 - else
14 15 - c = 'inactive'
15 16 %tr{:class => c}
16 17 = render :partial => 'grader', :locals => {:grader => grader}
17 18 - if not grader.terminated
18 19 - if GraderScript.grader_control_enabled?
19 - %td= link_to 'stop', {:action => 'stop', :id => grader}
20 + %td= link_to 'stop', {:action => 'stop', :id => grader}, class: 'btn btn-danger btn-xs btn-block'
20 21 - else
21 - %td= link_to 'clear', {:action => 'clear', :id => grader}
22 + %td= link_to 'clear', {:action => 'clear', :id => grader}, class: 'btn btn-danger btn-xs btn-block'
22 23 - else
23 24 %ul
24 25 %li None
25 26
@@ -1,73 +1,81
1 1 - content_for :head do
2 - = stylesheet_link_tag 'graders'
3 - = javascript_include_tag 'local_jquery'
4 2 <meta http-equiv ="refresh" content="60"/>
5 3
6 4 %h1 Grader information
7 5
8 - = link_to '[Refresh]', :action => 'list'
9 - %br/
6 + %p
7 + = link_to 'Refresh', { :action => 'list' }, class: 'btn btn-info'
8 +
9 + .panel.panel-primary
10 + .panel-heading
11 + Grader control:
12 + .panel-body
13 + =link_to 'Start Graders in grading env', { action: 'start_grading'}, class: 'btn btn-default', method: 'post'
14 + =link_to 'Start Graders in exam env', { action: 'start_exam'}, class: 'btn btn-default', method: 'post'
15 + =link_to 'Stop all running Graders', { action: 'stop_all'}, class: 'btn btn-default', method: 'post'
16 + =link_to 'Clear all data', { action: 'clear_all'}, class: 'btn btn-default', method: 'post'
10 17
11 18 .submitbox
12 19 .item
13 20 Grader control:
14 21 .item
15 22 = form_for :clear, :url => {:action => 'start_grading'} do |f|
16 23 = submit_tag 'Start graders in grading env'
17 24 .item
18 25 = form_for :clear, :url => {:action => 'start_exam'} do |f|
19 26 = submit_tag 'Start graders in exam env'
20 27 .item
21 28 = form_for :clear, :url => {:action => 'stop_all'} do |f|
22 29 = submit_tag 'Stop all running graders'
23 30 .item
24 31 = form_for :clear, :url => {:action => 'clear_all'} do |f|
25 32 = submit_tag 'Clear all data'
26 33 %br{:style => 'clear:both'}/
27 34
28 - %div{style: 'width:500px; float: left;'}
29 - - if @last_task
30 - Last task:
31 - = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task'
35 + .row
36 + .col-md-6
37 + - if @last_task
38 + Last task:
39 + = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task'
32 40
33 - %br/
41 + %br/
34 42
35 - - if @last_test_request
36 - Last test_request:
37 - = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest'
43 + - if @last_test_request
44 + Last test_request:
45 + = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest'
38 46
39 - %h2 Current graders
47 + %h2 Current graders
40 48
41 - = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes}
49 + = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes}
42 50
43 - %h2 Stalled graders
51 + %h2 Stalled graders
44 52
45 - = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes}
53 + = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes}
46 54
47 - %h2 Terminated graders
55 + %h2 Terminated graders
48 56
49 - = form_for :clear, :url => {:action => 'clear_terminated'} do |f|
50 - = submit_tag 'Clear data for terminated graders'
57 + %p= link_to 'Clear data for terminated graders', { action: 'clear_terminated'}, class: 'btn btn-default', method: 'post'
51 58
52 - = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes}
53 - %div{}
54 - %h2 Last 20 submissions
55 - %table.graders
56 - %thead
57 - %th ID
58 - %th User
59 - %th Problem
60 - %th Submitted
61 - %th Graded
62 - %th Result
63 - %tbody
64 - - @submission.each do |sub|
65 - %tr.inactive
66 - %td= link_to sub.id, controller: 'graders' ,action: 'submission', id: sub.id
67 - %td= sub.try(:user).try(:full_name)
68 - %td= sub.try(:problem).try(:full_name)
69 - %td= "#{time_ago_in_words(sub.submitted_at)} ago"
70 - %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
71 - %td= sub.grader_comment
59 + = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes}
60 + .col-md-6
61 + %h2 Last 20 submissions
62 + %table.table.table-striped.table-condensed
63 + %thead
64 + %th ID
65 + %th User
66 + %th Problem
67 + %th Submitted
68 + %th Graded
69 + %th Result
70 + %th
71 + %tbody
72 + - @submission.each do |sub|
73 + %tr.inactive
74 + %td= link_to sub.id, controller: 'graders' ,action: 'submission', id: sub.id
75 + %td= sub.try(:user).try(:full_name)
76 + %td= sub.try(:problem).try(:full_name)
77 + %td= "#{time_ago_in_words(sub.submitted_at)} ago"
78 + %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " "
79 + %td= sub.grader_comment
72 80
73 81
@@ -1,67 +1,90
1 - %style{type: "text/css"}
2 - = @css_style
3 - :css
4 - .field {
5 - font-weight: bold;
6 - text-align: right;
7 - padding: 3px;
8 - }
9 -
1 + //%style{type: "text/css"}
2 + // = @css_style
10 3
11 4 %h1= "Submission: #{@submission.id}"
12 5
13 -
14 - %h2 Stat
6 + %textarea#data{style: "display:none;"}
7 + :preserve
8 + #{@submission.source}
15 9
16 - %table.info
17 - %thead
18 - %tr.info-head
19 - %th Field
20 - %th Value
21 - %tbody
22 - %tr{class: cycle('info-even','info-odd')}
23 - %td.field User:
24 - %td.value
25 - - if @submission.user
26 - = link_to "(#{@submission.user.login})", controller: "users", action: "profile", id: @submission.user
27 - = @submission.user.full_name
28 - - else
29 - = "(n/a)"
30 - %tr{class: cycle('info-even','info-odd')}
31 - %td.field Problem:
32 - %td.value
33 - - if @submission.problem!=nil
34 - = link_to "(#{@submission.problem.name})", controller: "problems", action: "stat", id: @submission.problem
35 - = @submission.problem.full_name
36 - - else
37 - = "(n/a)"
38 - %tr{class: cycle('info-even','info-odd')}
39 - %td.field Tries:
40 - %td.value= @submission.number
41 - %tr{class: cycle('info-even','info-odd')}
42 - %td.field Submitted:
43 - %td.value #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
44 - %tr{class: cycle('info-even','info-odd')}
45 - %td.field Graded:
46 - %td.value #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
47 - %tr{class: cycle('info-even','info-odd')}
48 - %td.field Points:
49 - %td.value #{@submission.points}/#{@submission.problem.full_score}
50 - %tr{class: cycle('info-even','info-odd')}
51 - %td.field Comment:
52 - %td.value #{@submission.grader_comment}
53 - %tr{class: cycle('info-even','info-odd')}
54 - %td.field Runtime (s):
55 - %td.value #{@submission.max_runtime}
56 - %tr{class: cycle('info-even','info-odd')}
57 - %td.field Memory (kb):
58 - %td.value #{@submission.peak_memory}
59 - - if session[:admin]
60 - %tr{class: cycle('info-even','info-odd')}
61 - %td.field IP:
62 - %td.value #{@submission.ip_address}
10 + //%div.highlight{:style => "border: 1px solid black;"}
11 + //=@formatted_code.html_safe
12 + .containter
13 + .row
14 + .col-md-7
15 + %h2 Source Code
16 + .col-md-5
17 + %h2 Stat
18 + .row
19 + .col-md-7
20 + %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
21 + :javascript
22 + e = ace.edit("editor")
23 + e.setOptions({ maxLines: Infinity })
24 + e.setValue($("#data").text())
25 + e.gotoLine(1)
26 + e.getSession().setMode("#{get_ace_mode(@submission.language)}")
27 + e.setReadOnly(true)
28 + .col-md-5
29 + %table.table.table-striped
30 + %tr
31 + %td.text-right
32 + %strong User
33 + %td
34 + - if @submission.user
35 + = link_to "(#{@submission.user.login})", controller: "users", action: "profile", id: @submission.user
36 + = @submission.user.full_name
37 + - else
38 + = "(n/a)"
39 + %tr
40 + %td.text-right
41 + %strong Task
42 + %td
43 + - if @submission.problem!=nil
44 + = link_to "(#{@submission.problem.name})", controller: "problems", action: "stat", id: @submission.problem
45 + = @submission.problem.full_name
46 + - else
47 + = "(n/a)"
48 + %tr
49 + %td.text-right
50 + %strong Tries
51 + %td= @submission.number
52 + %tr
53 + %td.text-right
54 + %strong Language
55 + %td= @submission.language.pretty_name
56 + %tr
57 + %td.text-right
58 + %strong Submitted
59 + %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
60 + %tr
61 + %td.text-right
62 + %strong Graded
63 + - if @submission.graded_at
64 + %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
65 + - else
66 + %td -
67 + %tr
68 + %td.text-right
69 + %strong Points
70 + %td #{@submission.points}/#{@submission.problem.full_score}
71 + %tr
72 + %td.text-right
73 + %strong Comment
74 + %td #{@submission.grader_comment}
75 + %tr
76 + %td.text-right
77 + %strong Runtime (s)
78 + %td #{@submission.max_runtime}
79 + %tr
80 + %td.text-right
81 + %strong Memory (kb)
82 + %td #{@submission.peak_memory}
83 + - if session[:admin]
84 + %tr
85 + %td.text-right
86 + %strong IP
87 + %td #{@submission.ip_address}
63 88
64 - %h2 Source code
65 - //%div.highlight{:style => "border: 1px solid black;"}
66 - =@formatted_code.html_safe
67 89
90 +
@@ -1,19 +1,13
1 - .announcement{:id => "announcement-#{announcement.id}", :style => "#{'display: none; opacity: 0' if (defined? announcement_effect) and announcement_effect }"}
2 - %div
3 - .announcement-title
4 - -# .toggles
5 - -# %a{:href => '#', :onclick => "$(\"announcement-body-#{announcement.id}\").blindUp({duration: 0.2}); return false;"}
6 - -# [hide]
7 - -# %a{:href => '#', :onclick => "$(\"announcement-body-#{announcement.id}\").blindDown({duration: 0.2}); return false;"}
8 - -# [show]
9 - = announcement.title
10 - .announcement-body{:id => "announcement-body-#{announcement.id}"}
11 - = markdown(announcement.body)
12 - -#.pub-info
13 - -# %p= "#{announcement.author}, #{announcement.created_at}"
1 + %li.list-group-item
2 + %strong
3 + = announcement.title
4 + %small= "(updated #{time_ago_in_words(announcement.updated_at)} ago on #{announcement.updated_at})"
5 +
6 + %br
7 + = markdown(announcement.body)
8 + :javascript
9 + Announcement.updateRecentId(#{announcement.id});
10 + - if (defined? announcement_effect) and announcement_effect
14 11 :javascript
15 - Announcement.updateRecentId(#{announcement.id});
16 - - if (defined? announcement_effect) and announcement_effect
17 - :javascript
18 - $("announcement-#{announcement.id}").blindDown({duration: 0.2});
19 - $("announcement-#{announcement.id}").appear({duration: 0.5, queue: 'end'});
12 + $("announcement-#{announcement.id}").blindDown({duration: 0.2});
13 + $("announcement-#{announcement.id}").appear({duration: 0.5, queue: 'end'});
@@ -1,24 +1,26
1 1
2 - %tr{:class => ((submission_counter%2==0) ? "info-even" : "info-odd")}
3 - %td.info{:align => "center"}
2 + %tr
3 + %td{:align => "center"}
4 4 = submission_counter+1
5 - %td.info{:align => "center"}
5 + %td{:align => "center"}
6 6 = link_to "##{submission.id}", controller: :graders, action: :submission, id: submission.id
7 - %td.info
7 + %td
8 8 = l submission.submitted_at, format: :long
9 9 = "( #{time_ago_in_words(submission.submitted_at)} ago)"
10 - %td.info{:align => "center"}
10 + %td
11 11 = submission.source_filename
12 12 = " (#{submission.language.pretty_name}) "
13 13 = link_to('[load]',{:action => 'source', :id => submission.id})
14 - %td.info
15 - - if submission.graded_at!=nil
14 + %td
15 + - if submission.graded_at
16 16 = "Graded at #{format_short_time(submission.graded_at)}."
17 17 %br/
18 18 = "Score: #{(submission.points*100/submission.problem.full_score).to_i} " if GraderConfiguration['ui.show_score']
19 19 = " ["
20 20 %tt
21 21 = submission.grader_comment
22 22 = "]"
23 - %td.info
23 + %td
24 24 = render :partial => 'compiler_message', :locals => {:compiler_message => submission.compiler_message }
25 + %td
26 + = link_to 'Edit', direct_edit_submission_path(submission.id), class: 'btn btn-success'
@@ -1,26 +1,26
1 1
2 - - if submission==nil
2 + - if submission.nil?
3 3 = "-"
4 4 - else
5 - - if submission.graded_at==nil
5 + - if submission.graded_at.nil?
6 6 =t 'main.submitted_at'
7 7 = format_short_time(submission.submitted_at.localtime)
8 8 - else
9 9 = t 'main.graded_at'
10 10 = "#{format_short_time(submission.graded_at.localtime)}, "
11 11 - if GraderConfiguration['ui.show_score']
12 12 = t 'main.score'
13 13 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 14 = " ["
15 15 %tt
16 16 = submission.grader_comment
17 17 = "]"
18 18 - if GraderConfiguration.show_grading_result
19 19 = " | "
20 20 = link_to '[detailed result]', :action => 'result', :id => submission.id
21 21 = " | "
22 22 = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true})
23 23 = " | "
24 24 = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id})
25 - = " | "
26 - = link_to "[#{t 'main.submissions_link'}]", :action => 'submission', :id => problem_name
25 + //= " | "
26 + //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id)
@@ -1,53 +1,52
1 1 - content_for :head do
2 2 = javascript_include_tag "announcement_refresh"
3 3
4 4 = user_title_bar(@user)
5 5
6 - .announcementbox{:style => (@announcements.length==0 ? "display:none" : "")}
7 - %span{:class => 'title'}
8 - Announcements
9 - #announcementbox-body
10 - = render :partial => 'announcement', :collection => @announcements
11 -
12 - - if GraderConfiguration.show_submitbox_to?(@user)
13 - .submitbox
14 - = error_messages_for 'submission'
15 - = render :partial => 'submission_box'
16 -
17 -
18 - %hr/
19 -
20 6 - if (GraderConfiguration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true)
21 7 %p=t 'main.start_soon'
22 8
23 - - if GraderConfiguration.show_tasks_to?(@user)
24 - - if not GraderConfiguration.multicontests?
25 - %table.info
26 - %tr.info-head
27 - %th
28 - %th Tasks name
29 - %th Full name
30 - %th # of sub(s)
31 - %th Results
32 - = render :partial => 'problem', :collection => @problems
33 - - else
34 - - @contest_problems.each do |cp|
35 - - if cp[:problems].length > 0
36 - %h2{:class =>'contest-title'}
37 - = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}"
38 - %table.info
39 - %tr.info-head
40 - %th
41 - %th Tasks name
42 - %th Full name
43 - %th # of sub(s)
44 - %th Results
45 - = render :partial => 'problem', :collection => cp[:problems]
46 -
47 -
48 - %hr/
9 + .row
10 + .col-md-7
11 + - if GraderConfiguration.show_submitbox_to?(@user)
12 + .panel.panel-primary
13 + .panel-heading
14 + Submission
15 + .panel-body
16 + = render :partial => 'submission_box'
17 + - if GraderConfiguration.show_tasks_to?(@user)
18 + - if not GraderConfiguration.multicontests?
19 + %table.table.table-striped.table-condensed
20 + %thead
21 + %tr
22 + %th Task name
23 + %th Full name
24 + %th # of sub(s)
25 + %th Results
26 + %th
27 + %tbody
28 + = render :partial => 'problem', :collection => @problems
29 + - else
30 + - @contest_problems.each do |cp|
31 + - if cp[:problems].length > 0
32 + %h2{:class =>'contest-title'}
33 + = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}"
34 + %table.info
35 + %tr.info-head
36 + %th Task name
37 + %th Full name
38 + %th # of sub(s)
39 + %th Results
40 + %th
41 + = render :partial => 'problem', :collection => cp[:problems]
42 + .col-md-5
43 + .panel.panel-info
44 + .panel-heading
45 + Announcement
46 + %ul.list-group
47 + = render :partial => 'announcement', :collection => @announcements
49 48
50 49 %script{:type => 'text/javascript'}
51 50 = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';"
52 51 Announcement.registerRefreshEventTimer();
53 52
@@ -1,25 +1,31
1 1 = user_title_bar(@user)
2 2
3 - .task-menu
4 - Task List
5 - %br/
6 - - @problems.each do |problem|
7 - = link_to problem.name, :action => 'submission', :id => problem.name
3 + .panel.panel-info
4 + .panel-heading
5 + Select Problems
6 + .panel-body
7 + .form-inline
8 + = select 'submission',
9 + 'problem_id',
10 + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", main_submission_url(p.id)]},
11 + { selected: (@problem ? main_submission_url(@problem) : -1) },
12 + { class: 'select2 form-control'}
13 + %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: '#submission_problem_id'}} Go
8 14
9 15 - if @problem!=nil
10 16 %h2= "Task: #{@problem.full_name} (#{@problem.name})"
11 17
12 18 - if @submissions!=nil
13 19 - if @submissions.length>0
14 - %table.info
15 - %tr.info-head
16 - %th.info No.
17 - %th.info #
18 - %th.info At
19 - %th.info Source
20 - %th.info Result
21 - %th.info{:width => "300px"}
22 - Compiler message
20 + %table.table
21 + %thead
22 + %th No.
23 + %th #
24 + %th At
25 + %th Source
26 + %th Result
27 + %th{:width => "300px"} Compiler message
28 + %th
23 29 = render :partial => 'submission', :collection => @submissions
24 30 - else
25 31 No submission
@@ -1,127 +1,136
1 - - content_for :header do
2 - = javascript_include_tag 'local_jquery'
3 -
4 - :javascript
5 - $(document).ready( function() {
6 - $("#mem_remark").hover( function() {
7 - $("#mem_remark_box").show();
8 - }, function() {
9 - $("#mem_remark_box").hide();
10 - });
11 - });
12 1 :css
13 2 .hof_user { color: orangered; font-style: italic; }
14 3 .hof_language { color: green; font-style: italic; }
15 4 .hof_value { color: deeppink;font-style: italic; }
16 5 .info_param { font-weight: bold;text-align: right; }
17 6 .tooltip {
18 7 font-family: Verdana,sans-serif;
19 8 font-weight: normal;
20 9 text-align: left;
21 10 font-size: 1.0em;
22 11 color: black;
23 12 line-height: 1.1;
24 13 display: none;
25 14 min-width: 20em;
26 15 position: absolute;
27 16 left: 25px;
28 17 bottom: 5px;
29 18 border: 1px solid;
30 19 padding: 5px;
31 20 background-color: #FFF;
32 21 word-wrap: break-word;
33 22 z-index: 9999;
34 23 overflow: auto;
35 24 }
36 25
37 - %h1 (#{Problem.find(params[:id]).name}) #{Problem.find(params[:id]).full_name}
38 26
39 - %h2 Problem Stat
40 - %table.info
41 - %thead
42 - %tr.info-head
43 - %th Stat
44 - %th Value
45 - %tbody
46 - %tr{class: cycle('info-even','info-odd')}
47 - %td.info_param Submissions
48 - %td= @summary[:count]
49 - %tr{class: cycle('info-even','info-odd')}
50 - %td.info_param Solved/Attempted User
51 - %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
52 - - if @best
53 - %tr{class: cycle('info-even','info-odd')}
54 - %td.info_param Best Runtime
55 - %td
56 - by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
57 - using <span class="hof_language">#{@best[:runtime][:lang]}</span>
58 - with <span class="hof_value">#{@best[:runtime][:value] * 1000} milliseconds</span>
59 - at submission
60 - = link_to("#" + @best[:runtime][:sub_id].to_s, controller: 'graders', action: 'submission', id:@best[:runtime][:sub_id])
27 + .container
28 + .row
29 + .col-md-4
30 + %h2 Overall Stat
31 + %table.table.table-hover
32 + %thead
33 + %tr
34 + %th
35 + %th
36 + %tbody
37 + %tr
38 + %td.info_param Submissions
39 + %td= @summary[:count]
40 + %tr
41 + %td.info_param Solved/Attempted User
42 + %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
43 + - if @best
44 + %tr
45 + %td.info_param Best Runtime
46 + %td
47 + by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
48 + %br
49 + using <span class="text-success">#{@best[:runtime][:lang]}</span>
50 + %br
51 + with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
52 + %br
53 + at submission
54 + = link_to("#" + @best[:runtime][:sub_id].to_s, controller: 'graders', action: 'submission', id:@best[:runtime][:sub_id])
61 55
62 - %tr{class: cycle('info-even','info-odd')}
63 - %td.info_param
64 - Best Memory Usage
65 - %sup{ id: "mem_remark", style: "position:relative; color: blue;"}
66 - [?]
67 - %span.tooltip#mem_remark_box
68 - This counts only for submission with 100% score.
69 - Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)
70 - %td
71 - by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
72 - using <span class="hof_language">#{@best[:memory][:lang]}</span>
73 - with <span class="hof_value">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
74 - at submission
75 - = link_to("#" + @best[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id:@best[:memory][:sub_id])
56 + %tr
57 + %td.info_param
58 + Best Memory Usage
59 + %sup{ id: "xmem_remark",
60 + style: "position:relative; color: blue;",
61 + data: {toggle: 'tooltip', placement: 'top', animation: 'false', delay: 20},
62 + title: "This counts only for submission with 100% score. Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)"}
63 + [?]
64 + %td
65 + by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]}
66 + %br
67 + using <span class="text-success">#{@best[:memory][:lang]}</span>
68 + %br
69 + with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
70 + %br
71 + at submission
72 + = link_to("#" + @best[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id:@best[:memory][:sub_id])
76 73
77 - %tr{class: cycle('info-even','info-odd')}
78 - %td.info_param Shortest Code
79 - %td
80 - by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]}
81 - using <span class="hof_language">#{@best[:length][:lang]}</span>
82 - with <span class="hof_value">#{@best[:length][:value]} bytes</span>
83 - at submission
84 - = link_to("#" + @best[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:length][:sub_id])
85 -
86 - %tr{class: cycle('info-even','info-odd')}
87 - %td.info_param First solver
88 - %td
89 - #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver
90 - using <span class="hof_language">#{@best[:first][:lang]}</span>
91 - on <span class="hof_value">#{@best[:first][:value]}</span>
92 - at submission
93 - = link_to("#" + @best[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:first][:sub_id])
94 -
95 - - if @best
96 - %h2 By language
74 + %tr
75 + %td.info_param Shortest Code
76 + %td
77 + by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]}
78 + %br
79 + using <span class="text-success">#{@best[:length][:lang]}</span>
80 + %br
81 + with <span class="text-success">#{@best[:length][:value]} bytes</span>
82 + %br
83 + at submission
84 + = link_to("#" + @best[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:length][:sub_id])
97 85
98 - %table.info
99 - %thead
100 - %tr.info-head
101 - %th Language
102 - %th Best runtime (ms)
103 - %th Best memory (kbytes)
104 - %th Shortest Code (bytes)
105 - %th First solver
106 - %tbody
107 - - @by_lang.each do |lang,value|
108 - %tr{class: cycle('info-even','info-odd')}
109 - %td= lang
110 - %td
111 - = link_to value[:runtime][:user], controller: 'users', action: 'profile', id: value[:runtime][:user_id]
112 - = "(#{(value[:runtime][:value] * 1000).to_i} @"
113 - = "#{link_to("#" + value[:runtime][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:runtime][:sub_id])} )".html_safe
114 - %td
115 - = link_to value[:memory][:user], controller: 'users', action: 'profile', id: value[:memory][:user_id]
116 - = "(#{number_with_delimiter(value[:memory][:value])} @"
117 - = "#{link_to("#" + value[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:memory][:sub_id])} )".html_safe
118 - %td
119 - = link_to value[:length][:user], controller: 'users', action: 'profile', id: value[:length][:user_id]
120 - = "(#{value[:length][:value]} @"
121 - = "#{link_to("#" + value[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:length][:sub_id])} )".html_safe
122 - %td
123 - - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
124 - = link_to value[:first][:user], controller: 'users', action: 'profile', id: value[:first][:user_id]
125 - = "(#{value[:first][:value]} @"
126 - = "#{link_to("#" + value[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:first][:sub_id])} )".html_safe
86 + %tr
87 + %td.info_param First solver
88 + %td
89 + - if @best[:first][:user] != '(NULL)'
90 + #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver
91 + %br
92 + using <span class="text-success">#{@best[:first][:lang]}</span>
93 + %br
94 + on <span class="text-success">#{@best[:first][:value]}</span>
95 + %br
96 + at submission
97 + = link_to("#" + @best[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:first][:sub_id])
98 + - else
99 + no first solver
100 + .col-md-8
101 + - if @best
102 + %h2 By Language
103 + %table.table.table-hover
104 + %thead
105 + %tr
106 + %th Language
107 + %th Best runtime (ms)
108 + %th Best memory (kbytes)
109 + %th Shortest Code (bytes)
110 + %th First solver
111 + %tbody
112 + - @by_lang.each do |lang,value|
113 + %tr
114 + %td= lang
115 + %td
116 + = link_to value[:runtime][:user], controller: 'users', action: 'profile', id: value[:runtime][:user_id]
117 + %br
118 + = "(#{(value[:runtime][:value] * 1000).to_i} @"
119 + = "#{link_to("#" + value[:runtime][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:runtime][:sub_id])} )".html_safe
120 + %td
121 + = link_to value[:memory][:user], controller: 'users', action: 'profile', id: value[:memory][:user_id]
122 + %br
123 + = "(#{number_with_delimiter(value[:memory][:value])} @"
124 + = "#{link_to("#" + value[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:memory][:sub_id])} )".html_safe
125 + %td
126 + = link_to value[:length][:user], controller: 'users', action: 'profile', id: value[:length][:user_id]
127 + %br
128 + = "(#{value[:length][:value]} @"
129 + = "#{link_to("#" + value[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:length][:sub_id])} )".html_safe
130 + %td
131 + - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
132 + = link_to value[:first][:user], controller: 'users', action: 'profile', id: value[:first][:user_id]
133 + %br
134 + = "(#{value[:first][:value]} @"
135 + = "#{link_to("#" + value[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:first][:sub_id])} )".html_safe
127 136
@@ -1,23 +1,29
1 1
2 2 /- if params[:id]
3 3 / %h1 Tasks Hall of Fame
4 4 / = link_to('[back to All-Time Hall of Fame]', action: 'problem_hof', id: nil )
5 5 /- else
6 6 / %h1 All-Time Hall of Fame
7 7
8 + .panel.panel-info
9 + .panel-heading
10 + Select Task
11 + .panel-body
12 + .form-inline
13 + = select 'report',
14 + 'problem_id',
15 + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", report_problem_hof_url(p.id)]},
16 + {:selected => report_problem_hof_url(@problem)},
17 + { class: 'select2 form-control' }
18 + %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: "#report_problem_id"}} Go
8 19
9 - %h1 Hall of Fame
10 - .task-menu
11 - Tasks
12 - %br/
13 - - @problems.each do |prob|
14 - = link_to( "[#{prob.name}]", {id: prob.id})
15 20
16 21 - unless params[:id]
17 22 /=render partial: 'all_time_hof'
18 23 Please select a problem.
19 24 - else
20 - =render partial: 'task_hof'
25 + %h1 [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name}
21 26 %h2 Submission History
22 27 =render partial: 'application/bar_graph', locals: { histogram: @histogram }
28 + =render partial: 'task_hof'
23 29
@@ -1,59 +1,61
1 - - content_for :header do
2 - = javascript_include_tag 'local_jquery'
3 - = stylesheet_link_tag 'tablesorter-theme.cafe'
1 + /- content_for :header do
2 + / = javascript_include_tag 'local_jquery'
3 + / = stylesheet_link_tag 'tablesorter-theme.cafe'
4 4
5 5 %script{:type=>"text/javascript"}
6 6 $(function () {
7 7 $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
8 8 $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
9 - $('#my_table').tablesorter({widgets: ['zebra']});
9 + /$('#my_table').tablesorter({widgets: ['zebra']});
10 10 });
11 11
12 12 %h1 User grading results
13 13 %h2= params[:action] == 'user_stat' ? "Show scores from latest submission" : "Show max scores in submission range"
14 14
15 15
16 16 - if @problem and @problem.errors
17 17 =error_messages_for 'problem'
18 18
19 19 = render partial: 'submission_range'
20 20
21 21 - if params[:action] == 'user_stat'
22 22 %h3 Latest score
23 23 = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat, commit: 'download csv'
24 24 - else
25 25 %h3 Max score
26 26 = link_to '[Show only latest submissions]', controller: :user_admin, action: :user_stat
27 27 = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat_max, commit: 'download csv'
28 28
29 - %table.tablesorter-cafe#my_table
29 + %table.table.sortable.table-striped.table-bordered
30 30 %thead
31 31 %tr
32 - %th User
32 + %th Login
33 33 %th Name
34 34 %th Activated?
35 - %th Logged in
35 + %th Logged_in
36 36 %th Contest(s)
37 37 %th Remark
38 38 - @problems.each do |p|
39 - %th= p.name
40 - %th Total
41 - %th Passed
39 + %th.text-right= p.name
40 + %th.text-right Total
41 + %th.text-right Passed
42 42 %tbody
43 43 - @scorearray.each do |sc|
44 - %tr{class: cycle('info-even','info-odd')}
44 + %tr
45 45 - total,num_passed = 0,0
46 46 - sc.each_index do |i|
47 47 - if i == 0
48 48 %td= link_to sc[i].login, controller: 'users', action: 'profile', id: sc[i]
49 49 %td= sc[i].full_name
50 50 %td= sc[i].activated
51 51 %td= sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no'
52 52 %td= sc[i].contests.collect {|c| c.name}.join(', ')
53 53 %td= sc[i].remark
54 54 - else
55 - %td= sc[i][0]
55 + %td.text-right= sc[i][0]
56 56 - total += sc[i][0]
57 57 - num_passed += 1 if sc[i][1]
58 - %td= total
59 - %td= num_passed
58 + %td.text-right= total
59 + %td.text-right= num_passed
60 + :javascript
61 + $.bootstrapSortable(true,'reversed')
@@ -14,52 +14,57
14 14 # Settings in config/environments/* take precedence over those specified here.
15 15 # Application configuration should go into files in config/initializers
16 16 # -- all .rb files in that directory are automatically loaded.
17 17
18 18 # Custom directories with classes and modules you want to be autoloadable.
19 19 config.autoload_paths += %W(#{config.root}/lib)
20 20
21 21 # Only load the plugins named here, in the order given (default is alphabetical).
22 22 # :all can be used as a placeholder for all plugins not explicitly named.
23 23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24 24
25 25 # Activate observers that should always be running.
26 26 # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27 27
28 28 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29 29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30 30 config.time_zone = 'UTC'
31 31
32 32 # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33 33 # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34 34 config.i18n.default_locale = :en
35 35
36 36 # Configure the default encoding used in templates for Ruby 1.9.
37 37 config.encoding = "utf-8"
38 38
39 39 # Configure sensitive parameters which will be filtered from the log file.
40 40 config.filter_parameters += [:password]
41 41
42 42 # Enable escaping HTML in JSON.
43 43 config.active_support.escape_html_entities_in_json = true
44 44
45 45 # Use SQL instead of Active Record's schema dumper when creating the database.
46 46 # This is necessary if your schema can't be completely dumped by the schema dumper,
47 47 # like if you have constraints or database-specific column types
48 48 # config.active_record.schema_format = :sql
49 49
50 50 # Enforce whitelist mode for mass assignment.
51 51 # This will create an empty whitelist of attributes available for mass-assignment for all models
52 52 # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
53 53 # parameters by using an attr_accessible or attr_protected declaration.
54 54 config.active_record.whitelist_attributes = false
55 55
56 56 # Enable the asset pipeline
57 57 config.assets.enabled = true
58 58
59 59 # Version of your assets, change this if you want to expire all your assets
60 60 config.assets.version = '1.0'
61 61
62 - config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js','graders.css','problems.css']
62 + config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js']
63 63 config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css']
64 + %w( announcements configurations contests contest_management graders heartbeat
65 + login main messages problems report site sites sources tasks
66 + test user_admin users ).each do |controller|
67 + config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
68 + end
64 69 end
65 70 end
@@ -1,40 +1,40
1 1 CafeGrader::Application.configure do
2 2 # Settings specified here will take precedence over those in config/application.rb
3 3
4 4 # In the development environment your application's code is reloaded on
5 5 # every request. This slows down response time but is perfect for development
6 6 # since you don't have to restart the web server when you make code changes.
7 7 config.cache_classes = false
8 8
9 9 # Log error messages when you accidentally call methods on nil.
10 10 config.whiny_nils = true
11 11
12 12 # Show full error reports and disable caching
13 13 config.consider_all_requests_local = true
14 14 config.action_controller.perform_caching = false
15 15
16 16 # Don't care if the mailer can't send
17 17 config.action_mailer.raise_delivery_errors = false
18 18
19 19 # Print deprecation notices to the Rails logger
20 20 config.active_support.deprecation = :log
21 21
22 22 # Only use best-standards-support built into browsers
23 23 config.action_dispatch.best_standards_support = :builtin
24 24
25 25 # Raise exception on mass assignment protection for Active Record models
26 26 config.active_record.mass_assignment_sanitizer = :strict
27 27
28 28 # Log the query plan for queries taking more than this (works
29 29 # with SQLite, MySQL, and PostgreSQL)
30 30 config.active_record.auto_explain_threshold_in_seconds = 0.5
31 31
32 32 # Do not compress assets
33 33 config.assets.compress = false
34 34
35 35 # Expands the lines which load the assets
36 36 config.assets.debug = true
37 37
38 38 # Prevents assets from rendering twice
39 - config.serve_static_assets = false
39 + config.serve_static_assets = true
40 40 end
@@ -1,70 +1,70
1 1 # Sample localization file for English. Add more files in this directory for other locales.
2 2 # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 3
4 4 en:
5 5 cancel: 'Cancel'
6 6
7 7 login_label: 'Login'
8 8 full_name_label: 'Full name'
9 9 email_label: 'E-mail'
10 10 password_label: 'Password'
11 11
12 12 go_ahead_to: "Go ahead to "
13 13 go_back_to: "Go back to "
14 14 login_page: "login page"
15 15 home_page: "home page"
16 16
17 17 menu:
18 18 main: 'Main'
19 19 messages: 'Messages'
20 20 tasks: 'Tasks'
21 21 submissions: 'Submissions'
22 - test: 'Test Interface'
22 + test: 'Test'
23 23 hall_of_fame: 'Hall of Fame'
24 24 help: 'Help'
25 25 settings: 'Settings'
26 26 log_out: 'Log out'
27 27
28 28 title_bar:
29 29 current_time: "Current time is"
30 30 remaining_time: "Time left: "
31 31 contest_not_started: "The contest has not started."
32 32
33 33 login:
34 34 message: 'Please login to see the problem list'
35 35 login_submit: 'Login'
36 36 participation: 'Want to participate?'
37 37 please: 'Please'
38 38 register: 'register'
39 39 forget_password: 'Forget password?'
40 40
41 41 main:
42 42 start_soon: "The contest at your site will start soon. Please wait."
43 43 specified_in_header: "Specified in header"
44 44
45 45 problem_desc: "desc"
46 46 submitted_at: "Submitted at"
47 47 graded_at: "Graded at"
48 48 score: "score: "
49 49 cmp_msg: "compiler msg"
50 50 src_link: "src"
51 51 submissions_link: "submissions"
52 52
53 53 confirm_contest_start:
54 54 box_title: "Contest confirmation"
55 55 contest_list: "You will participate in contest:"
56 56 timer_starts_after_click: "The timer will start after you click the start button."
57 57 start_button: "Start!"
58 58 start_button_confirm: "Are you sure?"
59 59
60 60 test:
61 61 title: "Test Interface"
62 62 intro: "You can test your submission with your own test data on the grading environment using this test interface."
63 63 disabled_at_end_announcement: "<b>Note:</b> Test interface will be disabled in the last 30 minutes of the contest time on your site."
64 64
65 65 registration:
66 66 title: "New user registration"
67 67
68 68 description: "Please enter your information below. Please make sure your e-mail is correct, because you will have to confirm the registration through an e-mail we send to that e-mail address."
69 69
70 70 successful_title: "Registration successful"
@@ -1,74 +1,63
1 1 CafeGrader::Application.routes.draw do
2 - get "report/login"
2 + get "sources/direct_edit"
3 +
4 + root :to => 'main#login'
3 5
4 6 resources :contests
5 7
6 - resources :announcements
7 8 resources :sites
8 9
10 + resources :announcements do
11 + member do
12 + get 'toggle','toggle_front'
13 + end
14 + end
15 +
16 + resources :problems do
17 + member do
18 + get 'toggle'
19 + get 'toggle_test'
20 + end
21 + collection do
22 + get 'turn_all_off'
23 + get 'turn_all_on'
24 + get 'import'
25 + get 'manage'
26 + end
27 + end
28 +
9 29 resources :grader_configuration, controller: 'configurations'
10 30
11 - # The priority is based upon order of creation:
12 - # first created -> highest priority.
13 -
14 - # Sample of regular route:
15 - # match 'products/:id' => 'catalog#view'
16 - # Keep in mind you can assign values other than :controller and :action
17 -
18 - # Sample of named route:
19 - # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
20 - # This route can be invoked with purchase_url(:id => product.id)
21 -
22 - # Sample resource route (maps HTTP verbs to controller actions automatically):
23 - # resources :products
24 -
25 - # Sample resource route with options:
26 - # resources :products do
27 - # member do
28 - # get 'short'
29 - # post 'toggle'
30 - # end
31 - #
32 - # collection do
33 - # get 'sold'
34 - # end
35 - # end
31 + resources :users do
32 + member do
33 + get 'toggle_activate', 'toggle_enable'
34 + end
35 + end
36 36
37 - # Sample resource route with sub-resources:
38 - # resources :products do
39 - # resources :comments, :sales
40 - # resource :seller
41 - # end
37 + #source code edit
38 + get 'sources/direct_edit/:pid', to: 'sources#direct_edit', as: 'direct_edit'
39 + get 'sources/direct_edit_submission/:sid', to: 'sources#direct_edit_submission', as: 'direct_edit_submission'
40 + get 'sources/get_latest_submission_status/:uid/:pid', to: 'sources#get_latest_submission_status', as: 'get_latest_submission_status'
42 41
43 - # Sample resource route with more complex sub-resources
44 - # resources :products do
45 - # resources :comments
46 - # resources :sales do
47 - # get 'recent', :on => :collection
48 - # end
49 - # end
50 -
51 - # Sample resource route within a namespace:
52 - # namespace :admin do
53 - # # Directs /admin/products/* to Admin::ProductsController
54 - # # (app/controllers/admin/products_controller.rb)
55 - # resources :products
56 - # end
57 -
58 - # You can have the root of your site routed with "root"
59 - # just remember to delete public/index.html.
60 - # root :to => 'welcome#index'
61 -
62 - root :to => 'main#login'
63 42
64 43 match 'tasks/view/:file.:ext' => 'tasks#view'
65 44 match 'tasks/download/:id/:file.:ext' => 'tasks#download'
45 + match 'heartbeat/:id/edit' => 'heartbeat#edit'
66 46
67 - match 'heartbeat/:id/edit' => 'heartbeat#edit'
47 + #main
48 + get "main/list"
49 + get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
50 +
51 + #report
52 + get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
53 + get "report/login"
54 +
55 + #grader
56 + get 'graders/list', to: 'graders#list', as: 'grader_list'
68 57
69 58 # See how all your routes lay out with "rake routes"
70 59
71 60 # This is a legacy wild controller route that's not recommended for RESTful applications.
72 61 # Note: This route will make all actions in every controller accessible via GET requests.
73 62 match ':controller(/:action(/:id))(.:format)'
74 63 end
@@ -1,267 +1,266
1 1 # encoding: UTF-8
2 2 # This file is auto-generated from the current state of the database. Instead
3 3 # of editing this file, please use the migrations feature of Active Record to
4 4 # incrementally modify your database, and then regenerate this schema definition.
5 5 #
6 6 # Note that this schema.rb definition is the authoritative source for your
7 7 # database schema. If you need to create the application database on another
8 8 # system, you should be using db:schema:load, not running all the migrations
9 9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 10 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 11 #
12 12 # It's strongly recommended to check this file into your version control system.
13 13
14 14 ActiveRecord::Schema.define(:version => 20150916054105) do
15 15
16 16 create_table "announcements", :force => true do |t|
17 17 t.string "author"
18 - t.text "body", :limit => 16777215
18 + t.text "body"
19 19 t.boolean "published"
20 - t.datetime "created_at", :null => false
21 - t.datetime "updated_at", :null => false
22 - t.boolean "frontpage", :default => false
23 - t.boolean "contest_only", :default => false
20 + t.datetime "created_at", :null => false
21 + t.datetime "updated_at", :null => false
22 + t.boolean "frontpage", :default => false
23 + t.boolean "contest_only", :default => false
24 24 t.string "title"
25 25 t.string "notes"
26 26 end
27 27
28 28 create_table "contests", :force => true do |t|
29 29 t.string "title"
30 30 t.boolean "enabled"
31 31 t.datetime "created_at", :null => false
32 32 t.datetime "updated_at", :null => false
33 33 t.string "name"
34 34 end
35 35
36 36 create_table "contests_problems", :id => false, :force => true do |t|
37 37 t.integer "contest_id"
38 38 t.integer "problem_id"
39 39 end
40 40
41 41 create_table "contests_users", :id => false, :force => true do |t|
42 42 t.integer "contest_id"
43 43 t.integer "user_id"
44 44 end
45 45
46 46 create_table "countries", :force => true do |t|
47 47 t.string "name"
48 48 t.datetime "created_at", :null => false
49 49 t.datetime "updated_at", :null => false
50 50 end
51 51
52 52 create_table "descriptions", :force => true do |t|
53 - t.text "body", :limit => 16777215
53 + t.text "body"
54 54 t.boolean "markdowned"
55 - t.datetime "created_at", :null => false
56 - t.datetime "updated_at", :null => false
55 + t.datetime "created_at", :null => false
56 + t.datetime "updated_at", :null => false
57 57 end
58 58
59 59 create_table "grader_configurations", :force => true do |t|
60 60 t.string "key"
61 61 t.string "value_type"
62 62 t.string "value"
63 - t.datetime "created_at", :null => false
64 - t.datetime "updated_at", :null => false
65 - t.text "description", :limit => 16777215
63 + t.datetime "created_at", :null => false
64 + t.datetime "updated_at", :null => false
65 + t.text "description"
66 66 end
67 67
68 68 create_table "grader_processes", :force => true do |t|
69 69 t.string "host", :limit => 20
70 70 t.integer "pid"
71 71 t.string "mode"
72 72 t.boolean "active"
73 73 t.datetime "created_at", :null => false
74 74 t.datetime "updated_at", :null => false
75 75 t.integer "task_id"
76 76 t.string "task_type"
77 77 t.boolean "terminated"
78 78 end
79 79
80 80 add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
81 81
82 82 create_table "heart_beats", :force => true do |t|
83 83 t.integer "user_id"
84 84 t.string "ip_address"
85 85 t.datetime "created_at", :null => false
86 86 t.datetime "updated_at", :null => false
87 87 t.string "status"
88 88 end
89 89
90 90 add_index "heart_beats", ["updated_at"], :name => "index_heart_beats_on_updated_at"
91 91
92 92 create_table "languages", :force => true do |t|
93 93 t.string "name", :limit => 10
94 94 t.string "pretty_name"
95 95 t.string "ext", :limit => 10
96 96 t.string "common_ext"
97 97 end
98 98
99 99 create_table "logins", :force => true do |t|
100 100 t.integer "user_id"
101 101 t.string "ip_address"
102 102 t.datetime "created_at", :null => false
103 103 t.datetime "updated_at", :null => false
104 104 end
105 105
106 106 create_table "messages", :force => true do |t|
107 107 t.integer "sender_id"
108 108 t.integer "receiver_id"
109 109 t.integer "replying_message_id"
110 - t.text "body", :limit => 16777215
110 + t.text "body"
111 111 t.boolean "replied"
112 - t.datetime "created_at", :null => false
113 - t.datetime "updated_at", :null => false
112 + t.datetime "created_at", :null => false
113 + t.datetime "updated_at", :null => false
114 114 end
115 115
116 116 create_table "problems", :force => true do |t|
117 117 t.string "name", :limit => 30
118 118 t.string "full_name"
119 119 t.integer "full_score"
120 120 t.date "date_added"
121 121 t.boolean "available"
122 122 t.string "url"
123 123 t.integer "description_id"
124 124 t.boolean "test_allowed"
125 125 t.boolean "output_only"
126 126 t.string "description_filename"
127 127 end
128 128
129 129 create_table "rights", :force => true do |t|
130 130 t.string "name"
131 131 t.string "controller"
132 132 t.string "action"
133 133 end
134 134
135 135 create_table "rights_roles", :id => false, :force => true do |t|
136 136 t.integer "right_id"
137 137 t.integer "role_id"
138 138 end
139 139
140 140 add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
141 141
142 142 create_table "roles", :force => true do |t|
143 143 t.string "name"
144 144 end
145 145
146 146 create_table "roles_users", :id => false, :force => true do |t|
147 147 t.integer "role_id"
148 148 t.integer "user_id"
149 149 end
150 150
151 151 add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
152 152
153 153 create_table "sessions", :force => true do |t|
154 154 t.string "session_id"
155 - t.text "data", :limit => 16777215
155 + t.text "data"
156 156 t.datetime "updated_at"
157 157 end
158 158
159 159 add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
160 160 add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
161 161
162 162 create_table "sites", :force => true do |t|
163 163 t.string "name"
164 164 t.boolean "started"
165 165 t.datetime "start_time"
166 166 t.datetime "created_at", :null => false
167 167 t.datetime "updated_at", :null => false
168 168 t.integer "country_id"
169 169 t.string "password"
170 170 end
171 171
172 172 create_table "submission_view_logs", :force => true do |t|
173 173 t.integer "user_id"
174 174 t.integer "submission_id"
175 175 t.datetime "created_at", :null => false
176 176 t.datetime "updated_at", :null => false
177 177 end
178 178
179 179 create_table "submissions", :force => true do |t|
180 180 t.integer "user_id"
181 181 t.integer "problem_id"
182 182 t.integer "language_id"
183 - t.text "source", :limit => 16777215
183 + t.text "source"
184 184 t.binary "binary"
185 185 t.datetime "submitted_at"
186 186 t.datetime "compiled_at"
187 - t.text "compiler_message", :limit => 16777215
187 + t.text "compiler_message"
188 188 t.datetime "graded_at"
189 189 t.integer "points"
190 - t.text "grader_comment", :limit => 16777215
190 + t.text "grader_comment"
191 191 t.integer "number"
192 192 t.string "source_filename"
193 193 t.float "max_runtime"
194 194 t.integer "peak_memory"
195 195 t.integer "effective_code_length"
196 196 t.string "ip_address"
197 197 end
198 198
199 199 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
200 200 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
201 201
202 202 create_table "tasks", :force => true do |t|
203 203 t.integer "submission_id"
204 204 t.datetime "created_at"
205 205 t.integer "status"
206 206 t.datetime "updated_at"
207 207 end
208 208
209 209 create_table "test_pairs", :force => true do |t|
210 210 t.integer "problem_id"
211 - t.text "input", :limit => 2147483647
212 - t.text "solution", :limit => 2147483647
213 - t.datetime "created_at", :null => false
214 - t.datetime "updated_at", :null => false
211 + t.text "input", :limit => 16777215
212 + t.text "solution", :limit => 16777215
213 + t.datetime "created_at", :null => false
214 + t.datetime "updated_at", :null => false
215 215 end
216 216
217 217 create_table "test_requests", :force => true do |t|
218 218 t.integer "user_id"
219 219 t.integer "problem_id"
220 220 t.integer "submission_id"
221 221 t.string "input_file_name"
222 222 t.string "output_file_name"
223 223 t.string "running_stat"
224 224 t.integer "status"
225 - t.datetime "updated_at", :null => false
225 + t.datetime "updated_at", :null => false
226 226 t.datetime "submitted_at"
227 227 t.datetime "compiled_at"
228 - t.text "compiler_message", :limit => 16777215
228 + t.text "compiler_message"
229 229 t.datetime "graded_at"
230 230 t.string "grader_comment"
231 - t.datetime "created_at", :null => false
231 + t.datetime "created_at", :null => false
232 232 t.float "running_time"
233 233 t.string "exit_status"
234 234 t.integer "memory_usage"
235 235 end
236 236
237 237 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
238 238
239 239 create_table "user_contest_stats", :force => true do |t|
240 240 t.integer "user_id"
241 241 t.datetime "started_at"
242 242 t.datetime "created_at", :null => false
243 243 t.datetime "updated_at", :null => false
244 244 t.boolean "forced_logout"
245 245 end
246 246
247 247 create_table "users", :force => true do |t|
248 248 t.string "login", :limit => 50
249 249 t.string "full_name"
250 250 t.string "hashed_password"
251 251 t.string "salt", :limit => 5
252 252 t.string "alias"
253 253 t.string "email"
254 254 t.integer "site_id"
255 255 t.integer "country_id"
256 256 t.boolean "activated", :default => false
257 257 t.datetime "created_at"
258 258 t.datetime "updated_at"
259 - t.string "section"
260 259 t.boolean "enabled", :default => true
261 260 t.string "remark"
262 261 t.string "last_ip"
263 262 end
264 263
265 264 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
266 265
267 266 end
deleted file
deleted file
deleted file
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