line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Sport::Analytics::NHL::Report; |
2
|
|
|
|
|
|
|
|
3
|
52
|
|
|
52
|
|
220286
|
use v5.10.1; |
|
52
|
|
|
|
|
200
|
|
4
|
52
|
|
|
52
|
|
258
|
use strict; |
|
52
|
|
|
|
|
104
|
|
|
52
|
|
|
|
|
1284
|
|
5
|
52
|
|
|
52
|
|
261
|
use warnings FATAL => 'all'; |
|
52
|
|
|
|
|
103
|
|
|
52
|
|
|
|
|
1836
|
|
6
|
52
|
|
|
52
|
|
316
|
use experimental qw(smartmatch); |
|
52
|
|
|
|
|
113
|
|
|
52
|
|
|
|
|
296
|
|
7
|
|
|
|
|
|
|
|
8
|
52
|
|
|
52
|
|
9674
|
use Storable; |
|
52
|
|
|
|
|
35173
|
|
|
52
|
|
|
|
|
2691
|
|
9
|
|
|
|
|
|
|
|
10
|
52
|
|
|
52
|
|
20053
|
use Date::Calc qw(Decode_Date_US Decode_Date_EU); |
|
52
|
|
|
|
|
284340
|
|
|
52
|
|
|
|
|
3838
|
|
11
|
52
|
|
|
52
|
|
31102
|
use HTML::TreeBuilder; |
|
52
|
|
|
|
|
1454221
|
|
|
52
|
|
|
|
|
611
|
|
12
|
52
|
|
|
52
|
|
10967
|
use List::MoreUtils qw(firstval); |
|
52
|
|
|
|
|
177787
|
|
|
52
|
|
|
|
|
530
|
|
13
|
52
|
|
|
52
|
|
61942
|
use Module::Pluggable require => 1, search_path => ['Sport::Analytics::NHL::Report']; |
|
52
|
|
|
|
|
471362
|
|
|
52
|
|
|
|
|
404
|
|
14
|
52
|
|
|
52
|
|
11517
|
use Time::Local; |
|
52
|
|
|
|
|
22318
|
|
|
52
|
|
|
|
|
2994
|
|
15
|
|
|
|
|
|
|
|
16
|
52
|
|
|
52
|
|
6434
|
use Sport::Analytics::NHL::Util; |
|
52
|
|
|
|
|
119
|
|
|
52
|
|
|
|
|
3623
|
|
17
|
52
|
|
|
52
|
|
7689
|
use Sport::Analytics::NHL::Tools; |
|
52
|
|
|
|
|
129
|
|
|
52
|
|
|
|
|
10590
|
|
18
|
52
|
|
|
52
|
|
367
|
use Sport::Analytics::NHL::Config; |
|
52
|
|
|
|
|
118
|
|
|
52
|
|
|
|
|
10162
|
|
19
|
52
|
|
|
52
|
|
13929
|
use Sport::Analytics::NHL::Errors; |
|
52
|
|
|
|
|
312
|
|
|
52
|
|
|
|
|
10909
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 NAME |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Sport::Analytics::NHL::Report - Generic class for an NHL report |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSYS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Generic class for an NHL report |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Contains methods common for most (usually HTML) or all NHL reports. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
use Sport::Analytics::NHL::Report; |
32
|
|
|
|
|
|
|
my $report = Sport::Analytics::NHL::Report->new($args); |
33
|
|
|
|
|
|
|
$report->process(); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 METHODS |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=over 2 |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=item C |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Common constructor wrapper. Assigns the report plugin, and initializes the report object. For a json report usually an overloaded constructor is required. For an HTML report, the generic html_new (q.v.) method is usually sufficient. |
42
|
|
|
|
|
|
|
Arguments: the arguments hashref |
43
|
|
|
|
|
|
|
* file: the file with the report OR |
44
|
|
|
|
|
|
|
* data: the scalar with the report |
45
|
|
|
|
|
|
|
BUT NOT BOTH |
46
|
|
|
|
|
|
|
* type: explicitly specify the type of the report |
47
|
|
|
|
|
|
|
Returns: the blessed object of one of the Report's Plugins. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
The object represents an NHL game. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=item C |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Specific constructor for the HTML reports. Parses the HTML using HTML::TreeBuilder, immediately storing the tree as another storable (.tree) for re-use. The tree resides in $obj->{html}. The raw HTML is stored in $obj->{source}. The type of the report is set in $obj->{type}. |
54
|
|
|
|
|
|
|
Arguments: see new() (q.v.) |
55
|
|
|
|
|
|
|
Returns: the blessed object. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item C |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Converts the NHL HTML header date strings of start and end of the game into $obj->{start_ts} and $obj->{end_ts} timestamps and sets the object's time zone in $obj->{tz} and the month in $obj->{month}. |
60
|
|
|
|
|
|
|
Arguments: whether to force US date parsing or not. |
61
|
|
|
|
|
|
|
Note: uses $self->{date} anf $self->{time} from get_header() (q.v.) |
62
|
|
|
|
|
|
|
Returns: void. Sets object fields |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=item C |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Forces a decision setting on a goaltender in case the reports miss on it explicitly. Usually happens in tied games. |
67
|
|
|
|
|
|
|
Arguments: the team to force the decision |
68
|
|
|
|
|
|
|
Returns: void. Sets a team's goaltender with the decision. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=item C |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
Gets the HTML node path for the HTML report header (teams, score, location, attendance etc.) |
73
|
|
|
|
|
|
|
Arguments: none |
74
|
|
|
|
|
|
|
Returns: void. Sets the path in $obj->{head} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=item C |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Gets the node in the HTML Tree as set by a path. |
79
|
|
|
|
|
|
|
Arguments: |
80
|
|
|
|
|
|
|
* flag 0|1 whether the node or its contents are wanted |
81
|
|
|
|
|
|
|
* the walk path to the node as arrayref |
82
|
|
|
|
|
|
|
* optional: the sub tree to walk (or $obj->{html}) |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=item C |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
A post-process function for the report that should be overloaded. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item C |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
A processing function for the specific report that must be overloaded. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item C |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Read the boxscore: read the header, parse the rest (overloaded), normalize it (may be overloaded), delete the html tree to free the memory and delete the HTML source for the same purpose. |
95
|
|
|
|
|
|
|
Arguments: none |
96
|
|
|
|
|
|
|
Returns: void |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=item C |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Reads the arena information from the game header |
101
|
|
|
|
|
|
|
Arguments: the HTML element with the arena information |
102
|
|
|
|
|
|
|
Returns: void. Sets the arena and the attendance in the object. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item C |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Reads the date from the game header |
107
|
|
|
|
|
|
|
Arguments: the HTML element with the date information |
108
|
|
|
|
|
|
|
Returns: void. Sets the date in the object. Implies calling convert_date_time (q.v.) later. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=item C |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Reads the NHL id from the game header |
113
|
|
|
|
|
|
|
Arguments: the HTML element with the game id information |
114
|
|
|
|
|
|
|
Returns: void. Sets the nhl season game id in the object. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=item C |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Parses the header of the HTML report, dispatching the processing of the discovered information elements. |
119
|
|
|
|
|
|
|
Arguments: none |
120
|
|
|
|
|
|
|
Returns: void. Everything is set in the object. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=item C |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Checks if one of the sources of the boxscore is an HTML report |
125
|
|
|
|
|
|
|
Arguments: none |
126
|
|
|
|
|
|
|
Returns: True|False |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item C |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Reads the game status block from the game header |
131
|
|
|
|
|
|
|
Arguments: the HTML element with the game status and other information |
132
|
|
|
|
|
|
|
Returns: void. Sets the information in the object. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=item C |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Reads the actual status of the game from the header |
137
|
|
|
|
|
|
|
Arguments: the HTML element with the status information |
138
|
|
|
|
|
|
|
Returns: void. Sets the status in the object. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=item C |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
Reads the team information from the game header |
143
|
|
|
|
|
|
|
Arguments: the HTML element with the team information and the index of the team |
144
|
|
|
|
|
|
|
Returns: void. Sets the team information in the object. |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=item C |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
Reads the time from the game header |
149
|
|
|
|
|
|
|
Arguments: the HTML element with the time information |
150
|
|
|
|
|
|
|
Returns: void. Sets the date in the object. Implies calling convert_date_time (q.v.) later. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item C |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Sets the argument for the constructor. Juggles the data, file and type fields. |
155
|
|
|
|
|
|
|
Arguments: the args hashref: |
156
|
|
|
|
|
|
|
* the file to process OR |
157
|
|
|
|
|
|
|
* the scalar with the data to process, BUT NOT BOTH. |
158
|
|
|
|
|
|
|
* the explicit data type setting, |
159
|
|
|
|
|
|
|
optional when 'file' is specified. |
160
|
|
|
|
|
|
|
Returns: void. Updates the args hashref. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item C |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Sets extra data to already parsed events: |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
* The file type as event source |
167
|
|
|
|
|
|
|
* The game_id normalized |
168
|
|
|
|
|
|
|
* Bench player in case of bench penalty |
169
|
|
|
|
|
|
|
* Resolves teams to standard 3-letter codes |
170
|
|
|
|
|
|
|
* Converts time to timestamp (ts) |
171
|
|
|
|
|
|
|
* Sets field t for primary event team: |
172
|
|
|
|
|
|
|
0 for away, 1 for home, -1 - noplay event |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Arguments: none |
175
|
|
|
|
|
|
|
Returns: void. Updates the events in the object. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=cut |
180
|
|
|
|
|
|
|
|
181
|
52
|
|
|
52
|
|
375
|
use Data::Dumper; |
|
52
|
|
|
|
|
118
|
|
|
52
|
|
|
|
|
185906
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
our %REPORT_TYPES = ( |
184
|
|
|
|
|
|
|
BS => 'json', |
185
|
|
|
|
|
|
|
PB => 'json', |
186
|
|
|
|
|
|
|
Player => 'json', |
187
|
|
|
|
|
|
|
PL => 'html', |
188
|
|
|
|
|
|
|
RO => 'html', |
189
|
|
|
|
|
|
|
GS => 'html', |
190
|
|
|
|
|
|
|
BH => 'html', |
191
|
|
|
|
|
|
|
ES => 'html', |
192
|
|
|
|
|
|
|
); |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
our @HEADER_STATUS_METHODS = ( |
195
|
|
|
|
|
|
|
undef, |
196
|
|
|
|
|
|
|
undef, |
197
|
|
|
|
|
|
|
undef, |
198
|
|
|
|
|
|
|
undef, |
199
|
|
|
|
|
|
|
qw( |
200
|
|
|
|
|
|
|
read_date_info |
201
|
|
|
|
|
|
|
read_arena_info |
202
|
|
|
|
|
|
|
read_time_info |
203
|
|
|
|
|
|
|
read_game_info |
204
|
|
|
|
|
|
|
read_status_info |
205
|
|
|
|
|
|
|
), |
206
|
|
|
|
|
|
|
); |
207
|
|
|
|
|
|
|
our @HEADER_STATUS_METHODS_OLD = ( |
208
|
|
|
|
|
|
|
undef, |
209
|
|
|
|
|
|
|
undef, |
210
|
|
|
|
|
|
|
undef, |
211
|
|
|
|
|
|
|
undef, |
212
|
|
|
|
|
|
|
'read_game_info', |
213
|
|
|
|
|
|
|
undef, |
214
|
|
|
|
|
|
|
'read_date_info', |
215
|
|
|
|
|
|
|
undef, |
216
|
|
|
|
|
|
|
'read_arena_info', |
217
|
|
|
|
|
|
|
undef, |
218
|
|
|
|
|
|
|
'read_time_info', |
219
|
|
|
|
|
|
|
undef, |
220
|
|
|
|
|
|
|
'read_status_info', |
221
|
|
|
|
|
|
|
); |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
our $tb; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub set_args ($) { |
226
|
|
|
|
|
|
|
|
227
|
41
|
|
|
41
|
1
|
100
|
my $args = shift; |
228
|
|
|
|
|
|
|
|
229
|
41
|
100
|
100
|
|
|
278
|
if (! $args->{data} && ! $args->{file}) { |
230
|
2
|
|
|
|
|
58
|
print STDERR "Need to specify either file or data, choose one!\n"; |
231
|
2
|
|
|
|
|
21
|
return undef; |
232
|
|
|
|
|
|
|
} |
233
|
39
|
100
|
100
|
|
|
173
|
if ($args->{data} && $args->{file}) { |
234
|
1
|
|
|
|
|
12
|
print STDERR "Cannot specify both data and file, choose one!\n"; |
235
|
1
|
|
|
|
|
8
|
return undef; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
my $type = $args->{type} || ( |
238
|
38
|
|
100
|
|
|
468
|
$args->{file} ? ($args->{file} =~ m|/([A-Z]{2}).[a-z]{4}$| ? $1 : '') : '' |
239
|
|
|
|
|
|
|
); |
240
|
38
|
100
|
|
|
|
145
|
if (! $type) { |
241
|
1
|
|
|
|
|
11
|
print STDERR "Unable to determine the type of the report, please specify explicitly\n"; |
242
|
1
|
|
|
|
|
7
|
return undef; |
243
|
|
|
|
|
|
|
} |
244
|
37
|
|
|
|
|
118
|
$args->{type} = $type; |
245
|
37
|
100
|
|
|
|
269
|
$args->{data} = read_file($args->{file}) if ($args->{file}); |
246
|
37
|
|
|
|
|
149
|
1; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
sub new ($$) { |
250
|
|
|
|
|
|
|
|
251
|
41
|
|
|
41
|
1
|
10066
|
my $class = shift; |
252
|
41
|
|
100
|
|
|
184
|
my $args = shift || {}; |
253
|
|
|
|
|
|
|
|
254
|
41
|
100
|
|
|
|
161
|
set_args($args) || return undef; |
255
|
37
|
|
|
|
|
97
|
my $self = {}; |
256
|
37
|
|
|
|
|
193
|
bless $self, $class; |
257
|
37
|
100
|
|
|
|
325
|
$class .= "::$args->{type}" unless $class =~ /\:\:[A-Z]{2}$/; |
258
|
37
|
|
|
141
|
|
444
|
my $plugin = firstval {$class eq $_} $self->plugins(); |
|
141
|
|
|
|
|
259502
|
|
259
|
37
|
100
|
|
|
|
268
|
if (! $plugin) { |
260
|
1
|
|
|
|
|
28
|
print STDERR "Unknown report type $args->{type}\n"; |
261
|
1
|
|
|
|
|
18
|
return undef; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
$self = $REPORT_TYPES{$args->{type}} eq 'json' |
264
|
|
|
|
|
|
|
? $plugin->new($args->{data}) |
265
|
36
|
100
|
|
|
|
635
|
: $plugin->html_new($args); |
266
|
36
|
|
|
|
|
289
|
$self->{type} = $args->{type}; |
267
|
36
|
|
|
|
|
6574
|
$self; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub html_new ($$) { |
271
|
|
|
|
|
|
|
|
272
|
28
|
|
|
28
|
1
|
88
|
my $class = shift; |
273
|
28
|
|
|
|
|
69
|
my $args = shift; |
274
|
|
|
|
|
|
|
|
275
|
28
|
|
|
|
|
295
|
$tb = HTML::TreeBuilder->new; |
276
|
28
|
|
|
|
|
9405
|
my $self = {}; |
277
|
28
|
50
|
|
|
|
146
|
if ($args->{file}) { |
278
|
28
|
|
|
|
|
79
|
my $tree = $args->{file}; |
279
|
28
|
|
|
|
|
161
|
$tree =~ s/html/tree/; |
280
|
28
|
100
|
66
|
|
|
1244
|
if (-f $tree && (stat($tree))[9] > (stat($args->{file}))[9]-2) { |
281
|
23
|
|
|
|
|
165
|
debug "Using tree file"; |
282
|
23
|
|
|
|
|
141
|
$tb = retrieve $tree; |
283
|
23
|
|
|
|
|
606676
|
$self->{html} = $tb->{_body}; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
} |
286
|
28
|
100
|
|
|
|
182
|
if (! $self->{html}) { |
287
|
5
|
|
|
|
|
44
|
$tb->ignore_unknown(0); |
288
|
5
|
|
|
|
|
100
|
$tb->implicit_tags(1); |
289
|
|
|
|
|
|
|
# unidecode($args->{data); |
290
|
5
|
|
|
|
|
1378
|
$args->{data} =~ tr/Â / /; |
291
|
5
|
50
|
66
|
|
|
220
|
if ($args->{type} eq 'ES' && |
|
|
|
33
|
|
|
|
|
292
|
|
|
|
|
|
|
$args->{data} =~ /width\=100\%/i && |
293
|
|
|
|
|
|
|
$args->{data} !~ /width\=100\%\>/i |
294
|
|
|
|
|
|
|
) { |
295
|
0
|
|
|
|
|
0
|
$args->{data} =~ s/width\=100\%/width\=100\%\>/ig |
296
|
|
|
|
|
|
|
} |
297
|
5
|
|
|
|
|
105
|
$tb->parse($args->{data}); |
298
|
5
|
50
|
|
|
|
15370205
|
if ($args->{file}) { |
299
|
5
|
|
|
|
|
27
|
my $tree = $self->{file} = $args->{file}; |
300
|
5
|
|
|
|
|
31
|
$tree =~ s/html/tree/; |
301
|
5
|
|
|
|
|
38
|
verbose "Storing tree file $tree"; |
302
|
5
|
|
|
|
|
32
|
store $tb, $tree; |
303
|
|
|
|
|
|
|
} |
304
|
5
|
|
|
|
|
120262
|
$self->{html} = $tb->{_body}; |
305
|
|
|
|
|
|
|
} |
306
|
28
|
|
|
|
|
111
|
$self->{source} = $args->{data}; |
307
|
28
|
|
|
|
|
86
|
$self->{type} = $args->{type}; |
308
|
28
|
|
|
|
|
206
|
bless $self, $class; |
309
|
28
|
|
|
|
|
149
|
$self; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub has_html ($$) { |
313
|
|
|
|
|
|
|
|
314
|
6
|
|
|
6
|
1
|
12
|
my $self = shift; |
315
|
|
|
|
|
|
|
|
316
|
6
|
|
33
|
|
|
63
|
return $self->{GS} || $self->{ES} || $self->{RO} || $self->{PL}; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub read_status ($$$) { |
320
|
|
|
|
|
|
|
|
321
|
16
|
|
|
16
|
1
|
37
|
my $self = shift; |
322
|
16
|
|
|
|
|
33
|
my $cell = shift; |
323
|
|
|
|
|
|
|
|
324
|
16
|
|
|
|
|
40
|
my $r = 0; |
325
|
16
|
50
|
|
|
|
55
|
$cell = $self->get_sub_tree(0, [0,0], $cell) if $self->{old}; |
326
|
16
|
|
|
|
|
40
|
my $offset = 0; |
327
|
16
|
|
|
|
|
33
|
my $no_att = 0; |
328
|
16
|
|
|
|
|
87
|
while (my $row = $self->get_sub_tree(0, [$r], $cell)) { |
329
|
144
|
50
|
|
|
|
333
|
my $content = $self->{old} ? $row : $self->get_sub_tree(0, [0,0], $row); |
330
|
144
|
|
|
|
|
194
|
$r++; |
331
|
144
|
100
|
66
|
|
|
382
|
next unless $content and ! ref($content); |
332
|
112
|
0
|
33
|
|
|
232
|
if ($self->{old} && $r == 4 && $content =~ /\,/) { |
|
|
|
33
|
|
|
|
|
333
|
0
|
|
|
|
|
0
|
$offset = 1 + $self->{old}; |
334
|
|
|
|
|
|
|
} |
335
|
112
|
50
|
|
|
|
255
|
my $method = $self->{old} ? $HEADER_STATUS_METHODS_OLD[$r+$offset+$no_att] : $HEADER_STATUS_METHODS[$r+$offset]; |
336
|
112
|
50
|
33
|
|
|
624
|
if ($content && $content =~ /\s*(attendance|attd)\s+(\d+\S+\d+)\s*$/i) { |
337
|
0
|
|
|
|
|
0
|
$self->{attendance} = $2; |
338
|
0
|
|
|
|
|
0
|
$self->{attendance} =~ s/\D//g; |
339
|
0
|
|
|
|
|
0
|
next; |
340
|
|
|
|
|
|
|
} |
341
|
112
|
50
|
33
|
|
|
243
|
if ($r == 11 && ! $self->{attendance}) { |
342
|
0
|
|
|
|
|
0
|
$method = 'read_status_info'; |
343
|
|
|
|
|
|
|
} |
344
|
112
|
50
|
33
|
|
|
308
|
if ($content && $content =~ /^\s*(\d+\:\d+)\s+(\S\S)\s+(\S\S)\s+at\s+(.*)/) { |
345
|
0
|
|
|
|
|
0
|
$self->{time} = "$1 $3"; |
346
|
0
|
|
|
|
|
0
|
$self->{tz} = $3; |
347
|
0
|
|
|
|
|
0
|
$self->{location} = $4; |
348
|
0
|
|
|
|
|
0
|
next; |
349
|
|
|
|
|
|
|
} |
350
|
112
|
100
|
|
|
|
254
|
next unless $method; |
351
|
80
|
|
|
|
|
403
|
$self->$method($content); |
352
|
|
|
|
|
|
|
} |
353
|
16
|
|
|
|
|
114
|
$self->convert_time_date(); |
354
|
16
|
|
50
|
|
|
70
|
$self->{status} ||= 'Preview'; |
355
|
|
|
|
|
|
|
$self->{status} = 'Final' if |
356
|
|
|
|
|
|
|
$self->{status} eq 'End of Game' |
357
|
|
|
|
|
|
|
|| $self->{status} eq 'End of Period 4' |
358
|
16
|
50
|
33
|
|
|
159
|
|| $self->{status} eq 'Period 4 (0:00 Remaining)'; |
|
|
|
33
|
|
|
|
|
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub read_date_info ($$$) { |
362
|
|
|
|
|
|
|
|
363
|
16
|
|
|
16
|
1
|
35
|
my $self = shift; |
364
|
16
|
|
|
|
|
31
|
my $date = shift; |
365
|
|
|
|
|
|
|
|
366
|
16
|
|
|
|
|
107
|
($date) = ($date =~ /\S+,.*?(\S.*)$/); |
367
|
16
|
|
|
|
|
48
|
$date =~ s/Sept\./Sep/g; |
368
|
16
|
|
|
|
|
39
|
$date =~ s/Fev\.\S*/Feb/g; |
369
|
16
|
|
|
|
|
38
|
$date =~ s/Avr\.\S*/Apr/g; |
370
|
16
|
|
|
|
|
32
|
$date =~ s/Mai\S*/May/g; |
371
|
16
|
|
|
|
|
73
|
$self->{date} = $date; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub read_time_info ($$$) { |
375
|
|
|
|
|
|
|
|
376
|
16
|
|
|
16
|
1
|
38
|
my $self = shift; |
377
|
16
|
|
|
|
|
33
|
my $time = shift; |
378
|
|
|
|
|
|
|
|
379
|
16
|
|
|
|
|
59
|
$self->{time} = $time; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub read_arena_info ($$$) { |
383
|
|
|
|
|
|
|
|
384
|
16
|
|
|
16
|
1
|
33
|
my $self = shift; |
385
|
16
|
|
|
|
|
35
|
my $arena_info = shift; |
386
|
|
|
|
|
|
|
|
387
|
16
|
|
|
|
|
35
|
my $stadium; |
388
|
|
|
|
|
|
|
my $attendance; |
389
|
|
|
|
|
|
|
|
390
|
16
|
|
|
|
|
56
|
$arena_info =~ tr/\xA0/ /; |
391
|
16
|
50
|
|
|
|
174
|
if ($arena_info !~ /att/i) { |
|
|
50
|
|
|
|
|
|
392
|
0
|
|
|
|
|
0
|
$stadium = $arena_info; |
393
|
0
|
0
|
|
|
|
0
|
if ($arena_info =~ /(\d+\:\d+ \w\w \w\w) (at|\@) (.*)/) { |
394
|
0
|
|
|
|
|
0
|
$self->{time} = $1; |
395
|
0
|
|
|
|
|
0
|
$stadium = $3; |
396
|
|
|
|
|
|
|
} |
397
|
0
|
|
|
|
|
0
|
$attendance = 0; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
elsif ($arena_info =~ /attendance.*?(\d+)\,(\d+)\s*$/i) { |
400
|
0
|
|
|
|
|
0
|
$stadium = 'Unknown'; |
401
|
0
|
|
|
|
|
0
|
$attendance = $1*1000+$2; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
else { |
404
|
16
|
|
|
|
|
35
|
my $sep; |
405
|
16
|
|
|
|
|
102
|
($attendance, $sep, $stadium) = ($arena_info =~ /(\S+\d).*?(at\b|\@).*?(\w.*)/); |
406
|
16
|
50
|
|
|
|
44
|
unless ($attendance) { |
407
|
0
|
|
|
|
|
0
|
$attendance = 0; |
408
|
0
|
0
|
|
|
|
0
|
if ($arena_info =~ /(at|\@).*?(\w.*)/) { |
409
|
0
|
|
|
|
|
0
|
$stadium = $2, |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
else { |
413
|
16
|
|
|
|
|
70
|
$attendance =~ s/\D//g; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
} |
416
|
16
|
|
|
|
|
49
|
$self->{attendance} = $attendance; |
417
|
16
|
|
|
|
|
61
|
$stadium =~ s/^\s+//; |
418
|
16
|
|
|
|
|
48
|
$stadium =~ s/\s+$//; |
419
|
16
|
|
|
|
|
63
|
$stadium =~ s/\s+/ /g; |
420
|
16
|
|
|
|
|
81
|
$self->{location} = $stadium; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub read_game_info ($$$) { |
424
|
|
|
|
|
|
|
|
425
|
16
|
|
|
16
|
1
|
34
|
my $self = shift; |
426
|
16
|
|
|
|
|
27
|
my $game_info = shift; |
427
|
|
|
|
|
|
|
|
428
|
16
|
|
|
|
|
88
|
$game_info =~ /(Game|NHL)\D*(\d{4})/; |
429
|
16
|
|
|
|
|
77
|
$self->{season_id} = $2; |
430
|
16
|
|
|
|
|
50
|
return; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub read_status_info ($$$) { |
434
|
|
|
|
|
|
|
|
435
|
16
|
|
|
16
|
1
|
39
|
my $self = shift; |
436
|
16
|
|
|
|
|
29
|
my $status_info = shift; |
437
|
|
|
|
|
|
|
|
438
|
16
|
|
|
|
|
53
|
$status_info =~ s/^\s+//; |
439
|
16
|
|
|
|
|
41
|
$status_info =~ s/\s+$//; |
440
|
16
|
|
|
|
|
48
|
$self->{status} = $status_info; |
441
|
16
|
50
|
|
|
|
68
|
if ($status_info =~ / (\d+) \- (\S.*)/) { |
442
|
0
|
|
|
|
|
0
|
$self->{season_id} = $1; |
443
|
0
|
|
|
|
|
0
|
$self->{status} = $2; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
else { |
446
|
16
|
|
|
|
|
64
|
$self->{status} = $status_info; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
sub read_team ($$$$) { |
451
|
|
|
|
|
|
|
|
452
|
32
|
|
|
32
|
1
|
59
|
my $self = shift; |
453
|
32
|
|
|
|
|
49
|
my $cell = shift; |
454
|
32
|
|
|
|
|
46
|
my $idx = shift; |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
my $name = $self->{old} ? |
457
|
32
|
50
|
|
|
|
115
|
$self->get_sub_tree(0, [0,0,6], $cell) : |
458
|
|
|
|
|
|
|
$self->get_sub_tree(0, [2,0,0], $cell); |
459
|
32
|
0
|
33
|
|
|
87
|
if (ref $name && $self->{old}) { |
460
|
0
|
|
|
|
|
0
|
$name = $self->get_sub_tree(0, [0,0,5], $cell); |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
my $score = $self->{old} ? |
463
|
|
|
|
|
|
|
$self->get_sub_tree(0, [ |
464
|
0
|
|
|
|
|
0
|
2 - (scalar(@{$self->{head}})-1)*(1-$idx) |
465
|
0
|
|
|
|
|
0
|
+ $idx*($self->{gs}-5)-(scalar(@{$self->{head}})-1)*2*$idx, |
466
|
|
|
|
|
|
|
,0,0 |
467
|
32
|
50
|
|
|
|
129
|
], $cell->{_parent}) : $self->get_sub_tree(0, [1,0,0,0,1,0], $cell); |
468
|
32
|
50
|
|
|
|
202
|
$score = $self->get_sub_tree(0, [2+5*$idx+($self->{gs}>=12)*(1-$idx),0,0], $cell->{_parent}) if $score !~ /^\d{1,2}\s*$/; |
469
|
32
|
50
|
33
|
|
|
190
|
$score = $self->get_sub_tree(0, [9,0,0], $cell->{_parent}) if !defined $score || $score !~ /^\d{1,2}\s*$/; |
470
|
32
|
50
|
33
|
|
|
167
|
if (!defined $score || $score !~ /^\s*\d{1,2}\s*$/) { |
471
|
0
|
|
|
|
|
0
|
die "Unreadable header"; |
472
|
|
|
|
|
|
|
} |
473
|
32
|
50
|
|
|
|
74
|
if ($name) { |
474
|
32
|
|
|
|
|
84
|
$name =~ s/^\s+//g; |
475
|
32
|
|
|
|
|
86
|
$name =~ s/\s+$//g; |
476
|
32
|
|
|
|
|
132
|
$name =~ s/\s+/ /g; |
477
|
32
|
50
|
|
|
|
87
|
$name = 'MONTREAL CANADIENS' if $name eq 'CANADIENS MONTREAL'; |
478
|
32
|
|
|
|
|
110
|
$self->{teams}[$idx]{name} = $name; |
479
|
|
|
|
|
|
|
} |
480
|
32
|
|
|
|
|
67
|
$score =~ s/\D//g; |
481
|
32
|
|
|
|
|
98
|
$self->{teams}[$idx]{score} = $score; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
sub get_header ($) { |
485
|
|
|
|
|
|
|
|
486
|
16
|
|
|
16
|
1
|
33
|
my $self = shift; |
487
|
|
|
|
|
|
|
|
488
|
16
|
|
|
|
|
29
|
my $i = 0; |
489
|
16
|
|
|
|
|
53
|
$self->{head} = []; |
490
|
16
|
|
|
|
|
48
|
$self->{teams} = []; |
491
|
16
|
|
|
|
|
141
|
while(my $base_element = $self->get_sub_tree(0, [$i])) { |
492
|
48
|
50
|
|
|
|
88
|
last unless ref $base_element; |
493
|
48
|
100
|
|
|
|
157
|
if ($base_element->tag eq 'table') { |
494
|
16
|
|
|
|
|
108
|
push(@{$self->{head}}, $i); |
|
16
|
|
|
|
|
48
|
|
495
|
16
|
50
|
|
|
|
59
|
push(@{$self->{head}}, 0) if $base_element->{_content}[0]->tag eq 'tbody'; |
|
0
|
|
|
|
|
0
|
|
496
|
16
|
|
|
|
|
118
|
last; |
497
|
|
|
|
|
|
|
} |
498
|
32
|
|
|
|
|
304
|
$i++; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
sub read_header ($) { |
503
|
|
|
|
|
|
|
|
504
|
16
|
|
|
16
|
1
|
37
|
my $self = shift; |
505
|
|
|
|
|
|
|
|
506
|
16
|
|
|
|
|
110
|
$self->get_header(); |
507
|
|
|
|
|
|
|
|
508
|
16
|
|
|
|
|
45
|
my $main_table = $self->get_sub_tree(0, [@{$self->{head}}]); |
|
16
|
|
|
|
|
57
|
|
509
|
16
|
|
|
|
|
33
|
my $gameinfo_table; |
510
|
16
|
|
|
|
|
31
|
my $offset = 0; |
511
|
16
|
50
|
|
|
|
74
|
if ($main_table->attr('class')) { |
512
|
16
|
|
|
|
|
266
|
my $content_table = $self->get_sub_tree(0, [0,0,0],$main_table); |
513
|
16
|
|
|
|
|
68
|
$gameinfo_table = $self->get_sub_tree(0, [0,0,0], $content_table); |
514
|
16
|
|
|
|
|
50
|
$self->{content_table} = $content_table; |
515
|
16
|
|
|
|
|
39
|
$self->{old} = 0; |
516
|
16
|
|
|
|
|
35
|
$offset = 0; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
else { |
519
|
0
|
|
|
|
|
0
|
$gameinfo_table = $main_table; |
520
|
0
|
|
|
|
|
0
|
$self->{old} = 1; |
521
|
0
|
|
|
|
|
0
|
$offset = 2; |
522
|
|
|
|
|
|
|
} |
523
|
16
|
|
|
|
|
54
|
my $gameinfo_row = $self->get_sub_tree(0, [0], $gameinfo_table); |
524
|
16
|
|
|
|
|
30
|
my $gameinfo_size = @{$gameinfo_row->{_content}}; |
|
16
|
|
|
|
|
43
|
|
525
|
16
|
|
|
|
|
50
|
$self->{gs} = $gameinfo_size; |
526
|
16
|
|
|
|
|
56
|
for my $i (0..2) { |
527
|
48
|
|
|
|
|
75
|
my $cell; |
528
|
48
|
50
|
33
|
|
|
128
|
if ($self->{old} && @{$self->{head}} == 2) { |
|
0
|
|
|
|
|
0
|
|
529
|
0
|
|
|
|
|
0
|
$cell = $self->get_sub_tree(0, [ $i*$self->{old}*5 + $self->{old}*(2-$i) - 1 ], $gameinfo_row); |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
else { |
532
|
48
|
|
|
|
|
159
|
$offset = $i + $i*$self->{old}*$gameinfo_size/2 + $self->{old}*(1-2*$i); |
533
|
48
|
50
|
|
|
|
102
|
$offset += 1-$i if $gameinfo_size == 12; |
534
|
48
|
50
|
|
|
|
97
|
$offset += 1-$i if $gameinfo_size == 14; |
535
|
48
|
50
|
|
|
|
156
|
$cell = $self->get_sub_tree(0, [ $self->{old} ? $offset : ($offset, 0), ], $gameinfo_row); |
536
|
|
|
|
|
|
|
} |
537
|
48
|
100
|
|
|
|
320
|
($i % 2) ? $self->read_status($cell) : $self->read_team($cell, $i / 2); |
538
|
|
|
|
|
|
|
} |
539
|
16
|
50
|
33
|
|
|
74
|
if ($self->{status} =~ /end.*period (3|4)/i |
540
|
|
|
|
|
|
|
&& $self->{teams}[0]{score} != $self->{teams}[1]{score}) { |
541
|
0
|
|
|
|
|
0
|
$self->{status} = 'final'; |
542
|
|
|
|
|
|
|
} |
543
|
16
|
50
|
|
|
|
56
|
$self->{season}-- if ($self->{month} < 9); |
544
|
16
|
50
|
33
|
|
|
209
|
if ( |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
545
|
|
|
|
|
|
|
($self->{season} != 2012 && $self->{month} > 3 && |
546
|
|
|
|
|
|
|
$self->{month} < 8 && $self->{season_id} <= $LAST_PLAYOFF_GAME_INDEX) || |
547
|
|
|
|
|
|
|
($self->{season} == 2012 && $self->{start_ts} >= $LATE_START_IN_2012)) { |
548
|
0
|
|
|
|
|
0
|
$self->{stage} = $PLAYOFF; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
else { |
551
|
16
|
|
|
|
|
50
|
$self->{stage} = $REGULAR; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
$self->{teams}[0]{name} = 'MONTREAL CANADIENS' |
554
|
16
|
50
|
|
|
|
53
|
if $self->{teams}[0]{name} eq 'CANADIENS MONTREAL'; |
555
|
|
|
|
|
|
|
$self->{teams}[1]{name} = 'MONTREAL CANADIENS' |
556
|
16
|
50
|
|
|
|
44
|
if $self->{teams}[1]{name} eq 'CANADIENS MONTREAL'; |
557
|
16
|
|
|
|
|
65
|
$self->{_id} = $self->{season} * 100000 + $self->{stage} * 10000 + $self->{season_id}; |
558
|
16
|
|
50
|
|
|
108
|
$self->{periods} ||= [{},{},{}]; |
559
|
16
|
|
|
|
|
37
|
delete $self->{gs}; |
560
|
16
|
|
|
|
|
155
|
$self->fill_broken($BROKEN_HEADERS{$self->{_id}}); |
561
|
16
|
|
50
|
|
|
58
|
$self->{attendance} ||= 0; |
562
|
16
|
|
|
|
|
80
|
ref ($self) =~ /\:\:(\w\w)$/; |
563
|
16
|
|
|
|
|
55
|
$self->{type} = $1; |
564
|
16
|
|
|
|
|
51
|
$self->{status} = uc $self->{status}; |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
sub convert_time_date ($;$) { |
568
|
|
|
|
|
|
|
|
569
|
18
|
|
|
18
|
1
|
32
|
my $self = shift; |
570
|
18
|
|
100
|
|
|
99
|
my $force_us = shift || 0; |
571
|
|
|
|
|
|
|
|
572
|
18
|
|
|
|
|
45
|
my $date = $self->{date}; |
573
|
18
|
|
|
|
|
42
|
my $time = $self->{time}; |
574
|
18
|
50
|
66
|
|
|
228
|
my ($year, $month, $day) = $date =~ /^\d/ && ! $force_us |
575
|
|
|
|
|
|
|
? Decode_Date_EU($date) |
576
|
|
|
|
|
|
|
: Decode_Date_US($date); |
577
|
|
|
|
|
|
|
|
578
|
18
|
|
66
|
|
|
118
|
$self->{season} ||= $year; |
579
|
18
|
|
|
|
|
64
|
$self->{month} = $month; |
580
|
18
|
|
|
|
|
41
|
$year -= 1900; |
581
|
18
|
|
|
|
|
40
|
$month--; |
582
|
18
|
|
|
|
|
136
|
my ($start_h, $start_m, $start_tz, $end_h, $end_m, $end_tz) = |
583
|
|
|
|
|
|
|
($time =~ /(\d+):(\d+)\W*(\w{1,2}T)\s*\;\D*(\d+):(\d+)\W*(\w{1,2}T)/); |
584
|
18
|
100
|
|
|
|
60
|
unless ($end_h) { |
585
|
2
|
|
|
|
|
6
|
($start_h, $start_m, $start_tz) = ($time =~ /(\d+):(\d+)\W*(\w{1,2}T)\W*/); |
586
|
2
|
50
|
|
|
|
8
|
unless ($start_h) { |
587
|
2
|
|
|
|
|
4
|
$start_h = 12; |
588
|
2
|
|
|
|
|
4
|
$start_m = 0; |
589
|
2
|
|
|
|
|
5
|
$start_tz = 'EDT'; |
590
|
|
|
|
|
|
|
} |
591
|
2
|
|
|
|
|
4
|
$end_h = $start_h + 3; |
592
|
2
|
|
|
|
|
4
|
$end_m = $start_m; |
593
|
2
|
|
|
|
|
5
|
$end_tz = $start_tz; |
594
|
|
|
|
|
|
|
} |
595
|
18
|
100
|
|
|
|
63
|
$start_h += 12 if $start_h < 12; |
596
|
18
|
100
|
|
|
|
52
|
$end_h += 12 if $end_h < $start_h; |
597
|
18
|
|
|
|
|
123
|
$self->{start_ts} = timelocal(0, $start_m, $start_h, $day, $month, $year); |
598
|
18
|
50
|
|
|
|
1792
|
if ($end_h > 23) { |
599
|
0
|
|
|
|
|
0
|
$self->{end_ts} = $self->{start_ts} + 10800; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
else { |
602
|
18
|
|
|
|
|
170
|
$self->{end_ts} = timegm(0, $end_m, $end_h, $day, $month, $year); |
603
|
|
|
|
|
|
|
} |
604
|
18
|
|
33
|
|
|
455
|
$self->{tz} ||= $start_tz; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
0
|
|
|
0
|
1
|
0
|
sub parse ($) { die "Overload me" } |
608
|
|
|
|
4
|
1
|
|
sub normalize ($) { } |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub force_decision ($$) { |
611
|
|
|
|
|
|
|
|
612
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
613
|
0
|
|
|
|
|
0
|
my $team = shift; |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
my @goalies = sort { |
616
|
|
|
|
|
|
|
get_seconds($b->{timeOnIce}) <=> get_seconds($a->{timeOnIce}) |
617
|
0
|
|
|
|
|
0
|
} grep { $_->{position} eq 'G' } @{$team->{roster}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
618
|
0
|
|
|
|
|
0
|
my $goalie = $goalies[0]; |
619
|
0
|
0
|
|
|
|
0
|
if ($self->{_score}[0] == $self->{_score}[1]) { |
|
|
0
|
|
|
|
|
|
620
|
0
|
|
|
|
|
0
|
$goalie->{decision} = 'T'; |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
elsif ($self->{_score}[$self->{_t}] > $self->{_score}[1 - $self->{_t}]) { |
623
|
0
|
|
|
|
|
0
|
$goalie->{decision} = 'W'; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
else { |
626
|
0
|
0
|
0
|
|
|
0
|
$goalie->{decision} = $self->{ot} || $self->{so} ? 'O' : 'L'; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
sub get_sub_tree ($$$;$) { |
632
|
|
|
|
|
|
|
|
633
|
52982
|
|
|
52982
|
1
|
62825
|
my $self = shift; |
634
|
52982
|
|
|
|
|
54575
|
my $want_content = shift; |
635
|
52982
|
|
|
|
|
53948
|
my $walk = shift; |
636
|
52982
|
|
33
|
|
|
78711
|
my $tree = shift || $self->{html} || $self; |
637
|
|
|
|
|
|
|
|
638
|
52982
|
50
|
|
|
|
76762
|
print "Walking ",join(".", @{$walk}), "\n" if $ENV{SHOW_WALK}; |
|
0
|
|
|
|
|
0
|
|
639
|
52982
|
|
|
|
|
59555
|
my $tpointer = \$tree; |
640
|
52982
|
|
|
|
|
56797
|
for my $node (@{$walk}) { |
|
52982
|
|
|
|
|
67423
|
|
641
|
202676
|
100
|
100
|
|
|
437856
|
return undef unless $$tpointer && ref $$tpointer; |
642
|
202158
|
|
|
|
|
203245
|
my $tc = ${$tpointer}->{_content}[$node]; |
|
202158
|
|
|
|
|
301548
|
|
643
|
202158
|
|
|
|
|
261733
|
$tpointer = \$tc; |
644
|
|
|
|
|
|
|
} |
645
|
52464
|
|
|
|
|
63651
|
my $tcopy = $$tpointer; |
646
|
52464
|
50
|
|
|
|
117918
|
return $want_content ? $tcopy->{_content} : $tcopy; |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
sub process ($) { |
650
|
|
|
|
|
|
|
|
651
|
18
|
|
|
18
|
1
|
3753
|
my $self = shift; |
652
|
|
|
|
|
|
|
|
653
|
18
|
100
|
|
|
|
163
|
$self->read_header() unless $self->{type} eq 'BH'; |
654
|
18
|
|
|
|
|
96
|
$self->parse(); |
655
|
18
|
|
|
|
|
126
|
$self->normalize(); |
656
|
18
|
|
|
|
|
159
|
$self->{html}->delete(); |
657
|
18
|
|
|
|
|
1649242
|
delete $self->{source}; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
sub set_event_extra_data ($) { |
661
|
|
|
|
|
|
|
|
662
|
18
|
|
|
18
|
1
|
58414
|
my $self = shift; |
663
|
18
|
|
|
|
|
39
|
for my $event (@{$self->{events}}) { |
|
18
|
|
|
|
|
57
|
|
664
|
4186
|
|
|
|
|
11781
|
$event->{sources} = {$self->{type} => 1}; |
665
|
4186
|
50
|
|
|
|
7528
|
$event->{game_id} = delete $event->{game} if $event->{game}; |
666
|
4186
|
100
|
33
|
|
|
7248
|
$event->{player1} ||= $BENCH_PLAYER_ID if ($event->{penalty}); |
667
|
4186
|
|
|
|
|
4699
|
my $t = -1; |
668
|
4186
|
100
|
|
|
|
7108
|
if ($event->{team1}) { |
669
|
|
|
|
|
|
|
$event->{team1} = resolve_team($event->{team1}) if |
670
|
3476
|
50
|
|
|
|
8929
|
$event->{team1} ne 'OTH'; |
671
|
|
|
|
|
|
|
$t = $event->{team1} eq $self->{teams}[0]{name} |
672
|
|
|
|
|
|
|
? 0 |
673
|
|
|
|
|
|
|
: $event->{team1} eq $self->{teams}[1]{name} |
674
|
3476
|
100
|
|
|
|
11919
|
? 1 |
|
|
100
|
|
|
|
|
|
675
|
|
|
|
|
|
|
: -1; |
676
|
|
|
|
|
|
|
} |
677
|
4186
|
100
|
66
|
|
|
10111
|
$event->{team2} = resolve_team($event->{team2}) if $event->{team2} && $event->{team2} ne 'OTH'; |
678
|
4186
|
|
|
|
|
7573
|
$event->{t} = $t; |
679
|
|
|
|
|
|
|
$event->{ts} = |
680
|
|
|
|
|
|
|
$event->{special} ? 0 : |
681
|
|
|
|
|
|
|
$event->{stage} == $PLAYOFF || $event->{stage} == $REGULAR && $event->{period} < 5 ? |
682
|
4186
|
50
|
33
|
|
|
23317
|
($event->{period}-1) * 1200 + get_seconds($event->{time}) : 3900; |
|
|
50
|
|
|
|
|
|
683
|
|
|
|
|
|
|
} |
684
|
18
|
50
|
|
|
|
62
|
$self->{no_events} unless @{$self->{events}}; |
|
18
|
|
|
|
|
123
|
|
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
END { |
688
|
52
|
100
|
|
52
|
|
175845
|
$tb->delete if defined $tb; |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
1; |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
=head1 AUTHOR |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
More Hockey Stats, C<< >> |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=head1 BUGS |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
700
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
701
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head1 SUPPORT |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
perldoc Sport::Analytics::NHL::Report |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
You can also look for information at: |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
=over 4 |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
L |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
L |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=item * CPAN Ratings |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
L |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=item * Search CPAN |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
L |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=back |
732
|
|
|
|
|
|
|
|