datum och natt_datum i garmin_puls/garmin_stress/garmin_puls_stress beräknas
automatiskt av Supabase från lokal_tid. De kan aldrig bli fel och ska ALDRIG
uppdateras manuellt.
datum = lokal_tid::date (kalenderdatum)natt_datum = lokal_tid::date + 1 om kl ≥ 18, annars lokal_tid::dategarmin_puls.natt_datum = daglig_logg.datumSELECT datum, COUNT(*) as antal_rader
FROM garmin_puls
WHERE user_id = 1
GROUP BY datum
ORDER BY datum;
SELECT natt_datum, COUNT(*) as antal_rader
FROM garmin_puls
WHERE user_id = 1
GROUP BY natt_datum
ORDER BY natt_datum;
SELECT EXTRACT(HOUR FROM lokal_tid) AS timme, COUNT(*) AS antal
FROM garmin_puls
WHERE lokal_tid >= '2026-05-06 00:00:00'
AND lokal_tid < '2026-05-07 00:00:00'
AND user_id = 1
GROUP BY timme
ORDER BY timme;
SELECT datum, COUNT(*) as antal_artefakter
FROM garmin_puls
WHERE user_id = 1
AND EXTRACT(SECOND FROM lokal_tid) = 44
GROUP BY datum
ORDER BY datum;
SELECT conname, pg_get_constraintdef(oid)
FROM pg_constraint
WHERE conrelid = 'garmin_puls'::regclass;
SELECT column_name, data_type, is_generated, generation_expression
FROM information_schema.columns
WHERE table_name IN ('garmin_puls', 'garmin_stress', 'garmin_puls_stress')
AND column_name IN ('datum', 'natt_datum', 'lokal_tid')
ORDER BY table_name, column_name;
SELECT lokal_tid, puls_bpm, natt_datum
FROM garmin_puls
WHERE natt_datum = 'YYYY-MM-DD'
AND user_id = 1
ORDER BY lokal_tid;
DELETE FROM garmin_puls
WHERE user_id = 1
AND datum IN ('YYYY-MM-DD', 'YYYY-MM-DD')
AND EXTRACT(SECOND FROM lokal_tid) = 44;
-- OBS: använd lokal_tid-intervall för att vara exakt, inte datum
DELETE FROM garmin_puls
WHERE user_id = 1
AND natt_datum IN ('YYYY-MM-DD', 'YYYY-MM-DD');
DELETE FROM garmin_stress
WHERE user_id = 1
AND natt_datum IN ('YYYY-MM-DD', 'YYYY-MM-DD');
TRUNCATE TABLE garmin_puls, garmin_stress, garmin_puls_stress RESTART IDENTITY;
TRUNCATE TABLE kost_log, huvudvark_log, daglig_logg, interventioner RESTART IDENTITY;
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'daglig_logg'
ORDER BY ordinal_position;
SELECT conrelid::regclass AS tabell, conname AS constraint_namn
FROM pg_constraint
WHERE contype = 'u'
AND conrelid::regclass::text IN
('daglig_logg','huvudvark_log','kost_log','interventioner','garmin_puls','garmin_stress')
ORDER BY tabell;
Krävs för att dag_zon_v (Graf 10) ska vara snabb. Utan detta index scannas hela tabellen vid varje sidhämtning.
CREATE INDEX IF NOT EXISTS idx_garmin_puls_user_datum
ON garmin_puls (user_id, datum);
Beräknar andelen mätningar (08–20) per pulszon och dag. Baseras på MAXPULS 170. Används av Graf 10 (Zontrend dagtid) i halsa-dashboarden.
CREATE OR REPLACE VIEW dag_zon_v AS
SELECT
user_id,
datum,
ROUND(
COUNT(*) FILTER (WHERE puls_bpm < 85)::numeric / NULLIF(COUNT(*),0) * 100, 2
) AS vila,
ROUND(
COUNT(*) FILTER (WHERE puls_bpm >= 85 AND puls_bpm < 102)::numeric / NULLIF(COUNT(*),0) * 100, 2
) AS z1_aterhämtning,
ROUND(
COUNT(*) FILTER (WHERE puls_bpm >= 102 AND puls_bpm < 119)::numeric / NULLIF(COUNT(*),0) * 100, 2
) AS z2_aerob_bas,
ROUND(
COUNT(*) FILTER (WHERE puls_bpm >= 119 AND puls_bpm < 136)::numeric / NULLIF(COUNT(*),0) * 100, 2
) AS z3_aerob,
ROUND(
COUNT(*) FILTER (WHERE puls_bpm >= 136 AND puls_bpm < 153)::numeric / NULLIF(COUNT(*),0) * 100, 2
) AS z4_troskel,
ROUND(
COUNT(*) FILTER (WHERE puls_bpm >= 153)::numeric / NULLIF(COUNT(*),0) * 100, 2
) AS z5_anaerob
FROM garmin_puls
WHERE EXTRACT(HOUR FROM lokal_tid) >= 8
AND EXTRACT(HOUR FROM lokal_tid) < 20
GROUP BY user_id, datum;
Zongränser (MAXPULS 170): Vila <85 · Z1 85–101 · Z2 102–118 · Z3 119–135 · Z4 136–152 · Z5 ≥153 bpm.
garmin_upload.pygarmin_upload.py. Kan förekomma i gammal data.datum och natt_datum är immutabla och beräknade.
Python-skripten skickar ALDRIG dessa fält till Supabase (bara lokal_tid).(user_id, lokal_tid) – dubbletter kan aldrig uppstå.