Description:
test interface upload
git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@81 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r36:a3144ce1a174 - - 13 files changed: 269 inserted, 20 deleted
@@ -0,0 +1,64 | |||||
|
|
1 | + require 'fileutils' | ||
|
|
2 | + | ||
|
|
3 | + class TestRequest < Task | ||
|
|
4 | + | ||
|
|
5 | + set_table_name "test_requests" | ||
|
|
6 | + | ||
|
|
7 | + belongs_to :user | ||
|
|
8 | + belongs_to :problem | ||
|
|
9 | + belongs_to :submission | ||
|
|
10 | + | ||
|
|
11 | + def self.get_inqueue_and_change_status(status) | ||
|
|
12 | + # since there will be only one grader grading TestRequest | ||
|
|
13 | + # we do not need locking (hopefully) | ||
|
|
14 | + | ||
|
|
15 | + task = Task.find(:first, | ||
|
|
16 | + :order => "created_at", | ||
|
|
17 | + :conditions => {:status=> Task::STATUS_INQUEUE}) | ||
|
|
18 | + if task!=nil | ||
|
|
19 | + task.status = status | ||
|
|
20 | + task.save! | ||
|
|
21 | + end | ||
|
|
22 | + | ||
|
|
23 | + task | ||
|
|
24 | + end | ||
|
|
25 | + | ||
|
|
26 | + # interfacing with form | ||
|
|
27 | + def self.new_from_form_params(user,params) | ||
|
|
28 | + test_request = TestRequest.new | ||
|
|
29 | + test_request.user = user | ||
|
|
30 | + problem = Problem.find(params[:problem_id]) | ||
|
|
31 | + test_request.problem = problem | ||
|
|
32 | + test_request.submission = | ||
|
|
33 | + Submission.find_by_user_problem_number(user.id, | ||
|
|
34 | + problem.id, | ||
|
|
35 | + params[:submission_number]) | ||
|
|
36 | + test_request.input_file_name = save_input_file(params[:input_file], user, problem) | ||
|
|
37 | + test_request.submitted_at = Time.new | ||
|
|
38 | + test_request.status_inqueue | ||
|
|
39 | + test_request | ||
|
|
40 | + end | ||
|
|
41 | + | ||
|
|
42 | + protected | ||
|
|
43 | + def self.input_file_name(user,problem) | ||
|
|
44 | + begin | ||
|
|
45 | + tmpname = UPLOADED_INPUT_FILE_DIR + "/#{user.login}/#{problem.name}/#{rand(10000)}" | ||
|
|
46 | + end while File.exists?(tmpname) | ||
|
|
47 | + tmpname | ||
|
|
48 | + end | ||
|
|
49 | + | ||
|
|
50 | + def self.save_input_file(tempfile, user, problem) | ||
|
|
51 | + new_file_name = input_file_name(user,problem) | ||
|
|
52 | + dirname = File.dirname(new_file_name) | ||
|
|
53 | + FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname) | ||
|
|
54 | + if tempfile.instance_of?(Tempfile) | ||
|
|
55 | + tempfile.close | ||
|
|
56 | + FileUtils.move(tempfile.path,new_file_name) | ||
|
|
57 | + else | ||
|
|
58 | + File.open(new_file_name, "wb") do |f| | ||
|
|
59 | + f.write(tempfile.read) | ||
|
|
60 | + end | ||
|
|
61 | + end | ||
|
|
62 | + new_file_name | ||
|
|
63 | + end | ||
|
|
64 | + end |
@@ -0,0 +1,5 | |||||
|
|
1 | + %tr.test-request | ||
|
|
2 | + %td= test_request_counter +1 | ||
|
|
3 | + %td= test_request.problem.full_name | ||
|
|
4 | + %td= test_request.submission_id | ||
|
|
5 | + %td= test_request.status_str |
@@ -0,0 +1,67 | |||||
|
|
1 | + <h2>Test Interface</h2> | ||
|
|
2 | + | ||
|
|
3 | + <% if @problems.length==0 %> | ||
|
|
4 | + "There is no submission" | ||
|
|
5 | + <% return %> | ||
|
|
6 | + <% end %> | ||
|
|
7 | + | ||
|
|
8 | + <script type="text/javascript"> | ||
|
|
9 | + var submissionCount = { | ||
|
|
10 | + <% @submissions.each do |submission| %> | ||
|
|
11 | + <%= submission.problem_id %> : <%= submission.number %>, | ||
|
|
12 | + <% end %> | ||
|
|
13 | + }; | ||
|
|
14 | + function updateSubmissionList() { | ||
|
|
15 | + currentProb = document.getElementById("test_request_problem_id").value; | ||
|
|
16 | + count = submissionCount[currentProb]; | ||
|
|
17 | + submissionSelect = document.getElementById("test_request_submission_number"); | ||
|
|
18 | + submissionSelect.options.length = 0; | ||
|
|
19 | + for(i=0; i<count; i++) { | ||
|
|
20 | + submissionSelect.options[i] = new Option(""+(i+1),""+(i+1),false,false); | ||
|
|
21 | + } | ||
|
|
22 | + } | ||
|
|
23 | + </script> | ||
|
|
24 | + | ||
|
|
25 | + <% form_for :test_request, nil, | ||
|
|
26 | + :url => { :action => 'test_submit'}, | ||
|
|
27 | + :html => { :multipart => true } do |f| %> | ||
|
|
28 | + <table> | ||
|
|
29 | + <tr> | ||
|
|
30 | + <td>Task:</td> | ||
|
|
31 | + <td> | ||
|
|
32 | + <%= select(:test_request, | ||
|
|
33 | + :problem_id, | ||
|
|
34 | + @problems.collect {|p| [p.name, p.id]}, {}, | ||
|
|
35 | + { :onclick => "updateSubmissionList();" }) %> | ||
|
|
36 | + </td> | ||
|
|
37 | + </tr> | ||
|
|
38 | + <tr> | ||
|
|
39 | + <td>Submission:</td> | ||
|
|
40 | + <td> | ||
|
|
41 | + <%= select(:test_request, | ||
|
|
42 | + :submission_number, | ||
|
|
43 | + (1..@submissions[0].number).collect {|n| [n,n]}) %> | ||
|
|
44 | + </td> | ||
|
|
45 | + </tr> | ||
|
|
46 | + <tr> | ||
|
|
47 | + <td>Input data:</td> | ||
|
|
48 | + <td><%= f.file_field :input_file %></td> | ||
|
|
49 | + <tr> | ||
|
|
50 | + <td colspan="2"> | ||
|
|
51 | + <%= submit_tag 'submit' %> | ||
|
|
52 | + </td> | ||
|
|
53 | + </tr> | ||
|
|
54 | + </table> | ||
|
|
55 | + <% end %> | ||
|
|
56 | + | ||
|
|
57 | + <h3>Previous requests</h3> | ||
|
|
58 | + | ||
|
|
59 | + <table border="1"> | ||
|
|
60 | + <tr> | ||
|
|
61 | + <th></td> | ||
|
|
62 | + <th>problem</th> | ||
|
|
63 | + <th>#</th> | ||
|
|
64 | + <th>status</th> | ||
|
|
65 | + </tr> | ||
|
|
66 | + <%= render :partial => 'test_request', :collection => @user.test_requests %> | ||
|
|
67 | + </table> |
@@ -0,0 +1,30 | |||||
|
|
1 | + class CreateTestRequests < ActiveRecord::Migration | ||
|
|
2 | + def self.up | ||
|
|
3 | + create_table :test_requests do |t| | ||
|
|
4 | + t.column :user_id, :integer | ||
|
|
5 | + t.column :problem_id, :integer | ||
|
|
6 | + t.column :submission_id, :integer | ||
|
|
7 | + t.column :input_file_name, :string | ||
|
|
8 | + t.column :output_file_name, :string | ||
|
|
9 | + t.column :running_stat, :string | ||
|
|
10 | + | ||
|
|
11 | + # these are similar to tasks | ||
|
|
12 | + t.column :status, :integer | ||
|
|
13 | + t.column :updated_at, :datetime | ||
|
|
14 | + | ||
|
|
15 | + # these are intentionally similar to submissions | ||
|
|
16 | + t.column :submitted_at, :datetime | ||
|
|
17 | + t.column :compiled_at, :datetime | ||
|
|
18 | + t.column :compiler_message, :string | ||
|
|
19 | + t.column :graded_at, :datetime | ||
|
|
20 | + t.column :grader_comment, :string | ||
|
|
21 | + t.timestamps | ||
|
|
22 | + end | ||
|
|
23 | + add_index :test_requests, [:user_id, :problem_id] | ||
|
|
24 | + end | ||
|
|
25 | + | ||
|
|
26 | + def self.down | ||
|
|
27 | + remove_index :test_requests, :column => [:user_id, :problem_id] | ||
|
|
28 | + drop_table :test_requests | ||
|
|
29 | + end | ||
|
|
30 | + end |
@@ -0,0 +1,7 | |||||
|
|
1 | + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html | ||
|
|
2 | + | ||
|
|
3 | + # one: | ||
|
|
4 | + # column: value | ||
|
|
5 | + # | ||
|
|
6 | + # two: | ||
|
|
7 | + # column: value |
@@ -0,0 +1,8 | |||||
|
|
1 | + require File.dirname(__FILE__) + '/../test_helper' | ||
|
|
2 | + | ||
|
|
3 | + class TestRequestTest < ActiveSupport::TestCase | ||
|
|
4 | + # Replace this with your real tests. | ||
|
|
5 | + def test_truth | ||
|
|
6 | + assert true | ||
|
|
7 | + end | ||
|
|
8 | + end |
@@ -48,12 +48,29 | |||||
|
48 | :type => 'text/plain'}) |
|
48 | :type => 'text/plain'}) |
|
49 | else |
|
49 | else |
|
50 | flash[:notice] = 'Error viewing source' |
|
50 | flash[:notice] = 'Error viewing source' |
|
51 | end |
|
51 | end |
|
52 | end |
|
52 | end |
|
53 |
|
53 | ||
|
|
54 | + def test | ||
|
|
55 | + @user = User.find(session[:user_id]) | ||
|
|
56 | + @submissions = Submission.find_last_for_all_available_problems(@user.id) | ||
|
|
57 | + @problems = @submissions.collect { |submission| submission.problem } | ||
|
|
58 | + end | ||
|
|
59 | + | ||
|
|
60 | + def test_submit | ||
|
|
61 | + @user = User.find(session[:user_id]) | ||
|
|
62 | + test_request = TestRequest.new_from_form_params(@user,params[:test_request]) | ||
|
|
63 | + if test_request.save | ||
|
|
64 | + redirect_to :action => 'test' | ||
|
|
65 | + else | ||
|
|
66 | + flash[:notice] = 'Error saving your test submission' | ||
|
|
67 | + render :action => 'test' | ||
|
|
68 | + end | ||
|
|
69 | + end | ||
|
|
70 | + | ||
|
54 | protected |
|
71 | protected |
|
55 | def prepare_list_information |
|
72 | def prepare_list_information |
|
56 | @problems = Problem.find_available_problems |
|
73 | @problems = Problem.find_available_problems |
|
57 | @prob_submissions = Array.new |
|
74 | @prob_submissions = Array.new |
|
58 | @user = User.find(session[:user_id]) |
|
75 | @user = User.find(session[:user_id]) |
|
59 | @problems.each do |p| |
|
76 | @problems.each do |p| |
@@ -1,35 +1,33 | |||||
|
1 | # Methods added to this helper will be available to all templates in the application. |
|
1 | # Methods added to this helper will be available to all templates in the application. |
|
2 | module ApplicationHelper |
|
2 | module ApplicationHelper |
|
3 |
|
3 | ||
|
4 | def user_header |
|
4 | def user_header |
|
5 |
- |
|
5 | + menu_items = '' |
|
6 | user = User.find(session[:user_id]) |
|
6 | user = User.find(session[:user_id]) |
|
7 |
|
7 | ||
|
8 | # main page |
|
8 | # main page |
|
9 | - options += link_to_unless_current '[Main]', |
|
9 | + append_to menu_items, '[Main]', 'main', 'list' |
|
10 | - :controller => 'main', :action => 'list' |
|
10 | + append_to menu_items, '[Test]', 'main', 'test' |
|
11 | - options += ' ' |
|
||
|
12 |
|
11 | ||
|
13 | # admin menu |
|
12 | # admin menu |
|
14 | if (user!=nil) and (user.admin?) |
|
13 | if (user!=nil) and (user.admin?) |
|
15 | - options += |
|
14 | + append_to menu_items, '[Problem admin]', 'problems', 'index' |
|
16 | - (link_to_unless_current '[Problem admin]', |
|
15 | + append_to menu_items, '[User admin]', 'user_admin', 'index' |
|
17 | - :controller => 'problems', :action => 'index') + ' ' |
|
16 | + append_to menu_items, '[User stat]', 'user_admin', 'user_stat' |
|
18 | - options += |
|
||
|
19 | - (link_to_unless_current '[User admin]', |
|
||
|
20 | - :controller => 'user_admin', :action => 'index') + ' ' |
|
||
|
21 | - options += |
|
||
|
22 | - (link_to_unless_current '[User stat]', |
|
||
|
23 | - :controller => 'user_admin', :action => 'user_stat') + ' ' |
|
||
|
24 | end |
|
17 | end |
|
25 |
|
18 | ||
|
26 | # general options |
|
19 | # general options |
|
27 | - options += link_to_unless_current '[Settings]', |
|
20 | + append_to menu_items, '[Settings]', 'users', 'index' |
|
28 | - :controller => 'users', :action => 'index' |
|
21 | + append_to menu_items, '[Log out]', 'main', 'login' |
|
29 | - options += ' ' |
|
22 | + |
|
30 | - options += |
|
23 | + menu_items |
|
31 | - link_to('[Log out]', {:controller => 'main', :action => 'login'}) |
|
24 | + end |
|
32 | - options |
|
25 | + |
|
|
26 | + def append_to(option,label, controller, action) | ||
|
|
27 | + option << ' ' if option!='' | ||
|
|
28 | + option << link_to_unless_current(label, | ||
|
|
29 | + :controller => controller, | ||
|
|
30 | + :action => action) | ||
|
33 | end |
|
31 | end |
|
34 |
|
32 | ||
|
35 | end |
|
33 | end |
@@ -27,12 +27,31 | |||||
|
27 | "(SELECT MAX(id) FROM submissions AS subs " + |
|
27 | "(SELECT MAX(id) FROM submissions AS subs " + |
|
28 | "WHERE subs.user_id = submissions.user_id AND " + |
|
28 | "WHERE subs.user_id = submissions.user_id AND " + |
|
29 | "problem_id = " + problem_id.to_s + " " + |
|
29 | "problem_id = " + problem_id.to_s + " " + |
|
30 | "GROUP BY user_id)") |
|
30 | "GROUP BY user_id)") |
|
31 | end |
|
31 | end |
|
32 |
|
32 | ||
|
|
33 | + def self.find_last_for_all_available_problems(user_id) | ||
|
|
34 | + submissions = Array.new | ||
|
|
35 | + problems = Problem.find_available_problems | ||
|
|
36 | + problems.each do |problem| | ||
|
|
37 | + sub = Submission.find_last_by_user_and_problem(user_id, problem.id) | ||
|
|
38 | + submissions << sub if sub!=nil | ||
|
|
39 | + end | ||
|
|
40 | + submissions | ||
|
|
41 | + end | ||
|
|
42 | + | ||
|
|
43 | + def self.find_by_user_problem_number(user_id, problem_id, number) | ||
|
|
44 | + Submission.find(:first, | ||
|
|
45 | + :conditions => { | ||
|
|
46 | + :user_id => user_id, | ||
|
|
47 | + :problem_id => problem_id, | ||
|
|
48 | + :number => number | ||
|
|
49 | + }) | ||
|
|
50 | + end | ||
|
|
51 | + | ||
|
33 | protected |
|
52 | protected |
|
34 |
|
53 | ||
|
35 | def self.find_option_in_source(option, source) |
|
54 | def self.find_option_in_source(option, source) |
|
36 | if source==nil |
|
55 | if source==nil |
|
37 | return nil |
|
56 | return nil |
|
38 | end |
|
57 | end |
@@ -28,12 +28,23 | |||||
|
28 |
|
28 | ||
|
29 | def status_complete! |
|
29 | def status_complete! |
|
30 | status_complete |
|
30 | status_complete |
|
31 | self.save |
|
31 | self.save |
|
32 | end |
|
32 | end |
|
33 |
|
33 | ||
|
|
34 | + def status_str | ||
|
|
35 | + case self.status | ||
|
|
36 | + when Task::STATUS_INQUEUE | ||
|
|
37 | + "inqueue" | ||
|
|
38 | + when Task::STATUS_GRADING | ||
|
|
39 | + "grading" | ||
|
|
40 | + when Task::STATUS_COMPLETE | ||
|
|
41 | + "complete" | ||
|
|
42 | + end | ||
|
|
43 | + end | ||
|
|
44 | + | ||
|
34 | def self.get_inqueue_and_change_status(status) |
|
45 | def self.get_inqueue_and_change_status(status) |
|
35 | task = nil |
|
46 | task = nil |
|
36 | begin |
|
47 | begin |
|
37 | Task.transaction do |
|
48 | Task.transaction do |
|
38 | task = Task.find(:first, |
|
49 | task = Task.find(:first, |
|
39 | :order => "created_at", |
|
50 | :order => "created_at", |
@@ -1,12 +1,14 | |||||
|
1 | require 'digest/sha1' |
|
1 | require 'digest/sha1' |
|
2 |
|
2 | ||
|
3 | class User < ActiveRecord::Base |
|
3 | class User < ActiveRecord::Base |
|
4 |
|
4 | ||
|
5 | has_and_belongs_to_many :roles |
|
5 | has_and_belongs_to_many :roles |
|
6 |
|
6 | ||
|
|
7 | + has_many :test_requests, :order => "problem_id" | ||
|
|
8 | + | ||
|
7 | validates_presence_of :login |
|
9 | validates_presence_of :login |
|
8 | validates_presence_of :full_name |
|
10 | validates_presence_of :full_name |
|
9 | validates_length_of :full_name, :minimum => 1 |
|
11 | validates_length_of :full_name, :minimum => 1 |
|
10 |
|
12 | ||
|
11 | validates_presence_of :password, :if => :password_required? |
|
13 | validates_presence_of :password, :if => :password_required? |
|
12 | validates_length_of :password, :within => 4..20, :if => :password_required? |
|
14 | validates_length_of :password, :within => 4..20, :if => :password_required? |
@@ -55,6 +55,8 | |||||
|
55 |
|
55 | ||
|
56 | # Add new mime types for use in respond_to blocks: |
|
56 | # Add new mime types for use in respond_to blocks: |
|
57 | # Mime::Type.register "text/richtext", :rtf |
|
57 | # Mime::Type.register "text/richtext", :rtf |
|
58 | # Mime::Type.register "application/x-mobile", :mobile |
|
58 | # Mime::Type.register "application/x-mobile", :mobile |
|
59 |
|
59 | ||
|
60 | # Include your application configuration below |
|
60 | # Include your application configuration below |
|
|
61 | + | ||
|
|
62 | + UPLOADED_INPUT_FILE_DIR = '/home/jittat/web_grader/upload/' |
@@ -6,13 +6,13 | |||||
|
6 | # to create the application database on another system, you should be using db:schema:load, not running |
|
6 | # to create the application database on another system, you should be using db:schema:load, not running |
|
7 | # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations |
|
7 | # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations |
|
8 | # you'll amass, the slower it'll run and the greater likelihood for issues). |
|
8 | # you'll amass, the slower it'll run and the greater likelihood for issues). |
|
9 | # |
|
9 | # |
|
10 | # It's strongly recommended to check this file into your version control system. |
|
10 | # It's strongly recommended to check this file into your version control system. |
|
11 |
|
11 | ||
|
12 |
- ActiveRecord::Schema.define(:version => 1 |
|
12 | + ActiveRecord::Schema.define(:version => 19) do |
|
13 |
|
13 | ||
|
14 | create_table "grader_processes", :force => true do |t| |
|
14 | create_table "grader_processes", :force => true do |t| |
|
15 | t.string "host", :limit => 20 |
|
15 | t.string "host", :limit => 20 |
|
16 | t.integer "pid" |
|
16 | t.integer "pid" |
|
17 | t.string "mode" |
|
17 | t.string "mode" |
|
18 | t.boolean "active" |
|
18 | t.boolean "active" |
@@ -93,12 +93,31 | |||||
|
93 | t.integer "submission_id" |
|
93 | t.integer "submission_id" |
|
94 | t.datetime "created_at" |
|
94 | t.datetime "created_at" |
|
95 | t.integer "status" |
|
95 | t.integer "status" |
|
96 | t.datetime "updated_at" |
|
96 | t.datetime "updated_at" |
|
97 | end |
|
97 | end |
|
98 |
|
98 | ||
|
|
99 | + create_table "test_requests", :force => true do |t| | ||
|
|
100 | + t.integer "user_id" | ||
|
|
101 | + t.integer "problem_id" | ||
|
|
102 | + t.integer "submission_id" | ||
|
|
103 | + t.string "input_file_name" | ||
|
|
104 | + t.string "output_file_name" | ||
|
|
105 | + t.string "running_stat" | ||
|
|
106 | + t.integer "status" | ||
|
|
107 | + t.datetime "updated_at" | ||
|
|
108 | + t.datetime "submitted_at" | ||
|
|
109 | + t.datetime "compiled_at" | ||
|
|
110 | + t.string "compiler_message" | ||
|
|
111 | + t.datetime "graded_at" | ||
|
|
112 | + t.string "grader_comment" | ||
|
|
113 | + t.datetime "created_at" | ||
|
|
114 | + end | ||
|
|
115 | + | ||
|
|
116 | + add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id" | ||
|
|
117 | + | ||
|
99 | create_table "users", :force => true do |t| |
|
118 | create_table "users", :force => true do |t| |
|
100 | t.string "login", :limit => 10 |
|
119 | t.string "login", :limit => 10 |
|
101 | t.string "full_name" |
|
120 | t.string "full_name" |
|
102 | t.string "hashed_password" |
|
121 | t.string "hashed_password" |
|
103 | t.string "salt", :limit => 5 |
|
122 | t.string "salt", :limit => 5 |
|
104 | t.string "alias" |
|
123 | t.string "alias" |
You need to be logged in to leave comments.
Login now