mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
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:
1 parent
b8e78a78b3
commit
f2e32afb24
12 files changed
+347
-46
No files matched your search
@@ -0,0 +1,51 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
/**
|
||||
* v1.39.4 — the environment module stores coordinates at 1 decimal (about
|
||||
* 11 km), not 2 (about 1 km). The geocoder is where a picked city's
|
||||
* coordinates enter, so its results must already be coarse.
|
||||
*/
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe("coarse location", () => {
|
||||
it("rounds a coordinate to one decimal", async () => {
|
||||
const { roundCoarse } = await import("../open-meteo");
|
||||
expect(roundCoarse(52.5163)).toBe(52.5);
|
||||
expect(roundCoarse(13.3777)).toBe(13.4);
|
||||
expect(roundCoarse(-33.8688)).toBe(-33.9);
|
||||
// Already coarse values pass through unchanged.
|
||||
expect(roundCoarse(48.1)).toBe(48.1);
|
||||
});
|
||||
|
||||
it("hands back geocoding results at one decimal", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue(
|
||||
Response.json(
|
||||
{
|
||||
results: [
|
||||
{
|
||||
name: "Berlin",
|
||||
latitude: 52.52437,
|
||||
longitude: 13.41053,
|
||||
country: "Germany",
|
||||
timezone: "Europe/Berlin",
|
||||
},
|
||||
],
|
||||
},
|
||||
{ status: 200 },
|
||||
),
|
||||
),
|
||||
);
|
||||
const { geocodeLocation } = await import("../open-meteo");
|
||||
const [hit] = await geocodeLocation("Berlin");
|
||||
expect(hit).toMatchObject({ lat: 52.5, lon: 13.4 });
|
||||
});
|
||||
});
|
||||
@@ -40,7 +40,7 @@ export const OPEN_METEO_ATTRIBUTION = "Weather data by Open-Meteo.com";
|
||||
|
||||
/** One geocoding match — coarse city placement for the home-location picker. */
|
||||
export interface GeocodeResult {
|
||||
/** Rounded coarse latitude (2 dp — ~1 km, the privacy floor). */
|
||||
/** Rounded coarse latitude (1 dp, about 11 km, the privacy floor). */
|
||||
lat: number;
|
||||
/** Rounded coarse longitude (2 dp). */
|
||||
lon: number;
|
||||
@@ -68,9 +68,16 @@ export interface DailyEnvironmentObservation {
|
||||
weatherCode: number | null;
|
||||
}
|
||||
|
||||
/** Round a coordinate to 2 decimals (~1 km) — the coarse-location privacy floor. */
|
||||
/**
|
||||
* Round a coordinate to 1 decimal, the coarse-location privacy floor: about
|
||||
* 11 km north-south and 7 km east-west at 50° N, the size of a town or a
|
||||
* city district rather than a street. The weather reanalysis behind the
|
||||
* archive is gridded at 9 to 25 km, so a finer value buys no better weather.
|
||||
* Every stored coordinate of this module goes through here (v1.39.4; it was
|
||||
* 2 decimals, about 1 km, before).
|
||||
*/
|
||||
export function roundCoarse(value: number): number {
|
||||
return Math.round(value * 100) / 100;
|
||||
return Math.round(value * 10) / 10;
|
||||
}
|
||||
|
||||
function firstNumber(value: unknown): number | null {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import { prisma } from "@/lib/db";
|
||||
import {
|
||||
fetchDailyEnvironment,
|
||||
roundCoarse,
|
||||
type DailyEnvironmentObservation,
|
||||
} from "@/lib/environment/open-meteo";
|
||||
import type { EnvironmentLocationSource } from "@/generated/prisma/client";
|
||||
@@ -256,8 +257,10 @@ function buildUpsert(
|
||||
obs: DailyEnvironmentObservation,
|
||||
) {
|
||||
const data = {
|
||||
lat: loc.lat,
|
||||
lon: loc.lon,
|
||||
// Already coarse at the source; rounded again so a per-day row never holds
|
||||
// more precision than the privacy floor, whatever resolved it.
|
||||
lat: roundCoarse(loc.lat),
|
||||
lon: roundCoarse(loc.lon),
|
||||
locationLabel: loc.label,
|
||||
source: loc.source,
|
||||
tempMin: obs.tempMin,
|
||||
|
||||
Reference in new issue
Block a user