feat(pwa): optimize Mila app icons with safe-zone padding to prevent Android/iOS zoom crop
This commit is contained in:
@@ -30,6 +30,10 @@ from zoneinfo import ZoneInfo
|
||||
|
||||
import duckdb
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
logger = logging.getLogger("datastore")
|
||||
|
||||
def generate_ulid() -> str:
|
||||
@@ -225,6 +229,34 @@ class DatastoreEngine:
|
||||
restingHeartRate INTEGER,
|
||||
raw JSON
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW v_growth AS
|
||||
SELECT
|
||||
id,
|
||||
subject,
|
||||
actor,
|
||||
start,
|
||||
start AT TIME ZONE 'Europe/Berlin' AS local_time,
|
||||
CAST(start AT TIME ZONE 'Europe/Berlin' AS DATE) AS log_date,
|
||||
CAST(json_extract(payload, '$.weight_kg') AS DOUBLE) AS weight_kg,
|
||||
CAST(json_extract(payload, '$.height_cm') AS DOUBLE) AS height_cm,
|
||||
CAST(json_extract(payload, '$.head_circ_cm') AS DOUBLE) AS head_circ_cm,
|
||||
note,
|
||||
created_at
|
||||
FROM events
|
||||
WHERE kind = 'growth.measurement' AND deleted_at IS NULL;
|
||||
|
||||
CREATE OR REPLACE VIEW v_growth_daily AS
|
||||
SELECT
|
||||
subject,
|
||||
log_date,
|
||||
ROUND(AVG(weight_kg), 3) AS weight_kg,
|
||||
ROUND(AVG(height_cm), 1) AS height_cm,
|
||||
ROUND(AVG(head_circ_cm), 1) AS head_circ_cm,
|
||||
COUNT(*) AS measurement_count
|
||||
FROM v_growth
|
||||
GROUP BY subject, log_date
|
||||
ORDER BY log_date DESC;
|
||||
""")
|
||||
|
||||
def _load_all_data(self):
|
||||
@@ -387,6 +419,13 @@ class DatastoreEngine:
|
||||
elif kind.startswith("sleep."):
|
||||
if p1.get("type") == p2.get("type"):
|
||||
return True
|
||||
elif kind.startswith("growth."):
|
||||
w1, w2 = p1.get("weight_kg"), p2.get("weight_kg")
|
||||
h1, h2 = p1.get("height_cm"), p2.get("height_cm")
|
||||
match_w = (w1 is not None and w2 is not None and abs(float(w1) - float(w2)) < 0.001)
|
||||
match_h = (h1 is not None and h2 is not None and abs(float(h1) - float(h2)) < 0.001)
|
||||
if match_w or match_h:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_duplicate_event(
|
||||
@@ -870,6 +909,11 @@ class DatastoreEngine:
|
||||
if fee:
|
||||
res["feeding"] = fee[0]
|
||||
|
||||
# 6. Growth latest measurement
|
||||
grw = self.query(f"SELECT * FROM v_growth WHERE subject = '{subject}' ORDER BY start DESC LIMIT 1")
|
||||
if grw:
|
||||
res["growth_latest"] = grw[0]
|
||||
|
||||
return res
|
||||
|
||||
def find_places_near(
|
||||
@@ -952,6 +996,44 @@ class DatastoreEngine:
|
||||
"bottle_count": daily[0],
|
||||
"total_consumed_ml": daily[1],
|
||||
}
|
||||
elif kind.startswith("growth"):
|
||||
row = self.con.execute(
|
||||
"SELECT weight_kg, height_cm, head_circ_cm FROM v_growth WHERE subject = ? AND log_date = ? ORDER BY start DESC LIMIT 1",
|
||||
[subject, date_str],
|
||||
).fetchone()
|
||||
if row:
|
||||
w_kg, h_cm, hc_cm = row[0], row[1], row[2]
|
||||
p_row = self.con.execute("SELECT birthdate FROM people WHERE id = ?", [subject]).fetchone()
|
||||
bdate = p_row[0] if p_row and p_row[0] else datetime.date(2026, 3, 14)
|
||||
if isinstance(bdate, str):
|
||||
bdate = datetime.date.fromisoformat(bdate)
|
||||
age_days = (target_date - bdate).days
|
||||
age_months = max(0.0, age_days / 30.4375)
|
||||
try:
|
||||
from who_growth import format_growth_feedback, calculate_percentile
|
||||
fb_text = format_growth_feedback(w_kg, h_cm, age_months, date_str)
|
||||
fb_res: Dict[str, Any] = {
|
||||
"day_total_formatted": fb_text,
|
||||
"age_months": round(age_months, 2),
|
||||
}
|
||||
if w_kg is not None:
|
||||
pw = calculate_percentile("weight", w_kg, age_months)
|
||||
fb_res["weight_kg"] = w_kg
|
||||
fb_res["weight_percentile"] = pw["percentile"]
|
||||
fb_res["weight_percentile_str"] = pw["percentile_str"]
|
||||
if h_cm is not None:
|
||||
ph = calculate_percentile("height", h_cm, age_months)
|
||||
fb_res["height_cm"] = h_cm
|
||||
fb_res["height_percentile"] = ph["percentile"]
|
||||
fb_res["height_percentile_str"] = ph["percentile_str"]
|
||||
return fb_res
|
||||
except Exception as e:
|
||||
logger.warning(f"Error computing growth feedback: {e}")
|
||||
return {
|
||||
"day_total_formatted": f"Wachstum ({date_str}): Gewicht {w_kg} kg, Größe {h_cm} cm",
|
||||
"weight_kg": w_kg,
|
||||
"height_cm": h_cm,
|
||||
}
|
||||
return {}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -1013,11 +1095,11 @@ def run_mcp_server():
|
||||
TOOLS = [
|
||||
{
|
||||
"name": "datastore_add_event",
|
||||
"description": "Log an event into the structured datastore (e.g. nutrition.intake, sleep.session, feeding.bottle). Computes all macros and totals deterministically.",
|
||||
"description": "Log an event into the structured datastore (e.g. nutrition.intake, sleep.session, feeding.bottle, growth.measurement). Computes all macros, totals, and WHO percentiles deterministically.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {"type": "string", "description": "e.g. 'nutrition.intake', 'sleep.session', 'feeding.bottle'"},
|
||||
"kind": {"type": "string", "description": "e.g. 'nutrition.intake', 'sleep.session', 'feeding.bottle', 'growth.measurement'"},
|
||||
"subject": {"type": "string", "description": "'moritz', 'amelie', or 'mila'"},
|
||||
"actor": {"type": "string", "description": "'agent:koch', 'agent:hebamme', 'moritz', 'amelie'"},
|
||||
"payload": {"type": "object", "description": "Detailed payload matching the kind schema"},
|
||||
|
||||
Reference in New Issue
Block a user