gettc 1.5 → 1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/gettc +45 -29
- data/core/lib/gettc.rb +7 -0
- data/core/lib/{topcoder → gettc}/download.rb +53 -45
- data/core/lib/gettc/generate.rb +145 -0
- data/core/lib/{topcoder → gettc}/parse.rb +104 -102
- data/core/lib/{topcoder → gettc}/print.rb +11 -11
- data/core/lib/{topcoder → gettc}/problem.rb +11 -11
- data/core/lib/{topcoder → gettc}/signature.rb +8 -8
- data/core/lib/{topcoder → gettc}/types.rb +34 -17
- data/core/test/{topcoder → gettc}/download_test.rb +6 -6
- data/core/test/gettc/generate_test.rb +31 -0
- data/core/test/{topcoder → gettc}/parse_test.rb +26 -26
- data/core/test/gettc/signature_test.rb +54 -0
- data/core/test/gettc/types_test.rb +24 -0
- data/dist/config.yml +1 -0
- data/dist/include/cpp/engine.rb +32 -32
- data/dist/include/cpp/topcoder +89 -25
- data/dist/include/haskell/TopCoder.hs +72 -53
- data/dist/include/haskell/engine.rb +49 -47
- data/dist/include/java/TopCoder.jar +0 -0
- data/dist/include/java/engine.rb +71 -71
- data/dist/include/python/engine.rb +46 -0
- data/dist/include/python/topcoder/__init__.py +3 -0
- data/dist/include/python/topcoder/errors.py +4 -0
- data/dist/include/python/topcoder/reader.py +160 -0
- data/dist/include/python/topcoder/writer.py +13 -0
- data/dist/template/bin/runner.sh +16 -6
- data/dist/template/solve/cpp/Makefile +9 -5
- data/dist/template/solve/cpp/{name}.cpp +8 -8
- data/dist/template/solve/cpp/{name}Runner.cpp +8 -8
- data/dist/template/solve/haskell/Makefile +9 -5
- data/dist/template/solve/haskell/{name}.hs +1 -1
- data/dist/template/solve/haskell/{name}Runner.hs +5 -5
- data/dist/template/solve/java/Makefile +18 -0
- data/dist/template/solve/java/build.xml +7 -3
- data/dist/template/solve/java/{name}.java +1 -1
- data/dist/template/solve/java/{name}Runner.java +1 -1
- data/dist/template/solve/python/Makefile +27 -0
- data/dist/template/solve/python/{name}.py +4 -0
- data/dist/template/solve/python/{name}Runner.py +27 -0
- data/dist/template/util/check/Makefile +3 -1
- data/dist/usage +5 -0
- metadata +30 -24
- data/Rakefile +0 -41
- data/core/lib/topcoder.rb +0 -3
- data/core/lib/topcoder/generate.rb +0 -131
- data/core/test/topcoder/generate_test.rb +0 -31
- data/core/test/topcoder/signature_test.rb +0 -52
- data/core/test/topcoder/types_test.rb +0 -24
- data/dist/template/solve/cpp/{name}Test.cpp +0 -8
- data/dist/template/solve/haskell/{name}Test.hs +0 -10
- data/dist/template/solve/java/{name}Test.java +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20faeb1bc318f83ee20890b3c36a1722711ade15
|
4
|
+
data.tar.gz: f86143dc3392702f57f8b7eba0f5b6d4b9c042dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45124af073a77ce2ca421da0fdc1d03cc24ed0ca75abe080d29ca0e813857644c8d4832d9160e400516d0499c3e0bee0da71485060cdcf97dc9e76d8633f16ef
|
7
|
+
data.tar.gz: d4b0ea56988becb5ed1d7fe91bd0b92d2902ad02ae2d39f9724594c277c7eb2974ec1a10044fce9c755de26c4a2a717dfcb009054bc7e0ca0e800dad5768abc5
|
data/bin/gettc
CHANGED
@@ -1,32 +1,44 @@
|
|
1
1
|
#! /usr/bin/ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
include
|
3
|
+
require "gettc"
|
4
|
+
include Gettc
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
6
|
+
require "rubygems"
|
7
|
+
require "fileutils"
|
8
|
+
require "yaml"
|
9
9
|
|
10
|
-
def
|
11
|
-
|
10
|
+
def init
|
11
|
+
$config_d = ENV.fetch "GETTC_HOME", File.expand_path("~/.gettc")
|
12
|
+
gem_d = Gem.loaded_specs["gettc"]
|
12
13
|
if gem_d.nil? then
|
13
|
-
|
14
|
+
$root_d = File.join File.basename(__FILE__), "../"
|
14
15
|
else
|
15
|
-
|
16
|
+
$root_d = gem_d.full_gem_path
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
def replace_config
|
21
|
+
FileUtils.mkdir $config_d
|
22
|
+
default_d = File.join $root_d, "dist/."
|
23
|
+
FileUtils.cp_r default_d, $config_d
|
24
|
+
end
|
25
|
+
|
19
26
|
def load_config
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
27
|
+
replace_config unless Dir.exists? $config_d
|
28
|
+
config = YAML.load_file File.join($config_d, 'config.yml')
|
29
|
+
|
30
|
+
version = config["version"]
|
31
|
+
unless version.nil? then
|
32
|
+
version = Gem::Version.new version
|
33
|
+
end
|
34
|
+
|
35
|
+
if version.nil? or version < Gem::Version.new(VERSION) then
|
36
|
+
FileUtils.rm_rf $config_d
|
37
|
+
replace_config
|
25
38
|
end
|
26
|
-
|
27
|
-
account = Account.new config[
|
28
|
-
|
29
|
-
return account, source_d
|
39
|
+
|
40
|
+
account = Account.new config["username"], config["password"]
|
41
|
+
return account
|
30
42
|
end
|
31
43
|
|
32
44
|
def main
|
@@ -38,26 +50,30 @@ def main
|
|
38
50
|
id = ARGV[0].to_i
|
39
51
|
puts "You have given ID = #{id}"
|
40
52
|
begin
|
41
|
-
account
|
53
|
+
account = load_config
|
42
54
|
robot = Downloader.new account
|
43
55
|
parser = Parser.new robot
|
44
|
-
generator = Generator.new
|
56
|
+
generator = Generator.new $config_d, Dir.getwd
|
45
57
|
|
46
|
-
print
|
58
|
+
print "Downloading problem to raw HTML ... "
|
47
59
|
html = robot.download_problem id
|
48
|
-
puts
|
60
|
+
puts "Done"
|
49
61
|
|
50
|
-
print
|
62
|
+
print "Parsing problem from raw HTML ... "
|
51
63
|
prob = parser.parse html
|
52
|
-
puts
|
64
|
+
puts "Done"
|
53
65
|
|
54
66
|
print "Generating problem diectory for #{prob.name} ... "
|
55
67
|
generator.generate prob
|
56
|
-
puts
|
57
|
-
rescue =>
|
58
|
-
|
68
|
+
puts "Done"
|
69
|
+
rescue TemplateError => terr
|
70
|
+
puts terr
|
71
|
+
rescue StandardError => err
|
72
|
+
puts err
|
59
73
|
exit -1
|
60
|
-
end
|
61
74
|
end
|
75
|
+
end
|
62
76
|
end
|
63
|
-
|
77
|
+
|
78
|
+
init
|
79
|
+
main
|
data/core/lib/gettc.rb
ADDED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "net/https"
|
2
|
+
require "cgi"
|
3
|
+
require "uri"
|
4
|
+
require "ostruct"
|
5
5
|
|
6
|
-
module
|
6
|
+
module Gettc
|
7
7
|
class Account
|
8
8
|
attr_accessor :username, :password
|
9
9
|
def initialize username, password
|
@@ -31,16 +31,54 @@ module TopCoder
|
|
31
31
|
super "#{msg} (#{id})"
|
32
32
|
end
|
33
33
|
end
|
34
|
+
class ProxyError < DownloadError
|
35
|
+
attr_accessor :proxy
|
36
|
+
def initialize proxy, msg = 'Proxy error'
|
37
|
+
@proxy = proxy
|
38
|
+
super "#{msg}: http_proxy = #{proxy}"
|
39
|
+
end
|
40
|
+
end
|
34
41
|
class Downloader
|
35
|
-
ROOT =
|
42
|
+
ROOT = "http://community.topcoder.com"
|
36
43
|
LIMIT = 10
|
37
44
|
def initialize account
|
38
45
|
@account = account
|
39
46
|
@proxy = get_proxy
|
40
47
|
@raw = get_cookie
|
41
48
|
end
|
49
|
+
def download url
|
50
|
+
uri = url
|
51
|
+
unless uri.is_a? URI then
|
52
|
+
uri = url.start_with?("http") ? URI.parse(url) : URI.join(ROOT, url)
|
53
|
+
end
|
54
|
+
|
55
|
+
connect uri do |http|
|
56
|
+
LIMIT.times do
|
57
|
+
req = Net::HTTP::Get.new uri.request_uri
|
58
|
+
req["cookie"] = @raw
|
59
|
+
res = http.request req
|
60
|
+
|
61
|
+
return res.body if res.is_a? Net::HTTPSuccess
|
62
|
+
unless res.is_a? Net::HTTPMovedPermanently then
|
63
|
+
raise DownloadError.new res.class.to_s
|
64
|
+
end
|
65
|
+
uri = URI.parse res["location"]
|
66
|
+
end
|
67
|
+
|
68
|
+
raise DownloadError.new "Tried #{LIMIT} times without success"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def download_problem id
|
72
|
+
url = "/stat?c=problem_statement&pm=#{id}"
|
73
|
+
body = download url
|
74
|
+
if body.match("<h3>Problem Statement</h3>").nil? then
|
75
|
+
raise IDNotAvailable.new id
|
76
|
+
end
|
77
|
+
return body
|
78
|
+
end
|
79
|
+
private
|
42
80
|
def get_proxy
|
43
|
-
uri = URI.parse ENV[
|
81
|
+
uri = URI.parse ENV["http_proxy"]
|
44
82
|
proxy = OpenStruct.new
|
45
83
|
proxy.host, proxy.port = uri.host, uri.port
|
46
84
|
if uri.userinfo then
|
@@ -63,60 +101,30 @@ module TopCoder
|
|
63
101
|
begin
|
64
102
|
yield http
|
65
103
|
rescue Errno::ECONNRESET
|
66
|
-
raise
|
104
|
+
raise ProxyError.new @proxy
|
67
105
|
end
|
68
106
|
end
|
69
107
|
end
|
70
108
|
end
|
71
109
|
def get_cookie
|
72
|
-
uri = URI.join(ROOT,
|
110
|
+
uri = URI.join(ROOT, "tc?&module=Login")
|
73
111
|
|
74
112
|
req = Net::HTTP::Post.new uri.request_uri
|
75
|
-
req.set_form_data({
|
76
|
-
|
77
|
-
|
113
|
+
req.set_form_data({"username" => @account.username,
|
114
|
+
"password" => @account.password,
|
115
|
+
"rem" => "on" })
|
78
116
|
|
79
117
|
res = connect uri do |http|
|
80
118
|
http.request req
|
81
119
|
end
|
82
|
-
raw = res[
|
120
|
+
raw = res["set-cookie"]
|
83
121
|
|
84
122
|
cookie = CGI::Cookie.parse raw
|
85
|
-
if cookie[
|
123
|
+
if cookie["tcsso"].empty? then
|
86
124
|
raise LoginFailed.new @account, cookie
|
87
125
|
end
|
88
126
|
|
89
127
|
return raw
|
90
128
|
end
|
91
|
-
def download url
|
92
|
-
uri = url
|
93
|
-
unless uri.is_a? URI then
|
94
|
-
uri = url.start_with?('http') ? URI.parse(url) : URI.join(ROOT, url)
|
95
|
-
end
|
96
|
-
|
97
|
-
connect uri do |http|
|
98
|
-
LIMIT.times do
|
99
|
-
req = Net::HTTP::Get.new uri.request_uri
|
100
|
-
req['cookie'] = @raw
|
101
|
-
res = http.request req
|
102
|
-
|
103
|
-
return res.body if res.is_a? Net::HTTPSuccess
|
104
|
-
unless res.is_a? Net::HTTPMovedPermanently then
|
105
|
-
raise DownloadError.new res.class.to_s
|
106
|
-
end
|
107
|
-
uri = URI.parse res['location']
|
108
|
-
end
|
109
|
-
|
110
|
-
raise DownloadError.new "Tried #{LIMIT} times without success"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
def download_problem id
|
114
|
-
url = "/stat?c=problem_statement&pm=#{id}"
|
115
|
-
body = download url
|
116
|
-
if body.match('<h3>Problem Statement</h3>').nil? then
|
117
|
-
raise IDNotAvailable.new id
|
118
|
-
end
|
119
|
-
return body
|
120
|
-
end
|
121
129
|
end
|
122
|
-
end
|
130
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "pathname"
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
require "gettc/problem"
|
6
|
+
require "gettc/signature"
|
7
|
+
require "gettc/print"
|
8
|
+
|
9
|
+
module Gettc
|
10
|
+
GenerateError = Class.new StandardError
|
11
|
+
class ProblemDirExists < GenerateError
|
12
|
+
attr_accessor :dir
|
13
|
+
def initialize dir, msg = nil
|
14
|
+
if msg.nil? then
|
15
|
+
msg = "Cannot create problem directory because it already exists"
|
16
|
+
end
|
17
|
+
@dir = dir
|
18
|
+
super "#{msg} (#{dir})"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
class SourceDirNotExist < GenerateError
|
22
|
+
attr_accessor :dir
|
23
|
+
def initialize dir, msg = "Source directory does not exist"
|
24
|
+
@dir = dir
|
25
|
+
super "#{msg} (#{dir})"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
class TargetDirNotExist < GenerateError
|
29
|
+
attr_accessor :dir
|
30
|
+
def initialize dir, msg = "Target directory does not exist"
|
31
|
+
@dir = dir
|
32
|
+
super "#{msg} (#{dir})"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
class TemplateError < GenerateError
|
36
|
+
attr_accessor :dir
|
37
|
+
def initialize err, source, msg = "Template error"
|
38
|
+
@err = err
|
39
|
+
@source = source
|
40
|
+
super "#{msg} (#{source})\n#{err}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
class Generator
|
44
|
+
def initialize config_d, target_d
|
45
|
+
@source_d = File.join config_d, "template"
|
46
|
+
@target_d = target_d
|
47
|
+
raise SourceDirNotExist.new @source_d unless File.directory? @source_d
|
48
|
+
raise TargetDirNotExist.new @target_d unless File.directory? @target_d
|
49
|
+
|
50
|
+
include_d = File.join config_d, "include"
|
51
|
+
load_engines include_d
|
52
|
+
end
|
53
|
+
def generate prob
|
54
|
+
@prob = prob
|
55
|
+
@prob_d = File.join @target_d, prob.name
|
56
|
+
raise ProblemDirExists.new @prob_d if File.exists? @prob_d
|
57
|
+
FileUtils.mkdir @prob_d
|
58
|
+
|
59
|
+
method_sig = @prob.definitions["Method signature"]
|
60
|
+
if method_sig.nil? then
|
61
|
+
$stderr.puts "[Warning] No definition for method signature found"
|
62
|
+
else
|
63
|
+
vars = parse_method_signature method_sig
|
64
|
+
func = vars.shift
|
65
|
+
end
|
66
|
+
@context = binding
|
67
|
+
|
68
|
+
walk @source_d, @prob_d
|
69
|
+
end
|
70
|
+
private
|
71
|
+
def gen_images images, images_d
|
72
|
+
images.each do |image|
|
73
|
+
filename = File.join images_d, image.name
|
74
|
+
File.open filename, "wb" do |f| f.write image.content end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
def gen_cases cases, data_d
|
78
|
+
cases.each_index do |i|
|
79
|
+
c = cases[i]
|
80
|
+
File.open File.join(data_d, "#{i.to_s}.in"), "w" do |f|
|
81
|
+
f.write c.input
|
82
|
+
end
|
83
|
+
File.open File.join(data_d, "#{i.to_s}.out"), "w" do |f|
|
84
|
+
f.write c.output
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
def gen_template source, target
|
89
|
+
before = File.open source, "r" do |f| f.read end
|
90
|
+
begin
|
91
|
+
after = ERB.new(before).result @context
|
92
|
+
rescue SyntaxError, NameError => err
|
93
|
+
raise TemplateError.new err, source
|
94
|
+
end
|
95
|
+
File.open target, "w" do |f| f.write after end
|
96
|
+
end
|
97
|
+
def filter target_d, name
|
98
|
+
if name == "{images_d}" then
|
99
|
+
gen_images @prob.images, target_d
|
100
|
+
elsif name == "{examples_d}" then
|
101
|
+
gen_cases @prob.examples, target_d
|
102
|
+
elsif name == "{systests_d}" then
|
103
|
+
gen_cases @prob.systests, target_d
|
104
|
+
else
|
105
|
+
target_n = name.gsub /\{(\w*)\}/ do |match|
|
106
|
+
@prob.name if $1 == "name"
|
107
|
+
end
|
108
|
+
return target_n
|
109
|
+
end
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
def load_engines include_d
|
113
|
+
return unless File.exists? include_d
|
114
|
+
Dir.foreach include_d do |name|
|
115
|
+
child = File.join include_d, name
|
116
|
+
if File.directory? child then
|
117
|
+
engine = File.join child, "engine.rb"
|
118
|
+
if File.exists? engine then
|
119
|
+
unless (Pathname.new engine).absolute? then
|
120
|
+
engine = "./" + engine
|
121
|
+
end
|
122
|
+
require engine
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
def walk source_d, target_d
|
128
|
+
Dir.foreach source_d do |name|
|
129
|
+
if name != "." and name != ".." then
|
130
|
+
source_p = File.join source_d, name
|
131
|
+
target_n = filter target_d, name
|
132
|
+
unless target_n.nil? then
|
133
|
+
target_p = File.join target_d, target_n
|
134
|
+
if File.directory? source_p then
|
135
|
+
FileUtils.mkdir target_p unless File.exists? target_p
|
136
|
+
walk source_p, target_p
|
137
|
+
elsif File.file? source_p then
|
138
|
+
gen_template source_p, target_p
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -1,18 +1,81 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "gettc/problem"
|
2
|
+
require "gettc/download"
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
4
|
+
require "uri"
|
5
|
+
require "pathname"
|
6
|
+
require "hpricot"
|
7
7
|
|
8
|
-
module
|
8
|
+
module Gettc
|
9
9
|
class Parser
|
10
10
|
def initialize downloader
|
11
11
|
@downloader = downloader
|
12
12
|
@images = []
|
13
13
|
end
|
14
|
+
def parse html
|
15
|
+
@images = []
|
16
|
+
prob = Problem.new
|
17
|
+
doc = Hpricot(html)
|
18
|
+
|
19
|
+
prob.name = parse_name doc.search("tr/td.statTextBig").html
|
20
|
+
prob.notes = nil
|
21
|
+
prob.constraints = nil
|
22
|
+
prob.examples = nil
|
23
|
+
|
24
|
+
html = doc.search("td.problemText/table").html
|
25
|
+
|
26
|
+
_, x = indexes html, h3("Problem Statement")
|
27
|
+
y, z = indexes html, h3("Definition")
|
28
|
+
prob.statement = parse_statement html[x .. y]
|
14
29
|
|
15
|
-
|
30
|
+
x, y = indexes html, h3("Notes")
|
31
|
+
if x.nil? then
|
32
|
+
prob.notes = []
|
33
|
+
x, y = indexes html, h3("Constraints")
|
34
|
+
if x.nil? then
|
35
|
+
prob.constraints = []
|
36
|
+
x, y = indexes html, h3("Examples")
|
37
|
+
if x.nil? then
|
38
|
+
prob.examples = []
|
39
|
+
x = -2
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
prob.definitions = parse_definitions html[z .. x]
|
44
|
+
|
45
|
+
if prob.notes.nil? then
|
46
|
+
z, x = indexes html, h3("Constraints")
|
47
|
+
if z.nil? then
|
48
|
+
prob.constraints = []
|
49
|
+
z, x = indexes html, h3("Examples")
|
50
|
+
if z.nil? then
|
51
|
+
prob.examples = []
|
52
|
+
z = - 2
|
53
|
+
end
|
54
|
+
end
|
55
|
+
prob.notes = parse_notes html[y .. z]
|
56
|
+
x, y = z, x
|
57
|
+
end
|
58
|
+
|
59
|
+
if prob.constraints.nil? then
|
60
|
+
z, x = indexes html, h3("Examples")
|
61
|
+
if z.nil? then
|
62
|
+
prob.examples = []
|
63
|
+
z = -2
|
64
|
+
end
|
65
|
+
prob.constraints = parse_constraints html[y .. z]
|
66
|
+
end
|
67
|
+
|
68
|
+
if prob.examples.nil? then
|
69
|
+
prob.examples = parse_examples html[x .. -2]
|
70
|
+
end
|
71
|
+
|
72
|
+
prob.images = @images
|
73
|
+
prob.url, prob.source, prob.systests = parse_details doc
|
74
|
+
|
75
|
+
return prob
|
76
|
+
end
|
77
|
+
private
|
78
|
+
## @section General helpers
|
16
79
|
|
17
80
|
def indexes str, substr
|
18
81
|
from = str.index substr
|
@@ -21,13 +84,18 @@ module TopCoder
|
|
21
84
|
return from - 1, to
|
22
85
|
end
|
23
86
|
def filter html
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
html.gsub! /<
|
29
|
-
|
30
|
-
|
87
|
+
html = html.force_encoding("ISO-8859-1").encode("utf-8",
|
88
|
+
invaid: :replace,
|
89
|
+
undef: :replace,
|
90
|
+
replace: "")
|
91
|
+
html.gsub! /<b>(\w*)<\/b>/ do |match|
|
92
|
+
"*#{$1}*"
|
93
|
+
end
|
94
|
+
html.gsub! /<sup>(\w*)<\/sup>/ do |match|
|
95
|
+
"^(#{$1})"
|
96
|
+
end
|
97
|
+
html.gsub! " ", ""
|
98
|
+
html.gsub! " ", " "
|
31
99
|
text = Hpricot(html).to_plain_text
|
32
100
|
text.gsub! /\[img:(http:\/\/[^\]]*)\]/ do |match|
|
33
101
|
url = $1
|
@@ -50,7 +118,7 @@ module TopCoder
|
|
50
118
|
## @section Parse problem parts
|
51
119
|
|
52
120
|
def parse_name html
|
53
|
-
html.sub!
|
121
|
+
html.sub! "Problem Statement for", ""
|
54
122
|
return filter html
|
55
123
|
end
|
56
124
|
def parse_statement html
|
@@ -58,8 +126,8 @@ module TopCoder
|
|
58
126
|
end
|
59
127
|
def parse_definitions html
|
60
128
|
defs = { }
|
61
|
-
Hpricot(html).search
|
62
|
-
tds = tr.search
|
129
|
+
Hpricot(html).search "/tr/td.statText/table/tr" do |tr|
|
130
|
+
tds = tr.search "/td.statText"
|
63
131
|
if tds.size == 2 then
|
64
132
|
key = tds[0].to_plain_text[0 .. -2]
|
65
133
|
value = tds[1].to_plain_text
|
@@ -70,8 +138,8 @@ module TopCoder
|
|
70
138
|
end
|
71
139
|
def parse_notes html
|
72
140
|
notes = []
|
73
|
-
Hpricot(html).search
|
74
|
-
tds = tr.search
|
141
|
+
Hpricot(html).search "/tr" do |tr|
|
142
|
+
tds = tr.search "/td.statText"
|
75
143
|
notes << filter(tds[1].html) if tds.size == 2
|
76
144
|
end
|
77
145
|
return notes
|
@@ -83,13 +151,13 @@ module TopCoder
|
|
83
151
|
## @section Parse cases
|
84
152
|
|
85
153
|
def filter_inout text
|
86
|
-
text.gsub!
|
87
|
-
text.gsub!
|
154
|
+
text.gsub! "{", "["
|
155
|
+
text.gsub! "}", "]"
|
88
156
|
return text.strip
|
89
157
|
end
|
90
158
|
def parse_input html
|
91
159
|
text = nil
|
92
|
-
Hpricot(html).search
|
160
|
+
Hpricot(html).search "/table/tr/td.statText" do |td|
|
93
161
|
input = td.to_plain_text.strip
|
94
162
|
if text.nil? then
|
95
163
|
text = input
|
@@ -101,7 +169,7 @@ module TopCoder
|
|
101
169
|
end
|
102
170
|
def parse_output html
|
103
171
|
text = Hpricot(html).to_plain_text
|
104
|
-
text.sub!
|
172
|
+
text.sub! "Returns: ", ""
|
105
173
|
return filter_inout text
|
106
174
|
end
|
107
175
|
def parse_reason html
|
@@ -109,7 +177,7 @@ module TopCoder
|
|
109
177
|
end
|
110
178
|
def parse_examples html
|
111
179
|
examples = []
|
112
|
-
tds = Hpricot(html).search(
|
180
|
+
tds = Hpricot(html).search("/tr/td.statText/table/tr/td.statText")
|
113
181
|
i = 0
|
114
182
|
while i < tds.size do
|
115
183
|
example = Case.new
|
@@ -123,11 +191,11 @@ module TopCoder
|
|
123
191
|
end
|
124
192
|
def parse_systests html
|
125
193
|
systests = []
|
126
|
-
_, y = indexes html,
|
127
|
-
z, _ = indexes html,
|
128
|
-
return systests
|
129
|
-
Hpricot(html[y .. z]).search
|
130
|
-
tds = tr.search
|
194
|
+
_, y = indexes html, "<!-- System Testing -->"
|
195
|
+
z, _ = indexes html, "<!-- End System Testing -->"
|
196
|
+
return systests unless y and z
|
197
|
+
Hpricot(html[y .. z]).search "/table/tr[@valign=top]" do |tr|
|
198
|
+
tds = tr.search "/td.statText"
|
131
199
|
if tds.size == 3 then
|
132
200
|
test = Case.new
|
133
201
|
test.input = filter_inout tds[0].to_plain_text
|
@@ -139,17 +207,17 @@ module TopCoder
|
|
139
207
|
end
|
140
208
|
def download_systests detail_url
|
141
209
|
detail = @downloader.download detail_url
|
142
|
-
Hpricot(detail).search
|
143
|
-
solution = @downloader.download url.attributes[
|
210
|
+
Hpricot(detail).search "a[@href^=/stat?c=problem_solution]" do |url|
|
211
|
+
solution = @downloader.download url.attributes["href"]
|
144
212
|
systests = parse_systests solution
|
145
|
-
return systests
|
213
|
+
return systests unless systests.empty?
|
146
214
|
end
|
147
215
|
return []
|
148
216
|
end
|
149
217
|
def parse_details doc
|
150
|
-
url, source, systests =
|
151
|
-
doc.search
|
152
|
-
url = URI.join(Downloader::ROOT, elem.attributes[
|
218
|
+
url, source, systests = "", "", []
|
219
|
+
doc.search "a[@href^=/tc?module=ProblemDetail]" do |elem|
|
220
|
+
url = URI.join(Downloader::ROOT, elem.attributes["href"]).to_s
|
153
221
|
source = filter elem.html
|
154
222
|
begin
|
155
223
|
systests = download_systests url
|
@@ -161,71 +229,5 @@ module TopCoder
|
|
161
229
|
end
|
162
230
|
return url, source, systests
|
163
231
|
end
|
164
|
-
|
165
|
-
## @section Main method
|
166
|
-
|
167
|
-
def parse html
|
168
|
-
@images = []
|
169
|
-
prob = Problem.new
|
170
|
-
doc = Hpricot(html)
|
171
|
-
|
172
|
-
prob.name = parse_name doc.search('tr/td.statTextBig').html
|
173
|
-
prob.notes = nil
|
174
|
-
prob.constraints = nil
|
175
|
-
prob.examples = nil
|
176
|
-
|
177
|
-
html = doc.search('td.problemText/table').html
|
178
|
-
|
179
|
-
_, x = indexes html, h3('Problem Statement')
|
180
|
-
y, z = indexes html, h3('Definition')
|
181
|
-
prob.statement = parse_statement html[x .. y]
|
182
|
-
|
183
|
-
x, y = indexes html, h3('Notes')
|
184
|
-
if x.nil? then
|
185
|
-
prob.notes = []
|
186
|
-
x, y = indexes html, h3('Constraints')
|
187
|
-
if x.nil? then
|
188
|
-
prob.constraints = []
|
189
|
-
x, y = indexes html, h3('Examples')
|
190
|
-
if x.nil? then
|
191
|
-
prob.examples = []
|
192
|
-
x = -2
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
prob.definitions = parse_definitions html[z .. x]
|
197
|
-
|
198
|
-
if prob.notes.nil? then
|
199
|
-
z, x = indexes html, h3('Constraints')
|
200
|
-
if z.nil? then
|
201
|
-
prob.constraints = []
|
202
|
-
z, x = indexes html, h3('Examples')
|
203
|
-
if z.nil? then
|
204
|
-
prob.examples = []
|
205
|
-
z = - 2
|
206
|
-
end
|
207
|
-
end
|
208
|
-
prob.notes = parse_notes html[y .. z]
|
209
|
-
x, y = z, x
|
210
|
-
end
|
211
|
-
|
212
|
-
if prob.constraints.nil? then
|
213
|
-
z, x = indexes html, h3('Examples')
|
214
|
-
if z.nil? then
|
215
|
-
prob.examples = []
|
216
|
-
z = -2
|
217
|
-
end
|
218
|
-
prob.constraints = parse_constraints html[y .. z]
|
219
|
-
end
|
220
|
-
|
221
|
-
if prob.examples.nil? then
|
222
|
-
prob.examples = parse_examples html[x .. -2]
|
223
|
-
end
|
224
|
-
|
225
|
-
prob.images = @images
|
226
|
-
prob.url, prob.source, prob.systests = parse_details doc
|
227
|
-
|
228
|
-
return prob
|
229
|
-
end
|
230
232
|
end
|
231
|
-
end
|
233
|
+
end
|