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 -- Add up migration script here
CREATE TABLE IF NOT EXISTS leagues ( CREATE TABLE IF NOT EXISTS leagues (
id SERIAL PRIMARY KEY NOT NULL, id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL
start_date TIMESTAMPTZ NOT NULL,
end_date TIMESTAMPTZ
); );

@ -1,5 +1,5 @@
-- Add up migration script here -- Add up migration script here
INSERT INTO leagues INSERT INTO leagues
(id, name, start_date, end_date) (id, name)
VALUES 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, division INTEGER NOT NULL,
team_home INTEGER NOT NULL, team_home INTEGER NOT NULL,
team_away 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 -- home and away teams need to actually be teams
CONSTRAINT team_home_fk CONSTRAINT team_home_fk
FOREIGN KEY(team_home) 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 -- 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, id SERIAL PRIMARY KEY NOT NULL,
team INTEGER NOT NULL, team INTEGER NOT NULL,
game INTEGER NOT NULL,
player INTEGER NOT NULL, player INTEGER NOT NULL,
position 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, player_number INTEGER NOT NULL,
CONSTRAINT team_fk CONSTRAINT team_fk
FOREIGN KEY(team) FOREIGN KEY(team)
REFERENCES teams(id) REFERENCES teams(id)
ON DELETE RESTRICT, ON DELETE RESTRICT,
CONSTRAINT game_fk
FOREIGN KEY(game)
REFERENCES games(id)
ON DELETE RESTRICT,
CONSTRAINT player_fk CONSTRAINT player_fk
FOREIGN KEY(player) FOREIGN KEY(player)
REFERENCES players(id) REFERENCES players(id)

