line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Sport::Analytics::NHL::Report; |
2
|
|
|
|
|
|
|
|
3
|
41
|
|
|
41
|
|
157936
|
use v5.10.1; |
|
41
|
|
|
|
|
142
|
|
4
|
41
|
|
|
41
|
|
173
|
use strict; |
|
41
|
|
|
|
|
64
|
|
|
41
|
|
|
|
|
878
|
|
5
|
41
|
|
|
41
|
|
182
|
use warnings FATAL => 'all'; |
|
41
|
|
|
|
|
97
|
|
|
41
|
|
|
|
|
1249
|
|
6
|
41
|
|
|
41
|
|
192
|
use experimental qw(smartmatch); |
|
41
|
|
|
|
|
114
|
|
|
41
|
|
|
|
|
221
|
|
7
|
|
|
|
|
|
|
|
8
|
41
|
|
|
41
|
|
7828
|
use Storable; |
|
41
|
|
|
|
|
30492
|
|
|
41
|
|
|
|
|
1811
|
|
9
|
|
|
|
|
|
|
|
10
|
41
|
|
|
41
|
|
14391
|
use Date::Calc qw(Decode_Date_US Decode_Date_EU); |
|
41
|
|
|
|
|
198660
|
|
|
41
|
|
|
|
|
2604
|
|
11
|
41
|
|
|
41
|
|
22894
|
use HTML::TreeBuilder; |
|
41
|
|
|
|
|
990891
|
|
|
41
|
|
|
|
|
370
|
|
12
|
41
|
|
|
41
|
|
7875
|
use List::MoreUtils qw(firstval); |
|
41
|
|
|
|
|
128748
|
|
|
41
|
|
|
|
|
326
|
|
13
|
41
|
|
|
41
|
|
42783
|
use Module::Pluggable require => 1, search_path => ['Sport::Analytics::NHL::Report']; |
|
41
|
|
|
|
|
325679
|
|
|
41
|
|
|
|
|
260
|
|
14
|
41
|
|
|
41
|
|
8182
|
use Time::Local; |
|
41
|
|
|
|
|
17587
|
|
|
41
|
|
|
|
|
1860
|
|
15
|
|
|
|
|
|
|
|
16
|
41
|
|
|
41
|
|
5387
|
use Sport::Analytics::NHL::Util; |
|
41
|
|
|
|
|
89
|
|
|
41
|
|
|
|
|
2371
|
|
17
|
41
|
|
|
41
|
|
6313
|
use Sport::Analytics::NHL::Tools; |
|
41
|
|
|
|
|
100
|
|
|
41
|
|
|
|
|
6426
|
|
18
|
41
|
|
|
41
|
|
248
|
use Sport::Analytics::NHL::Config; |
|
41
|
|
|
|
|
78
|
|
|
41
|
|
|
|
|
6038
|
|
19
|
41
|
|
|
41
|
|
9019
|
use Sport::Analytics::NHL::Errors; |
|
41
|
|
|
|
|
491
|
|
|
41
|
|
|
|
|
7270
|
|
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
|
41
|
|
|
41
|
|
261
|
use Data::Dumper; |
|
41
|
|
|
|
|
71
|
|
|
41
|
|
|
|
|
131059
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
our %REPORT_TYPES = ( |
184
|
|
|
|
|
|
|
BS => 'json', |
185
|
|
|
|
|
|
|
PB => 'json', |
186
|
|
|
|
|
|
|
PL => 'html', |
187
|
|
|
|
|
|
|
RO => 'html', |
188
|
|
|
|
|
|
|
GS => 'html', |
189
|
|
|
|
|
|
|
BH => 'html', |
190
|
|
|
|
|
|
|
ES => 'html', |
191
|
|
|
|
|
|
|
); |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
our @HEADER_STATUS_METHODS = ( |
194
|
|
|
|
|
|
|
undef, |
195
|
|
|
|
|
|
|
undef, |
196
|
|
|
|
|
|
|
undef, |
197
|
|
|
|
|
|
|
undef, |
198
|
|
|
|
|
|
|
qw( |
199
|
|
|
|
|
|
|
read_date_info |
200
|
|
|
|
|
|
|
read_arena_info |
201
|
|
|
|
|
|
|
read_time_info |
202
|
|
|
|
|
|
|
read_game_info |
203
|
|
|
|
|
|
|
read_status_info |
204
|
|
|
|
|
|
|
), |
205
|
|
|
|
|
|
|
); |
206
|
|
|
|
|
|
|
our @HEADER_STATUS_METHODS_OLD = ( |
207
|
|
|
|
|
|
|
undef, |
208
|
|
|
|
|
|
|
undef, |
209
|
|
|
|
|
|
|
undef, |
210
|
|
|
|
|
|
|
undef, |
211
|
|
|
|
|
|
|
'read_game_info', |
212
|
|
|
|
|
|
|
undef, |
213
|
|
|
|
|
|
|
'read_date_info', |
214
|
|
|
|
|
|
|
undef, |
215
|
|
|
|
|
|
|
'read_arena_info', |
216
|
|
|
|
|
|
|
undef, |
217
|
|
|
|
|
|
|
'read_time_info', |
218
|
|
|
|
|
|
|
undef, |
219
|
|
|
|
|
|
|
'read_status_info', |
220
|
|
|
|
|
|
|
); |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
our $tb; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub set_args ($) { |
225
|
|
|
|
|
|
|
|
226
|
30
|
|
|
30
|
1
|
51
|
my $args = shift; |
227
|
|
|
|
|
|
|
|
228
|
30
|
100
|
100
|
|
|
205
|
if (! $args->{data} && ! $args->{file}) { |
229
|
2
|
|
|
|
|
56
|
print STDERR "Need to specify either file or data, choose one!\n"; |
230
|
2
|
|
|
|
|
14
|
return undef; |
231
|
|
|
|
|
|
|
} |
232
|
28
|
100
|
100
|
|
|
130
|
if ($args->{data} && $args->{file}) { |
233
|
1
|
|
|
|
|
9
|
print STDERR "Cannot specify both data and file, choose one!\n"; |
234
|
1
|
|
|
|
|
5
|
return undef; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
my $type = $args->{type} || ( |
237
|
27
|
|
100
|
|
|
288
|
$args->{file} ? ($args->{file} =~ m|/([A-Z]{2}).[a-z]{4}$| ? $1 : '') : '' |
238
|
|
|
|
|
|
|
); |
239
|
27
|
100
|
|
|
|
92
|
if (! $type) { |
240
|
1
|
|
|
|
|
7
|
print STDERR "Unable to determine the type of the report, please specify explicitly\n"; |
241
|
1
|
|
|
|
|
6
|
return undef; |
242
|
|
|
|
|
|
|
} |
243
|
26
|
|
|
|
|
71
|
$args->{type} = $type; |
244
|
26
|
100
|
|
|
|
153
|
$args->{data} = read_file($args->{file}) if ($args->{file}); |
245
|
26
|
|
|
|
|
99
|
1; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub new ($$) { |
249
|
|
|
|
|
|
|
|
250
|
30
|
|
|
30
|
1
|
7431
|
my $class = shift; |
251
|
30
|
|
100
|
|
|
112
|
my $args = shift || {}; |
252
|
|
|
|
|
|
|
|
253
|
30
|
100
|
|
|
|
104
|
set_args($args) || return undef; |
254
|
26
|
|
|
|
|
62
|
my $self = {}; |
255
|
26
|
|
|
|
|
156
|
bless $self, $class; |
256
|
26
|
100
|
|
|
|
211
|
$class .= "::$args->{type}" unless $class =~ /\:\:[A-Z]{2}$/; |
257
|
26
|
|
|
92
|
|
281
|
my $plugin = firstval {$class eq $_} $self->plugins(); |
|
92
|
|
|
|
|
130017
|
|
258
|
26
|
100
|
|
|
|
158
|
if (! $plugin) { |
259
|
1
|
|
|
|
|
33
|
print STDERR "Unknown report type $args->{type}\n"; |
260
|
1
|
|
|
|
|
12
|
return undef; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
$self = $REPORT_TYPES{$args->{type}} eq 'json' |
263
|
|
|
|
|
|
|
? $plugin->new($args->{data}) |
264
|
25
|
100
|
|
|
|
326
|
: $plugin->html_new($args); |
265
|
25
|
|
|
|
|
178
|
$self->{type} = $args->{type}; |
266
|
25
|
|
|
|
|
27394
|
$self; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub html_new ($$) { |
270
|
|
|
|
|
|
|
|
271
|
20
|
|
|
20
|
1
|
42
|
my $class = shift; |
272
|
20
|
|
|
|
|
36
|
my $args = shift; |
273
|
|
|
|
|
|
|
|
274
|
20
|
|
|
|
|
221
|
$tb = HTML::TreeBuilder->new; |
275
|
20
|
|
|
|
|
6630
|
my $self = {}; |
276
|
20
|
50
|
|
|
|
100
|
if ($args->{file}) { |
277
|
20
|
|
|
|
|
52
|
my $tree = $args->{file}; |
278
|
20
|
|
|
|
|
92
|
$tree =~ s/html/tree/; |
279
|
20
|
100
|
66
|
|
|
832
|
if (-f $tree && (stat($tree))[9] > (stat($args->{file}))[9]-2) { |
280
|
15
|
|
|
|
|
90
|
debug "Using tree file"; |
281
|
15
|
|
|
|
|
83
|
$tb = retrieve $tree; |
282
|
15
|
|
|
|
|
375628
|
$self->{html} = $tb->{_body}; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
} |
285
|
20
|
100
|
|
|
|
109
|
if (! $self->{html}) { |
286
|
5
|
|
|
|
|
29
|
$tb->ignore_unknown(0); |
287
|
5
|
|
|
|
|
83
|
$tb->implicit_tags(1); |
288
|
|
|
|
|
|
|
# unidecode($args->{data); |
289
|
5
|
|
|
|
|
1026
|
$args->{data} =~ tr/Â / /; |
290
|
5
|
50
|
66
|
|
|
218
|
if ($args->{type} eq 'ES' && |
|
|
|
33
|
|
|
|
|
291
|
|
|
|
|
|
|
$args->{data} =~ /width\=100\%/i && |
292
|
|
|
|
|
|
|
$args->{data} !~ /width\=100\%\>/i |
293
|
|
|
|
|
|
|
) { |
294
|
0
|
|
|
|
|
0
|
$args->{data} =~ s/width\=100\%/width\=100\%\>/ig |
295
|
|
|
|
|
|
|
} |
296
|
5
|
|
|
|
|
85
|
$tb->parse($args->{data}); |
297
|
5
|
50
|
|
|
|
11805775
|
if ($args->{file}) { |
298
|
5
|
|
|
|
|
31
|
my $tree = $self->{file} = $args->{file}; |
299
|
5
|
|
|
|
|
29
|
$tree =~ s/html/tree/; |
300
|
5
|
|
|
|
|
45
|
verbose "Storing tree file $tree"; |
301
|
5
|
|
|
|
|
29
|
store $tb, $tree; |
302
|
|
|
|
|
|
|
} |
303
|
5
|
|
|
|
|
114434
|
$self->{html} = $tb->{_body}; |
304
|
|
|
|
|
|
|
} |
305
|
20
|
|
|
|
|
68
|
$self->{source} = $args->{data}; |
306
|
20
|
|
|
|
|
60
|
$self->{type} = $args->{type}; |
307
|
20
|
|
|
|
|
129
|
bless $self, $class; |
308
|
20
|
|
|
|
|
115
|
$self; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub has_html ($$) { |
312
|
|
|
|
|
|
|
|
313
|
6
|
|
|
6
|
1
|
11
|
my $self = shift; |
314
|
|
|
|
|
|
|
|
315
|
6
|
|
33
|
|
|
65
|
return $self->{GS} || $self->{ES} || $self->{RO} || $self->{PL}; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub read_status ($$$) { |
319
|
|
|
|
|
|
|
|
320
|
8
|
|
|
8
|
1
|
18
|
my $self = shift; |
321
|
8
|
|
|
|
|
17
|
my $cell = shift; |
322
|
|
|
|
|
|
|
|
323
|
8
|
|
|
|
|
16
|
my $r = 0; |
324
|
8
|
50
|
|
|
|
25
|
$cell = $self->get_sub_tree(0, [0,0], $cell) if $self->{old}; |
325
|
8
|
|
|
|
|
14
|
my $offset = 0; |
326
|
8
|
|
|
|
|
13
|
my $no_att = 0; |
327
|
8
|
|
|
|
|
27
|
while (my $row = $self->get_sub_tree(0, [$r], $cell)) { |
328
|
72
|
50
|
|
|
|
169
|
my $content = $self->{old} ? $row : $self->get_sub_tree(0, [0,0], $row); |
329
|
72
|
|
|
|
|
98
|
$r++; |
330
|
72
|
100
|
66
|
|
|
175
|
next unless $content and ! ref($content); |
331
|
56
|
0
|
33
|
|
|
117
|
if ($self->{old} && $r == 4 && $content =~ /\,/) { |
|
|
|
33
|
|
|
|
|
332
|
0
|
|
|
|
|
0
|
$offset = 1 + $self->{old}; |
333
|
|
|
|
|
|
|
} |
334
|
56
|
50
|
|
|
|
123
|
my $method = $self->{old} ? $HEADER_STATUS_METHODS_OLD[$r+$offset+$no_att] : $HEADER_STATUS_METHODS[$r+$offset]; |
335
|
56
|
50
|
33
|
|
|
297
|
if ($content && $content =~ /\s*(attendance|attd)\s+(\d+\S+\d+)\s*$/i) { |
336
|
0
|
|
|
|
|
0
|
$self->{attendance} = $2; |
337
|
0
|
|
|
|
|
0
|
$self->{attendance} =~ s/\D//g; |
338
|
0
|
|
|
|
|
0
|
next; |
339
|
|
|
|
|
|
|
} |
340
|
56
|
50
|
33
|
|
|
111
|
if ($r == 11 && ! $self->{attendance}) { |
341
|
0
|
|
|
|
|
0
|
$method = 'read_status_info'; |
342
|
|
|
|
|
|
|
} |
343
|
56
|
50
|
33
|
|
|
147
|
if ($content && $content =~ /^\s*(\d+\:\d+)\s+(\S\S)\s+(\S\S)\s+at\s+(.*)/) { |
344
|
0
|
|
|
|
|
0
|
$self->{time} = "$1 $3"; |
345
|
0
|
|
|
|
|
0
|
$self->{tz} = $3; |
346
|
0
|
|
|
|
|
0
|
$self->{location} = $4; |
347
|
0
|
|
|
|
|
0
|
next; |
348
|
|
|
|
|
|
|
} |
349
|
56
|
100
|
|
|
|
122
|
next unless $method; |
350
|
40
|
|
|
|
|
226
|
$self->$method($content); |
351
|
|
|
|
|
|
|
} |
352
|
8
|
|
|
|
|
62
|
$self->convert_time_date(); |
353
|
8
|
|
50
|
|
|
27
|
$self->{status} ||= 'Preview'; |
354
|
|
|
|
|
|
|
$self->{status} = 'Final' if |
355
|
|
|
|
|
|
|
$self->{status} eq 'End of Game' |
356
|
|
|
|
|
|
|
|| $self->{status} eq 'End of Period 4' |
357
|
8
|
50
|
33
|
|
|
70
|
|| $self->{status} eq 'Period 4 (0:00 Remaining)'; |
|
|
|
33
|
|
|
|
|
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub read_date_info ($$$) { |
361
|
|
|
|
|
|
|
|
362
|
8
|
|
|
8
|
1
|
24
|
my $self = shift; |
363
|
8
|
|
|
|
|
17
|
my $date = shift; |
364
|
|
|
|
|
|
|
|
365
|
8
|
|
|
|
|
63
|
($date) = ($date =~ /\S+,.*?(\S.*)$/); |
366
|
8
|
|
|
|
|
24
|
$date =~ s/Sept\./Sep/g; |
367
|
8
|
|
|
|
|
17
|
$date =~ s/Fev\.\S*/Feb/g; |
368
|
8
|
|
|
|
|
20
|
$date =~ s/Avr\.\S*/Apr/g; |
369
|
8
|
|
|
|
|
18
|
$date =~ s/Mai\S*/May/g; |
370
|
8
|
|
|
|
|
32
|
$self->{date} = $date; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub read_time_info ($$$) { |
374
|
|
|
|
|
|
|
|
375
|
8
|
|
|
8
|
1
|
18
|
my $self = shift; |
376
|
8
|
|
|
|
|
14
|
my $time = shift; |
377
|
|
|
|
|
|
|
|
378
|
8
|
|
|
|
|
38
|
$self->{time} = $time; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub read_arena_info ($$$) { |
382
|
|
|
|
|
|
|
|
383
|
8
|
|
|
8
|
1
|
18
|
my $self = shift; |
384
|
8
|
|
|
|
|
16
|
my $arena_info = shift; |
385
|
|
|
|
|
|
|
|
386
|
8
|
|
|
|
|
16
|
my $stadium; |
387
|
|
|
|
|
|
|
my $attendance; |
388
|
|
|
|
|
|
|
|
389
|
8
|
|
|
|
|
28
|
$arena_info =~ tr/\xA0/ /; |
390
|
8
|
50
|
|
|
|
97
|
if ($arena_info !~ /att/i) { |
|
|
50
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
$stadium = $arena_info; |
392
|
0
|
0
|
|
|
|
0
|
if ($arena_info =~ /(\d+\:\d+ \w\w \w\w) (at|\@) (.*)/) { |
393
|
0
|
|
|
|
|
0
|
$self->{time} = $1; |
394
|
0
|
|
|
|
|
0
|
$stadium = $3; |
395
|
|
|
|
|
|
|
} |
396
|
0
|
|
|
|
|
0
|
$attendance = 0; |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
elsif ($arena_info =~ /attendance.*?(\d+)\,(\d+)\s*$/i) { |
399
|
0
|
|
|
|
|
0
|
$stadium = 'Unknown'; |
400
|
0
|
|
|
|
|
0
|
$attendance = $1*1000+$2; |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
else { |
403
|
8
|
|
|
|
|
17
|
my $sep; |
404
|
8
|
|
|
|
|
54
|
($attendance, $sep, $stadium) = ($arena_info =~ /(\S+\d).*?(at\b|\@).*?(\w.*)/); |
405
|
8
|
50
|
|
|
|
23
|
unless ($attendance) { |
406
|
0
|
|
|
|
|
0
|
$attendance = 0; |
407
|
0
|
0
|
|
|
|
0
|
if ($arena_info =~ /(at|\@).*?(\w.*)/) { |
408
|
0
|
|
|
|
|
0
|
$stadium = $2, |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
else { |
412
|
8
|
|
|
|
|
36
|
$attendance =~ s/\D//g; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
8
|
|
|
|
|
23
|
$self->{attendance} = $attendance; |
416
|
8
|
|
|
|
|
20
|
$stadium =~ s/^\s+//; |
417
|
8
|
|
|
|
|
21
|
$stadium =~ s/\s+$//; |
418
|
8
|
|
|
|
|
26
|
$stadium =~ s/\s+/ /g; |
419
|
8
|
|
|
|
|
33
|
$self->{location} = $stadium; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub read_game_info ($$$) { |
423
|
|
|
|
|
|
|
|
424
|
8
|
|
|
8
|
1
|
19
|
my $self = shift; |
425
|
8
|
|
|
|
|
26
|
my $game_info = shift; |
426
|
|
|
|
|
|
|
|
427
|
8
|
|
|
|
|
51
|
$game_info =~ /(Game|NHL)\D*(\d{4})/; |
428
|
8
|
|
|
|
|
35
|
$self->{season_id} = $2; |
429
|
8
|
|
|
|
|
28
|
return; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub read_status_info ($$$) { |
433
|
|
|
|
|
|
|
|
434
|
8
|
|
|
8
|
1
|
23
|
my $self = shift; |
435
|
8
|
|
|
|
|
15
|
my $status_info = shift; |
436
|
|
|
|
|
|
|
|
437
|
8
|
|
|
|
|
23
|
$status_info =~ s/^\s+//; |
438
|
8
|
|
|
|
|
20
|
$status_info =~ s/\s+$//; |
439
|
8
|
|
|
|
|
17
|
$self->{status} = $status_info; |
440
|
8
|
50
|
|
|
|
35
|
if ($status_info =~ / (\d+) \- (\S.*)/) { |
441
|
0
|
|
|
|
|
0
|
$self->{season_id} = $1; |
442
|
0
|
|
|
|
|
0
|
$self->{status} = $2; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
else { |
445
|
8
|
|
|
|
|
31
|
$self->{status} = $status_info; |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub read_team ($$$$) { |
450
|
|
|
|
|
|
|
|
451
|
16
|
|
|
16
|
1
|
26
|
my $self = shift; |
452
|
16
|
|
|
|
|
22
|
my $cell = shift; |
453
|
16
|
|
|
|
|
24
|
my $idx = shift; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
my $name = $self->{old} ? |
456
|
16
|
50
|
|
|
|
70
|
$self->get_sub_tree(0, [0,0,6], $cell) : |
457
|
|
|
|
|
|
|
$self->get_sub_tree(0, [2,0,0], $cell); |
458
|
16
|
0
|
33
|
|
|
39
|
if (ref $name && $self->{old}) { |
459
|
0
|
|
|
|
|
0
|
$name = $self->get_sub_tree(0, [0,0,5], $cell); |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
my $score = $self->{old} ? |
462
|
|
|
|
|
|
|
$self->get_sub_tree(0, [ |
463
|
0
|
|
|
|
|
0
|
2 - (scalar(@{$self->{head}})-1)*(1-$idx) |
464
|
0
|
|
|
|
|
0
|
+ $idx*($self->{gs}-5)-(scalar(@{$self->{head}})-1)*2*$idx, |
465
|
|
|
|
|
|
|
,0,0 |
466
|
16
|
50
|
|
|
|
58
|
], $cell->{_parent}) : $self->get_sub_tree(0, [1,0,0,0,1,0], $cell); |
467
|
16
|
50
|
|
|
|
107
|
$score = $self->get_sub_tree(0, [2+5*$idx+($self->{gs}>=12)*(1-$idx),0,0], $cell->{_parent}) if $score !~ /^\d{1,2}\s*$/; |
468
|
16
|
50
|
33
|
|
|
93
|
$score = $self->get_sub_tree(0, [9,0,0], $cell->{_parent}) if !defined $score || $score !~ /^\d{1,2}\s*$/; |
469
|
16
|
50
|
33
|
|
|
81
|
if (!defined $score || $score !~ /^\s*\d{1,2}\s*$/) { |
470
|
0
|
|
|
|
|
0
|
die "Unreadable header"; |
471
|
|
|
|
|
|
|
} |
472
|
16
|
50
|
|
|
|
32
|
if ($name) { |
473
|
16
|
|
|
|
|
39
|
$name =~ s/^\s+//g; |
474
|
16
|
|
|
|
|
43
|
$name =~ s/\s+$//g; |
475
|
16
|
|
|
|
|
63
|
$name =~ s/\s+/ /g; |
476
|
16
|
50
|
|
|
|
42
|
$name = 'MONTREAL CANADIENS' if $name eq 'CANADIENS MONTREAL'; |
477
|
16
|
|
|
|
|
48
|
$self->{teams}[$idx]{name} = $name; |
478
|
|
|
|
|
|
|
} |
479
|
16
|
|
|
|
|
32
|
$score =~ s/\D//g; |
480
|
16
|
|
|
|
|
46
|
$self->{teams}[$idx]{score} = $score; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
sub get_header ($) { |
484
|
|
|
|
|
|
|
|
485
|
8
|
|
|
8
|
1
|
14
|
my $self = shift; |
486
|
|
|
|
|
|
|
|
487
|
8
|
|
|
|
|
13
|
my $i = 0; |
488
|
8
|
|
|
|
|
19
|
$self->{head} = []; |
489
|
8
|
|
|
|
|
21
|
$self->{teams} = []; |
490
|
8
|
|
|
|
|
141
|
while(my $base_element = $self->get_sub_tree(0, [$i])) { |
491
|
24
|
50
|
|
|
|
43
|
last unless ref $base_element; |
492
|
24
|
100
|
|
|
|
61
|
if ($base_element->tag eq 'table') { |
493
|
8
|
|
|
|
|
50
|
push(@{$self->{head}}, $i); |
|
8
|
|
|
|
|
23
|
|
494
|
8
|
50
|
|
|
|
31
|
push(@{$self->{head}}, 0) if $base_element->{_content}[0]->tag eq 'tbody'; |
|
0
|
|
|
|
|
0
|
|
495
|
8
|
|
|
|
|
82
|
last; |
496
|
|
|
|
|
|
|
} |
497
|
16
|
|
|
|
|
119
|
$i++; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
sub read_header ($) { |
502
|
|
|
|
|
|
|
|
503
|
8
|
|
|
8
|
1
|
17
|
my $self = shift; |
504
|
|
|
|
|
|
|
|
505
|
8
|
|
|
|
|
38
|
$self->get_header(); |
506
|
|
|
|
|
|
|
|
507
|
8
|
|
|
|
|
21
|
my $main_table = $self->get_sub_tree(0, [@{$self->{head}}]); |
|
8
|
|
|
|
|
27
|
|
508
|
8
|
|
|
|
|
16
|
my $gameinfo_table; |
509
|
8
|
|
|
|
|
11
|
my $offset = 0; |
510
|
8
|
50
|
|
|
|
29
|
if ($main_table->attr('class')) { |
511
|
8
|
|
|
|
|
120
|
my $content_table = $self->get_sub_tree(0, [0,0,0],$main_table); |
512
|
8
|
|
|
|
|
28
|
$gameinfo_table = $self->get_sub_tree(0, [0,0,0], $content_table); |
513
|
8
|
|
|
|
|
20
|
$self->{content_table} = $content_table; |
514
|
8
|
|
|
|
|
17
|
$self->{old} = 0; |
515
|
8
|
|
|
|
|
18
|
$offset = 0; |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
else { |
518
|
0
|
|
|
|
|
0
|
$gameinfo_table = $main_table; |
519
|
0
|
|
|
|
|
0
|
$self->{old} = 1; |
520
|
0
|
|
|
|
|
0
|
$offset = 2; |
521
|
|
|
|
|
|
|
} |
522
|
8
|
|
|
|
|
28
|
my $gameinfo_row = $self->get_sub_tree(0, [0], $gameinfo_table); |
523
|
8
|
|
|
|
|
15
|
my $gameinfo_size = @{$gameinfo_row->{_content}}; |
|
8
|
|
|
|
|
20
|
|
524
|
8
|
|
|
|
|
28
|
$self->{gs} = $gameinfo_size; |
525
|
8
|
|
|
|
|
31
|
for my $i (0..2) { |
526
|
24
|
|
|
|
|
40
|
my $cell; |
527
|
24
|
50
|
33
|
|
|
58
|
if ($self->{old} && @{$self->{head}} == 2) { |
|
0
|
|
|
|
|
0
|
|
528
|
0
|
|
|
|
|
0
|
$cell = $self->get_sub_tree(0, [ $i*$self->{old}*5 + $self->{old}*(2-$i) - 1 ], $gameinfo_row); |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
else { |
531
|
24
|
|
|
|
|
75
|
$offset = $i + $i*$self->{old}*$gameinfo_size/2 + $self->{old}*(1-2*$i); |
532
|
24
|
50
|
|
|
|
51
|
$offset += 1-$i if $gameinfo_size == 12; |
533
|
24
|
50
|
|
|
|
44
|
$offset += 1-$i if $gameinfo_size == 14; |
534
|
24
|
50
|
|
|
|
82
|
$cell = $self->get_sub_tree(0, [ $self->{old} ? $offset : ($offset, 0), ], $gameinfo_row); |
535
|
|
|
|
|
|
|
} |
536
|
24
|
100
|
|
|
|
143
|
($i % 2) ? $self->read_status($cell) : $self->read_team($cell, $i / 2); |
537
|
|
|
|
|
|
|
} |
538
|
8
|
50
|
33
|
|
|
30
|
if ($self->{status} =~ /end.*period (3|4)/i |
539
|
|
|
|
|
|
|
&& $self->{teams}[0]{score} != $self->{teams}[1]{score}) { |
540
|
0
|
|
|
|
|
0
|
$self->{status} = 'final'; |
541
|
|
|
|
|
|
|
} |
542
|
8
|
50
|
|
|
|
25
|
$self->{season}-- if ($self->{month} < 9); |
543
|
8
|
50
|
33
|
|
|
137
|
if ( |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
544
|
|
|
|
|
|
|
($self->{season} != 2012 && $self->{month} > 3 && |
545
|
|
|
|
|
|
|
$self->{month} < 8 && $self->{season_id} <= $LAST_PLAYOFF_GAME_INDEX) || |
546
|
|
|
|
|
|
|
($self->{season} == 2012 && $self->{start_ts} >= $LATE_START_IN_2012)) { |
547
|
0
|
|
|
|
|
0
|
$self->{stage} = $PLAYOFF; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
else { |
550
|
8
|
|
|
|
|
23
|
$self->{stage} = $REGULAR; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
$self->{teams}[0]{name} = 'MONTREAL CANADIENS' |
553
|
8
|
50
|
|
|
|
29
|
if $self->{teams}[0]{name} eq 'CANADIENS MONTREAL'; |
554
|
|
|
|
|
|
|
$self->{teams}[1]{name} = 'MONTREAL CANADIENS' |
555
|
8
|
50
|
|
|
|
25
|
if $self->{teams}[1]{name} eq 'CANADIENS MONTREAL'; |
556
|
8
|
|
|
|
|
32
|
$self->{_id} = $self->{season} * 100000 + $self->{stage} * 10000 + $self->{season_id}; |
557
|
8
|
|
50
|
|
|
49
|
$self->{periods} ||= [{},{},{}]; |
558
|
8
|
|
|
|
|
18
|
delete $self->{gs}; |
559
|
8
|
|
|
|
|
70
|
$self->fill_broken($BROKEN_HEADERS{$self->{_id}}); |
560
|
8
|
|
50
|
|
|
27
|
$self->{attendance} ||= 0; |
561
|
8
|
|
|
|
|
42
|
ref ($self) =~ /\:\:(\w\w)$/; |
562
|
8
|
|
|
|
|
25
|
$self->{type} = $1; |
563
|
8
|
|
|
|
|
24
|
$self->{status} = uc $self->{status}; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub convert_time_date ($;$) { |
567
|
|
|
|
|
|
|
|
568
|
10
|
|
|
10
|
1
|
22
|
my $self = shift; |
569
|
10
|
|
100
|
|
|
46
|
my $force_us = shift || 0; |
570
|
|
|
|
|
|
|
|
571
|
10
|
|
|
|
|
25
|
my $date = $self->{date}; |
572
|
10
|
|
|
|
|
20
|
my $time = $self->{time}; |
573
|
10
|
50
|
66
|
|
|
136
|
my ($year, $month, $day) = $date =~ /^\d/ && ! $force_us |
574
|
|
|
|
|
|
|
? Decode_Date_EU($date) |
575
|
|
|
|
|
|
|
: Decode_Date_US($date); |
576
|
|
|
|
|
|
|
|
577
|
10
|
|
66
|
|
|
53
|
$self->{season} ||= $year; |
578
|
10
|
|
|
|
|
34
|
$self->{month} = $month; |
579
|
10
|
|
|
|
|
20
|
$year -= 1900; |
580
|
10
|
|
|
|
|
17
|
$month--; |
581
|
10
|
|
|
|
|
66
|
my ($start_h, $start_m, $start_tz, $end_h, $end_m, $end_tz) = |
582
|
|
|
|
|
|
|
($time =~ /(\d+):(\d+)\W*(\w{1,2}T)\s*\;\D*(\d+):(\d+)\W*(\w{1,2}T)/); |
583
|
10
|
100
|
|
|
|
34
|
unless ($end_h) { |
584
|
2
|
|
|
|
|
5
|
($start_h, $start_m, $start_tz) = ($time =~ /(\d+):(\d+)\W*(\w{1,2}T)\W*/); |
585
|
2
|
50
|
|
|
|
4
|
unless ($start_h) { |
586
|
2
|
|
|
|
|
4
|
$start_h = 12; |
587
|
2
|
|
|
|
|
2
|
$start_m = 0; |
588
|
2
|
|
|
|
|
4
|
$start_tz = 'EDT'; |
589
|
|
|
|
|
|
|
} |
590
|
2
|
|
|
|
|
4
|
$end_h = $start_h + 3; |
591
|
2
|
|
|
|
|
4
|
$end_m = $start_m; |
592
|
2
|
|
|
|
|
3
|
$end_tz = $start_tz; |
593
|
|
|
|
|
|
|
} |
594
|
10
|
100
|
|
|
|
33
|
$start_h += 12 if $start_h < 12; |
595
|
10
|
100
|
|
|
|
25
|
$end_h += 12 if $end_h < $start_h; |
596
|
10
|
|
|
|
|
68
|
$self->{start_ts} = timelocal(0, $start_m, $start_h, $day, $month, $year); |
597
|
10
|
50
|
|
|
|
1022
|
if ($end_h > 23) { |
598
|
0
|
|
|
|
|
0
|
$self->{end_ts} = $self->{start_ts} + 10800; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
else { |
601
|
10
|
|
|
|
|
37
|
$self->{end_ts} = timegm(0, $end_m, $end_h, $day, $month, $year); |
602
|
|
|
|
|
|
|
} |
603
|
10
|
|
33
|
|
|
236
|
$self->{tz} ||= $start_tz; |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
0
|
|
|
0
|
1
|
0
|
sub parse ($) { die "Overload me" } |
607
|
|
|
|
2
|
1
|
|
sub normalize ($) { } |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
sub force_decision ($$) { |
610
|
|
|
|
|
|
|
|
611
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
612
|
0
|
|
|
|
|
0
|
my $team = shift; |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
my @goalies = sort { |
615
|
|
|
|
|
|
|
get_seconds($b->{timeOnIce}) <=> get_seconds($a->{timeOnIce}) |
616
|
0
|
|
|
|
|
0
|
} grep { $_->{position} eq 'G' } @{$team->{roster}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
617
|
0
|
|
|
|
|
0
|
my $goalie = $goalies[0]; |
618
|
0
|
0
|
|
|
|
0
|
if ($self->{_score}[0] == $self->{_score}[1]) { |
|
|
0
|
|
|
|
|
|
619
|
0
|
|
|
|
|
0
|
$goalie->{decision} = 'T'; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
elsif ($self->{_score}[$self->{_t}] > $self->{_score}[1 - $self->{_t}]) { |
622
|
0
|
|
|
|
|
0
|
$goalie->{decision} = 'W'; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
else { |
625
|
0
|
0
|
0
|
|
|
0
|
$goalie->{decision} = $self->{ot} || $self->{so} ? 'O' : 'L'; |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
sub get_sub_tree ($$$;$) { |
631
|
|
|
|
|
|
|
|
632
|
27436
|
|
|
27436
|
1
|
31515
|
my $self = shift; |
633
|
27436
|
|
|
|
|
27960
|
my $want_content = shift; |
634
|
27436
|
|
|
|
|
28026
|
my $walk = shift; |
635
|
27436
|
|
33
|
|
|
40313
|
my $tree = shift || $self->{html} || $self; |
636
|
|
|
|
|
|
|
|
637
|
27436
|
50
|
|
|
|
39209
|
print "Walking ",join(".", @{$walk}), "\n" if $ENV{SHOW_WALK}; |
|
0
|
|
|
|
|
0
|
|
638
|
27436
|
|
|
|
|
30509
|
my $tpointer = \$tree; |
639
|
27436
|
|
|
|
|
30145
|
for my $node (@{$walk}) { |
|
27436
|
|
|
|
|
35134
|
|
640
|
102416
|
100
|
100
|
|
|
220419
|
return undef unless $$tpointer && ref $$tpointer; |
641
|
102156
|
|
|
|
|
104444
|
my $tc = ${$tpointer}->{_content}[$node]; |
|
102156
|
|
|
|
|
160033
|
|
642
|
102156
|
|
|
|
|
129630
|
$tpointer = \$tc; |
643
|
|
|
|
|
|
|
} |
644
|
27176
|
|
|
|
|
32859
|
my $tcopy = $$tpointer; |
645
|
27176
|
50
|
|
|
|
59636
|
return $want_content ? $tcopy->{_content} : $tcopy; |
646
|
|
|
|
|
|
|
} |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
sub process ($) { |
649
|
|
|
|
|
|
|
|
650
|
10
|
|
|
10
|
1
|
3246
|
my $self = shift; |
651
|
|
|
|
|
|
|
|
652
|
10
|
100
|
|
|
|
78
|
$self->read_header() unless $self->{type} eq 'BH'; |
653
|
10
|
|
|
|
|
59
|
$self->parse(); |
654
|
10
|
|
|
|
|
60
|
$self->normalize(); |
655
|
10
|
|
|
|
|
88
|
$self->{html}->delete(); |
656
|
10
|
|
|
|
|
828383
|
delete $self->{source}; |
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
sub set_event_extra_data ($) { |
660
|
|
|
|
|
|
|
|
661
|
14
|
|
|
14
|
1
|
47774
|
my $self = shift; |
662
|
14
|
|
|
|
|
29
|
for my $event (@{$self->{events}}) { |
|
14
|
|
|
|
|
44
|
|
663
|
3525
|
|
|
|
|
9286
|
$event->{sources} = {$self->{type} => 1}; |
664
|
3525
|
50
|
|
|
|
6024
|
$event->{game_id} = delete $event->{game} if $event->{game}; |
665
|
3525
|
100
|
33
|
|
|
5608
|
$event->{player1} ||= $BENCH_PLAYER_ID if ($event->{penalty}); |
666
|
3525
|
|
|
|
|
4004
|
my $t = -1; |
667
|
3525
|
100
|
|
|
|
5911
|
if ($event->{team1}) { |
668
|
|
|
|
|
|
|
$event->{team1} = resolve_team($event->{team1}) if |
669
|
2924
|
50
|
|
|
|
7199
|
$event->{team1} ne 'OTH'; |
670
|
|
|
|
|
|
|
$t = $event->{team1} eq $self->{teams}[0]{name} |
671
|
|
|
|
|
|
|
? 0 |
672
|
|
|
|
|
|
|
: $event->{team1} eq $self->{teams}[1]{name} |
673
|
2924
|
100
|
|
|
|
9061
|
? 1 |
|
|
100
|
|
|
|
|
|
674
|
|
|
|
|
|
|
: -1; |
675
|
|
|
|
|
|
|
} |
676
|
3525
|
100
|
66
|
|
|
8086
|
$event->{team2} = resolve_team($event->{team2}) if $event->{team2} && $event->{team2} ne 'OTH'; |
677
|
3525
|
|
|
|
|
6108
|
$event->{t} = $t; |
678
|
|
|
|
|
|
|
$event->{ts} = |
679
|
|
|
|
|
|
|
$event->{special} ? 0 : |
680
|
|
|
|
|
|
|
$event->{stage} == $PLAYOFF || $event->{stage} == $REGULAR && $event->{period} < 5 ? |
681
|
3525
|
50
|
33
|
|
|
17996
|
($event->{period}-1) * 1200 + get_seconds($event->{time}) : 3900; |
|
|
50
|
|
|
|
|
|
682
|
|
|
|
|
|
|
} |
683
|
14
|
50
|
|
|
|
39
|
$self->{no_events} unless @{$self->{events}}; |
|
14
|
|
|
|
|
86
|
|
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
END { |
687
|
41
|
100
|
|
41
|
|
80403
|
$tb->delete if defined $tb; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
1; |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=head1 AUTHOR |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
More Hockey Stats, C<< >> |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=head1 BUGS |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
699
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
700
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
=head1 SUPPORT |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
perldoc Sport::Analytics::NHL::Report |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
You can also look for information at: |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=over 4 |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
L |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
L |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=item * CPAN Ratings |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
L |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=item * Search CPAN |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
L |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=back |
731
|
|
|
|
|
|
|
|