Catch up documentation on tonight's real-data/timezone work
Changelog rows for the COALESCE upsert fix, both timezone fixes, the ENABLE_LVGMC_FTP_JOBS/ENABLE_HARMONIE_JOBS split, and the VPS compose LVGMC templating fix — none had one yet. Updated the stale "currently deployed" references (were stille446f1ff) to the actual final release (3eddf95). Recorded the DMI Harmonie research (no API key needed, new domain, verified live) and the 5-step verification plan for actually wiring it up, deferred to a dedicated session. One line about the VPS's first FTP fetch is marked pending — a background watch is still confirming it as of this commit; will follow up once it resolves.
This commit is contained in:
+6
-5
@@ -6,21 +6,22 @@ This directory contains the working documentation for the WeatherTool modernizat
|
||||
|
||||
- Windows is restricted to source editing, review, and Git operations. Rocky is the sole compile, build, development-runtime, and test environment. The Ubuntu VPS is a deployment target only.
|
||||
- A production-like staging copy runs through Docker Compose on Rocky Linux at `http://192.168.1.101:9190`.
|
||||
- Staging ingests real LVĢMC station and water-temperature observations from the free `data.gov.lv` open-data feeds; synthetic data generation was removed once real ingestion was verified. The private LVGMC FTP feed and DMI HARMONIE forecast feed are each gated independently (`ENABLE_LVGMC_FTP_JOBS`, `ENABLE_HARMONIE_JOBS`, both default off) since real credentials for the two arrive on different timelines — FTP creds were obtained and verified 2026-08-23; HARMONIE's are still pending.
|
||||
- The safe scheduled jobs (open-data station ingestion, GRIB cleanup) run in staging; the legacy FTP/HARMONIE jobs stay off by default so placeholder credentials can't crash-loop the app.
|
||||
- Both Rocky and the VPS now ingest real data from two independent LVĢMC sources at once: the free `data.gov.lv` open-data feed and the private FTP feed (`ftp.meteo.lv`, real credentials obtained and verified 2026-08-23). The two are gated independently (`ENABLE_LVGMC_FTP_JOBS`, `ENABLE_HARMONIE_JOBS`) since real credentials for FTP and DMI HARMONIE arrive on different timelines — FTP is on; HARMONIE's credentials turned out not to be needed at all (DMI dropped its API key requirement — confirmed live, see the roadmap changelog) but wiring it up is deferred to a dedicated verification session, since the GRIB-parsing and map-rendering code has real, untested risk (a hardcoded crop/rotation calibration that may not match the current model grid).
|
||||
- A real UTC-vs-local timezone mismatch between the two sources (open-data's timestamps were UTC, FTP's already local, both stored in the same column with no conversion) was found and fixed before enabling both together; both `weather` tables were backed up and wiped for a clean, consistently-timestamped restart.
|
||||
- The safe scheduled jobs (open-data station ingestion, GRIB cleanup) always run; the FTP job now runs too since real credentials exist; HARMONIE's job stays off pending its own implementation work.
|
||||
- PostgreSQL is private to the project Compose network; only the Scala application publishes a host port.
|
||||
- The operator-facing workspaces now use the Latvian workflow names **Stacijas**, **Kartes**, **Faktiskā**, **Ūdens**, **Brīdinājumi**, **Apskats**, **Arhīvs**, **Harmonie**, and **LVĢMC**. Kartes retains custom analytical map outputs, while Faktiskā is a fixed 13-position, latest-temperature newsroom workflow with a locked 3840×1440 export.
|
||||
- Faktiskā symbol placement is automatic after manual image selection and is anchored to each rendered temperature badge.
|
||||
- **Ūdens** auto-populates its six ranges on load with real per-zone water-temperature min/max (65 LVĢMC stations classified into the 6 named zones), with manual override and reset still available. Uses separate authoritative 1920×1080 and 3840×1440 production templates; both exports have been visually validated.
|
||||
- **Brīdinājumi** renders current LVĢMC warning polygons over a production border overlay with feathered severity fills, plus draggable/resizable per-warning weather-symbol placement. Its lon/lat-to-pixel projection is an affine fit calibrated against the same validated city pixel positions Kartes/Faktiskā already use, replacing an earlier bounding-box calibration that drifted up to ~200px on the 3840 canvas.
|
||||
- Confirmed local Monda Regular/Bold files provide interface and generated-graphic typography; weather symbols use normalized transparent image assets.
|
||||
- Release `e446f1ff` is deployed as immutable image `weathertool:e446f1ff094d428bba0921d0058570764779328e`; release `d7439a2b` remains the immediate application rollback. This release is a frontend design pass: a reworked visual palette (layered blue-gray background, kept the original accent blue for buttons/icons), Faktiskā decluttered (collapsible station list and map settings, a fixed cascade bug in the resolution buttons, "Kartes noformējums" restructured and translated to Latvian, including a fresh-eyes-review catch on `WindInputs` that had been missed), a temperature-badge vertical-centering fix, Brīdinājumi promoted into the visible header nav with icons throughout, and the weather-icon-picker CSS (shared by Faktiskā and Brīdinājumi) consolidated into one real definition instead of a cross-file collision masked by `!important`. No database or scheduler changes — application-only.
|
||||
- Release `3eddf95` is deployed as immutable image `weathertool:3eddf95008b103f34d50dbc86c5634d3c0fa3523`; release `e446f1ff` remains the immediate application rollback. On top of the earlier frontend design pass (visual palette rework, Faktiskā decluttering, header nav, weather-icon CSS consolidation), this release fixes the UTC-vs-local timezone mismatch described above, hardens the `weather` table upsert to be non-destructive across sources (`COALESCE` instead of a blind overwrite), and splits the scheduler flag so FTP and HARMONIE can be enabled independently. `deploy/vps/compose.yml` was also fixed to actually read `LVGMC_*` from `.env.staging` (it was hardcoding inert placeholders directly in the file before, so real credentials alone wouldn't have taken effect) — real credentials and `ENABLE_LVGMC_FTP_JOBS=true` are live on the VPS as of 2026-08-23.
|
||||
- The isolated VPS UAT stack is running and healthy: WeatherTool is bound to `127.0.0.1:8002`, Authelia to `127.0.0.1:9091`, and PostgreSQL has no host port. Public access is routed through Cloudflare, Nginx, and Authelia.
|
||||
- Cloudflare delegation is active, strict origin TLS covers only `laikapstak.li` and `auth.laikapstak.li`, and the public Nginx/Authelia login flow is operational without changing the existing HOP site.
|
||||
- The VPS `weather` table now holds only real LVĢMC open-data station observations; its original 14-day synthetic dataset was backed up to `/srv/weathertool/backups/pre-real-data-release/` and wiped on 2026-08-23 as part of the `138f57c8` release. The scheduled open-data job runs on the VPS (minutes 15/45); the legacy FTP/HARMONIE jobs stay off pending real credentials.
|
||||
- The VPS `weather` table was wiped twice on 2026-08-23: first to remove the original 14-day synthetic dataset (backed up to `/srv/weathertool/backups/pre-real-data-release/`), then again after the UTC/local timezone fix (backed up to `/srv/weathertool/backups/pre-timezone-fix-wipe/`) so the two real sources it now holds — open-data (minutes 15/45) and FTP (minutes 11/13/23/30) — are consistently timestamped from a clean start.
|
||||
- Approved 1920×1080 and 3840×1440 PNG production bases are now the rendering source for Faktiskā and Ūdens temperatūra; code draws only the changing values, selected weather symbols, and wind data over those fixed newsroom graphics.
|
||||
- Browser branding assets and Latvian Open Graph/Twitter metadata are included for favicon, Apple home-screen icon, and link-preview support. Public crawler access still depends on the Nginx/Authelia policy used for the metadata and preview image.
|
||||
- Browser verification is complete for the deployed `e446f1ff` release: exact release image smoke-tested on Rocky before transfer (bundle hash and headless-browser screenshot matched the known-good local build), checksum verified on both ends, container health/loopback/public HTTPS confirmed. Newsroom testers will be given the domain first, without walkthrough, to see how intuitive the workflow is before any guidance is provided.
|
||||
- Browser and API verification is complete for the deployed `3eddf95` release: exact release image smoke-tested on Rocky before transfer (bundle hash and correct-local-time API responses matched the known-good local build), checksum verified on both ends, container health/loopback/public HTTPS confirmed. VPS's first scheduled FTP fetch specifically (not just open-data, which fires more often) is being watched for overnight to confirm outbound FTP actually works from the VPS network before relying on it unattended. Newsroom testers will be given the domain first, without walkthrough, to see how intuitive the workflow is before any guidance is provided.
|
||||
- Frontend dependency maintenance is complete: Solid runtime and Vite tooling were updated, obsolete packages were removed, TypeScript checking was added, and a clean Rocky `npm ci`, typecheck, production build, full audit, and production-only audit all pass with zero known vulnerabilities.
|
||||
- This is not yet approved or hardened for workplace production.
|
||||
|
||||
|
||||
+80
-5
@@ -7,7 +7,7 @@ This document tracks proposed WeatherTool improvements. Work should be delivered
|
||||
- **Windows source workspace:** source editing, review, and Git operations only; do not install dependencies, compile, build, run, or test here.
|
||||
- **Rocky development and staging:** the sole compile, build, development-runtime, and test environment, with production-like Docker staging at `http://192.168.1.101:9190`.
|
||||
- **Git over SSH:** Windows pushes reviewed commits to a private bare repository on Rocky; the Rocky staging checkout pulls those commits and rebuilds.
|
||||
- **Ubuntu VPS deployment:** release `e446f1ff` is publicly operational behind Cloudflare strict TLS, Nginx, and Authelia, ingesting real LVĢMC open-data station/water-temperature observations on a schedule (synthetic data wiped 2026-08-23); the VPS does not compile or build the project.
|
||||
- **Ubuntu VPS deployment:** release `3eddf95` is publicly operational behind Cloudflare strict TLS, Nginx, and Authelia, ingesting real LVĢMC open-data station/water-temperature observations on a schedule, plus real LVGMC FTP station data as of 2026-08-23 (`ENABLE_LVGMC_FTP_JOBS=true`, real credentials); the `weather` table was wiped and re-populated fresh once the UTC/local timezone mismatch between the two sources was fixed. The VPS does not compile or build the project.
|
||||
- **Workplace production:** remains separate until changes are reviewed, tested, and explicitly approved for workplace use.
|
||||
|
||||
Do not synchronize `.env`, database directories, generated dependencies, build output, or provider credentials between machines.
|
||||
@@ -213,10 +213,7 @@ running in parallel until each real source is proven, not cut over in one step.
|
||||
different timelines and a single flag would have enabled Harmonie
|
||||
(still placeholder) the moment FTP's were ready — caught this before
|
||||
it could crash-loop the app (the Grib job was ~15 min from its first
|
||||
scheduled run when this was noticed). FTP enabled on Rocky; Harmonie
|
||||
stays off pending real DMI credentials, still the user's own
|
||||
follow-up. Worth confirming DMI's endpoint migration away from
|
||||
`dmigw.govcloud.dk` before wiring anything.
|
||||
scheduled run when this was noticed). FTP enabled on Rocky.
|
||||
Also found and fixed a real UTC-vs-local timezone mismatch while
|
||||
verifying FTP alongside open-data: both write into `weather.dateTime`
|
||||
with no conversion, but the open-data portal's DATETIME is UTC while
|
||||
@@ -231,6 +228,79 @@ running in parallel until each real source is proven, not cut over in one step.
|
||||
always carry null visibilityMin/dewPoint/sunDuration and an empty
|
||||
phenomena array — a blind overwrite would silently erase FTP's real
|
||||
values for those fields whenever open-data's write landed later.
|
||||
- [x] Went live on both Rocky and the VPS with all of the above: backed up
|
||||
and wiped both `weather` tables (Rocky: `/tmp` scratch backup this
|
||||
session; VPS: `/srv/weathertool/backups/pre-timezone-fix-wipe/`) now
|
||||
that the timezone bug is fixed, so history going forward is correctly
|
||||
and consistently timestamped rather than mixing the old UTC-mislabeled
|
||||
open-data rows with correctly-local FTP rows. Fixed a second real bug
|
||||
found only when wiring up the VPS specifically: `deploy/vps/compose.yml`
|
||||
hardcoded `LVGMC_USER`/`PASSWORD`/`URL` to inert placeholder strings
|
||||
directly in the file (unlike `POSTGRES_*`, which already read from
|
||||
`.env.staging`) — real credentials in `.env.staging` alone would have
|
||||
had no effect until this was switched to the same `${VAR}` substitution
|
||||
pattern. VPS now runs release `3eddf95`/`dc04f66` with real FTP
|
||||
credentials (added directly on the VPS by the user, never typed into
|
||||
this session) and `ENABLE_LVGMC_FTP_JOBS=true`; watching the container
|
||||
logs for VPS's first scheduled FTP fetch specifically (not just the
|
||||
open-data job, which fires more often and was initially mistaken for
|
||||
it on one premature check) to confirm real rows land before leaving it
|
||||
unattended overnight — outbound FTP from the VPS network is a real,
|
||||
non-hypothetical risk worth checking, not something safe to assume
|
||||
just because it worked from Rocky. **Result pending as of this
|
||||
writing — update this line once confirmed.**
|
||||
- [x] Researched DMI HARMONIE credentials while waiting on the above and
|
||||
confirmed live (not just from search results, which claimed a specific
|
||||
date that wasn't independently verified): DMI's Forecast Data EDR and
|
||||
STAC APIs no longer validate an API key at all — a direct request to
|
||||
both the old `dmigw.govcloud.dk` and the new `opendataapi.dmi.dk` with
|
||||
*no* `api-key` parameter returned real GRIB/STAC data from both. New
|
||||
collection-items STAC path confirmed as
|
||||
`https://opendataapi.dmi.dk/v1/forecastdata/collections/harmonie_dini_sf/items`,
|
||||
with the same `properties.modelRun`/`properties.datetime` schema
|
||||
`fetch.dmi.FetchService` already expects — no response-shape changes
|
||||
needed, just the domain and dropping real key handling (the app's own
|
||||
config loader still needs *some* non-empty string for
|
||||
`HARMONIE_EDR_API_KEY`/`HARMONIE_STAC_API_KEY`, but DMI ignores it).
|
||||
Not wired up yet — see "Harmonie verification (planned)" below.
|
||||
- [ ] **Harmonie verification (planned, next session)** — the user confirmed
|
||||
this data is used daily today (exported as a numbered PNG frame
|
||||
sequence per forecast hour, zipped, imported into After Effects for
|
||||
the on-air animated forecast loop — confirmed directly in
|
||||
`SlideShow.tsx`'s `downloadImagesAsZip`), so getting it working
|
||||
correctly matters, but the code has real, untested risk after a long
|
||||
dormant period (no HARMONIE fixtures, no configured provider access
|
||||
in staging, ever, per the existing product-workflow notes). Real data
|
||||
access itself is solved (see above); what's unverified is everything
|
||||
downstream. Plan, in order:
|
||||
1. Fetch one real current GRIB file and confirm `parse.grib.GribParser`
|
||||
decodes it cleanly with sane values — the low-level GRIB2 parsing
|
||||
reads discipline/category/product generically per the spec, so
|
||||
likely still fine, but not yet checked against a real current file.
|
||||
2. Verify each frontend field renderer (`draw/temperature.ts`,
|
||||
`windDirection.ts`, `precipitation.ts`, `snowDepth.ts`) against real
|
||||
data via screenshot — check in particular whether "Crop Latvia"
|
||||
(`interfaces.ts`'s `CROP_BOUNDS = { x: 1906-1-660, y: 840, width:
|
||||
660, height: 620, angle: 26 }`) still aligns correctly. This is a
|
||||
hardcoded pixel/grid offset into HARMONIE's specific grid
|
||||
domain/rotation — there's already one commented-out earlier version
|
||||
of this constant, meaning it's been hand-recalibrated at least once
|
||||
before, so it's a real, not hypothetical, risk if DMI's grid
|
||||
definition has shifted at all since.
|
||||
3. Verify the frame-export ZIP workflow end-to-end with real
|
||||
multi-hour data — frame count, naming, timing — against the user's
|
||||
own older HARMONIE exports and existing After Effects projects,
|
||||
which they have on hand for direct comparison.
|
||||
4. Only then: update `HARMONIE_EDR_URL`/`HARMONIE_STAC_URL` to
|
||||
`opendataapi.dmi.dk` (see above), set
|
||||
`HARMONIE_EDR_API_KEY`/`HARMONIE_STAC_API_KEY` to any non-empty
|
||||
placeholder (DMI no longer validates them), flip
|
||||
`ENABLE_HARMONIE_JOBS=true`.
|
||||
5. Lower priority, later: the Harmonie workspace is still raw/English
|
||||
dev-tool UI ("Crop Latvia", "Contour", "Interpolate" checkboxes,
|
||||
unstyled file list) unlike the polished, Latvian, newly-redesigned
|
||||
Faktiskā/Ūdens/Brīdinājumi workspaces — worth the same treatment
|
||||
once the data/rendering itself is trusted.
|
||||
- [x] Add real water temperature data for Ūdens: `fetch.lvgmc.WaterTemperatureService`
|
||||
fetches LVĢMC's open hydrological data (`data.gov.lv`, same free CKAN
|
||||
API), mapping one representative real station per named zone (coastal
|
||||
@@ -326,3 +396,8 @@ Record completed work here by date and commit after the Git workflow is establis
|
||||
| 2026-08-23 | `f78b0a26` | Deploy the full frontend design pass (background/accent rework, Faktiskā decluttering, temperature-badge centering fix, header nav) to the VPS — no database or scheduler changes, application-only release | Yes — exact release image smoke-tested on Rocky (bundle hash and headless-browser screenshot confirmed against the known-good local build) before transfer; checksum verified on both ends; `app` service recreated without touching PostgreSQL/Authelia; confirmed healthy container, matching bundle hash on the VPS, working loopback and public HTTPS (302 unauthenticated page, 401 unauthenticated API), and real warnings/water-temperature API responses |
|
||||
| 2026-08-23 | `d7439a2b` | Fresh-eyes review of the whole design pass turned up two real issues, fixed here: `WindInputs` had no Latvian branching and was showing "Wind direction"/"Wind speed"/"Gusts" in English right under the freshly-translated "Temperatūra un vējš" heading on Faktiskā (threaded the same `productionTemplate` flag `MapView.tsx` already uses elsewhere), and a confirmed-unreferenced `.assignedSymbol` CSS rule was removed. The review also flagged two things left alone: a pre-existing (not introduced this session) `.symbolPalette`/`.selectedPreview`/`.currentSymbol` cross-file class collision in `weatherIcons.css` currently masked by `!important` rather than actually resolved, and a naming nit on "Mākoņi pilsētām" (the section assigns arbitrary weather symbols, not just clouds) — both worth a look later, not blocking | Yes — Rocky typecheck, build, and headless-browser screenshot confirming the wind fields now render in Latvian; deployed to VPS in release `d7439a2b`, exact release image smoke-tested (bundle hash matched) before transfer, checksum verified, container healthy, public HTTPS/API checks passing |
|
||||
| 2026-08-23 | `e446f1f` | Consolidate `.symbolPalette`/`.selectedPreview`/`.currentSymbol` (used by both Faktiskā's `IconInputs.tsx` and Brīdinājumi's `Warnings.tsx`) into one real definition in `weatherIcons.css`, the file both actually import, removing the dead/duplicate versions in `mapGraphics.css` and the `!important` size patches that were masking the collision. "Mākoņi pilsētām" naming confirmed intentional — the newsroom users are non-technical and the same people who'll use this long-term, so intuitive-but-imprecise beats literally-correct-but-jargony | Yes — Rocky typecheck, build, headless-browser screenshot on Faktiskā (pixel-identical to before), and computed-style verification on both Faktiskā and Brīdinājumi confirming identical resolved CSS with no `!important`; caught one incidental fix for free (`.currentSymbol.empty`'s Inter font was silently losing to the `!important` rule, normal cascade now applies it correctly); deployed to VPS in release `e446f1ff`, exact release image smoke-tested (bundle hash matched) before transfer, checksum verified, container healthy, public HTTPS/API checks passing |
|
||||
| 2026-08-23 | `2b5dff6` | Make the `weather` table upsert non-destructive: `ON CONFLICT DO UPDATE` was a blind full-row overwrite, so open-data's always-null `visibilityMin`/`dewPoint`/`sunDuration` and always-empty `phenomena` would silently erase real FTP values whenever open-data's write landed later for the same row. Switched to `COALESCE(excluded.field, weather.field)`, with `NULLIF` against an empty array specifically for `phenomena` since Scala's `List[String]` never maps to SQL `NULL` | Yes — Scala tests; found while wiring up real FTP credentials, before any real dual-source writes had happened |
|
||||
| 2026-08-23 | `cab94d0` | Fix a real UTC-vs-local timezone mismatch: the open-data portal's `DATETIME` is UTC, stored into `weather.dateTime` with no conversion, while FTP's "Laiks" column is already local and also stored as-is — silently present since open-data went live (`893a09a`), invisible until a second, correctly-labeled source existed alongside it. Fixed by converting to `Europe/Riga` at ingestion in `OpenDataStationService` | Yes — Scala tests; verified live: at real local time 20:31 EEST the fix produces `dateTime=20:00` instead of the previous `17:00`, matching what FTP writes for the same real hour |
|
||||
| 2026-08-23 | `3315f00` | Fix the same UTC-vs-local mismatch in Ūdens's `observedAt` display (`WaterTemperatureService`) — same open-data portal, same root cause, display-only (internal recency filtering was already self-consistent either way) | Yes — Scala tests; verified live via `/api/water-temperatures`: `observedAt` now matches real local time |
|
||||
| 2026-08-23 | `3eddf95` | Split `ENABLE_LEGACY_PROVIDER_JOBS` into independent `ENABLE_LVGMC_FTP_JOBS`/`ENABLE_HARMONIE_JOBS` — real LVGMC FTP credentials arrived today, real DMI HARMONIE credentials haven't, and the combined flag would have enabled both together, crash-looping the app on HARMONIE's still-placeholder values via `parMapN` | Yes — Scala tests; caught before it could happen (the Grib job was ~15 min from its first scheduled run when noticed) and reverted within under a minute; re-verified after the fix that only "Fetch Weather Stations" scheduled, not "Fetch Grib" |
|
||||
| 2026-08-23 | `dc04f66` | Fix `deploy/vps/compose.yml` hardcoding `LVGMC_USER`/`PASSWORD`/`URL` to inert placeholder strings directly in the file (unlike `POSTGRES_*`, which already read from `.env.staging`) — real credentials added to `.env.staging` alone would have had no effect until this switched to the same `${VAR}` substitution pattern. Enabled `ENABLE_LVGMC_FTP_JOBS` on the VPS | Yes — `docker compose config` syntax valid; VPS `app` container came up healthy with real credentials loaded (would have crashed immediately on missing-config if not) |
|
||||
|
||||
Reference in New Issue
Block a user