| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Sport::Analytics::NHL::Report::BS; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 41 |  |  | 41 |  | 62314 | use v5.10.1; | 
|  | 41 |  |  |  |  | 203 |  | 
| 4 | 41 |  |  | 41 |  | 177 | use strict; | 
|  | 41 |  |  |  |  | 117 |  | 
|  | 41 |  |  |  |  | 860 |  | 
| 5 | 41 |  |  | 41 |  | 174 | use warnings FATAL => 'all'; | 
|  | 41 |  |  |  |  | 82 |  | 
|  | 41 |  |  |  |  | 1210 |  | 
| 6 | 41 |  |  | 41 |  | 18024 | use utf8; | 
|  | 41 |  |  |  |  | 491 |  | 
|  | 41 |  |  |  |  | 186 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 41 |  |  | 41 |  | 1158 | use experimental qw(smartmatch); | 
|  | 41 |  |  |  |  | 79 |  | 
|  | 41 |  |  |  |  | 274 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 41 |  |  | 41 |  | 8402 | use Encode; | 
|  | 41 |  |  |  |  | 115070 |  | 
|  | 41 |  |  |  |  | 2369 |  | 
| 11 | 41 |  |  | 41 |  | 1646 | use Storable qw(dclone); | 
|  | 41 |  |  |  |  | 6919 |  | 
|  | 41 |  |  |  |  | 1440 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 41 |  |  | 41 |  | 1524 | use JSON; | 
|  | 41 |  |  |  |  | 26142 |  | 
|  | 41 |  |  |  |  | 265 |  | 
| 14 | 41 |  |  | 41 |  | 9608 | use Try::Tiny; | 
|  | 41 |  |  |  |  | 22797 |  | 
|  | 41 |  |  |  |  | 1768 |  | 
| 15 | 41 |  |  | 41 |  | 16715 | use Text::Unidecode; | 
|  | 41 |  |  |  |  | 44843 |  | 
|  | 41 |  |  |  |  | 1735 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 41 |  |  | 41 |  | 603 | use parent 'Sport::Analytics::NHL::Report'; | 
|  | 41 |  |  |  |  | 272 |  | 
|  | 41 |  |  |  |  | 261 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 41 |  |  | 41 |  | 2265 | use Sport::Analytics::NHL::Config; | 
|  | 41 |  |  |  |  | 129 |  | 
|  | 41 |  |  |  |  | 6406 |  | 
| 20 | 41 |  |  | 41 |  | 258 | use Sport::Analytics::NHL::Errors; | 
|  | 41 |  |  |  |  | 72 |  | 
|  | 41 |  |  |  |  | 6283 |  | 
| 21 | 41 |  |  | 41 |  | 362 | use Sport::Analytics::NHL::Tools; | 
|  | 41 |  |  |  |  | 89 |  | 
|  | 41 |  |  |  |  | 5307 |  | 
| 22 | 41 |  |  | 41 |  | 299 | use Sport::Analytics::NHL::Util; | 
|  | 41 |  |  |  |  | 83 |  | 
|  | 41 |  |  |  |  | 2187 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 41 |  |  | 41 |  | 230 | use Data::Dumper; | 
|  | 41 |  |  |  |  | 86 |  | 
|  | 41 |  |  |  |  | 131583 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 NAME | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | Sport::Analytics::NHL::Report::BS - Class for the Boxscore JSON report | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | =head1 SYNOPSYS | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | Class for the Boxscore JSON report. | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | use Sport::Analytics::NHL::Report::BS; | 
| 35 |  |  |  |  |  |  | my $report = Sport::Analytics::NHL::Report::BS->new($json) | 
| 36 |  |  |  |  |  |  | $report->process(); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =head1 METHODS | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | =over 2 | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =item C | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | Create the Boxscore object with the JSON. | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | =item C | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | Process the Boxscore into the object compatible with further processing, etc. | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =item C | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | Assign specific event data | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | Arguments: | 
| 55 |  |  |  |  |  |  | * the event hashref we're building | 
| 56 |  |  |  |  |  |  | * the original event from the JSON | 
| 57 |  |  |  |  |  |  | Returns: void. | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =item C | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | Assign specific goal event data | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | Arguments: | 
| 64 |  |  |  |  |  |  | * the goal event hashref we're building | 
| 65 |  |  |  |  |  |  | * the original goal event from the JSON | 
| 66 |  |  |  |  |  |  | Returns: void. | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | =item C | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | Assign specific penalty event data | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | Arguments: | 
| 73 |  |  |  |  |  |  | * the penalty event hashref we're building | 
| 74 |  |  |  |  |  |  | * the original penalty event from the JSON | 
| 75 |  |  |  |  |  |  | Returns: void. | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | =item C | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | Checks if the event is marked as 'BROKEN' and assigns default values to it. | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | Arguments: | 
| 82 |  |  |  |  |  |  | * the event hashref we're building | 
| 83 |  |  |  |  |  |  | * the original event from the JSON | 
| 84 |  |  |  |  |  |  | Returns: void. | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | Note: should probably be replaced with fill_broken from the Tools package. | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | =item C | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | Fixes the goalie scored upon in the goal events of old boxscores (pre-1987). | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | Arguments: the goal event | 
| 93 |  |  |  |  |  |  | Returns: void. | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | =item C | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | Fixes the served by data in a penalty event, when a bench or coach penalty is assigned in error to a player. | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | Arguments: the event | 
| 100 |  |  |  |  |  |  | Returns: void. | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | =item C | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | Assigns the player1, player2 and servedby players in a variety of events | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | Arguments: | 
| 107 |  |  |  |  |  |  | * the event hashref we're building | 
| 108 |  |  |  |  |  |  | * the original event from the JSON | 
| 109 |  |  |  |  |  |  | Returns: void. | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | =item C | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | Marks a player entry in the boxscore as a broken one beyond repair | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | Arguments: the player hashref | 
| 116 |  |  |  |  |  |  | Returns: void. | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =item C | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | Converts the events as specified in the boxscore JSON into the $boxscore->{events} arrayref with adjusted fields. | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | Arguments: the JSON events | 
| 123 |  |  |  |  |  |  | Returns: void. | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | =item C | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | Sets some extra header data not handled by any explicit methods: status, location, shootout data etc. | 
| 128 |  |  |  |  |  |  | Arguments: the boxscore JSON hashref | 
| 129 |  |  |  |  |  |  | Returns: void. | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | =item C | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | Sets the game id data: the season, the stage, the season ID, and the overall 9-digit id. | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | Arguments: the ID field of the boxscore JSON | 
| 136 |  |  |  |  |  |  | Returns: void. | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | =item C | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | Sets the officials for the game. | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | Arguments: the officials' section in the boxscore JSON hashref | 
| 143 |  |  |  |  |  |  | Returns: void. | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | =item C | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | Set the game periods data from the boxscore JSON hashref | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | Arguments: the boxscore JSON hashref | 
| 150 |  |  |  |  |  |  | Returns: void. | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =item C | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | Sets the player in a team roster from the boxscore JSON hashref in accordance with our data model. | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | Arguments: the player live data entry from the boxscore JSON hashref | 
| 157 |  |  |  |  |  |  | Returns: void. | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | =item C | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | Parses the game stars. Not in use. | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | =item C | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | Sets the team data and the team rosters from the boxscore JSON hashref in accordance with our data model. | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | Arguments: the boxscore JSON hashref | 
| 168 |  |  |  |  |  |  | Returns: void. | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | =item C | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | Sets the start, end and last updated timestamps for the game from the date strings in the JSON | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | Argument: the boxscore JSON hashref | 
| 175 |  |  |  |  |  |  | Returns: void. | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | =item C | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | Builds a resolution cache for the roster, with keys as numbers of players pointing at their whole player record in the game. The players who are not numbered are stored as reference to their record in a special list [names]. | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Arguments: none | 
| 182 |  |  |  |  |  |  | Returns: void. Sets $self->{resolve_cache} | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | Note: the caches are per team, and valid for one game only. | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | =back | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | =cut | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | our @JS_IGNORED_EVENT_TYPES = qw( | 
| 191 |  |  |  |  |  |  | PERIOD_READY GAME_SCHEDULED PERIOD_OFFICIAL GAME_OFFICIAL | 
| 192 |  |  |  |  |  |  | SHOOTOUT_COMPLETE EARLY_INT_END EARLY_INT_START EMERGENCY_GOALTENDER | 
| 193 |  |  |  |  |  |  | ); | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | our %PLAYER_ID_MAP = ( | 
| 196 |  |  |  |  |  |  | Winner   => 'player1', | 
| 197 |  |  |  |  |  |  | Loser    => 'player2', | 
| 198 |  |  |  |  |  |  | Hitter   => 'player1', | 
| 199 |  |  |  |  |  |  | Hittee   => 'player2', | 
| 200 |  |  |  |  |  |  | Shooter  => 'player1', | 
| 201 |  |  |  |  |  |  | Blocker  => 'player2', | 
| 202 |  |  |  |  |  |  | PlayerID => 'player1', | 
| 203 |  |  |  |  |  |  | PenaltyOn => 'player1', | 
| 204 |  |  |  |  |  |  | DrewBy    => 'player2', | 
| 205 |  |  |  |  |  |  | ServedBy  => 'servedby', | 
| 206 |  |  |  |  |  |  | Scorer    => 'player1', | 
| 207 |  |  |  |  |  |  | Assist    => 'void', | 
| 208 |  |  |  |  |  |  | Goalie    => 'player2', | 
| 209 |  |  |  |  |  |  | ); | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub new ($$) { | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 15 |  |  | 15 | 1 | 42 | my $class = shift; | 
| 214 | 15 |  |  |  |  | 47 | my $json  = shift; | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 15 |  |  |  |  | 257 | my $code = JSON->new(); | 
| 217 | 15 |  |  |  |  | 127 | $code->utf8(1); | 
| 218 | 15 |  |  |  |  | 30 | my $self; | 
| 219 | 15 | 50 |  |  |  | 61 | return undef unless $json; | 
| 220 | 15 |  |  | 15 |  | 1009 | try { $self = {json => $code->decode(decode "UTF-8", $json)} } | 
| 221 | 15 |  |  | 7 |  | 188 | catch { $self = {json => $code->decode($json)} }; | 
|  | 7 |  |  |  |  | 20060 |  | 
| 222 |  |  |  |  |  |  | return undef unless | 
| 223 |  |  |  |  |  |  | $self->{json}{gameData}{status}{abstractGameState} | 
| 224 | 15 | 50 | 33 |  |  | 46785 | && $self->{json}{gameData}{status}{abstractGameState} eq 'Final'; | 
| 225 | 15 |  |  |  |  | 52 | bless $self, $class; | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 15 |  |  |  |  | 142 | $self; | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | sub set_id_data ($$) { | 
| 231 |  |  |  |  |  |  |  | 
| 232 | 7 |  |  | 7 | 1 | 1383 | my $self = shift; | 
| 233 | 7 |  |  |  |  | 21 | my $id_data = shift; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 7 |  |  |  |  | 36 | my $season_info    = parse_nhl_game_id($id_data); | 
| 236 | 7 |  |  |  |  | 22 | $self->{season}    = $season_info->{season}; | 
| 237 | 7 |  |  |  |  | 18 | $self->{stage}     = $season_info->{stage}; | 
| 238 | 7 |  |  |  |  | 15 | $self->{season_id} = $season_info->{season_id}; | 
| 239 | 7 |  |  |  |  | 42 | $self->{_id}       = $self->{season} * 100000 + $self->{stage} * 10000 + $self->{season_id}; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub set_timestamps ($$) { | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 6 |  |  | 6 | 1 | 14 | my $self = shift; | 
| 245 | 6 |  |  |  |  | 14 | my $json = shift; | 
| 246 |  |  |  |  |  |  |  | 
| 247 | 6 |  |  |  |  | 14 | my $ts = $json->{metaData}{timeStamp}; $ts =~ s/_/ /; | 
|  | 6 |  |  |  |  | 38 |  | 
| 248 | 6 |  |  |  |  | 34 | $self->{last_updated} = str3time($ts); | 
| 249 | 6 |  |  |  |  | 118 | $self->{start_ts}     = str3time($json->{gameData}{datetime}{dateTime}); | 
| 250 |  |  |  |  |  |  | $self->{stop_ts}      = $json->{gameData}{datetime}{endDateTime} | 
| 251 |  |  |  |  |  |  | ? str3time($json->{gameData}{datetime}{endDateTime}) | 
| 252 | 6 | 100 |  |  |  | 44 | : $self->{start_ts} + 9000; | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | sub set_extra_header_data ($$) { | 
| 257 |  |  |  |  |  |  |  | 
| 258 | 6 |  |  | 6 | 1 | 20 | my $self = shift; | 
| 259 | 6 |  |  |  |  | 10 | my $json = shift; | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 6 |  |  |  |  | 16 | $self->{status}       = 'FINAL'; | 
| 262 | 6 |  |  |  |  | 18 | $self->{location}     = $json->{gameData}{venue}{name}; | 
| 263 | 6 | 50 |  |  |  | 140 | if ($json->{liveData}{linescore}{hasShootout}) { | 
| 264 | 0 |  |  |  |  | 0 | $self->{so} = 1; | 
| 265 | 0 |  |  |  |  | 0 | $self->{shootout} = dclone $json->{liveData}{linescore}{shootoutInfo}; | 
| 266 | 0 |  |  |  |  | 0 | delete $self->{shootout}{startTime}; | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub set_officials ($$) { | 
| 272 |  |  |  |  |  |  |  | 
| 273 | 6 |  |  | 6 | 1 | 17 | my $self      = shift; | 
| 274 | 6 |  |  |  |  | 10 | my $officials = shift; | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | $self->{officials} = { | 
| 277 | 6 |  |  |  |  | 28 | referees => [], | 
| 278 |  |  |  |  |  |  | linesmen => [], | 
| 279 |  |  |  |  |  |  | }; | 
| 280 | 6 |  |  |  |  | 11 | for my $official (@{$officials}) { | 
|  | 6 |  |  |  |  | 14 |  | 
| 281 |  |  |  |  |  |  | my $o = { | 
| 282 |  |  |  |  |  |  | name        => unidecode(uc $official->{official}{fullName}), | 
| 283 | 22 |  | 100 |  |  | 87 | official_id => $official->{official}{id} || 0, | 
| 284 |  |  |  |  |  |  | }; | 
| 285 |  |  |  |  |  |  | push( | 
| 286 | 22 |  |  |  |  | 317 | @{$self->{officials}{ | 
| 287 | 22 | 100 |  |  |  | 69 | $official->{officialType} eq 'Referee' ? 'referees' : 'linesmen' | 
| 288 |  |  |  |  |  |  | }}, $o | 
| 289 |  |  |  |  |  |  | ); | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  | } | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | sub set_broken_player ($$) { | 
| 294 |  |  |  |  |  |  |  | 
| 295 | 245 |  |  | 245 | 1 | 266 | my $self   = shift; | 
| 296 | 245 |  |  |  |  | 260 | my $player = shift; | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 245 | 0 | 33 |  |  | 640 | if ( | 
|  |  |  | 0 |  |  |  |  | 
| 299 |  |  |  |  |  |  | $BROKEN_PLAYERS{BS}->{$self->{_id}} | 
| 300 |  |  |  |  |  |  | && $BROKEN_PLAYERS{BS}->{$self->{_id}}->{$player} | 
| 301 |  |  |  |  |  |  | && $BROKEN_PLAYERS{BS}->{$self->{_id}}->{$player} == -1 | 
| 302 |  |  |  |  |  |  | ) { | 
| 303 |  |  |  |  |  |  | return { | 
| 304 |  |  |  |  |  |  | broken => 1, | 
| 305 |  |  |  |  |  |  | _id    => $player, | 
| 306 | 0 |  |  |  |  | 0 | number => $self->{broken_number}++, | 
| 307 |  |  |  |  |  |  | }; | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | sub set_player ($$) { | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 245 |  |  | 245 | 1 | 268 | my $self      = shift; | 
| 314 | 245 |  |  |  |  | 267 | my $ld_player = shift; | 
| 315 |  |  |  |  |  |  |  | 
| 316 | 245 | 100 | 100 |  |  | 488 | unless ($ld_player->{stats}{skaterStats} || $ld_player->{stats}{goalieStats}) { | 
| 317 |  |  |  |  |  |  | $ld_player->{stats}{ | 
| 318 | 25 | 50 |  |  |  | 76 | $ld_player->{position}{code} eq 'G' ? 'goalieStats' : 'skaterStats' | 
| 319 |  |  |  |  |  |  | } = { broken => 1 }, | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  | my $player = { | 
| 322 |  |  |  |  |  |  | _id      => $ld_player->{person}{id}, | 
| 323 |  |  |  |  |  |  | name     => uc unidecode($ld_player->{person}{fullName}), | 
| 324 |  |  |  |  |  |  | number   => $ld_player->{jerseyNumber}, | 
| 325 |  |  |  |  |  |  | position => uc $ld_player->{position}{code}, | 
| 326 |  |  |  |  |  |  | $ld_player->{stats}{skaterStats}           ? | 
| 327 | 233 |  |  |  |  | 7710 | %{dclone $ld_player->{stats}{skaterStats}} : | 
| 328 | 245 | 100 | 50 |  |  | 466 | %{dclone ($ld_player->{stats}{goalieStats} || {})}, | 
|  | 12 |  |  |  |  | 408 |  | 
| 329 |  |  |  |  |  |  | }; | 
| 330 | 245 | 0 | 33 |  |  | 960 | if ($BROKEN_PLAYERS{BS}->{$self->{_id}} | 
|  |  |  | 33 |  |  |  |  | 
| 331 |  |  |  |  |  |  | && $BROKEN_PLAYERS{BS}->{$self->{_id}}->{$ld_player->{person}{id}} | 
| 332 |  |  |  |  |  |  | && ref $BROKEN_PLAYERS{BS}->{$self->{_id}}->{$ld_player->{person}{id}}) { | 
| 333 | 0 |  |  |  |  | 0 | for my $stat (keys %{$BROKEN_PLAYERS{BS}->{$self->{_id}}->{$ld_player->{person}{id}}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 334 | 0 |  |  |  |  | 0 | $player->{$stat} = $BROKEN_PLAYERS{BS}->{$self->{_id}}->{$ld_player->{person}{id}}{$stat}; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | } | 
| 337 | 245 | 100 | 50 |  |  | 469 | $player->{pim} ||= ($player->{penaltyMinutes} || 0) if $player->{position} eq 'G'; | 
|  |  |  | 33 |  |  |  |  | 
| 338 | 245 |  |  |  |  | 504 | $player; | 
| 339 |  |  |  |  |  |  | } | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | sub set_teams ($$) { | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 6 |  |  | 6 | 1 | 17 | my $self = shift; | 
| 344 | 6 |  |  |  |  | 23 | my $json = shift; | 
| 345 |  |  |  |  |  |  |  | 
| 346 | 6 |  |  |  |  | 16 | $self->{teams}        = []; | 
| 347 |  |  |  |  |  |  | $self->{_score} = [ | 
| 348 |  |  |  |  |  |  | $json->{liveData}{linescore}{teams}{away}{goals}, | 
| 349 |  |  |  |  |  |  | $json->{liveData}{linescore}{teams}{home}{goals}, | 
| 350 | 6 |  |  |  |  | 41 | ]; | 
| 351 | 6 |  |  |  |  | 11 | my $t = 0; | 
| 352 | 6 |  |  |  |  | 30 | for my $team_key (qw(away home)) { | 
| 353 | 12 |  |  |  |  | 31 | $self->{_t} = $t; | 
| 354 | 12 |  |  |  |  | 26 | my $ldb_team = $json->{liveData}{boxscore}{teams}{$team_key}; | 
| 355 | 12 |  |  |  |  | 24 | my $ldl_team = $json->{liveData}{linescore}{teams}{$team_key}; | 
| 356 |  |  |  |  |  |  | my $team = { | 
| 357 |  |  |  |  |  |  | orig      => $ldb_team->{team}{triCode} || $json->{gameData}{teams}{$team_key}{abbreviation}, | 
| 358 |  |  |  |  |  |  | stats     => $ldb_team->{teamStats}{teamSkaterStats}, | 
| 359 |  |  |  |  |  |  | roster    => [], | 
| 360 |  |  |  |  |  |  | scratches => $ldb_team->{scratches}, | 
| 361 |  |  |  |  |  |  | coach     => $ldb_team->{coaches}[0]{person}{fullName} | 
| 362 |  |  |  |  |  |  | ? uc $ldb_team->{coaches}[0]{person}{fullName} | 
| 363 |  |  |  |  |  |  | : 'UNKNOWN COACH', | 
| 364 |  |  |  |  |  |  | score     => $ldl_team->{goals}, | 
| 365 |  |  |  |  |  |  | shots     => $ldl_team->{shotsOnGoal}, | 
| 366 |  |  |  |  |  |  | pull      => $ldl_team->{goaliePulled}, | 
| 367 |  |  |  |  |  |  | teamid    => $ldl_team->{team}{id}, | 
| 368 | 12 | 100 | 33 |  |  | 120 | }; | 
| 369 | 12 |  |  |  |  | 46 | $team->{name} = resolve_team($team->{orig}); | 
| 370 | 12 | 50 |  |  |  | 82 | $team->{coach} = $BROKEN_COACHES{$team->{coach}} if $BROKEN_COACHES{$team->{coach}}; | 
| 371 | 12 |  |  |  |  | 25 | $self->{broken_number} = 101; | 
| 372 | 12 |  |  |  |  | 16 | for my $ld_player (values %{$ldb_team->{players}}) { | 
|  | 12 |  |  |  |  | 45 |  | 
| 373 |  |  |  |  |  |  | my $player = | 
| 374 | 245 |  | 33 |  |  | 432 | $self->set_broken_player($ld_player->{person}{id}) || | 
| 375 |  |  |  |  |  |  | $self->set_player($ld_player); | 
| 376 | 245 |  |  |  |  | 269 | push(@{$team->{roster}}, $player); | 
|  | 245 |  |  |  |  | 345 |  | 
| 377 | 245 | 100 |  |  |  | 453 | $team->{_decision} = $player->{decision} if ($player->{decision}); | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  | push( | 
| 380 | 0 |  |  |  |  | 0 | @{$team->{roster}}, | 
| 381 | 0 |  |  |  |  | 0 | @{$MISSING_PLAYERS{$self->{_id}}->[$t]} | 
| 382 |  |  |  |  |  |  | ) if ($MISSING_PLAYERS{$self->{_id}} | 
| 383 | 12 | 50 | 33 |  |  | 43 | && @{$MISSING_PLAYERS{$self->{_id}}->[$t]}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 384 | 12 | 50 |  |  |  | 31 | $self->force_decision($team) unless $team->{_decision}; | 
| 385 | 12 |  |  |  |  | 16 | push(@{$self->{teams}}, $team); | 
|  | 12 |  |  |  |  | 24 |  | 
| 386 | 12 |  |  |  |  | 22 | $t++; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | } | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | sub set_stars ($$) { | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 394 | 0 |  |  |  |  | 0 | my $json = shift; | 
| 395 |  |  |  |  |  |  |  | 
| 396 | 0 |  |  |  |  | 0 | $self->{stars} = []; | 
| 397 | 0 |  |  |  |  | 0 | for my $star (qw(firstStar secondStar thirdStar)) { | 
| 398 | 0 |  |  |  |  | 0 | push(@{$self->{stars}}, $json->{liveData}{decisions}{$star}{id}) | 
| 399 | 0 | 0 |  |  |  | 0 | if $json->{liveData}{decisions}{$star}; | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  | } | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | sub set_periods ($$) { | 
| 404 |  |  |  |  |  |  |  | 
| 405 | 6 |  |  | 6 | 1 | 51 | my $self = shift; | 
| 406 | 6 |  |  |  |  | 9 | my $json = shift; | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 6 |  |  |  |  | 16 | $self->{periods} = []; | 
| 409 | 6 |  |  |  |  | 13 | my $p = 1; | 
| 410 | 6 |  |  |  |  | 11 | for my $ld_period (@{$json->{liveData}{linescore}{periods}}) { | 
|  | 6 |  |  |  |  | 24 |  | 
| 411 |  |  |  |  |  |  | my $period = { | 
| 412 |  |  |  |  |  |  | id       => $p, | 
| 413 |  |  |  |  |  |  | start_ts => $ld_period->{startTime} | 
| 414 |  |  |  |  |  |  | ? str3time($ld_period->{startTime}) | 
| 415 |  |  |  |  |  |  | : $self->{start_ts} + 2250*($p-1), | 
| 416 |  |  |  |  |  |  | stop_ts  => $ld_period->{endTime} | 
| 417 |  |  |  |  |  |  | ? str3time($ld_period->{endTime}) | 
| 418 |  |  |  |  |  |  | : $self->{start_ts} + 2250*$p-1, | 
| 419 |  |  |  |  |  |  | score    => [ | 
| 420 |  |  |  |  |  |  | $ld_period->{away}{goals}, $ld_period->{away}{shotsOnGoal}, | 
| 421 |  |  |  |  |  |  | $ld_period->{home}{shotsOnGoal}, $ld_period->{home}{goals}, | 
| 422 |  |  |  |  |  |  | ], | 
| 423 |  |  |  |  |  |  | type     => $ld_period->{periodType}, | 
| 424 | 18 | 100 |  |  |  | 66 | }; | 
|  |  | 100 |  |  |  |  |  | 
| 425 | 18 |  |  |  |  | 32 | push(@{$self->{periods}}, $period); | 
|  | 18 |  |  |  |  | 35 |  | 
| 426 | 18 | 50 | 33 |  |  | 101 | $self->{ot} ||= $ld_period->{periodType} eq 'OVERTIME' ? 1 : 0; | 
| 427 | 18 |  |  |  |  | 33 | $p++; | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | sub place_players ($$) { | 
| 432 |  |  |  |  |  |  |  | 
| 433 | 756 |  |  | 756 | 1 | 844 | my $event    = shift; | 
| 434 | 756 |  |  |  |  | 759 | my $ld_event = shift; | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 756 |  |  |  |  | 762 | for my $player (@{$ld_event->{players}}) { | 
|  | 756 |  |  |  |  | 1320 |  | 
| 437 | 1537 |  |  |  |  | 4616 | $event->{$PLAYER_ID_MAP{$player->{playerType}}} = $player->{player}{id}; | 
| 438 |  |  |  |  |  |  | } | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | sub assign_specific_penalty_data ($$) { | 
| 442 |  |  |  |  |  |  |  | 
| 443 | 100 |  |  | 100 | 1 | 115 | my $event    = shift; | 
| 444 | 100 |  |  |  |  | 111 | my $ld_event = shift; | 
| 445 |  |  |  |  |  |  |  | 
| 446 | 100 |  |  |  |  | 183 | place_players($event, $ld_event); | 
| 447 | 100 |  |  |  |  | 188 | $event->{penalty}  = $ld_event->{result}{secondaryType}; | 
| 448 | 100 |  | 100 |  |  | 282 | $event->{severity} = uc($ld_event->{result}{penaltySeverity} || ''); | 
| 449 | 100 |  |  |  |  | 154 | $event->{length}   = $ld_event->{result}{penaltyMinutes}; | 
| 450 | 100 | 50 | 33 |  |  | 259 | if ($event->{penalty} =~ /bench/i && $event->{penalty} !~ /leaving/i) { | 
| 451 | 0 | 0 |  |  |  | 0 | $event->{servedby} = $event->{player1} if $event->{player1}; | 
| 452 | 0 |  |  |  |  | 0 | $event->{player1}  = $BENCH_PLAYER_ID; | 
| 453 |  |  |  |  |  |  | } | 
| 454 | 100 | 100 | 66 |  |  | 634 | if ($event->{penalty} =~ /too many/i || $event->{description} =~ /^\s+against/i) { | 
|  |  | 50 |  |  |  |  |  | 
| 455 | 5 | 50 | 33 |  |  | 66 | $event->{servedby} ||= $event->{player1} if $event->{player1}; | 
| 456 | 5 |  |  |  |  | 11 | $event->{player1}  = $BENCH_PLAYER_ID; | 
| 457 | 5 |  |  |  |  | 24 | $event->{pim_correction} += 2; | 
| 458 |  |  |  |  |  |  | } | 
| 459 |  |  |  |  |  |  | elsif ($event->{penalty} =~ /(.*\w)\W*\bcoach$/i) { | 
| 460 | 0 | 0 |  |  |  | 0 | $event->{servedby} = $event->{player1} if $event->{player1}; | 
| 461 | 0 |  |  |  |  | 0 | $event->{player1}  = $COACH_PLAYER_ID; | 
| 462 | 0 |  |  |  |  | 0 | $event->{penalty} = $1; | 
| 463 |  |  |  |  |  |  | } | 
| 464 | 100 |  |  |  |  | 241 | $event->{penalty} = normalize_penalty($event->{penalty}); | 
| 465 |  |  |  |  |  |  | } | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | sub assign_specific_goal_data ($$) { | 
| 468 |  |  |  |  |  |  |  | 
| 469 | 26 |  |  | 26 | 1 | 47 | my $event    = shift; | 
| 470 | 26 |  |  |  |  | 36 | my $ld_event = shift; | 
| 471 |  |  |  |  |  |  |  | 
| 472 | 26 |  |  |  |  | 62 | place_players($event, $ld_event); | 
| 473 |  |  |  |  |  |  | $event->{assists} = [ map { | 
| 474 | 102 | 100 |  |  |  | 197 | $_->{playerType} eq 'Assist' ? $_->{player}{id} : (), | 
| 475 | 26 |  |  |  |  | 37 | } @{$ld_event->{players}} ]; | 
|  | 26 |  |  |  |  | 49 |  | 
| 476 | 26 |  | 100 |  |  | 99 | $event->{shot_type} = vocabulary_lookup('shot_type', $ld_event->{result}{secondaryType} || ''); | 
| 477 | 26 |  |  |  |  | 71 | $event->{en}        = $ld_event->{result}{emptyNet}; | 
| 478 | 26 |  |  |  |  | 62 | $event->{gwg}       = $ld_event->{result}{gameWinningGoal}; | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | sub assign_specific_event_data ($$) { | 
| 482 |  |  |  |  |  |  |  | 
| 483 | 1616 |  |  | 1616 | 1 | 1792 | my $event    = shift; | 
| 484 | 1616 |  |  |  |  | 1650 | my $ld_event = shift; | 
| 485 |  |  |  |  |  |  |  | 
| 486 | 1616 |  |  |  |  | 2243 | for ($event->{type}) { | 
| 487 | 1616 |  |  |  |  | 2708 | when ('FAC') { | 
| 488 | 325 |  |  |  |  | 369 | for my $player (@{$ld_event->{players}}) { | 
|  | 325 |  |  |  |  | 556 |  | 
| 489 | 650 |  |  |  |  | 1906 | $event->{$PLAYER_ID_MAP{$player->{playerType}}} = $player->{player}{id}; | 
| 490 |  |  |  |  |  |  | } | 
| 491 |  |  |  |  |  |  | $event->{winning_team} = $ld_event->{team}{triCode}, | 
| 492 | 325 |  |  |  |  | 771 | } | 
| 493 | 1291 |  |  |  |  | 1460 | when ('GIVE') { $event->{player1} = $ld_event->{players}[0]{player}{id}; } | 
|  | 50 |  |  |  |  | 188 |  | 
| 494 | 1241 |  |  |  |  | 1320 | when ('TAKE') { $event->{player1} = $ld_event->{players}[0]{player}{id}; } | 
|  | 45 |  |  |  |  | 205 |  | 
| 495 | 1196 |  |  |  |  | 1266 | when ('MISS') { | 
| 496 | 160 |  |  |  |  | 477 | $event->{player1} = $ld_event->{players}[0]{player}{id}; | 
| 497 | 160 |  |  |  |  | 496 | $event->{description} =~ /\- (.*)/; | 
| 498 | 160 |  | 50 |  |  | 558 | $event->{miss}    = vocabulary_lookup('miss', $1 || ''); | 
| 499 |  |  |  |  |  |  | } | 
| 500 | 1036 |  |  |  |  | 1152 | when ('STOP')  { $event->{stopreason} = [ vocabulary_lookup('stopreason', $event->{description})] } | 
|  | 245 |  |  |  |  | 435 |  | 
| 501 | 791 |  |  |  |  | 839 | when ('HIT')   { place_players($event, $ld_event); } | 
|  | 160 |  |  |  |  | 281 |  | 
| 502 | 631 |  |  |  |  | 697 | when ('BLOCK') { | 
| 503 | 155 |  |  |  |  | 333 | place_players($event, $ld_event); | 
| 504 | 155 |  |  |  |  | 214 | my $x = $event->{player2}; | 
| 505 | 155 |  |  |  |  | 195 | $event->{player2} = $event->{player1}; | 
| 506 | 155 |  |  |  |  | 235 | $event->{player1} = $x; | 
| 507 |  |  |  |  |  |  | } | 
| 508 | 476 |  |  |  |  | 540 | when ('SHOT') { | 
| 509 | 315 |  |  |  |  | 596 | place_players($event, $ld_event); | 
| 510 | 315 |  | 50 |  |  | 866 | $event->{shot_type} = vocabulary_lookup('shot_type', $ld_event->{result}{secondaryType} || ''); | 
| 511 |  |  |  |  |  |  | } | 
| 512 | 161 |  |  |  |  | 191 | when ('PENL') { assign_specific_penalty_data($event, $ld_event); } | 
|  | 100 |  |  |  |  | 180 |  | 
| 513 | 61 |  |  |  |  | 83 | when ('GOAL') {	assign_specific_goal_data($event, $ld_event);    } | 
|  | 26 |  |  |  |  | 72 |  | 
| 514 |  |  |  |  |  |  | } | 
| 515 | 1616 |  |  |  |  | 2221 | delete $event->{void}; | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | sub check_broken ($$) { | 
| 519 |  |  |  |  |  |  |  | 
| 520 | 1616 |  |  | 1616 | 1 | 1663 | my $event    = shift; | 
| 521 | 1616 |  |  |  |  | 1585 | my $ld_event = shift; | 
| 522 |  |  |  |  |  |  |  | 
| 523 | 1616 | 50 | 33 |  |  | 5531 | if ( | 
| 524 |  |  |  |  |  |  | $BROKEN_EVENTS{BS}->{$event->{game_id}} && | 
| 525 |  |  |  |  |  |  | (my $evx = $BROKEN_EVENTS{BS}->{$event->{game_id}}->{$ld_event->{about}{eventIdx}}) | 
| 526 |  |  |  |  |  |  | ) { | 
| 527 | 0 |  |  |  |  | 0 | for my $key (keys %{$evx}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 528 | 0 |  |  |  |  | 0 | $event->{$key} = $evx->{$key}; | 
| 529 |  |  |  |  |  |  | } | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | sub fix_servedby ($) { | 
| 534 |  |  |  |  |  |  |  | 
| 535 | 1616 |  |  | 1616 | 1 | 1727 | my $event = shift; | 
| 536 |  |  |  |  |  |  |  | 
| 537 | 1616 | 50 | 100 |  |  | 2779 | if ($event->{type} eq 'PENL' | 
|  |  |  | 66 |  |  |  |  | 
| 538 |  |  |  |  |  |  | && $event->{description} =~ /served by/i | 
| 539 |  |  |  |  |  |  | && ! $event->{servedby}) { | 
| 540 | 0 |  |  |  |  | 0 | $event->{_servedby} = 1; | 
| 541 |  |  |  |  |  |  | my @pl = ( | 
| 542 |  |  |  |  |  |  | 3,2, | 
| 543 |  |  |  |  |  |  | ($event->{description} =~ /\b(illegal|official|proceed|dress|refusal|objects|misconduct|ineligible|conduct|bench|coach|delay|abus|leaving)/i && $event->{description} !~ /leaves.*bench/i) | 
| 544 |  |  |  |  |  |  | || $event->{severity} =~ /BENCH/ | 
| 545 | 0 | 0 | 0 |  |  | 0 | || $event->{description} =~ /minor served by/i | 
| 546 |  |  |  |  |  |  | ? 1 : () | 
| 547 |  |  |  |  |  |  | ); | 
| 548 | 0 |  |  |  |  | 0 | for my $p (@pl) { | 
| 549 | 0 |  |  |  |  | 0 | my $field = "player$p"; | 
| 550 | 0 | 0 |  |  |  | 0 | if (defined $event->{$field}) { | 
| 551 | 0 |  |  |  |  | 0 | delete $event->{_servedby}; | 
| 552 | 0 |  |  |  |  | 0 | $event->{servedby} = delete $event->{$field}; | 
| 553 | 0 | 0 |  |  |  | 0 | if ($field eq 'player1') { | 
| 554 | 0 | 0 |  |  |  | 0 | $event->{$field} = $event->{description} =~ /coach/i ? | 
| 555 |  |  |  |  |  |  | $COACH_PLAYER_ID : $BENCH_PLAYER_ID; | 
| 556 |  |  |  |  |  |  | } | 
| 557 | 0 |  |  |  |  | 0 | return; | 
| 558 |  |  |  |  |  |  | } | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  | } | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | sub fix_old_goalie ($$) { | 
| 564 |  |  |  |  |  |  |  | 
| 565 | 1616 |  |  | 1616 | 1 | 1734 | my $self = shift; | 
| 566 | 1616 |  |  |  |  | 1668 | my $event = shift; | 
| 567 |  |  |  |  |  |  |  | 
| 568 | 1616 | 50 | 66 |  |  | 2919 | if ($event->{type} eq 'GOAL' | 
|  |  |  | 66 |  |  |  |  | 
| 569 |  |  |  |  |  |  | && ! $event->{en} | 
| 570 |  |  |  |  |  |  | && ! $event->{player2} | 
| 571 |  |  |  |  |  |  | ) { | 
| 572 |  |  |  |  |  |  | my $team = $self->{teams}[ | 
| 573 | 0 | 0 |  |  |  | 0 | $event->{team1} eq $self->{teams}[0]{name} ? 1 : 0, | 
| 574 |  |  |  |  |  |  | ]; | 
| 575 |  |  |  |  |  |  | my @goalies = sort { | 
| 576 |  |  |  |  |  |  | get_seconds($a->{timeOnIce}) <=> get_seconds($b->{timeOnIce}) | 
| 577 | 0 |  |  |  |  | 0 | } grep { | 
| 578 | 0 |  |  |  |  | 0 | $_->{position} eq 'G' | 
| 579 | 0 |  |  |  |  | 0 | } @{$team->{roster}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 580 | 0 |  |  |  |  | 0 | my $goalie = $goalies[0]; | 
| 581 | 0 |  |  |  |  | 0 | $event->{player2} = $goalie->{_id}; | 
| 582 | 0 |  |  |  |  | 0 | $event->{team2}   = $team->{name}; | 
| 583 |  |  |  |  |  |  | } | 
| 584 |  |  |  |  |  |  | } | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | sub set_events ($$) { | 
| 587 |  |  |  |  |  |  |  | 
| 588 | 6 |  |  | 6 | 1 | 22 | my $self   = shift; | 
| 589 | 6 |  |  |  |  | 11 | my $events = shift; | 
| 590 |  |  |  |  |  |  |  | 
| 591 | 6 |  |  |  |  | 16 | $self->{events} = []; | 
| 592 |  |  |  |  |  |  |  | 
| 593 | 6 |  |  |  |  | 14 | for my $ld_event (@{$events}) { | 
|  | 6 |  |  |  |  | 17 |  | 
| 594 |  |  |  |  |  |  | next if $BROKEN_EVENTS{BS}->{$self->{_id}}->{$ld_event->{about}{eventIdx}} && | 
| 595 | 1651 | 50 | 33 |  |  | 5199 | $BROKEN_EVENTS{BS}->{$self->{_id}}->{$ld_event->{about}{eventIdx}} eq '-1'; | 
| 596 | 1651 | 100 |  |  |  | 2188 | next if grep { $_ eq $ld_event->{result}{eventTypeId} } @JS_IGNORED_EVENT_TYPES; | 
|  | 13208 |  |  |  |  | 18288 |  | 
| 597 |  |  |  |  |  |  | my $event = { | 
| 598 |  |  |  |  |  |  | season      => $self->{season}, | 
| 599 |  |  |  |  |  |  | stage       => $self->{stage}, | 
| 600 |  |  |  |  |  |  | game_id     => $self->{_id}, | 
| 601 |  |  |  |  |  |  | period      => $ld_event->{about}{period}, | 
| 602 |  |  |  |  |  |  | time        => $ld_event->{about}{periodTime}, | 
| 603 |  |  |  |  |  |  | coordinates => $ld_event->{coordinates}, | 
| 604 |  |  |  |  |  |  | type        => vocabulary_lookup('events', $ld_event->{result}{eventTypeId}), | 
| 605 |  |  |  |  |  |  | description => uc $ld_event->{result}{description}, | 
| 606 |  |  |  |  |  |  | strength    => vocabulary_lookup('strength', $ld_event->{result}{strength}{code} || ''), | 
| 607 |  |  |  |  |  |  | event_idx   => $ld_event->{about}{eventIdx}, | 
| 608 |  |  |  |  |  |  | bsjs_id     => $ld_event->{about}{eventId}, | 
| 609 |  |  |  |  |  |  | event_code  => uc ($ld_event->{about}{eventCode} || ''), | 
| 610 |  |  |  |  |  |  | so          => $ld_event->{about}{periodType} eq 'SHOOTOUT' ? 1 : 0, | 
| 611 |  |  |  |  |  |  | $BROKEN_EVENTS{BS}->{$self->{_id}}->{$ld_event->{about}{eventIdx}} ? | 
| 612 | 0 |  |  |  |  | 0 | %{$BROKEN_EVENTS{BS}->{$self->{_id}}->{$ld_event->{about}{eventIdx}}} : (), | 
| 613 |  |  |  |  |  |  | $ld_event->{team} ? ( | 
| 614 |  |  |  |  |  |  | team1       => resolve_team($ld_event->{team}{triCode} || $ld_event->{team}{name}), | 
| 615 |  |  |  |  |  |  | teamid      => $ld_event->{team}{id}, | 
| 616 | 1616 | 50 | 100 |  |  | 4289 | ) : (), | 
|  |  | 50 | 50 |  |  |  |  | 
|  |  | 100 | 33 |  |  |  |  | 
| 617 |  |  |  |  |  |  | }; | 
| 618 | 1616 | 100 |  |  |  | 7680 | $event->{time} = substr($event->{time}, 1) if $event->{time} =~ /^0/; | 
| 619 |  |  |  |  |  |  | $event->{special} = 1 | 
| 620 | 1616 | 50 | 33 |  |  | 3310 | if $SPECIAL_EVENTS{$self->{_id}} && $SPECIAL_EVENTS{$self->{_id}}->{$ld_event->{about}{eventIdx}}; | 
| 621 | 1616 |  |  |  |  | 3036 | assign_specific_event_data($event, $ld_event); | 
| 622 | 1616 |  |  |  |  | 3223 | $self->fix_old_goalie($event); | 
| 623 | 1616 |  |  |  |  | 2864 | check_broken($event, $ld_event); | 
| 624 | 1616 |  |  |  |  | 2748 | fix_servedby($event); | 
| 625 | 1616 |  |  |  |  | 1610 | push(@{$self->{events}}, $event); | 
|  | 1616 |  |  |  |  | 3539 |  | 
| 626 |  |  |  |  |  |  | } | 
| 627 |  |  |  |  |  |  | } | 
| 628 |  |  |  |  |  |  |  | 
| 629 |  |  |  |  |  |  | sub build_resolve_cache ($) { | 
| 630 |  |  |  |  |  |  |  | 
| 631 | 8 |  |  | 8 | 1 | 14899 | my $self  = shift; | 
| 632 |  |  |  |  |  |  |  | 
| 633 | 8 |  |  |  |  | 30 | $self->{resolve_cache} = {}; | 
| 634 | 8 |  |  |  |  | 25 | for my $t (0,1) { | 
| 635 | 16 |  |  |  |  | 26 | my $unknown_number = 100; | 
| 636 | 16 |  |  | 331 |  | 62 | $self->{teams}[$t]{roster} = [ my_uniq { $_->{_id} } @{$self->{teams}[$t]{roster}} ]; | 
|  | 331 |  |  |  |  | 853 |  | 
|  | 16 |  |  |  |  | 86 |  | 
| 637 | 16 |  |  |  |  | 65 | for my $player (@{$self->{teams}[$t]{roster}}) { | 
|  | 16 |  |  |  |  | 52 |  | 
| 638 | 331 | 100 |  |  |  | 479 | if (! $player->{broken}) { | 
| 639 | 296 | 100 |  |  |  | 423 | $player->{penaltyMinutes} = delete $player->{pim} if defined $player->{pim}; | 
| 640 | 296 |  | 33 |  |  | 417 | $player->{number} ||= $unknown_number++; | 
| 641 | 296 |  |  |  |  | 540 | while ($self->{resolve_cache}{$self->{teams}[$t]{name}}->{$player->{number}}) { | 
| 642 | 0 |  |  |  |  | 0 | $player->{number} += 100; | 
| 643 |  |  |  |  |  |  | } | 
| 644 | 296 |  |  |  |  | 619 | $self->{resolve_cache}{$self->{teams}[$t]{name}}->{$player->{number}} = \$player; | 
| 645 |  |  |  |  |  |  | } | 
| 646 |  |  |  |  |  |  | else { | 
| 647 |  |  |  |  |  |  | $self->{resolve_cache}{$self->{teams}[$t]{name}}->{names} | 
| 648 | 35 |  | 100 |  |  | 154 | ||= []; | 
| 649 | 35 |  |  |  |  | 42 | push(@{$self->{resolve_cache}{$self->{teams}[$t]{name}}->{names}}, \$player); | 
|  | 35 |  |  |  |  | 75 |  | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  | } | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | sub process ($) { | 
| 656 |  |  |  |  |  |  |  | 
| 657 | 5 |  |  | 5 | 1 | 643 | my $self = shift; | 
| 658 |  |  |  |  |  |  |  | 
| 659 | 5 |  |  |  |  | 36 | $self->set_id_data($self->{json}{gamePk}); | 
| 660 | 5 |  |  |  |  | 22 | $self->set_timestamps($self->{json}); | 
| 661 | 5 |  |  |  |  | 27 | $self->set_extra_header_data($self->{json}); | 
| 662 | 5 |  |  |  |  | 66 | $self->set_periods($self->{json}); | 
| 663 | 5 |  |  |  |  | 27 | $self->set_officials($self->{json}{liveData}{boxscore}{officials}); | 
| 664 | 5 |  |  |  |  | 45 | $self->set_teams($self->{json}); | 
| 665 | 5 | 50 |  |  |  | 8 | if (@{$self->{json}{liveData}{plays}{allPlays}}) { | 
|  | 5 |  |  |  |  | 23 |  | 
| 666 | 5 |  |  |  |  | 30 | $self->set_events($self->{json}{liveData}{plays}{allPlays}); | 
| 667 |  |  |  |  |  |  | } | 
| 668 |  |  |  |  |  |  | else { | 
| 669 | 0 |  |  |  |  | 0 | $BROKEN_FILES{$self->{_id}}->{BS} = $NO_EVENTS; | 
| 670 | 0 |  |  |  |  | 0 | $self->{events} = []; | 
| 671 |  |  |  |  |  |  | } | 
| 672 | 5 |  |  |  |  | 22 | $self->{type} = 'BS'; | 
| 673 | 5 |  |  |  |  | 4798 | delete $self->{json}; | 
| 674 | 5 |  |  |  |  | 80 | 1; | 
| 675 |  |  |  |  |  |  | } | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | 1; | 
| 678 |  |  |  |  |  |  |  | 
| 679 |  |  |  |  |  |  | =head1 AUTHOR | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | More Hockey Stats, C<<  >> | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | =head1 BUGS | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | Please report any bugs or feature requests to C, or through | 
| 686 |  |  |  |  |  |  | the web interface at L.  I will be notified, and then you'll | 
| 687 |  |  |  |  |  |  | automatically be notified of progress on your bug as I make changes. | 
| 688 |  |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | =head1 SUPPORT | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | You can find documentation for this module with the perldoc command. | 
| 693 |  |  |  |  |  |  |  | 
| 694 |  |  |  |  |  |  | perldoc Sport::Analytics::NHL::Report::BS | 
| 695 |  |  |  |  |  |  |  | 
| 696 |  |  |  |  |  |  | You can also look for information at: | 
| 697 |  |  |  |  |  |  |  | 
| 698 |  |  |  |  |  |  | =over 4 | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  | =item * RT: CPAN's request tracker (report bugs here) | 
| 701 |  |  |  |  |  |  |  | 
| 702 |  |  |  |  |  |  | L | 
| 703 |  |  |  |  |  |  |  | 
| 704 |  |  |  |  |  |  | =item * AnnoCPAN: Annotated CPAN documentation | 
| 705 |  |  |  |  |  |  |  | 
| 706 |  |  |  |  |  |  | L | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | =item * CPAN Ratings | 
| 709 |  |  |  |  |  |  |  | 
| 710 |  |  |  |  |  |  | L | 
| 711 |  |  |  |  |  |  |  | 
| 712 |  |  |  |  |  |  | =item * Search CPAN | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | L | 
| 715 |  |  |  |  |  |  |  | 
| 716 |  |  |  |  |  |  | =back |