Enhance application configuration and functionality
- Updated `.env` file to include `DB_MAX_CONNECTIONS` and `ENVIRONMENT` variables. - Modified `Cargo.toml` to add `thiserror` and `time` dependencies, and updated `sqlx` features. - Refactored `main.rs` to implement periodic tasks for checking future shifts and printing the current hospital. - Expanded routing in `routes.rs` to include a new endpoint for current hospital status. - Improved database initialization in `init.rs` to conditionally drop tables based on the environment. - Enhanced error handling in database operations and added logging for better traceability. - Updated hospital management logic in `hospital.rs` to fetch and store shifts with improved timestamp logging.
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
use core::panic;
|
||||
use std::io;
|
||||
use chrono::{offset::LocalResult, DateTime, Datelike, Duration, Local, TimeZone};
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Hospital {
|
||||
id: u32,
|
||||
id: i32,
|
||||
name: String,
|
||||
}
|
||||
|
||||
@@ -12,30 +17,43 @@ struct Hospital {
|
||||
// to: DateTime<chrono_tz::Tz>,
|
||||
// }
|
||||
|
||||
pub fn create_hospital_list() {
|
||||
// Helper function to print with timestamp
|
||||
fn log_with_timestamp(message: &str) {
|
||||
let now = Local::now();
|
||||
println!("[{}] {}", now.format("%Y-%m-%d %H:%M:%S"), message);
|
||||
}
|
||||
|
||||
// create the hospitals
|
||||
let hospitals: [Hospital; 2] = [
|
||||
Hospital {
|
||||
id: 1,
|
||||
name: "Centre Hospitalier".to_string(),
|
||||
},
|
||||
Hospital {
|
||||
id: 2,
|
||||
name: "Hopital Kirchberg".to_string(),
|
||||
},
|
||||
];
|
||||
// Static variable to store the last printed hospital ID
|
||||
static LAST_HOSPITAL_ID: Mutex<Option<i32>> = Mutex::new(None);
|
||||
|
||||
pub async fn create_hospital_list(pool: Arc<PgPool>) -> Result<(), sqlx::Error> {
|
||||
// Fetch hospitals from the database
|
||||
let hospitals = sqlx::query_as!(
|
||||
Hospital,
|
||||
r#"
|
||||
SELECT id, name FROM hospitals ORDER BY id
|
||||
"#
|
||||
)
|
||||
.fetch_all(&*pool)
|
||||
.await?;
|
||||
|
||||
if hospitals.is_empty() {
|
||||
log_with_timestamp("No hospitals found in the database.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// let mut start: DateTime<Local> = Local.with_ymd_and_hms(2025, 1, 6, 8, 0, 0).unwrap(); // set date for testing
|
||||
let mut start: DateTime<Local> = Local::now();
|
||||
let mut end: DateTime<Local> = get_end_of_shift(start);
|
||||
|
||||
println!("It is now {} and the current Shift is ending on {}", start.format("%A %d/%m/%Y %H:%M"), end.format("%A %d/%m/%Y %H:%M"));
|
||||
println!("Whitch Hospital is actualy on duty?");
|
||||
log_with_timestamp(&format!("It is now {} and the current Shift is ending on {}",
|
||||
start.format("%A %d/%m/%Y %H:%M"),
|
||||
end.format("%A %d/%m/%Y %H:%M")));
|
||||
log_with_timestamp("Which Hospital is currently on duty?");
|
||||
|
||||
// print out the hospitals
|
||||
for hospital in &hospitals {
|
||||
println!("{}) {}", hospital.id, hospital.name)
|
||||
log_with_timestamp(&format!("{}) {}", hospital.id, hospital.name));
|
||||
}
|
||||
|
||||
let mut hospital_id_input = String::new();
|
||||
@@ -46,26 +64,249 @@ pub fn create_hospital_list() {
|
||||
let hospital_id: i32 = match hospital_id_input.parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("Please enter a valid number!");
|
||||
return;
|
||||
log_with_timestamp("Please enter a valid number!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let mut hospital_index: usize = (hospital_id - 1) as usize;
|
||||
|
||||
// Find the index of the selected hospital
|
||||
let mut hospital_index: usize = 0;
|
||||
let mut found = false;
|
||||
for (i, hospital) in hospitals.iter().enumerate() {
|
||||
if hospital.id == hospital_id {
|
||||
hospital_index = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
log_with_timestamp(&format!("Hospital with ID {} not found.", hospital_id));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for _ in 1..=100 {
|
||||
|
||||
// Print the current hospital on duty
|
||||
println!("From {} to {} is {} on duty.", start.format("%A %d/%m/%Y %H:%M"), end.format("%A %d/%m/%Y %H:%M"), hospitals[hospital_index].name );
|
||||
log_with_timestamp("Generating and storing shifts in the database...");
|
||||
|
||||
// Clear existing shifts
|
||||
sqlx::query("DELETE FROM shifts")
|
||||
.execute(&*pool)
|
||||
.await?;
|
||||
|
||||
// Generate and store 100 shifts
|
||||
for i in 1..=100 {
|
||||
// Format the timestamps as strings for database storage
|
||||
let start_str = start.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||
let end_str = end.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||
|
||||
// Store the current shift in the database
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO shifts (hospital_id, start_time, end_time)
|
||||
VALUES ($1, $2::timestamp with time zone, $3::timestamp with time zone)
|
||||
"#,
|
||||
)
|
||||
.bind(hospitals[hospital_index].id)
|
||||
.bind(start_str)
|
||||
.bind(end_str)
|
||||
.execute(&*pool)
|
||||
.await?;
|
||||
|
||||
// Print progress only every 10 shifts
|
||||
if i % 10 == 0 {
|
||||
log_with_timestamp(&format!("Stored {} shifts...", i));
|
||||
}
|
||||
|
||||
// new shift
|
||||
start = end;
|
||||
end = get_end_of_shift(start);
|
||||
|
||||
// change hospital
|
||||
hospital_index = if hospital_index == 0 { 1 } else { 0 }
|
||||
hospital_index = if hospital_index == 0 { 1 } else { 0 };
|
||||
|
||||
// If we only have one hospital, don't switch
|
||||
if hospitals.len() == 1 {
|
||||
hospital_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
log_with_timestamp("Successfully stored 100 shifts in the database.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn check_and_create_future_shifts(pool: Arc<PgPool>) -> Result<(), sqlx::Error> {
|
||||
// Get the current time
|
||||
let now = Local::now();
|
||||
|
||||
// Calculate the date two months from now
|
||||
let two_months_from_now = now + Duration::days(60);
|
||||
|
||||
// Convert chrono DateTime to time OffsetDateTime for SQLx
|
||||
let now_offset = OffsetDateTime::from_unix_timestamp(now.timestamp())
|
||||
.expect("Failed to convert to OffsetDateTime");
|
||||
let two_months_offset = OffsetDateTime::from_unix_timestamp(two_months_from_now.timestamp())
|
||||
.expect("Failed to convert to OffsetDateTime");
|
||||
|
||||
// Check if there are any shifts in the next two months
|
||||
let future_shifts_count = sqlx::query_scalar::<_, i64>(
|
||||
r#"
|
||||
SELECT COUNT(*) FROM shifts
|
||||
WHERE start_time > $1 AND start_time < $2
|
||||
"#,
|
||||
)
|
||||
.bind(now_offset)
|
||||
.bind(two_months_offset)
|
||||
.fetch_one(&*pool)
|
||||
.await?;
|
||||
|
||||
// If there are no future shifts, create them
|
||||
if future_shifts_count == 0 {
|
||||
log_with_timestamp("No future shifts found. Creating shifts for the next two months...");
|
||||
|
||||
// Get the current hospital on duty
|
||||
let current_hospital = sqlx::query_as!(
|
||||
Hospital,
|
||||
r#"
|
||||
SELECT h.id, h.name
|
||||
FROM hospitals h
|
||||
JOIN shifts s ON h.id = s.hospital_id
|
||||
WHERE s.end_time > $1
|
||||
ORDER BY s.end_time ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
now_offset
|
||||
)
|
||||
.fetch_optional(&*pool)
|
||||
.await?;
|
||||
|
||||
// If no current hospital is found, get the first hospital from the database
|
||||
let hospital_id = match current_hospital {
|
||||
Some(hospital) => hospital.id,
|
||||
None => {
|
||||
let first_hospital = sqlx::query_as!(
|
||||
Hospital,
|
||||
r#"
|
||||
SELECT id, name FROM hospitals ORDER BY id LIMIT 1
|
||||
"#
|
||||
)
|
||||
.fetch_optional(&*pool)
|
||||
.await?
|
||||
.ok_or_else(|| sqlx::Error::RowNotFound)?;
|
||||
|
||||
first_hospital.id
|
||||
}
|
||||
};
|
||||
|
||||
// Find the index of the selected hospital
|
||||
let hospitals = sqlx::query_as!(
|
||||
Hospital,
|
||||
r#"
|
||||
SELECT id, name FROM hospitals ORDER BY id
|
||||
"#
|
||||
)
|
||||
.fetch_all(&*pool)
|
||||
.await?;
|
||||
|
||||
let mut hospital_index = 0;
|
||||
for (i, hospital) in hospitals.iter().enumerate() {
|
||||
if hospital.id == hospital_id {
|
||||
hospital_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate shifts starting from now
|
||||
let mut start = now;
|
||||
let mut end = get_end_of_shift(start);
|
||||
|
||||
// Generate shifts for the next two months (approximately 60 days)
|
||||
for _ in 1..=120 {
|
||||
// Convert chrono DateTime to time OffsetDateTime for SQLx
|
||||
let start_offset = OffsetDateTime::from_unix_timestamp(start.timestamp())
|
||||
.expect("Failed to convert to OffsetDateTime");
|
||||
let end_offset = OffsetDateTime::from_unix_timestamp(end.timestamp())
|
||||
.expect("Failed to convert to OffsetDateTime");
|
||||
|
||||
// Store the current shift in the database
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO shifts (hospital_id, start_time, end_time)
|
||||
VALUES ($1, $2, $3)
|
||||
"#,
|
||||
)
|
||||
.bind(hospitals[hospital_index].id)
|
||||
.bind(start_offset)
|
||||
.bind(end_offset)
|
||||
.execute(&*pool)
|
||||
.await?;
|
||||
|
||||
// new shift
|
||||
start = end;
|
||||
end = get_end_of_shift(start);
|
||||
|
||||
// change hospital
|
||||
hospital_index = if hospital_index == 0 { 1 } else { 0 };
|
||||
|
||||
// If we only have one hospital, don't switch
|
||||
if hospitals.len() == 1 {
|
||||
hospital_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
log_with_timestamp("Successfully created future shifts for the next two months.");
|
||||
} else {
|
||||
log_with_timestamp(&format!("Found {} future shifts. No need to create more.", future_shifts_count));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn print_current_hospital(pool: Arc<PgPool>) -> Result<(), sqlx::Error> {
|
||||
// Get the current time
|
||||
let now = Local::now();
|
||||
|
||||
// Convert chrono DateTime to time OffsetDateTime for SQLx
|
||||
let now_offset = OffsetDateTime::from_unix_timestamp(now.timestamp())
|
||||
.expect("Failed to convert to OffsetDateTime");
|
||||
|
||||
// Get the current hospital on duty
|
||||
let current_hospital = sqlx::query_as!(
|
||||
Hospital,
|
||||
r#"
|
||||
SELECT h.id, h.name
|
||||
FROM hospitals h
|
||||
JOIN shifts s ON h.id = s.hospital_id
|
||||
WHERE s.start_time <= $1 AND s.end_time > $1
|
||||
ORDER BY s.end_time ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
now_offset
|
||||
)
|
||||
.fetch_optional(&*pool)
|
||||
.await?;
|
||||
|
||||
// Get the last printed hospital ID
|
||||
let mut last_id = LAST_HOSPITAL_ID.lock().unwrap();
|
||||
|
||||
// Check if the hospital has changed
|
||||
let current_id = current_hospital.as_ref().map(|h| h.id);
|
||||
let has_changed = *last_id != current_id;
|
||||
|
||||
// Only print if the hospital has changed
|
||||
if has_changed {
|
||||
match ¤t_hospital {
|
||||
Some(hospital) => {
|
||||
log_with_timestamp(&format!("Current hospital on duty: {} (ID: {})", hospital.name, hospital.id));
|
||||
},
|
||||
None => {
|
||||
log_with_timestamp("No hospital currently on duty.");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the last printed hospital ID
|
||||
*last_id = current_id;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_end_of_shift(start: DateTime<Local>) -> DateTime<Local> {
|
||||
|
||||
Reference in New Issue
Block a user