refactor: db availability

This commit is contained in:
Max Hohlfeld 2025-07-07 16:07:57 +02:00
parent 8491491e25
commit dcfb097114
25 changed files with 193 additions and 182 deletions

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO assignment (eventId, availabilityId, function, startTimestamp, endTimestamp)\n VALUES ($1, $2, $3, $4, $5);\n ",
"query": "INSERT INTO assignment (eventId, availabilityId, function, startTimestamp, endTimestamp)\nVALUES ($1, $2, $3, $4, $5);\n",
"describe": {
"columns": [],
"parameters": {
@ -25,5 +25,5 @@
},
"nullable": []
},
"hash": "47bd20303899d5e804f8651c17428ee92f80321d1a5423be52e4f9a275180748"
"hash": "13c282f93a07edc488d1734917ff3d8ac3f81b082932c6502eadbffee18a1087"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availability\n JOIN user_ ON availability.userId = user_.id\n WHERE availability.startTimestamp::date = $1\n AND user_.areaId = $2;\n ",
"query": "SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\nFROM availability\nJOIN\n user_ ON availability.userId = user_.id\nWHERE\n availability.startTimestamp::date = $1\n AND user_.areaId = $2;\n",
"describe": {
"columns": [
{
@ -132,5 +132,5 @@
false
]
},
"hash": "ae18073da63db05840fdd113a2a4727a7e2531cc54ca2bf35ba4f4f86d35a4d3"
"hash": "14fbf5259ce8c783177fbdc7304ce12bd9b31ef0e805427e5e48142ab2189a9f"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availability\n JOIN user_ ON availability.userId = user_.id\n WHERE availability.starttimestamp::date = $1\n AND user_.areaId = $2\n AND availability.startTimestamp <= $3 AND availability.endTimestamp >= $4;\n ",
"query": "SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\nFROM availability\nJOIN\n user_ ON availability.userId = user_.id\nWHERE\n availability.starttimestamp::date = $1\n AND user_.areaId = $2\n AND availability.startTimestamp <= $3\n AND availability.endTimestamp >= $4;\n",
"describe": {
"columns": [
{
@ -134,5 +134,5 @@
false
]
},
"hash": "7b3d623440f6ab8060223289d13687921a5c27317ea27d1b2c960b5e809a7497"
"hash": "1881e09cead57f5bc1888b41d0a52afcf0f97afd335ddc70fe11cb5e7d9e978a"
}

View File

@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO availability (userId, startTimestamp, endTimestamp, comment)\nVALUES ($1, $2, $3, $4);\n",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Timestamptz",
"Timestamptz",
"Text"
]
},
"nullable": []
},
"hash": "4087f8c12d7ea29be931f9205919cc8a35bac096b8fd0a86645dc2bc5b175210"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\n FROM assignment\n WHERE assignment.eventId = $1;\n ",
"query": "SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\nFROM assignment\nWHERE assignment.eventId = $1;\n",
"describe": {
"columns": [
{
@ -53,5 +53,5 @@
false
]
},
"hash": "9315f185d76ae8e25c9bb0273fce388ca559c4494f82d31524f224c6b70000f9"
"hash": "499c8a6a46ea5efb42cdde670f8ddee29ef920f49d7e088bf23064c2eedadc10"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\n FROM assignment\n WHERE assignment.AvailabilityId = $1;\n ",
"query": "SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\nFROM assignment\nWHERE assignment.AvailabilityId = $1;\n",
"describe": {
"columns": [
{
@ -53,5 +53,5 @@
false
]
},
"hash": "8893828aa4509fc81f078b313a88c34d05eb7c5988e4d595768bc7dd47a7892f"
"hash": "500739a566f98cbfd96605d2d5d7d6a0751e87d31fa50f173fc2eb44d0a0a133"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\n FROM assignment\n JOIN availability ON assignment.availabilityId = availability.id\n WHERE assignment.starttimestamp::date >= $1\n AND assignment.starttimestamp::date <= $2\n AND availability.userId = $3\n ORDER BY assignment.starttimestamp;\n ",
"query": "SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\nFROM assignment\nJOIN\n availability ON assignment.availabilityId = availability.id\nWHERE\n assignment.starttimestamp::date >= $1\n AND assignment.starttimestamp::date <= $2\n AND availability.userId = $3\nORDER BY assignment.starttimestamp;\n",
"describe": {
"columns": [
{
@ -55,5 +55,5 @@
false
]
},
"hash": "6788747f70812d6a87833d821c9845f59b4338bc42a40050a68736ba30d937d5"
"hash": "5b5795f479ec4dcc01c15278d4f71e368475972884c13cf9990b279077c619ba"
}

View File

@ -1,48 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\n FROM availability\n WHERE availability.userId = $1\n AND availability.starttimestamp::date >= $2\n AND availability.endtimestamp::date <= $3\n ORDER BY availability.starttimestamp;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "userid",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "starttimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "endtimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "comment",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Int4",
"Date",
"Date"
]
},
"nullable": [
false,
false,
false,
false,
true
]
},
"hash": "66638de4a321ba610206a9f5c237cb17eb5221c1dbe4d4c5e83167b2d2807db4"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\n FROM availability\n WHERE availability.userId = $1\n AND availability.starttimestamp::date = $2;\n ",
"query": "SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\nFROM availability\nWHERE\n availability.userId = $1\n AND availability.starttimestamp::date >= $2\n AND availability.endtimestamp::date <= $3\nORDER BY availability.starttimestamp;\n",
"describe": {
"columns": [
{
@ -32,6 +32,7 @@
"parameters": {
"Left": [
"Int4",
"Date",
"Date"
]
},
@ -43,5 +44,5 @@
true
]
},
"hash": "f90aa1fd76ddf0f667fc77f1cbb02b219aff6d73a3ee1e0afeb9eeea7e194f9d"
"hash": "996b63cfc4e08c874f24286c2467e63fc1c981807dfa25b42a9d52124f3096e6"
}

