|
@@ -10,20 +10,30 @@ pub struct Session {
|
|
|
pub id: Uuid,
|
|
pub id: Uuid,
|
|
|
pub workout: Uuid,
|
|
pub workout: Uuid,
|
|
|
pub user_id: Uuid,
|
|
pub user_id: Uuid,
|
|
|
- #[serde(with = "time::serde::iso8601")]
|
|
|
|
|
- pub created_at: OffsetDateTime,
|
|
|
|
|
|
|
+ #[serde(with = "time::serde::rfc3339")]
|
|
|
|
|
+ pub start_time: OffsetDateTime,
|
|
|
|
|
+ #[serde(with = "time::serde::rfc3339")]
|
|
|
|
|
+ pub end_time: OffsetDateTime,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Session {
|
|
impl Session {
|
|
|
- pub async fn create<'c, E>(executor: E, workout_id: Uuid, user_id: Uuid) -> Result<Session, sqlx::Error>
|
|
|
|
|
|
|
+ pub async fn create<'c, E>(
|
|
|
|
|
+ executor: E,
|
|
|
|
|
+ workout_id: Uuid,
|
|
|
|
|
+ user_id: Uuid,
|
|
|
|
|
+ start_time: OffsetDateTime,
|
|
|
|
|
+ end_time: OffsetDateTime,
|
|
|
|
|
+ ) -> Result<Session, sqlx::Error>
|
|
|
where
|
|
where
|
|
|
E: sqlx::Executor<'c, Database = Postgres>,
|
|
E: sqlx::Executor<'c, Database = Postgres>,
|
|
|
{
|
|
{
|
|
|
sqlx::query_as::<_, Session>(
|
|
sqlx::query_as::<_, Session>(
|
|
|
- "INSERT INTO sessions (workout, user_id) VALUES ($1, $2) RETURNING *",
|
|
|
|
|
|
|
+ "INSERT INTO sessions (workout, user_id, start_time, end_time) VALUES ($1, $2, $3, $4) RETURNING *",
|
|
|
)
|
|
)
|
|
|
.bind(workout_id)
|
|
.bind(workout_id)
|
|
|
.bind(user_id)
|
|
.bind(user_id)
|
|
|
|
|
+ .bind(start_time)
|
|
|
|
|
+ .bind(end_time)
|
|
|
.fetch_one(executor)
|
|
.fetch_one(executor)
|
|
|
.await
|
|
.await
|
|
|
}
|
|
}
|
|
@@ -35,14 +45,50 @@ impl Session {
|
|
|
.await
|
|
.await
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub async fn find_by_user(pool: &PgPool, user_id: Uuid) -> Result<Vec<Session>, sqlx::Error> {
|
|
|
|
|
|
|
+ pub async fn find_by_workout_paginated(
|
|
|
|
|
+ pool: &PgPool,
|
|
|
|
|
+ workout_id: Uuid,
|
|
|
|
|
+ start: Option<OffsetDateTime>,
|
|
|
|
|
+ end: Option<OffsetDateTime>,
|
|
|
|
|
+ limit: i64,
|
|
|
|
|
+ offset: i64,
|
|
|
|
|
+ ) -> Result<Vec<Session>, sqlx::Error> {
|
|
|
sqlx::query_as::<_, Session>(
|
|
sqlx::query_as::<_, Session>(
|
|
|
- "SELECT * FROM sessions WHERE user_id = $1 ORDER BY created_at DESC",
|
|
|
|
|
|
|
+ r#"SELECT * FROM sessions
|
|
|
|
|
+ WHERE workout = $1
|
|
|
|
|
+ AND ($2::timestamptz IS NULL OR start_time >= $2)
|
|
|
|
|
+ AND ($3::timestamptz IS NULL OR start_time <= $3)
|
|
|
|
|
+ ORDER BY start_time DESC
|
|
|
|
|
+ LIMIT $4 OFFSET $5"#,
|
|
|
)
|
|
)
|
|
|
- .bind(user_id)
|
|
|
|
|
|
|
+ .bind(workout_id)
|
|
|
|
|
+ .bind(start)
|
|
|
|
|
+ .bind(end)
|
|
|
|
|
+ .bind(limit)
|
|
|
|
|
+ .bind(offset)
|
|
|
.fetch_all(pool)
|
|
.fetch_all(pool)
|
|
|
.await
|
|
.await
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ pub async fn count_by_workout(
|
|
|
|
|
+ pool: &PgPool,
|
|
|
|
|
+ workout_id: Uuid,
|
|
|
|
|
+ start: Option<OffsetDateTime>,
|
|
|
|
|
+ end: Option<OffsetDateTime>,
|
|
|
|
|
+ ) -> Result<i64, sqlx::Error> {
|
|
|
|
|
+ let row: (i64,) = sqlx::query_as(
|
|
|
|
|
+ r#"SELECT COUNT(*) FROM sessions
|
|
|
|
|
+ WHERE workout = $1
|
|
|
|
|
+ AND ($2::timestamptz IS NULL OR start_time >= $2)
|
|
|
|
|
+ AND ($3::timestamptz IS NULL OR start_time <= $3)"#,
|
|
|
|
|
+ )
|
|
|
|
|
+ .bind(workout_id)
|
|
|
|
|
+ .bind(start)
|
|
|
|
|
+ .bind(end)
|
|
|
|
|
+ .fetch_one(pool)
|
|
|
|
|
+ .await?;
|
|
|
|
|
+ Ok(row.0)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[derive(sqlx::FromRow, Serialize, Clone)]
|
|
#[derive(sqlx::FromRow, Serialize, Clone)]
|
|
@@ -54,11 +100,6 @@ pub struct SessionExercise {
|
|
|
pub notes: Option<String>,
|
|
pub notes: Option<String>,
|
|
|
pub exercise_type: ExerciseType,
|
|
pub exercise_type: ExerciseType,
|
|
|
pub position: i32,
|
|
pub position: i32,
|
|
|
- pub sets: Option<i32>,
|
|
|
|
|
- pub reps: Option<i32>,
|
|
|
|
|
- pub weight: Option<f64>,
|
|
|
|
|
- pub duration_seconds: Option<i32>,
|
|
|
|
|
- pub distance_meters: Option<f64>,
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl SessionExercise {
|
|
impl SessionExercise {
|
|
@@ -77,19 +118,12 @@ impl SessionExercise {
|
|
|
notes: Option<&str>,
|
|
notes: Option<&str>,
|
|
|
exercise_type: ExerciseType,
|
|
exercise_type: ExerciseType,
|
|
|
position: i32,
|
|
position: i32,
|
|
|
- sets: Option<i32>,
|
|
|
|
|
- reps: Option<i32>,
|
|
|
|
|
- weight: Option<f64>,
|
|
|
|
|
- duration_seconds: Option<i32>,
|
|
|
|
|
- distance_meters: Option<f64>,
|
|
|
|
|
) -> Result<SessionExercise, sqlx::Error>
|
|
) -> Result<SessionExercise, sqlx::Error>
|
|
|
where
|
|
where
|
|
|
E: sqlx::Executor<'c, Database = Postgres>,
|
|
E: sqlx::Executor<'c, Database = Postgres>,
|
|
|
{
|
|
{
|
|
|
sqlx::query_as::<_, SessionExercise>(
|
|
sqlx::query_as::<_, SessionExercise>(
|
|
|
- r#"INSERT INTO session_exercises
|
|
|
|
|
- (session, exercise, name, notes, exercise_type, position, sets, reps, weight, duration_seconds, distance_meters)
|
|
|
|
|
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *"#,
|
|
|
|
|
|
|
+ "INSERT INTO session_exercises (session, exercise, name, notes, exercise_type, position) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",
|
|
|
)
|
|
)
|
|
|
.bind(session_id)
|
|
.bind(session_id)
|
|
|
.bind(exercise_id)
|
|
.bind(exercise_id)
|
|
@@ -97,11 +131,51 @@ impl SessionExercise {
|
|
|
.bind(notes)
|
|
.bind(notes)
|
|
|
.bind(exercise_type)
|
|
.bind(exercise_type)
|
|
|
.bind(position)
|
|
.bind(position)
|
|
|
- .bind(sets)
|
|
|
|
|
|
|
+ .fetch_one(executor)
|
|
|
|
|
+ .await
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(sqlx::FromRow, Serialize, Clone)]
|
|
|
|
|
+pub struct SessionSet {
|
|
|
|
|
+ pub id: Uuid,
|
|
|
|
|
+ pub session_exercise: Uuid,
|
|
|
|
|
+ pub reps: Option<i32>,
|
|
|
|
|
+ pub weight: Option<f64>,
|
|
|
|
|
+ pub duration_seconds: Option<i32>,
|
|
|
|
|
+ pub distance_meters: Option<f64>,
|
|
|
|
|
+ pub position: i32,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl SessionSet {
|
|
|
|
|
+ pub async fn find_by_session_exercise(pool: &PgPool, session_exercise_id: Uuid) -> Result<Vec<SessionSet>, sqlx::Error> {
|
|
|
|
|
+ sqlx::query_as::<_, SessionSet>("SELECT * FROM session_sets WHERE session_exercise = $1 ORDER BY position")
|
|
|
|
|
+ .bind(session_exercise_id)
|
|
|
|
|
+ .fetch_all(pool)
|
|
|
|
|
+ .await
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub async fn create<'c, E>(
|
|
|
|
|
+ executor: E,
|
|
|
|
|
+ session_exercise_id: Uuid,
|
|
|
|
|
+ reps: Option<i32>,
|
|
|
|
|
+ weight: Option<f64>,
|
|
|
|
|
+ duration_seconds: Option<i32>,
|
|
|
|
|
+ distance_meters: Option<f64>,
|
|
|
|
|
+ position: i32,
|
|
|
|
|
+ ) -> Result<SessionSet, sqlx::Error>
|
|
|
|
|
+ where
|
|
|
|
|
+ E: sqlx::Executor<'c, Database = Postgres>,
|
|
|
|
|
+ {
|
|
|
|
|
+ sqlx::query_as::<_, SessionSet>(
|
|
|
|
|
+ "INSERT INTO session_sets (session_exercise, reps, weight, duration_seconds, distance_meters, position) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",
|
|
|
|
|
+ )
|
|
|
|
|
+ .bind(session_exercise_id)
|
|
|
.bind(reps)
|
|
.bind(reps)
|
|
|
.bind(weight)
|
|
.bind(weight)
|
|
|
.bind(duration_seconds)
|
|
.bind(duration_seconds)
|
|
|
.bind(distance_meters)
|
|
.bind(distance_meters)
|
|
|
|
|
+ .bind(position)
|
|
|
.fetch_one(executor)
|
|
.fetch_one(executor)
|
|
|
.await
|
|
.await
|
|
|
}
|
|
}
|