diff --git a/Gemfile b/Gemfile --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '3.2.8' +gem 'rails', '3.2.19' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' @@ -35,6 +35,12 @@ # To use debugger # gem 'debugger' +# + +gem 'jquery-rails' +gem 'jquery-ui-sass-rails' +gem 'jquery-timepicker-addon-rails' + gem "haml" gem "mail" diff --git a/Gemfile.lock b/Gemfile.lock --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,130 +1,146 @@ GIT remote: git://github.com/sikachu/verification.git - revision: 344ad2535da3dc9671872628ff9c79d6f59af9da + revision: 76eaf51b13276ecae54bd9cd115832595d2ff56d specs: verification (1.0.3) - actionpack (>= 3.0.0, < 3.3.0) - activesupport (>= 3.0.0, < 3.3.0) + actionpack (>= 3.0.0, < 5.0) + activesupport (>= 3.0.0, < 5.0) GEM remote: https://rubygems.org/ specs: - actionmailer (3.2.8) - actionpack (= 3.2.8) - mail (~> 2.4.4) - actionpack (3.2.8) - activemodel (= 3.2.8) - activesupport (= 3.2.8) + actionmailer (3.2.19) + actionpack (= 3.2.19) + mail (~> 2.5.4) + actionpack (3.2.19) + activemodel (= 3.2.19) + activesupport (= 3.2.19) builder (~> 3.0.0) erubis (~> 2.7.0) journey (~> 1.0.4) - rack (~> 1.4.0) + rack (~> 1.4.5) rack-cache (~> 1.2) rack-test (~> 0.6.1) - sprockets (~> 2.1.3) - activemodel (3.2.8) - activesupport (= 3.2.8) + sprockets (~> 2.2.1) + activemodel (3.2.19) + activesupport (= 3.2.19) builder (~> 3.0.0) - activerecord (3.2.8) - activemodel (= 3.2.8) - activesupport (= 3.2.8) + activerecord (3.2.19) + activemodel (= 3.2.19) + activesupport (= 3.2.19) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.2.8) - activemodel (= 3.2.8) - activesupport (= 3.2.8) - activesupport (3.2.8) - i18n (~> 0.6) + activeresource (3.2.19) + activemodel (= 3.2.19) + activesupport (= 3.2.19) + activesupport (3.2.19) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - arel (3.0.2) - builder (3.0.3) + arel (3.0.3) + builder (3.0.4) coffee-rails (3.2.2) coffee-script (>= 2.2.0) railties (~> 3.2.0) - coffee-script (2.2.0) + coffee-script (2.3.0) coffee-script-source execjs - coffee-script-source (1.3.3) - diff-lcs (1.1.3) + coffee-script-source (1.7.1) + diff-lcs (1.2.5) dynamic_form (1.1.4) erubis (2.7.0) - execjs (1.4.0) - multi_json (~> 1.0) - haml (3.1.7) - hike (1.2.1) - i18n (0.6.1) + execjs (2.2.1) + haml (4.0.5) + tilt + hike (1.2.3) + i18n (0.6.11) in_place_editing (1.2.0) journey (1.0.4) - json (1.7.5) - mail (2.4.4) - i18n (>= 0.4.0) + jquery-rails (3.1.1) + railties (>= 3.0, < 5.0) + thor (>= 0.14, < 2.0) + jquery-timepicker-addon-rails (1.4.1) + railties (>= 3.1) + jquery-ui-rails (4.0.3) + jquery-rails + railties (>= 3.1.0) + jquery-ui-sass-rails (4.0.3.0) + jquery-rails + jquery-ui-rails (= 4.0.3) + railties (>= 3.1.0) + json (1.8.1) + mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.19) - multi_json (1.3.6) - mysql2 (0.3.11) - polyglot (0.3.3) + mime-types (1.25.1) + multi_json (1.10.1) + mysql2 (0.3.16) + polyglot (0.3.5) + power_assert (0.1.3) prototype-rails (3.2.1) rails (~> 3.2) - rack (1.4.1) + rack (1.4.5) rack-cache (1.2) rack (>= 0.4) - rack-ssl (1.3.2) + rack-ssl (1.3.4) rack rack-test (0.6.2) rack (>= 1.0) - rails (3.2.8) - actionmailer (= 3.2.8) - actionpack (= 3.2.8) - activerecord (= 3.2.8) - activeresource (= 3.2.8) - activesupport (= 3.2.8) + rails (3.2.19) + actionmailer (= 3.2.19) + actionpack (= 3.2.19) + activerecord (= 3.2.19) + activeresource (= 3.2.19) + activesupport (= 3.2.19) bundler (~> 1.0) - railties (= 3.2.8) - railties (3.2.8) - actionpack (= 3.2.8) - activesupport (= 3.2.8) + railties (= 3.2.19) + railties (3.2.19) + actionpack (= 3.2.19) + activesupport (= 3.2.19) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - rake (0.9.2.2) - rdiscount (1.6.8) - rdoc (3.12) + rake (10.3.2) + rdiscount (2.1.7.1) + rdoc (3.12.2) json (~> 1.4) - rspec (2.11.0) - rspec-core (~> 2.11.0) - rspec-expectations (~> 2.11.0) - rspec-mocks (~> 2.11.0) - rspec-core (2.11.1) - rspec-expectations (2.11.3) - diff-lcs (~> 1.1.3) - rspec-mocks (2.11.3) - rspec-rails (2.11.0) + rspec-collection_matchers (1.0.0) + rspec-expectations (>= 2.99.0.beta1) + rspec-core (2.99.2) + rspec-expectations (2.99.2) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.99.2) + rspec-rails (2.99.0) actionpack (>= 3.0) + activemodel (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec (~> 2.11.0) - sass (3.2.1) - sass-rails (3.2.5) + rspec-collection_matchers + rspec-core (~> 2.99.0) + rspec-expectations (~> 2.99.0) + rspec-mocks (~> 2.99.0) + sass (3.4.1) + sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - sprockets (2.1.3) + sprockets (2.2.2) hike (~> 1.2) + multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - test-unit (2.5.2) - thor (0.16.0) - tilt (1.3.3) - treetop (1.4.10) + test-unit (3.0.1) + power_assert + thor (0.19.1) + tilt (1.4.1) + treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.33) - uglifier (1.3.0) + tzinfo (0.3.41) + uglifier (2.5.3) execjs (>= 0.3.0) - multi_json (~> 1.0, >= 1.0.2) - will_paginate (3.0.3) + json (>= 1.8.0) + will_paginate (3.0.7) PLATFORMS ruby @@ -134,10 +150,13 @@ dynamic_form haml in_place_editing + jquery-rails + jquery-timepicker-addon-rails + jquery-ui-sass-rails mail mysql2 prototype-rails - rails (= 3.2.8) + rails (= 3.2.19) rdiscount rspec-rails (~> 2.0) sass-rails (~> 3.2.3) diff --git a/app/assets/javascripts/new.js b/app/assets/javascripts/new.js new file mode 100644 --- /dev/null +++ b/app/assets/javascripts/new.js @@ -0,0 +1,6 @@ +//= require jquery +//= require jquery_ujs +//= require jquery.ui.all +//= require jquery.ui.datepicker +//= require jquery.ui.slider +//= require jquery-ui-timepicker-addon diff --git a/app/assets/javascripts/report.js.coffee b/app/assets/javascripts/report.js.coffee new file mode 100644 --- /dev/null +++ b/app/assets/javascripts/report.js.coffee @@ -0,0 +1,3 @@ +# 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/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,3 +1,11 @@ + +@import jquery.ui.core +@import jquery.ui.theme +@import jquery.ui.datepicker +@import jquery.ui.slider +@import jquery-ui-timepicker-addon + + body background: white image-url("topbg.jpg") repeat-x top center font-size: 13px @@ -290,4 +298,4 @@ h2.contest-title margin-top: 5px - margin-bottom: 5px \ No newline at end of file + margin-bottom: 5px diff --git a/app/assets/stylesheets/report.css.scss b/app/assets/stylesheets/report.css.scss new file mode 100644 --- /dev/null +++ b/app/assets/stylesheets/report.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the report controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -22,6 +22,9 @@ end end + #save login information + Login.create(user_id: user.id, ip_address: request.remote_ip) + redirect_to :controller => 'main', :action => 'list' else flash[:notice] = 'Wrong password' 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 @@ -13,6 +13,7 @@ append_to menu_items, '[Problems]', 'problems', 'index' append_to menu_items, '[Users]', 'user_admin', 'index' append_to menu_items, '[Results]', 'user_admin', 'user_stat' + append_to menu_items, '[Report]', 'report', 'login_stat' append_to menu_items, '[Graders]', 'graders', 'list' append_to menu_items, '[Contests]', 'contest_management', 'index' append_to menu_items, '[Sites]', 'sites', 'index' diff --git a/app/helpers/report_helper.rb b/app/helpers/report_helper.rb new file mode 100644 --- /dev/null +++ b/app/helpers/report_helper.rb @@ -0,0 +1,2 @@ +module ReportHelper +end diff --git a/app/models/login.rb b/app/models/login.rb new file mode 100644 --- /dev/null +++ b/app/models/login.rb @@ -0,0 +1,3 @@ +class Login < ActiveRecord::Base + attr_accessible :ip_address, :logged_in_at, :user_id +end diff --git a/app/models/problem.rb b/app/models/problem.rb --- a/app/models/problem.rb +++ b/app/models/problem.rb @@ -14,7 +14,7 @@ DEFAULT_MEMORY_LIMIT = 32 def self.find_available_problems - Problem.available.all(:order => "date_added DESC") + Problem.available.all(:order => "date_added DESC, name ASC") end def self.create_from_import_form_params(params, old_problem=nil) @@ -43,6 +43,7 @@ if not importer.import_from_file(import_params[:file], import_params[:time_limit], import_params[:memory_limit], + import_params[:checker_name], import_to_db) problem.errors.add_to_base('Import error.') end @@ -90,6 +91,11 @@ problem.errors.add_to_base('No testdata file.') end + checker_name = 'text' + if ['text','float'].include? params[:checker] + checker_name = params[:checker] + end + file = params[:file] if !problem.errors.empty? @@ -106,7 +112,8 @@ return [{ :time_limit => time_limit, :memory_limit => memory_limit, - :file => file + :file => file, + :checker_name => checker_name }, problem] end diff --git a/app/models/user.rb b/app/models/user.rb --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,7 @@ require 'digest/sha1' require 'net/pop' +require 'net/https' +require 'net/http' require 'json' class User < ActiveRecord::Base @@ -81,7 +83,7 @@ end end - def authenticated_by_pop3?(password) + def authenticated_by_pop3?(password) Net::POP3.enable_ssl pop = Net::POP3.new('pops.it.chula.ac.th') authen = true @@ -107,8 +109,15 @@ #simple call begin - resp = Net::HTTP.post_form(url, post_args) - result = JSON.parse resp.body + http = Net::HTTP.new('www.cas.chula.ac.th', 443) + http.use_ssl = true + result = [ ] + http.start do |http| + req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate') + param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}" + resp = http.request(req,param) + result = JSON.parse resp.body + end return true if result["type"] == "beanStudent" rescue return false diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -5,7 +5,9 @@ <%= stylesheet_link_tag "application", :media => "all" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> + <%= content_for :header %> <%= yield :head %> + diff --git a/app/views/main/_problem.html.erb b/app/views/main/_problem.html.erb --- a/app/views/main/_problem.html.erb +++ b/app/views/main/_problem.html.erb @@ -3,7 +3,7 @@ <%= "#{problem_counter+1}" %> - <%= "#{problem.full_name} (#{problem.name})" %> + <%= "(#{problem.name}) #{problem.full_name}" %> <%= link_to_description_if_any "[#{t 'main.problem_desc'}]", problem %> diff --git a/app/views/problems/import.html.haml b/app/views/problems/import.html.haml --- a/app/views/problems/import.html.haml +++ b/app/views/problems/import.html.haml @@ -32,6 +32,19 @@ %br/ You may put task description in *.html for raw html and *.md or *.markdown for markdown. + %br/ + You may also put a pdf file for the task description + %tr + %td Checker: + %td= select_tag 'checker', options_for_select([['Text checker','text'],['Float checker','float']], 'text') + %tr + %td + %td + %span{:class => 'help'} + "Text" checker checks if the text (including numbers) is the same, ignoring any whitespace + %br/ + "Float" checker checks if all numbers is within EPSILON error using formula |a-b| < EPSILON * max(|a|,|b|) + - if @allow_test_pair_import %tr %td diff --git a/app/views/report/_date_range.html.haml b/app/views/report/_date_range.html.haml new file mode 100644 --- /dev/null +++ b/app/views/report/_date_range.html.haml @@ -0,0 +1,23 @@ + += form_tag({session: :url }) do + .submitbox + %table + %tr + %td{colspan: 6, style: 'font-weight: bold'}= title + %tr + %td{style: 'width: 120px; font-weight: bold'}= param_text + %td{align: 'right'} since: + %td= text_field_tag 'since_datetime' + %tr + %td + %td{align: 'right'} until: + %td= text_field_tag 'until_datetime' + %tr + %td + %td + %td Blank mean no condition + %tr + %td + %td + %td= submit_tag 'query' + diff --git a/app/views/report/login_stat.html.haml b/app/views/report/login_stat.html.haml new file mode 100644 --- /dev/null +++ b/app/views/report/login_stat.html.haml @@ -0,0 +1,33 @@ +- content_for :header do + = javascript_include_tag 'new' + +%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"} ); + }); + + +%h1 Login status + + +=render partial: 'report_menu' +=render partial: 'date_range', locals: {param_text: 'Login date range:', title: 'Query login stat in the range' } + +%table.info + %thead + %tr.info-head + %th login + %th full name + %th login count + %th earliest + %th latest + %tbody + - @logins.each do |l| + %tr{class: cycle('info-even','info-odd')} + %td= l[:login] + %td= l[:full_name] + %td= l[:count] + %td= l[:min] ? l[:min].in_time_zone.strftime('%Y-%m-%d %H:%M') : '' + %td= l[:max] ? l[:max].in_time_zone.strftime('%Y-%m-%d %H:%M') : '' + diff --git a/app/views/report/submission_stat.html.haml b/app/views/report/submission_stat.html.haml new file mode 100644 --- /dev/null +++ b/app/views/report/submission_stat.html.haml @@ -0,0 +1,41 @@ +- content_for :header do + = javascript_include_tag 'new' + +%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"} ); + }); + + +%h1 Login status + + + + +=render partial: 'report_menu' +=render partial: 'date_range', locals: {param_text: 'Submission date range:', title: 'Query submission stat in the range' } + +%table.info + %thead + %tr.info-head + %th login + %th full name + %th total submissions + %th submissions + %tbody + - @submissions.each do |user_id,data| + %tr{class: cycle('info-even','info-odd')} + %td= data[:login] + %td= data[:full_name] + %td= data[:count] + %td + - data[:sub].each do |prob_id,sub_data| + = "#{sub_data[:prob_name]}: [" + - st = [] + - sub_data[:sub_ids].each do |id| + - st << link_to(id, controller: 'graders' , action: 'submission', id: id) + = raw st.join ', ' + = ']' + %br/ + diff --git a/config/routes.rb b/config/routes.rb --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ CafeGrader::Application.routes.draw do + get "report/login" + resources :contests resources :announcements diff --git a/db/migrate/20140826095949_create_logins.rb b/db/migrate/20140826095949_create_logins.rb new file mode 100644 --- /dev/null +++ b/db/migrate/20140826095949_create_logins.rb @@ -0,0 +1,10 @@ +class CreateLogins < ActiveRecord::Migration + def change + create_table :logins do |t| + t.string :user_id + t.string :ip_address + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20140823031747) do +ActiveRecord::Schema.define(:version => 20140826095949) do create_table "announcements", :force => true do |t| t.string "author" @@ -86,6 +86,13 @@ t.string "common_ext" end + create_table "logins", :force => true do |t| + t.string "user_id" + t.string "ip_address" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "messages", :force => true do |t| t.integer "sender_id" t.integer "receiver_id" diff --git a/lib/grader_script.rb b/lib/grader_script.rb --- a/lib/grader_script.rb +++ b/lib/grader_script.rb @@ -50,7 +50,7 @@ Dir.chdir(cur_dir) - return output + return "import CMD: #{cmd}\n" + output end return '' end diff --git a/lib/testdata_importer.rb b/lib/testdata_importer.rb --- a/lib/testdata_importer.rb +++ b/lib/testdata_importer.rb @@ -11,6 +11,7 @@ def import_from_file(tempfile, time_limit, memory_limit, + checker_name='text', import_to_db=false) dirname = extract(tempfile) @@ -19,7 +20,8 @@ @log_msg = GraderScript.call_import_problem(@problem.name, dirname, time_limit, - memory_limit) + memory_limit, + checker_name) else # Import test data to test pairs. diff --git a/spec/models/login_spec.rb b/spec/models/login_spec.rb new file mode 100644 --- /dev/null +++ b/spec/models/login_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Login do + pending "add some examples to (or delete) #{__FILE__}" +end