// =========================================================================
// SHORTCODE: Goal Value Report - /goal-value/
// =========================================================================
add_shortcode('otg_goal_value', 'otg_render_goal_value');
function otg_render_goal_value($atts) {
global $wpdb;
// 1. Get Parameters & Dropdown Options
$available_seasons = $wpdb->get_col("SELECT DISTINCT season FROM otg_rangers_games ORDER BY season DESC");
$most_recent_season = $wpdb->get_var("SELECT season FROM otg_rangers_games ORDER BY game_date DESC LIMIT 1");
$default_season = !empty($most_recent_season) ? $most_recent_season : (!empty($available_seasons) ? reset($available_seasons) : '');
$game_type = isset($_GET['game_type']) ? sanitize_text_field($_GET['game_type']) : '2';
$season_start = isset($_GET['season_start']) ? sanitize_text_field($_GET['season_start']) : $default_season;
$season_end = isset($_GET['season_end']) ? sanitize_text_field($_GET['season_end']) : $default_season;
$opponent_abbr = isset($_GET['opponent_abbr']) ? sanitize_text_field($_GET['opponent_abbr']) : 'ALL';
// Sorting parameters
$gv_sort = isset($_GET['gv_sort']) ? sanitize_text_field($_GET['gv_sort']) : 'total_value';
$gv_dir = isset($_GET['gv_dir']) ? sanitize_text_field($_GET['gv_dir']) : 'desc';
// Smart Year Validation
if (!empty($season_start) && !empty($season_end) && $season_start > $season_end) {
$season_start = $season_end;
}
$season_where = "";
if (!empty($season_start) && !empty($season_end)) {
$season_where = $wpdb->prepare("AND g.season BETWEEN %s AND %s", $season_start, $season_end);
}
$game_filter = "";
if ($opponent_abbr !== 'ALL') {
$game_filter = $wpdb->prepare("AND g.opponent_abbr = %s", $opponent_abbr);
}
// Fetch Teams for filters
$season_teams_query = $wpdb->prepare("
SELECT DISTINCT st.abbreviation, st.team
FROM otg_standings_nhl st
JOIN otg_rangers_games g ON st.abbreviation = g.opponent_abbr AND st.season = g.season
WHERE g.season = %s
ORDER BY st.team ASC
", $season_start);
$season_teams = $wpdb->get_results($season_teams_query);
$all_teams = $wpdb->get_results("SELECT DISTINCT abbreviation, team FROM otg_standings_nhl ORDER BY team ASC");
$season_team_abbrs = [];
foreach ($season_teams as $st) {
$season_team_abbrs[] = $st->abbreviation;
}
// 2. Fetch all scoring plays for Rangers games matching filters to calculate total game goals & situational weights
$scoring_query = $wpdb->prepare("
SELECT s.game_id, s.period, s.time_elapsed, s.team_abbr, s.scorer_name, s.scorer_id,
s.strength, s.home_score, s.away_score, s.goal_modifier, g.home_team_abbr, g.season, p.id as player_internal_id
FROM otg_rangers_scoring s
JOIN otg_rangers_games g ON s.game_id = g.game_id
LEFT JOIN otg_players p ON s.scorer_id = p.nhl_id
WHERE s.time_elapsed != '00:00'
AND (s.goal_modifier IS NULL OR s.goal_modifier != 'shootout')
AND g.game_type = %s
$season_where
$game_filter
ORDER BY s.game_id ASC, s.period ASC,
CAST(SUBSTRING_INDEX(s.time_elapsed, ':', 1) AS UNSIGNED) ASC,
CAST(SUBSTRING_INDEX(s.time_elapsed, ':', -1) AS UNSIGNED) ASC
", $game_type);
$all_scoring_plays = $wpdb->get_results($scoring_query);
// Group plays by game_id to count total goals per game and track running scores/game-winners
$games_plays = [];
foreach ($all_scoring_plays as $play) {
$games_plays[$play->game_id][] = $play;
}
$player_goal_values = [];
foreach ($games_plays as $game_id => $plays) {
$total_game_goals = count($plays);
if ($total_game_goals <= 0) continue;
$base_fraction = 1 / $total_game_goals;
$final_home = (int)end($plays)->home_score;
$final_away = (int)end($plays)->away_score;
$nyr_won = ($final_home > $final_away && $plays[0]->home_team_abbr === 'NYR') || ($final_away > $final_home && $plays[0]->home_team_abbr !== 'NYR');
$loser_score = ($final_home > $final_away) ? $final_away : $final_home;
$gw_target = $loser_score + 1;
$gwg_assigned = false;
foreach ($plays as $index => $play) {
$is_nyr = ($play->team_abbr === 'NYR');
$home_score = (int)$play->home_score;
$away_score = (int)$play->away_score;
$prev_h = ($index > 0) ? (int)$plays[$index - 1]->home_score : 0;
$prev_a = ($index > 0) ? (int)$plays[$index - 1]->away_score : 0;
// Check GWG for this play
$is_gwg = false;
$is_winner_team = ($final_home > $final_away) ? ($play->home_team_abbr === 'NYR') : ($play->home_team_abbr !== 'NYR');
if ($nyr_won && $is_winner_team && !$gwg_assigned) {
$cur_nyr_score = ($play->home_team_abbr === 'NYR') ? $home_score : $away_score;
if ($cur_nyr_score === $gw_target) {
$is_gwg = true;
$gwg_assigned = true;
}
}
if ($is_nyr) {
$goal_val = $base_fraction;
$is_ot = ((int)$play->period >= 4);
if ($is_ot) $goal_val += 1.0;
$is_first = ($index === 0);
if ($is_first) $goal_val += 0.5;
$was_tied_or_trailing = ($play->home_team_abbr === 'NYR') ? ($prev_h <= $prev_a) : ($prev_a <= $prev_h);
$is_now_leading = ($play->home_team_abbr === 'NYR') ? ($home_score > $away_score) : ($away_score > $home_score);
$is_go_ahead = ($was_tied_or_trailing && $is_now_leading);
if ($is_go_ahead) $goal_val += 0.5;
if ($is_gwg) $goal_val += 0.5;
$was_leading_by_1 = ($play->home_team_abbr === 'NYR') ? ($prev_h == $prev_a + 1) : ($prev_a == $prev_h + 1);
$is_now_leading_by_2 = ($play->home_team_abbr === 'NYR') ? ($home_score == $away_score + 2) : ($away_score == $home_score + 2);
$is_insurance = ($was_leading_by_1 && $is_now_leading_by_2);
if ($is_insurance) $goal_val += 0.25;
$is_sh = (isset($play->strength) && strtolower($play->strength) === 'sh');
if ($is_sh) $goal_val += 0.5;
$is_pp = (isset($play->strength) && strtolower($play->strength) === 'pp');
if ($is_pp) $goal_val += 0.25;
$is_en = (isset($play->goal_modifier) && strtolower($play->goal_modifier) === 'empty-net');
if ($is_en) $goal_val += 0.1;
$p_name = !empty($play->scorer_name) ? $play->scorer_name : 'Unknown';
$p_id = !empty($play->player_internal_id) ? $play->player_internal_id : '';
if (!isset($player_goal_values[$p_name])) {
$player_goal_values[$p_name] = [
'player_internal_id' => $p_id,
'name' => $p_name,
'goals' => 0,
'total_value' => 0.0,
'ot_count' => 0,
'first_count' => 0,
'go_ahead_count' => 0,
'gwg_count' => 0,
'insurance_count' => 0,
'shg_count' => 0,
'ppg_count' => 0,
'eng_count' => 0
];
}
if (empty($player_goal_values[$p_name]['player_internal_id']) && !empty($p_id)) {
$player_goal_values[$p_name]['player_internal_id'] = $p_id;
}
$player_goal_values[$p_name]['goals']++;
$player_goal_values[$p_name]['total_value'] += $goal_val;
if ($is_ot) $player_goal_values[$p_name]['ot_count']++;
if ($is_first) $player_goal_values[$p_name]['first_count']++;
if ($is_go_ahead) $player_goal_values[$p_name]['go_ahead_count']++;
if ($is_gwg) $player_goal_values[$p_name]['gwg_count']++;
if ($is_insurance) $player_goal_values[$p_name]['insurance_count']++;
if ($is_sh) $player_goal_values[$p_name]['shg_count']++;
if ($is_pp) $player_goal_values[$p_name]['ppg_count']++;
if ($is_en) $player_goal_values[$p_name]['eng_count']++;
}
}
}
// Sort player goal values table
uasort($player_goal_values, function($a, $b) use ($gv_sort, $gv_dir) {
$val_a = $a[$gv_sort] ?? 0;
$val_b = $b[$gv_sort] ?? 0;
if ($gv_sort === 'name') {
$cmp = strcasecmp($a['name'], $b['name']);
return ($gv_dir === 'desc') ? -$cmp : $cmp;
}
if ($val_a == $val_b) {
return strcasecmp($a['name'], $b['name']);
}
return ($gv_dir === 'desc') ? ($val_b - $val_a) : ($val_a - $val_b);
});
// Helper for table headers sorting
$render_gv_th = function($col_key, $label, $align = 'center') use ($gv_sort, $gv_dir) {
$is_sorted = ($gv_sort === $col_key);
$arrow = '';
if ($is_sorted) {
$arrow = ($gv_dir === 'desc') ? ' ▲ ' : ' ▼ ';
}
$class = $is_sorted ? 'highlight-col' : '';
return '
' . $label . '' . $arrow . ' ';
};
$input_style = 'width: 100%; height: 38px; padding: 0 10px; background: #222; border: 1px solid #444; color: #fff; border-radius: 4px; box-sizing: border-box; font-size: 0.9em;';
$current_url_clean = strtok($_SERVER["URI"] ?? $_SERVER['REQUEST_URI'], '?');
$output = '';
// Header
$output .= '
';
$output .= '
Rangers Advanced Goal Value Report
';
$base_q_params = '&opponent_abbr='.urlencode($opponent_abbr).'&season_start='.urlencode($season_start).'&season_end='.urlencode($season_end).'&gv_sort='.urlencode($gv_sort).'&gv_dir='.urlencode($gv_dir);
$output .= '
';
$output .= '
';
// Filters Form
$output .= '
';
// Explanation Box / Formula Legend
$output .= '
';
$output .= '
Goal Value Scoring System Legend: ';
$output .= '
';
$output .= '
• Base Value: 1 / Total Goals Scored in Game
';
$output .= '
• Overtime Goal (OT): +1.0
';
$output .= '
• First Goal of Game: +0.5
';
$output .= '
• Go-Ahead Goal: +0.5
';
$output .= '
• Game Winner (GWG): +0.5
';
$output .= '
• Insurance Goal (+2 lead): +0.25
';
$output .= '
• Short-Handed Goal (SH): +0.5
';
$output .= '
• Power Play Goal (PP): +0.25
';
$output .= '
• Empty Net Goal (EN): +0.1
';
$output .= '
';
$output .= '
';
// Table Styling
$output .= '';
// Render Table Card
$output .= '
';
$output .= '
Player Goal Value Rankings
';
$output .= '
';
$output .= '
';
$output .= '';
// Name header
$is_name_sorted = ($gv_sort === 'name');
$name_arrow = $is_name_sorted ? (($gv_dir === 'asc') ? ' ▲ ' : ' ▼ ') : '';
$output .= 'Player Name' . $name_arrow . ' ';
$output .= $render_gv_th('goals', 'Goals');
$output .= $render_gv_th('total_value', 'Total Goal Value');
$output .= $render_gv_th('ot_count', 'OT Goals');
$output .= $render_gv_th('first_count', '1st Goals');
$output .= $render_gv_th('go_ahead_count', 'Go-Ahead');
$output .= $render_gv_th('gwg_count', 'GWG');
$output .= $render_gv_th('insurance_count', 'Insurance');
$output .= $render_gv_th('shg_count', 'SHG');
$output .= $render_gv_th('ppg_count', 'PPG');
$output .= $render_gv_th('eng_count', 'ENG');
$output .= ' ';
if (!empty($player_goal_values)) {
$tot_goals = 0;
$tot_value = 0.0;
$tot_ot = 0;
$tot_first = 0;
$tot_go_ahead = 0;
$tot_gwg = 0;
$tot_insurance = 0;
$tot_shg = 0;
$tot_ppg = 0;
$tot_eng = 0;
foreach ($player_goal_values as $p_name => $stats) {
$tot_goals += $stats['goals'];
$tot_value += $stats['total_value'];
$tot_ot += $stats['ot_count'];
$tot_first += $stats['first_count'];
$tot_go_ahead += $stats['go_ahead_count'];
$tot_gwg += $stats['gwg_count'];
$tot_insurance += $stats['insurance_count'];
$tot_shg += $stats['shg_count'];
$tot_ppg += $stats['ppg_count'];
$tot_eng += $stats['eng_count'];
$display_name = esc_html($stats['name']);
if (!empty($stats['player_internal_id'])) {
$p_url = home_url('/profile/?player_id=' . urlencode($stats['player_internal_id']));
$display_name = '' . esc_html($stats['name']) . ' ';
}
$output .= '';
$output .= '' . $display_name . ' ';
$cells = [
'goals' => $stats['goals'],
'total_value' => number_format($stats['total_value'], 3),
'ot_count' => ($stats['ot_count'] > 0 ? $stats['ot_count'] : '-'),
'first_count' => ($stats['first_count'] > 0 ? $stats['first_count'] : '-'),
'go_ahead_count' => ($stats['go_ahead_count'] > 0 ? $stats['go_ahead_count'] : '-'),
'gwg_count' => ($stats['gwg_count'] > 0 ? $stats['gwg_count'] : '-'),
'insurance_count' => ($stats['insurance_count'] > 0 ? $stats['insurance_count'] : '-'),
'shg_count' => ($stats['shg_count'] > 0 ? $stats['shg_count'] : '-'),
'ppg_count' => ($stats['ppg_count'] > 0 ? $stats['ppg_count'] : '-'),
'eng_count' => ($stats['eng_count'] > 0 ? $stats['eng_count'] : '-')
];
foreach ($cells as $ckey => $cval) {
$is_col_sorted = ($gv_sort === $ckey);
$extra_style = ($ckey === 'total_value') ? 'font-weight: bold; color: #3498db;' : '';
$output .= '' . $cval . ' ';
}
$output .= ' ';
}
// Totals row
$output .= '';
$output .= 'Team Totals ';
$output .= '' . $tot_goals . ' ';
$output .= '' . number_format($tot_value, 3) . ' ';
$output .= '' . $tot_ot . ' ';
$output .= '' . $tot_first . ' ';
$output .= '' . $tot_go_ahead . ' ';
$output .= '' . $tot_gwg . ' ';
$output .= '' . $tot_insurance . ' ';
$output .= '' . $tot_shg . ' ';
$output .= '' . $tot_ppg . ' ';
$output .= '' . $tot_eng . ' ';
$output .= ' ';
} else {
$output .= 'No goal records found matching the selected filters. ';
}
$output .= '
';
// JavaScript for Column Sorting
$output .= '';
$output .= '
';
return $output;
}
RANGERS ADD BJORKSTRAND - OutsideTheGarden
Skip to content
RANGERS ADD BJORKSTRAND
With their third transaction of the day, the Rangers signed winger Oliver Bjorkstand to a one year $4.5 million contract, adding a right-handed forward who can play either wing. The 31 year old had 12 goals and 32 points in 80 games with the Tampa Bay Lightning last season, and has 184 goals and 416 points in 704 NHL games.
Drafted by the Blue Jackets in the 3rd round of the 2013 entry draft, the Dane has also played for the Kraken and Lighting, making the Rangers his fourth NHL team. Last season with Tampa was a bit of a down year, particularly on goal scoring where his 12 goals were well below his career average of 21. A middle six forward who averages around 15:30 over his career, he should see time on the second PP unit, though is unlikely to kill penalties.
Standard Stats Table Scor Scor Scor Goal Goal Goal Goal Assi Assi Assi Shot Shot Shot Ice Ice Face Face Face Season Age Team Lg Pos GP G A PTS +/- PIM EVG PPG SHG GWG EV PP SH SOG SPCT TSA TOI ATOI FOW FOL FO% BLK HIT TAKE GIVE 2015-16 20 CBJ NHL RW 12 4 4 8 6 0 4 0 0 1 4 0 0 25 16.0 44 191:43 15:59 0 0 5 9 6 6 2016-17 21 CBJ NHL RW 26 6 7 13 4 6 6 0 0 2 7 0 0 55 10.9 90 366:20 14:05 1 0 100.0 9 14 8 8 2017-18 22 CBJ NHL RW 82 11 29 40 4 9 8 3 0 2 23 6 0 163 6.7 291 1171:58 14:18 1 7 12.5 43 64 26 25 2018-19 23 CBJ NHL RW 77 23 13 36 8 8 20 3 0 1 13 0 0 161 14.3 261 950:03 12:20 5 8 38.5 34 43 31 14 2019-20 24 CBJ NHL RW 49 21 15 36 8 12 18 3 0 5 12 3 0 162 13.0 277 879:08 17:56 6 13 31.6 16 43 32 27 2020-21 25 CBJ NHL RW 56 18 26 44 -15 23 16 2 0 0 22 4 0 143 12.6 237 986:08 17:37 3 4 42.9 30 42 39 26 2021-22 26 CBJ NHL RW 80 28 29 57 -35 16 19 9 0 1 19 10 0 215 13.0 402 1435:59 17:57 10 13 43.5 31 85 57 33 2022-23 27 SEA NHL RW 81 20 25 45 -1 16 15 5 0 1 20 5 0 198 10.1 358 1265:30 15:37 18 41 30.5 38 63 56 20 2023-24 28 SEA NHL RW 82 20 39 59 -20 12 12 8 0 3 22 17 0 186 10.8 428 1327:00 16:11 9 19 32.1 29 67 63 24 2024-25 29 2TM NHL RW 79 21 25 46 2 18 16 5 0 2 18 7 0 154 13.6 303 1196:43 15:09 3 7 30.0 26 74 26 65 2024-25 29 SEA NHL RW 61 16 21 37 -1 14 12 4 0 1 17 4 0 124 12.9 244 943:52 15:28 2 5 28.6 15 61 21 56 2024-25 29 TBL NHL RW 18 5 4 9 3 4 4 1 0 1 1 3 0 30 16.7 59 252:51 14:03 1 2 33.3 11 13 5 9 2025-26 30 TBL NHL RW 80 12 20 32 -14 16 3 9 0 1 15 5 0 130 9.2 266 1090:54 13:38 7 9 43.8 39 85 14 38 NHL NHL NHL NHL 704 184 232 416 -53 136 137 47 0 19 175 57 0 1592 11.6 2957 10861:26 15:26 63 121 34.2 300 589 358 286 NHL NHL NHL NHL 82 21 27 48 -6 16 16 5 0 2 20 7 0 185 11.6 344 1265 15:26 7 14 34.2 35 69 42 33
wpDiscuz
Would love your thoughts, please comment.
x
Insert