mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
Fetch the Open-Meteo air-quality feed (CAMS, domains=auto) with the weather: 17 hourly variables folded into local days by fixed rules (means and maxima only with 18 of 24 hours, ozone as the highest 8-hour running mean, UV only with the midday hours), a value the feed did not cover stored as null, never as zero. Requests are cut into 90-day chunks, newest first, and chunks before mid 2022 ask for the eight pollutant and index variables only. An air-quality failure never costs the weather row; the day stays unfetched and the nightly gap fill (up to 90 days per account, newest first) asks for it again. Every Open-Meteo request asks an instance-wide budget kept in Postgres first (500 a minute, 4000 an hour, 8000 a day, weighted by the hosted pricing rule). A refused request is not sent and the run ends as done with budget_blocked. OPENMETEO_AIR_QUALITY_URL and ENVIRONMENT_AIR_QUALITY_DISABLED are honoured; the account switch lives on PATCH /api/environment/preferences. The home, each dated location period and each stored day keep their coarse location sealed as one value under healthlog/environment-location/v1. The writers write the sealed value only, the readers open it and fall back to the readable columns for rows the free-text encryption backfill has not reached, and the backfill now seals all three and clears the readable columns. The backup carries the location readable in a portable file and sealed in a disaster-recovery file; a restore seals either under the receiving key. Three correlation channels join the environment family (PM2.5, the ozone 8-hour high, the day's highest pollen count), read through the averaged same-day window and left empty while air quality is off. The Coach and MCP gain get_environment: module-gated, clamped to the lookback limit, no coordinate or place name anywhere, with provenance and the attribution lines. The settings section gets the switch, the coverage and the newest day's values; the dashboard shows a quiet chip on a day with high pollen, a warm night or very poor air. Copernicus and Open-Meteo are credited next to the values.
53 lines
1.2 KiB
TypeScript
53 lines
1.2 KiB
TypeScript
/**
|
|
* v1.42 (#615) — the dashboard environment chip stays quiet: no flag, no
|
|
* chip; an unfetched air-quality part raises no air or pollen flag.
|
|
*/
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { environmentChipFlags } from "../environment-chip";
|
|
|
|
const POLLEN = {
|
|
alder: 0,
|
|
birch: 0,
|
|
grass: 0,
|
|
mugwort: 0,
|
|
olive: 0,
|
|
ragweed: 0,
|
|
};
|
|
|
|
describe("environmentChipFlags", () => {
|
|
it("raises nothing on an ordinary day", () => {
|
|
expect(
|
|
environmentChipFlags({
|
|
date: "2026-10-07",
|
|
tempMin: 9,
|
|
airQuality: { eaqiMax: 35, pollen: POLLEN },
|
|
}),
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("names the high pollen kinds, the warm night and the very poor air", () => {
|
|
expect(
|
|
environmentChipFlags({
|
|
date: "2026-07-20",
|
|
tempMin: 21,
|
|
airQuality: { eaqiMax: 84, pollen: { ...POLLEN, grass: 70 } },
|
|
}),
|
|
).toEqual([
|
|
{ kind: "pollen", kinds: ["grass"] },
|
|
{ kind: "hotNight" },
|
|
{ kind: "veryPoorAir" },
|
|
]);
|
|
});
|
|
|
|
it("raises no air flag for a day whose air quality was not fetched", () => {
|
|
expect(
|
|
environmentChipFlags({
|
|
date: "2026-07-20",
|
|
tempMin: 10,
|
|
airQuality: null,
|
|
}),
|
|
).toEqual([]);
|
|
});
|
|
});
|