gettc 1.5 → 1.6
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.
- 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
|