fix(privacy): store environment coordinates at 1 decimal instead of 2

The home location of the environment module was rounded to 2 decimals,
about 1 km, which places a home in a neighbourhood rather than the town the
schema comment promised. The weather archive it feeds is gridded at 9 to
25 km, so the extra precision bought nothing.

Every stored coordinate of the module now goes through the same 1-decimal
rounding (about 11 km north-south, 7 km east-west at 50° N): the home, the
travel periods, the geocoder's results, the per-day weather rows, and a
restore of a file written with finer values. Migration 0361 rounds the
existing users, travel periods and per-day rows; running it twice changes
nothing more. The answers keep their shape, plain numbers. The API
descriptions and the schema comment say 1 decimal now.
This commit is contained in:
Marc-André Bombeck committed 2026-09-27 10:37:37 +02:00
1 parent b8e78a78b3
commit f2e32afb24
12 files changed
+347 -46

No files matched your search

@@ -12,9 +12,11 @@
-- columns until the backfill has reached a row. The old columns are dropped in
-- a later release, once the backfill reports nothing left on every instance.
--
-- Additive only: no table rewrite, no data change. The geometry loses its NOT
-- NULL because every new track is written to the ciphertext column and the
-- readable one stays empty.
-- The encryption half is additive: no table rewrite, no data change. The
-- geometry loses its NOT NULL because every new track is written to the
-- ciphertext column and the readable one stays empty. The last block below is
-- the one data change in this file: it coarsens the environment module's
-- stored coordinates.
ALTER TABLE "workout_routes" ADD COLUMN "geometry_encrypted" BYTEA;
ALTER TABLE "workout_routes" ALTER COLUMN "geometry" DROP NOT NULL;
@@ -35,3 +37,22 @@ CREATE INDEX IF NOT EXISTS "workout_routes_geometry_backfill_idx"
CREATE INDEX IF NOT EXISTS "practitioners_contact_backfill_idx"
ON "practitioners" ("user_id")
WHERE "phone" IS NOT NULL OR "location" IS NOT NULL;
-- The environment module's stored coordinates drop to 1 decimal, about 11 km
-- north-south and 7 km east-west at 50° N. They were rounded to 2 decimals
-- (about 1 km), which places a home in a neighbourhood rather than a town;
-- the weather reanalysis the module reads is gridded at 9 to 25 km, so the
-- coarser value costs no accuracy. The app rounds every new write the same
-- way. Idempotent: rounding an already-rounded value changes nothing.
UPDATE "users"
SET "home_lat" = round("home_lat"::numeric, 1)::double precision,
"home_lon" = round("home_lon"::numeric, 1)::double precision
WHERE "home_lat" IS NOT NULL OR "home_lon" IS NOT NULL;
UPDATE "environment_travel_locations"
SET "lat" = round("lat"::numeric, 1)::double precision,
"lon" = round("lon"::numeric, 1)::double precision;
UPDATE "environment_contexts"
SET "lat" = round("lat"::numeric, 1)::double precision,
"lon" = round("lon"::numeric, 1)::double precision;
+9 -6
View File
@@ -81,10 +81,13 @@ model User {
timezone String @default("Europe/Berlin")
// v1.25 (W-ENV) — opt-in environmental-context module home location. A coarse,
// user-set city (rounded lat/lon, ~city granularity — never a GPS track) the
// nightly weather fetch resolves against when no travel override covers the
// day. City-level granularity is the privacy floor: the weather model only
// needs ~10–25 km. All nullable — an account without the module enabled (or
// user-set city (never a GPS track) the nightly weather fetch resolves
// against when no travel override covers the day. Stored rounded to 1
// decimal (`roundCoarse`): about 11 km north-south and 7 km east-west at
// 50° N, a town or a city district. That is the privacy floor, and the
// weather reanalysis is gridded at 9 to 25 km, so nothing finer is needed.
// Up to v1.39.3 the rounding was 2 decimals (about 1 km, a neighbourhood);
// migration 0361 rounded the existing rows. All nullable — an account without the module enabled (or
// without a home set) simply has no environment rows. `homeTimezone` anchors
// the day-key an observation is stored under (it can differ from the display
// `timezone` above for an account that tracks weather for another locale).
@@ -1055,8 +1058,8 @@ model EnvironmentContext {
/// YYYY-MM-DD anchored to the resolved location's timezone.
date String
/// Coarse resolved location for the day (rounded lat/lon + label + the source
/// that won precedence).
/// Coarse resolved location for the day (lat/lon rounded to 1 decimal, about
/// 11 km, + label + the source that won precedence).
lat Float
lon Float
/// @internal: human-readable label of the resolved location, stored as