# HG changeset patch # User Nattee Niparnan # Date 2016-08-09 11:44:41 # Node ID 4afa0e9c66d262d4965522765b7d93ade1905e59 # Parent 6d57d5eff450de0b2e1a5abafd88d6ecd13d353a # Parent 8d31c8c43cb3bd93e81185a2a152b649071d954c merge bootstrap back to algo diff --git a/Gemfile b/Gemfile --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,8 @@ gem 'rails', '3.2.21' +gem 'select2-rails' + # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' @@ -45,11 +47,26 @@ gem 'jquery-ui-sass-rails' gem 'jquery-timepicker-addon-rails' gem 'jquery-tablesorter' +gem 'jquery-countdown-rails' #syntax highlighter gem 'rouge' +#add bootstrap +gem 'bootstrap-sass', '~> 3.2.0' +gem 'bootstrap-switch-rails' +gem 'bootstrap-toggle-rails' +gem 'autoprefixer-rails' + +#bootstrap sortable +gem 'momentjs-rails' +gem 'rails_bootstrap_sortable' + +#ace editor +gem 'ace-rails-ap' + gem 'haml' +gem 'haml-rails' gem 'mail' gem 'rdiscount' gem 'test-unit' diff --git a/Gemfile.lock b/Gemfile.lock --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,6 +9,7 @@ GEM remote: https://rubygems.org/ specs: + ace-rails-ap (4.0.2) actionmailer (3.2.21) actionpack (= 3.2.21) mail (~> 2.5.4) @@ -37,9 +38,16 @@ i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) arel (3.0.3) + autoprefixer-rails (6.0.3) + execjs + json best_in_place (3.0.3) actionpack (>= 3.2) railties (>= 3.2) + bootstrap-sass (3.2.0.2) + sass (~> 3.2) + bootstrap-switch-rails (3.3.3) + bootstrap-toggle-rails (2.2.1.0) builder (3.0.4) coffee-rails (3.2.2) coffee-script (>= 2.2.0) @@ -54,10 +62,16 @@ execjs (2.3.0) haml (4.0.6) tilt + haml-rails (0.4) + actionpack (>= 3.1, < 4.1) + activesupport (>= 3.1, < 4.1) + haml (>= 3.1, < 4.1) + railties (>= 3.1, < 4.1) hike (1.2.3) i18n (0.7.0) in_place_editing (1.2.0) journey (1.0.4) + jquery-countdown-rails (2.0.2) jquery-rails (3.1.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) @@ -77,6 +91,8 @@ mime-types (~> 1.16) treetop (~> 1.4.8) mime-types (1.25.1) + momentjs-rails (2.11.1) + railties (>= 3.1) multi_json (1.10.1) mysql2 (0.3.20) polyglot (0.3.5) @@ -98,6 +114,8 @@ activesupport (= 3.2.21) bundler (~> 1.0) railties (= 3.2.21) + rails_bootstrap_sortable (2.0.0) + momentjs-rails (~> 2, >= 2.8.3) railties (3.2.21) actionpack (= 3.2.21) activesupport (= 3.2.21) @@ -130,6 +148,8 @@ railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) + select2-rails (4.0.1) + thor (~> 0.14) sprockets (2.2.3) hike (~> 1.2) multi_json (~> 1.0) @@ -152,23 +172,33 @@ ruby DEPENDENCIES + ace-rails-ap + autoprefixer-rails best_in_place (~> 3.0.1) + bootstrap-sass (~> 3.2.0) + bootstrap-switch-rails + bootstrap-toggle-rails coffee-rails (~> 3.2.2) dynamic_form haml + haml-rails in_place_editing + jquery-countdown-rails jquery-rails jquery-tablesorter jquery-timepicker-addon-rails jquery-ui-sass-rails mail + momentjs-rails mysql2 prototype-rails rails (= 3.2.21) + rails_bootstrap_sortable rdiscount rouge rspec-rails (~> 2.99.0) sass-rails (~> 3.2.6) + select2-rails test-unit uglifier verification! diff --git a/app/assets/javascripts/announcements.js.coffee b/app/assets/javascripts/announcements.js.coffee new file mode 100644 --- /dev/null +++ b/app/assets/javascripts/announcements.js.coffee @@ -0,0 +1,6 @@ +#js for announcement +$ -> + $(document).ajaxError (event, jqxhr, settings, exception) -> + if jqxhr.status + alert 'We\'re sorry, but something went wrong (' + jqxhr.status + ')' + return diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,8 +10,35 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // -//= require prototype -//= require prototype_ujs -//= require effects -//= require dragdrop -//= require controls +//= require jquery +//= require jquery_ujs +//= require jquery.ui.all +//= require bootstrap-sprockets +//= require moment +//= require bootstrap-sortable +//= require select2 +//= require ace-rails-ap +//= require ace/mode-c_cpp +//= require ace/mode-python +//= require ace/mode-ruby +//= require ace/mode-pascal +//= require ace/mode-javascript +//= require ace/mode-java +//= require ace/theme-merbivore +//= require custom +//= require jquery.countdown +//-------------- addition from local_jquery ----------- +//= require jquery.ui.datepicker +//= require jquery.ui.slider +//= require jquery-ui-timepicker-addon +//= require jquery-tablesorter +//= require best_in_place +//= require best_in_place.jquery-ui +//= require brython + +// since this is after blank line, it is not downloaded +//x= require prototype +//x= require prototype_ujs +//x= require effects +//x= require dragdrop +//x= require controls diff --git a/app/assets/javascripts/configurations.js.coffee b/app/assets/javascripts/configurations.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/contest_management.js.coffee b/app/assets/javascripts/contest_management.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/contests.js.coffee b/app/assets/javascripts/contests.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/custom.js.coffee b/app/assets/javascripts/custom.js.coffee new file mode 100644 --- /dev/null +++ b/app/assets/javascripts/custom.js.coffee @@ -0,0 +1,52 @@ +$(document).on 'change', '.btn-file :file', -> + input = $(this) + numFiles = if input.get(0).files then input.get(0).files.length else 1 + label = input.val().replace(/\\/g, '/').replace(/.*\//, '') + input.trigger 'fileselect', [ + numFiles + label + ] + return + + +# document ready + +$ -> + $(".select2").select2() + #$(".bootstrap-switch").bootstrapSwitch() + #$(".bootstrap-toggle").bootstrapToggle() + $('.btn-file :file').on 'fileselect', (event, numFiles, label) -> + input = $(this).parents('.input-group').find(':text') + log = if numFiles > 1 then numFiles + ' files selected' else label + if input.length + input.val log + else + if log + alert log + return + $(".go-button").on 'click', (event) -> + link = $(this).attr("data-source") + url = $(link).val() + if url + window.location.href = url + return + $('.ajax-toggle').on 'click', (event) -> + target = $(event.target) + target.removeClass 'btn-default' + target.removeClass 'btn-success' + target.addClass 'btn-warning' + target.text '...' + return + + + #ace editor + if $("#editor").length > 0 + e = ace.edit("editor") + e.setTheme('ace/theme/merbivore') + e.getSession().setTabSize(2) + e.getSession().setUseSoftTabs(true) + + #best in place + jQuery(".best_in_place").best_in_place() + + return diff --git a/app/assets/javascripts/graders.js.coffee b/app/assets/javascripts/graders.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/heartbeat.js.coffee b/app/assets/javascripts/heartbeat.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/login.js.coffee b/app/assets/javascripts/login.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/messages.js.coffee b/app/assets/javascripts/messages.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/problems.js.coffee b/app/assets/javascripts/problems.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/report.js.coffee b/app/assets/javascripts/report.js.coffee --- a/app/assets/javascripts/report.js.coffee +++ b/app/assets/javascripts/report.js.coffee @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/site.js.coffee b/app/assets/javascripts/site.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/sites.js.coffee b/app/assets/javascripts/sites.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/sources.js.coffee b/app/assets/javascripts/sources.js.coffee new file mode 100644 --- /dev/null +++ b/app/assets/javascripts/sources.js.coffee @@ -0,0 +1,32 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ + + +$ -> + $("#live_submit").on "click", (event) -> + h = $("#editor_text") + e = ace.edit("editor") + h.val(e.getValue()) + + $("#language_id").on "change", (event) -> + text = $("#language_id option:selected").text() + mode = 'ace/mode/c_cpp' + switch text + when 'Pascal' then mode = 'ace/mode/pascal' + when 'C++','C' then mode = 'ace/mode/c_cpp' + when 'Ruby' then mode = 'ace/mode/ruby' + when 'Python' then mode = 'ace/mode/python' + when 'Java' then mode = 'ace/mode/java' + editor = ace.edit('editor') + editor.getSession().setMode(mode) + + e = ace.edit("editor") + e.setValue($("#text_haha").val()) + e.gotoLine(1) + $("#language_id").trigger('change') + + + + + return diff --git a/app/assets/javascripts/tasks.js.coffee b/app/assets/javascripts/tasks.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/test.js.coffee b/app/assets/javascripts/test.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/user_admin.js.coffee b/app/assets/javascripts/user_admin.js.coffee new file mode 100644 diff --git a/app/assets/javascripts/users.js.coffee b/app/assets/javascripts/users.js.coffee new file mode 100644 diff --git a/app/assets/stylesheets/announcements.css.scss b/app/assets/stylesheets/announcements.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/application.css.sass b/app/assets/stylesheets/application.css.sass --- a/app/assets/stylesheets/application.css.sass +++ b/app/assets/stylesheets/application.css.sass @@ -1,19 +1,150 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any styles + * defined in the other CSS/SCSS files in this directory. It is generally better to create a new + * file per style scope. + * + // bootstrap says that we should not do this, but @import each file instead + # *= require_tree . + # *= require_self + */ +@import jquery.ui.all @import jquery.ui.core @import jquery.ui.theme @import jquery.ui.datepicker @import jquery.ui.slider @import jquery-ui-timepicker-addon @import jquery-tablesorter/theme.metro-dark +@import jquery.countdown @import tablesorter-theme.cafe +//bootstrap +@import bootstrap-sprockets +@import bootstrap +@import select2 +@import select2-bootstrap +//@import bootstrap3-switch +@import bootstrap-toggle +@import bootstrap-sortable + +//bootstrap navbar color (from) +$bgDefault : #19197b +$bgHighlight : #06064b +$colDefault : #8e8eb4 +$colHighlight : #ffffff +$dropDown : false +.navbar-default + background-color: $bgDefault + border-color: $bgHighlight + .navbar-brand + color: $colDefault + &:hover, &:focus + color: $colHighlight + .navbar-text + color: $colDefault + .navbar-nav + > li + > a + color: $colDefault + &:hover, &:focus + color: $colHighlight + @if $dropDown + > .dropdown-menu + background-color: $bgDefault + > li + > a + color: $colDefault + &:hover, &:focus + color: $colHighlight + background-color: $bgHighlight + > .divider + background-color: $bgHighlight + @if $dropDown + .open .dropdown-menu > .active + > a, > a:hover, > a:focus + color: $colHighlight + background-color: $bgHighlight + > .active + > a, > a:hover, > a:focus + color: $colHighlight + background-color: $bgHighlight + > .open + > a, > a:hover, > a:focus + color: $colHighlight + background-color: $bgHighlight + .navbar-toggle + border-color: $bgHighlight + &:hover, &:focus + background-color: $bgHighlight + .icon-bar + background-color: $colDefault + .navbar-collapse, + .navbar-form + border-color: $colDefault + .navbar-link + color: $colDefault + &:hover + color: $colHighlight +@media (max-width: 767px) + .navbar-default .navbar-nav .open .dropdown-menu + > li > a + color: $colDefault + &:hover, &:focus + color: $colHighlight + > .active + > a, > a:hover, > a:focus + color: $colHighlight + background-color: $bgHighlight + +.secondnavbar + top: 50px + + +// --------------- bootstrap file upload ---------------------- +.btn-file + position: relative + overflow: hidden + +.btn-file input[type=file] + position: absolute + top: 0 + right: 0 + min-width: 100% + min-height: 100% + font-size: 100px + text-align: right + filter: alpha(opacity=0) + opacity: 0 + outline: none + background: white + cursor: inherit + display: block + body background: white image-url("topbg.jpg") repeat-x top center - font-size: 13px - font-family: Tahoma, "sans-serif" + //font-size: 13px + //font-family: Tahoma, "sans-serif" margin: 10px padding: 10px + padding-top: 60px +// ------------------ bootstrap sortable -------------------- +table.sortable th + padding-right: 20px !important + span.sign + right: -15px !important + &.text-right + padding-left: 20px !important + padding-right: 8px !important + &:after, span.sign + left: -15px !important input font-family: Tahoma, "sans-serif" @@ -36,17 +167,17 @@ border-bottom: 1px solid #eeeeee -a - color: #6666cc - text-decoration: none - - &:link, &:visited - color: #6666cc - text-decoration: none - - &:hover, &:focus - color: #111166 - text-decoration: none +//#a +// color: #6666cc +// text-decoration: none +// +// &:link, &:visited +// color: #6666cc +// text-decoration: none +// +// &:hover, &:focus +// color: #111166 +// text-decoration: none div diff --git a/app/assets/stylesheets/configurations.css.scss b/app/assets/stylesheets/configurations.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/contest_management.css.scss b/app/assets/stylesheets/contest_management.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/contests.css.scss b/app/assets/stylesheets/contests.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/graders.css.scss b/app/assets/stylesheets/graders.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/heartbeat.css.scss b/app/assets/stylesheets/heartbeat.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/login.css.scss b/app/assets/stylesheets/login.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/main.css.scss b/app/assets/stylesheets/main.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/messages.css.scss b/app/assets/stylesheets/messages.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/problems.css.scss b/app/assets/stylesheets/problems.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/site.css.scss b/app/assets/stylesheets/site.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/sites.css.scss b/app/assets/stylesheets/sites.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/sources.css.scss b/app/assets/stylesheets/sources.css.scss new file mode 100644 --- /dev/null +++ b/app/assets/stylesheets/sources.css.scss @@ -0,0 +1,4 @@ +// Place all the styles related to the sources controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ + diff --git a/app/assets/stylesheets/tablesorter-theme.cafe.css b/app/assets/stylesheets/tablesorter-theme.cafe.css --- a/app/assets/stylesheets/tablesorter-theme.cafe.css +++ b/app/assets/stylesheets/tablesorter-theme.cafe.css @@ -3,7 +3,7 @@ *************/ /* overall */ .tablesorter-cafe { - // font: 12px/18px 'Segoe UI Semilight', 'Open Sans', Verdana, Arial, Helvetica, sans-serif; + /* font: 12px/18px 'Segoe UI Semilight', 'Open Sans', Verdana, Arial, Helvetica, sans-serif; */ color: #000; background-color: #777; margin: 10px 0 15px; @@ -25,8 +25,10 @@ .tablesorter-cafe thead td, .tablesorter-cafe tfoot th, .tablesorter-cafe tfoot td { + /* //font-weight: 300; //font-size: 15px; + */ color: #fff; background-color: #777; padding: 2px; diff --git a/app/assets/stylesheets/tasks.css.scss b/app/assets/stylesheets/tasks.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/test.css.scss b/app/assets/stylesheets/test.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/user_admin.css.scss b/app/assets/stylesheets/user_admin.css.scss new file mode 100644 diff --git a/app/assets/stylesheets/users.css.scss b/app/assets/stylesheets/users.css.scss new file mode 100644 diff --git a/app/controllers/announcements_controller.rb b/app/controllers/announcements_controller.rb --- a/app/controllers/announcements_controller.rb +++ b/app/controllers/announcements_controller.rb @@ -69,14 +69,34 @@ if @announcement.update_attributes(params[:announcement]) flash[:notice] = 'Announcement was successfully updated.' format.html { redirect_to(@announcement) } + format.js {} format.xml { head :ok } else format.html { render :action => "edit" } + format.js {} format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity } end end end + def toggle + @announcement = Announcement.find(params[:id]) + @announcement.update_attributes( published: !@announcement.published? ) + respond_to do |format| + format.js { render partial: 'toggle_button', + locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } } + end + end + + def toggle_front + @announcement = Announcement.find(params[:id]) + @announcement.update_attributes( frontpage: !@announcement.frontpage? ) + respond_to do |format| + format.js { render partial: 'toggle_button', + locals: {button_id: "#announcement_toggle_front_#{@announcement.id}",button_on: @announcement.frontpage? } } + end + end + # DELETE /announcements/1 # DELETE /announcements/1.xml def destroy diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,17 @@ class ApplicationController < ActionController::Base protect_from_forgery + before_filter :current_user + SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode' MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login' + # Returns the current logged-in user (if any). + def current_user + return nil unless session[:user_id] + @current_user ||= User.find(session[:user_id]) + end + def admin_authorization return false unless authenticate user = User.find(session[:user_id], :include => ['roles']) @@ -39,12 +47,17 @@ # check if run in single user mode if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY] - user = User.find(session[:user_id]) + user = User.find_by_id(session[:user_id]) if user==nil or (not user.admin?) flash[:notice] = 'You cannot log in at this time' redirect_to :controller => 'main', :action => 'login' return false end + unless user.enabled? + flash[:notice] = 'Your account is disabled' + redirect_to :controller => 'main', :action => 'login' + return false + end return true end diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -69,6 +69,14 @@ @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '') @submission.source_filename = params['file'].original_filename end + + if (params[:editor_text]) + language = Language.find_by_id(params[:language_id]) + @submission.source = params[:editor_text] + @submission.source_filename = "live_edit.#{language.ext}" + @submission.language = language + end + @submission.submitted_at = Time.new.gmtime @submission.ip_address = request.remote_ip @@ -123,8 +131,8 @@ @problem = nil @submissions = nil else - @problem = Problem.find_by_name(params[:id]) - if not @problem.available + @problem = Problem.find_by_id(params[:id]) + if (@problem == nil) or (not @problem.available) redirect_to :action => 'list' flash[:notice] = 'Error: submissions for that problem are not viewable.' return diff --git a/app/controllers/problems_controller.rb b/app/controllers/problems_controller.rb --- a/app/controllers/problems_controller.rb +++ b/app/controllers/problems_controller.rb @@ -7,8 +7,7 @@ in_place_edit_for :problem, :full_score def index - list - render :action => 'list' + @problems = Problem.find(:all, :order => 'date_added DESC') end # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) @@ -17,11 +16,7 @@ :do_manage, :do_import, :update ], - :redirect_to => { :action => :list } - - def list - @problems = Problem.find(:all, :order => 'date_added DESC') - end + :redirect_to => { :action => :index } def show @problem = Problem.find(params[:id]) @@ -45,7 +40,7 @@ @problem.description = @description if @problem.save flash[:notice] = 'Problem was successfully created.' - redirect_to :action => 'list' + redirect_to action: :index else render :action => 'new' end @@ -61,10 +56,10 @@ @problem.date_added = Time.new if @problem.save flash[:notice] = 'Problem was successfully created.' - redirect_to :action => 'list' + redirect_to action: :index else flash[:notice] = 'Error saving problem' - redirect_to :action => 'list' + redirect_to action: :index end end @@ -121,13 +116,23 @@ def destroy Problem.find(params[:id]).destroy - redirect_to :action => 'list' + redirect_to action: :index end def toggle @problem = Problem.find(params[:id]) - @problem.available = !(@problem.available) - @problem.save + @problem.update_attributes(available: !(@problem.available) ) + respond_to do |format| + format.js { } + end + end + + def toggle_test + @problem = Problem.find(params[:id]) + @problem.update_attributes(test_allowed: !(@problem.test_allowed?) ) + respond_to do |format| + format.js { } + end end def turn_all_off @@ -136,7 +141,7 @@ problem.available = false problem.save end - redirect_to :action => 'list' + redirect_to action: :index end def turn_all_on @@ -145,7 +150,7 @@ problem.available = true problem.save end - redirect_to :action => 'list' + redirect_to action: :index end def stat diff --git a/app/controllers/report_controller.rb b/app/controllers/report_controller.rb --- a/app/controllers/report_controller.rb +++ b/app/controllers/report_controller.rb @@ -12,6 +12,36 @@ admin_authorization } + def score + if params[:commit] == 'download csv' + @problems = Problem.all + else + @problems = Problem.find_available_problems + end + @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true) + @scorearray = Array.new + @users.each do |u| + ustat = Array.new + ustat[0] = u + @problems.each do |p| + sub = Submission.find_last_by_user_and_problem(u.id,p.id) + if (sub!=nil) and (sub.points!=nil) and p and p.full_score + ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)] + else + ustat << [0,false] + end + end + @scorearray << ustat + end + if params[:commit] == 'download csv' then + csv = gen_csv_from_scorearray(@scorearray,@problems) + send_data csv, filename: 'last_score.csv' + else + render template: 'user_admin/user_stat' + end + + end + def login_stat @logins = Array.new @@ -73,12 +103,7 @@ Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s| if @submissions[s.user_id] if not @submissions[s.user_id][:sub].has_key?(s.problem_id) - a = nil - begin - a = Problem.find(s.problem_id) - rescue - a = nil - end + a = Problem.find_by_id(s.problem_id) @submissions[s.user_id][:sub][s.problem_id] = { prob_name: (a ? a.full_name : '(NULL)'), sub_ids: [s.id] } diff --git a/app/controllers/sources_controller.rb b/app/controllers/sources_controller.rb new file mode 100644 --- /dev/null +++ b/app/controllers/sources_controller.rb @@ -0,0 +1,27 @@ +class SourcesController < ApplicationController + before_filter :authenticate + + def direct_edit + @problem = Problem.find(params[:pid]) + @source = '' + end + + def direct_edit_submission + @submission = Submission.find(params[:sid]) + @source = @submission.source.to_s + @problem = @submission.problem + @lang_id = @submission.language.id + render 'direct_edit' + end + + def get_latest_submission_status + @problem = Problem.find(params[:pid]) + @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid]) + puts User.find(params[:uid]).login + puts Problem.find(params[:pid]).name + puts 'nil' unless @submission + respond_to do |format| + format.js + end + end +end diff --git a/app/controllers/user_admin_controller.rb b/app/controllers/user_admin_controller.rb --- a/app/controllers/user_admin_controller.rb +++ b/app/controllers/user_admin_controller.rb @@ -17,7 +17,6 @@ def index list - render :action => 'list' end def list @@ -56,7 +55,7 @@ @user.activated = true if @user.save flash[:notice] = 'User was successfully created.' - redirect_to :action => 'list' + redirect_to :action => 'index' else render :action => 'new' end @@ -66,7 +65,7 @@ @user = User.find(params[:id]) @user.last_ip = nil @user.save - redirect_to action: 'list', page: params[:page] + redirect_to action: 'index', page: params[:page] end def create_from_list @@ -114,7 +113,7 @@ flash[:notice] = 'User(s) ' + note.join(', ') + ' were successfully created. ' + '( (+) - created with random passwords.)' - redirect_to :action => 'list' + redirect_to :action => 'index' end def edit @@ -133,7 +132,7 @@ def destroy User.find(params[:id]).destroy - redirect_to :action => 'list' + redirect_to :action => 'index' end def user_stat @@ -200,7 +199,7 @@ def import if params[:file]=='' flash[:notice] = 'Error importing no file' - redirect_to :action => 'list' and return + redirect_to :action => 'index' and return end import_from_file(params[:file]) end @@ -253,7 +252,7 @@ if user and contest user.contests << contest end - redirect_to :action => 'list' + redirect_to :action => 'index' end def remove_from_contest @@ -262,7 +261,7 @@ if user and contest user.contests.delete(contest) end - redirect_to :action => 'list' + redirect_to :action => 'index' end def contest_management diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -133,6 +133,24 @@ problem.each_value { |v| @summary[:solve] += 1 if v == 1 } end + def toggle_activate + @user = User.find(params[:id]) + @user.update_attributes( activated: !@user.activated? ) + respond_to do |format| + format.js { render partial: 'toggle_button', + locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } } + end + end + + def toggle_enable + @user = User.find(params[:id]) + @user.update_attributes( enabled: !@user.enabled? ) + respond_to do |format| + format.js { render partial: 'toggle_button', + locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } } + end + end + protected def verify_online_registration @@ -192,4 +210,5 @@ admin_authorization end + end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,6 +1,38 @@ # Methods added to this helper will be available to all templates in the application. module ApplicationHelper + def navbar_user_header + left_menu = '' + right_menu = '' + user = User.find(session[:user_id]) + + if (user!=nil) and (GraderConfiguration.show_tasks_to?(user)) + left_menu << add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list') + left_menu << add_menu("#{I18n.t 'menu.submissions'}", 'main', 'submission') + left_menu << add_menu("#{I18n.t 'menu.test'}", 'test', 'index') + end + + if GraderConfiguration['right.user_hall_of_fame'] + left_menu << add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof') + end + + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help') + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}}) + if GraderConfiguration['system.user_setting_enabled'] + right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}}) + end + 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'}}) + + + 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') + end + + def add_menu(title, controller, action,html_option = {}) + link_option = {controller: controller, action: action} + html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option) + content_tag(:li, link_to(title,link_option),html_option) + end + def user_header menu_items = '' user = User.find(session[:user_id]) @@ -75,6 +107,34 @@ end end + def toggle_button(on,toggle_url,id, option={}) + btn_size = option[:size] || 'btn-xs' + link_to (on ? "Yes" : "No"), toggle_url, + {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle", + id: id, + data: {remote: true, method: 'get'}} + end + + def get_ace_mode(language) + # return ace mode string from Language + + case language.pretty_name + when 'Pascal' + 'ace/mode/pascal' + when 'C++','C' + 'ace/mode/c_cpp' + when 'Ruby' + 'ace/mode/ruby' + when 'Python' + 'ace/mode/python' + when 'Java' + 'ace/mode/java' + else + 'ace/mode/c_cpp' + end + end + + def user_title_bar(user) header = '' time_left = '' diff --git a/app/helpers/sources_helper.rb b/app/helpers/sources_helper.rb new file mode 100644 --- /dev/null +++ b/app/helpers/sources_helper.rb @@ -0,0 +1,2 @@ +module SourcesHelper +end diff --git a/app/models/problem.rb b/app/models/problem.rb --- a/app/models/problem.rb +++ b/app/models/problem.rb @@ -61,6 +61,10 @@ result[:total_sub] = Submission.where(problem_id: self.id).count result[:attempted_user] = Submission.where(problem_id: self.id).group_by(:user_id) end + + def long_name + "[#{name}] #{full_name}" + end protected diff --git a/app/models/user.rb b/app/models/user.rb --- a/app/models/user.rb +++ b/app/models/user.rb @@ -239,7 +239,7 @@ def update_start_time stat = self.contest_stat - if stat == nil or stat.started_at == nil + if (stat.nil?) or (stat.started_at.nil?) stat ||= UserContestStat.new(:user => self) stat.started_at = Time.now.gmtime stat.save diff --git a/app/views/announcements/index.html.erb b/app/views/announcements/index.html.erb deleted file mode 100644 --- a/app/views/announcements/index.html.erb +++ /dev/null @@ -1,40 +0,0 @@ -

