ojagent 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/quicksubmit +9 -5
- data/examples/hdoj3337.rb +57 -0
- data/examples/rosalind.rb +66 -0
- data/lib/ojagent.rb +12 -3
- data/lib/ojagent/hdoj_agent.rb +41 -0
- data/lib/ojagent/livearchive_agent.rb +8 -21
- data/lib/ojagent/ojagent.rb +58 -2
- data/lib/ojagent/rosalind_agent.rb +44 -0
- data/lib/ojagent/timus_agent.rb +13 -25
- data/lib/ojagent/version.rb +1 -1
- metadata +6 -2
data/bin/quicksubmit
CHANGED
@@ -11,10 +11,13 @@ require 'highline'
|
|
11
11
|
|
12
12
|
ME = 'quicksubmit'
|
13
13
|
|
14
|
-
Agents = {
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
Agents = {}
|
15
|
+
OJAgent.all.each do |agent|
|
16
|
+
name = agent.name.downcase
|
17
|
+
name.sub!(/.*::/, '')
|
18
|
+
name.sub!(/agent$/, '')
|
19
|
+
Agents[name.to_sym] = agent
|
20
|
+
end
|
18
21
|
|
19
22
|
Ext2Lang = {
|
20
23
|
'.pas' => :pascal,
|
@@ -138,7 +141,8 @@ $*.uniq.each do |path|
|
|
138
141
|
path.sub!(/#{ext}$/, ".#{Time.now.to_i}#{ext}") if File.exists? path
|
139
142
|
IO.write(path, code)
|
140
143
|
|
141
|
-
|
144
|
+
level = status[:status] =~ /correct|accepted/i ? Logger::INFO : Logger::WARN
|
145
|
+
LOG.add level, status
|
142
146
|
rescue Exception => ex
|
143
147
|
LOG.error ex
|
144
148
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'ojagent'
|
4
|
+
|
5
|
+
# Change these two lines
|
6
|
+
USER = ENV['OJ_USERNAME'] || 'ojagent'
|
7
|
+
PASS = ENV['OJ_PASSWORD'] || 'ppnn13%dkstFeb.1st'
|
8
|
+
|
9
|
+
HDOJ = OJAgent::HDOJAgent.new
|
10
|
+
HDOJ.login USER, PASS
|
11
|
+
|
12
|
+
def code(off)
|
13
|
+
return <<-CODE
|
14
|
+
#include <time.h>
|
15
|
+
#include <stdio.h>
|
16
|
+
#include <stdlib.h>
|
17
|
+
#include <string.h>
|
18
|
+
|
19
|
+
int main(void) {
|
20
|
+
int n = #{off};
|
21
|
+
clock_t cpu;
|
22
|
+
size_t mem;
|
23
|
+
char* p = NULL;
|
24
|
+
|
25
|
+
while (n-- > 0) {
|
26
|
+
getchar();
|
27
|
+
}
|
28
|
+
n = getchar();
|
29
|
+
if (n == EOF) {
|
30
|
+
n = 0;
|
31
|
+
}
|
32
|
+
|
33
|
+
cpu = ((n >> 4) * 100 + 10) * (CLOCKS_PER_SEC / 1000);
|
34
|
+
mem = ((n & 0xF) * 1000 + 10) * 1024;
|
35
|
+
p = malloc(mem);
|
36
|
+
memset(p, 0xff, mem);
|
37
|
+
while (clock() < cpu) {
|
38
|
+
p[rand() % mem] = (char)cpu;
|
39
|
+
}
|
40
|
+
putchar(p[0]);
|
41
|
+
free(p);
|
42
|
+
|
43
|
+
return 0;
|
44
|
+
}
|
45
|
+
CODE
|
46
|
+
end
|
47
|
+
|
48
|
+
input = ''
|
49
|
+
50.times do
|
50
|
+
id = HDOJ.submit! '3337', code(input.size), :c
|
51
|
+
status = HDOJ.status! id
|
52
|
+
ascii = (status[:time].to_i / 100) * 16 + (status[:mem].to_i / 1000)
|
53
|
+
break if ascii.zero?
|
54
|
+
input << ascii.chr
|
55
|
+
puts input
|
56
|
+
end
|
57
|
+
p input
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
BEGIN {
|
4
|
+
require 'ojagent'
|
5
|
+
|
6
|
+
$filename = 'rosalind.cookie'
|
7
|
+
$rosalind = OJAgent::RosalindAgent.new
|
8
|
+
$rosalind.agent.cookie_jar.load $filename if File.readable? $filename
|
9
|
+
}
|
10
|
+
|
11
|
+
END {
|
12
|
+
$rosalind.agent.cookie_jar.save_as $filename
|
13
|
+
FileUtils.chmod 0600, $filename
|
14
|
+
}
|
15
|
+
|
16
|
+
require 'highline'
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'wirble'
|
20
|
+
def pp(status)
|
21
|
+
puts Wirble::Colorize.colorize(status.inspect)
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
alias :pp :p
|
25
|
+
end
|
26
|
+
|
27
|
+
def usage
|
28
|
+
print <<-USAGE
|
29
|
+
Usage:
|
30
|
+
rosalind.rb login
|
31
|
+
rosalind.rb $pid.pl dl
|
32
|
+
rosalind.rb $pid.pl up
|
33
|
+
USAGE
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
$pl, $op = *$*
|
38
|
+
|
39
|
+
if $pl =~ /login/i
|
40
|
+
hl = HighLine.new
|
41
|
+
user = hl.ask("Username: ")
|
42
|
+
pass = hl.ask("Password: "){|q| q.echo = false}
|
43
|
+
$rosalind.login user, pass
|
44
|
+
elsif $pl =~ /[a-z]*/
|
45
|
+
id = $&
|
46
|
+
if $op =~ /dl|download|open/i
|
47
|
+
input = $rosalind.open! id
|
48
|
+
IO.write input.filename, input.content
|
49
|
+
system %{perl #{$pl} #{input.filename} | tee output}
|
50
|
+
elsif $op =~ /up|upload|submit/i
|
51
|
+
output = IO.read('output')
|
52
|
+
source = IO.read($pl)
|
53
|
+
status = $rosalind.judge! id, [output, 'output'], [source, $pl]
|
54
|
+
pp status
|
55
|
+
if status && status[:status] == 'Correct'
|
56
|
+
FileUtils.mv $pl, 'pl/', :verbose => true
|
57
|
+
FileUtils.mv "rosalind_#{id}.txt", 'txt/', :verbose => true
|
58
|
+
FileUtils.rm "rosalind_#{id}.txt.index", :force => true
|
59
|
+
end
|
60
|
+
else
|
61
|
+
usage
|
62
|
+
end
|
63
|
+
else
|
64
|
+
usage
|
65
|
+
end
|
66
|
+
|
data/lib/ojagent.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
require 'ojagent/version'
|
2
2
|
require 'ojagent/ojagent.rb'
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
libdir = File.join(File.expand_path(File.dirname(__FILE__)), 'ojagent')
|
5
|
+
Dir.entries(libdir).each do |filename|
|
6
|
+
require File.join(libdir, filename) if filename.end_with? '_agent.rb'
|
7
|
+
end
|
5
8
|
|
6
9
|
module OJAgent
|
7
|
-
#
|
10
|
+
# Return all avialbe agents.
|
11
|
+
def self.all
|
12
|
+
ObjectSpace.each_object(Class).select{|agent| agent < OJAgent}
|
13
|
+
end
|
14
|
+
|
15
|
+
# All avialbe agents at initial time.
|
16
|
+
StandardAgents = all
|
8
17
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module OJAgent
|
2
|
+
class HDOJAgent < OJAgent
|
3
|
+
def initialize
|
4
|
+
super 'http://acm.hdu.edu.cn',
|
5
|
+
:cpp => 0,
|
6
|
+
:c => 1,
|
7
|
+
:mscpp => 2,
|
8
|
+
:msc => 3,
|
9
|
+
:pascal => 4,
|
10
|
+
:java => 5
|
11
|
+
end
|
12
|
+
|
13
|
+
def login(user, pass)
|
14
|
+
@user = user
|
15
|
+
page = get '/'
|
16
|
+
login_form = page.form_with :action => '/userloginex.php?action=login'
|
17
|
+
login_form.set_fields :username => user,
|
18
|
+
:userpass => pass
|
19
|
+
login_form.submit
|
20
|
+
end
|
21
|
+
|
22
|
+
def submit(pid, code, lang)
|
23
|
+
page = get '/submit.php'
|
24
|
+
submit_form = page.form_with :action => './submit.php?action=submit'
|
25
|
+
submit_form.set_fields :problemid => pid,
|
26
|
+
:language => languages[lang],
|
27
|
+
:usercode => code
|
28
|
+
submit_form.submit
|
29
|
+
pid.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
@@selector = '#fixed_table table.table_text tr'
|
33
|
+
@@thead = [:id, :date, :status, :pid, :time, :mem, :length, :lang, :user]
|
34
|
+
|
35
|
+
def status(pid)
|
36
|
+
parse_status '/status.php?user=' + user, @@selector, @@thead do |tr|
|
37
|
+
tr[3] && tr[3].inner_text == pid
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -9,6 +9,7 @@ module OJAgent
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def login(user, pass)
|
12
|
+
@user = user
|
12
13
|
page = get '/'
|
13
14
|
login_form = page.form_with :id => 'mod_loginform'
|
14
15
|
login_form.set_fields :username => user,
|
@@ -17,9 +18,6 @@ module OJAgent
|
|
17
18
|
result = login_form.submit
|
18
19
|
end
|
19
20
|
|
20
|
-
def logout
|
21
|
-
end
|
22
|
-
|
23
21
|
def submit(pid, code, lang)
|
24
22
|
page = get '/index.php?option=com_onlinejudge&Itemid=25'
|
25
23
|
action = 'index.php?option=com_onlinejudge&Itemid=25&page=save_submission'
|
@@ -38,25 +36,14 @@ module OJAgent
|
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
status = (page / '.maincontent table tr').
|
44
|
-
map{|tr| tr / 'td'}.
|
45
|
-
find{|td| !td.empty? && td[0].inner_text == id}
|
39
|
+
@@selector = '.maincontent table tr'
|
40
|
+
@@thead = [:id, :pid, :pname, :status, :lang, :time, :data]
|
46
41
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
:status => status[3].inner_text.strip,
|
53
|
-
:lang => status[4].inner_text,
|
54
|
-
:time => status[5].inner_text,
|
55
|
-
:date => status[6].inner_text
|
56
|
-
}
|
57
|
-
url = status[3] / 'a[href]'
|
58
|
-
ret[:url] = url.map{|a| a['href']} unless url.empty?
|
59
|
-
ret
|
42
|
+
def status(id)
|
43
|
+
url = '/index.php?option=com_onlinejudge&Itemid=9'
|
44
|
+
parse_status url, @@selector, @@thead do |tr|
|
45
|
+
!tr.empty? && tr[0].inner_text == id
|
46
|
+
end
|
60
47
|
end
|
61
48
|
end
|
62
49
|
end
|
data/lib/ojagent/ojagent.rb
CHANGED
@@ -2,6 +2,14 @@ require 'forwardable'
|
|
2
2
|
require 'mechanize'
|
3
3
|
require 'nokogiri'
|
4
4
|
|
5
|
+
class Mechanize::Form::FileUpload
|
6
|
+
def file=(file, default_name = 'noname')
|
7
|
+
data, name = *file
|
8
|
+
self.file_data = data
|
9
|
+
self.file_name = name || default_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
5
13
|
module OJAgent
|
6
14
|
class LoginFailureError < RuntimeError
|
7
15
|
end
|
@@ -16,9 +24,9 @@ module OJAgent
|
|
16
24
|
|
17
25
|
attr_accessor :retries, :duration
|
18
26
|
|
19
|
-
attr_reader :agent, :base_uri, :languages
|
27
|
+
attr_reader :agent, :base_uri, :languages, :user
|
20
28
|
|
21
|
-
def initialize(base_uri, languages)
|
29
|
+
def initialize(base_uri = nil, languages = {})
|
22
30
|
@agent = Mechanize.new
|
23
31
|
@agent.open_timeout = 20
|
24
32
|
@agent.read_timeout = 60
|
@@ -44,6 +52,10 @@ module OJAgent
|
|
44
52
|
raise NotImplementedError
|
45
53
|
end
|
46
54
|
|
55
|
+
def open(pid)
|
56
|
+
# noop
|
57
|
+
end
|
58
|
+
|
47
59
|
def submit(pid, code, lang)
|
48
60
|
raise NotImplementedError
|
49
61
|
end
|
@@ -52,6 +64,43 @@ module OJAgent
|
|
52
64
|
raise NotImplementedError
|
53
65
|
end
|
54
66
|
|
67
|
+
def parse_status(url, selector, thead, &block)
|
68
|
+
tbody = get(url) / selector
|
69
|
+
tr = tbody.map{|tr| tr / 'td'}.find{|tr| yield tr}
|
70
|
+
return nil unless tr
|
71
|
+
|
72
|
+
ret = {}
|
73
|
+
thead.zip(tr) do |th, td|
|
74
|
+
next unless th && td
|
75
|
+
ret[th] = td.inner_text.strip.gsub(/\s+/, ' ')
|
76
|
+
if th == :status
|
77
|
+
url = td / 'a[href]'
|
78
|
+
if url.size > 0
|
79
|
+
ret[:url] = (ret[:url] || []) + url.map{|a| a['href']}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
ret
|
84
|
+
end
|
85
|
+
|
86
|
+
# Open the problem or download the input. For online judges that ask
|
87
|
+
# users to download the input and upload the output in time limit,
|
88
|
+
# user must open (download the input) before submit (upload the output).
|
89
|
+
# For other online judges, it does nothing.
|
90
|
+
def open!(pid, retries = 1)
|
91
|
+
retries ||= self.retries
|
92
|
+
retries.times do
|
93
|
+
begin
|
94
|
+
ret = open pid
|
95
|
+
return ret if ret
|
96
|
+
rescue
|
97
|
+
end
|
98
|
+
end
|
99
|
+
raise OperationFailureError, "Fail to open"
|
100
|
+
end
|
101
|
+
|
102
|
+
# Submit solution and retry on failure. The return value is a hint for
|
103
|
+
# getting status and varys at different online judges.
|
55
104
|
def submit!(pid, code, lang, retries = nil)
|
56
105
|
retries ||= self.retries
|
57
106
|
retries.times do
|
@@ -64,6 +113,7 @@ module OJAgent
|
|
64
113
|
raise OperationFailureError, "Fail to submit"
|
65
114
|
end
|
66
115
|
|
116
|
+
# Get status and retry on failure.
|
67
117
|
def status!(id, retries = nil, duration = nil)
|
68
118
|
retries ||= self.retries
|
69
119
|
duration ||= self.duration
|
@@ -84,5 +134,11 @@ module OJAgent
|
|
84
134
|
return ret if ret
|
85
135
|
raise OperationFailureError, "Fail to get status"
|
86
136
|
end
|
137
|
+
|
138
|
+
# Submit solution and get the corresponding status.
|
139
|
+
def judge!(pid, code, lang, retries = nil, duration = nil)
|
140
|
+
id = submit!(pid, code, lang, retries)
|
141
|
+
status!(id, retries, duration)
|
142
|
+
end
|
87
143
|
end
|
88
144
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module OJAgent
|
2
|
+
class RosalindAgent < OJAgent
|
3
|
+
def initialize
|
4
|
+
super 'http://rosalind.info'
|
5
|
+
end
|
6
|
+
|
7
|
+
def login(user, pass)
|
8
|
+
@user = user
|
9
|
+
page = get '/accounts/login/'
|
10
|
+
login_form = page.form_with :id => 'id_form_login'
|
11
|
+
login_form.set_fields :username => user,
|
12
|
+
:password => pass
|
13
|
+
login_form.submit
|
14
|
+
end
|
15
|
+
|
16
|
+
def open(pid)
|
17
|
+
get "/problems/#{pid}/dataset/"
|
18
|
+
end
|
19
|
+
|
20
|
+
def submit(pid, ans, src = nil)
|
21
|
+
page = get "/problems/#{pid}/"
|
22
|
+
submit_form = page.form_with :id => 'id_form_submission'
|
23
|
+
submit_form.file_upload_with(:name => 'output_file').file = ans
|
24
|
+
submit_form.file_upload_with(:name => 'code').file = src if src
|
25
|
+
submit_form.submit
|
26
|
+
end
|
27
|
+
|
28
|
+
@@close = "\xc3\x97".force_encoding('utf-8')
|
29
|
+
|
30
|
+
def status(page)
|
31
|
+
status = {}
|
32
|
+
[:error, :warning, :info, :success].each do |type|
|
33
|
+
messages = []
|
34
|
+
(page / "div.main div.alert-#{type}.flash-message").each do |alert|
|
35
|
+
message = alert.inner_text.strip.gsub(@@close, '').gsub(/\s+/, ' ')
|
36
|
+
messages << message unless message.empty?
|
37
|
+
end
|
38
|
+
status[type] = messages unless messages.empty?
|
39
|
+
end
|
40
|
+
status[:status] = status[:success] && !status[:error] ? 'Correct' : 'Wrong'
|
41
|
+
status
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/ojagent/timus_agent.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module OJAgent
|
2
2
|
class TimusAgent < OJAgent
|
3
|
-
attr_accessor :user, :pass
|
4
|
-
|
5
3
|
def initialize
|
6
4
|
super 'http://acm.timus.ru',
|
7
5
|
:pascal => '3',
|
@@ -12,43 +10,33 @@ module OJAgent
|
|
12
10
|
end
|
13
11
|
|
14
12
|
def login(user, pass)
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
def logout
|
13
|
+
@user = user
|
14
|
+
@pass = pass
|
20
15
|
end
|
21
16
|
|
22
17
|
def submit(pid, code, lang)
|
23
18
|
page = get '/submit.aspx'
|
24
19
|
submit_form = page.form_with :action => 'submit.aspx?space=1'
|
25
|
-
submit_form.set_fields :JudgeID => user + pass,
|
20
|
+
submit_form.set_fields :JudgeID => user + @pass,
|
26
21
|
:Language => languages[lang],
|
27
22
|
:ProblemNum => pid,
|
28
23
|
:Source => code
|
29
24
|
submit_form.submit
|
30
|
-
pid
|
25
|
+
pid.to_s
|
31
26
|
end
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
status = (page / 'table.status tr').
|
36
|
-
map{|tr| tr / 'td'}.
|
37
|
-
find{|td| td[3] && td[3].inner_text.start_with?(pid)}
|
28
|
+
@@selector = 'table.status tr'
|
29
|
+
@@thead = [:id, :date, :user, :pname, :lang, :status, :testid, :time, :mem]
|
38
30
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
ret[k] = status[i].inner_text.strip.gsub('\s+', ' ')
|
31
|
+
def status(pid)
|
32
|
+
url = '/status.aspx?author=' + user
|
33
|
+
status = parse_status url, @@selector, @@thead do |tr|
|
34
|
+
tr[3] && tr[3].inner_text.start_with?(pid)
|
44
35
|
end
|
45
|
-
if
|
46
|
-
|
47
|
-
ret[:pname] = $2
|
36
|
+
if status[:pname] =~ /^(\d+).\s*(.*)$/
|
37
|
+
status[:pid], status[:pname] = $1, $2
|
48
38
|
end
|
49
|
-
|
50
|
-
ret[:url] = url.map{|a| a['href']} unless url.empty?
|
51
|
-
ret
|
39
|
+
status
|
52
40
|
end
|
53
41
|
end
|
54
42
|
end
|
data/lib/ojagent/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ojagent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mechanize
|
@@ -94,9 +94,13 @@ files:
|
|
94
94
|
- README.md
|
95
95
|
- Rakefile
|
96
96
|
- bin/quicksubmit
|
97
|
+
- examples/hdoj3337.rb
|
98
|
+
- examples/rosalind.rb
|
97
99
|
- lib/ojagent.rb
|
100
|
+
- lib/ojagent/hdoj_agent.rb
|
98
101
|
- lib/ojagent/livearchive_agent.rb
|
99
102
|
- lib/ojagent/ojagent.rb
|
103
|
+
- lib/ojagent/rosalind_agent.rb
|
100
104
|
- lib/ojagent/timus_agent.rb
|
101
105
|
- lib/ojagent/version.rb
|
102
106
|
- ojagent.gemspec
|