implement basic multi-day support

This commit is contained in:
Khaïs COLIN 2025-10-28 20:26:30 +01:00
parent 4a3ffb5ac6
commit d7fc530201
Signed by: logistic-bot
SSH key fingerprint: SHA256:RlpiqKeXpcPFZZ4y9Ou4xi2M8OhRJovIwDlbCaMsuAo
10 changed files with 98 additions and 44 deletions

BIN
foods.db

Binary file not shown.

View file

@ -1,14 +1,17 @@
UPDATE INSERT OR REPLACE INTO
food day_serving (day, food_id, servings_eaten)
SET VALUES (
actual_servings = MAX( CURRENT_DATE,
(SELECT ?1,
actual_servings MAX(coalesce((
SELECT
servings_eaten
FROM FROM
food day_serving
WHERE id = ?1) - 1, WHERE
0) food_id = ?1
WHERE AND day = CURRENT_DATE
id = ?1 ) - 1,
RETURNING 0
actual_servings ), 0)
) RETURNING servings_eaten;

View file

@ -4,9 +4,14 @@ SELECT
name, name,
kc_per_serving, kc_per_serving,
target_servings, target_servings,
actual_servings, coalesce(day_serving.servings_eaten, 0),
color color
FROM FROM
food food
LEFT JOIN
day_serving
ON
day_serving.food_id = food.id
AND day_serving.day = CURRENT_DATE
WHERE WHERE
id = ?1 food.id = ?1;

View file

@ -1,12 +1,18 @@
SELECT SELECT
id, food.id,
portion, food.portion,
name, food.name,
kc_per_serving, food.kc_per_serving,
target_servings, food.target_servings,
actual_servings, coalesce(day_serving.servings_eaten, 0) as servings_eaten,
color food.color
FROM FROM
food food
LEFT JOIN
day_serving
ON
day_serving.food_id = food.id
WHERE
coalesce(day_serving.day, CURRENT_DATE) = CURRENT_DATE
ORDER BY ORDER BY
sort_order, name; sort_order, name;

View file

@ -1,6 +1,11 @@
SELECT SELECT
SUM(kc_per_serving * actual_servings) AS kc, SUM(kc_per_serving * servings_eaten) AS kc,
SUM(protein_per_portion * actual_servings) AS protein, SUM(protein_per_portion * servings_eaten) AS protein,
SUM(fiber_per_portion * actual_servings) AS bs SUM(fiber_per_portion * servings_eaten) AS bs
FROM FROM
food food
LEFT JOIN
day_serving
ON
day_serving.food_id = food.id
AND day_serving.day = CURRENT_DATE;

View file

@ -1,9 +1,17 @@
UPDATE INSERT OR REPLACE INTO
food day_serving (day, food_id, servings_eaten)
SET actual_servings = ( VALUES (
SELECT actual_servings CURRENT_DATE,
FROM food ?1,
WHERE id = ?1 coalesce((
) + 1 SELECT
WHERE id = ?1 servings_eaten
RETURNING actual_servings FROM
day_serving
WHERE
food_id = ?1
AND day = CURRENT_DATE
) + 1,
1
)
) RETURNING servings_eaten;

View file

@ -56,7 +56,7 @@ struct Food {
name: String, name: String,
kc_per_serving: i32, kc_per_serving: i32,
target_servings: i32, target_servings: i32,
actual_servings: i32, servings_eaten: i32,
color: String, color: String,
} }
@ -69,7 +69,7 @@ impl Food {
name: row.get(2)?, name: row.get(2)?,
kc_per_serving: row.get(3)?, kc_per_serving: row.get(3)?,
target_servings: row.get(4)?, target_servings: row.get(4)?,
actual_servings: row.get(5)?, servings_eaten: row.get(5)?,
color: row.get(6)?, color: row.get(6)?,
}) })
} }
@ -127,6 +127,8 @@ struct AppError(anyhow::Error);
// Tell axum how to convert `AppError` into a response. // Tell axum how to convert `AppError` into a response.
impl IntoResponse for AppError { impl IntoResponse for AppError {
fn into_response(self) -> Response { fn into_response(self) -> Response {
let error = &self.0;
error!(?error, "error returned to client");
( (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", self.0), format!("Something went wrong: {}", self.0),
@ -152,12 +154,13 @@ fn get_version(tx: &Transaction) -> anyhow::Result<usize> {
Ok(tx.query_one(include_str!("get_version.sql"), (), |row| row.get(0))?) Ok(tx.query_one(include_str!("get_version.sql"), (), |row| row.get(0))?)
} }
fn get_migrations() -> [&'static str; 4] { fn get_migrations() -> [&'static str; 5] {
[ [
include_str!("migrations/1.sql"), include_str!("migrations/1.sql"),
include_str!("migrations/2.sql"), include_str!("migrations/2.sql"),
include_str!("migrations/3.sql"), include_str!("migrations/3.sql"),
include_str!("migrations/4.sql"), include_str!("migrations/4.sql"),
include_str!("migrations/5.sql"),
] ]
} }

23
src/migrations/5.sql Normal file
View file

@ -0,0 +1,23 @@
CREATE TABLE day_serving (
-- ISO-8601
day TEXT NOT NULL DEFAULT CURRENT_DATE,
food_id INTEGER NOT NULL,
servings_eaten INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY(day, food_id),
FOREIGN KEY(food_id) REFERENCES food(id)
) STRICT;
INSERT INTO
day_serving (food_id, servings_eaten)
SELECT
id,
actual_servings
FROM
food;
ALTER TABLE
food
DROP COLUMN
actual_servings;
PRAGMA user_version = 5;

View file

@ -1,8 +1,9 @@
UPDATE UPDATE
food day_serving
SET SET
actual_servings = MAX(?2, 0) servings_eaten = MAX(?2, 0)
WHERE WHERE
id = ?1 food_id = ?1
AND day = CURRENT_DATE
RETURNING RETURNING
actual_servings servings_eaten

View file

@ -20,7 +20,7 @@
{% for counter in self::range(10) %} {% for counter in self::range(10) %}
{% if loop.index as i32 <= food.target_servings %} {% if loop.index as i32 <= food.target_servings %}
<label class="ok"> <label class="ok">
{% if loop.index as i32 <= food.actual_servings %} {% if loop.index as i32 <= food.servings_eaten %}
<input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 - 1}}" hx-target="#card-{{ food.id }}" value="●" class="checked"> <input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 - 1}}" hx-target="#card-{{ food.id }}" value="●" class="checked">
{% else %} {% else %}
<input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 }}" hx-target="#card-{{ food.id }}" value="●" class="unchecked"> <input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 }}" hx-target="#card-{{ food.id }}" value="●" class="unchecked">
@ -28,7 +28,7 @@
</label> </label>
{% else %} {% else %}
<label class="bad"> <label class="bad">
{% if loop.index as i32 <= food.actual_servings %} {% if loop.index as i32 <= food.servings_eaten %}
<input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 - 1}}" hx-target="#card-{{ food.id }}" value="●" class="checked"> <input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 - 1}}" hx-target="#card-{{ food.id }}" value="●" class="checked">
{% else %} {% else %}
<input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 }}" hx-target="#card-{{ food.id }}" value="●" class="unchecked"> <input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 }}" hx-target="#card-{{ food.id }}" value="●" class="unchecked">