mlb_terminal 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +38 -10
- data/bin/mlb +57 -11
- data/lib/mlb_terminal.rb +1 -1
- data/lib/mlb_terminal/game.rb +153 -0
- data/lib/mlb_terminal/version.rb +1 -1
- data/mlb_terminal.gemspec +2 -2
- metadata +6 -5
- data/lib/mlb_terminal/client.rb +0 -85
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,22 +9,23 @@ Don't you just wish you could have a little terminal app to stream out real-time
|
|
9
9
|
### Syntax
|
10
10
|
|
11
11
|
#### Commands
|
12
|
-
|
13
|
-
* help: Display global or [command] help documentation.
|
14
|
-
*
|
12
|
+
|
13
|
+
* help: Display global or [command] help documentation.
|
14
|
+
* games: Print out a list of scheduled games for the specified date
|
15
|
+
* game: Print out play-by-play events for a specific game. Use `--pitches` to output realtime pitch trajectory data.
|
15
16
|
|
16
17
|
#### Global options
|
17
|
-
|
18
|
+
|
18
19
|
* -h, --help: Display help documentation
|
19
|
-
|
20
|
+
|
20
21
|
* -v, --version: Display version information
|
21
|
-
|
22
|
+
|
22
23
|
* -t, --trace: Display backtrace when an error occurs
|
23
24
|
|
24
25
|
### Examples
|
25
26
|
|
26
27
|
> # List todays games
|
27
|
-
> mlb
|
28
|
+
> mlb games
|
28
29
|
0 Red Sox (69-88) @ Orioles (90-67) 7:05 ET (F) 1-9
|
29
30
|
1 Reds (95-62) @ Pirates (76-81) 7:05 ET (F) 1-0
|
30
31
|
2 Royals (70-87) @ Indians (66-91) 7:05 ET (F) 5-8
|
@@ -41,6 +42,33 @@ Don't you just wish you could have a little terminal app to stream out real-time
|
|
41
42
|
13 Giants (92-65) @ Padres (74-83) 10:05 ET (F) 3-1
|
42
43
|
14 Rockies (62-95) @ Dodgers (82-75) 10:10 ET (F) 0-8
|
43
44
|
|
44
|
-
# How did the Nationals do on
|
45
|
-
> mlb
|
46
|
-
Nationals (29-21) @ Marlins (29-22)
|
45
|
+
# How did the Nationals do on 5/30/2012?
|
46
|
+
> mlb games --date 2012-05-30 | grep -i nationals | cut -f 2,4
|
47
|
+
Nationals (29-21) @ Marlins (29-22) 3-5
|
48
|
+
|
49
|
+
> # How did Bryce Harper do tonight?
|
50
|
+
> mlb games | grep Nationals | cut -f 1 | xargs mlb game | grep Harper
|
51
|
+
2012-09-29 19:18:00 1 2 1/2/1 Bryce Harper singles on a soft line drive to center fielder Jon Jay.
|
52
|
+
2012-09-29 19:20:11 1 3 1/2/1 Ryan Zimmerman doubles (36) on a fly ball to left fielder Matt Holliday. Bryce Harper to 3rd.
|
53
|
+
2012-09-29 19:26:56 1 5 0/0/1 Play reviewed and overturned: Michael Morse hits a grand slam (17) to right field. Bryce Harper scores. Ryan Zimmerman scores. Adam LaRoche scores.
|
54
|
+
2012-09-29 19:48:35 2 14 3/2/3 Bryce Harper grounds out, second baseman Skip Schumaker to first baseman Allen Craig.
|
55
|
+
2012-09-29 19:56:08 2 17 3/2/2 Carlos Beltran lines out to center fielder Bryce Harper.
|
56
|
+
2012-09-29 20:09:19 3 23 3/2/1 Pete Kozma flies out to center fielder Bryce Harper.
|
57
|
+
2012-09-29 20:38:38 5 36 3/3/1 Bryce Harper called out on strikes.
|
58
|
+
2012-09-29 21:15:16 7 51 2/2/1 Bryce Harper singles on a line drive to left fielder Matt Holliday.
|
59
|
+
2012-09-29 21:19:08 7 53 1/1/2 Adam LaRoche singles on a sharp line drive to first baseman Allen Craig. Bryce Harper to 3rd.
|
60
|
+
2012-09-29 21:24:34 7 55 0/1/1 Yadier Molina flies out to center fielder Bryce Harper.
|
61
|
+
2012-09-29 21:37:46 7 61 1/2/2 Matt Carpenter flies out to center fielder Bryce Harper.
|
62
|
+
2012-09-29 22:10:49 9 71 1/0/2 Bryce Harper doubles (25) on a line drive to left fielder Matt Holliday.
|
63
|
+
2012-09-29 22:21:20 9 76 0/0/2 Jon Jay out on a sacrifice fly to center fielder Bryce Harper. Pete Kozma scores.
|
64
|
+
|
65
|
+
> # Visualize pitch speed for Gio Gonzalez on his full game on August 8, 2012.
|
66
|
+
> mlb games --date 2012-08-08 \
|
67
|
+
| grep Nationals \
|
68
|
+
| cut -f 1 \
|
69
|
+
| xargs mlb game --pitches --date 2012-08-08 \
|
70
|
+
| awk 'BEGIN{FS="\t"}{ if ($4=="Gio Gonzalez") print}' \
|
71
|
+
| cut -f 1,9,6 \
|
72
|
+
| Rscript -e 'library(ggplot2); library(scales); d <- read.csv("stdin", header=F, sep="\\t", col.names=c("time", "type", "pitch_speed")); png("gio-pitch-speed.png"); ggplot(d, aes(x=as.POSIXct(time), y=pitch_speed, colour=type, shape=type)) + geom_point() + opts(title="Pitch Speed Over Time for Gio Gonzalez") + scale_x_datetime(breaks=date_breaks("1 hour"), minor_breaks=date_breaks("15 min"), labels=date_format("%H:%M")) + scale_colour_discrete(name = "Pitch Type") + scale_shape_discrete(name = "Pitch Type") + xlab("Time") + ylab("Pitch Speed"); dev.off()'
|
73
|
+
> open gio-pitch-speed.png
|
74
|
+
![gio-pitch-speed.png](http://i.imgur.com/WFw3J.png)
|
data/bin/mlb
CHANGED
@@ -17,7 +17,7 @@ command :games do |c|
|
|
17
17
|
c.option '--date STRING', String, 'List games for specified dates (Default: today)'
|
18
18
|
c.action do |args, options|
|
19
19
|
options.default :date => Time.now.to_date.to_s
|
20
|
-
MLBTerminal::
|
20
|
+
MLBTerminal::Game.list(Date.parse options.date).each_with_index do |game, index|
|
21
21
|
puts [
|
22
22
|
index,
|
23
23
|
"#{game[:away_team][:name]} " \
|
@@ -36,6 +36,7 @@ command :game do |c|
|
|
36
36
|
c.description = 'Print play-by-play action for the corresponding game. Columns: time, inning, balls/strikes/outs/description.'
|
37
37
|
c.example 'description', 'command example'
|
38
38
|
c.option '--date STRING', String, 'Specify the date for the corresponding game number.'
|
39
|
+
c.option '--pitches', 'Print pitch-by-pitch action'
|
39
40
|
c.action do |args, options|
|
40
41
|
options.default :date => Time.now.to_date.to_s
|
41
42
|
|
@@ -47,23 +48,68 @@ command :game do |c|
|
|
47
48
|
end
|
48
49
|
|
49
50
|
game_number = args.first.to_i
|
50
|
-
game_info = MLBTerminal::
|
51
|
+
game_info = MLBTerminal::Game.list(date)[game_number]
|
51
52
|
|
52
53
|
if game_info.nil?
|
53
54
|
puts "Error: Game does not exist"
|
54
55
|
break
|
55
56
|
end
|
56
57
|
|
57
|
-
game = MLBTerminal::
|
58
|
+
game = MLBTerminal::Game.new(game_info[:game_id])
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
60
|
+
begin
|
61
|
+
if options.pitches
|
62
|
+
game.pitches.each do |pitch|
|
63
|
+
puts [
|
64
|
+
pitch[:time].getlocal.strftime("%Y-%m-%d %H:%M:%S"),
|
65
|
+
pitch[:inning],
|
66
|
+
pitch[:inning_loc],
|
67
|
+
pitch[:pitcher],
|
68
|
+
pitch[:batter],
|
69
|
+
pitch[:type],
|
70
|
+
pitch[:x],
|
71
|
+
pitch[:y],
|
72
|
+
pitch[:start_speed],
|
73
|
+
pitch[:end_speed],
|
74
|
+
pitch[:sz_top],
|
75
|
+
pitch[:sz_bot],
|
76
|
+
pitch[:pfx_x],
|
77
|
+
pitch[:pfx_z],
|
78
|
+
pitch[:px],
|
79
|
+
pitch[:pz],
|
80
|
+
pitch[:x0],
|
81
|
+
pitch[:y0],
|
82
|
+
pitch[:z0],
|
83
|
+
pitch[:vx0],
|
84
|
+
pitch[:vy0],
|
85
|
+
pitch[:vz0],
|
86
|
+
pitch[:ax],
|
87
|
+
pitch[:ay],
|
88
|
+
pitch[:az],
|
89
|
+
pitch[:break_y],
|
90
|
+
pitch[:break_angle],
|
91
|
+
pitch[:break_length],
|
92
|
+
pitch[:pitch_type],
|
93
|
+
pitch[:type_confidence],
|
94
|
+
pitch[:zone],
|
95
|
+
pitch[:nasty],
|
96
|
+
pitch[:spin_dir],
|
97
|
+
pitch[:spin_rate],
|
98
|
+
pitch[:cc],
|
99
|
+
pitch[:mt]].join("\t")
|
100
|
+
end
|
101
|
+
else
|
102
|
+
game.events.each do |event|
|
103
|
+
puts [
|
104
|
+
event[:time].getlocal.strftime("%Y-%m-%d %H:%M:%S"),
|
105
|
+
event[:inning],
|
106
|
+
event[:num],
|
107
|
+
"#{event[:balls]}/#{event[:strikes]}/#{event[:outs]}",
|
108
|
+
event[:desc]].join("\t")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
rescue SystemExit, Interrupt
|
112
|
+
# Do nothing if a user issues ctrl-c.
|
66
113
|
end
|
67
114
|
end
|
68
115
|
end
|
69
|
-
|
data/lib/mlb_terminal.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'mlb_terminal/version'
|
2
|
-
require 'mlb_terminal/
|
2
|
+
require 'mlb_terminal/game'
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'active_support/core_ext/integer/inflections'
|
5
|
+
|
6
|
+
module MLBTerminal
|
7
|
+
MLB_BASE_URL = 'http://gd2.mlb.com/components/game/mlb'
|
8
|
+
|
9
|
+
class Game
|
10
|
+
def self.list(date = Time.now.to_date)
|
11
|
+
doc = Nokogiri::HTML(open "#{base_url_for_date date}/epg.xml")
|
12
|
+
|
13
|
+
doc.xpath("//epg/game").map{|game|
|
14
|
+
{:home_team => {
|
15
|
+
:name => game["home_team_name"],
|
16
|
+
:wins => game["home_win"],
|
17
|
+
:losses => game["home_loss"]},
|
18
|
+
:away_team => {
|
19
|
+
:name => game["away_team_name"],
|
20
|
+
:wins => game["away_win"],
|
21
|
+
:losses => game["away_loss"]},
|
22
|
+
:score => {
|
23
|
+
:home => game["home_team_runs"],
|
24
|
+
:away => game["away_team_runs"]},
|
25
|
+
:starts => "#{game["time"]} #{game["time_zone"]}",
|
26
|
+
:status => "#{game["status"]}" \
|
27
|
+
"#{game["status"] == "In Progress" ?
|
28
|
+
", #{game["top_inning"]=="Y" ? "Top" : "Bot"} of #{game["inning"].to_i.ordinalize}" :
|
29
|
+
""}",
|
30
|
+
:game_id => game["gameday"]}}
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(gameday)
|
34
|
+
@gameday = gameday
|
35
|
+
year, month, day, game_name = /([0-9]{4})_([0-9]{2})_([0-9]{2})_(.*)/.match(@gameday).to_a.slice(1,4)
|
36
|
+
@base_url = "#{Game.base_url_for_date(Date.new(year.to_i, month.to_i, day.to_i))}/gid_#{@gameday}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def events(delay = 5, &block)
|
40
|
+
last_atbat = 0
|
41
|
+
|
42
|
+
Enumerator.new do |y|
|
43
|
+
begin
|
44
|
+
doc = Nokogiri::HTML(open "#{@base_url}/game_events.xml")
|
45
|
+
|
46
|
+
doc.xpath("//game/inning[*/atbat/@num > #{last_atbat}]").each do |inning|
|
47
|
+
inning.xpath("*/atbat").each do |at_bat|
|
48
|
+
y.yield({
|
49
|
+
:inning => inning["num"],
|
50
|
+
:inning_loc => at_bat.xpath("..").first.name.camelcase,
|
51
|
+
:num => at_bat["num"],
|
52
|
+
:balls => at_bat["b"],
|
53
|
+
:strikes => at_bat["s"],
|
54
|
+
:outs => at_bat["o"],
|
55
|
+
:time => Time.parse(at_bat["start_tfs_zulu"], "%Y-%m-%dT%H:%M:%SZ"),
|
56
|
+
:desc => at_bat["des"].strip})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
last_atbat = doc.xpath("//game/inning/*/atbat/@num").map(&:value).map(&:to_i).max
|
61
|
+
|
62
|
+
sleep delay
|
63
|
+
|
64
|
+
if doc.xpath("//game").first["ind"] != "F"
|
65
|
+
doc = Nokogiri::HTML(open "#{@base_url}/inning/inning_all.xml")
|
66
|
+
end
|
67
|
+
end while game_status != "F"
|
68
|
+
end.each(&block)
|
69
|
+
end
|
70
|
+
|
71
|
+
def pitches(delay = 5, &block)
|
72
|
+
last_pitch = 0
|
73
|
+
players = player_lookup
|
74
|
+
doc = Nokogiri::HTML(open "#{@base_url}/inning/inning_all.xml")
|
75
|
+
|
76
|
+
Enumerator.new do |y|
|
77
|
+
begin
|
78
|
+
doc.xpath("//game/inning[*/*/pitch/@id > #{last_pitch}]").each do |inning|
|
79
|
+
|
80
|
+
inning.xpath("*/atbat").each do |at_bat|
|
81
|
+
pitcher = players[at_bat["pitcher"]]
|
82
|
+
batter = players[at_bat["batter"]]
|
83
|
+
|
84
|
+
at_bat.xpath("pitch").each do |pitch|
|
85
|
+
y.yield({
|
86
|
+
:time => Time.parse(pitch["tfs_zulu"], "%Y-%m-%dT%H:%M:%SZ"),
|
87
|
+
:inning => inning["num"],
|
88
|
+
:inning_loc => at_bat.xpath("..").first.name.camelcase,
|
89
|
+
:pitcher => pitcher,
|
90
|
+
:batter => batter,
|
91
|
+
:type => pitch["type"],
|
92
|
+
:x => pitch["x"],
|
93
|
+
:y => pitch["y"],
|
94
|
+
:start_speed => pitch["start_speed"],
|
95
|
+
:end_speed => pitch["end_speed"],
|
96
|
+
:sz_top => pitch["sz_top"],
|
97
|
+
:sz_bot => pitch["sz_bot"],
|
98
|
+
:pfx_x => pitch["pfx_x"],
|
99
|
+
:pfx_z => pitch["pfx_z"],
|
100
|
+
:px => pitch["px"],
|
101
|
+
:pz => pitch["pz"],
|
102
|
+
:x0 => pitch["x0"],
|
103
|
+
:y0 => pitch["y0"],
|
104
|
+
:z0 => pitch["z0"],
|
105
|
+
:vx0 => pitch["vx0"],
|
106
|
+
:vy0 => pitch["vy0"],
|
107
|
+
:vz0 => pitch["vz0"],
|
108
|
+
:ax => pitch["ax"],
|
109
|
+
:ay => pitch["ay"],
|
110
|
+
:az => pitch["az"],
|
111
|
+
:break_y => pitch["break_y"],
|
112
|
+
:break_angle => pitch["break_angle"],
|
113
|
+
:break_length => pitch["break_length"],
|
114
|
+
:pitch_type => pitch["pitch_type"],
|
115
|
+
:type_confidence => pitch["type_confidence"],
|
116
|
+
:zone => pitch["zone"],
|
117
|
+
:nasty => pitch["nasty"],
|
118
|
+
:spin_dir => pitch["spin_dir"],
|
119
|
+
:spin_rate => pitch["spin_rate"],
|
120
|
+
:cc => pitch["cc"],
|
121
|
+
:mt => pitch["mt"]})
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
last_pitch = doc.xpath("//game/inning/*/*/pitch/@id").map(&:value).map(&:to_i).max
|
127
|
+
|
128
|
+
sleep delay
|
129
|
+
|
130
|
+
if doc.xpath("//game").first["ind"] != "F"
|
131
|
+
doc = Nokogiri::HTML(open "#{@base_url}/inning/inning_all.xml")
|
132
|
+
end
|
133
|
+
end while game_status != "F"
|
134
|
+
end.each(&block)
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.base_url_for_date(date = Time.now.to_date)
|
138
|
+
"#{MLB_BASE_URL}/year_#{date.year}/month_#{"%02d" % date.month}/day_#{"%02d" % date.day}"
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def player_lookup
|
144
|
+
doc = Nokogiri::HTML(open( "#{@base_url}/players.xml"))
|
145
|
+
Hash[doc.xpath("//game/team/player").map{|x| [x["id"], "#{x["first"]} #{x["last"]}"]}]
|
146
|
+
end
|
147
|
+
|
148
|
+
def game_status
|
149
|
+
doc = Nokogiri::HTML(open( "#{@base_url}/linescore.xml"))
|
150
|
+
doc.xpath("//game").first["ind"]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/mlb_terminal/version.rb
CHANGED
data/mlb_terminal.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Stefan Novak"]
|
10
10
|
s.email = ["stefan.louis.novak@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/slnovak/mlb_terminal"
|
12
|
-
s.description = %q{A small terminal app to
|
13
|
-
s.summary = %q{
|
12
|
+
s.description = %q{A small terminal app to pipe formatted MLB baseball feeds into the terminal for easy manipulation. Includes access to both historic and real-time baseball feeds including play-by-play and pitch-by-pitch data. Visit the homepage for examples!}
|
13
|
+
s.summary = %q{A small terminal app for accessing historic and realtime MLB game data}
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n")
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mlb_terminal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -59,8 +59,9 @@ dependencies:
|
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 3.2.8
|
62
|
-
description: A small terminal app to
|
63
|
-
for easy manipulation.
|
62
|
+
description: A small terminal app to pipe formatted MLB baseball feeds into the terminal
|
63
|
+
for easy manipulation. Includes access to both historic and real-time baseball feeds
|
64
|
+
including play-by-play and pitch-by-pitch data. Visit the homepage for examples!
|
64
65
|
email:
|
65
66
|
- stefan.louis.novak@gmail.com
|
66
67
|
executables:
|
@@ -75,7 +76,7 @@ files:
|
|
75
76
|
- Rakefile
|
76
77
|
- bin/mlb
|
77
78
|
- lib/mlb_terminal.rb
|
78
|
-
- lib/mlb_terminal/
|
79
|
+
- lib/mlb_terminal/game.rb
|
79
80
|
- lib/mlb_terminal/version.rb
|
80
81
|
- mlb_terminal.gemspec
|
81
82
|
homepage: https://github.com/slnovak/mlb_terminal
|
@@ -101,5 +102,5 @@ rubyforge_project:
|
|
101
102
|
rubygems_version: 1.8.24
|
102
103
|
signing_key:
|
103
104
|
specification_version: 3
|
104
|
-
summary:
|
105
|
+
summary: A small terminal app for accessing historic and realtime MLB game data
|
105
106
|
test_files: []
|
data/lib/mlb_terminal/client.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'date'
|
2
|
-
require 'nokogiri'
|
3
|
-
require 'open-uri'
|
4
|
-
require 'active_support/core_ext/integer/inflections'
|
5
|
-
|
6
|
-
module MLBTerminal
|
7
|
-
MLB_BASE_URL = 'http://gd2.mlb.com/components/game/mlb'
|
8
|
-
|
9
|
-
class Client
|
10
|
-
def self.list_games(date = Time.now.to_date)
|
11
|
-
doc = Nokogiri::HTML(open "#{base_url_for_date date}/epg.xml")
|
12
|
-
doc.xpath("//epg/game").map{|game|
|
13
|
-
{:home_team => {
|
14
|
-
:name => game["home_team_name"],
|
15
|
-
:wins => game["home_win"],
|
16
|
-
:losses => game["home_loss"]},
|
17
|
-
:away_team => {
|
18
|
-
:name => game["away_team_name"],
|
19
|
-
:wins => game["away_win"],
|
20
|
-
:losses => game["away_loss"]},
|
21
|
-
:score => {
|
22
|
-
:home => game["home_team_runs"],
|
23
|
-
:away => game["away_team_runs"]},
|
24
|
-
:starts => "#{game["time"]} #{game["time_zone"]}",
|
25
|
-
:status => "#{game["status"]}" \
|
26
|
-
"#{game["status"] == "In Progress" ?
|
27
|
-
", #{game["top_inning"]=="Y" ? "Top" : "Bot"} of #{game["inning"].to_i.ordinalize}" :
|
28
|
-
""}",
|
29
|
-
:game_id => game["gameday"]}}
|
30
|
-
end
|
31
|
-
|
32
|
-
def initialize(gameday)
|
33
|
-
@gameday = gameday
|
34
|
-
year, month, day, game_name = /([0-9]{4})_([0-9]{2})_([0-9]{2})_(.*)/.match(@gameday).to_a.slice(1,4)
|
35
|
-
@base_url = "#{Client.base_url_for_date(Date.new(year.to_i, month.to_i, day.to_i))}/gid_#{@gameday}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def events(start_inning = 1, end_inning = 9, delay = 5, &block)
|
39
|
-
current_inning = start_inning
|
40
|
-
current_inning_loc= "top"
|
41
|
-
|
42
|
-
innings = ["top", "bottom"]
|
43
|
-
|
44
|
-
Enumerator.new do |y|
|
45
|
-
while current_inning <= end_inning and not current_inning_loc.nil?
|
46
|
-
doc = Nokogiri::HTML(open "#{@base_url}/game_events.xml")
|
47
|
-
|
48
|
-
doc.xpath("//game/inning[@num >= #{current_inning}]").each do |inning|
|
49
|
-
|
50
|
-
current_inning_loc ||= "top"
|
51
|
-
|
52
|
-
innings.slice(innings.index(current_inning_loc), 2).each do |inning_loc|
|
53
|
-
if inning.xpath(inning_loc).nil?
|
54
|
-
break
|
55
|
-
else
|
56
|
-
inning.xpath("#{inning_loc}/atbat").each do |atbat|
|
57
|
-
y.yield({:inning => current_inning,
|
58
|
-
:num => atbat["num"],
|
59
|
-
:balls => atbat["b"],
|
60
|
-
:strikes => atbat["s"],
|
61
|
-
:outs => atbat["o"],
|
62
|
-
:time => Time.parse(atbat["start_tfs_zulu"], "%Y-%m-%dT%H:%M:%SZ"),
|
63
|
-
:desc => atbat["des"]})
|
64
|
-
end
|
65
|
-
|
66
|
-
current_inning_loc = innings[innings.index(current_inning_loc) + 1]
|
67
|
-
end
|
68
|
-
end
|
69
|
-
current_inning += 1
|
70
|
-
end
|
71
|
-
|
72
|
-
# If we had to break early, wait before trying again.
|
73
|
-
if current_inning != end_inning and not current_inning_loc.nil?
|
74
|
-
sleep delay
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
end.each(&block)
|
79
|
-
end
|
80
|
-
|
81
|
-
def self.base_url_for_date(date = Time.now.to_date)
|
82
|
-
"#{MLB_BASE_URL}/year_#{date.year}/month_#{"%02d" % date.month}/day_#{"%02d" % date.day}"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|