From d354a5db0c4ab4b492f8ada1d6bda3ee853eed40 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 10 Dec 2022 09:26:02 -0500 Subject: [PATCH] Store extra items in the database too --- ...0221210140958_mealplans_primary_key.up.sql | 19 +- ...21210161348_extra_itmes_inventory.down.sql | 57 ++++++ ...0221210161348_extra_itmes_inventory.up.sql | 68 +++++++ kitchen/sqlx-data.json | 172 ++++++++++-------- kitchen/src/web/mod.rs | 103 +++++++++-- kitchen/src/web/storage/fetch_extra_items.sql | 18 ++ .../storage/fetch_extra_items_for_date.sql | 7 + .../fetch_inventory_filtered_ingredients.sql | 23 ++- .../storage/fetch_inventory_modified_amts.sql | 25 ++- kitchen/src/web/storage/mod.rs | 53 ++++-- .../save_inventory_filtered_ingredients.sql | 4 +- .../storage/save_inventory_modified_amts.sql | 4 +- kitchen/src/web/storage/store_extra_items.sql | 3 + 13 files changed, 441 insertions(+), 115 deletions(-) create mode 100644 kitchen/migrations/20221210161348_extra_itmes_inventory.down.sql create mode 100644 kitchen/migrations/20221210161348_extra_itmes_inventory.up.sql create mode 100644 kitchen/src/web/storage/fetch_extra_items.sql create mode 100644 kitchen/src/web/storage/fetch_extra_items_for_date.sql create mode 100644 kitchen/src/web/storage/store_extra_items.sql diff --git a/kitchen/migrations/20221210140958_mealplans_primary_key.up.sql b/kitchen/migrations/20221210140958_mealplans_primary_key.up.sql index f266bd0..4a4ee89 100644 --- a/kitchen/migrations/20221210140958_mealplans_primary_key.up.sql +++ b/kitchen/migrations/20221210140958_mealplans_primary_key.up.sql @@ -1,2 +1,19 @@ -- Add up migration script here -create unique index mealplan_lookup_index on plan_recipes (user_id, plan_date, recipe_id); \ No newline at end of file +-- First we collect a safe set of the data deduped for the unique index to handle using max to select a winning count. +create temp table TEMP_plan_recipes_deduped as + select user_id, plan_date, recipe_id, max(count) as count + from plan_recipes + group by user_id, plan_date, recipe_id; + +-- Then we drop the plan_recipes from the table +delete from plan_recipes; + +-- Create the unique index +create unique index mealplan_lookup_index + on plan_recipes (user_id, plan_date, recipe_id); + +-- And finally insert the dedeuped records back into the table before dropping the temp table. +insert into plan_recipes + select user_id, plan_date, recipe_id, count + from TEMP_plan_recipes_deduped; +drop table TEMP_plan_recipes_deduped; \ No newline at end of file diff --git a/kitchen/migrations/20221210161348_extra_itmes_inventory.down.sql b/kitchen/migrations/20221210161348_extra_itmes_inventory.down.sql new file mode 100644 index 0000000..a9b8191 --- /dev/null +++ b/kitchen/migrations/20221210161348_extra_itmes_inventory.down.sql @@ -0,0 +1,57 @@ +-- Add down migration script here +drop table extra_items; + +-- make a copy of of the filtered_ingredients table with only latest plan_date rows +create temp table TEMP_filtered_ingredients_copy as + select + user_id, + name, + max(plan_date) as plan_date, + form, + measure_type + from filtered_ingredients + group by user_id, name, form, measure_type; + +-- Drop the filtered ingredients table and recreate without plan_date +drop table filtered_ingredients; +create table filtered_ingredients( + user_id TEXT NOT NULL, + name TEXT NOT NULL, + form TEXT NOT NULL, + measure_type TEXT NOT NULL, + primary key(user_id, name, form, measure_type) +); + +-- Populate the new filtered ingredients table from the copied table +insert into filtered_ingredients + select user_id, name, form, measure_type + from TEMP_filtered_ingredients_copy; + +-- make a copy of of the modified_amts table with only latest plan_date rows +create temp table TEMP_modified_amts_copy as + select + user_id, + name, + form, + measure_type, + max(plan_date) as plan_date, + amt + from modified_amts; + +-- Drop modified_amts and recreate without plan_date. +drop table modified_amts; +create table modified_amts( + user_id TEXT NOT NULL, + name TEXT NOT NULL, + form TEXT NOT NULL, + measure_type TEXT NOT NULL, + amt TEXT NOT NULL, + primary key(user_id, name, form, measure_type) +); + +-- Populate the new modified amts with rows from the copy. +insert into modified_amts + select user_id, name, form, measure_type, amt + from TEMP_modified_amts_copy; + +drop table TEMP_modified_amts_copy; \ No newline at end of file diff --git a/kitchen/migrations/20221210161348_extra_itmes_inventory.up.sql b/kitchen/migrations/20221210161348_extra_itmes_inventory.up.sql new file mode 100644 index 0000000..aae3561 --- /dev/null +++ b/kitchen/migrations/20221210161348_extra_itmes_inventory.up.sql @@ -0,0 +1,68 @@ +-- Add up migration script here + +-- Create our extra items table +create table extra_items( + user_id TEXT NOT NULL, + name TEXT NOT NULL, + plan_date DATE NOT NULL, + amt TEXT NOT NULL, + primary key(user_id, name, plan_date) +); + +-- Store a copy of filtered ingredients with current date as plan_date +create temp table TEMP_filtered_ingredients_copy as + select + user_id, + name, + date() as plan_date, + form, + measure_type + from filtered_ingredients; + +-- Drop the filtered ingredients table and recreate with plan_date in the primary key +drop table filtered_ingredients; +create table filtered_ingredients( + user_id TEXT NOT NULL, + name TEXT NOT NULL, + form TEXT NOT NULL, + measure_type TEXT NOT NULL, + plan_date DATE NOT NULL, + primary key(user_id, name, form, measure_type, plan_date) +); + +-- Populate the new filtered ingredients table from the copied table +insert into filtered_ingredients + select user_id, name, form, measure_type, plan_date + from TEMP_filtered_ingredients_copy; + +drop table TEMP_filtered_ingredients_copy; + +-- make a copy of of the modified_amts table with current date as plan_date +create temp table TEMP_modified_amts_copy as + select + user_id, + name, + form, + measure_type, + date() as plan_date, + amt + from modified_amts; + +-- Drop modified_amts and recreate with plan_date as part of primary key. +drop table modified_amts; +create table modified_amts( + user_id TEXT NOT NULL, + name TEXT NOT NULL, + form TEXT NOT NULL, + measure_type TEXT NOT NULL, + plan_date DATE NOT NULL, + amt TEXT NOT NULL, + primary key(user_id, name, form, measure_type, plan_date) +); + +-- Populate the new modified amts with rows from the copy. +insert into modified_amts + select user_id, name, form, measure_type, plan_date, amt + from TEMP_modified_amts_copy; + +drop table TEMP_modified_amts_copy; diff --git a/kitchen/sqlx-data.json b/kitchen/sqlx-data.json index d64b96f..44341ba 100644 --- a/kitchen/sqlx-data.json +++ b/kitchen/sqlx-data.json @@ -1,14 +1,34 @@ { "db": "SQLite", - "07f619ff4474e9eb5f4d56497abb724e6952b4e43d681ba5ecd61490cf990ae9": { + "04987493e4b13793a2dff75cc2710972bb28abf303275f5e6346470cdf5c2c17": { "describe": { - "columns": [], - "nullable": [], + "columns": [ + { + "name": "name", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "form", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "measure_type", + "ordinal": 2, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false + ], "parameters": { "Right": 1 } }, - "query": "delete from filtered_ingredients where user_id = ?" + "query": "with latest_dates as (\n select\n user_id,\n name,\n form,\n measure_type,\n max(plan_date) as plan_date\n from filtered_ingredients\n where user_id = ?\n)\n\nselect\n filtered_ingredients.name,\n filtered_ingredients.form,\n filtered_ingredients.measure_type\nfrom latest_dates\ninner join filtered_ingredients on\n latest_dates.user_id = filtered_ingredients.user_id\n and latest_dates.name = filtered_ingredients.name\n and latest_dates.form = filtered_ingredients.form\n and latest_dates.measure_type = filtered_ingredients.measure_type\n and latest_dates.plan_date = filtered_ingredients.plan_date" }, "104f07472670436d3eee1733578bbf0c92dc4f965d3d13f9bf4bfbc92958c5b6": { "describe": { @@ -28,6 +48,16 @@ }, "query": "select password_hashed from users where id = ?" }, + "160a9dfccf2e91a37d81f75eba21ec73105a7453c4f1fe76a430d04e525bc6cd": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 4 + } + }, + "query": "insert into filtered_ingredients(user_id, name, form, measure_type, plan_date)\n values (?, ?, ?, ?, date()) on conflict(user_id, name, form, measure_type, plan_date) DO NOTHING" + }, "196e289cbd65224293c4213552160a0cdf82f924ac597810fe05102e247b809d": { "describe": { "columns": [ @@ -82,6 +112,16 @@ }, "query": "select plan_date as \"plan_date: NaiveDate\", recipe_id, count\nfrom plan_recipes\nwhere\n user_id = ?\n and date(plan_date) > ?\norder by user_id, plan_date" }, + "3caefb86073c47b5dd5d05f639ddef2f7ed2d1fd80f224457d1ec34243cc56c7": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 3 + } + }, + "query": "insert into extra_items (user_id, name, plan_date, amt)\nvalues (?, ?, date(), ?)\non conflict (user_id, name, plan_date) do update set amt=excluded.amt" + }, "3fd4017569dca4fe73e97e0e2bd612027a8c1b17b0b10faabd6459f56ca1c0bb": { "describe": { "columns": [], @@ -92,6 +132,42 @@ }, "query": "insert into recipes (user_id, recipe_id, recipe_text) values (?, ?, ?)\n on conflict(user_id, recipe_id) do update set recipe_text=excluded.recipe_text" }, + "406aac6ac2b0084c31c29adec6fa2fb9bb925d92121305c8afbac009caf1ecc0": { + "describe": { + "columns": [ + { + "name": "name", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "form", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "measure_type", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "amt", + "ordinal": 3, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "with latest_dates as (\n select\n user_id,\n name,\n form,\n measure_type,\n amt,\n max(plan_date) as plan_date\n from modified_amts\n where user_id = ?\n)\n\nselect\n modified_amts.name,\n modified_amts.form,\n modified_amts.measure_type,\n modified_amts.amt\nfrom latest_dates\ninner join modified_amts on\n latest_dates.user_id = modified_amts.user_id\n and latest_dates.name = modified_amts.name\n and latest_dates.form = modified_amts.form\n and latest_dates.amt = modified_amts.amt\n and latest_dates.plan_date = modified_amts.plan_date" + }, "5d743897fb0d8fd54c3708f1b1c6e416346201faa9e28823c1ba5a421472b1fa": { "describe": { "columns": [], @@ -102,6 +178,16 @@ }, "query": "insert into users (id, password_hashed) values (?, ?)" }, + "6e28698330e42fd6c87ba1e6f1deb664c0d3995caa2b937ceac8c908e98aded6": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 5 + } + }, + "query": "insert into modified_amts(user_id, name, form, measure_type, amt, plan_date)\n values (?, ?, ?, ?, ?, date()) on conflict (user_id, name, form, measure_type, plan_date) do update set amt=excluded.amt" + }, "7578157607967a6a4c60f12408c5d9900d15b429a49681a4cae4e02d31c524ec": { "describe": { "columns": [], @@ -184,16 +270,6 @@ }, "query": "insert into sessions (id, session_value) values (?, ?)" }, - "9e24ed2ea4d235e3a036025a0a0b5ea685546a81d7f2469a59a2fc1fc88798dc": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 1 - } - }, - "query": "delete from modified_amts where user_id = ?" - }, "ad3408cd773dd8f9308255ec2800171638a1aeda9817c57fb8360f97115f8e97": { "describe": { "columns": [ @@ -242,36 +318,6 @@ }, "query": "select category_text from categories where user_id = ?" }, - "d7d94a87b0153d1436eac0f6db820f25594e94decc8d740037c10802aa49157f": { - "describe": { - "columns": [ - { - "name": "name", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "form", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "measure_type", - "ordinal": 2, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - false - ], - "parameters": { - "Right": 1 - } - }, - "query": "select name, form, measure_type from filtered_ingredients where user_id = ?" - }, "d84685a82585c5e4ae72c86ba1fe6e4a7241c4c3c9e948213e5849d956132bad": { "describe": { "columns": [], @@ -282,17 +328,7 @@ }, "query": "delete from sessions" }, - "f510d7f8dcb79907abd8b17bd52127af1699b3b79d2737c7729972d6bd5a0693": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 4 - } - }, - "query": "insert into filtered_ingredients(user_id, name, form, measure_type)\n values (?, ?, ?, ?) on conflict(user_id, name, form, measure_type) DO NOTHING" - }, - "fc294739374d2a791214f747095e0bf9378989d1ff07d96a5431dbb208f21951": { + "f34ec23c5cc8f61f92464ecf68620150a8d4521b68b5099a0a7dac3328651880": { "describe": { "columns": [ { @@ -300,25 +336,13 @@ "ordinal": 0, "type_info": "Text" }, - { - "name": "form", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "measure_type", - "ordinal": 2, - "type_info": "Text" - }, { "name": "amt", - "ordinal": 3, + "ordinal": 1, "type_info": "Text" } ], "nullable": [ - false, - false, false, false ], @@ -326,16 +350,6 @@ "Right": 1 } }, - "query": "select name, form, measure_type, amt from modified_amts where user_id = ?;" - }, - "fc9d3f8ce9d0b42f34307aeb31c128cc3267e77613d6f2e170c95e83d6e361df": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 5 - } - }, - "query": "insert into modified_amts(user_id, name, form, measure_type, amt)\n values (?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type) do update set amt=excluded.amt" + "query": "with latest_dates as (\n select\n user_id,\n name,\n max(plan_date) as plan_date\n from extra_items\n where user_id = ?\n group by user_id, name\n)\n\nselect\n extra_items.name,\n extra_items.amt\nfrom latest_dates\ninner join extra_items on\n latest_dates.user_id = extra_items.user_id\n and latest_dates.name = extra_items.name\n and latest_dates.plan_date= extra_items.plan_date" } } \ No newline at end of file diff --git a/kitchen/src/web/mod.rs b/kitchen/src/web/mod.rs index 650f920..8610681 100644 --- a/kitchen/src/web/mod.rs +++ b/kitchen/src/web/mod.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; // Copyright 2022 Jeremy Wall // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,9 +12,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; +use std::{collections::BTreeSet, net::SocketAddr}; use axum::{ body::{boxed, Full}, @@ -263,13 +264,13 @@ async fn api_save_plan( } } -async fn api_inventory( +async fn api_inventory_v2( Extension(app_store): Extension>, session: storage::UserIdFromSession, ) -> impl IntoResponse { use storage::{UserId, UserIdFromSession::FoundUserId}; if let FoundUserId(UserId(id)) = session { - match app_store.fetch_inventory_data(id).await { + match app_store.fetch_latest_inventory_data(id).await { Ok(tpl) => Ok(axum::Json::from(tpl)), Err(e) => { error!(err=?e); @@ -284,6 +285,76 @@ async fn api_inventory( } } +async fn api_inventory( + Extension(app_store): Extension>, + session: storage::UserIdFromSession, +) -> impl IntoResponse { + use storage::{UserId, UserIdFromSession::FoundUserId}; + if let FoundUserId(UserId(id)) = session { + match app_store.fetch_latest_inventory_data(id).await { + Ok((item1, item2, _)) => Ok(axum::Json::from((item1, item2))), + Err(e) => { + error!(err=?e); + Err((StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e))) + } + } + } else { + Err(( + StatusCode::UNAUTHORIZED, + "You must be authorized to use this API call".to_owned(), + )) + } +} + +async fn save_inventory_data( + app_store: Arc, + id: String, + filtered_ingredients: BTreeSet, + modified_amts: BTreeMap, + extra_items: Vec<(String, String)>, +) -> (StatusCode, String) { + if let Err(e) = app_store + .save_inventory_data(id, filtered_ingredients, modified_amts, extra_items) + .await + { + error!(err=?e); + return (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)); + } + ( + StatusCode::OK, + "Successfully saved inventory data".to_owned(), + ) +} + +async fn api_save_inventory_v2( + Extension(app_store): Extension>, + session: storage::UserIdFromSession, + Json((filtered_ingredients, modified_amts, extra_items)): Json<( + Vec, + Vec<(IngredientKey, String)>, + Vec<(String, String)>, + )>, +) -> impl IntoResponse { + use storage::{UserId, UserIdFromSession::FoundUserId}; + if let FoundUserId(UserId(id)) = session { + let filtered_ingredients = filtered_ingredients.into_iter().collect(); + let modified_amts = modified_amts.into_iter().collect(); + save_inventory_data( + app_store, + id, + filtered_ingredients, + modified_amts, + extra_items, + ) + .await + } else { + ( + StatusCode::UNAUTHORIZED, + "You must be authorized to use this API call".to_owned(), + ) + } +} + async fn api_save_inventory( Extension(app_store): Extension>, session: storage::UserIdFromSession, @@ -296,17 +367,14 @@ async fn api_save_inventory( if let FoundUserId(UserId(id)) = session { let filtered_ingredients = filtered_ingredients.into_iter().collect(); let modified_amts = modified_amts.into_iter().collect(); - if let Err(e) = app_store - .save_inventory_data(id, filtered_ingredients, modified_amts) - .await - { - error!(err=?e); - return (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)); - } - ( - StatusCode::OK, - "Successfully saved inventory data".to_owned(), + save_inventory_data( + app_store, + id, + filtered_ingredients, + modified_amts, + Vec::new(), ) + .await } else { ( StatusCode::UNAUTHORIZED, @@ -333,12 +401,13 @@ pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socke let router = Router::new() .route("/", get(|| async { Redirect::temporary("/ui/plan") })) .route("/ui/*path", get(ui_static_assets)) + // TODO(jwall): Cleanup the routing using nested routes + // TODO(jwall): We should use route_layer to enforce the authorization + // requirements here. // recipes api path route .route("/api/v1/recipes", get(api_recipes).post(api_save_recipes)) // recipe entry api path route .route("/api/v1/recipe/:recipe_id", get(api_recipe_entry)) - // TODO(jwall): We should use route_layer to enforce the authorization - // requirements here. // mealplan api path routes .route("/api/v1/plan", get(api_plan).post(api_save_plan)) .route("/api/v1/plan/:date", get(api_plan_since)) @@ -347,6 +416,10 @@ pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socke "/api/v1/inventory", get(api_inventory).post(api_save_inventory), ) + .route( + "/api/v2/inventory", + get(api_inventory_v2).post(api_save_inventory_v2), + ) // categories api path route .route( "/api/v1/categories", diff --git a/kitchen/src/web/storage/fetch_extra_items.sql b/kitchen/src/web/storage/fetch_extra_items.sql new file mode 100644 index 0000000..bff8fbd --- /dev/null +++ b/kitchen/src/web/storage/fetch_extra_items.sql @@ -0,0 +1,18 @@ +with latest_dates as ( + select + user_id, + name, + max(plan_date) as plan_date + from extra_items + where user_id = ? + group by user_id, name +) + +select + extra_items.name, + extra_items.amt +from latest_dates +inner join extra_items on + latest_dates.user_id = extra_items.user_id + and latest_dates.name = extra_items.name + and latest_dates.plan_date= extra_items.plan_date \ No newline at end of file diff --git a/kitchen/src/web/storage/fetch_extra_items_for_date.sql b/kitchen/src/web/storage/fetch_extra_items_for_date.sql new file mode 100644 index 0000000..61c4174 --- /dev/null +++ b/kitchen/src/web/storage/fetch_extra_items_for_date.sql @@ -0,0 +1,7 @@ +select + name, + amt +from extra_items +where + user_id = ? + and plan_date = ? \ No newline at end of file diff --git a/kitchen/src/web/storage/fetch_inventory_filtered_ingredients.sql b/kitchen/src/web/storage/fetch_inventory_filtered_ingredients.sql index 9ba0497..d98c5ff 100644 --- a/kitchen/src/web/storage/fetch_inventory_filtered_ingredients.sql +++ b/kitchen/src/web/storage/fetch_inventory_filtered_ingredients.sql @@ -1 +1,22 @@ -select name, form, measure_type from filtered_ingredients where user_id = ? \ No newline at end of file +with latest_dates as ( + select + user_id, + name, + form, + measure_type, + max(plan_date) as plan_date + from filtered_ingredients + where user_id = ? +) + +select + filtered_ingredients.name, + filtered_ingredients.form, + filtered_ingredients.measure_type +from latest_dates +inner join filtered_ingredients on + latest_dates.user_id = filtered_ingredients.user_id + and latest_dates.name = filtered_ingredients.name + and latest_dates.form = filtered_ingredients.form + and latest_dates.measure_type = filtered_ingredients.measure_type + and latest_dates.plan_date = filtered_ingredients.plan_date \ No newline at end of file diff --git a/kitchen/src/web/storage/fetch_inventory_modified_amts.sql b/kitchen/src/web/storage/fetch_inventory_modified_amts.sql index 6903a6a..6a3d317 100644 --- a/kitchen/src/web/storage/fetch_inventory_modified_amts.sql +++ b/kitchen/src/web/storage/fetch_inventory_modified_amts.sql @@ -1 +1,24 @@ -select name, form, measure_type, amt from modified_amts where user_id = ?; \ No newline at end of file +with latest_dates as ( + select + user_id, + name, + form, + measure_type, + amt, + max(plan_date) as plan_date + from modified_amts + where user_id = ? +) + +select + modified_amts.name, + modified_amts.form, + modified_amts.measure_type, + modified_amts.amt +from latest_dates +inner join modified_amts on + latest_dates.user_id = modified_amts.user_id + and latest_dates.name = modified_amts.name + and latest_dates.form = modified_amts.form + and latest_dates.amt = modified_amts.amt + and latest_dates.plan_date = modified_amts.plan_date \ No newline at end of file diff --git a/kitchen/src/web/storage/mod.rs b/kitchen/src/web/storage/mod.rs index c609c14..70a2ee0 100644 --- a/kitchen/src/web/storage/mod.rs +++ b/kitchen/src/web/storage/mod.rs @@ -121,16 +121,21 @@ pub trait APIStore { date: NaiveDate, ) -> Result<()>; - async fn fetch_inventory_data + Send>( + async fn fetch_latest_inventory_data + Send>( &self, user_id: S, - ) -> Result<(Vec, Vec<(IngredientKey, String)>)>; + ) -> Result<( + Vec, + Vec<(IngredientKey, String)>, + Vec<(String, String)>, + )>; async fn save_inventory_data + Send>( &self, user_id: S, filtered_ingredients: BTreeSet, modified_amts: BTreeMap, + extra_items: Vec<(String, String)>, ) -> Result<()>; } @@ -504,10 +509,15 @@ impl APIStore for SqliteStore { Ok(Some(result)) } - async fn fetch_inventory_data + Send>( + // TODO(jwall): Do we need fetch for date variants of this. + async fn fetch_latest_inventory_data + Send>( &self, user_id: S, - ) -> Result<(Vec, Vec<(IngredientKey, String)>)> { + ) -> Result<( + Vec, + Vec<(IngredientKey, String)>, + Vec<(String, String)>, + )> { let user_id = user_id.as_ref(); struct FilteredIngredientRow { name: String, @@ -561,7 +571,22 @@ impl APIStore for SqliteStore { row.amt, )); } - Ok((filtered_ingredients, modified_amts)) + pub struct ExtraItemRow { + name: String, + amt: String, + } + let extra_items_rows = sqlx::query_file_as!( + ExtraItemRow, + "src/web/storage/fetch_extra_items.sql", + user_id, + ) + .fetch_all(self.pool.as_ref()) + .await?; + let mut extra_items = Vec::new(); + for row in extra_items_rows { + extra_items.push((row.name, row.amt)); + } + Ok((filtered_ingredients, modified_amts, extra_items)) } async fn save_inventory_data + Send>( @@ -569,18 +594,11 @@ impl APIStore for SqliteStore { user_id: S, filtered_ingredients: BTreeSet, modified_amts: BTreeMap, + extra_items: Vec<(String, String)>, ) -> Result<()> { let user_id = user_id.as_ref(); let mut transaction = self.pool.as_ref().begin().await?; - sqlx::query!( - "delete from filtered_ingredients where user_id = ?", - user_id - ) - .execute(&mut transaction) - .await?; - sqlx::query!("delete from modified_amts where user_id = ?", user_id) - .execute(&mut transaction) - .await?; + // store the filtered_ingredients for key in filtered_ingredients { let name = key.name(); let form = key.form(); @@ -595,6 +613,7 @@ impl APIStore for SqliteStore { .execute(&mut transaction) .await?; } + // store the modified amts for (key, amt) in modified_amts { let name = key.name(); let form = key.form(); @@ -611,6 +630,12 @@ impl APIStore for SqliteStore { .execute(&mut transaction) .await?; } + // Store the extra items + for (name, amt) in extra_items { + sqlx::query_file!("src/web/storage/store_extra_items.sql", user_id, name, amt) + .execute(&mut transaction) + .await?; + } transaction.commit().await?; Ok(()) } diff --git a/kitchen/src/web/storage/save_inventory_filtered_ingredients.sql b/kitchen/src/web/storage/save_inventory_filtered_ingredients.sql index 3472aa0..7e8fe61 100644 --- a/kitchen/src/web/storage/save_inventory_filtered_ingredients.sql +++ b/kitchen/src/web/storage/save_inventory_filtered_ingredients.sql @@ -1,2 +1,2 @@ -insert into filtered_ingredients(user_id, name, form, measure_type) - values (?, ?, ?, ?) on conflict(user_id, name, form, measure_type) DO NOTHING \ No newline at end of file +insert into filtered_ingredients(user_id, name, form, measure_type, plan_date) + values (?, ?, ?, ?, date()) on conflict(user_id, name, form, measure_type, plan_date) DO NOTHING \ No newline at end of file diff --git a/kitchen/src/web/storage/save_inventory_modified_amts.sql b/kitchen/src/web/storage/save_inventory_modified_amts.sql index 88ec9f0..400caa9 100644 --- a/kitchen/src/web/storage/save_inventory_modified_amts.sql +++ b/kitchen/src/web/storage/save_inventory_modified_amts.sql @@ -1,2 +1,2 @@ -insert into modified_amts(user_id, name, form, measure_type, amt) - values (?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type) do update set amt=excluded.amt \ No newline at end of file +insert into modified_amts(user_id, name, form, measure_type, amt, plan_date) + values (?, ?, ?, ?, ?, date()) on conflict (user_id, name, form, measure_type, plan_date) do update set amt=excluded.amt \ No newline at end of file diff --git a/kitchen/src/web/storage/store_extra_items.sql b/kitchen/src/web/storage/store_extra_items.sql new file mode 100644 index 0000000..daa0b40 --- /dev/null +++ b/kitchen/src/web/storage/store_extra_items.sql @@ -0,0 +1,3 @@ +insert into extra_items (user_id, name, plan_date, amt) +values (?, ?, date(), ?) +on conflict (user_id, name, plan_date) do update set amt=excluded.amt \ No newline at end of file