@ -1,2 +1,2 @@
-- Add down migration script here -- 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, on_net BOOLEAN NOT NULL,
-- did the puck go in? -- did the puck go in?
goal BOOLEAN NOT NULL, goal BOOLEAN NOT NULL,
-- what team was the shooter on
shooter_team INTEGER NOT NULL,
-- which player is the shooter -- which player is the shooter
shooter INTEGER NOT NULL, shooter INTEGER NOT NULL,
-- which player was the goalie -- which player was the goalie
@ -28,32 +26,27 @@ CREATE TABLE IF NOT EXISTS shots (
-- was the shooter a real player -- was the shooter a real player
CONSTRAINT shooter_fk CONSTRAINT shooter_fk
FOREIGN KEY(shooter) FOREIGN KEY(shooter)
REFERENCES players(id) REFERENCES game_players(id)
ON DELETE RESTRICT, ON DELETE RESTRICT,
-- was the assistant is a real player -- was the assistant is a real player
CONSTRAINT assistant_fk CONSTRAINT assistant_fk
FOREIGN KEY(assistant) FOREIGN KEY(assistant)
REFERENCES players(id) REFERENCES game_players(id)
ON DELETE RESTRICT, ON DELETE RESTRICT,
-- was the second assistant a real player -- was the second assistant a real player
CONSTRAINT assistant_second_fk CONSTRAINT assistant_second_fk
FOREIGN KEY(assistant_second) FOREIGN KEY(assistant_second)
REFERENCES players(id) REFERENCES game_players(id)
ON DELETE RESTRICT, ON DELETE RESTRICT,
-- was the goalie a real player -- was the goalie a real player
CONSTRAINT goalie_fk CONSTRAINT goalie_fk
FOREIGN KEY(goalie) FOREIGN KEY(goalie)
REFERENCES players(id) REFERENCES game_players(id)
ON DELETE RESTRICT, ON DELETE RESTRICT,
-- was the (optional) blocker a real player -- was the (optional) blocker a real player
CONSTRAINT blocker_fk CONSTRAINT blocker_fk
FOREIGN KEY(blocker) FOREIGN KEY(blocker)
REFERENCES players(id) REFERENCES game_players(id)
ON DELETE RESTRICT,
-- was the shooter's team a real team
CONSTRAINT shooter_team_fk
FOREIGN KEY(shooter_team)
REFERENCES teams(id)
ON DELETE RESTRICT, ON DELETE RESTRICT,
-- is the period references a real period -- is the period references a real period
CONSTRAINT period_fk 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> { pub async fn connect() -> Pool<Postgres> {
PgPoolOptions::new() PgPoolOptions::new()
.max_connections(8) .max_connections(8)
.connect("postgres://ibihf:ibihf@localhost/ibihf").await .connect("postgres://ibihf2:ibihf@localhost/ibihf").await
.unwrap() .unwrap()
} }

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

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

@ -4,9 +4,43 @@ use crate::model::{
Player, Player,
Game, Game,
League, League,
Division,
Period,
}; };
use serde::{Serialize, Deserialize}; 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)] #[derive(FromRow, Deserialize, Serialize, Debug)]
pub struct TeamStats { pub struct TeamStats {
pub name: String, pub name: String,
@ -39,53 +73,45 @@ SELECT
SELECT COUNT(shots.id) SELECT COUNT(shots.id)
FROM shots FROM shots
JOIN periods ON periods.id=shots.period JOIN periods ON periods.id=shots.period
WHERE shooter=players.id WHERE shots.goal=true
AND goal=true AND (shots.shooter=game_players.id
AND periods.game=$1 OR shots.assistant=game_players.id
) AS goals, OR shots.assistant_second=game_players.id)
) AS points,
( (
SELECT COUNT(shots.id) SELECT COUNT(shots.id)
FROM shots FROM shots
JOIN periods ON periods.id=shots.period JOIN periods ON periods.id=shots.period
WHERE (assistant=players.id WHERE shots.goal=true
OR assistant_second=players.id) AND shots.shooter=game_players.id
AND goal=true ) AS goals,
AND periods.game=$1
) AS assists,
( (
SELECT COUNT(shots.id) SELECT COUNT(shots.id)
FROM shots FROM shots
JOIN periods ON periods.id=shots.period JOIN periods ON periods.id=shots.period
WHERE (assistant=players.id WHERE shots.goal=true
OR assistant_second=players.id AND (shots.assistant=game_players.id
OR shooter=players.id) OR shots.assistant_second=game_players.id)
AND goal=true ) AS assists,
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
( (
SELECT name
FROM players
WHERE id=game_players.player
) AS name
FROM game_players
WHERE game_players.game=$1
AND (
SELECT COUNT(shots.id) SELECT COUNT(shots.id)
FROM shots FROM shots
JOIN periods ON periods.id=shots.period JOIN periods ON periods.id=shots.period
WHERE (assistant=players.id WHERE shots.goal=true
OR assistant_second=players.id AND (shots.shooter=game_players.id
OR shooter=players.id) OR shots.assistant=game_players.id
AND goal=true OR shots.assistant_second=game_players.id)
AND periods.game=$1
) > 0 ) > 0
ORDER BY ORDER BY
points DESC, points DESC,
goals DESC, goals DESC;
players.name;
"#; "#;
sqlx::query_as::<_, PlayerStats>(query) sqlx::query_as::<_, PlayerStats>(query)
.bind(game.id) .bind(game.id)
@ -98,12 +124,13 @@ pub async fn get_latest_league_for_player(pool: &PgPool, player: &Player) -> Res
r#" r#"
SELECT leagues.* SELECT leagues.*
FROM players FROM players
JOIN team_players ON team_players.player=players.id JOIN game_players ON game_players.player=players.id
JOIN teams ON teams.id=team_players.team JOIN games ON games.id=game_players.game
JOIN teams ON teams.id=game_players.team
JOIN divisions ON divisions.id=teams.division JOIN divisions ON divisions.id=teams.division
JOIN leagues ON leagues.id=divisions.league JOIN leagues ON leagues.id=divisions.league
WHERE players.id=$1 WHERE players.id=$1
ORDER BY leagues.end_date DESC ORDER BY games.end_at DESC
LIMIT 1; LIMIT 1;
"#; "#;
sqlx::query_as::<_, League>(query) 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> { pub async fn get_latest_stats(pool: &PgPool, player: &Player) -> Result<Vec<GoalDetails>, sqlx::Error> {
let query = r#" let query =
r#"
SELECT SELECT
shots.shooter AS player_id, players.id AS player_id,
shots.assistant AS first_assist_id, p_assist.id AS first_assist_id,
shots.assistant_second AS second_assist_id, p_assist_second.id AS second_assist_id,
( players.name AS player_name,
SELECT name p_assist.name AS first_assist_name,
FROM players p_assist_second.name AS second_assist_name,
WHERE id=shots.shooter game_players.player_number AS player_number,
) AS player_name, gp_assist.player_number AS first_assist_number,
( gp_assist_second.player_number AS second_assist_number,
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,
teams.name AS team_name, teams.name AS team_name,
teams.id AS team_id, teams.id AS team_id,
shots.shooter_team AS player_team,
shots.period_time AS time_remaining, shots.period_time AS time_remaining,
period_types.id AS period_id, period_types.id AS period_id,
period_types.short_name AS period_short_name period_types.short_name AS period_short_name
FROM shots 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 periods ON shots.period=periods.id
JOIN period_types ON periods.period_type=period_types.id JOIN period_types ON period_types.id=periods.period_type
JOIN teams ON shots.shooter_team=teams.id WHERE players.id=$1
WHERE shots.shooter=$1
ORDER BY ORDER BY
shots.created_at DESC, shots.created_at DESC,
periods.period_type DESC, periods.period_type DESC,
shots.period_time ASC shots.period_time ASC
LIMIT 5; LIMIT 5;
"#; "#;
sqlx::query_as::<_, GoalDetails>(query) sqlx::query_as::<_, GoalDetails>(&query)
.bind(player.id) .bind(player.id)
.fetch_all(pool) .fetch_all(pool)
.await .await
@ -227,30 +233,26 @@ LIMIT 5;
pub async fn get_all_player_stats(pool: &PgPool, player: &Player) -> Result<PlayerStats, sqlx::Error> { pub async fn get_all_player_stats(pool: &PgPool, player: &Player) -> Result<PlayerStats, sqlx::Error> {
let query =r#" let query =r#"
SELECT SELECT
( COUNT(goals) AS goals,
SELECT COUNT(id) COUNT(assists) AS assists,
FROM shots COUNT(points) AS points,
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,
players.name AS name players.name AS name
FROM players 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) sqlx::query_as::<_, PlayerStats>(query)
.bind(player.id) .bind(player.id)
@ -266,6 +268,7 @@ pub struct GoalDetails {
pub team_name: String, pub team_name: String,
pub team_id: i32, pub team_id: i32,
pub time_remaining: i32, pub time_remaining: i32,
pub period_id: i32,
pub period_short_name: String, pub period_short_name: String,
pub first_assist_name: Option<String>, pub first_assist_name: Option<String>,
pub first_assist_number: Option<i32>, pub first_assist_number: Option<i32>,
@ -300,51 +303,30 @@ SELECT
shots.shooter AS player_id, shots.shooter AS player_id,
shots.assistant AS first_assist_id, shots.assistant AS first_assist_id,
shots.assistant_second AS second_assist_id, shots.assistant_second AS second_assist_id,
( players.name AS player_name,
SELECT name p_assist.name AS first_assist_name,
FROM players p_assist_second.name AS second_assist_name,
WHERE id=shots.shooter game_players.player_number AS player_number,
) AS player_name, gp_assist.player_number AS first_assist_number,
( gp_assist_second.player_number AS second_assist_number,
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,
teams.name AS team_name, teams.name AS team_name,
teams.id AS team_id, teams.id AS team_id,
shots.shooter_team AS player_team,
shots.period_time AS time_remaining, shots.period_time AS time_remaining,
period_types.id AS period_id, period_types.id AS period_id,
period_types.short_name AS period_short_name period_types.short_name AS period_short_name
FROM shots FROM shots
JOIN periods ON shots.period=periods.id JOIN game_players ON game_players.id=shots.shooter
JOIN period_types ON periods.period_type=period_types.id JOIN players ON players.id=game_players.player
JOIN teams ON shots.shooter_team=teams.id 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 WHERE shots.goal=true
AND periods.game=$1 AND games.id=$1
ORDER BY ORDER BY
periods.period_type ASC, periods.period_type ASC,
shots.period_time DESC; shots.period_time DESC;
@ -362,49 +344,27 @@ SELECT
shots.assistant AS first_assist_id, shots.assistant AS first_assist_id,
shots.assistant_second AS second_assist_id, shots.assistant_second AS second_assist_id,
shots.goal AS is_goal, shots.goal AS is_goal,
( players.name AS player_name,
SELECT name p_assistant.name AS first_assist_name,
FROM players p_assistant_second.name AS second_assist_name,
WHERE id=shots.shooter game_players.player_number AS player_number,
) AS player_name, gp_assistant.player_number AS first_assist_number,
( gp_assistant_second.player_number AS second_assist_number,
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,
teams.name AS team_name, teams.name AS team_name,
teams.id AS team_id, teams.id AS team_id,
shots.shooter_team AS player_team,
shots.period_time AS time_remaining, shots.period_time AS time_remaining,
period_types.id AS period_id, period_types.id AS period_id,
period_types.short_name AS period_short_name period_types.short_name AS period_short_name
FROM shots 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 periods ON shots.period=periods.id
JOIN period_types ON periods.period_type=period_types.id JOIN period_types ON periods.period_type=period_types.id
JOIN teams ON shots.shooter_team=teams.id
WHERE periods.game=$1 WHERE periods.game=$1
ORDER BY ORDER BY
periods.period_type ASC, 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> { pub async fn get_score_from_game(pool: &PgPool, game: &Game) -> Result<Vec<TeamStats>, sqlx::Error> {
let query = r#" let query = r#"
SELECT SELECT
( COUNT(CASE WHEN shots.goal = true THEN shots.id END) AS goals,
SELECT COUNT(shots.id) COUNT(shots.id) AS shots,
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,
teams.name AS name teams.name AS name
FROM games FROM games
JOIN teams ON teams.id=games.team_home OR teams.id=games.team_away JOIN periods ON periods.game=games.id
WHERE games.id=$1; 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) sqlx::query_as::<_, TeamStats>(query)
.bind(game.id) .bind(game.id)
@ -480,6 +431,7 @@ ORDER BY
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::env; use std::env;
use ormx::Table;
use crate::model::{ use crate::model::{
Game, Game,
Player, Player,
@ -495,14 +447,27 @@ mod tests {
get_league_player_stats, get_league_player_stats,
get_all_player_stats, get_all_player_stats,
get_latest_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] #[test]
fn get_latest_stats_of_player() { fn get_latest_stats_of_player() {
tokio_test::block_on(async move { tokio_test::block_on(async move {
let pool = db_connect().await; let pool = db_connect().await;
let player = sqlx::query_as::<_, Player>("SELECT * FROM id=2;") let player = Player::get(&pool, 2)
.fetch_one(&pool)
.await .await
.unwrap(); .unwrap();
let latest = get_latest_stats(&pool, &player) let latest = get_latest_stats(&pool, &player)
@ -561,7 +526,7 @@ mod tests {
fn check_score_details_from_game() { fn check_score_details_from_game() {
tokio_test::block_on(async move { tokio_test::block_on(async move {
let pool = db_connect().await; 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) .fetch_one(&pool)
.await .await
.unwrap(); .unwrap();
@ -629,17 +594,17 @@ SELECT
teams.name AS scorer_team_name, teams.name AS scorer_team_name,
players.name AS scorer_name, players.name AS scorer_name,
positions.name AS position, 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, shots.period_time AS period_time_left,
period_types.name AS period_name period_types.name AS period_name
FROM FROM
shots shots
JOIN teams ON teams.id=shots.shooter_team JOIN game_players ON game_players.id=shots.shooter
JOIN players ON players.id=shots.shooter JOIN players ON players.id=game_players.player
JOIN team_players ON team_players.player=players.id AND team_players.team=teams.id JOIN teams ON teams.id=game_players.team
JOIN periods ON periods.id=shots.period JOIN periods ON periods.id=shots.period
JOIN period_types ON period_types.id=periods.period_type 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) let result = sqlx::query_as::<_, Notification>(query)
.fetch_one(&pool) .fetch_one(&pool)

@ -1,6 +1,6 @@
<h1 id="first-heading">Divisions for the {{ league.name }}</h1> <h1 id="first-heading">Divisions for the {{ league.name }}</h1>
<ul aria-labelledby="first-heading"> <ul aria-labelledby="first-heading">
{% for division in divisions %} {% 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 %} {% endfor %}
</ul> </ul>

@ -2,7 +2,7 @@
{% if games.len() > 0 %} {% if games.len() > 0 %}
<ol aria-labelledby="games"> <ol aria-labelledby="games">
{% for game in 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 %} {% endfor %}
</ol> </ol>
{% else %} {% else %}

Loading…
Cancel
Save