team_players -> game_players; help from ChatGPT to simplify some queries using conditional aggragation

master
Tait Hoyem 1 year ago
parent ffdea810ad
commit b2331d7fb0

@ -1,7 +1,5 @@
-- Add up migration script here
CREATE TABLE IF NOT EXISTS leagues (
id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(255) NOT NULL,
start_date TIMESTAMPTZ NOT NULL,
end_date TIMESTAMPTZ
name VARCHAR(255) NOT NULL
);

@ -1,5 +1,5 @@
-- Add up migration script here
INSERT INTO leagues
(id, name, start_date, end_date)
(id, name)
VALUES
(1, '2022 Canadian National Blind Hockey Tournament', '2022-03-24', '2022-03-26');
(1, '2022 Canadian National Blind Hockey Tournament');

@ -1,2 +0,0 @@
-- Add down migration script here
DROP TABLE IF EXISTS team_players;

@ -8,6 +8,8 @@ CREATE TABLE IF NOT EXISTS games (
division INTEGER NOT NULL,
team_home INTEGER NOT NULL,
team_away INTEGER NOT NULL,
start_at TIMESTAMPTZ NOT NULL,
end_at TIMESTAMPTZ NOT NULL,
-- home and away teams need to actually be teams
CONSTRAINT team_home_fk
FOREIGN KEY(team_home)

@ -0,0 +1,39 @@
INSERT INTO games
(id, name, division, team_home, team_away, start_at, end_at)
VALUES
(
1,
'Game 1',
1, -- LV/D
1, -- Bullseye
2, -- Seecats
'2022-03-25 17:00:00 America/Toronto',
'2022-03-25 18:30:00 America/Toronto'
),
(
2,
'Game 2',
1, -- LV/D
1, -- Bullseye
2, -- Seecats
'2022-03-26 12:00:00 America/Toronto',
'2022-03-26 13:30:00 America/Toronto'
),
(
3,
'Game 3',
1, -- LV/D
1, -- Bullseye
2, -- Seecats
'2022-03-26 17:30:00 America/Toronto',
'2022-03-26 19:00:00 America/Toronto'
),
(
4,
'Game 4',
1, -- LV/D
1, -- Bullseye
2, -- Seecats
'2022-03-27 10:00:00 America/Toronto',
'2022-03-27 11:30:00 America/Toronto'
);

@ -0,0 +1,2 @@
-- Add down migration script here
DROP TABLE IF EXISTS game_players;

@ -1,15 +1,21 @@
-- Add up migration script here
CREATE TABLE IF NOT EXISTS team_players (
CREATE TABLE IF NOT EXISTS game_players (
id SERIAL PRIMARY KEY NOT NULL,
team INTEGER NOT NULL,
game INTEGER NOT NULL,
player INTEGER NOT NULL,
position INTEGER NOT NULL,
-- not a foreign key
-- the number of the player, usually a 1 or 2 digit number
player_number INTEGER NOT NULL,
CONSTRAINT team_fk
FOREIGN KEY(team)
REFERENCES teams(id)
ON DELETE RESTRICT,
CONSTRAINT game_fk
FOREIGN KEY(game)
REFERENCES games(id)
ON DELETE RESTRICT,
CONSTRAINT player_fk
FOREIGN KEY(player)
REFERENCES players(id)

@ -1,2 +1,2 @@
-- Add down migration script here
DELETE FROM team_players;
DELETE FROM game_players;

@ -0,0 +1,816 @@
-- Add up migration script here
INSERT INTO game_players
(team, player, position, player_number, game)
VALUES
(
1,
31,
1,
11,
1
),
(
1,
1,
3,
3,
1
),
(
1,
2,
4,
8,
1
),
(
1,
3,
5,
1,
1
),
(
1,
4,
2,
14,
1
),
(
1,
5,
4,
91,
1
),
(
1,
7,
1,
15,
1
),
(
1,
8,
4,
10,
1
),
(
1,
9,
3,
13,
1
),
(
1,
10,
1,
10,
1
),
(
1,
11,
2,
84,
1
),
(
2,
12,
5,
35,
1
),
(
2,
13,
5,
30,
1
),
(
2,
14,
1,
15,
1
),
(
2,
15,
2,
17,
1
),
(
2,
16,
1,
3,
1
),
(
2,
17,
2,
9,
1
),
(
2,
18,
4,
16,
1
),
(
2,
19,
4,
4,
1
),
(
2,
21,
4,
14,
1
),
(
2,
22,
4,
12,
1
),
(
2,
23,
2,
10,
1
),
(
2,
24,
7,
0,
1
),
(
2,
25,
7,
0,
1
),
(
2,
26,
7,
0,
1
),
(
1,
27,
7,
0,
1
),
(
1,
28,
7,
0,
1
),
(
1,
29,
7,
0,
1
),
(
1,
30,
7,
0,
1
),
(
1,
31,
1,
11,
2
),
(
1,
1,
3,
3,
2
),
(
1,
2,
4,
8,
2
),
(
1,
3,
5,
1,
2
),
(
1,
4,
2,
14,
2
),
(
1,
5,
4,
91,
2
),
(
1,
7,
1,
15,
2
),
(
1,
8,
4,
10,
2
),
(
1,
9,
3,
13,
2
),
(
1,
10,
1,
10,
2
),
(
1,
11,
2,
84,
2
),
(
2,
12,
5,
35,
2
),
(
2,
13,
5,
30,
2
),
(
2,
14,
1,
15,
2
),
(
2,
15,
2,
17,
2
),
(
2,
16,
1,
3,
2
),
(
2,
17,
2,
9,
2
),
(
2,
18,
4,
16,
2
),
(
2,
19,
4,
4,
2
),
(
2,
21,
4,
14,
2
),
(
2,
22,
4,
12,
2
),
(
2,
23,
2,
10,
2
),
(
2,
24,
7,
0,
2
),
(
2,
25,
7,
0,
2
),
(
2,
26,
7,
0,
2
),
(
1,
27,
7,
0,
2
),
(
1,
28,
7,
0,
2
),
(
1,
29,
7,
0,
2
),
(
1,
30,
7,
0,
2
),
(
1,
31,
1,
11,
3
),
(
1,
1,
3,
3,
3
),
(
1,
2,
4,
8,
3
),
(
1,
3,
5,
1,
3
),
(
1,
4,
2,
14,
3
),
(
1,
5,
4,
91,
3
),
(
1,
7,
1,
15,
3
),
(
1,
8,
4,
10,
3
),
(
1,
9,
3,
13,
3
),
(
1,
10,
1,
10,
3
),
(
1,
11,
2,
84,
3
),
(
2,
12,
5,
35,
3
),
(
2,
13,
5,
30,
3
),
(
2,
14,
1,
15,
3
),
(
2,
15,
2,
17,
3
),
(
2,
16,
1,
3,
3
),
(
2,
17,
2,
9,
3
),
(
2,
18,
4,
16,
3
),
(
2,
19,
4,
4,
3
),
(
2,
21,
4,
14,
3
),
(
2,
22,
4,
12,
3
),
(
2,
23,
2,
10,
3
),
(
2,
24,
7,
0,
3
),
(
2,
25,
7,
0,
3
),
(
2,
26,
7,
0,
3
),
(
1,
27,
7,
0,
3
),
(
1,
28,
7,
0,
3
),
(
1,
29,
7,
0,
3
),
(
1,
30,
7,
0,
3
),
(
1,
31,
1,
11,
4
),
(
1,
1,
3,
3,
4
),
(
1,
2,
4,
8,
4
),
(
1,
3,
5,
1,
4
),
(
1,
4,
2,
14,
4
),
(
1,
5,
4,
91,
4
),
(
1,
7,
1,
15,
4
),
(
1,
8,
4,
10,
4
),
(
1,
9,
3,
13,
4
),
(
1,
10,
1,
10,
4
),
(
1,
11,
2,
84,
4
),
(
2,
12,
5,
35,
4
),
(
2,
13,
5,
30,
4
),
(
2,
14,
1,
15,
4
),
(
2,
15,
2,
17,
4
),
(
2,
16,
1,
3,
4
),
(
2,
17,
2,
9,
4
),
(
2,
18,
4,
16,
4
),
(
2,
19,
4,
4,
4
),
(
2,
21,
4,
14,
4
),
(
2,
22,
4,
12,
4
),
(
2,
23,
2,
10,
4
),
(
2,
24,
7,
0,
4
),
(
2,
25,
7,
0,
4
),
(
2,
26,
7,
0,
4
),
(
1,
27,
7,
0,
4
),
(
1,
28,
7,
0,
4
),
(
1,
29,
7,
0,
4
),
(
1,
30,
7,
0,
4
);

@ -1,178 +0,0 @@
-- Add up migration script here
INSERT INTO team_players
(team, player, position, player_number)
VALUES
(
1,
31,
1,
11
),
(
1,
1,
3,
3
),
(
1,
2,
4,
8
),
(
1,
3,
5,
1
),
(
1,
4,
2,
14
),
(
1,
5,
4,
91
),
(
1,
7,
1,
15
),
(
1,
8,
4,
10
),
(
1,
9,
3,
13
),
(
1,
10,
1,
10
),
(
1,
11,
2,
84
),
(
2,
12,
5,
35
),
(
2,
13,
5,
30
),
(
2,
14,
1,
15
),
(
2,
15,
2,
17
),
(
2,
16,
1,
3
),
(
2,
17,
2,
9
),
(
2,
18,
4,
16
),
(
2,
19,
4,
4
),
(
2,
21,
4,
14
),
(
2,
22,
4,
12
),
(
2,
23,
2,
10
),
(
2,
24,
7,
0
),
(
2,
25,
7,
0
),
(
2,
26,
7,
0
),
(
1,
27,
7,
0
),
(
1,
28,
7,
0
),
(
1,
29,
7,
0
),
(
1,
30,
7,
0
);

@ -1,31 +0,0 @@
INSERT INTO games
(id, name, division, team_home, team_away)
VALUES
(
1,
'Game 1',
1, -- LV/D
1, -- Bullseye
2 -- Seecats
),
(
2,
'Game 2',
1, -- LV/D
1, -- Bullseye
2 -- Seecats
),
(
3,
'Game 3',
1, -- LV/D
1, -- Bullseye
2 -- Seecats
),
(
4,
'Game 4',
1, -- LV/D
1, -- Bullseye
2 -- Seecats
);

@ -10,8 +10,6 @@ CREATE TABLE IF NOT EXISTS shots (
on_net BOOLEAN NOT NULL,
-- did the puck go in?
goal BOOLEAN NOT NULL,
-- what team was the shooter on
shooter_team INTEGER NOT NULL,
-- which player is the shooter
shooter INTEGER NOT NULL,
-- which player was the goalie
@ -28,32 +26,27 @@ CREATE TABLE IF NOT EXISTS shots (
-- was the shooter a real player
CONSTRAINT shooter_fk
FOREIGN KEY(shooter)
REFERENCES players(id)
REFERENCES game_players(id)
ON DELETE RESTRICT,
-- was the assistant is a real player
CONSTRAINT assistant_fk
FOREIGN KEY(assistant)
REFERENCES players(id)
REFERENCES game_players(id)
ON DELETE RESTRICT,
-- was the second assistant a real player
CONSTRAINT assistant_second_fk
FOREIGN KEY(assistant_second)
REFERENCES players(id)
REFERENCES game_players(id)
ON DELETE RESTRICT,
-- was the goalie a real player
CONSTRAINT goalie_fk
FOREIGN KEY(goalie)
REFERENCES players(id)
REFERENCES game_players(id)
ON DELETE RESTRICT,
-- was the (optional) blocker a real player
CONSTRAINT blocker_fk
FOREIGN KEY(blocker)
REFERENCES players(id)
ON DELETE RESTRICT,
-- was the shooter's team a real team
CONSTRAINT shooter_team_fk
FOREIGN KEY(shooter_team)
REFERENCES teams(id)
REFERENCES game_players(id)
ON DELETE RESTRICT,
-- is the period references a real period
CONSTRAINT period_fk

File diff suppressed because it is too large Load Diff

@ -4,6 +4,6 @@ use sqlx::postgres::PgPoolOptions;
pub async fn connect() -> Pool<Postgres> {
PgPoolOptions::new()
.max_connections(8)
.connect("postgres://ibihf:ibihf@localhost/ibihf").await
.connect("postgres://ibihf2:ibihf@localhost/ibihf").await
.unwrap()
}

@ -5,14 +5,13 @@ mod filters;
mod translations;
use translations::{
TranslatedKey,
SupportedLanguage,
};
use crate::model::{
League,
Team,
Division,
TeamPlayer,
GamePlayer,
Player,
Shot,
Game,
@ -45,7 +44,6 @@ use axum::{
},
response::{
Json,
Html,
IntoResponse,
},
routing::get,
@ -233,7 +231,7 @@ async fn league_html(State(server_config): State<ServerState>, Path(lang): Path<
(StatusCode::OK, leagues_template)
}
async fn divisions_for_league_html(State(server_config): State<ServerState>, Path(league_id): Path<i32>, Path(lang): Path<SupportedLanguage>) -> impl IntoResponse {
async fn divisions_for_league_html(State(server_config): State<ServerState>, Path((lang,league_id)): Path<(SupportedLanguage, i32)>) -> impl IntoResponse {
let league = League::get(&*server_config.db_pool, league_id)
.await
.unwrap();
@ -248,7 +246,7 @@ async fn divisions_for_league_html(State(server_config): State<ServerState>, Pat
(StatusCode::OK, html)
}
async fn games_for_division_html(State(server_config): State<ServerState>, Path(division_id): Path<i32>, Path(lang): Path<SupportedLanguage>) -> impl IntoResponse {
async fn games_for_division_html(State(server_config): State<ServerState>, Path((lang,division_id)): Path<(SupportedLanguage,i32)>) -> impl IntoResponse {
let division = Division::get(&*server_config.db_pool, division_id)
.await
.unwrap();
@ -262,7 +260,7 @@ async fn games_for_division_html(State(server_config): State<ServerState>, Path(
};
(StatusCode::OK, games_template)
}
async fn score_for_game_html(State(server_config): State<ServerState>, Path(game_id): Path<i32>, Path(lang): Path<SupportedLanguage>) -> impl IntoResponse {
async fn score_for_game_html(State(server_config): State<ServerState>, Path((lang,game_id)): Path<(SupportedLanguage, i32)>) -> impl IntoResponse {
let game = sqlx::query_as::<_, Game>(
"SELECT * FROM games WHERE id = $1;"
)
@ -318,9 +316,9 @@ macro_rules! impl_all_query_types {
}
impl_all_query_types!(
TeamPlayer,
team_player_all,
team_player_id
GamePlayer,
game_player_all,
game_player_id
);
impl_all_query_types!(
Player,

@ -1,6 +1,6 @@
use sqlx::FromRow;
use sqlx::types::chrono::{DateTime, Utc};
use chrono::serde::{ts_seconds, ts_seconds_option};
use chrono::serde::ts_seconds;
use serde::{Serialize, Deserialize};
pub trait TableName {
@ -20,19 +20,11 @@ pub struct League {
#[ormx(default)]
pub id: i32,
pub name: String,
#[serde(with = "ts_seconds")]
pub start_date: DateTime<Utc>,
#[serde(with = "ts_seconds_option")]
pub end_date: Option<DateTime<Utc>>,
}
#[derive(FromRow, Serialize, Deserialize, Debug, ormx::Patch)]
#[ormx(table_name = "leagues", table = League, id = "id")]
pub struct NewLeague {
pub name: String,
#[serde(with = "ts_seconds")]
pub start_date: DateTime<Utc>,
#[serde(with = "ts_seconds_option")]
pub end_date: Option<DateTime<Utc>>,
}
#[derive(FromRow, Serialize, Deserialize, Debug, ormx::Table)]
@ -102,7 +94,6 @@ pub struct NewPlayer {
pub struct Shot {
#[ormx(default)]
pub id: i32,
pub shooter_team: i32,
pub shooter: i32,
pub goalie: i32,
pub assistant: Option<i32>,
@ -118,13 +109,14 @@ pub struct Shot {
}
#[derive(FromRow, Deserialize, Serialize, Debug, ormx::Table)]
#[ormx(table = "team_players", id = id, insertable, deletable)]
pub struct TeamPlayer {
#[ormx(table = "game_players", id = id, insertable, deletable)]
pub struct GamePlayer {
#[ormx(default)]
pub id: i32,
pub team: i32,
pub player: i32,
pub position: i32,
pub game: i32,
}
#[derive(FromRow, Deserialize, Serialize, Debug, ormx::Table)]
@ -139,19 +131,29 @@ pub struct Game {
pub team_away: i32,
}
impl_table_name!(TeamPlayer, "team_players");
#[derive(FromRow, Deserialize, Serialize, Debug, ormx::Table)]
#[ormx(table = "periods", id = id, insertable, deletable)]
pub struct Period {
pub id: i32,
pub period_type: i32,
#[ormx(get_many(i32))]
pub game: i32,
}
impl_table_name!(GamePlayer, "game_players");
impl_table_name!(Player, "players");
impl_table_name!(League, "leagues");
impl_table_name!(Division, "divisions");
impl_table_name!(Team, "teams");
impl_table_name!(Shot, "shots");
impl_table_name!(Game, "games");
impl_table_name!(Period, "periods");
#[cfg(test)]
mod tests {
use std::env;
use crate::model::{
TeamPlayer,
GamePlayer,
Player,
League,
Division,
@ -207,7 +209,7 @@ mod tests {
}
}
}
generate_select_test!(TeamPlayer, select_team_player);
generate_select_test!(GamePlayer, selec_game_player);
generate_select_test!(Player, select_player);
generate_select_test!(League, select_league);
generate_select_test!(Division, select_division);

@ -4,9 +4,43 @@ use crate::model::{
Player,
Game,
League,
Division,
Period,
};
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,
@ -39,53 +73,45 @@ SELECT
SELECT COUNT(shots.id)
FROM shots
JOIN periods ON periods.id=shots.period
WHERE shooter=players.id
AND goal=true
AND periods.game=$1
) AS goals,
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 (assistant=players.id
OR assistant_second=players.id)
AND goal=true
AND periods.game=$1
) AS assists,
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 (assistant=players.id
OR assistant_second=players.id
OR shooter=players.id)
AND goal=true
AND periods.game=$1
) AS points,
players.name AS name
FROM players
JOIN shots ON shots.shooter=players.id OR shots.assistant=players.id
JOIN periods ON periods.id=shots.period
WHERE periods.game = $1
GROUP BY players.id
-- exclude players who do not have any points
-- NOTE: we can NOT use the aliased column "points" here, so we need to recalculate it.
-- This should not be a performance problem because the optimizer should deal with duplicate sub-queries.
HAVING
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 (assistant=players.id
OR assistant_second=players.id
OR shooter=players.id)
AND goal=true
AND periods.game=$1
WHERE shots.goal=true
AND (shots.shooter=game_players.id
OR shots.assistant=game_players.id
OR shots.assistant_second=game_players.id)
) > 0
ORDER BY
points DESC,
goals DESC,
players.name;
goals DESC;
"#;
sqlx::query_as::<_, PlayerStats>(query)
.bind(game.id)
@ -98,12 +124,13 @@ pub async fn get_latest_league_for_player(pool: &PgPool, player: &Player) -> Res
r#"
SELECT leagues.*
FROM players
JOIN team_players ON team_players.player=players.id
JOIN teams ON teams.id=team_players.team
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 leagues.end_date DESC
ORDER BY games.end_at DESC
LIMIT 1;
"#;
sqlx::query_as::<_, League>(query)
@ -163,62 +190,41 @@ WHERE id=$1;
}
pub async fn get_latest_stats(pool: &PgPool, player: &Player) -> Result<Vec<GoalDetails>, sqlx::Error> {
let query = r#"
let query =
r#"
SELECT
shots.shooter AS player_id,
shots.assistant AS first_assist_id,
shots.assistant_second AS second_assist_id,
(
SELECT name
FROM players
WHERE id=shots.shooter
) AS player_name,
(
SELECT name
FROM players
WHERE id=shots.assistant
) AS first_assist_name,
(
SELECT name
FROM players
WHERE id=shots.assistant_second
) AS second_assist_name,
(
SELECT player_number
FROM team_players
WHERE player=shots.shooter
AND team=shots.shooter_team
) AS player_number,
(
SELECT player_number
FROM team_players
WHERE player=shots.assistant
AND team=shots.shooter_team
) AS first_assist_number,
(
SELECT player_number
FROM team_players
WHERE player=shots.assistant_second
AND team=shots.shooter_team
) AS second_assist_number,
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.shooter_team AS player_team,
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 periods.period_type=period_types.id
JOIN teams ON shots.shooter_team=teams.id
WHERE shots.shooter=$1
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)
sqlx::query_as::<_, GoalDetails>(&query)
.bind(player.id)
.fetch_all(pool)
.await
@ -227,30 +233,26 @@ LIMIT 5;
pub async fn get_all_player_stats(pool: &PgPool, player: &Player) -> Result<PlayerStats, sqlx::Error> {
let query =r#"
SELECT
(
SELECT COUNT(id)
FROM shots
WHERE shots.goal=true
AND shots.shooter=players.id
) AS goals,
(
SELECT COUNT(id)
FROM shots
WHERE shots.goal=true
AND (shots.assistant=players.id
OR shots.assistant_second=players.id)
) AS assists,
(
SELECT COUNT(id)
FROM shots
WHERE shots.goal=true
AND (shots.shooter=players.id
OR shots.assistant=players.id
OR shots.assistant_second=players.id)
) AS points,
COUNT(goals) AS goals,
COUNT(assists) AS assists,
COUNT(points) AS points,
players.name AS name
FROM players
WHERE id=$1;
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)
@ -266,6 +268,7 @@ pub struct GoalDetails {
pub team_name: String,
pub team_id: i32,
pub time_remaining: i32,
pub period_id: i32,
pub period_short_name: String,
pub first_assist_name: Option<String>,
pub first_assist_number: Option<i32>,
@ -300,51 +303,30 @@ SELECT
shots.shooter AS player_id,
shots.assistant AS first_assist_id,
shots.assistant_second AS second_assist_id,
(
SELECT name
FROM players
WHERE id=shots.shooter
) AS player_name,
(
SELECT name
FROM players
WHERE id=shots.assistant
) AS first_assist_name,
(
SELECT name
FROM players
WHERE id=shots.assistant_second
) AS second_assist_name,
(
SELECT player_number
FROM team_players
WHERE player=shots.shooter
AND team=shots.shooter_team
) AS player_number,
(
SELECT player_number
FROM team_players
WHERE player=shots.assistant
AND team=shots.shooter_team
) AS first_assist_number,
(
SELECT player_number
FROM team_players
WHERE player=shots.assistant_second
AND team=shots.shooter_team
) AS second_assist_number,
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.shooter_team AS player_team,
shots.period_time AS time_remaining,
period_types.id AS period_id,
period_types.short_name AS period_short_name
FROM shots
JOIN periods ON shots.period=periods.id
JOIN period_types ON periods.period_type=period_types.id
JOIN teams ON shots.shooter_team=teams.id
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 periods.game=$1
AND games.id=$1
ORDER BY
periods.period_type ASC,
shots.period_time DESC;
@ -362,49 +344,27 @@ SELECT
shots.assistant AS first_assist_id,
shots.assistant_second AS second_assist_id,
shots.goal AS is_goal,
(
SELECT name
FROM players
WHERE id=shots.shooter
) AS player_name,
(
SELECT name
FROM players
WHERE id=shots.assistant
) AS first_assist_name,
(
SELECT name
FROM players
WHERE id=shots.assistant_second
) AS second_assist_name,
(
SELECT player_number
FROM team_players
WHERE player=shots.shooter
AND team=shots.shooter_team
) AS player_number,
(
SELECT player_number
FROM team_players
WHERE player=shots.assistant
AND team=shots.shooter_team
) AS first_assist_number,
(
SELECT player_number
FROM team_players
WHERE player=shots.assistant_second
AND team=shots.shooter_team
) AS second_assist_number,
players.name AS player_name,
p_assistant.name AS first_assist_name,
p_assistant_second.name AS second_assist_name,
game_players.player_number AS player_number,
gp_assistant.player_number AS first_assist_number,
gp_assistant_second.player_number AS second_assist_number,
teams.name AS team_name,
teams.id AS team_id,
shots.shooter_team AS player_team,
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_assistant ON gp_assistant.id=shots.assistant
LEFT JOIN players p_assistant ON p_assistant.id=gp_assistant.player
LEFT JOIN game_players gp_assistant_second ON gp_assistant_second.id=shots.assistant_second
LEFT JOIN players p_assistant_second ON p_assistant_second.id=gp_assistant_second.player
JOIN periods ON shots.period=periods.id
JOIN period_types ON periods.period_type=period_types.id
JOIN teams ON shots.shooter_team=teams.id
WHERE periods.game=$1
ORDER BY
periods.period_type ASC,
@ -418,25 +378,16 @@ ORDER BY
pub async fn get_score_from_game(pool: &PgPool, game: &Game) -> Result<Vec<TeamStats>, sqlx::Error> {
let query = r#"
SELECT
(
SELECT COUNT(shots.id)
FROM shots
JOIN periods ON periods.id=shots.period
WHERE periods.game=$1
AND shots.goal=true
AND shots.shooter_team=teams.id
) AS goals,
(
SELECT COUNT(shots.id)
FROM shots
JOIN periods ON periods.id=shots.period
WHERE periods.game=$1
AND shooter_team=teams.id
) AS shots,
COUNT(CASE WHEN shots.goal = true THEN shots.id END) AS goals,
COUNT(shots.id) AS shots,
teams.name AS name
FROM games
JOIN teams ON teams.id=games.team_home OR teams.id=games.team_away
WHERE games.id=$1;
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)
@ -480,6 +431,7 @@ ORDER BY
#[cfg(test)]
mod tests {
use std::env;
use ormx::Table;
use crate::model::{
Game,
Player,
@ -495,14 +447,27 @@ mod tests {
get_league_player_stats,
get_all_player_stats,
get_latest_stats,
get_play_by_play_from_game,
};
#[test]
fn check_play_by_play() {
tokio_test::block_on(async move {
let pool = db_connect().await;
let game = Game::get(&pool, 3)
.await
.unwrap();
let pbp = get_play_by_play_from_game(&pool, &game)
.await
.unwrap();
})
}
#[test]
fn get_latest_stats_of_player() {
tokio_test::block_on(async move {
let pool = db_connect().await;
let player = sqlx::query_as::<_, Player>("SELECT * FROM id=2;")
.fetch_one(&pool)
let player = Player::get(&pool, 2)
.await
.unwrap();
let latest = get_latest_stats(&pool, &player)
@ -561,7 +526,7 @@ 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=1;")
let game = sqlx::query_as::<_, Game>("SELECT * FROM games WHERE id=3;")
.fetch_one(&pool)
.await
.unwrap();
@ -629,17 +594,17 @@ SELECT
teams.name AS scorer_team_name,
players.name AS scorer_name,
positions.name AS position,
team_players.player_number AS scorer_number,
game_players.player_number AS scorer_number,
shots.period_time AS period_time_left,
period_types.name AS period_name
FROM
shots
JOIN teams ON teams.id=shots.shooter_team
JOIN players ON players.id=shots.shooter
JOIN team_players ON team_players.player=players.id AND team_players.team=teams.id
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
JOIN periods ON periods.id=shots.period
JOIN period_types ON period_types.id=periods.period_type
JOIN positions ON positions.id=team_players.position;
JOIN positions ON positions.id=game_players.position;
"#;
let result = sqlx::query_as::<_, Notification>(query)
.fetch_one(&pool)

@ -1,6 +1,6 @@
<h1 id="first-heading">Divisions for the {{ league.name }}</h1>
<ul aria-labelledby="first-heading">
{% for division in divisions %}
<li><a href="/division/{{ division.id }}/">{{ division.name }}</a></li>
<li><a href="/en/division/{{ division.id }}/">{{ division.name }}</a></li>
{% endfor %}
</ul>

@ -2,7 +2,7 @@
{% if games.len() > 0 %}
<ol aria-labelledby="games">
{% for game in games %}
<li><a href="/game/{{ game.id }}/">{{ game.name }}</a></li>
<li><a href="/en/game/{{ game.id }}/">{{ game.name }}</a></li>
{% endfor %}
</ol>
{% else %}

Loading…
Cancel
Save