Description:
copy working trunk (r383, before major changes to combine judge and to support grader message queue) to trunk git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@396 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r188:0b607f9b89c1 - - 12 files changed: 14 inserted, 400 deleted

@@ -0,0 +1,10
1 + # Rails app directory
2 + RAILS_ROOT = "/home/jittat/web_grader"
3 +
4 + GRADER_ROOT = "/home/jittat/grader/scripts"
5 +
6 + # This load all required codes
7 + require File.join(File.dirname(__FILE__),'../lib/boot')
8 +
9 + # load the required environment file
10 + require File.dirname(__FILE__) + "/env_#{GRADER_ENV}.rb"
@@ -1,206 +1,195
1 1 # This file is auto-generated from the current state of the database. Instead of editing this file,
2 2 # please use the migrations feature of Active Record to incrementally modify your database, and
3 3 # then regenerate this schema definition.
4 4 #
5 5 # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6 6 # to create the application database on another system, you should be using db:schema:load, not running
7 7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8 8 # you'll amass, the slower it'll run and the greater likelihood for issues).
9 9 #
10 10 # It's strongly recommended to check this file into your version control system.
11 11
12 - ActiveRecord::Schema.define(:version => 20090429014554) do
12 + ActiveRecord::Schema.define(:version => 20090426131044) do
13 13
14 14 create_table "announcements", :force => true do |t|
15 15 t.string "author"
16 16 t.text "body"
17 17 t.boolean "published"
18 18 t.datetime "created_at"
19 19 t.datetime "updated_at"
20 20 t.boolean "frontpage", :default => false
21 21 t.boolean "contest_only", :default => false
22 22 end
23 23
24 24 create_table "configurations", :force => true do |t|
25 25 t.string "key"
26 26 t.string "value_type"
27 27 t.string "value"
28 28 t.datetime "created_at"
29 29 t.datetime "updated_at"
30 30 end
31 31
32 32 create_table "countries", :force => true do |t|
33 33 t.string "name"
34 34 t.datetime "created_at"
35 35 t.datetime "updated_at"
36 36 end
37 37
38 38 create_table "descriptions", :force => true do |t|
39 39 t.text "body"
40 40 t.boolean "markdowned"
41 41 t.datetime "created_at"
42 42 t.datetime "updated_at"
43 43 end
44 44
45 - create_table "grader_messages", :force => true do |t|
46 - t.integer "grader_process_id"
47 - t.integer "command"
48 - t.string "options"
49 - t.integer "target_id"
50 - t.boolean "taken"
51 - t.integer "taken_grader_process_id"
52 - t.datetime "created_at"
53 - t.datetime "updated_at"
54 - end
55 -
56 45 create_table "grader_processes", :force => true do |t|
57 46 t.string "host", :limit => 20
58 47 t.integer "pid"
59 48 t.string "mode"
60 49 t.boolean "active"
61 50 t.datetime "created_at"
62 51 t.datetime "updated_at"
63 52 t.integer "task_id"
64 53 t.string "task_type"
65 54 t.boolean "terminated"
66 55 end
67 56
68 57 add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
69 58
70 59 create_table "languages", :force => true do |t|
71 60 t.string "name", :limit => 10
72 61 t.string "pretty_name"
73 62 t.string "ext", :limit => 10
74 63 t.string "common_ext"
75 64 end
76 65
77 66 create_table "messages", :force => true do |t|
78 67 t.integer "sender_id"
79 68 t.integer "receiver_id"
80 69 t.integer "replying_message_id"
81 70 t.text "body"
82 71 t.boolean "replied"
83 72 t.datetime "created_at"
84 73 t.datetime "updated_at"
85 74 end
86 75
87 76 create_table "problems", :force => true do |t|
88 77 t.string "name", :limit => 30
89 78 t.string "full_name"
90 79 t.integer "full_score"
91 80 t.date "date_added"
92 81 t.boolean "available"
93 82 t.string "url"
94 83 t.integer "description_id"
95 84 t.boolean "test_allowed"
96 85 t.boolean "output_only"
97 86 end
98 87
99 88 create_table "rights", :force => true do |t|
100 89 t.string "name"
101 90 t.string "controller"
102 91 t.string "action"
103 92 end
104 93
105 94 create_table "rights_roles", :id => false, :force => true do |t|
106 95 t.integer "right_id"
107 96 t.integer "role_id"
108 97 end
109 98
110 99 add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
111 100
112 101 create_table "roles", :force => true do |t|
113 102 t.string "name"
114 103 end
115 104
116 105 create_table "roles_users", :id => false, :force => true do |t|
117 106 t.integer "role_id"
118 107 t.integer "user_id"
119 108 end
120 109
121 110 add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
122 111
123 112 create_table "sessions", :force => true do |t|
124 113 t.string "session_id"
125 114 t.text "data"
126 115 t.datetime "updated_at"
127 116 end
128 117
129 118 add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
130 119 add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
131 120
132 121 create_table "sites", :force => true do |t|
133 122 t.string "name"
134 123 t.boolean "started"
135 124 t.datetime "start_time"
136 125 t.datetime "created_at"
137 126 t.datetime "updated_at"
138 127 t.integer "country_id"
139 128 t.string "password"
140 129 end
141 130
142 131 create_table "submissions", :force => true do |t|
143 132 t.integer "user_id"
144 133 t.integer "problem_id"
145 134 t.integer "language_id"
146 135 t.text "source"
147 136 t.binary "binary"
148 137 t.datetime "submitted_at"
149 138 t.datetime "compiled_at"
150 139 t.text "compiler_message"
151 140 t.datetime "graded_at"
152 141 t.integer "points"
153 142 t.text "grader_comment"
154 143 t.integer "number"
155 144 t.string "source_filename"
156 145 end
157 146
158 147 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
159 148 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
160 149
161 150 create_table "tasks", :force => true do |t|
162 151 t.integer "submission_id"
163 152 t.datetime "created_at"
164 153 t.integer "status"
165 154 t.datetime "updated_at"
166 155 end
167 156
168 157 create_table "test_requests", :force => true do |t|
169 158 t.integer "user_id"
170 159 t.integer "problem_id"
171 160 t.integer "submission_id"
172 161 t.string "input_file_name"
173 162 t.string "output_file_name"
174 163 t.string "running_stat"
175 164 t.integer "status"
176 165 t.datetime "updated_at"
177 166 t.datetime "submitted_at"
178 167 t.datetime "compiled_at"
179 168 t.text "compiler_message"
180 169 t.datetime "graded_at"
181 170 t.string "grader_comment"
182 171 t.datetime "created_at"
183 172 t.float "running_time"
184 173 t.string "exit_status"
185 174 t.integer "memory_usage"
186 175 end
187 176
188 177 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
189 178
190 179 create_table "users", :force => true do |t|
191 180 t.string "login", :limit => 50
192 181 t.string "full_name"
193 182 t.string "hashed_password"
194 183 t.string "salt", :limit => 5
195 184 t.string "alias"
196 185 t.string "email"
197 186 t.integer "site_id"
198 187 t.integer "country_id"
199 188 t.boolean "activated", :default => false
200 189 t.datetime "created_at"
201 190 t.datetime "updated_at"
202 191 end
203 192
204 193 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
205 194
206 195 end
@@ -1,217 +1,217
1 1 #!/usr/bin/ruby
2 2
3 3 def stop_grader(id)
4 4 if id==:all
5 5 File.open(File.dirname(__FILE__) + "/stop.all",'w').close
6 6 else
7 7 File.open(File.dirname(__FILE__) + "/stop.#{id}",'w').close
8 8 end
9 9 end
10 10
11 11 def check_stopfile
12 12 FileTest.exist?(File.dirname(__FILE__) + "/stop.all") or
13 13 FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
14 14 end
15 15
16 16 def clear_stopfile
17 17 if FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
18 18 system("rm " + File.dirname(__FILE__) + "/stop.#{Process.pid}")
19 19 end
20 20 end
21 21
22 22 def config
23 23 Grader::Configuration.get_instance
24 24 end
25 25
26 26 def log_file_name
27 27 if !File.exists?(config.log_dir)
28 28 raise "Log directory does not exist: #{config.log_dir}"
29 29 end
30 30 config.log_dir +
31 - "/#{GRADER_ENV}.#{Process.pid}"
31 + "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}"
32 32 end
33 33
34 34 def log(str)
35 35 if config.talkative
36 36 puts str
37 37 end
38 38 if config.logging
39 39 fp = File.open(log_file_name,"a")
40 40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
41 41 fp.close
42 42 end
43 43 end
44 44
45 45 def display_manual
46 46 puts <<USAGE
47 47 Grader.
48 48 using: (1) grader
49 49 (2) grader environment [mode]
50 50 (3) grader stop [all|pids-list]
51 51 (4) grader --help
52 52 (1) call grader with environment = 'exam', mode = 'queue'
53 53 (2) possible modes are: 'queue', 'prob', 'test_request'
54 54 (3) create stop-file to stop running grader in queue mode
55 55 (4) You are here.
56 56 USAGE
57 57 end
58 58
59 59 #########################################
60 60 # main program
61 61 #########################################
62 62
63 63 # with --help
64 64 if (ARGV.length==1) and (/help/.match(ARGV[0]))
65 65 display_manual
66 66 exit(0)
67 67 end
68 68
69 69 # reading environment and options
70 70 if (ARGV.length >= 1) and (ARGV[0]=='stop')
71 71 if ARGV.length==1
72 72 puts "you should specify pid-list or 'all'"
73 73 display_manual
74 74 elsif (ARGV.length==2) and (ARGV[1]=='all')
75 75 stop_grader(:all)
76 76 puts "A global stop file ('stop.all') created."
77 77 puts "You should remove it manually later."
78 78 else
79 79 (1..ARGV.length-1).each do |i|
80 80 stop_grader(ARGV[i])
81 81 end
82 82 puts "stop file(s) created"
83 83 end
84 84 exit(0)
85 85 end
86 86
87 87 if check_stopfile
88 88 puts "Stop file exists. Terminated."
89 89 clear_stopfile
90 90 exit(0)
91 91 end
92 92
93 93 grader_mode = 'queue'
94 94 if ARGV.length >= 1
95 95 GRADER_ENV = ARGV[0]
96 96 if ARGV.length >=2
97 97 grader_mode = ARGV[1]
98 98 end
99 99 else
100 100 GRADER_ENV = 'exam'
101 101 end
102 102
103 103 puts "environment: #{GRADER_ENV}"
104 104 require File.join(File.dirname(__FILE__),'config/environment')
105 105
106 106 # add grader_mode to config
107 107 # this is needed because method log needs it. TODO: clean this up
108 108 class << config
109 109 attr_accessor :grader_mode
110 110 end
111 111 config.grader_mode = grader_mode
112 112
113 113 # reading rails environment
114 114 log 'Reading rails environment'
115 115
116 116 RAILS_ENV = config.rails_env
117 117 require RAILS_ROOT + '/config/environment'
118 118
119 119 # register grader process
120 120 if config.report_grader
121 121 grader_proc = GraderProcess.register(config.grader_hostname,
122 122 Process.pid,
123 123 grader_mode)
124 124 else
125 125 grader_proc = nil
126 126 end
127 127
128 128 #set loggin environment
129 129 ENV['GRADER_LOGGING'] = log_file_name
130 130
131 131 # register exit handler to report inactive, and terminated
132 132 at_exit do
133 133 if grader_proc!=nil
134 134 grader_proc.report_inactive
135 135 grader_proc.terminate
136 136 end
137 137 end
138 138
139 139 #
140 140 # MAIN LOOP
141 141 #
142 142
143 143 case grader_mode
144 144 when "queue", "test_request"
145 145 log "Grader: #{grader_mode}"
146 146 if grader_mode=="queue"
147 147 engine = Grader::Engine.new
148 148 else
149 149 engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
150 150 Grader::TestRequestReporter.new)
151 151 end
152 152
153 153 runner = Grader::Runner.new(engine, grader_proc)
154 154 while true
155 155
156 156 if check_stopfile # created by calling grader stop
157 157 clear_stopfile
158 158 log "stopped (with stop file)"
159 159 break
160 160 end
161 161
162 162 if grader_mode=="queue"
163 163 task = runner.grade_oldest_task
164 164 else
165 165 task = runner.grade_oldest_test_request
166 166 end
167 167 if task==nil
168 168 sleep(1)
169 169 end
170 170 end
171 171
172 172 when "prob"
173 173 engine = Grader::Engine.new
174 174 runner = Grader::Runner.new(engine, grader_proc)
175 175
176 176 grader_proc.report_active if grader_proc!=nil
177 177
178 178 ARGV.shift
179 179 ARGV.shift
180 180
181 181 ARGV.each do |prob_name|
182 182 prob = Problem.find_by_name(prob_name)
183 183 if prob==nil
184 184 puts "cannot find problem: #{prob_name}"
185 185 else
186 186 runner.grade_problem(prob)
187 187 end
188 188 end
189 189
190 190 when "sub"
191 191 engine = Grader::Engine.new
192 192 runner = Grader::Runner.new(engine, grader_proc)
193 193
194 194 grader_proc.report_active if grader_proc!=nil
195 195
196 196 ARGV.shift
197 197 ARGV.shift
198 198
199 199 ARGV.each do |sub_id|
200 200 puts "Grading #{sub_id}"
201 201 begin
202 202 submission = Submission.find(sub_id.to_i)
203 203 rescue ActiveRecord::RecordNotFound
204 204 puts "Record not found"
205 205 submission = nil
206 206 end
207 207
208 208 if submission!=nil
209 209 runner.grade_submission(submission)
210 210 end
211 211 end
212 212
213 213 else
214 214 display_manual
215 215 exit(0)
216 216 end
217 217
@@ -1,175 +1,175
1 1 require 'fileutils'
2 2 require 'ftools'
3 3 require 'lib/dir_init'
4 4
5 5 module Grader
6 6
7 7 #
8 8 # A grader engine grades a submission, against anything: a test
9 9 # data, or a user submitted test data. It uses two helpers objects:
10 10 # room_maker and reporter.
11 11 #
12 12 class Engine
13 13
14 14 attr_writer :room_maker
15 15 attr_writer :reporter
16 16
17 17 def initialize(room_maker=nil, reporter=nil)
18 18 @config = Grader::Configuration.get_instance
19 19
20 20 @room_maker = room_maker || Grader::SubmissionRoomMaker.new
21 21 @reporter = reporter || Grader::SubmissionReporter.new
22 22 end
23 23
24 24 # takes a submission, asks room_maker to produce grading directories,
25 25 # calls grader scripts, and asks reporter to save the result
26 26 def grade(submission)
27 27 current_dir = `pwd`.chomp
28 28
29 29 user = submission.user
30 30 problem = submission.problem
31 31
32 32 # TODO: will have to create real exception for this
33 33 if user==nil or problem == nil
34 34 @reporter.report_error(submission,"Grading error: problem with submission")
35 35 #raise "engine: user or problem is nil"
36 36 end
37 37
38 38 # TODO: this is another hack so that output only task can be judged
39 39 if submission.language!=nil
40 40 language = submission.language.name
41 41 lang_ext = submission.language.ext
42 42 else
43 43 language = 'c'
44 44 lang_ext = 'c'
45 45 end
46 46
47 47 # FIX THIS
48 48 talk 'some hack on language'
49 49 if language == 'cpp'
50 50 language = 'c++'
51 51 end
52 52
53 53 # COMMENT: should it be only source.ext?
54 54 if problem!=nil
55 55 source_name = "#{problem.name}.#{lang_ext}"
56 56 else
57 57 source_name = "source.#{lang_ext}"
58 58 end
59 59
60 60 begin
61 61 grading_dir = @room_maker.produce_grading_room(submission)
62 62 @room_maker.save_source(submission,source_name)
63 63 problem_home = @room_maker.find_problem_home(submission)
64 64
65 65 # puts "GRADING DIR: #{grading_dir}"
66 66 # puts "PROBLEM DIR: #{problem_home}"
67 67
68 68 dinit = DirInit::Manager.new(problem_home)
69 69
70 70 dinit.setup do
71 71 copy_log = copy_script(problem_home)
72 72 save_copy_log(problem_home,copy_log)
73 73 end
74 74
75 75 call_judge(problem_home,language,grading_dir,source_name)
76 76
77 77 @reporter.report(submission,"#{grading_dir}/test-result")
78 78
79 79 dinit.teardown do
80 80 copy_log = load_copy_log(problem_home)
81 81 clear_copy_log(problem_home)
82 82 clear_script(copy_log,problem_home)
83 83 end
84 84
85 85 rescue RuntimeError => msg
86 86 @reporter.report_error(submission,"Grading error: #{msg}")
87 87
88 88 ensure
89 89 @room_maker.clean_up(submission)
90 90 Dir.chdir(current_dir) # this is really important
91 91 end
92 92 end
93 93
94 94 protected
95 95
96 96 def talk(str)
97 97 if @config.talkative
98 98 puts str
99 99 end
100 100 end
101 101
102 102 def call_judge(problem_home,language,grading_dir,fname)
103 103 ENV['PROBLEM_HOME'] = problem_home
104 104
105 - talk "Grading in #{grading_dir}"
105 + talk grading_dir
106 106 Dir.chdir grading_dir
107 107 cmd = "#{problem_home}/script/judge #{language} #{fname}"
108 108 talk "CMD: #{cmd}"
109 109 system(cmd)
110 110 end
111 111
112 112 def get_std_script_dir
113 113 GRADER_ROOT + '/std-script'
114 114 end
115 115
116 116 def copy_script(problem_home)
117 117 script_dir = "#{problem_home}/script"
118 118 std_script_dir = get_std_script_dir
119 119
120 120 raise "std-script directory not found" if !FileTest.exist?(std_script_dir)
121 121
122 122 scripts = Dir[std_script_dir + '/*']
123 123
124 124 copied = []
125 125
126 126 scripts.each do |s|
127 127 fname = File.basename(s)
128 128 if !FileTest.exist?("#{script_dir}/#{fname}")
129 129 copied << fname
130 130 system("cp #{s} #{script_dir}")
131 131 end
132 132 end
133 133
134 134 return copied
135 135 end
136 136
137 137 def copy_log_filename(problem_home)
138 138 return File.join(problem_home, '.scripts_copied')
139 139 end
140 140
141 141 def save_copy_log(problem_home, log)
142 142 f = File.new(copy_log_filename(problem_home),"w")
143 143 log.each do |fname|
144 144 f.write("#{fname}\n")
145 145 end
146 146 f.close
147 147 end
148 148
149 149 def load_copy_log(problem_home)
150 150 f = File.new(copy_log_filename(problem_home),"r")
151 151 log = []
152 152 f.readlines.each do |line|
153 153 log << line.strip
154 154 end
155 155 f.close
156 156 log
157 157 end
158 158
159 159 def clear_copy_log(problem_home)
160 160 File.delete(copy_log_filename(problem_home))
161 161 end
162 162
163 163 def clear_script(log,problem_home)
164 164 log.each do |s|
165 165 system("rm #{problem_home}/script/#{s}")
166 166 end
167 167 end
168 168
169 169 def mkdir_if_does_not_exist(dirname)
170 170 Dir.mkdir(dirname) if !FileTest.exist?(dirname)
171 171 end
172 172
173 173 end
174 174
175 175 end
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
You need to be logged in to leave comments. Login now