|
|
|
@ -9,38 +9,6 @@ use crate::model::{
|
|
|
|
|
};
|
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
|
|
|
|
|
|
#[derive(FromRow, Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct IihfPoints {
|
|
|
|
|
pub team_id: i32,
|
|
|
|
|
pub team_name: String,
|
|
|
|
|
pub reg_wins: i64,
|
|
|
|
|
pub reg_losses: i64,
|
|
|
|
|
pub ot_wins: i64,
|
|
|
|
|
pub ot_losses: i64,
|
|
|
|
|
pub ties: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Division {
|
|
|
|
|
pub async fn team_iihf_points(&self, pool: &PgPool) -> Result<Vec<IihfPoints>, sqlx::Error> {
|
|
|
|
|
let games = Game::by_division(pool, self.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut scores = Vec::new();
|
|
|
|
|
for game in games {
|
|
|
|
|
let score = get_score_from_game(pool, &game)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let periods_len = Period::by_game(pool, game.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap()
|
|
|
|
|
.len();
|
|
|
|
|
scores.push((periods_len, score));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(FromRow, Deserialize, Serialize, Debug)]
|
|
|
|
|
pub struct TeamStats {
|
|
|
|
|
pub name: String,
|
|
|
|
@ -48,6 +16,13 @@ pub struct TeamStats {
|
|
|
|
|
pub shots: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(FromRow, Deserialize, Serialize, Debug)]
|
|
|
|
|
pub struct IihfGameStats {
|
|
|
|
|
pub team_name: String,
|
|
|
|
|
pub team_id: i32,
|
|
|
|
|
pub points: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(FromRow, Deserialize, Serialize, Debug)]
|
|
|
|
|
pub struct Notification {
|
|
|
|
|
pub scorer_name: String,
|
|
|
|
@ -66,199 +41,270 @@ pub struct PlayerStats {
|
|
|
|
|
pub points: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_box_score_from_game(pool: &PgPool, game: &Game) -> Result<Vec<PlayerStats>, sqlx::Error> {
|
|
|
|
|
impl Game {
|
|
|
|
|
pub async fn box_score(pool: &PgPool, id: i32) -> Result<Vec<PlayerStats>, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(shots.id) AS points,
|
|
|
|
|
COUNT(CASE WHEN shots.shooter = game_players.id THEN shots.id END) AS goals,
|
|
|
|
|
COUNT(CASE WHEN shots.assistant = game_players.id OR shots.assistant_second = game_players.id THEN shots.id END) AS assists,
|
|
|
|
|
players.name
|
|
|
|
|
FROM game_players
|
|
|
|
|
JOIN players ON game_players.player = players.id
|
|
|
|
|
LEFT JOIN shots
|
|
|
|
|
ON shots.goal=true
|
|
|
|
|
AND (shots.shooter=game_players.id
|
|
|
|
|
OR shots.assistant=game_players.id
|
|
|
|
|
OR shots.assistant_second=game_players.id)
|
|
|
|
|
WHERE game_players.game=$1
|
|
|
|
|
GROUP BY
|
|
|
|
|
game_players.id,
|
|
|
|
|
players.name
|
|
|
|
|
HAVING COUNT(shots.id) > 0
|
|
|
|
|
ORDER BY
|
|
|
|
|
points DESC,
|
|
|
|
|
goals DESC;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.bind(id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
pub async fn goals(pool: &PgPool, id: i32) -> Result<Vec<GoalDetails>, sqlx::Error> {
|
|
|
|
|
sqlx::query_as::<_, GoalDetails>(
|
|
|
|
|
r#"
|
|
|
|
|
SELECT
|
|
|
|
|
shots.shooter AS player_id,
|
|
|
|
|
shots.assistant AS first_assist_id,
|
|
|
|
|
shots.assistant_second AS second_assist_id,
|
|
|
|
|
players.name AS player_name,
|
|
|
|
|
p_assist.name AS first_assist_name,
|
|
|
|
|
p_assist_second.name AS second_assist_name,
|
|
|
|
|
game_players.player_number AS player_number,
|
|
|
|
|
gp_assist.player_number AS first_assist_number,
|
|
|
|
|
gp_assist_second.player_number AS second_assist_number,
|
|
|
|
|
teams.name AS team_name,
|
|
|
|
|
teams.id AS team_id,
|
|
|
|
|
shots.period_time AS time_remaining,
|
|
|
|
|
period_types.id AS period_id,
|
|
|
|
|
period_types.short_name AS period_short_name
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN players ON players.id=game_players.player
|
|
|
|
|
LEFT JOIN game_players gp_assist ON gp_assist.id=shots.assistant
|
|
|
|
|
LEFT JOIN players p_assist ON p_assist.id=gp_assist.player
|
|
|
|
|
LEFT JOIN game_players gp_assist_second ON gp_assist.id=shots.assistant_second
|
|
|
|
|
LEFT JOIN players p_assist_second ON p_assist.id=gp_assist_second.player
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
JOIN period_types ON period_types.id=periods.period_type
|
|
|
|
|
JOIN games ON games.id=periods.game
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND games.id=$1
|
|
|
|
|
ORDER BY
|
|
|
|
|
periods.period_type ASC,
|
|
|
|
|
shots.period_time DESC;
|
|
|
|
|
"#)
|
|
|
|
|
.bind(id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
/// Returns the number of points using IIHF scoring rules for each team.
|
|
|
|
|
pub async fn iihf_score(pool: &PgPool, game_id: i32) -> Result<Vec<IihfGameStats>, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(CASE WHEN shots.goal = true THEN shots.id END) AS points,
|
|
|
|
|
teams.id AS team_id,
|
|
|
|
|
teams.name AS team_name
|
|
|
|
|
FROM games
|
|
|
|
|
JOIN periods ON periods.game=games.id
|
|
|
|
|
JOIN shots ON shots.period=periods.id
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
WHERE games.id=$1
|
|
|
|
|
GROUP BY teams.id;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, IihfGameStats>(query)
|
|
|
|
|
.bind(game_id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
/// Returns the number of shots and goals for each team in the game.
|
|
|
|
|
pub async fn score(pool: &PgPool, game_id: i32) -> Result<Vec<TeamStats>, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(CASE WHEN shots.goal = true THEN shots.id END) AS goals,
|
|
|
|
|
COUNT(shots.id) AS shots,
|
|
|
|
|
teams.name AS name
|
|
|
|
|
FROM games
|
|
|
|
|
JOIN periods ON periods.game=games.id
|
|
|
|
|
JOIN shots ON shots.period=periods.id
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
WHERE games.id=$1
|
|
|
|
|
GROUP BY teams.id;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, TeamStats>(query)
|
|
|
|
|
.bind(game_id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Player {
|
|
|
|
|
pub async fn latest_league(pool: &PgPool, id: i32) -> Result<Option<League>, sqlx::Error> {
|
|
|
|
|
let query =
|
|
|
|
|
r#"
|
|
|
|
|
SELECT leagues.*
|
|
|
|
|
FROM players
|
|
|
|
|
JOIN game_players ON game_players.player=players.id
|
|
|
|
|
JOIN games ON games.id=game_players.game
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
JOIN divisions ON divisions.id=teams.division
|
|
|
|
|
JOIN leagues ON leagues.id=divisions.league
|
|
|
|
|
WHERE players.id=$1
|
|
|
|
|
ORDER BY games.end_at DESC
|
|
|
|
|
LIMIT 1;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, League>(query)
|
|
|
|
|
.bind(id)
|
|
|
|
|
.fetch_optional(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
pub async fn latest_stats(pool: &PgPool, id: i32) -> Result<Vec<GoalDetails>, sqlx::Error> {
|
|
|
|
|
let query =
|
|
|
|
|
r#"
|
|
|
|
|
SELECT
|
|
|
|
|
players.id AS player_id,
|
|
|
|
|
p_assist.id AS first_assist_id,
|
|
|
|
|
p_assist_second.id AS second_assist_id,
|
|
|
|
|
players.name AS player_name,
|
|
|
|
|
p_assist.name AS first_assist_name,
|
|
|
|
|
p_assist_second.name AS second_assist_name,
|
|
|
|
|
game_players.player_number AS player_number,
|
|
|
|
|
gp_assist.player_number AS first_assist_number,
|
|
|
|
|
gp_assist_second.player_number AS second_assist_number,
|
|
|
|
|
teams.name AS team_name,
|
|
|
|
|
teams.id AS team_id,
|
|
|
|
|
shots.period_time AS time_remaining,
|
|
|
|
|
period_types.id AS period_id,
|
|
|
|
|
period_types.short_name AS period_short_name
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN players ON players.id=game_players.player
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
LEFT JOIN game_players gp_assist ON gp_assist.id=shots.assistant
|
|
|
|
|
LEFT JOIN players p_assist ON p_assist.id=gp_assist.player
|
|
|
|
|
LEFT JOIN game_players gp_assist_second ON gp_assist_second.id=shots.assistant_second
|
|
|
|
|
LEFT JOIN players p_assist_second ON p_assist_second.id=gp_assist_second.id
|
|
|
|
|
JOIN periods ON shots.period=periods.id
|
|
|
|
|
JOIN period_types ON period_types.id=periods.period_type
|
|
|
|
|
WHERE players.id=$1
|
|
|
|
|
ORDER BY
|
|
|
|
|
shots.created_at DESC,
|
|
|
|
|
periods.period_type DESC,
|
|
|
|
|
shots.period_time ASC
|
|
|
|
|
LIMIT 5;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, GoalDetails>(&query)
|
|
|
|
|
.bind(id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
pub async fn lifetime_stats(pool: &PgPool, id: i32) -> Result<PlayerStats, sqlx::Error> {
|
|
|
|
|
let query =r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(goals) AS goals,
|
|
|
|
|
COUNT(assists) AS assists,
|
|
|
|
|
COUNT(points) AS points,
|
|
|
|
|
players.name AS name
|
|
|
|
|
FROM players
|
|
|
|
|
JOIN game_players ON game_players.player=players.id
|
|
|
|
|
LEFT JOIN shots points
|
|
|
|
|
ON (points.shooter=game_players.id
|
|
|
|
|
OR points.assistant=game_players.id
|
|
|
|
|
OR points.assistant_second=game_players.id)
|
|
|
|
|
AND points.goal=true
|
|
|
|
|
LEFT JOIN shots goals
|
|
|
|
|
ON goals.shooter=game_players.id
|
|
|
|
|
AND goals.goal=true
|
|
|
|
|
LEFT JOIN shots assists
|
|
|
|
|
ON (points.assistant=game_players.id
|
|
|
|
|
OR points.assistant_second=game_players.id)
|
|
|
|
|
AND points.goal=true
|
|
|
|
|
WHERE players.id=$1
|
|
|
|
|
GROUP BY players.id;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.bind(id)
|
|
|
|
|
.fetch_one(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
async fn get_player_stats_overview(pool: PgPool) -> Result<Vec<PlayerStats>, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND (shots.shooter=game_players.id
|
|
|
|
|
OR shots.assistant=game_players.id
|
|
|
|
|
OR shots.assistant_second=game_players.id)
|
|
|
|
|
) AS points,
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND shots.shooter=game_players.id
|
|
|
|
|
) AS goals,
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND (shots.assistant=game_players.id
|
|
|
|
|
OR shots.assistant_second=game_players.id)
|
|
|
|
|
) AS assists,
|
|
|
|
|
(
|
|
|
|
|
SELECT name
|
|
|
|
|
FROM players
|
|
|
|
|
WHERE id=game_players.player
|
|
|
|
|
) AS name
|
|
|
|
|
FROM game_players
|
|
|
|
|
WHERE game_players.game=$1
|
|
|
|
|
AND (
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND (shots.shooter=game_players.id
|
|
|
|
|
OR shots.assistant=game_players.id
|
|
|
|
|
OR shots.assistant_second=game_players.id)
|
|
|
|
|
) > 0
|
|
|
|
|
COUNT(shots.id) AS points,
|
|
|
|
|
COUNT(CASE WHEN shots.shooter = game_players.id THEN shots.id END) AS goals,
|
|
|
|
|
COUNT(CASE WHEN shots.assistant = game_players.id OR shots.assistant_second = game_players.id THEN shots.id END) AS assists,
|
|
|
|
|
players.name
|
|
|
|
|
FROM game_players
|
|
|
|
|
JOIN players ON game_players.player = players.id
|
|
|
|
|
LEFT JOIN shots
|
|
|
|
|
ON shots.goal=true
|
|
|
|
|
AND (shots.shooter=game_players.id
|
|
|
|
|
OR shots.assistant=game_players.id
|
|
|
|
|
OR shots.assistant_second=game_players.id)
|
|
|
|
|
GROUP BY
|
|
|
|
|
game_players.id,
|
|
|
|
|
players.name
|
|
|
|
|
ORDER BY
|
|
|
|
|
points DESC,
|
|
|
|
|
goals DESC;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.bind(game.id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_latest_league_for_player(pool: &PgPool, player: &Player) -> Result<Option<League>, sqlx::Error> {
|
|
|
|
|
let query =
|
|
|
|
|
r#"
|
|
|
|
|
SELECT leagues.*
|
|
|
|
|
FROM players
|
|
|
|
|
JOIN game_players ON game_players.player=players.id
|
|
|
|
|
JOIN games ON games.id=game_players.game
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
JOIN divisions ON divisions.id=teams.division
|
|
|
|
|
JOIN leagues ON leagues.id=divisions.league
|
|
|
|
|
WHERE players.id=$1
|
|
|
|
|
ORDER BY games.end_at DESC
|
|
|
|
|
LIMIT 1;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, League>(query)
|
|
|
|
|
.bind(player.id)
|
|
|
|
|
.fetch_optional(pool)
|
|
|
|
|
.fetch_all(&pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_league_player_stats(pool: &PgPool, player: &Player, league: &League) -> Result<PlayerStats, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
JOIN games ON games.id=periods.game
|
|
|
|
|
JOIN divisions ON divisions.id=games.division
|
|
|
|
|
JOIN leagues ON leagues.id=divisions.league
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND shots.shooter=players.id
|
|
|
|
|
AND leagues.id=$2
|
|
|
|
|
) AS goals,
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
JOIN games ON games.id=periods.game
|
|
|
|
|
JOIN divisions ON divisions.id=games.division
|
|
|
|
|
JOIN leagues ON leagues.id=divisions.league
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND leagues.id=$2
|
|
|
|
|
AND (shots.assistant=players.id
|
|
|
|
|
OR shots.assistant_second=players.id)
|
|
|
|
|
) AS assists,
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(shots.id)
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
JOIN games ON games.id=periods.game
|
|
|
|
|
JOIN divisions ON divisions.id=games.division
|
|
|
|
|
JOIN leagues ON leagues.id=divisions.league
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND leagues.id=$2
|
|
|
|
|
AND (shots.shooter=players.id
|
|
|
|
|
OR shots.assistant=players.id
|
|
|
|
|
OR shots.assistant_second=players.id)
|
|
|
|
|
) AS points,
|
|
|
|
|
players.name AS name
|
|
|
|
|
FROM players
|
|
|
|
|
WHERE id=$1;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.bind(player.id)
|
|
|
|
|
.bind(league.id)
|
|
|
|
|
.fetch_one(pool)
|
|
|
|
|
.await
|
|
|
|
|
impl League {
|
|
|
|
|
pub async fn player_stats(pool: &PgPool, player_id: i32, league_id: i32) -> Result<PlayerStats, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(goals.id) AS goals,
|
|
|
|
|
COUNT(assists.id) AS assists,
|
|
|
|
|
COUNT(points.id) AS points,
|
|
|
|
|
players.name AS name
|
|
|
|
|
FROM players
|
|
|
|
|
JOIN game_players ON game_players.player=players.id
|
|
|
|
|
LEFT JOIN shots goals
|
|
|
|
|
ON goals.goal=true
|
|
|
|
|
AND goals.shooter=game_players.id
|
|
|
|
|
LEFT JOIN shots assists
|
|
|
|
|
ON assists.goal=true
|
|
|
|
|
AND (assists.assistant=game_players.id
|
|
|
|
|
OR assists.assistant_second=game_players.id)
|
|
|
|
|
LEFT JOIN shots points
|
|
|
|
|
ON points.goal=true
|
|
|
|
|
AND (points.shooter=game_players.id
|
|
|
|
|
OR points.assistant=game_players.id
|
|
|
|
|
OR points.assistant_second=game_players.id)
|
|
|
|
|
JOIN games ON games.id=game_players.game
|
|
|
|
|
JOIN divisions ON divisions.id=games.division
|
|
|
|
|
JOIN leagues ON leagues.id=divisions.league
|
|
|
|
|
WHERE leagues.id=$1
|
|
|
|
|
AND players.id=$2
|
|
|
|
|
GROUP BY players.id;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.bind(league_id)
|
|
|
|
|
.bind(player_id)
|
|
|
|
|
.fetch_one(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_latest_stats(pool: &PgPool, player: &Player) -> Result<Vec<GoalDetails>, sqlx::Error> {
|
|
|
|
|
let query =
|
|
|
|
|
r#"
|
|
|
|
|
SELECT
|
|
|
|
|
players.id AS player_id,
|
|
|
|
|
p_assist.id AS first_assist_id,
|
|
|
|
|
p_assist_second.id AS second_assist_id,
|
|
|
|
|
players.name AS player_name,
|
|
|
|
|
p_assist.name AS first_assist_name,
|
|
|
|
|
p_assist_second.name AS second_assist_name,
|
|
|
|
|
game_players.player_number AS player_number,
|
|
|
|
|
gp_assist.player_number AS first_assist_number,
|
|
|
|
|
gp_assist_second.player_number AS second_assist_number,
|
|
|
|
|
teams.name AS team_name,
|
|
|
|
|
teams.id AS team_id,
|
|
|
|
|
shots.period_time AS time_remaining,
|
|
|
|
|
period_types.id AS period_id,
|
|
|
|
|
period_types.short_name AS period_short_name
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN players ON players.id=game_players.player
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
LEFT JOIN game_players gp_assist ON gp_assist.id=shots.assistant
|
|
|
|
|
LEFT JOIN players p_assist ON p_assist.id=gp_assist.player
|
|
|
|
|
LEFT JOIN game_players gp_assist_second ON gp_assist_second.id=shots.assistant_second
|
|
|
|
|
LEFT JOIN players p_assist_second ON p_assist_second.id=gp_assist_second.id
|
|
|
|
|
JOIN periods ON shots.period=periods.id
|
|
|
|
|
JOIN period_types ON period_types.id=periods.period_type
|
|
|
|
|
WHERE players.id=$1
|
|
|
|
|
ORDER BY
|
|
|
|
|
shots.created_at DESC,
|
|
|
|
|
periods.period_type DESC,
|
|
|
|
|
shots.period_time ASC
|
|
|
|
|
LIMIT 5;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, GoalDetails>(&query)
|
|
|
|
|
.bind(player.id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_all_player_stats(pool: &PgPool, player: &Player) -> Result<PlayerStats, sqlx::Error> {
|
|
|
|
|
let query =r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(goals) AS goals,
|
|
|
|
|
COUNT(assists) AS assists,
|
|
|
|
|
COUNT(points) AS points,
|
|
|
|
|
players.name AS name
|
|
|
|
|
FROM players
|
|
|
|
|
JOIN game_players ON game_players.player=players.id
|
|
|
|
|
LEFT JOIN shots points
|
|
|
|
|
ON (points.shooter=game_players.id
|
|
|
|
|
OR points.assistant=game_players.id
|
|
|
|
|
OR points.assistant_second=game_players.id)
|
|
|
|
|
AND points.goal=true
|
|
|
|
|
LEFT JOIN shots goals
|
|
|
|
|
ON goals.shooter=game_players.id
|
|
|
|
|
AND goals.goal=true
|
|
|
|
|
LEFT JOIN shots assists
|
|
|
|
|
ON (points.assistant=game_players.id
|
|
|
|
|
OR points.assistant_second=game_players.id)
|
|
|
|
|
AND points.goal=true
|
|
|
|
|
WHERE players.id=$1
|
|
|
|
|
GROUP BY players.id;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.bind(player.id)
|
|
|
|
|
.fetch_one(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(FromRow, Deserialize, Serialize, Debug)]
|
|
|
|
|
pub struct GoalDetails {
|
|
|
|
@ -296,45 +342,6 @@ pub struct ShotDetails {
|
|
|
|
|
pub second_assist_number: Option<i32>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_goals_from_game(pool: &PgPool, game: &Game) -> Result<Vec<GoalDetails>, sqlx::Error> {
|
|
|
|
|
sqlx::query_as::<_, GoalDetails>(
|
|
|
|
|
r#"
|
|
|
|
|
SELECT
|
|
|
|
|
shots.shooter AS player_id,
|
|
|
|
|
shots.assistant AS first_assist_id,
|
|
|
|
|
shots.assistant_second AS second_assist_id,
|
|
|
|
|
players.name AS player_name,
|
|
|
|
|
p_assist.name AS first_assist_name,
|
|
|
|
|
p_assist_second.name AS second_assist_name,
|
|
|
|
|
game_players.player_number AS player_number,
|
|
|
|
|
gp_assist.player_number AS first_assist_number,
|
|
|
|
|
gp_assist_second.player_number AS second_assist_number,
|
|
|
|
|
teams.name AS team_name,
|
|
|
|
|
teams.id AS team_id,
|
|
|
|
|
shots.period_time AS time_remaining,
|
|
|
|
|
period_types.id AS period_id,
|
|
|
|
|
period_types.short_name AS period_short_name
|
|
|
|
|
FROM shots
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN players ON players.id=game_players.player
|
|
|
|
|
LEFT JOIN game_players gp_assist ON gp_assist.id=shots.assistant
|
|
|
|
|
LEFT JOIN players p_assist ON p_assist.id=gp_assist.player
|
|
|
|
|
LEFT JOIN game_players gp_assist_second ON gp_assist.id=shots.assistant_second
|
|
|
|
|
LEFT JOIN players p_assist_second ON p_assist.id=gp_assist_second.player
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
JOIN periods ON periods.id=shots.period
|
|
|
|
|
JOIN period_types ON period_types.id=periods.period_type
|
|
|
|
|
JOIN games ON games.id=periods.game
|
|
|
|
|
WHERE shots.goal=true
|
|
|
|
|
AND games.id=$1
|
|
|
|
|
ORDER BY
|
|
|
|
|
periods.period_type ASC,
|
|
|
|
|
shots.period_time DESC;
|
|
|
|
|
"#)
|
|
|
|
|
.bind(game.id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_play_by_play_from_game(pool: &PgPool, game: &Game) -> Result<Vec<ShotDetails>, sqlx::Error> {
|
|
|
|
|
sqlx::query_as::<_, ShotDetails>(
|
|
|
|
@ -375,58 +382,7 @@ ORDER BY
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_score_from_game(pool: &PgPool, game: &Game) -> Result<Vec<TeamStats>, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
COUNT(CASE WHEN shots.goal = true THEN shots.id END) AS goals,
|
|
|
|
|
COUNT(shots.id) AS shots,
|
|
|
|
|
teams.name AS name
|
|
|
|
|
FROM games
|
|
|
|
|
JOIN periods ON periods.game=games.id
|
|
|
|
|
JOIN shots ON shots.period=periods.id
|
|
|
|
|
JOIN game_players ON game_players.id=shots.shooter
|
|
|
|
|
JOIN teams ON teams.id=game_players.team
|
|
|
|
|
WHERE games.id=$1
|
|
|
|
|
GROUP BY teams.id;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, TeamStats>(query)
|
|
|
|
|
.bind(game.id)
|
|
|
|
|
.fetch_all(pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_player_stats_overview(pool: PgPool) -> Result<Vec<PlayerStats>, sqlx::Error> {
|
|
|
|
|
let query = r#"
|
|
|
|
|
SELECT
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(id)
|
|
|
|
|
FROM shots
|
|
|
|
|
WHERE shooter=players.id
|
|
|
|
|
AND goal=true
|
|
|
|
|
) AS goals,
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(id)
|
|
|
|
|
FROM shots
|
|
|
|
|
WHERE (assistant=players.id OR assistant_second=players.id)
|
|
|
|
|
AND goal=true
|
|
|
|
|
) AS assists,
|
|
|
|
|
(
|
|
|
|
|
SELECT COUNT(id)
|
|
|
|
|
FROM shots
|
|
|
|
|
WHERE assistant=players.id
|
|
|
|
|
OR shooter=players.id
|
|
|
|
|
) AS points,
|
|
|
|
|
players.name AS name
|
|
|
|
|
FROM players
|
|
|
|
|
ORDER BY
|
|
|
|
|
points DESC,
|
|
|
|
|
goals DESC,
|
|
|
|
|
players.name;
|
|
|
|
|
"#;
|
|
|
|
|
sqlx::query_as::<_, PlayerStats>(query)
|
|
|
|
|
.fetch_all(&pool)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
@ -440,13 +396,6 @@ mod tests {
|
|
|
|
|
use crate::views::{
|
|
|
|
|
Notification,
|
|
|
|
|
get_player_stats_overview,
|
|
|
|
|
get_score_from_game,
|
|
|
|
|
get_goals_from_game,
|
|
|
|
|
get_box_score_from_game,
|
|
|
|
|
get_latest_league_for_player,
|
|
|
|
|
get_league_player_stats,
|
|
|
|
|
get_all_player_stats,
|
|
|
|
|
get_latest_stats,
|
|
|
|
|
get_play_by_play_from_game,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -470,22 +419,9 @@ mod tests {
|
|
|
|
|
let player = Player::get(&pool, 2)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let latest = get_latest_stats(&pool, &player)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn check_all_player_stats() {
|
|
|
|
|
tokio_test::block_on(async move {
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let player = sqlx::query_as::<_, Player>("SELECT * FROM players WHERE id=2;")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let latest = Player::latest_stats(&pool, player.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let stats = get_all_player_stats(&pool, &player).await.unwrap();
|
|
|
|
|
assert_eq!(stats.name, "Hillary Scanlon");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -493,15 +429,13 @@ mod tests {
|
|
|
|
|
fn check_league_player_stats() {
|
|
|
|
|
tokio_test::block_on(async move {
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let league = sqlx::query_as::<_, League>("SELECT * FROM leagues WHERE id=1;")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let league = League::get(&pool, 1)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let player = sqlx::query_as::<_, Player>("SELECT * FROM players WHERE id=2;")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let player = Player::get(&pool, 2)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let stats = get_league_player_stats(&pool, &player, &league).await.unwrap();
|
|
|
|
|
let stats = League::player_stats(&pool, player.id, league.id).await.unwrap();
|
|
|
|
|
assert_eq!(stats.name, "Hillary Scanlon");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
@ -510,11 +444,10 @@ mod tests {
|
|
|
|
|
fn check_latest_league_for_player() {
|
|
|
|
|
tokio_test::block_on(async move {
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let player = sqlx::query_as::<_, Player>("SELECT * FROM players WHERE id=5")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let player = Player::get(&pool, 5)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let league = get_latest_league_for_player(&pool, &player)
|
|
|
|
|
let league = Player::latest_league(&pool, player.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap()
|
|
|
|
|
.unwrap();
|
|
|
|
@ -526,11 +459,10 @@ mod tests {
|
|
|
|
|
fn check_score_details_from_game() {
|
|
|
|
|
tokio_test::block_on(async move {
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let game = sqlx::query_as::<_, Game>("SELECT * FROM games WHERE id=3;")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let game = Game::get(&pool, 3)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let scores = get_goals_from_game(&pool, &game)
|
|
|
|
|
let scores = Game::goals(&pool, game.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
println!("{scores:?}");
|
|
|
|
@ -541,11 +473,10 @@ mod tests {
|
|
|
|
|
fn check_box_score_from_game() {
|
|
|
|
|
tokio_test::block_on(async move{
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let game = sqlx::query_as::<_, Game>("SELECT * FROM games WHERE id=4;")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let game = Game::get(&pool, 4)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let scores = get_box_score_from_game(&pool, &game)
|
|
|
|
|
let scores = Game::box_score(&pool, game.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
println!("{scores:?}");
|
|
|
|
@ -557,16 +488,30 @@ mod tests {
|
|
|
|
|
assert_eq!(scores.len(), 8, "Players which did not receive any points should not be in the box score.");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn check_iihf_score() {
|
|
|
|
|
tokio_test::block_on(async move{
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let game = Game::get(&pool, 1)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let score = Game::iihf_score(&pool, game.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert_eq!(score.get(0).unwrap().points, 3);
|
|
|
|
|
assert_eq!(score.get(0).unwrap().team_name, "Bullseye");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn check_game_score() {
|
|
|
|
|
tokio_test::block_on(async move{
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let game = sqlx::query_as::<_, Game>("SELECT * FROM games WHERE id=1;")
|
|
|
|
|
.fetch_one(&pool)
|
|
|
|
|
let game = Game::get(&pool, 1)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let score = get_score_from_game(&pool, &game)
|
|
|
|
|
let score = Game::score(&pool, game.id)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert_eq!(score.get(0).unwrap().goals, 1);
|
|
|
|
@ -585,6 +530,16 @@ mod tests {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn check_lifetime_stats() {
|
|
|
|
|
tokio_test::block_on(async move {
|
|
|
|
|
let pool = db_connect().await;
|
|
|
|
|
let lifetime_stats = Player::lifetime_stats(&pool, 5)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn check_notification_query() {
|
|
|
|
|
tokio_test::block_on(async move {
|
|
|
|
@ -633,4 +588,5 @@ JOIN positions ON positions.id=game_players.position;
|
|
|
|
|
.await
|
|
|
|
|
.expect("Active database connection must be made")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|