Update database schema
Also bump major version to match schema version
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1584,7 +1584,7 @@ checksum = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stl"
|
name = "stl"
|
||||||
version = "0.2.0"
|
version = "2.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "stl"
|
name = "stl"
|
||||||
description = "studielogg aka scuffed toggl"
|
description = "studielogg aka scuffed toggl"
|
||||||
version = "0.2.0"
|
version = "2.0.0"
|
||||||
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|||||||
36
src/database/migrations/mod.rs
Normal file
36
src/database/migrations/mod.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
pub mod v1_to_v2;
|
||||||
|
|
||||||
|
use crate::database::unversioned::global::schema_version;
|
||||||
|
use duplicate::duplicate;
|
||||||
|
use sled::Db;
|
||||||
|
|
||||||
|
pub fn migrate(db: &mut Db, from: schema_version::V, to: schema_version::V) -> Result<(), MigrationError> {
|
||||||
|
for current in from..to {
|
||||||
|
let next = current + 1;
|
||||||
|
|
||||||
|
println!("Will migrate from {} to {}", current, next);
|
||||||
|
match (current, next) {
|
||||||
|
(1, 2) => v1_to_v2::migrate(db)?,
|
||||||
|
_ => panic!("No valid migration from version {} to version {}", current, next),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MigrationError {
|
||||||
|
Serde(bincode::Error),
|
||||||
|
Sled(sled::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[duplicate(
|
||||||
|
Variant Error;
|
||||||
|
[ Sled ] [ sled::Error ];
|
||||||
|
[ Serde ] [ bincode::Error ];
|
||||||
|
)]
|
||||||
|
impl From<Error> for MigrationError {
|
||||||
|
fn from(e: Error) -> MigrationError {
|
||||||
|
MigrationError::Variant(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/database/migrations/v1_to_v2.rs
Normal file
84
src/database/migrations/v1_to_v2.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use crate::database::v1;
|
||||||
|
use crate::database::v2;
|
||||||
|
use super::MigrationError;
|
||||||
|
use bincode::{serialize, deserialize};
|
||||||
|
use sled::Db;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
/// Migrate from database version 1 to version 2
|
||||||
|
pub fn migrate(db: &mut Db) -> Result<(), MigrationError> {
|
||||||
|
|
||||||
|
{ // Migrate sessions
|
||||||
|
|
||||||
|
// Open old & new trees
|
||||||
|
let v1_past_sessions = db.open_tree(v1::trees::past_sessions::NAME)?;
|
||||||
|
let v2_sessions = db.open_tree(v2::trees::sessions::NAME)?;
|
||||||
|
|
||||||
|
// Iterate over old tree
|
||||||
|
for r in v1_past_sessions.iter() {
|
||||||
|
let (k, v) = r?;
|
||||||
|
|
||||||
|
// Deserialize old value
|
||||||
|
let v1::trees::past_sessions::V {
|
||||||
|
category,
|
||||||
|
started,
|
||||||
|
ended
|
||||||
|
} = deserialize(&v)?;
|
||||||
|
|
||||||
|
// Convert to new value
|
||||||
|
let v2_value = v2::trees::sessions::V {
|
||||||
|
category,
|
||||||
|
started: DateTime::<Utc>::from_utc(started, Utc).into(),
|
||||||
|
ended: DateTime::<Utc>::from_utc(ended, Utc).into(),
|
||||||
|
deleted: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert into new tree
|
||||||
|
v2_sessions.insert(k, serialize(&v2_value)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old tree
|
||||||
|
v1_past_sessions.clear()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Migrate categories
|
||||||
|
// Open the old tree, and a TMP tree since the old tree name hasn't changed
|
||||||
|
let categories = db.open_tree(v1::trees::categories::NAME)?;
|
||||||
|
let v2_categories_tmp = db.open_tree("TEMP")?;
|
||||||
|
|
||||||
|
// Iterate over old tree
|
||||||
|
for r in categories.iter() {
|
||||||
|
let (k, v) = r?;
|
||||||
|
|
||||||
|
// Deserialize old value
|
||||||
|
let v1::trees::categories::V {
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
started,
|
||||||
|
} = deserialize(&v)?;
|
||||||
|
|
||||||
|
// Convert to new value
|
||||||
|
let v2_value = v2::trees::categories::V {
|
||||||
|
name,
|
||||||
|
description: None,
|
||||||
|
color,
|
||||||
|
started: started.map(|ndt| DateTime::<Utc>::from_utc(ndt, Utc).into()),
|
||||||
|
parent: None,
|
||||||
|
deleted: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert into temporary tree
|
||||||
|
v2_categories_tmp.insert(k, serialize(&v2_value)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data from temp-tree back into old tree
|
||||||
|
categories.clear()?;
|
||||||
|
for r in v2_categories_tmp.iter() {
|
||||||
|
let (k, v) = r?;
|
||||||
|
categories.insert(k ,v)?;
|
||||||
|
}
|
||||||
|
v2_categories_tmp.clear()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -1,5 +1,8 @@
|
|||||||
pub mod unversioned;
|
pub mod unversioned;
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
|
pub mod v2;
|
||||||
|
|
||||||
pub const SCHEMA_VERSION: unversioned::global::schema_version::V = 1;
|
pub mod migrations;
|
||||||
pub use self::v1 as latest;
|
|
||||||
|
pub const SCHEMA_VERSION: unversioned::global::schema_version::V = 2;
|
||||||
|
pub use v2 as latest;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(dead_code)] // old schema, not used anymore
|
||||||
|
|
||||||
pub(self) use chrono::NaiveDateTime;
|
pub(self) use chrono::NaiveDateTime;
|
||||||
pub(self) use serde_derive::{Deserialize, Serialize};
|
pub(self) use serde_derive::{Deserialize, Serialize};
|
||||||
pub(self) use uuid::Uuid;
|
pub(self) use uuid::Uuid;
|
||||||
@ -18,7 +20,7 @@ pub mod trees {
|
|||||||
/// The name of the category
|
/// The name of the category
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
/// The color of the button in the rendered view
|
/// The HTML color of the category in the rendered view
|
||||||
pub color: String,
|
pub color: String,
|
||||||
|
|
||||||
/// If the session is not active, this will be None
|
/// If the session is not active, this will be None
|
||||||
|
|||||||
64
src/database/v2.rs
Normal file
64
src/database/v2.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
pub(self) use chrono::{DateTime, Local};
|
||||||
|
pub(self) use serde_derive::{Deserialize, Serialize};
|
||||||
|
pub(self) use uuid::Uuid;
|
||||||
|
|
||||||
|
/// Stuff in the default namespace
|
||||||
|
pub mod global {}
|
||||||
|
|
||||||
|
pub mod trees {
|
||||||
|
pub mod categories {
|
||||||
|
use super::super::*;
|
||||||
|
|
||||||
|
pub const NAME: &str = "CATEGORIES";
|
||||||
|
|
||||||
|
pub type K = Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct V {
|
||||||
|
/// The name of the category
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// The description of the category
|
||||||
|
pub description: Option<String>,
|
||||||
|
|
||||||
|
/// The HTML color of the category in the rendered view
|
||||||
|
pub color: String,
|
||||||
|
|
||||||
|
/// If the session is not active, this will be None
|
||||||
|
pub started: Option<DateTime<Local>>,
|
||||||
|
|
||||||
|
/// The parent category of this category
|
||||||
|
/// If none, the category has no parent
|
||||||
|
pub parent: Option<K>,
|
||||||
|
|
||||||
|
// FIXME: this field is currently not used
|
||||||
|
/// Whether the item has been "deleted", e.g. it shoudn't be shown in the view
|
||||||
|
pub deleted: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod sessions {
|
||||||
|
use super::super::*;
|
||||||
|
|
||||||
|
pub const NAME: &str = "SESSIONS";
|
||||||
|
|
||||||
|
pub type K = Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct V {
|
||||||
|
/// The UUID of the category to which this session belongs
|
||||||
|
pub category: trees::categories::K,
|
||||||
|
|
||||||
|
/// The time when this session was started
|
||||||
|
pub started: DateTime<Local>,
|
||||||
|
|
||||||
|
/// The time when this session was ended
|
||||||
|
pub ended: DateTime<Local>,
|
||||||
|
|
||||||
|
// FIXME: this field is currently not used
|
||||||
|
/// Whether the item has been "deleted", e.g. it shoudn't be shown in the view
|
||||||
|
pub deleted: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::{DateTime, Local};
|
||||||
use handlebars::handlebars_helper;
|
use handlebars::handlebars_helper;
|
||||||
use rocket_contrib::templates::Engines;
|
use rocket_contrib::templates::Engines;
|
||||||
|
|
||||||
pub fn register_helpers(engines: &mut Engines) {
|
pub fn register_helpers(engines: &mut Engines) {
|
||||||
handlebars_helper!(pretty_datetime: |dt: str| {
|
handlebars_helper!(pretty_datetime: |dt: str| {
|
||||||
let dt: NaiveDateTime = dt.parse().unwrap();
|
let dt: DateTime<Local> = dt.parse().unwrap();
|
||||||
format!("{}", dt.format("%Y-%m-%d %H:%M"))
|
format!("{}", dt.format("%Y-%m-%d %H:%M"))
|
||||||
});
|
});
|
||||||
engines
|
engines
|
||||||
|
|||||||
@ -5,7 +5,9 @@ mod routes;
|
|||||||
mod status_json;
|
mod status_json;
|
||||||
|
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
use database::{unversioned::global::schema_version, SCHEMA_VERSION};
|
use crate::database::SCHEMA_VERSION;
|
||||||
|
use crate::database::unversioned::global::schema_version;
|
||||||
|
use crate::database::migrations::migrate;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use rocket_contrib::serve::StaticFiles;
|
use rocket_contrib::serve::StaticFiles;
|
||||||
use rocket_contrib::templates::Template;
|
use rocket_contrib::templates::Template;
|
||||||
@ -16,7 +18,7 @@ fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
let db_path = env::var("DB_PATH").expect("DB_PATH not set");
|
let db_path = env::var("DB_PATH").expect("DB_PATH not set");
|
||||||
|
|
||||||
let sled = sled::open(db_path)?;
|
let mut sled = sled::open(db_path)?;
|
||||||
match sled.insert(
|
match sled.insert(
|
||||||
serialize(schema_version::K).unwrap(),
|
serialize(schema_version::K).unwrap(),
|
||||||
serialize(&SCHEMA_VERSION).unwrap(),
|
serialize(&SCHEMA_VERSION).unwrap(),
|
||||||
@ -27,6 +29,9 @@ fn main() -> io::Result<()> {
|
|||||||
"Schema version: {}, previously: {}",
|
"Schema version: {}, previously: {}",
|
||||||
SCHEMA_VERSION, prev_schema_version
|
SCHEMA_VERSION, prev_schema_version
|
||||||
);
|
);
|
||||||
|
|
||||||
|
migrate(&mut sled, prev_schema_version, SCHEMA_VERSION)
|
||||||
|
.expect("Migration failed")
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("Schema version: {}, previously: None", SCHEMA_VERSION);
|
println!("Schema version: {}, previously: None", SCHEMA_VERSION);
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use crate::database::latest::trees::{categories, past_sessions};
|
use crate::database::latest::trees::{categories, sessions};
|
||||||
use crate::routes::pages;
|
use crate::routes::pages;
|
||||||
use crate::status_json::StatusJson;
|
use crate::status_json::StatusJson;
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::{NaiveDateTime, TimeZone, Duration, Local};
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::request::{Form, FromForm};
|
use rocket::request::{Form, FromForm};
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
@ -29,8 +28,11 @@ pub fn create_category(
|
|||||||
serialize(&uuid::Uuid::new_v4())?,
|
serialize(&uuid::Uuid::new_v4())?,
|
||||||
serialize(&categories::V {
|
serialize(&categories::V {
|
||||||
name: category.name,
|
name: category.name,
|
||||||
|
description: None,
|
||||||
color: category.color,
|
color: category.color,
|
||||||
started: None,
|
started: None,
|
||||||
|
parent: None,
|
||||||
|
deleted: false,
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -68,8 +70,16 @@ pub fn bump_session(
|
|||||||
let mut category: categories::V = deserialize(&data).unwrap();
|
let mut category: categories::V = deserialize(&data).unwrap();
|
||||||
match category.started.as_mut() {
|
match category.started.as_mut() {
|
||||||
Some(started) => {
|
Some(started) => {
|
||||||
*started -= duration;
|
if let Some(new_started) = started.checked_sub_signed(duration) {
|
||||||
tx_categories.insert(&category_uuid_s, serialize(&category).unwrap())?;
|
*started = new_started;
|
||||||
|
tx_categories.insert(&category_uuid_s, serialize(&category).unwrap())?;
|
||||||
|
} else {
|
||||||
|
return Ok(Err(StatusJson::new(
|
||||||
|
Status::BadRequest,
|
||||||
|
"Duration subtract resulted in overflow",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Ok(Redirect::to(uri!(pages::index))))
|
Ok(Ok(Redirect::to(uri!(pages::index))))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -92,15 +102,15 @@ pub fn toggle_category_session(
|
|||||||
let category_uuid_s = sled::IVec::from(serialize(&category_uuid.into_inner())?);
|
let category_uuid_s = sled::IVec::from(serialize(&category_uuid.into_inner())?);
|
||||||
|
|
||||||
let categories_tree = db.open_tree(categories::NAME)?;
|
let categories_tree = db.open_tree(categories::NAME)?;
|
||||||
let past_sessions_tree = db.open_tree(past_sessions::NAME)?;
|
let sessions_tree = db.open_tree(sessions::NAME)?;
|
||||||
|
|
||||||
Ok((&categories_tree, &past_sessions_tree).transaction(
|
Ok((&categories_tree, &sessions_tree).transaction(
|
||||||
|(tx_categories, tx_past_sessions)| {
|
|(tx_categories, tx_sessions)| {
|
||||||
match tx_categories.get(&category_uuid_s)? {
|
match tx_categories.get(&category_uuid_s)? {
|
||||||
None => return Ok(Err(Status::NotFound)),
|
None => return Ok(Err(Status::NotFound)),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let mut category: categories::V = deserialize(&data).unwrap();
|
let mut category: categories::V = deserialize(&data).unwrap();
|
||||||
let now = Utc::now().naive_utc();
|
let now = Local::now();
|
||||||
|
|
||||||
match (set_active, category.started.take()) {
|
match (set_active, category.started.take()) {
|
||||||
(false, Some(started)) => {
|
(false, Some(started)) => {
|
||||||
@ -108,13 +118,14 @@ pub fn toggle_category_session(
|
|||||||
let duration = now - started;
|
let duration = now - started;
|
||||||
if duration > Duration::minutes(5) {
|
if duration > Duration::minutes(5) {
|
||||||
let session_uuid = serialize(&uuid::Uuid::new_v4()).unwrap();
|
let session_uuid = serialize(&uuid::Uuid::new_v4()).unwrap();
|
||||||
let past_session = past_sessions::V {
|
let session = sessions::V {
|
||||||
category: category_uuid.into_inner(),
|
category: category_uuid.into_inner(),
|
||||||
started,
|
started,
|
||||||
ended: now,
|
ended: now,
|
||||||
|
deleted: category.deleted,
|
||||||
};
|
};
|
||||||
tx_past_sessions
|
tx_sessions
|
||||||
.insert(session_uuid, serialize(&past_session).unwrap())?;
|
.insert(session_uuid, serialize(&session).unwrap())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(true, None) => {
|
(true, None) => {
|
||||||
@ -140,6 +151,7 @@ pub struct EditSession {
|
|||||||
category: Uuid,
|
category: Uuid,
|
||||||
started: String,
|
started: String,
|
||||||
ended: String,
|
ended: String,
|
||||||
|
deleted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/session/<session_uuid>/edit", data = "<session>")]
|
#[post("/session/<session_uuid>/edit", data = "<session>")]
|
||||||
@ -150,12 +162,11 @@ pub fn edit_session(
|
|||||||
) -> Result<Redirect, StatusJson> {
|
) -> Result<Redirect, StatusJson> {
|
||||||
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
|
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
|
||||||
|
|
||||||
dbg!(&session);
|
let session = sessions::V {
|
||||||
|
|
||||||
let session = past_sessions::V {
|
|
||||||
category: session.category.into_inner(),
|
category: session.category.into_inner(),
|
||||||
started: NaiveDateTime::parse_from_str(&session.started, "%Y-%m-%d %H:%M")?,
|
started: Local.from_local_datetime(&NaiveDateTime::parse_from_str(&session.started, "%Y-%m-%d %H:%M")?).unwrap(),
|
||||||
ended: NaiveDateTime::parse_from_str(&session.ended, "%Y-%m-%d %H:%M")?,
|
ended: Local.from_local_datetime(&NaiveDateTime::parse_from_str(&session.ended, "%Y-%m-%d %H:%M")?).unwrap(),
|
||||||
|
deleted: session.deleted,
|
||||||
};
|
};
|
||||||
|
|
||||||
if session.started >= session.ended {
|
if session.started >= session.ended {
|
||||||
@ -165,7 +176,7 @@ pub fn edit_session(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.open_tree(past_sessions::NAME)?
|
db.open_tree(sessions::NAME)?
|
||||||
.insert(session_uuid_s, serialize(&session)?)?;
|
.insert(session_uuid_s, serialize(&session)?)?;
|
||||||
|
|
||||||
// FIXME: Uuid does not implement FromUriParam for some reason... File an issue?
|
// FIXME: Uuid does not implement FromUriParam for some reason... File an issue?
|
||||||
@ -177,19 +188,19 @@ pub fn edit_session(
|
|||||||
pub fn delete_session(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Redirect, StatusJson> {
|
pub fn delete_session(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Redirect, StatusJson> {
|
||||||
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
|
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
|
||||||
|
|
||||||
let past_sessions_tree = db.open_tree(past_sessions::NAME)?;
|
let sessions_tree = db.open_tree(sessions::NAME)?;
|
||||||
|
|
||||||
match past_sessions_tree.remove(session_uuid_s)? {
|
match sessions_tree.remove(session_uuid_s)? {
|
||||||
Some(_) => Ok(Redirect::to(uri!(pages::history))),
|
Some(_) => Ok(Redirect::to(uri!(pages::history))),
|
||||||
None => Err(Status::NotFound.into()),
|
None => Err(Status::NotFound.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: mark as deleted instead of removing
|
// TODO: mark as deleted instead of removing
|
||||||
// Ok(past_sessions_tree.transaction(|tx_past_sessions| {
|
// Ok(sessions_tree.transaction(|tx_sessions| {
|
||||||
// match tx_past_sessions.get(&session_uuid_s)? {
|
// match tx_sessions.get(&session_uuid_s)? {
|
||||||
// None => return Ok(Err(Status::NotFound)),
|
// None => return Ok(Err(Status::NotFound)),
|
||||||
// Some(data) => {
|
// Some(data) => {
|
||||||
// let mut past_session: past_sessions::V = deserialize(&data).unwrap();
|
// let mut session: sessions::V = deserialize(&data).unwrap();
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// Ok(Ok(Redirect::to(uri!(pages::history))))
|
// Ok(Ok(Redirect::to(uri!(pages::history))))
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::database::latest::trees::{categories, past_sessions};
|
use crate::database::latest::trees::{categories, sessions};
|
||||||
use crate::status_json::StatusJson;
|
use crate::status_json::StatusJson;
|
||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
@ -35,14 +35,14 @@ pub fn index(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
|||||||
pub fn session_edit(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
pub fn session_edit(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct SessionPageContext {
|
struct SessionPageContext {
|
||||||
session: past_sessions::V,
|
session: sessions::V,
|
||||||
session_id: uuid::Uuid,
|
session_id: uuid::Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
|
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
|
||||||
|
|
||||||
let past_sessions_tree = db.open_tree(past_sessions::NAME)?;
|
let sessions_tree = db.open_tree(sessions::NAME)?;
|
||||||
match past_sessions_tree.get(session_uuid_s)? {
|
match sessions_tree.get(session_uuid_s)? {
|
||||||
None => Err(Status::NotFound)?,
|
None => Err(Status::NotFound)?,
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let context = SessionPageContext {
|
let context = SessionPageContext {
|
||||||
@ -60,7 +60,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct HistoryEntryContext {
|
struct HistoryEntryContext {
|
||||||
category: categories::V,
|
category: categories::V,
|
||||||
session: past_sessions::V,
|
session: sessions::V,
|
||||||
session_id: uuid::Uuid,
|
session_id: uuid::Uuid,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let categories_tree = db.open_tree(categories::NAME)?;
|
let categories_tree = db.open_tree(categories::NAME)?;
|
||||||
let past_sessions_tree = db.open_tree(past_sessions::NAME)?;
|
let sessions_tree = db.open_tree(sessions::NAME)?;
|
||||||
|
|
||||||
let categories: HashMap<categories::K, categories::V> = categories_tree
|
let categories: HashMap<categories::K, categories::V> = categories_tree
|
||||||
.iter()
|
.iter()
|
||||||
@ -80,7 +80,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
|||||||
})
|
})
|
||||||
.collect::<Result<Result<_, _>, _>>()??;
|
.collect::<Result<Result<_, _>, _>>()??;
|
||||||
|
|
||||||
let past_sessions: HashMap<past_sessions::K, past_sessions::V> = past_sessions_tree
|
let sessions: HashMap<sessions::K, sessions::V> = sessions_tree
|
||||||
.iter()
|
.iter()
|
||||||
.map(|result| {
|
.map(|result| {
|
||||||
result.map(|(k, v)| deserialize(&k).and_then(|k| deserialize(&v).map(|v| (k, v))))
|
result.map(|(k, v)| deserialize(&k).and_then(|k| deserialize(&v).map(|v| (k, v))))
|
||||||
@ -88,7 +88,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
|
|||||||
.collect::<Result<Result<_, _>, _>>()??;
|
.collect::<Result<Result<_, _>, _>>()??;
|
||||||
|
|
||||||
let mut context = TemplateContext {
|
let mut context = TemplateContext {
|
||||||
entries: past_sessions
|
entries: sessions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(session_id, session)| HistoryEntryContext {
|
.map(|(session_id, session)| HistoryEntryContext {
|
||||||
duration: (session.ended - session.started).to_std().unwrap(),
|
duration: (session.ended - session.started).to_std().unwrap(),
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<form action="/session/{{session_id}}/edit" method="post">
|
<form action="/session/{{session_id}}/edit" method="post">
|
||||||
<input type="hidden" id="category" name="category" value="{{session.category}}">
|
<input type="hidden" id="category" name="category" value="{{session.category}}">
|
||||||
|
<input type="hidden" id="deleted" name="deleted" value="{{session.deleted}}">
|
||||||
<span>Started:</span>
|
<span>Started:</span>
|
||||||
<input type="text" id="started" name="started" value="{{pretty_datetime session.started}}"></input>
|
<input type="text" id="started" name="started" value="{{pretty_datetime session.started}}"></input>
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
Reference in New Issue
Block a user