Listing announcements

- -<%= link_to 'New announcement', new_announcement_path %> - - - - - - - - - - - - -<% for announcement in @announcements %> - - <% @announcement = announcement %> - - - - - - - - -<% end %> -
UpdatedAnnouncementAuthorPublished
<%= time_ago_in_words announcement.updated_at %> - <% if !announcement.title.blank? %> - Title: <%=h announcement.title %>
- <% end %> - <% if !announcement.notes.blank? %> - Notes: <%=h announcement.notes %>
- <% end %> - <%=h announcement.body %> -
<%=h announcement.author %><%= in_place_editor_field :announcement, :published, {}, :rows => 1 %><%= link_to 'Show', announcement %><%= link_to 'Edit', edit_announcement_path(announcement) %><%= link_to 'Destroy', announcement, :confirm => 'Are you sure?', :method => :delete %>
- -
- -<%= link_to 'New announcement', new_announcement_path %> diff --git a/app/views/announcements/index.html.haml b/app/views/announcements/index.html.haml new file mode 100644 --- /dev/null +++ b/app/views/announcements/index.html.haml @@ -0,0 +1,37 @@ +%h1 Listing announcements + += link_to '+ Add announcement', new_announcement_path, class: 'btn btn-success' +%br +%br + +%table.table.table-striped + %thead + %th Updated + %th Announcement + %th Author + %th{style: 'width: 100px'} Published? + %th{style: 'width: 100px'}Front? + %th + %th + - for announcement in @announcements + %tr + - @announcement = announcement + %td= time_ago_in_words announcement.updated_at + %td + - if !announcement.title.blank? + %b Title: + = h announcement.title + %br/ + - if !announcement.notes.blank? + %b + Notes: #{h announcement.notes} + %br/ + = h announcement.body + %td= h announcement.author + %td= toggle_button(announcement.published?, toggle_announcement_url(@announcement), "announcement_toggle_#{@announcement.id}", {size: 'btn-sm'}) + %td= toggle_button(announcement.frontpage?, toggle_front_announcement_url(@announcement), "announcement_toggle_front_#{@announcement.id}", {size: 'btn-sm'}) + %td= link_to 'Edit', edit_announcement_path(announcement), class: 'btn btn-block btn-sm btn-info' + %td= link_to 'Destroy', announcement, :confirm => 'Are you sure?', :method => :delete, class: "btn btn-block btn-sm btn-danger" +%br + += link_to '+ Add announcement', new_announcement_path, class: 'btn btn-success' diff --git a/app/views/application/_submission_short.html.haml b/app/views/application/_submission_short.html.haml new file mode 100644 --- /dev/null +++ b/app/views/application/_submission_short.html.haml @@ -0,0 +1,26 @@ + +- if submission.nil? + = "-" +- else + - if submission.graded_at.nil? + =t 'main.submitted_at' + = format_short_time(submission.submitted_at.localtime) + - else + = t 'main.graded_at' + = "#{format_short_time(submission.graded_at.localtime)}, " + - if GraderConfiguration['ui.show_score'] + = t 'main.score' + = "#{(submission.points*100/submission.problem.full_score).to_i} " + = " [" + %tt + = submission.grader_comment + = "]" + - if GraderConfiguration.show_grading_result + = " | " + = link_to '[detailed result]', :action => 'result', :id => submission.id + = " | " + = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true}) + = " | " + = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id}) + //= " | " + //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id) diff --git a/app/views/application/_toggle_button.js.haml b/app/views/application/_toggle_button.js.haml new file mode 100644 --- /dev/null +++ b/app/views/application/_toggle_button.js.haml @@ -0,0 +1,7 @@ +:plain + var t = $("#{button_id}"); + t.removeClass('btn-default'); + t.removeClass('btn-success'); + t.removeClass('btn-warning'); + t.addClass("btn-#{button_on ? 'success' : 'default'}"); + t.text("#{button_on ? 'Yes' : 'No'}"); diff --git a/app/views/configurations/index.html.haml b/app/views/configurations/index.html.haml --- a/app/views/configurations/index.html.haml +++ b/app/views/configurations/index.html.haml @@ -1,5 +1,5 @@ -- content_for :header do - = javascript_include_tag 'local_jquery' +/- content_for :header do +/ = javascript_include_tag 'local_jquery' %h1 System configuration @@ -13,9 +13,11 @@ - @grader_configuration = conf %tr{:class => cycle("info-odd", "info-even")} %td - = in_place_editor_field :grader_configuration, :key, {}, :rows=>1 + /= in_place_editor_field :grader_configuration, :key, {}, :rows=>1 + = @grader_configuration.key %td - = in_place_editor_field :grader_configuration, :value_type, {}, :rows=>1 + /= in_place_editor_field :grader_configuration, :value_type, {}, :rows=>1 + = @grader_configuration.value_type %td = best_in_place @grader_configuration, :value, ok_button: "ok", cancel_button: "cancel" %td= conf.description diff --git a/app/views/graders/_grader.html.haml b/app/views/graders/_grader.html.haml --- a/app/views/graders/_grader.html.haml +++ b/app/views/graders/_grader.html.haml @@ -2,10 +2,10 @@ %td= grader.host %td= grader.pid %td= grader.mode -%td= grader.updated_at.strftime("%H:%M:%S") if grader.updated_at!=nil +%td= grader.updated_at.strftime("%H:%M:%S") unless grader.updated_at.nil? %td= grader.task_type %td - - if grader.task_id==nil + - if grader.task_id.nil? idle - else = link_to "#{grader.task_id}", :action => 'view', :id => grader.task_id, :type => grader.task_type diff --git a/app/views/graders/_grader_list.html.haml b/app/views/graders/_grader_list.html.haml --- a/app/views/graders/_grader_list.html.haml +++ b/app/views/graders/_grader_list.html.haml @@ -1,5 +1,5 @@ - if grader_list.length!=0 - %table.graders + %table.table.table-striped.table-condensed %tr %th host %th pid @@ -7,6 +7,7 @@ %th last updated %th type %th task + %th - grader_list.each do |grader| - if grader.active - c = 'active' @@ -16,9 +17,9 @@ = render :partial => 'grader', :locals => {:grader => grader} - if not grader.terminated - if GraderScript.grader_control_enabled? - %td= link_to 'stop', {:action => 'stop', :id => grader} + %td= link_to 'stop', {:action => 'stop', :id => grader}, class: 'btn btn-danger btn-xs btn-block' - else - %td= link_to 'clear', {:action => 'clear', :id => grader} + %td= link_to 'clear', {:action => 'clear', :id => grader}, class: 'btn btn-danger btn-xs btn-block' - else %ul %li None diff --git a/app/views/graders/list.html.haml b/app/views/graders/list.html.haml --- a/app/views/graders/list.html.haml +++ b/app/views/graders/list.html.haml @@ -1,12 +1,19 @@ - content_for :head do - = stylesheet_link_tag 'graders' - = javascript_include_tag 'local_jquery' %h1 Grader information -= link_to '[Refresh]', :action => 'list' -%br/ +%p + = link_to 'Refresh', { :action => 'list' }, class: 'btn btn-info' + +.panel.panel-primary + .panel-heading + Grader control: + .panel-body + =link_to 'Start Graders in grading env', { action: 'start_grading'}, class: 'btn btn-default', method: 'post' + =link_to 'Start Graders in exam env', { action: 'start_exam'}, class: 'btn btn-default', method: 'post' + =link_to 'Stop all running Graders', { action: 'stop_all'}, class: 'btn btn-default', method: 'post' + =link_to 'Clear all data', { action: 'clear_all'}, class: 'btn btn-default', method: 'post' .submitbox .item @@ -25,49 +32,50 @@ = submit_tag 'Clear all data' %br{:style => 'clear:both'}/ -%div{style: 'width:500px; float: left;'} - - if @last_task - Last task: - = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task' +.row + .col-md-6 + - if @last_task + Last task: + = link_to "#{@last_task.id}", :action => 'view', :id => @last_task.id, :type => 'Task' - %br/ + %br/ - - if @last_test_request - Last test_request: - = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest' + - if @last_test_request + Last test_request: + = link_to "#{@last_test_request.id}", :action => 'view', :id => @last_test_request.id, :type => 'TestRequest' - %h2 Current graders + %h2 Current graders - = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes} + = render :partial => 'grader_list', :locals => {:grader_list => @grader_processes} - %h2 Stalled graders + %h2 Stalled graders - = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes} + = render :partial => 'grader_list', :locals => {:grader_list => @stalled_processes} - %h2 Terminated graders + %h2 Terminated graders - = form_for :clear, :url => {:action => 'clear_terminated'} do |f| - = submit_tag 'Clear data for terminated graders' + %p= link_to 'Clear data for terminated graders', { action: 'clear_terminated'}, class: 'btn btn-default', method: 'post' - = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes} -%div{} - %h2 Last 20 submissions - %table.graders - %thead - %th ID - %th User - %th Problem - %th Submitted - %th Graded - %th Result - %tbody - - @submission.each do |sub| - %tr.inactive - %td= link_to sub.id, controller: 'graders' ,action: 'submission', id: sub.id - %td= sub.try(:user).try(:full_name) - %td= sub.try(:problem).try(:full_name) - %td= "#{time_ago_in_words(sub.submitted_at)} ago" - %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " " - %td= sub.grader_comment + = render :partial => 'grader_list', :locals => {:grader_list => @terminated_processes} + .col-md-6 + %h2 Last 20 submissions + %table.table.table-striped.table-condensed + %thead + %th ID + %th User + %th Problem + %th Submitted + %th Graded + %th Result + %th + %tbody + - @submission.each do |sub| + %tr.inactive + %td= link_to sub.id, controller: 'graders' ,action: 'submission', id: sub.id + %td= sub.try(:user).try(:full_name) + %td= sub.try(:problem).try(:full_name) + %td= "#{time_ago_in_words(sub.submitted_at)} ago" + %td= sub.graded_at ? "#{time_ago_in_words(sub.graded_at)} ago" : " " + %td= sub.grader_comment diff --git a/app/views/graders/submission.html.haml b/app/views/graders/submission.html.haml --- a/app/views/graders/submission.html.haml +++ b/app/views/graders/submission.html.haml @@ -1,67 +1,90 @@ -%style{type: "text/css"} - = @css_style -:css - .field { - font-weight: bold; - text-align: right; - padding: 3px; - } - +//%style{type: "text/css"} +// = @css_style %h1= "Submission: #{@submission.id}" - -%h2 Stat +%textarea#data{style: "display:none;"} + :preserve + #{@submission.source} -%table.info - %thead - %tr.info-head - %th Field - %th Value - %tbody - %tr{class: cycle('info-even','info-odd')} - %td.field User: - %td.value - - if @submission.user - = link_to "(#{@submission.user.login})", controller: "users", action: "profile", id: @submission.user - = @submission.user.full_name - - else - = "(n/a)" - %tr{class: cycle('info-even','info-odd')} - %td.field Problem: - %td.value - - if @submission.problem!=nil - = link_to "(#{@submission.problem.name})", controller: "problems", action: "stat", id: @submission.problem - = @submission.problem.full_name - - else - = "(n/a)" - %tr{class: cycle('info-even','info-odd')} - %td.field Tries: - %td.value= @submission.number - %tr{class: cycle('info-even','info-odd')} - %td.field Submitted: - %td.value #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)}) - %tr{class: cycle('info-even','info-odd')} - %td.field Graded: - %td.value #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)}) - %tr{class: cycle('info-even','info-odd')} - %td.field Points: - %td.value #{@submission.points}/#{@submission.problem.full_score} - %tr{class: cycle('info-even','info-odd')} - %td.field Comment: - %td.value #{@submission.grader_comment} - %tr{class: cycle('info-even','info-odd')} - %td.field Runtime (s): - %td.value #{@submission.max_runtime} - %tr{class: cycle('info-even','info-odd')} - %td.field Memory (kb): - %td.value #{@submission.peak_memory} - - if session[:admin] - %tr{class: cycle('info-even','info-odd')} - %td.field IP: - %td.value #{@submission.ip_address} +//%div.highlight{:style => "border: 1px solid black;"} +//=@formatted_code.html_safe +.containter + .row + .col-md-7 + %h2 Source Code + .col-md-5 + %h2 Stat + .row + .col-md-7 + %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" } + :javascript + e = ace.edit("editor") + e.setOptions({ maxLines: Infinity }) + e.setValue($("#data").text()) + e.gotoLine(1) + e.getSession().setMode("#{get_ace_mode(@submission.language)}") + e.setReadOnly(true) + .col-md-5 + %table.table.table-striped + %tr + %td.text-right + %strong User + %td + - if @submission.user + = link_to "(#{@submission.user.login})", controller: "users", action: "profile", id: @submission.user + = @submission.user.full_name + - else + = "(n/a)" + %tr + %td.text-right + %strong Task + %td + - if @submission.problem!=nil + = link_to "(#{@submission.problem.name})", controller: "problems", action: "stat", id: @submission.problem + = @submission.problem.full_name + - else + = "(n/a)" + %tr + %td.text-right + %strong Tries + %td= @submission.number + %tr + %td.text-right + %strong Language + %td= @submission.language.pretty_name + %tr + %td.text-right + %strong Submitted + %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)}) + %tr + %td.text-right + %strong Graded + - if @submission.graded_at + %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)}) + - else + %td - + %tr + %td.text-right + %strong Points + %td #{@submission.points}/#{@submission.problem.full_score} + %tr + %td.text-right + %strong Comment + %td #{@submission.grader_comment} + %tr + %td.text-right + %strong Runtime (s) + %td #{@submission.max_runtime} + %tr + %td.text-right + %strong Memory (kb) + %td #{@submission.peak_memory} + - if session[:admin] + %tr + %td.text-right + %strong IP + %td #{@submission.ip_address} -%h2 Source code -//%div.highlight{:style => "border: 1px solid black;"} -=@formatted_code.html_safe + diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml new file mode 100644 --- /dev/null +++ b/app/views/layouts/_header.html.haml @@ -0,0 +1,84 @@ +%header.navbar.navbar-default.navbar-fixed-top + %nav + .container-fluid + .navbar-header + %a.navbar-brand{href: main_list_path} + %span.glyphicon.glyphicon-home + MAIN + .collapse.navbar-collapse + %ul.nav.navbar-nav + - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user)) + //= add_menu("#{I18n.t 'menu.tasks'}", 'tasks', 'list') + %li.dropdown + %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} + = "#{I18n.t 'menu.submissions'}" + %span.caret + %ul.dropdown-menu + = add_menu("View", 'main', 'submission') + = add_menu("Self Test", 'test', 'index') + - if GraderConfiguration['right.user_hall_of_fame'] + = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof') + / display MODE button (with countdown in contest mode) + - if GraderConfiguration.analysis_mode? + %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE" + - elsif GraderConfiguration.time_limit_mode? + - if @current_user.contest_finished? + %div.navbar-btn.btn.btn-danger#countdown= "Contest is over" + - elsif !@current_user.contest_started? + %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started') + - else + %div.navbar-btn.btn.btn-primary#countdown asdf + :javascript + $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'}); + / admin section + - if (@current_user!=nil) and (session[:admin]) + %li.dropdown + %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} + Manage + %span.caret + %ul.dropdown-menu + = add_menu( 'Announcements', 'announcements', 'index') + = add_menu( 'Problems', 'problems', 'index') + = add_menu( 'Users', 'user_admin', 'index') + = add_menu( 'Graders', 'graders', 'list') + = add_menu( 'Message ', 'messages', 'console') + %li.divider{role: 'separator'} + = add_menu( 'System config', 'configurations', 'index') + %li.divider{role: 'separator'} + = add_menu( 'Sites', 'sites', 'index') + = add_menu( 'Contests', 'contest_management', 'index') + %li.dropdown + %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"} + Report + %span.caret + %ul.dropdown-menu + = add_menu( 'Results', 'user_admin', 'user_stat') + = add_menu( 'Report', 'report', 'multiple_login') + - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0 + =link_to "#{ungraded} backlogs!", + grader_list_path, + class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission' + + %ul.nav.navbar-nav.navbar-right + = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help') + = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}}) + - if GraderConfiguration['system.user_setting_enabled'] + = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}}) + = 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'}}) + +/ + - if (@current_user!=nil) and (session[:admin]) + %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar + .container-fluid + .collapse.navbar-collapse + %ul.nav.navbar-nav + = add_menu( '[Announcements]', 'announcements', 'index') + = add_menu( '[Msg console]', 'messages', 'console') + = add_menu( '[Problems]', 'problems', 'index') + = add_menu( '[Users]', 'user_admin', 'index') + = add_menu( '[Results]', 'user_admin', 'user_stat') + = add_menu( '[Report]', 'report', 'multiple_login') + = add_menu( '[Graders]', 'graders', 'list') + = add_menu( '[Contests]', 'contest_management', 'index') + = add_menu( '[Sites]', 'sites', 'index') + = add_menu( '[System config]', 'configurations', 'index') diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb deleted file mode 100644 --- a/app/views/layouts/application.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - - - - <%= GraderConfiguration['contest.name'] %> - <%= stylesheet_link_tag "application", :media => "all" %> - <%= javascript_include_tag "application" %> - <%= csrf_meta_tags %> - <%= content_for :header %> - <%= yield :head %> - - - - -
-<%= user_header %> -
- -<%= content_tag(:p,flash[:notice],:style => "color:green") if flash[:notice]!=nil %> - -<%= yield %> - - - diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,15 @@ + +%html +%head + %title= GraderConfiguration['contest.name'] + = stylesheet_link_tag "application", params[:controller], :media => "all" + = javascript_include_tag "application", params[:controller] + = csrf_meta_tags + = content_for :header + = yield :head + +%body + = render 'layouts/header' + + = content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil + = yield diff --git a/app/views/main/_announcement.html.haml b/app/views/main/_announcement.html.haml --- a/app/views/main/_announcement.html.haml +++ b/app/views/main/_announcement.html.haml @@ -1,19 +1,13 @@ -.announcement{:id => "announcement-#{announcement.id}", :style => "#{'display: none; opacity: 0' if (defined? announcement_effect) and announcement_effect }"} - %div - .announcement-title - -# .toggles - -# %a{:href => '#', :onclick => "$(\"announcement-body-#{announcement.id}\").blindUp({duration: 0.2}); return false;"} - -# [hide] - -# %a{:href => '#', :onclick => "$(\"announcement-body-#{announcement.id}\").blindDown({duration: 0.2}); return false;"} - -# [show] - = announcement.title - .announcement-body{:id => "announcement-body-#{announcement.id}"} - = markdown(announcement.body) - -#.pub-info - -# %p= "#{announcement.author}, #{announcement.created_at}" +%li.list-group-item + %strong + = announcement.title + %small= "(updated #{time_ago_in_words(announcement.updated_at)} ago on #{announcement.updated_at})" + + %br + = markdown(announcement.body) + :javascript + Announcement.updateRecentId(#{announcement.id}); + - if (defined? announcement_effect) and announcement_effect :javascript - Announcement.updateRecentId(#{announcement.id}); - - if (defined? announcement_effect) and announcement_effect - :javascript - $("announcement-#{announcement.id}").blindDown({duration: 0.2}); - $("announcement-#{announcement.id}").appear({duration: 0.5, queue: 'end'}); + $("announcement-#{announcement.id}").blindDown({duration: 0.2}); + $("announcement-#{announcement.id}").appear({duration: 0.5, queue: 'end'}); diff --git a/app/views/main/_problem.html.erb b/app/views/main/_problem.html.erb deleted file mode 100644 --- a/app/views/main/_problem.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -"> - - <%= "#{problem_counter+1}" %> - - - <%= "#{problem.name}"%> - - - <%= "#{problem.full_name}" %> - <%= link_to_description_if_any "[#{t 'main.problem_desc'}]", problem %> - - - <%= @prob_submissions[problem.id][:count] %> - - - <%= render :partial => 'submission_short', - :locals => { - :submission => @prob_submissions[problem.id][:submission], - :problem_name => problem.name }%> - - diff --git a/app/views/main/_problem.html.haml b/app/views/main/_problem.html.haml new file mode 100644 --- /dev/null +++ b/app/views/main/_problem.html.haml @@ -0,0 +1,17 @@ +%tr + %td + = "#{problem.name}" + %td + = "#{problem.full_name}" + = link_to_description_if_any "[#{t 'main.problem_desc'}] ".html_safe, problem + %td + = @prob_submissions[problem.id][:count] + = link_to "[subs]", main_submission_path(problem.id) + %td + = render :partial => 'submission_short', + :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name } + %td + - if @prob_submissions[problem.id][:submission] + = link_to 'Edit', direct_edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success' + - else + = link_to 'New', direct_edit_path(problem.id), class: 'btn btn-success' diff --git a/app/views/main/_submission.html.haml b/app/views/main/_submission.html.haml --- a/app/views/main/_submission.html.haml +++ b/app/views/main/_submission.html.haml @@ -1,18 +1,18 @@ -%tr{:class => ((submission_counter%2==0) ? "info-even" : "info-odd")} - %td.info{:align => "center"} +%tr + %td{:align => "center"} = submission_counter+1 - %td.info{:align => "center"} + %td{:align => "center"} = link_to "##{submission.id}", controller: :graders, action: :submission, id: submission.id - %td.info + %td = l submission.submitted_at, format: :long = "( #{time_ago_in_words(submission.submitted_at)} ago)" - %td.info{:align => "center"} + %td = submission.source_filename = " (#{submission.language.pretty_name}) " = link_to('[load]',{:action => 'source', :id => submission.id}) - %td.info - - if submission.graded_at!=nil + %td + - if submission.graded_at = "Graded at #{format_short_time(submission.graded_at)}." %br/ = "Score: #{(submission.points*100/submission.problem.full_score).to_i} " if GraderConfiguration['ui.show_score'] @@ -20,5 +20,7 @@ %tt = submission.grader_comment = "]" - %td.info + %td = render :partial => 'compiler_message', :locals => {:compiler_message => submission.compiler_message } + %td + = link_to 'Edit', direct_edit_submission_path(submission.id), class: 'btn btn-success' diff --git a/app/views/main/_submission_box.html.erb b/app/views/main/_submission_box.html.erb deleted file mode 100644 --- a/app/views/main/_submission_box.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -<%= form_tag({:action => 'submit'}, :multipart => true) do %> -Problem: <%= select 'submission', 'problem_id', - [[(t 'main.specified_in_header'),'-1']] + - @problems.collect {|p| [p.full_name, p.id]}, - :selected => '-1' %> -File: <%= file_field_tag 'file' %> -<%= submit_tag 'Submit' %> -<% end %> diff --git a/app/views/main/_submission_box.html.haml b/app/views/main/_submission_box.html.haml new file mode 100644 --- /dev/null +++ b/app/views/main/_submission_box.html.haml @@ -0,0 +1,20 @@ += form_tag({:action => 'submit'}, :multipart => true, class: 'form') do + - if @submission and @submission.errors.any? + #error_explanation + .alert.alert-danger + %h3= "#{pluralize(@submission.errors.count, "error")} prohibited this user from being saved:" + %ul + - @submission.errors.full_messages.each do |msg| + %li= msg + .form-group + = label_tag :submission, 'Problem:' + = 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' } + .form-group + = label_tag :file, 'File:' + .input-group + %span.input-group-btn + %span.btn.btn-default.btn-file + Browse + = file_field_tag 'file' + = text_field_tag '' , nil, {readonly: true, class: 'form-control'} + = submit_tag 'Submit', class: 'btn btn-default' diff --git a/app/views/main/_submission_short.html.haml b/app/views/main/_submission_short.html.haml --- a/app/views/main/_submission_short.html.haml +++ b/app/views/main/_submission_short.html.haml @@ -1,8 +1,8 @@ -- if submission==nil +- if submission.nil? = "-" - else - - if submission.graded_at==nil + - if submission.graded_at.nil? =t 'main.submitted_at' = format_short_time(submission.submitted_at.localtime) - else @@ -22,5 +22,5 @@ = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true}) = " | " = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id}) - = " | " - = link_to "[#{t 'main.submissions_link'}]", :action => 'submission', :id => problem_name + //= " | " + //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id) diff --git a/app/views/main/list.html.haml b/app/views/main/list.html.haml --- a/app/views/main/list.html.haml +++ b/app/views/main/list.html.haml @@ -3,49 +3,48 @@ = user_title_bar(@user) -.announcementbox{:style => (@announcements.length==0 ? "display:none" : "")} - %span{:class => 'title'} - Announcements - #announcementbox-body - = render :partial => 'announcement', :collection => @announcements - -- if GraderConfiguration.show_submitbox_to?(@user) - .submitbox - = error_messages_for 'submission' - = render :partial => 'submission_box' - - -%hr/ - - if (GraderConfiguration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true) %p=t 'main.start_soon' -- if GraderConfiguration.show_tasks_to?(@user) - - if not GraderConfiguration.multicontests? - %table.info - %tr.info-head - %th - %th Tasks name - %th Full name - %th # of sub(s) - %th Results - = render :partial => 'problem', :collection => @problems - - else - - @contest_problems.each do |cp| - - if cp[:problems].length > 0 - %h2{:class =>'contest-title'} - = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}" - %table.info - %tr.info-head - %th - %th Tasks name - %th Full name - %th # of sub(s) - %th Results - = render :partial => 'problem', :collection => cp[:problems] - - -%hr/ +.row + .col-md-7 + - if GraderConfiguration.show_submitbox_to?(@user) + .panel.panel-primary + .panel-heading + Submission + .panel-body + = render :partial => 'submission_box' + - if GraderConfiguration.show_tasks_to?(@user) + - if not GraderConfiguration.multicontests? + %table.table.table-striped.table-condensed + %thead + %tr + %th Task name + %th Full name + %th # of sub(s) + %th Results + %th + %tbody + = render :partial => 'problem', :collection => @problems + - else + - @contest_problems.each do |cp| + - if cp[:problems].length > 0 + %h2{:class =>'contest-title'} + = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}" + %table.info + %tr.info-head + %th Task name + %th Full name + %th # of sub(s) + %th Results + %th + = render :partial => 'problem', :collection => cp[:problems] + .col-md-5 + .panel.panel-info + .panel-heading + Announcement + %ul.list-group + = render :partial => 'announcement', :collection => @announcements %script{:type => 'text/javascript'} = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';" diff --git a/app/views/main/submission.html.haml b/app/views/main/submission.html.haml --- a/app/views/main/submission.html.haml +++ b/app/views/main/submission.html.haml @@ -1,25 +1,31 @@ = user_title_bar(@user) -.task-menu - Task List - %br/ - - @problems.each do |problem| - = link_to problem.name, :action => 'submission', :id => problem.name +.panel.panel-info + .panel-heading + Select Problems + .panel-body + .form-inline + = select 'submission', + 'problem_id', + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", main_submission_url(p.id)]}, + { selected: (@problem ? main_submission_url(@problem) : -1) }, + { class: 'select2 form-control'} + %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: '#submission_problem_id'}} Go - if @problem!=nil %h2= "Task: #{@problem.full_name} (#{@problem.name})" - if @submissions!=nil - if @submissions.length>0 - %table.info - %tr.info-head - %th.info No. - %th.info # - %th.info At - %th.info Source - %th.info Result - %th.info{:width => "300px"} - Compiler message + %table.table + %thead + %th No. + %th # + %th At + %th Source + %th Result + %th{:width => "300px"} Compiler message + %th = render :partial => 'submission', :collection => @submissions - else No submission diff --git a/app/views/problems/index.html.haml b/app/views/problems/index.html.haml new file mode 100644 --- /dev/null +++ b/app/views/problems/index.html.haml @@ -0,0 +1,50 @@ +- content_for :head do + = stylesheet_link_tag 'problems' +%h1 Listing problems +%p + = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm' + = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm' + = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm' + = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm' + = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm' +.submitbox + = form_tag :action => 'quick_create' do + %b Quick New: + %label{:for => "problem_name"} Name + = text_field 'problem', 'name' + | + %label{:for => "problem_full_name"} Full name + = text_field 'problem', 'full_name' + = submit_tag "Create" +%table.table.table-condense.table-hover + %thead + %th Name + %th Full name + %th.text-right Full score + %th Date added + %th.text-center + Avail? + %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?] + %th.text-center + Test? + %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?] + - if GraderConfiguration.multicontests? + %th Contests + - for problem in @problems + %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"} + - @problem=problem + %td= in_place_editor_field :problem, :name, {}, :rows=>1 + %td= in_place_editor_field :problem, :full_name, {}, :rows=>1 + %td.text-right= in_place_editor_field :problem, :full_score, {}, :rows=>1 + %td= problem.date_added + %td= toggle_button(@problem.available?, toggle_problem_url(@problem), "problem-avail-#{@problem.id}") + %td= toggle_button(@problem.test_allowed?, toggle_test_problem_url(@problem), "problem-test-#{@problem.id}") + - if GraderConfiguration.multicontests? + %td + = problem.contests.collect { |c| c.name }.join(', ') + %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block' + %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block' + %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block' + %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-xs btn-block' +%br/ += link_to '[New problem]', :action => 'new' diff --git a/app/views/problems/list.html.erb b/app/views/problems/list.html.erb deleted file mode 100644 --- a/app/views/problems/list.html.erb +++ /dev/null @@ -1,66 +0,0 @@ -<% content_for :head do %> - <%= stylesheet_link_tag 'problems' %> -<% end %> - -

