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
food
SET
actual_servings = MAX(
(SELECT
actual_servings
FROM
food
WHERE id = ?1) - 1,
0)
WHERE
id = ?1
RETURNING
actual_servings
INSERT OR REPLACE INTO
day_serving (day, food_id, servings_eaten)
VALUES (
CURRENT_DATE,
?1,
MAX(coalesce((
SELECT
servings_eaten
FROM
day_serving
WHERE
food_id = ?1
AND day = CURRENT_DATE
) - 1,
0
), 0)
) RETURNING servings_eaten;

View file

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

View file

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

View file

@ -1,6 +1,11 @@
SELECT
SUM(kc_per_serving * actual_servings) AS kc,
SUM(protein_per_portion * actual_servings) AS protein,
SUM(fiber_per_portion * actual_servings) AS bs
SUM(kc_per_serving * servings_eaten) AS kc,
SUM(protein_per_portion * servings_eaten) AS protein,
SUM(fiber_per_portion * servings_eaten) AS bs
FROM
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
food
SET actual_servings = (
SELECT actual_servings
FROM food
WHERE id = ?1
) + 1
WHERE id = ?1
RETURNING actual_servings
INSERT OR REPLACE INTO
day_serving (day, food_id, servings_eaten)
VALUES (
CURRENT_DATE,
?1,
coalesce((
SELECT
servings_eaten
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,
kc_per_serving: i32,
target_servings: i32,
actual_servings: i32,
servings_eaten: i32,
color: String,
}
@ -69,7 +69,7 @@ impl Food {
name: row.get(2)?,
kc_per_serving: row.get(3)?,
target_servings: row.get(4)?,
actual_servings: row.get(5)?,
servings_eaten: row.get(5)?,
color: row.get(6)?,
})
}
@ -127,6 +127,8 @@ struct AppError(anyhow::Error);
// Tell axum how to convert `AppError` into a response.
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let error = &self.0;
error!(?error, "error returned to client");
(
StatusCode::INTERNAL_SERVER_ERROR,
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))?)
}
fn get_migrations() -> [&'static str; 4] {
fn get_migrations() -> [&'static str; 5] {
[
include_str!("migrations/1.sql"),
include_str!("migrations/2.sql"),
include_str!("migrations/3.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
food
day_serving
SET
actual_servings = MAX(?2, 0)
servings_eaten = MAX(?2, 0)
WHERE
id = ?1
food_id = ?1
AND day = CURRENT_DATE
RETURNING
actual_servings
servings_eaten

View file

@ -20,7 +20,7 @@
{% for counter in self::range(10) %}
{% if loop.index as i32 <= food.target_servings %}
<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">
{% else %}
<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>
{% else %}
<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">
{% else %}
<input type="button" hx-post="/set/{{ food.id }}/to/{{ loop.index as i32 }}" hx-target="#card-{{ food.id }}" value="●" class="unchecked">