Add timezone config to user

demo-mode
Wes Holland 1 year ago
parent 5c0e250a36
commit 74342dcc82

@ -4,6 +4,7 @@ CREATE TABLE IF NOT EXISTS User (
id INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
role INTEGER NOT NULL,
tz_offset INTEGER NOT NULL,
FOREIGN KEY(role) REFERENCES UserRole(id)
);

@ -8,6 +8,8 @@ use tracing::info;
use chrono::prelude::*;
use crate::db::positive_adjustment::{get_positive_adjustments_target_date_range, DbPositiveAdjustment};
use crate::error::{AppError, QueryExtractor};
use crate::session::SessionUser;
use crate::util::time::tz_offset_to_string;
#[derive(Template)]
#[template(path = "history.html")]
@ -17,6 +19,7 @@ struct HistoryLogTemplate {
start_time: String,
end_date: String,
end_time: String,
time_zone: String,
}
#[derive(Template)]
@ -36,12 +39,15 @@ pub struct DatetimeRangeQueryArgs {
pub end_date: Option<String>,
#[serde(rename = "end-time", alias = "et")]
pub end_time: Option<String>,
#[serde(rename = "timezone-offset", alias = "tz")]
pub time_zone_offset: Option<i32>,
}
pub async fn history_log_handler(
QueryExtractor(query): QueryExtractor<DatetimeRangeQueryArgs>,
HxRequest(hx_request): HxRequest,
State(db): State<SqlitePool>
State(db): State<SqlitePool>,
user: SessionUser
) -> Result<Response, AppError> {
let today = Local::now().naive_local().date();
@ -50,7 +56,8 @@ pub async fn history_log_handler(
let start_time = query.start_time.unwrap_or("00:00:00".to_string());
let end_date = query.end_date.unwrap_or(today.to_string());
let end_time = query.end_time.unwrap_or("11:59:59".to_string());
let timezone = FixedOffset::west_opt(6 * 3600)
let tz_offset = query.time_zone_offset.unwrap_or(user.tz_offset);
let timezone = FixedOffset::east_opt(tz_offset)
.ok_or(anyhow::anyhow!("Invalid timezone"))?;
let naive_start_date = start_date.parse::<NaiveDate>()?;
@ -84,12 +91,14 @@ pub async fn history_log_handler(
Ok(HistoryLogItemFragmentTemplate {items}.into_response())
}
else {
let time_zone = tz_offset_to_string(tz_offset);
Ok(HistoryLogTemplate {
items,
start_date,
start_time,
end_date,
end_time,
time_zone,
}.into_response())
}
}

@ -151,6 +151,7 @@ pub async fn auth_authorized(
name: oauth_user_data.name,
verified_email: oauth_user_data.verified_email,
picture: oauth_user_data.picture,
tz_offset: db_user.tz_offset as i32,
};
// STEP 10 - Save user session data

@ -1,9 +1,12 @@
use std::str::FromStr;
use crate::db::user::{add_user, get_user_by_name, get_user_count, DbUserRole};
use anyhow::{anyhow, Result};
use serde::Deserialize;
use sqlx::SqlitePool;
use tracing::info;
const DEFAULT_TIMEZONE: i64 = -6 * 3600;
#[derive(Debug, Deserialize)]
struct BootstrapData {
users: Vec<BootstrapUser>,
@ -12,7 +15,8 @@ struct BootstrapData {
#[derive(Debug, Deserialize)]
struct BootstrapUser {
name: String,
role: String
role: String,
tz_offset: Option<String>,
}
pub async fn bootstrap_database(db: &SqlitePool) -> Result<()> {
@ -33,8 +37,11 @@ pub async fn bootstrap_database(db: &SqlitePool) -> Result<()> {
if db_needs_users(db).await? {
for user in &data.users {
let role = DbUserRole::try_from_str(&user.role).ok_or(anyhow!("invalid role {}", user.role))?;
let tz = if let Some(tz) = &user.tz_offset {
i64::from_str(&tz).unwrap_or(DEFAULT_TIMEZONE)
} else { DEFAULT_TIMEZONE };
if get_user_by_name(db, &user.name).await?.is_none() {
let new_id = add_user(db, &user.name, role).await?;
let new_id = add_user(db, &user.name, role, tz).await?;
info!("bootstrap new user {}:{} ({})", new_id, user.name, user.role);
}
}

@ -8,35 +8,38 @@ pub struct DbUser {
pub id: i64,
pub name: String,
pub role: i64,
pub tz_offset: i64,
}
pub async fn get_user_by_name(db: &SqlitePool, user: &str) -> Result<Option<DbUser>> {
sqlx::query_as!(
DbUser,
sqlx::query_as::<_, DbUser>(
r#"
SELECT
id,
name,
role
role,
tz_offset
FROM
User
WHERE
name = ?
"#,
user
).fetch_optional(db).await
.map_err(From::from)
"#)
.bind(user)
.fetch_optional(db).await
.map_err(From::from)
}
pub async fn add_user(db: &SqlitePool, name: &str, role: DbUserRole) -> Result<i64> {
pub async fn add_user(db: &SqlitePool, name: &str, role: DbUserRole, tz_offset: i64) -> Result<i64> {
let role = role.value();
let res = sqlx::query!(
let res = sqlx::query(
r#"
INSERT INTO User(name, role)
VALUES (?, ?)
"#,
name, role
).execute(db).await?;
INSERT INTO User(name, role, tz_offset)
VALUES (?, ?, ?)
"#)
.bind(name)
.bind(role)
.bind(tz_offset)
.execute(db).await?;
let new_id = res.last_insert_rowid();

@ -17,6 +17,7 @@ mod auth;
mod static_routes;
mod app;
mod ingest;
mod util;
//NOTE TO FUTURE ME: I'm leaving a bunch of notes about these things as part of the learning
// process. There is a lot of implementation details that are obscured by all these pieces, and it
// can be hard to tell how heavy a line is. Lots of comment in this file and some of the kind of

@ -63,6 +63,7 @@ pub struct SessionUser {
pub name: String,
pub verified_email: bool,
pub picture: String,
pub tz_offset: i32,
}
/// A custom error for the User extractor

@ -0,0 +1 @@
pub mod time;

@ -0,0 +1,19 @@
/// Super naive implementation. Just a quick and dirty to get a string
pub fn tz_offset_to_string(tz_offset_seconds: i32) -> String {
if tz_offset_seconds == 3600 * -6 {
return "Central".to_string();
}
else if tz_offset_seconds == 3600 * -5 {
return "Eastern".to_string();
}
else if tz_offset_seconds == 3600 * -7 {
return "Pacific".to_string();
}
else if tz_offset_seconds < 0 {
return format!("UTC{}", tz_offset_seconds).to_string();
}
format!("UTC+{}", tz_offset_seconds).to_string()
}

@ -15,6 +15,7 @@
<fieldset>
<label for="start-time">Start Time</label>
<input type="time" id="start-time" name="start" value="{{ start_time }}"/>
<small>Timezone {{ time_zone }}</small>
</fieldset>
</div>
@ -26,6 +27,7 @@
<fieldset>
<label for="end-time">End Time</label>
<input type="time" id="end-time" name="end" value="{{ end_time }}"/>
<small>Timezone {{ time_zone }}</small>
</fieldset>
</div>
</form>

Loading…
Cancel
Save

Powered by TurnKey Linux.