Listing problems

- -

-<%= link_to '[New problem]', :action => 'new' %> -<%= link_to '[Manage problems]', :action => 'manage' %> -<%= link_to '[Import problems]', :action => 'import' %> -<%= link_to '[Turn off all problems]', :action => 'turn_all_off' %> -<%= link_to '[Turn on all problems]', :action => 'turn_all_on' %> -

- -
- <%= form_tag :action => 'quick_create' do %> - Quick New: - - <%= text_field 'problem', 'name' %> | - - <%= text_field 'problem', 'full_name' %> - <%= submit_tag "Create" %> - <% end %> -
- - - - - - - - - - <% if GraderConfiguration.multicontests? %> - - <% end %> - - -<% for problem in @problems %> - "> - <% @problem=problem %> - - - - - - - - <% if GraderConfiguration.multicontests? %> - - <% end %> - - - - - - - -<% end %> -
NameFull nameFull scoreDate addedAvail?Test?Contests
<%= in_place_editor_field :problem, :name, {}, :rows=>1 %><%= in_place_editor_field :problem, :full_name, {}, :rows=>1 %><%= in_place_editor_field :problem, :full_score, {}, :rows=>1 %><%= problem.date_added %><%= problem.available %><%= problem.test_allowed %> - <%= problem.contests.collect { |c| c.name }.join(', ') %> - <%= link_to '[Toggle]', {:action => 'toggle', :id => problem.id }, :remote => true %><%= link_to '[Stat]', :action => 'stat', :id => problem.id %><%= link_to '[Show]', :action => 'show', :id => problem %><%= link_to '[Edit]', :action => 'edit', :id => problem %><%= link_to '[Destroy]', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :post %>
- -
- -<%= link_to '[New problem]', :action => 'new' %> diff --git a/app/views/problems/toggle.js.haml b/app/views/problems/toggle.js.haml new file mode 100644 --- /dev/null +++ b/app/views/problems/toggle.js.haml @@ -0,0 +1,8 @@ += render partial: 'toggle_button', + locals: {button_id: "#problem-avail-#{@problem.id}",button_on: @problem.available } +:plain + r = $("#prob-#{@problem.id}"); + r.removeClass('success'); + r.removeClass('danger'); + r.addClass("#{@problem.available? ? 'success' : 'danger'}"); + diff --git a/app/views/problems/toggle.js.rjs b/app/views/problems/toggle.js.rjs deleted file mode 100644 --- a/app/views/problems/toggle.js.rjs +++ /dev/null @@ -1,6 +0,0 @@ -if @problem.available - page["prob-#{@problem.id}"].className = "available" -else - page["prob-#{@problem.id}"].className = "not-available" -end -page["prob-#{@problem.id}-avail"].innerHTML = @problem.available.to_s diff --git a/app/views/problems/toggle_test.js.haml b/app/views/problems/toggle_test.js.haml new file mode 100644 --- /dev/null +++ b/app/views/problems/toggle_test.js.haml @@ -0,0 +1,2 @@ += render partial: 'toggle_button', + locals: {button_id: "#problem-test-#{@problem.id}",button_on: @problem.test_allowed?} diff --git a/app/views/report/_task_hof.html.haml b/app/views/report/_task_hof.html.haml --- a/app/views/report/_task_hof.html.haml +++ b/app/views/report/_task_hof.html.haml @@ -1,14 +1,3 @@ -- content_for :header do - = javascript_include_tag 'local_jquery' - -:javascript - $(document).ready( function() { - $("#mem_remark").hover( function() { - $("#mem_remark_box").show(); - }, function() { - $("#mem_remark_box").hide(); - }); - }); :css .hof_user { color: orangered; font-style: italic; } .hof_language { color: green; font-style: italic; } @@ -34,94 +23,114 @@ overflow: auto; } -%h1 (#{Problem.find(params[:id]).name}) #{Problem.find(params[:id]).full_name} -%h2 Problem Stat -%table.info - %thead - %tr.info-head - %th Stat - %th Value - %tbody - %tr{class: cycle('info-even','info-odd')} - %td.info_param Submissions - %td= @summary[:count] - %tr{class: cycle('info-even','info-odd')} - %td.info_param Solved/Attempted User - %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) - - if @best - %tr{class: cycle('info-even','info-odd')} - %td.info_param Best Runtime - %td - by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]} - using #{@best[:runtime][:lang]} - with #{@best[:runtime][:value] * 1000} milliseconds - at submission - = link_to("#" + @best[:runtime][:sub_id].to_s, controller: 'graders', action: 'submission', id:@best[:runtime][:sub_id]) +.container + .row + .col-md-4 + %h2 Overall Stat + %table.table.table-hover + %thead + %tr + %th + %th + %tbody + %tr + %td.info_param Submissions + %td= @summary[:count] + %tr + %td.info_param Solved/Attempted User + %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%) + - if @best + %tr + %td.info_param Best Runtime + %td + by #{link_to @best[:runtime][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]} + %br + using #{@best[:runtime][:lang]} + %br + with #{@best[:runtime][:value] * 1000} milliseconds + %br + at submission + = link_to("#" + @best[:runtime][:sub_id].to_s, controller: 'graders', action: 'submission', id:@best[:runtime][:sub_id]) - %tr{class: cycle('info-even','info-odd')} - %td.info_param - Best Memory Usage - %sup{ id: "mem_remark", style: "position:relative; color: blue;"} - [?] - %span.tooltip#mem_remark_box - This counts only for submission with 100% score. - Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...) - %td - by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]} - using #{@best[:memory][:lang]} - with #{number_with_delimiter(@best[:memory][:value])} kbytes - at submission - = link_to("#" + @best[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id:@best[:memory][:sub_id]) + %tr + %td.info_param + Best Memory Usage + %sup{ id: "xmem_remark", + style: "position:relative; color: blue;", + data: {toggle: 'tooltip', placement: 'top', animation: 'false', delay: 20}, + title: "This counts only for submission with 100% score. Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)"} + [?] + %td + by #{link_to @best[:memory][:user], controller:'users', action:'profile', id:@best[:memory][:user_id]} + %br + using #{@best[:memory][:lang]} + %br + with #{number_with_delimiter(@best[:memory][:value])} kbytes + %br + at submission + = link_to("#" + @best[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id:@best[:memory][:sub_id]) - %tr{class: cycle('info-even','info-odd')} - %td.info_param Shortest Code - %td - by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]} - using #{@best[:length][:lang]} - with #{@best[:length][:value]} bytes - at submission - = link_to("#" + @best[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:length][:sub_id]) - - %tr{class: cycle('info-even','info-odd')} - %td.info_param First solver - %td - #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver - using #{@best[:first][:lang]} - on #{@best[:first][:value]} - at submission - = link_to("#" + @best[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:first][:sub_id]) - -- if @best - %h2 By language + %tr + %td.info_param Shortest Code + %td + by #{link_to @best[:length][:user], controller:'users', action:'profile', id:@best[:length][:user_id]} + %br + using #{@best[:length][:lang]} + %br + with #{@best[:length][:value]} bytes + %br + at submission + = link_to("#" + @best[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:length][:sub_id]) - %table.info - %thead - %tr.info-head - %th Language - %th Best runtime (ms) - %th Best memory (kbytes) - %th Shortest Code (bytes) - %th First solver - %tbody - - @by_lang.each do |lang,value| - %tr{class: cycle('info-even','info-odd')} - %td= lang - %td - = link_to value[:runtime][:user], controller: 'users', action: 'profile', id: value[:runtime][:user_id] - = "(#{(value[:runtime][:value] * 1000).to_i} @" - = "#{link_to("#" + value[:runtime][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:runtime][:sub_id])} )".html_safe - %td - = link_to value[:memory][:user], controller: 'users', action: 'profile', id: value[:memory][:user_id] - = "(#{number_with_delimiter(value[:memory][:value])} @" - = "#{link_to("#" + value[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:memory][:sub_id])} )".html_safe - %td - = link_to value[:length][:user], controller: 'users', action: 'profile', id: value[:length][:user_id] - = "(#{value[:length][:value]} @" - = "#{link_to("#" + value[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:length][:sub_id])} )".html_safe - %td - - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong... - = link_to value[:first][:user], controller: 'users', action: 'profile', id: value[:first][:user_id] - = "(#{value[:first][:value]} @" - = "#{link_to("#" + value[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:first][:sub_id])} )".html_safe + %tr + %td.info_param First solver + %td + - if @best[:first][:user] != '(NULL)' + #{link_to @best[:first][:user], controller:'users', action:'profile', id:@best[:first][:user_id]} is the first solver + %br + using #{@best[:first][:lang]} + %br + on #{@best[:first][:value]} + %br + at submission + = link_to("#" + @best[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: @best[:first][:sub_id]) + - else + no first solver + .col-md-8 + - if @best + %h2 By Language + %table.table.table-hover + %thead + %tr + %th Language + %th Best runtime (ms) + %th Best memory (kbytes) + %th Shortest Code (bytes) + %th First solver + %tbody + - @by_lang.each do |lang,value| + %tr + %td= lang + %td + = link_to value[:runtime][:user], controller: 'users', action: 'profile', id: value[:runtime][:user_id] + %br + = "(#{(value[:runtime][:value] * 1000).to_i} @" + = "#{link_to("#" + value[:runtime][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:runtime][:sub_id])} )".html_safe + %td + = link_to value[:memory][:user], controller: 'users', action: 'profile', id: value[:memory][:user_id] + %br + = "(#{number_with_delimiter(value[:memory][:value])} @" + = "#{link_to("#" + value[:memory][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:memory][:sub_id])} )".html_safe + %td + = link_to value[:length][:user], controller: 'users', action: 'profile', id: value[:length][:user_id] + %br + = "(#{value[:length][:value]} @" + = "#{link_to("#" + value[:length][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:length][:sub_id])} )".html_safe + %td + - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong... + = link_to value[:first][:user], controller: 'users', action: 'profile', id: value[:first][:user_id] + %br + = "(#{value[:first][:value]} @" + = "#{link_to("#" + value[:first][:sub_id].to_s, controller: 'graders' , action: 'submission', id: value[:first][:sub_id])} )".html_safe diff --git a/app/views/report/problem_hof.html.haml b/app/views/report/problem_hof.html.haml --- a/app/views/report/problem_hof.html.haml +++ b/app/views/report/problem_hof.html.haml @@ -5,19 +5,25 @@ /- else / %h1 All-Time Hall of Fame +.panel.panel-info + .panel-heading + Select Task + .panel-body + .form-inline + = select 'report', + 'problem_id', + @problems.collect {|p| ["[#{p.name}] #{p.full_name}", report_problem_hof_url(p.id)]}, + {:selected => report_problem_hof_url(@problem)}, + { class: 'select2 form-control' } + %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: "#report_problem_id"}} Go -%h1 Hall of Fame -.task-menu - Tasks - %br/ - - @problems.each do |prob| - = link_to( "[#{prob.name}]", {id: prob.id}) - unless params[:id] /=render partial: 'all_time_hof' Please select a problem. - else - =render partial: 'task_hof' + %h1 [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name} %h2 Submission History =render partial: 'application/bar_graph', locals: { histogram: @histogram } + =render partial: 'task_hof' diff --git a/app/views/sites/edit.html.erb b/app/views/sites/edit.html.erb deleted file mode 100644 --- a/app/views/sites/edit.html.erb +++ /dev/null @@ -1,32 +0,0 @@ -

Editing site

- -<%= error_messages_for :site %> - -<%= form_for(@site) do |f| %> -

- Name
- <%= f.text_field :name %> -

- -

- Password
- <%= f.text_field :password %> -

- -

- Started
- <%= f.check_box :started %> -

- -

- Start time
- <%= f.datetime_select :start_time %> -

- -

- <%= f.submit "Update" %> -

-<% end %> - -<%= link_to 'Show', @site %> | -<%= link_to 'Back', sites_path %> diff --git a/app/views/sites/edit.html.haml b/app/views/sites/edit.html.haml new file mode 100644 --- /dev/null +++ b/app/views/sites/edit.html.haml @@ -0,0 +1,24 @@ +%h1 Editing site += error_messages_for :site += form_for(@site) do |f| + %p + %b Name + %br/ + = f.text_field :name + %p + %b Password + %br/ + = f.text_field :password + %p + %b Started + %br/ + = f.check_box :started + %p + %b Start time + %br/ + = f.datetime_select :start_time, :include_blank => true + %p + = f.submit "Update" += link_to 'Show', @site +| += link_to 'Back', sites_path diff --git a/app/views/sites/index.html.erb b/app/views/sites/index.html.erb deleted file mode 100644 --- a/app/views/sites/index.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -

Listing sites

- - - - - - - - - -<% for site in @sites %> - <% background = "white" %> - <% background = "grey" if (site.started==true) and (site.finished? == true) %> - <% background = "lightgreen" if (site.started==true) and (site.finished? != true) %> - - - - - - - - - -<% end %> -
NamePasswordStartedStart time
<%=h site.name %><%=h site.password %><%=h site.started %><%=h site.start_time %><%= link_to 'Show', site %><%= link_to 'Edit', edit_site_path(site) %><%= link_to 'Destroy', site, :confirm => 'Are you sure?', :method => :delete %>
- -
- -<%= link_to 'New site', new_site_path %> diff --git a/app/views/sites/index.html.haml b/app/views/sites/index.html.haml new file mode 100644 --- /dev/null +++ b/app/views/sites/index.html.haml @@ -0,0 +1,24 @@ +%h1 Listing sites +%table.table.table-striped + %tr + %th Name + %th Password + %th Started + %th Start time + %th + %th + %th + - for site in @sites + - background = "white" + - background = "grey" if (site.started==true) and (site.finished? == true) + - background = "lightgreen" if (site.started==true) and (site.finished? != true) + %tr{:style => "background: #{background};"} + %td= h site.name + %td= h site.password + %td= h site.started + %td= h site.start_time + %td= link_to 'Show', site, class: 'btn btn-default' + %td= link_to 'Edit', edit_site_path(site), class: 'btn btn-default' + %td= link_to 'Destroy', site, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-default' +%br/ += link_to '+ New site', new_site_path, class: 'btn btn-success' diff --git a/app/views/sites/new.html.erb b/app/views/sites/new.html.erb deleted file mode 100644 --- a/app/views/sites/new.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -

New site

- -<%= error_messages_for :site %> - -<%= form_for(@site) do |f| %> -

- Name
- <%= f.text_field :name %> -

- -

- Password
- <%= f.text_field :password %> -

- -

- Started
- <%= f.check_box :started %> -

- -

- Start time
- <%= f.datetime_select :start_time %> -

- -

- <%= f.submit "Create" %> -

-<% end %> - -<%= link_to 'Back', sites_path %> diff --git a/app/views/sites/new.html.haml b/app/views/sites/new.html.haml new file mode 100644 --- /dev/null +++ b/app/views/sites/new.html.haml @@ -0,0 +1,22 @@ +%h1 New site += error_messages_for :site += form_for(@site) do |f| + %p + %b Name + %br/ + = f.text_field :name + %p + %b Password + %br/ + = f.text_field :password + %p + %b Started + %br/ + = f.check_box :started + %p + %b Start time + %br/ + = f.datetime_select :start_time + %p + = f.submit "Create" += link_to 'Back', sites_path diff --git a/app/views/sites/show.html.erb b/app/views/sites/show.html.erb deleted file mode 100644 --- a/app/views/sites/show.html.erb +++ /dev/null @@ -1,23 +0,0 @@ -

- Name: - <%=h @site.name %> -

- -

- Password: - <%=h @site.password %> -

- -

- Started: - <%=h @site.started %> -

- -

- Start time: - <%=h @site.start_time %> -

- - -<%= link_to 'Edit', edit_site_path(@site) %> | -<%= link_to 'Back', sites_path %> diff --git a/app/views/sites/show.html.haml b/app/views/sites/show.html.haml new file mode 100644 --- /dev/null +++ b/app/views/sites/show.html.haml @@ -0,0 +1,15 @@ +%p + %b Name: + = h @site.name +%p + %b Password: + = h @site.password +%p + %b Started: + = h @site.started +%p + %b Start time: + = h @site.start_time += link_to 'Edit', edit_site_path(@site) +| += link_to 'Back', sites_path diff --git a/app/views/sources/direct_edit.html.haml b/app/views/sources/direct_edit.html.haml new file mode 100644 --- /dev/null +++ b/app/views/sources/direct_edit.html.haml @@ -0,0 +1,264 @@ +%h2 Live submit +%br + +%textarea#text_haha{style: "display:none"}~ @source +.container + .row + .col-md-12 + .alert.alert-info + Write your code in the following box, choose language, and click submit button when finished + .row + .col-md-8 + %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'} + .col-md-4 + = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do + + = hidden_field_tag 'editor_text', @source + = hidden_field_tag 'submission[problem_id]', @problem.id + .form-group + = label_tag "Task:" + = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true + + .form-group + = label_tag 'Language' + = 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" + .form-group + = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit', + data: {confirm: "Submitting this source code for task #{@problem.long_name}?"} + .panel.panel-info + .panel-heading + Latest Submission Status + .panel-body + - if @submission + = render :partial => 'submission_short', + :locals => {:submission => @submission, :problem_name => @problem.name } + .row + .col-md-12 + %h2 Console + %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20} + +:javascript + $(document).ready(function() { + brython(); + }); + + +%script#__main__{type:'text/python3'} + :plain + import sys + import traceback + + from browser import document as doc + from browser import window, alert, console + + _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands + for supporting Python development. See www.python.org for more information.""" + + _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com + All Rights Reserved. + + Copyright (c) 2001-2013 Python Software Foundation. + All Rights Reserved. + + Copyright (c) 2000 BeOpen.com. + All Rights Reserved. + + Copyright (c) 1995-2001 Corporation for National Research Initiatives. + All Rights Reserved. + + Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. + All Rights Reserved.""" + + _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. Redistributions in binary + form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided + with the distribution. + Neither the name of the nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + """ + + def credits(): + print(_credits) + credits.__repr__ = lambda:_credits + + def copyright(): + print(_copyright) + copyright.__repr__ = lambda:_copyright + + def license(): + print(_license) + license.__repr__ = lambda:_license + + def write(data): + doc['console'].value += str(data) + + + sys.stdout.write = sys.stderr.write = write + history = [] + current = 0 + _status = "main" # or "block" if typing inside a block + + # execution namespace + editor_ns = {'credits':credits, + 'copyright':copyright, + 'license':license, + '__name__':'__main__'} + + def cursorToEnd(*args): + pos = len(doc['console'].value) + doc['console'].setSelectionRange(pos, pos) + doc['console'].scrollTop = doc['console'].scrollHeight + + def get_col(area): + # returns the column num of cursor + sel = doc['console'].selectionStart + lines = doc['console'].value.split('\n') + for line in lines[:-1]: + sel -= len(line) + 1 + return sel + + + def myKeyPress(event): + global _status, current + if event.keyCode == 9: # tab key + event.preventDefault() + doc['console'].value += " " + elif event.keyCode == 13: # return + src = doc['console'].value + if _status == "main": + currentLine = src[src.rfind('>>>') + 4:] + elif _status == "3string": + currentLine = src[src.rfind('>>>') + 4:] + currentLine = currentLine.replace('\n... ', '\n') + else: + currentLine = src[src.rfind('...') + 4:] + if _status == 'main' and not currentLine.strip(): + doc['console'].value += '\n>>> ' + event.preventDefault() + return + doc['console'].value += '\n' + history.append(currentLine) + current = len(history) + if _status == "main" or _status == "3string": + try: + _ = editor_ns['_'] = eval(currentLine, editor_ns) + if _ is not None: + write(repr(_)+'\n') + doc['console'].value += '>>> ' + _status = "main" + except IndentationError: + doc['console'].value += '... ' + _status = "block" + except SyntaxError as msg: + if str(msg) == 'invalid syntax : triple string end not found' or \ + str(msg).startswith('Unbalanced bracket'): + doc['console'].value += '... ' + _status = "3string" + elif str(msg) == 'eval() argument must be an expression': + try: + exec(currentLine, editor_ns) + except: + traceback.print_exc() + doc['console'].value += '>>> ' + _status = "main" + elif str(msg) == 'decorator expects function': + doc['console'].value += '... ' + _status = "block" + else: + traceback.print_exc() + doc['console'].value += '>>> ' + _status = "main" + except: + traceback.print_exc() + doc['console'].value += '>>> ' + _status = "main" + elif currentLine == "": # end of block + block = src[src.rfind('>>>') + 4:].splitlines() + block = [block[0]] + [b[4:] for b in block[1:]] + block_src = '\n'.join(block) + # status must be set before executing code in globals() + _status = "main" + try: + _ = exec(block_src, editor_ns) + if _ is not None: + print(repr(_)) + except: + traceback.print_exc() + doc['console'].value += '>>> ' + else: + doc['console'].value += '... ' + + cursorToEnd() + event.preventDefault() + + def myKeyDown(event): + global _status, current + if event.keyCode == 37: # left arrow + sel = get_col(doc['console']) + if sel < 5: + event.preventDefault() + event.stopPropagation() + elif event.keyCode == 36: # line start + pos = doc['console'].selectionStart + col = get_col(doc['console']) + doc['console'].setSelectionRange(pos - col + 4, pos - col + 4) + event.preventDefault() + elif event.keyCode == 38: # up + if current > 0: + pos = doc['console'].selectionStart + col = get_col(doc['console']) + # remove current line + doc['console'].value = doc['console'].value[:pos - col + 4] + current -= 1 + doc['console'].value += history[current] + event.preventDefault() + elif event.keyCode == 40: # down + if current < len(history) - 1: + pos = doc['console'].selectionStart + col = get_col(doc['console']) + # remove current line + doc['console'].value = doc['console'].value[:pos - col + 4] + current += 1 + doc['console'].value += history[current] + event.preventDefault() + elif event.keyCode == 8: # backspace + src = doc['console'].value + lstart = src.rfind('\n') + if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6): + event.preventDefault() + event.stopPropagation() + + + doc['console'].bind('keypress', myKeyPress) + doc['console'].bind('keydown', myKeyDown) + doc['console'].bind('click', cursorToEnd) + v = sys.implementation.version + doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % ( + v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion) + #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.' + doc['console'].focus() + cursorToEnd() + + + + diff --git a/app/views/sources/get_latest_submission_status.js.haml b/app/views/sources/get_latest_submission_status.js.haml new file mode 100644 --- /dev/null +++ b/app/views/sources/get_latest_submission_status.js.haml @@ -0,0 +1,2 @@ +:javascript + $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}") diff --git a/app/views/user_admin/index.html.haml b/app/views/user_admin/index.html.haml new file mode 100644 --- /dev/null +++ b/app/views/user_admin/index.html.haml @@ -0,0 +1,100 @@ +%h1 Listing users + +.panel.panel-primary + .panel-title.panel-heading + Quick Add + .panel-body + = form_tag( {method: 'post'}, {class: 'form-inline'}) do + .form-group + = label_tag 'user_login', 'Login' + = text_field 'user', 'login', :size => 10,class: 'form-control' + .form-group + = label_tag 'user_full_name', 'Full Name' + = text_field 'user', 'full_name', :size => 10,class: 'form-control' + .form-group + = label_tag 'user_password', 'Password' + = text_field 'user', 'password', :size => 10,class: 'form-control' + .form-group + = label_tag 'user_password_confirmation', 'Confirm' + = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control' + .form-group + = label_tag 'user_email', 'email' + = text_field 'user', 'email', :size => 10,class: 'form-control' + =submit_tag "Create", class: 'btn btn-primary' + +.panel.panel-primary + .panel-title.panel-heading + Import from site management + .panel-body + = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do + .form-group + = label_tag :file, 'File:' + .input-group + %span.input-group-btn + %span.btn.btn-default.btn-file + Browse + = file_field_tag 'file' + = text_field_tag '' , nil, {readonly: true, class: 'form-control'} + = submit_tag 'Submit', class: 'btn btn-default' + + +%p + = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '} + = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '} + = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '} + = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '} + = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '} + = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '} + +- if GraderConfiguration.multicontests? + %br/ + %b Multi-contest: + = link_to '[Manage bulk users in contests]', :action => 'contest_management' + View users in: + - @contests.each do |contest| + = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id + = link_to "[no contest]", :action => 'contests', :id => 'none' + +Total #{@user_count} users | +- if !@paginated + Display all users. + \#{link_to '[show in pages]', :action => 'index', :page => '1'} +- else + Display in pages. + \#{link_to '[display all]', :action => 'index', :page => 'all'} | + \#{will_paginate @users, :container => false} + + +%table.table.table-hover.table-condense + %thead + %th Login + %th Full name + %th email + %th Remark + %th + Activated + %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?] + %th + Enabled + %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?] + %th Last IP + %th + %th + %th + %th + - for user in @users + %tr + %td= link_to user.login, controller: :users, :action => 'profile', :id => user + %td= user.full_name + %td= user.email + %td= user.remark + %td= toggle_button(user.activated?, toggle_activate_user_url(user),"toggle_activate_user_#{user.id}") + %td= toggle_button(user.enabled?, toggle_enable_user_url(user),"toggle_enable_user_#{user.id}") + %td= user.last_ip + %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' + %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block' + %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block' + %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :post, class: 'btn btn-danger btn-xs btn-block' +%br/ += link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '} += link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '} diff --git a/app/views/user_admin/list.html.erb b/app/views/user_admin/list.html.erb deleted file mode 100644 --- a/app/views/user_admin/list.html.erb +++ /dev/null @@ -1,89 +0,0 @@ -

Listing users

- -
- Quick add - <%= form_tag :action => 'create' do %> - - - - - - - - - - - - - - - - -
<%= text_field 'user', 'login', :size => 10 %><%= text_field 'user', 'full_name', :size => 30 %><%= password_field 'user', 'password', :size => 10 %><%= password_field 'user', 'password_confirmation', :size => 10 %><%= email_field 'user', 'email', :size => 15 %><%= submit_tag "Create" %>
- <% end %> -
- Import from site management - <%= form_tag({:action => 'import'}, :multipart => true) do %> - File: <%= file_field_tag 'file' %> <%= submit_tag 'Import' %> - <% end %> -
- What else: - <%= link_to '[New user]', :action => 'new' %> - <%= link_to '[New list of users]', :action => 'new_list' %> - <%= link_to '[View administrators]', :action => 'admin' %> - <%= link_to '[Random passwords]', :action => 'random_all_passwords' %> - <%= link_to '[View active users]', :action => 'active' %> - <%= link_to '[Mass mailing]', :action => 'mass_mailing' %> - <% if GraderConfiguration.multicontests? %> -
Multi-contest: - <%= link_to '[Manage bulk users in contests]', :action => 'contest_management' %> - View users in: - <% @contests.each do |contest| %> - <%= link_to "[#{contest.name}]", :action => 'contests', :id => contest.id %> - <% end %> - <%= link_to "[no contest]", :action => 'contests', :id => 'none' %> - <% end %> -
- -Total <%= @user_count %> users | -<% if !@paginated %> - Display all users. - <%= link_to '[show in pages]', :action => 'list', :page => '1' %> -<% else %> - Display in pages. - <%= link_to '[display all]', :action => 'list', :page => 'all' %> | - <%= will_paginate @users, :container => false %> -<% end %> - - - <% for column in User.content_columns %> - <% if !@hidden_columns.index(column.name) %> - - <% end %> - <% end %> - - - - - - -<% for user in @users %> - "> - - <% for column in User.content_columns %> - <% if !@hidden_columns.index(column.name) and column.name != 'login' %> - - <% end %> - <% end %> - - - - - -<% end %> -
<%= column.human_name %>
<%= link_to user.login, controller: :users, :action => 'profile', :id => user %><%=h user.send(column.name) %><%= 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?' %><%= link_to 'Show', :action => 'show', :id => user %><%= link_to 'Edit', :action => 'edit', :id => user %><%= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :post %>
- -
- -<%= link_to '[New user]', :action => 'new' %> -<%= link_to '[New list of users]', :action => 'new_list' %> diff --git a/app/views/user_admin/user_stat.html.haml b/app/views/user_admin/user_stat.html.haml --- a/app/views/user_admin/user_stat.html.haml +++ b/app/views/user_admin/user_stat.html.haml @@ -1,12 +1,12 @@ -- content_for :header do - = javascript_include_tag 'local_jquery' - = stylesheet_link_tag 'tablesorter-theme.cafe' +/- content_for :header do +/ = javascript_include_tag 'local_jquery' +/ = stylesheet_link_tag 'tablesorter-theme.cafe' %script{:type=>"text/javascript"} $(function () { $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} ); $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} ); - $('#my_table').tablesorter({widgets: ['zebra']}); + /$('#my_table').tablesorter({widgets: ['zebra']}); }); %h1 User grading results @@ -26,22 +26,22 @@ = link_to '[Show only latest submissions]', controller: :user_admin, action: :user_stat = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat_max, commit: 'download csv' -%table.tablesorter-cafe#my_table +%table.table.sortable.table-striped.table-bordered %thead %tr - %th User + %th Login %th Name %th Activated? - %th Logged in + %th Logged_in %th Contest(s) %th Remark - @problems.each do |p| - %th= p.name - %th Total - %th Passed + %th.text-right= p.name + %th.text-right Total + %th.text-right Passed %tbody - @scorearray.each do |sc| - %tr{class: cycle('info-even','info-odd')} + %tr - total,num_passed = 0,0 - sc.each_index do |i| - if i == 0 @@ -52,8 +52,10 @@ %td= sc[i].contests.collect {|c| c.name}.join(', ') %td= sc[i].remark - else - %td= sc[i][0] + %td.text-right= sc[i][0] - total += sc[i][0] - num_passed += 1 if sc[i][1] - %td= total - %td= num_passed + %td.text-right= total + %td.text-right= num_passed +:javascript + $.bootstrapSortable(true,'reversed') diff --git a/config/application.rb.SAMPLE b/config/application.rb.SAMPLE --- a/config/application.rb.SAMPLE +++ b/config/application.rb.SAMPLE @@ -59,7 +59,12 @@ # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' - config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js','graders.css','problems.css'] + config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] + %w( announcements configurations contests contest_management graders heartbeat + login main messages problems report site sites sources tasks + test user_admin users ).each do |controller| + config.assets.precompile += ["#{controller}.js", "#{controller}.css"] + end end end diff --git a/config/environments/development.rb b/config/environments/development.rb --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -36,5 +36,5 @@ config.assets.debug = true # Prevents assets from rendering twice - config.serve_static_assets = false + config.serve_static_assets = true end diff --git a/config/initializers/abstract_mysql2_adapter.rb.SAMPLE b/config/initializers/abstract_mysql2_adapter.rb.SAMPLE new file mode 100644 --- /dev/null +++ b/config/initializers/abstract_mysql2_adapter.rb.SAMPLE @@ -0,0 +1,3 @@ +class ActiveRecord::ConnectionAdapters::Mysql2Adapter + NATIVE_DATABASE_TYPES[:primary_key] = "int(11) auto_increment PRIMARY KEY" +end diff --git a/config/locales/en.yml b/config/locales/en.yml --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -19,7 +19,7 @@ messages: 'Messages' tasks: 'Tasks' submissions: 'Submissions' - test: 'Test Interface' + test: 'Test' hall_of_fame: 'Hall of Fame' help: 'Help' settings: 'Settings' diff --git a/config/routes.rb b/config/routes.rb --- a/config/routes.rb +++ b/config/routes.rb @@ -1,70 +1,59 @@ CafeGrader::Application.routes.draw do - get "report/login" + get "sources/direct_edit" + + root :to => 'main#login' resources :contests - resources :announcements resources :sites + resources :announcements do + member do + get 'toggle','toggle_front' + end + end + + resources :problems do + member do + get 'toggle' + get 'toggle_test' + end + collection do + get 'turn_all_off' + get 'turn_all_on' + get 'import' + get 'manage' + end + end + resources :grader_configuration, controller: 'configurations' - # The priority is based upon order of creation: - # first created -> highest priority. - - # Sample of regular route: - # match 'products/:id' => 'catalog#view' - # Keep in mind you can assign values other than :controller and :action - - # Sample of named route: - # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase - # This route can be invoked with purchase_url(:id => product.id) - - # Sample resource route (maps HTTP verbs to controller actions automatically): - # resources :products - - # Sample resource route with options: - # resources :products do - # member do - # get 'short' - # post 'toggle' - # end - # - # collection do - # get 'sold' - # end - # end + resources :users do + member do + get 'toggle_activate', 'toggle_enable' + end + end - # Sample resource route with sub-resources: - # resources :products do - # resources :comments, :sales - # resource :seller - # end + #source code edit + get 'sources/direct_edit/:pid', to: 'sources#direct_edit', as: 'direct_edit' + get 'sources/direct_edit_submission/:sid', to: 'sources#direct_edit_submission', as: 'direct_edit_submission' + get 'sources/get_latest_submission_status/:uid/:pid', to: 'sources#get_latest_submission_status', as: 'get_latest_submission_status' - # Sample resource route with more complex sub-resources - # resources :products do - # resources :comments - # resources :sales do - # get 'recent', :on => :collection - # end - # end - - # Sample resource route within a namespace: - # namespace :admin do - # # Directs /admin/products/* to Admin::ProductsController - # # (app/controllers/admin/products_controller.rb) - # resources :products - # end - - # You can have the root of your site routed with "root" - # just remember to delete public/index.html. - # root :to => 'welcome#index' - - root :to => 'main#login' match 'tasks/view/:file.:ext' => 'tasks#view' match 'tasks/download/:id/:file.:ext' => 'tasks#download' + match 'heartbeat/:id/edit' => 'heartbeat#edit' - match 'heartbeat/:id/edit' => 'heartbeat#edit' + #main + get "main/list" + get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission' + + #report + get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof' + get "report/login" + + #grader + get 'graders/list', to: 'graders#list', as: 'grader_list' # See how all your routes lay out with "rake routes" diff --git a/db/schema.rb b/db/schema.rb --- a/db/schema.rb +++ b/db/schema.rb @@ -15,12 +15,12 @@ create_table "announcements", :force => true do |t| t.string "author" - t.text "body", :limit => 16777215 + t.text "body" t.boolean "published" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.boolean "frontpage", :default => false - t.boolean "contest_only", :default => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "frontpage", :default => false + t.boolean "contest_only", :default => false t.string "title" t.string "notes" end @@ -50,19 +50,19 @@ end create_table "descriptions", :force => true do |t| - t.text "body", :limit => 16777215 + t.text "body" t.boolean "markdowned" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end create_table "grader_configurations", :force => true do |t| t.string "key" t.string "value_type" t.string "value" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.text "description", :limit => 16777215 + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "description" end create_table "grader_processes", :force => true do |t| @@ -107,10 +107,10 @@ t.integer "sender_id" t.integer "receiver_id" t.integer "replying_message_id" - t.text "body", :limit => 16777215 + t.text "body" t.boolean "replied" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end create_table "problems", :force => true do |t| @@ -152,7 +152,7 @@ create_table "sessions", :force => true do |t| t.string "session_id" - t.text "data", :limit => 16777215 + t.text "data" t.datetime "updated_at" end @@ -180,14 +180,14 @@ t.integer "user_id" t.integer "problem_id" t.integer "language_id" - t.text "source", :limit => 16777215 + t.text "source" t.binary "binary" t.datetime "submitted_at" t.datetime "compiled_at" - t.text "compiler_message", :limit => 16777215 + t.text "compiler_message" t.datetime "graded_at" t.integer "points" - t.text "grader_comment", :limit => 16777215 + t.text "grader_comment" t.integer "number" t.string "source_filename" t.float "max_runtime" @@ -208,10 +208,10 @@ create_table "test_pairs", :force => true do |t| t.integer "problem_id" - t.text "input", :limit => 2147483647 - t.text "solution", :limit => 2147483647 - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.text "input", :limit => 16777215 + t.text "solution", :limit => 16777215 + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end create_table "test_requests", :force => true do |t| @@ -222,13 +222,13 @@ t.string "output_file_name" t.string "running_stat" t.integer "status" - t.datetime "updated_at", :null => false + t.datetime "updated_at", :null => false t.datetime "submitted_at" t.datetime "compiled_at" - t.text "compiler_message", :limit => 16777215 + t.text "compiler_message" t.datetime "graded_at" t.string "grader_comment" - t.datetime "created_at", :null => false + t.datetime "created_at", :null => false t.float "running_time" t.string "exit_status" t.integer "memory_usage" @@ -256,7 +256,6 @@ t.boolean "activated", :default => false t.datetime "created_at" t.datetime "updated_at" - t.string "section" t.boolean "enabled", :default => true t.string "remark" t.string "last_ip" diff --git a/spec/controllers/sources_controller_spec.rb b/spec/controllers/sources_controller_spec.rb new file mode 100644 --- /dev/null +++ b/spec/controllers/sources_controller_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe SourcesController do + + describe "GET 'direct_edit'" do + it "returns http success" do + get 'direct_edit' + response.should be_success + end + end + +end diff --git a/spec/helpers/sources_helper_spec.rb b/spec/helpers/sources_helper_spec.rb new file mode 100644 --- /dev/null +++ b/spec/helpers/sources_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the SourcesHelper. For example: +# +# describe SourcesHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +describe SourcesHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/sources/direct_edit.html.haml_spec.rb b/spec/views/sources/direct_edit.html.haml_spec.rb new file mode 100644 --- /dev/null +++ b/spec/views/sources/direct_edit.html.haml_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe "sources/direct_edit.html.haml" do + pending "add some examples to (or delete) #{__FILE__}" +end