ojagent 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/Rakefile +1 -0
- data/bin/quicksubmit +147 -0
- data/lib/ojagent.rb +8 -0
- data/lib/ojagent/livearchive_agent.rb +62 -0
- data/lib/ojagent/ojagent.rb +88 -0
- data/lib/ojagent/timus_agent.rb +54 -0
- data/lib/ojagent/version.rb +3 -0
- data/ojagent.gemspec +27 -0
- metadata +127 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Zejun Wu
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# OJAgent
|
2
|
+
|
3
|
+
OJAgent is a client to submit solutions and query status at different online
|
4
|
+
judges. It provides a uniformed interface to a lot of famous online judges.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'ojagent'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install ojagent
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
OJ Agent:
|
23
|
+
|
24
|
+
require 'ojagent'
|
25
|
+
|
26
|
+
oj = OJAgent::AnyAnget.new
|
27
|
+
oj.login($user, $pass)
|
28
|
+
oj.submit!($pid, $code, $lang)
|
29
|
+
p oj.status!
|
30
|
+
|
31
|
+
Quick Submit:
|
32
|
+
|
33
|
+
$ quicksubmit -j pat -u admin -i 1001 -s pat/1001/ -t pat/1001/test/
|
34
|
+
$ export OJ_JUDGE=zoj
|
35
|
+
$ export OJ_USERNAME=watashi
|
36
|
+
$ export OJ_PASSWORD=*******
|
37
|
+
$ quicksubmit 1001.c
|
38
|
+
|
39
|
+
For more information:
|
40
|
+
|
41
|
+
$ quicksubmit -h
|
42
|
+
$ quicksubmit -l
|
43
|
+
$ ri OJAgent
|
44
|
+
|
45
|
+
## Contributing
|
46
|
+
|
47
|
+
1. Fork it
|
48
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
49
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
50
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
51
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/quicksubmit
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'ojagent'
|
4
|
+
|
5
|
+
require 'find'
|
6
|
+
require 'logger'
|
7
|
+
require 'stringio'
|
8
|
+
|
9
|
+
require 'slop'
|
10
|
+
require 'highline'
|
11
|
+
|
12
|
+
ME = 'quicksubmit'
|
13
|
+
|
14
|
+
Agents = {
|
15
|
+
:livearchive => OJAgent::LiveArchiveAgent,
|
16
|
+
:timus => OJAgent::TimusAgent
|
17
|
+
}
|
18
|
+
|
19
|
+
Ext2Lang = {
|
20
|
+
'.pas' => :pascal,
|
21
|
+
'.c' => :c,
|
22
|
+
'.cs' => :cs,
|
23
|
+
'.cpp' => :cpp,
|
24
|
+
'.java' => :java
|
25
|
+
}
|
26
|
+
|
27
|
+
def to_comment(status, lang)
|
28
|
+
if lang == :pascal
|
29
|
+
open, close, inline = '{', '}', nil
|
30
|
+
else
|
31
|
+
open, close, inline = '/*', '*/', nil
|
32
|
+
end
|
33
|
+
|
34
|
+
ios = StringIO.new('', 'w')
|
35
|
+
ios.puts
|
36
|
+
ios.puts open if open
|
37
|
+
status.each do |k, v|
|
38
|
+
ios.puts "#{k} => #{v}"
|
39
|
+
end
|
40
|
+
ios.puts close if close
|
41
|
+
ios.string
|
42
|
+
end
|
43
|
+
|
44
|
+
# opts
|
45
|
+
opts = Slop.new :strict => true, :help => true do
|
46
|
+
banner <<-BANNER
|
47
|
+
#{ME} -j judge [options] [-s src]
|
48
|
+
#{ME} -j judge [options] [files ...]
|
49
|
+
#{ME} -j judge [options] -i pid [-e lang] file
|
50
|
+
#{ME} -l
|
51
|
+
BANNER
|
52
|
+
|
53
|
+
on :l, :list, 'List all available online judges.'
|
54
|
+
on :j, :judge=, 'The remote online judge name, required.',
|
55
|
+
:default => ENV['OJ_JUDGE'], :required => ENV['OJ_JUDGE'].nil?, :as => :symbol
|
56
|
+
on :s, :source=, 'The directory of files to processe.'
|
57
|
+
on :t, :dest=, 'The directory for storing processed files.'
|
58
|
+
on :r, :retries=, 'The number of retries for each operation.', :as => :integer
|
59
|
+
on :d, :duration=, 'The time to wait before each retry.', :as => :float
|
60
|
+
on :b, :break=, 'The break between submissions.', :as => :float
|
61
|
+
on :u, :user=, 'Override $OJ_USERNAME, usually required.',
|
62
|
+
:default => ENV['OJ_USERNAME'], :required => ENV['OJ_USERNAME'].nil?
|
63
|
+
on :p, :pass=, 'Override $OJ_PASSWORD, prompt to ask when required.',
|
64
|
+
:default => ENV['OJ_PASSWORD']
|
65
|
+
on :i, :pid=, 'Problem id, extract from filename when omitted.'
|
66
|
+
on :e, :lang=, 'Program language, extract from extension when omitted.',
|
67
|
+
:as => :symbol
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
opts.parse!
|
72
|
+
rescue Slop::Error
|
73
|
+
raise unless opts[:help] || opts[:list]
|
74
|
+
end
|
75
|
+
|
76
|
+
# help
|
77
|
+
exit 0 if opts[:help]
|
78
|
+
|
79
|
+
# list
|
80
|
+
if opts[:list]
|
81
|
+
Agents.to_a.sort.each do |name, agent|
|
82
|
+
puts '%-12s %s' % [name, agent.new.base_uri]
|
83
|
+
end
|
84
|
+
exit 0
|
85
|
+
end
|
86
|
+
|
87
|
+
# init
|
88
|
+
judge = opts[:judge]
|
89
|
+
if Agents[judge].nil?
|
90
|
+
raise Slop::InvalidArgumentError, "#{judge} is not a supported judge"
|
91
|
+
else
|
92
|
+
OJ = Agents[judge].new
|
93
|
+
end
|
94
|
+
|
95
|
+
OJ.retries = opts[:retries] if opts[:retries]
|
96
|
+
OJ.duration = opts[:duration] if opts[:duration]
|
97
|
+
|
98
|
+
user = opts[:user]
|
99
|
+
pass = opts[:pass] ||
|
100
|
+
HighLine.new.ask("#{user}@#{judge}'s password: "){|q| q.echo = false}
|
101
|
+
OJ.login(user, pass)
|
102
|
+
|
103
|
+
DESTINATION = opts[:dest] || '.'
|
104
|
+
|
105
|
+
BREAK = opts[:break] ? opts[:break] : 10
|
106
|
+
|
107
|
+
LOG = Logger.new($stderr)
|
108
|
+
|
109
|
+
# main
|
110
|
+
if opts[:source] || $*.empty?
|
111
|
+
Find.find(opts[:source] || '.') do |path|
|
112
|
+
$* << path if File.file?(path)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
$*.uniq.each do |path|
|
117
|
+
base = File.basename(path)
|
118
|
+
pid = opts[:pid] || base[/^\d{4}/]
|
119
|
+
|
120
|
+
ext = File.extname(path)
|
121
|
+
lang = opts[:lang] || Ext2Lang[ext]
|
122
|
+
|
123
|
+
if pid && lang
|
124
|
+
LOG.info "PROCESS #{path}"
|
125
|
+
else
|
126
|
+
LOG.warn "IGNORE #{path}"
|
127
|
+
next
|
128
|
+
end
|
129
|
+
|
130
|
+
begin
|
131
|
+
code = IO.read(path)
|
132
|
+
id = OJ.submit! pid, code, lang
|
133
|
+
status = OJ.status! id
|
134
|
+
code += to_comment(status, lang)
|
135
|
+
|
136
|
+
FileUtils.mkdir_p(DESTINATION)
|
137
|
+
path = "#{DESTINATION}/#{base}"
|
138
|
+
path.sub!(/#{ext}$/, ".#{Time.now.to_i}#{ext}") if File.exists? path
|
139
|
+
IO.write(path, code)
|
140
|
+
|
141
|
+
LOG.add status[:status] =~ /accepted/i ? Logger::INFO : Logger::WARN, status
|
142
|
+
rescue Exception => ex
|
143
|
+
LOG.error ex
|
144
|
+
end
|
145
|
+
|
146
|
+
sleep BREAK
|
147
|
+
end
|
data/lib/ojagent.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module OJAgent
|
2
|
+
class LiveArchiveAgent < OJAgent
|
3
|
+
def initialize
|
4
|
+
super 'http://icpcarchive.ecs.baylor.edu',
|
5
|
+
:c => '1',
|
6
|
+
:java => '2',
|
7
|
+
:cpp => '3',
|
8
|
+
:pascal => '4'
|
9
|
+
end
|
10
|
+
|
11
|
+
def login(user, pass)
|
12
|
+
page = get '/'
|
13
|
+
login_form = page.form_with :id => 'mod_loginform'
|
14
|
+
login_form.set_fields :username => user,
|
15
|
+
:passwd => pass
|
16
|
+
login_form.checkbox_with(:name => 'remember').check
|
17
|
+
result = login_form.submit
|
18
|
+
end
|
19
|
+
|
20
|
+
def logout
|
21
|
+
end
|
22
|
+
|
23
|
+
def submit(pid, code, lang)
|
24
|
+
page = get '/index.php?option=com_onlinejudge&Itemid=25'
|
25
|
+
action = 'index.php?option=com_onlinejudge&Itemid=25&page=save_submission'
|
26
|
+
submit_form = page.form_with :action => action
|
27
|
+
submit_form.set_fields :localid => pid,
|
28
|
+
:code => code
|
29
|
+
submit_form.radiobutton_with(
|
30
|
+
:name => 'language', :value => languages[lang]).check
|
31
|
+
result = submit_form.submit
|
32
|
+
|
33
|
+
message = result / '.maincontent div.message'
|
34
|
+
if message && message.inner_text =~ /Submission received with ID (\d+)/
|
35
|
+
$1
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
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}
|
46
|
+
|
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
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'mechanize'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module OJAgent
|
6
|
+
class LoginFailureError < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
class OperationFailureError < RuntimeError
|
10
|
+
end
|
11
|
+
|
12
|
+
class OJAgent
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
def_delegators :agent, :submit
|
16
|
+
|
17
|
+
attr_accessor :retries, :duration
|
18
|
+
|
19
|
+
attr_reader :agent, :base_uri, :languages
|
20
|
+
|
21
|
+
def initialize(base_uri, languages)
|
22
|
+
@agent = Mechanize.new
|
23
|
+
@agent.open_timeout = 20
|
24
|
+
@agent.read_timeout = 60
|
25
|
+
@base_uri = base_uri
|
26
|
+
@languages = languages
|
27
|
+
@retries = 10
|
28
|
+
@duration = 2
|
29
|
+
end
|
30
|
+
|
31
|
+
def get(uri, parameters = [], referer = nil, headers = {})
|
32
|
+
agent.get base_uri + uri, parameters, referer
|
33
|
+
end
|
34
|
+
|
35
|
+
def post(uri, query={}, headers={})
|
36
|
+
agent.post base_uri + uri, query, headers
|
37
|
+
end
|
38
|
+
|
39
|
+
def login(user, pass)
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
|
43
|
+
def logout
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
def submit(pid, code, lang)
|
48
|
+
raise NotImplementedError
|
49
|
+
end
|
50
|
+
|
51
|
+
def status(id)
|
52
|
+
raise NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
def submit!(pid, code, lang, retries = nil)
|
56
|
+
retries ||= self.retries
|
57
|
+
retries.times do
|
58
|
+
begin
|
59
|
+
ret = submit pid, code, lang
|
60
|
+
return ret if ret
|
61
|
+
rescue
|
62
|
+
end
|
63
|
+
end
|
64
|
+
raise OperationFailureError, "Fail to submit"
|
65
|
+
end
|
66
|
+
|
67
|
+
def status!(id, retries = nil, duration = nil)
|
68
|
+
retries ||= self.retries
|
69
|
+
duration ||= self.duration
|
70
|
+
ret = nil
|
71
|
+
retries.times do
|
72
|
+
begin
|
73
|
+
ret = status id
|
74
|
+
if ret &&
|
75
|
+
ret[:status] &&
|
76
|
+
ret[:status] =~ /[a-z]/i &&
|
77
|
+
ret[:status] !~ /sent to judge|(?:pend|queu|compil|link|runn|judg)ing/i
|
78
|
+
return ret
|
79
|
+
end
|
80
|
+
rescue
|
81
|
+
end
|
82
|
+
sleep duration
|
83
|
+
end
|
84
|
+
return ret if ret
|
85
|
+
raise OperationFailureError, "Fail to get status"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module OJAgent
|
2
|
+
class TimusAgent < OJAgent
|
3
|
+
attr_accessor :user, :pass
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super 'http://acm.timus.ru',
|
7
|
+
:pascal => '3',
|
8
|
+
:java => '7',
|
9
|
+
:c => '9',
|
10
|
+
:cpp => '10',
|
11
|
+
:cs => '11'
|
12
|
+
end
|
13
|
+
|
14
|
+
def login(user, pass)
|
15
|
+
self.user = user
|
16
|
+
self.pass = pass
|
17
|
+
end
|
18
|
+
|
19
|
+
def logout
|
20
|
+
end
|
21
|
+
|
22
|
+
def submit(pid, code, lang)
|
23
|
+
page = get '/submit.aspx'
|
24
|
+
submit_form = page.form_with :action => 'submit.aspx?space=1'
|
25
|
+
submit_form.set_fields :JudgeID => user + pass,
|
26
|
+
:Language => languages[lang],
|
27
|
+
:ProblemNum => pid,
|
28
|
+
:Source => code
|
29
|
+
submit_form.submit
|
30
|
+
pid
|
31
|
+
end
|
32
|
+
|
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)}
|
38
|
+
|
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+', ' ')
|
44
|
+
end
|
45
|
+
if ret[:pname] =~ /^(\d+).\s*(.*)$/
|
46
|
+
ret[:pid] = $1
|
47
|
+
ret[:pname] = $2
|
48
|
+
end
|
49
|
+
url = status[5] / 'a[href]'
|
50
|
+
ret[:url] = url.map{|a| a['href']} unless url.empty?
|
51
|
+
ret
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/ojagent.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ojagent/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "ojagent"
|
8
|
+
gem.version = OJAgent::VERSION
|
9
|
+
gem.authors = ["Zejun Wu"]
|
10
|
+
gem.email = ["zejun.wu@gmail.com"]
|
11
|
+
gem.description = <<-EOF
|
12
|
+
OJAgent is a client to submit solutions and query status at different online
|
13
|
+
judges. It provides a uniformed interface to a lot of famous online judges.
|
14
|
+
EOF
|
15
|
+
gem.summary = %q{Agent/Bot/Proxy for Online Judges.}
|
16
|
+
gem.homepage = "https://github.com/watashi/ojagent"
|
17
|
+
|
18
|
+
gem.files = `git ls-files`.split($/)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ["lib"]
|
22
|
+
|
23
|
+
gem.add_dependency 'mechanize', '~> 2.5.1'
|
24
|
+
gem.add_dependency 'nokogiri', '~> 1.5.5'
|
25
|
+
gem.add_dependency 'slop', '~> 3.3.3'
|
26
|
+
gem.add_dependency 'highline', '~> 1.6.15'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ojagent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Zejun Wu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mechanize
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.5.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.5.1
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.5.5
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.5.5
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: slop
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 3.3.3
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.3.3
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: highline
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.6.15
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.6.15
|
78
|
+
description: ! 'OJAgent is a client to submit solutions and query status at different
|
79
|
+
online
|
80
|
+
|
81
|
+
judges. It provides a uniformed interface to a lot of famous online judges.
|
82
|
+
|
83
|
+
'
|
84
|
+
email:
|
85
|
+
- zejun.wu@gmail.com
|
86
|
+
executables:
|
87
|
+
- quicksubmit
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- bin/quicksubmit
|
97
|
+
- lib/ojagent.rb
|
98
|
+
- lib/ojagent/livearchive_agent.rb
|
99
|
+
- lib/ojagent/ojagent.rb
|
100
|
+
- lib/ojagent/timus_agent.rb
|
101
|
+
- lib/ojagent/version.rb
|
102
|
+
- ojagent.gemspec
|
103
|
+
homepage: https://github.com/watashi/ojagent
|
104
|
+
licenses: []
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.8.23
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: Agent/Bot/Proxy for Online Judges.
|
127
|
+
test_files: []
|