ojagent 0.0.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/bin/quicksubmit CHANGED
@@ -11,10 +11,13 @@ require 'highline'
11
11
 
12
12
  ME = 'quicksubmit'
13
13
 
14
- Agents = {
15
- :livearchive => OJAgent::LiveArchiveAgent,
16
- :timus => OJAgent::TimusAgent
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
- LOG.add status[:status] =~ /accepted/i ? Logger::INFO : Logger::WARN, status
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
- require 'ojagent/timus_agent.rb'
4
- require 'ojagent/livearchive_agent.rb'
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
- # Your code goes here...
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
- def status(id)
42
- page = get '/index.php?option=com_onlinejudge&Itemid=9'
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
- return nil unless status
48
- ret = {
49
- :id => status[0].inner_text,
50
- :pid => status[1].inner_text,
51
- :pname => status[2].inner_text,
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
@@ -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
@@ -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
- self.user = user
16
- self.pass = pass
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
- def status(pid)
34
- page = get '/status.aspx?author=' + user
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
- return nil unless status
40
- ret = {}
41
- ths = [:id, :date, :user, :pname, :lang, :status, :testid, :time, :mem]
42
- ths.each_with_index do |k, i|
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 ret[:pname] =~ /^(\d+).\s*(.*)$/
46
- ret[:pid] = $1
47
- ret[:pname] = $2
36
+ if status[:pname] =~ /^(\d+).\s*(.*)$/
37
+ status[:pid], status[:pname] = $1, $2
48
38
  end
49
- url = status[5] / 'a[href]'
50
- ret[:url] = url.map{|a| a['href']} unless url.empty?
51
- ret
39
+ status
52
40
  end
53
41
  end
54
42
  end
@@ -1,3 +1,3 @@
1
1
  module OJAgent
2
- VERSION = "0.0.1"
2
+ VERSION = '0.0.4'
3
3
  end
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.1
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-06 00:00:00.000000000 Z
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