mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-24 19:59:50 -04:00
Store extra items in the database too
This commit is contained in:
parent
8fd940bd00
commit
d354a5db0c
@ -1,2 +1,19 @@
|
|||||||
-- Add up migration script here
|
-- Add up migration script here
|
||||||
create unique index mealplan_lookup_index on plan_recipes (user_id, plan_date, recipe_id);
|
-- 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;
|
@ -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;
|
@ -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;
|
@ -1,14 +1,34 @@
|
|||||||
{
|
{
|
||||||
"db": "SQLite",
|
"db": "SQLite",
|
||||||
"07f619ff4474e9eb5f4d56497abb724e6952b4e43d681ba5ecd61490cf990ae9": {
|
"04987493e4b13793a2dff75cc2710972bb28abf303275f5e6346470cdf5c2c17": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [
|
||||||
"nullable": [],
|
{
|
||||||
|
"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": {
|
"parameters": {
|
||||||
"Right": 1
|
"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": {
|
"104f07472670436d3eee1733578bbf0c92dc4f965d3d13f9bf4bfbc92958c5b6": {
|
||||||
"describe": {
|
"describe": {
|
||||||
@ -28,6 +48,16 @@
|
|||||||
},
|
},
|
||||||
"query": "select password_hashed from users where id = ?"
|
"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": {
|
"196e289cbd65224293c4213552160a0cdf82f924ac597810fe05102e247b809d": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"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"
|
"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": {
|
"3fd4017569dca4fe73e97e0e2bd612027a8c1b17b0b10faabd6459f56ca1c0bb": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"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"
|
"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": {
|
"5d743897fb0d8fd54c3708f1b1c6e416346201faa9e28823c1ba5a421472b1fa": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@ -102,6 +178,16 @@
|
|||||||
},
|
},
|
||||||
"query": "insert into users (id, password_hashed) values (?, ?)"
|
"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": {
|
"7578157607967a6a4c60f12408c5d9900d15b429a49681a4cae4e02d31c524ec": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@ -184,16 +270,6 @@
|
|||||||
},
|
},
|
||||||
"query": "insert into sessions (id, session_value) values (?, ?)"
|
"query": "insert into sessions (id, session_value) values (?, ?)"
|
||||||
},
|
},
|
||||||
"9e24ed2ea4d235e3a036025a0a0b5ea685546a81d7f2469a59a2fc1fc88798dc": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "delete from modified_amts where user_id = ?"
|
|
||||||
},
|
|
||||||
"ad3408cd773dd8f9308255ec2800171638a1aeda9817c57fb8360f97115f8e97": {
|
"ad3408cd773dd8f9308255ec2800171638a1aeda9817c57fb8360f97115f8e97": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
@ -242,36 +318,6 @@
|
|||||||
},
|
},
|
||||||
"query": "select category_text from categories where user_id = ?"
|
"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": {
|
"d84685a82585c5e4ae72c86ba1fe6e4a7241c4c3c9e948213e5849d956132bad": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@ -282,17 +328,7 @@
|
|||||||
},
|
},
|
||||||
"query": "delete from sessions"
|
"query": "delete from sessions"
|
||||||
},
|
},
|
||||||
"f510d7f8dcb79907abd8b17bd52127af1699b3b79d2737c7729972d6bd5a0693": {
|
"f34ec23c5cc8f61f92464ecf68620150a8d4521b68b5099a0a7dac3328651880": {
|
||||||
"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": {
|
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@ -300,25 +336,13 @@
|
|||||||
"ordinal": 0,
|
"ordinal": 0,
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "form",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "measure_type",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "amt",
|
"name": "amt",
|
||||||
"ordinal": 3,
|
"ordinal": 1,
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [
|
"nullable": [
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
],
|
],
|
||||||
@ -326,16 +350,6 @@
|
|||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query": "select name, form, measure_type, amt from modified_amts where user_id = ?;"
|
"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"
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
// Copyright 2022 Jeremy Wall
|
// Copyright 2022 Jeremy Wall
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// 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.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{collections::BTreeSet, net::SocketAddr};
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::{boxed, Full},
|
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<Arc<storage::SqliteStore>>,
|
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
||||||
session: storage::UserIdFromSession,
|
session: storage::UserIdFromSession,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
use storage::{UserId, UserIdFromSession::FoundUserId};
|
use storage::{UserId, UserIdFromSession::FoundUserId};
|
||||||
if let FoundUserId(UserId(id)) = session {
|
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)),
|
Ok(tpl) => Ok(axum::Json::from(tpl)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(err=?e);
|
error!(err=?e);
|
||||||
@ -284,6 +285,76 @@ async fn api_inventory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn api_inventory(
|
||||||
|
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
||||||
|
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<storage::SqliteStore>,
|
||||||
|
id: String,
|
||||||
|
filtered_ingredients: BTreeSet<IngredientKey>,
|
||||||
|
modified_amts: BTreeMap<IngredientKey, String>,
|
||||||
|
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<Arc<storage::SqliteStore>>,
|
||||||
|
session: storage::UserIdFromSession,
|
||||||
|
Json((filtered_ingredients, modified_amts, extra_items)): Json<(
|
||||||
|
Vec<IngredientKey>,
|
||||||
|
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(
|
async fn api_save_inventory(
|
||||||
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
||||||
session: storage::UserIdFromSession,
|
session: storage::UserIdFromSession,
|
||||||
@ -296,17 +367,14 @@ async fn api_save_inventory(
|
|||||||
if let FoundUserId(UserId(id)) = session {
|
if let FoundUserId(UserId(id)) = session {
|
||||||
let filtered_ingredients = filtered_ingredients.into_iter().collect();
|
let filtered_ingredients = filtered_ingredients.into_iter().collect();
|
||||||
let modified_amts = modified_amts.into_iter().collect();
|
let modified_amts = modified_amts.into_iter().collect();
|
||||||
if let Err(e) = app_store
|
save_inventory_data(
|
||||||
.save_inventory_data(id, filtered_ingredients, modified_amts)
|
app_store,
|
||||||
.await
|
id,
|
||||||
{
|
filtered_ingredients,
|
||||||
error!(err=?e);
|
modified_amts,
|
||||||
return (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e));
|
Vec::new(),
|
||||||
}
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
"Successfully saved inventory data".to_owned(),
|
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
@ -333,12 +401,13 @@ pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socke
|
|||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.route("/", get(|| async { Redirect::temporary("/ui/plan") }))
|
.route("/", get(|| async { Redirect::temporary("/ui/plan") }))
|
||||||
.route("/ui/*path", get(ui_static_assets))
|
.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
|
// recipes api path route
|
||||||
.route("/api/v1/recipes", get(api_recipes).post(api_save_recipes))
|
.route("/api/v1/recipes", get(api_recipes).post(api_save_recipes))
|
||||||
// recipe entry api path route
|
// recipe entry api path route
|
||||||
.route("/api/v1/recipe/:recipe_id", get(api_recipe_entry))
|
.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
|
// mealplan api path routes
|
||||||
.route("/api/v1/plan", get(api_plan).post(api_save_plan))
|
.route("/api/v1/plan", get(api_plan).post(api_save_plan))
|
||||||
.route("/api/v1/plan/:date", get(api_plan_since))
|
.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",
|
"/api/v1/inventory",
|
||||||
get(api_inventory).post(api_save_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
|
// categories api path route
|
||||||
.route(
|
.route(
|
||||||
"/api/v1/categories",
|
"/api/v1/categories",
|
||||||
|
18
kitchen/src/web/storage/fetch_extra_items.sql
Normal file
18
kitchen/src/web/storage/fetch_extra_items.sql
Normal file
@ -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
|
7
kitchen/src/web/storage/fetch_extra_items_for_date.sql
Normal file
7
kitchen/src/web/storage/fetch_extra_items_for_date.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
select
|
||||||
|
name,
|
||||||
|
amt
|
||||||
|
from extra_items
|
||||||
|
where
|
||||||
|
user_id = ?
|
||||||
|
and plan_date = ?
|
@ -1 +1,22 @@
|
|||||||
select name, form, measure_type from filtered_ingredients where user_id = ?
|
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
|
@ -1 +1,24 @@
|
|||||||
select name, form, measure_type, amt from modified_amts where user_id = ?;
|
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
|
@ -121,16 +121,21 @@ pub trait APIStore {
|
|||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
async fn fetch_inventory_data<S: AsRef<str> + Send>(
|
async fn fetch_latest_inventory_data<S: AsRef<str> + Send>(
|
||||||
&self,
|
&self,
|
||||||
user_id: S,
|
user_id: S,
|
||||||
) -> Result<(Vec<IngredientKey>, Vec<(IngredientKey, String)>)>;
|
) -> Result<(
|
||||||
|
Vec<IngredientKey>,
|
||||||
|
Vec<(IngredientKey, String)>,
|
||||||
|
Vec<(String, String)>,
|
||||||
|
)>;
|
||||||
|
|
||||||
async fn save_inventory_data<S: AsRef<str> + Send>(
|
async fn save_inventory_data<S: AsRef<str> + Send>(
|
||||||
&self,
|
&self,
|
||||||
user_id: S,
|
user_id: S,
|
||||||
filtered_ingredients: BTreeSet<IngredientKey>,
|
filtered_ingredients: BTreeSet<IngredientKey>,
|
||||||
modified_amts: BTreeMap<IngredientKey, String>,
|
modified_amts: BTreeMap<IngredientKey, String>,
|
||||||
|
extra_items: Vec<(String, String)>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,10 +509,15 @@ impl APIStore for SqliteStore {
|
|||||||
Ok(Some(result))
|
Ok(Some(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_inventory_data<S: AsRef<str> + Send>(
|
// TODO(jwall): Do we need fetch for date variants of this.
|
||||||
|
async fn fetch_latest_inventory_data<S: AsRef<str> + Send>(
|
||||||
&self,
|
&self,
|
||||||
user_id: S,
|
user_id: S,
|
||||||
) -> Result<(Vec<IngredientKey>, Vec<(IngredientKey, String)>)> {
|
) -> Result<(
|
||||||
|
Vec<IngredientKey>,
|
||||||
|
Vec<(IngredientKey, String)>,
|
||||||
|
Vec<(String, String)>,
|
||||||
|
)> {
|
||||||
let user_id = user_id.as_ref();
|
let user_id = user_id.as_ref();
|
||||||
struct FilteredIngredientRow {
|
struct FilteredIngredientRow {
|
||||||
name: String,
|
name: String,
|
||||||
@ -561,7 +571,22 @@ impl APIStore for SqliteStore {
|
|||||||
row.amt,
|
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<S: AsRef<str> + Send>(
|
async fn save_inventory_data<S: AsRef<str> + Send>(
|
||||||
@ -569,18 +594,11 @@ impl APIStore for SqliteStore {
|
|||||||
user_id: S,
|
user_id: S,
|
||||||
filtered_ingredients: BTreeSet<IngredientKey>,
|
filtered_ingredients: BTreeSet<IngredientKey>,
|
||||||
modified_amts: BTreeMap<IngredientKey, String>,
|
modified_amts: BTreeMap<IngredientKey, String>,
|
||||||
|
extra_items: Vec<(String, String)>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let user_id = user_id.as_ref();
|
let user_id = user_id.as_ref();
|
||||||
let mut transaction = self.pool.as_ref().begin().await?;
|
let mut transaction = self.pool.as_ref().begin().await?;
|
||||||
sqlx::query!(
|
// store the filtered_ingredients
|
||||||
"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?;
|
|
||||||
for key in filtered_ingredients {
|
for key in filtered_ingredients {
|
||||||
let name = key.name();
|
let name = key.name();
|
||||||
let form = key.form();
|
let form = key.form();
|
||||||
@ -595,6 +613,7 @@ impl APIStore for SqliteStore {
|
|||||||
.execute(&mut transaction)
|
.execute(&mut transaction)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
// store the modified amts
|
||||||
for (key, amt) in modified_amts {
|
for (key, amt) in modified_amts {
|
||||||
let name = key.name();
|
let name = key.name();
|
||||||
let form = key.form();
|
let form = key.form();
|
||||||
@ -611,6 +630,12 @@ impl APIStore for SqliteStore {
|
|||||||
.execute(&mut transaction)
|
.execute(&mut transaction)
|
||||||
.await?;
|
.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?;
|
transaction.commit().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
insert into filtered_ingredients(user_id, name, form, measure_type)
|
insert into filtered_ingredients(user_id, name, form, measure_type, plan_date)
|
||||||
values (?, ?, ?, ?) on conflict(user_id, name, form, measure_type) DO NOTHING
|
values (?, ?, ?, ?, date()) on conflict(user_id, name, form, measure_type, plan_date) DO NOTHING
|
@ -1,2 +1,2 @@
|
|||||||
insert into modified_amts(user_id, name, form, measure_type, amt)
|
insert into modified_amts(user_id, name, form, measure_type, amt, plan_date)
|
||||||
values (?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type) do update set amt=excluded.amt
|
values (?, ?, ?, ?, ?, date()) on conflict (user_id, name, form, measure_type, plan_date) do update set amt=excluded.amt
|
3
kitchen/src/web/storage/store_extra_items.sql
Normal file
3
kitchen/src/web/storage/store_extra_items.sql
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user