View File

@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO availability (userId, startTimestamp, endTimestamp, comment)\n VALUES ($1, $2, $3, $4);\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Timestamptz",
"Timestamptz",
"Text"
]
},
"nullable": []
},
"hash": "a55c1e8926f4aa039d8b14a054cb7d4e80387970467bf940c79433c08cb4f879"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\n FROM assignment\n WHERE\n assignment.eventId = $1 AND\n assignment.availabilityId = $2;\n ",
"query": "SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\nFROM assignment\nWHERE\n assignment.eventId = $1\n AND assignment.availabilityId = $2;\n",
"describe": {
"columns": [
{
@ -54,5 +54,5 @@
false
]
},
"hash": "d2014068ac3b84a3682054e13e20ada311b1c36389b60ce8800d52ba4047730c"
"hash": "bdcaf04876e24ac0dad5b9733261f474f05dbeb27a3aecc292ef0d2e982349d2"
}

View File

@ -0,0 +1,47 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\nFROM availability\nWHERE\n availability.userId = $1\n AND availability.starttimestamp::date = $2\nORDER BY availability.starttimestamp;\n",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "userid",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "starttimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "endtimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "comment",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Int4",
"Date"
]
},
"nullable": [
false,
false,
false,
false,
true
]
},
"hash": "ec5dfa3b6bcc1cc70d34287e1405c0a6e2c15d92e6e8f259b4d1e06d93ed55e7"
}

View File

@ -0,0 +1,2 @@
INSERT INTO availability (userId, startTimestamp, endTimestamp, comment)
VALUES ($1, $2, $3, $4);

View File

@ -0,0 +1,22 @@
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment,
user_.name,
user_.email,
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
user_.receiveNotifications
FROM availability
JOIN
user_ ON availability.userId = user_.id
WHERE
availability.startTimestamp::date = $1
AND user_.areaId = $2;

View File

