feat: Ali je vroče? ERA5 — live climate explorer for Slovenia#448
feat: Ali je vroče? ERA5 — live climate explorer for Slovenia#448kesma01 wants to merge 6 commits into
Conversation
Adds a full ERA5-Land climate dashboard for Slovenia as a new page at /ali-je-vroce-era5/, powered by a dedicated Flask sidecar that serves precomputed SQLite data. New nav link "ERA5" added alongside existing "Ali je vroče?". - code/ali-je-vroce-era5/: SolidJS app — today card, distribution chart, regression panel, station map (Highcharts), season heatmap, SPEI heatmap, hero cards, trend/year-round charts; tropical/sea-level sections feature-flagged off for Slovenia - scripts/era5/: mk_collect.py, mk_precompute.py (SQLite), mk_sidecar.py (Flask /api/live/* endpoints), si.yaml config, locales, entrypoint - deployment/: Dockerfile.era5-sidecar, Dockerfile.website.era5, default.conf.era5.template (nginx proxy to sidecar on :5052) - compose.yaml: era5-sidecar + website-era5 services added - eleventy.config.mjs: era5 entry wired into Vite build - styles/era5.css: ERA5 dashboard styles - pages/ali-je-vroce-era5/index.md: new page mounting the SolidJS app - pages/_includes/base.html: ERA5 nav link + footer link added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions - si.yaml: enable drought_trend_chart, tropical_days_chart, tropical_nights_chart, sea_level_section (was false for all four) - mk_sidecar.py: add /api/live/tropical_days and /api/live/tropical_nights endpoints using Negative Binomial GLM (statsmodels) — correct model for integer count data with overdispersion; includes 95% CI and PI bands, current-year exclusion from fit, streak filter, lapse-rate correction - pyproject.toml: add statsmodels>=0.14 dependency - TropicalChart.tsx: fix endpoint URLs to /api/live/ prefix (via SIDECAR env var); update model_used type to boolean | "nb" - public/data/: add flood-stats.json and 25 flood PNG overlays (10–250 cm) for the Koper sea-level widget - README.md: document ERA5 sidecar, Docker setup, and chart inventory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… through an exception' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
… through an exception' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
|
We use kubernetes and not docker-compose. The config is AFAIK only provided for local development needs. I think if you want to have a separate service, we'd have to setup its running on the (staging) server. |
Two new GitHub Actions workflows build and push to GHCR on merge to main: - ghcr.io/podnebnik/era5-sidecar (Flask API + data collection) - ghcr.io/podnebnik/website-era5 (nginx with /api/live/ proxy) deployment/kubernetes/ provides illustrative manifests (PVC, Deployment with initContainer + two containers, Service, nightly CronJob) for the DevOps team to adapt into their FluxCD/Kustomize GitOps repo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new /ali-je-vroce-era5/ SolidJS-based “live ERA5-Land climate explorer” page for Slovenia, backed by a Python sidecar that collects ERA5-Land data from Open‑Meteo, precomputes derived statistics into SQLite, and serves the results via /api/live/* behind an nginx (and dev-server) reverse proxy.
Changes:
- Adds a new ERA5 page entrypoint and a substantial new SolidJS/Highcharts UI surface (today status, trends, regression, SPEI, tropical days/nights, sea-level widget, etc.).
- Adds Python data pipeline scripts (
mk_collect.py,mk_precompute.py) plus containerization and deployment manifests (Docker, Compose, Kubernetes) for a sidecar API. - Integrates the feature into the existing site with a nav/footer link, Tailwind CSS import, and a local dev proxy for
/api/live.
Reviewed changes
Copilot reviewed 47 out of 73 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Adds Leaflet + type packages to lockfile. |
| styles/main.css | Imports ERA5-specific stylesheet. |
| scripts/era5/si.yaml | Slovenia config: stations, feature flags, map + branding metadata. |
| scripts/era5/pyproject.toml | Declares Python sidecar dependencies. |
| scripts/era5/mk_precompute.py | Precomputes SQLite tables (daily windows, annual trend, season heatmap). |
| scripts/era5/mk_collect.py | Collects ERA5-Land daily series from Open‑Meteo and writes per-station CSVs. |
| scripts/era5/docker-entrypoint.sh | Container entrypoint: initial collect, precompute, then run sidecar. |
| README.md | Documents local Docker setup and explains ERA5 stack. |
| public/data/flood-stats.json | Static flood impact metadata for sea-level widget. |
| pages/ali-je-vroce-era5/index.md | Adds the new page and bootstraps the SolidJS entry module. |
| pages/_includes/base.html | Adds ERA5 link to nav and footer. |
| package.json | Adds Leaflet and Leaflet types to dependencies. |
| eleventy.config.mjs | Adds dev proxy for /api/live to sidecar URL. |
| deployment/kubernetes/era5-service.yaml | Kubernetes Service for the ERA5 pod. |
| deployment/kubernetes/era5-pvc.yaml | Persistent volume claim for ERA5 collected/precomputed data. |
| deployment/kubernetes/era5-deployment.yaml | K8s Deployment for website + sidecar in one pod plus initContainer. |
| deployment/kubernetes/era5-cronjob.yaml | Nightly refresh CronJob (collect + precompute). |
| deployment/Dockerfile.website.era5 | Builds an ERA5-enabled website image with nginx proxy template. |
| deployment/Dockerfile.era5-sidecar | Builds the Python sidecar image using uv. |
| deployment/default.conf.era5.template | nginx config to proxy /api/live/* to sidecar container. |
| compose.yaml | Adds era5-sidecar service and wires dev env/proxy. |
| code/ali-je-vroce-era5/types.ts | TypeScript API response types for the ERA5 frontend. |
| code/ali-je-vroce-era5/entry.tsx | SolidJS entrypoint mounting the ERA5 app with query client. |
| code/ali-je-vroce-era5/components/TodayTrendChart.tsx | Highcharts trend chart for today’s date context. |
| code/ali-je-vroce-era5/components/TodayLast7Chart.tsx | Last-7-days category strip chart. |
| code/ali-je-vroce-era5/components/TodayFlag.tsx | SVG-based Slovenian flag overlay widget by category. |
| code/ali-je-vroce-era5/components/TodayCard.tsx | “Today status” card with controls, gauge, last-7-days, and narrative. |
| code/ali-je-vroce-era5/components/StationMap.tsx | Highcharts map-based station selector. |
| code/ali-je-vroce-era5/components/RegressionSection.tsx | Regression UI section (variable/day/window/method controls). |
| code/ali-je-vroce-era5/components/RegressionPanel.tsx | Regression panel state store + toolbar + charts. |
| code/ali-je-vroce-era5/charts/YearRoundChart.tsx | Year-round trend/decade column visualization. |
| code/ali-je-vroce-era5/charts/TropicalChart.tsx | Tropical days/nights chart with NB fit bands and controls. |
| code/ali-je-vroce-era5/charts/TodayGauge.tsx | Highcharts gauge for today percentile/category. |
| code/ali-je-vroce-era5/charts/SpeiTrendChart.tsx | SPEI station/season trend scatter + stats. |
| code/ali-je-vroce-era5/charts/SpeiHeatmap.tsx | SPEI seasonal heatmap grid with animation + tooltip. |
| code/ali-je-vroce-era5/charts/SeasonHeatmap.tsx | Seasonal temperature heatmap grid with animation + tooltip. |
| code/ali-je-vroce-era5/charts/SeaLevelWidget.tsx | Leaflet-based sea-level rise widget with flood overlays. |
| code/ali-je-vroce-era5/charts/RegressionChart.tsx | Regression scatter + trend line + CI band rendering. |
| code/ali-je-vroce-era5/charts/DistributionChart.tsx | KDE distribution chart with percentile zone bands. |
| code/ali-je-vroce-era5/charts/AnnualTrendChart.tsx | Annual trend chart (hist + projection bands + points). |
| code/ali-je-vroce-era5/api.ts | Fetch helpers for all /api/live/* endpoints. |
| code/ali-je-vroce-era5/AliJeVroceERA5.tsx | Main page composition and section wiring / feature gating. |
| .github/workflows/docker-era5-website.yaml | CI workflow to build/push website-era5 image. |
| .github/workflows/docker-era5-sidecar.yaml | CI workflow to build/push era5-sidecar image. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | ||
|
|
||
| cache_session = requests_cache.CachedSession(".openmeteo_cache", expire_after=-1) | ||
| cache_session.verify = False # disable SSL verification (corporate proxy workaround) | ||
| retry_session = retry(cache_session, retries=3, backoff_factor=2) | ||
| openmeteo = openmeteo_requests.Client(session=retry_session) |
| df_new = pd.DataFrame({ | ||
| "date": pd.date_range( | ||
| start=pd.to_datetime(daily.Time(), unit="s", utc=True), | ||
| end=pd.to_datetime(daily.TimeEnd(), unit="s", utc=True), | ||
| freq=pd.Timedelta(seconds=daily.Interval()), | ||
| inclusive="left", | ||
| ).date, | ||
| "temperature_max": daily.Variables(0).ValuesAsNumpy(), | ||
| "temperature_min": daily.Variables(1).ValuesAsNumpy(), | ||
| "temperature_mean": daily.Variables(2).ValuesAsNumpy(), | ||
| "precipitation_sum": daily.Variables(3).ValuesAsNumpy(), | ||
| "et0_evapotranspiration": daily.Variables(4).ValuesAsNumpy(), | ||
| }) |
| location /api/live/ { | ||
| proxy_pass http://${ERA5_SIDECAR_HOST}:5052; | ||
| proxy_set_header Host $host; | ||
| proxy_set_header X-Real-IP $remote_addr; | ||
| proxy_read_timeout 30s; | ||
| add_header Access-Control-Allow-Origin *; | ||
| } |
| const onUpdate = () => { renderFloodCanvas(); setViewFrac(calcViewFrac()); }; | ||
| leafletMap.on('move zoom moveend zoomend viewreset resize', onUpdate); | ||
| new ResizeObserver(onUpdate).observe(mapWrapEl); | ||
|
|
- Remove TLS verify=False from mk_collect.py (MITM risk) - Make precipitation/ET0 DataFrame columns conditional on DAILY_VARIABLES to prevent IndexError when SPEI features are disabled - Remove flask_cors CORS(app) wildcard from mk_sidecar.py — sidecar is always accessed via nginx proxy, no CORS headers needed - Remove Access-Control-Allow-Origin header from nginx config (was also redundant since the sidecar itself set it) - Disconnect ResizeObserver in SeaLevelWidget onCleanup to prevent leak on component unmount Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
This PR adds a new page
/ali-je-vroce-era5/— a live ERA5-Land climate explorer for Slovenia powered by an independent Python sidecar service. It runs alongside the existing website stack without touching any existing code paths./ali-je-vroce-era5/scripts/era5/) fetches ERA5-Land data from Open-Meteo, precomputes statistics into SQLite, and serves them at/api/live/*/api/live/*to the sidecar — frontend sees a single origin, no CORSscripts/era5/si.yamlso sections can be toggled without code changesWhat's new
Charts / sections (all confirmed working in Docker)
New files
code/ali-je-vroce-era5/— 22 SolidJS/TypeScript components and chartsscripts/era5/— sidecar:mk_collect.py,mk_precompute.py,mk_sidecar.py,si.yaml,pyproject.toml,docker-entrypoint.sh,locales/deployment/Dockerfile.era5-sidecar,Dockerfile.website.era5,default.conf.era5.templatepages/ali-je-vroce-era5/index.md— new pagestyles/era5.css— component stylespublic/data/flood-stats.json+public/data/flood/flood-{010..250}cm.png— static flood data for Koper sea-level widgetModified files (minimal, non-breaking)
pages/_includes/base.html— adds ERA5 nav + footer linkstyles/main.css—@import "./era5.css"eleventy.config.mjs— ERA5 page routecompose.yaml—era5-sidecarservice +era5-datavolumepackage.json/yarn.lock— no new runtime deps beyond what's already in the projectREADME.md— ERA5 section with Docker setup instructionsDocker test results
The full stack was tested locally using the production Docker setup (
Dockerfile.website.era5+Dockerfile.era5-sidecar) — the same build that will run on the Hetzner servers. Both images built cleanly, all 8 page sections rendered correctly, API routes confirmed via network inspector.Deploy notes
The sidecar needs to run as a separate process/container alongside nginx:
ERA5_SIDECAR_HOSTenv var tells nginx where to proxy/api/live/(default:era5-sidecarin compose)DATA_DIRdefaults to/app/data/si— mount a persistent volume heremk_collect.py) keeps data currentTest plan
docker-compose -f compose.yaml upstarts cleanly withera5-sidecaralongside existing services/ali-je-vroce-era5/loads and shows today's temperature percentile for Slovenia/ali-je-vroce/page unaffected🤖 Generated with Claude Code