Skip to content

Commit

Permalink
feat: added full support for driver standings
Browse files Browse the repository at this point in the history
  • Loading branch information
thibault-cne committed Apr 12, 2024
1 parent 05bc3af commit 1d6354b
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 0 deletions.
30 changes: 30 additions & 0 deletions crates/api/src/driver_standing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use rocket::serde::json::Json;
use rocket::{get, routes, State};

use infrastructure::ConnectionPool;
use shared::prelude::*;

#[get("/<series>/drivers/standing?<param..>")]
pub fn driver_standings(
db: &State<ConnectionPool>,
series: Series,
param: shared::parameters::GetDriverStandingsParameter,
) -> Result<Json<Response<Vec<DriverStandings>>>> {
let conn = &mut db.from_series(series).get().unwrap();

let query = application::driver_standing::DriverStandingQueryBuilder::params(param).build();

let res = query.query_and_count(conn);

let response = Response {
data: res.0,
pagination: res.1,
series,
};

Ok(Json(response))
}

pub fn handlers() -> Vec<rocket::Route> {
routes![driver_standings]
}
2 changes: 2 additions & 0 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod circuit;
mod constructor;
mod constructor_standing;
mod driver;
mod driver_standing;

pub mod handlers {
use crate::*;
Expand All @@ -15,6 +16,7 @@ pub mod handlers {
.chain(driver::handlers())
.chain(constructor::handlers())
.chain(constructor_standing::handlers())
.chain(driver_standing::handlers())
.collect()
}
}
1 change: 1 addition & 0 deletions crates/application/src/constructor_standing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ impl ConstructorStandingQueryBuilder {
.distinct()
.columns(
[
Constructors::ConstructorId,
Constructors::ConstructorRef,
Constructors::Name,
Constructors::Nationality,
Expand Down
141 changes: 141 additions & 0 deletions crates/application/src/driver_standing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use sea_query::{Expr, Func, IntoColumnRef, Query, SelectStatement};

use shared::models::DriverStandings as DriverStandingsModel;
use shared::parameters::GetDriverStandingsParameter;

use crate::{
iden::*,
pagination::{Paginate, Paginated},
sql::SqlBuilder,
};

pub struct DriverStandingQueryBuilder {
params: GetDriverStandingsParameter,
stmt: SelectStatement,
}

impl DriverStandingQueryBuilder {
pub fn params(params: GetDriverStandingsParameter) -> Self {
let stmt = Query::select()
.distinct()
.columns(
[
Drivers::DriverId,
Drivers::DriverRef,
Drivers::Number,
Drivers::Code,
Drivers::Forename,
Drivers::Surname,
Drivers::Dob,
Drivers::Nationality,
Drivers::Url,
]
.into_iter()
.map(|c| (Drivers::Table, c).into_column_ref())
.chain(
[
DriverStandings::Points,
DriverStandings::Position,
DriverStandings::PositionText,
DriverStandings::Wins,
]
.into_iter()
.map(|c| (DriverStandings::Table, c).into_column_ref()),
)
.chain(
[Races::Round, Races::Year]
.into_iter()
.map(|c| (Races::Table, c).into_column_ref()),
),
)
.from(Drivers::Table)
.from(DriverStandings::Table)
.from(Races::Table)
.and_where(
Expr::col((DriverStandings::Table, DriverStandings::RaceId))
.equals((Races::Table, Races::RaceId)),
)
.and_where(
Expr::col((DriverStandings::Table, DriverStandings::DriverId))
.equals((Drivers::Table, Drivers::DriverId)),
)
.to_owned();

Self { params, stmt }
}

pub fn build(self) -> Paginated<DriverStandingsModel> {
let page: u64 = self.params.page.unwrap_or_default().0;
let limit: u64 = self.params.limit.unwrap_or_default().0;

self.and_where(|s| {
s.params.position.map(|p| {
Expr::col((DriverStandings::Table, DriverStandings::PositionText))
.eq(Expr::value(*p))
})
})
.and_where(|s| {
s.params
.driver_ref
.as_ref()
.map(|c| Expr::col((Drivers::Table, Drivers::DriverRef)).eq(Expr::value(&**c)))
})
.and_where(|s| {
s.params
.year
.map(|y| Expr::col((Races::Table, Races::Year)).eq(Expr::val(*y)))
})
.and_clause()
.stmt
.paginate(page)
.per_page(limit)
}

fn and_clause(self) -> Self {
if let Some(round) = self.params.round {
return self.and_where(|_| {
Some(Expr::col((Races::Table, Races::Round)).eq(Expr::value(*round)))
});
}

let expr = self.params.year.map_or(
Expr::tuple(
[
Expr::col((Races::Table, Races::Year)),
Expr::col((Races::Table, Races::Round)),
]
.map(Into::into),
)
.in_subquery(
Query::select()
.column(Races::Year)
.expr(Func::max(Expr::col(Races::Round)))
.from(Races::Table)
.group_by_col(Races::Year)
.to_owned(),
),
|year| {
Expr::col((Races::Table, Races::Round)).in_subquery(
Query::select()
.from(Races::Table)
.from(DriverStandings::Table)
.expr(Func::max(Expr::col(Races::Round)))
.and_where(
Expr::col((DriverStandings::Table, DriverStandings::RaceId))
.equals((Races::Table, Races::RaceId)),
)
.and_where(Expr::col((Races::Table, Races::Year)).eq(*year))
.to_owned(),
)
},
);

self.and_where(|_| Some(expr))
}
}

impl SqlBuilder for DriverStandingQueryBuilder {
fn stmt(&mut self) -> &mut SelectStatement {
&mut self.stmt
}
}
1 change: 1 addition & 0 deletions crates/application/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod circuit;
pub mod constructor;
pub mod constructor_standing;
pub mod driver;
pub mod driver_standing;
pub(crate) mod iden;
mod pagination;
pub(crate) mod sql;
29 changes: 29 additions & 0 deletions crates/shared/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub struct Constructors {

#[derive(FromRow, Debug, Serialize)]
pub struct ConstructorStandings {
#[mysql(rename = "constructorId")]
constructor_id: i32,
#[mysql(rename = "constructorRef")]
constructor_ref: String,
name: String,
Expand All @@ -69,3 +71,30 @@ pub struct ConstructorStandings {
year: i32,
round: i32,
}

#[derive(FromRow, Debug, Serialize)]
pub struct DriverStandings {
#[mysql(rename = "driverId")]
driver_id: i32,
#[mysql(rename = "driverRef")]
driver_ref: String,
#[serde(skip_serializing_if = "Option::is_none")]
number: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
code: Option<String>,
forename: String,
surname: String,
#[serde(skip_serializing_if = "Option::is_none")]
dob: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
nationality: Option<String>,
url: String,
points: f32,
position: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[mysql(rename = "positionText")]
position_text: Option<String>,
wins: i32,
year: i32,
round: i32,
}
12 changes: 12 additions & 0 deletions crates/shared/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ pub struct GetConstructorStandingsParameter {
pub round: Option<Round>,
}

#[derive(Debug, Default, FilterValidation, FromForm)]
pub struct GetDriverStandingsParameter {
#[validation(skip)]
pub limit: Option<Limit>,
#[validation(skip)]
pub page: Option<Page>,
pub driver_ref: Option<DriverRef>,
pub position: Option<ConstructorStanding>,
pub year: Option<Year>,
pub round: Option<Round>,
}

impl Default for Page {
fn default() -> Self {
Self(1)
Expand Down

0 comments on commit 1d6354b

Please sign in to comment.