@ -0,0 +1,24 @@
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment,
user_.name,
user_.email,
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
user_.receiveNotifications
FROM availability
JOIN
user_ ON availability.userId = user_.id
WHERE
availability.starttimestamp::date = $1
AND user_.areaId = $2
AND availability.startTimestamp <= $3
AND availability.endTimestamp >= $4;

View File

@ -0,0 +1,11 @@
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment
FROM availability
WHERE
availability.userId = $1
AND availability.starttimestamp::date = $2
ORDER BY availability.starttimestamp;

View File

@ -0,0 +1,12 @@
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment
FROM availability
WHERE
availability.userId = $1
AND availability.starttimestamp::date >= $2
AND availability.endtimestamp::date <= $3
ORDER BY availability.starttimestamp;

View File

@ -1,5 +1,5 @@
use chrono::{NaiveDate, NaiveDateTime};
use sqlx::{PgPool, query};
use sqlx::{PgPool, query, query_file};
use super::{Area, AvailabilityChangeset, Result, Role, User, UserFunction};
@ -19,11 +19,8 @@ impl Availability {
user_id: i32,
changeset: AvailabilityChangeset,
) -> Result<()> {
query!(
r#"
INSERT INTO availability (userId, startTimestamp, endTimestamp, comment)
VALUES ($1, $2, $3, $4);
"#,
query_file!(
"sql/availability/create.sql",
user_id,
changeset.time.0.and_utc(),
changeset.time.1.and_utc(),
@ -35,34 +32,13 @@ impl Availability {
Ok(())
}
pub async fn read_by_date_and_area_including_user(
pub async fn read_all_by_date_and_area_including_user(
pool: &PgPool,
date: NaiveDate,
area_id: i32,
) -> Result<Vec<Availability>> {
let records = query!(
r##"
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment,
user_.name,
user_.email,
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
user_.receiveNotifications
FROM availability
JOIN user_ ON availability.userId = user_.id
WHERE availability.startTimestamp::date = $1
AND user_.areaId = $2;
"##,
let records = query_file!(
"sql/availability/read_all_by_date_and_area_including_user.sql",
date,
area_id
)
@ -97,40 +73,17 @@ impl Availability {
Ok(availabilities)
}
pub async fn read_by_date_time_area_including_user(
pub async fn read_all_by_daterange_and_area_including_user_for_event_planning(
pool: &PgPool,
date: NaiveDate,
time: (NaiveDateTime, NaiveDateTime),
date_range: (NaiveDateTime, NaiveDateTime),
area_id: i32,
) -> Result<Vec<Availability>> {
let records = query!(
r##"
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment,
user_.name,
user_.email,
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
user_.receiveNotifications
FROM availability
JOIN user_ ON availability.userId = user_.id
WHERE availability.starttimestamp::date = $1
AND user_.areaId = $2
AND availability.startTimestamp <= $3 AND availability.endTimestamp >= $4;
"##,
date,
let records = query_file!(
"sql/availability/read_all_by_daterange_and_area_including_user_for_event_planning.sql",
date_range.0.date(),
area_id,
time.0.and_utc(),
time.1.and_utc()
date_range.0.and_utc(),
date_range.1.and_utc()
)
.fetch_all(pool)
.await?;
@ -303,23 +256,13 @@ impl Availability {
Ok(availabilities)
}
pub async fn read_by_user_and_date(
pub async fn read_all_by_user_and_date(
pool: &PgPool,
user_id: i32,
date: &NaiveDate,
) -> Result<Vec<Availability>> {
let records = query!(
r##"
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment
FROM availability
WHERE availability.userId = $1
AND availability.starttimestamp::date = $2;
"##,
let records = query_file!(
"sql/availability/read_all_by_user_and_date.sql",
user_id,
date
)
@ -341,25 +284,13 @@ impl Availability {
Ok(availabilities)
}
pub async fn read_by_user_and_daterange(
pub async fn read_all_by_user_and_daterange(
pool: &PgPool,
user_id: i32,
date_range: (&NaiveDate, &NaiveDate),
) -> Result<Vec<Availability>> {
let records = query!(
r##"
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment
FROM availability
WHERE availability.userId = $1
AND availability.starttimestamp::date >= $2
AND availability.endtimestamp::date <= $3
ORDER BY availability.starttimestamp;
"##,
let records = query_file!(
"sql/availability/read_all_by_user_and_daterange.sql",
user_id,
date_range.0,
date_range.1

View File

@ -27,7 +27,7 @@ impl<'a> AsyncValidate<'a> for AvailabilityChangeset {
context: &'a Self::Context,
) -> Result<(), AsyncValidateError> {
let mut existing_availabilities =
Availability::read_by_user_and_date(context.pool, context.user_id, &self.time.0.date())
Availability::read_all_by_user_and_date(context.pool, context.user_id, &self.time.0.date())
.await?;
start_date_time_lies_before_end_date_time(&self.time.0, &self.time.1)?;

View File

@ -23,6 +23,15 @@
- all fields are as they are on the model, all field must be supplied; no way for "partial updates"
- partial updates for only one or two fields get a special method on the model
- validate using garde and custom context with gives access to database pool
- when sql fits into oneline, leave it inline in code; otherwise put it into separate file and use query_file!
### Naming Scheme for methods
- get single entry -> read / read_by_...(special field)
- get multiple entries -> read_all / read_all_by...(special field)
- create
- update / update_...(single field)
- delete
TODO:

View File

@ -65,7 +65,7 @@ async fn get(
None => None,
};
let availabilities = Availability::read_by_date_and_area_including_user(
let availabilities = Availability::read_all_by_date_and_area_including_user(
pool.get_ref(),
date,
query.area.unwrap_or(user.area_id),
@ -73,7 +73,7 @@ async fn get(
.await?;
let availabilities_from_user =
Availability::read_by_user_and_date(pool.get_ref(), user.id, &date).await?;
Availability::read_all_by_user_and_date(pool.get_ref(), user.id, &date).await?;
//println!("{availabilities_from_user:#?}");
let user_can_create_availability = availabilities_from_user.is_empty()

View File

@ -21,7 +21,7 @@ pub async fn get(
query: web::Query<AvailabilityNewQuery>,
) -> Result<impl Responder, ApplicationError> {
let availabilities_from_user =
Availability::read_by_user_and_date(pool.get_ref(), user.id, &query.date).await?;
Availability::read_all_by_user_and_date(pool.get_ref(), user.id, &query.date).await?;
let slot_suggestions = find_free_date_time_slots(&availabilities_from_user);
let user_can_create_availability =

View File

@ -40,7 +40,7 @@ async fn get(
.await?;
let availabilities =
Availability::read_by_user_and_daterange(pool.get_ref(), user.id, date_range).await?;
Availability::read_all_by_user_and_daterange(pool.get_ref(), user.id, date_range).await?;
let assignments =
Assignment::read_all_by_daterange_and_user(pool.get_ref(), date_range, user.id)

View File

@ -23,7 +23,7 @@ pub async fn get(
}
let availabilities =
Availability::read_by_user_and_date(pool.get_ref(), user.id, &availability.start.date())
Availability::read_all_by_user_and_date(pool.get_ref(), user.id, &availability.start.date())
.await?;
let slot_suggestions = find_free_date_time_slots(&availabilities)

View File

@ -11,13 +11,13 @@ pub async fn generate_availability_assignment_list(
pool: &PgPool,
event: &Event,
) -> Result<Vec<(Availability, AvailabilityAssignmentState)>, ApplicationError> {
let availabilities_in_db = Availability::read_by_date_time_area_including_user(
pool,
event.start.date(),
(event.start, event.end),
event.location.as_ref().unwrap().area_id,
)
.await?;
let availabilities_in_db =
Availability::read_all_by_daterange_and_area_including_user_for_event_planning(
pool,
(event.start, event.end),
event.location.as_ref().unwrap().area_id,
)
.await?;
let mut availabilities = Vec::new();
for availability in availabilities_in_db {