mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
feat(environment): fill air quality for every past day with entries
A background job fetches air quality, pollen and UV for every earlier local day with a reading, mood entry, dose, symptom or workout: after the update (boot and nightly discovery), when the home is set and when air quality is switched on. A day with a weather row gets its air quality; a day without one gets both, at the covering location period or else at the home, also before the home was set. Days the source does not reach (before 2013 in Europe, before August 2022 elsewhere) stay empty and are not counted. Pending days are grouped by coarse location and cut into ranges of up to 90 days, joining days at most 14 apart, newest first. A run holds itself to 300 of the account's 400 daily calls and half of each instance window, stores its progress on the user (migration 0391) and sends a follow-up while it makes progress; a budget stop waits for the next discovery. The Location & weather card shows the progress. The air-quality client now asks pollen and dust from 2021 and skips the request outside Europe before CAMS global starts.
This commit is contained in:
1 parent
7340cd3ab7
commit
d81a6ddf25
30 files changed
+1747
-52
No files matched your search
@@ -18,6 +18,7 @@ import { homeLocationSchema } from "@/lib/validations/environment";
|
||||
import { roundCoarse } from "@/lib/environment/open-meteo";
|
||||
import { sealLocation } from "@/lib/environment/location-cipher";
|
||||
import { enqueueEnvironmentFetch } from "@/lib/jobs/environment-fetch";
|
||||
import { enqueueAirQualityHistory } from "@/lib/jobs/environment-air-quality-history";
|
||||
|
||||
export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
const { user } = await requireAuth();
|
||||
@@ -67,6 +68,9 @@ export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
// Kick a lookback refresh so recent days populate promptly. No-ops cleanly
|
||||
// when no worker is bound; the nightly cron still covers it.
|
||||
await enqueueEnvironmentFetch({ userId: user.id });
|
||||
// And the past: air quality for every earlier day with entries, in the
|
||||
// background and inside the request budget. A no-op while it is off.
|
||||
await enqueueAirQualityHistory(user.id);
|
||||
|
||||
annotate({ action: { name: "environment.home.set" } });
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { prisma } from "@/lib/db";
|
||||
import { environmentPreferencesSchema } from "@/lib/validations/environment";
|
||||
import { isAirQualityOperatorDisabled } from "@/lib/environment/open-meteo-air-quality";
|
||||
import { enqueueEnvironmentFetch } from "@/lib/jobs/environment-fetch";
|
||||
import { enqueueAirQualityHistory } from "@/lib/jobs/environment-air-quality-history";
|
||||
|
||||
export const PATCH = apiHandler(async (request: NextRequest) => {
|
||||
const { user } = await requireAuth();
|
||||
@@ -57,6 +58,8 @@ export const PATCH = apiHandler(async (request: NextRequest) => {
|
||||
before?.environmentAirQualityEnabled === false
|
||||
) {
|
||||
await enqueueEnvironmentFetch({ userId: user.id });
|
||||
// Switched on: fill the past as well, every earlier day with entries.
|
||||
await enqueueAirQualityHistory(user.id);
|
||||
}
|
||||
|
||||
annotate({
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
* (the account switch, the operator switch, how many days carry air-quality
|
||||
* values), the newest stored day for the dashboard chips (with its air
|
||||
* quality only while the part is on), and every attribution line in
|
||||
* `attributions`.
|
||||
* `attributions`, and the progress of the air-quality history backfill
|
||||
* (`airQuality.history`).
|
||||
*/
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import { apiSuccess } from "@/lib/api-response";
|
||||
@@ -23,6 +24,7 @@ import { isAirQualityOperatorDisabled } from "@/lib/environment/open-meteo-air-q
|
||||
import { environmentAttributionLines } from "@/lib/environment/air-quality-contract";
|
||||
import { readLocation } from "@/lib/environment/location-cipher";
|
||||
import { isAirQualityActive } from "@/lib/environment/service";
|
||||
import { readAirQualityHistoryState } from "@/lib/environment/air-quality-history";
|
||||
import { ENVIRONMENT_FETCH_QUEUE } from "@/lib/jobs/environment-fetch";
|
||||
import { readQueueFailureForUser } from "@/lib/jobs/job-failures";
|
||||
|
||||
@@ -54,6 +56,7 @@ export const GET = apiHandler(async () => {
|
||||
homeSince: true,
|
||||
timezone: true,
|
||||
environmentAirQualityEnabled: true,
|
||||
environmentAqHistoryJson: true,
|
||||
},
|
||||
}),
|
||||
prisma.environmentTravelLocation.findMany({
|
||||
@@ -157,6 +160,19 @@ export const GET = apiHandler(async () => {
|
||||
const accountEnabled = profile?.environmentAirQualityEnabled ?? true;
|
||||
const operatorDisabled = isAirQualityOperatorDisabled();
|
||||
const airActive = isAirQualityActive(accountEnabled);
|
||||
// The history backfill's progress, while the part is on: what the last run
|
||||
// counted, so the card can say how far the past has been filled.
|
||||
const historyState = airActive
|
||||
? readAirQualityHistoryState(profile?.environmentAqHistoryJson)
|
||||
: null;
|
||||
const history = historyState
|
||||
? {
|
||||
total: historyState.total,
|
||||
done: historyState.done,
|
||||
complete: historyState.complete,
|
||||
checkedAt: historyState.checkedAt,
|
||||
}
|
||||
: null;
|
||||
const latestYear = Number(
|
||||
(latestAir?.date ?? latest?.date ?? new Date().toISOString()).slice(0, 4),
|
||||
);
|
||||
@@ -219,6 +235,7 @@ export const GET = apiHandler(async () => {
|
||||
days: airDays,
|
||||
latestDate: latestAir?.date ?? null,
|
||||
domain: latestAir?.aqDomain ?? null,
|
||||
history,
|
||||
},
|
||||
latestDay,
|
||||
// null = the last background runs did not fail (or there is no queue to
|
||||
|
||||
Reference in new issue
Block a user