Backfills knoebels."LZ_open_meteo_hourly" from 2024-05-15 (start of queue
collection) and keeps it current. Imperial units; timestamps stored as naive
America/New_York to line up with the rest of the knoebels schema.
- weather_logger.py: --backfill uses the ERA5 Archive API; default mode does a
self-healing recent sync via the Forecast API (past_days=7, upsert on conflict)
- docker-compose.yml: add `weather` oneshot service (also align scraper to its
live `oneshot` profile)
- systemd/knb-weather.{service,timer}: nox user timer, every 6h (linger enabled)
- queries/dm_knb_queue_weather.sql: dm_knb_queue_weather view joining the full
queue-time series to hourly weather (hour-bucketed), with a WMO code decode
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
3.0 KiB
SQL
62 lines
3.0 KiB
SQL
-- Full queue-time time series joined to hourly Open-Meteo weather.
|
|
-- Queue readings (sub-hour, every ~5 min) are bucketed to the hour and matched
|
|
-- to knoebels."LZ_open_meteo_hourly". Both sides are naive America/New_York, so
|
|
-- the hour buckets line up directly with no timezone math. LEFT JOIN keeps every
|
|
-- queue row even if an hour's weather is missing (e.g. the once-a-year DST hour).
|
|
SELECT a.time_stamp,
|
|
a._id AS ride_id,
|
|
TRIM(r.name) AS ride_name,
|
|
TRIM(c."name") AS category,
|
|
ROUND((r.minimum_height_requirement * 39.37)::numeric, 0) AS minimum_height_requirement_inches,
|
|
ROUND((r.minimum_unaccompanied_height_requirement * 39.37)::numeric, 0) AS minimum_unaccompanied_height_requirement_inches,
|
|
ROUND((r.maximum_height_requirement * 39.37)::numeric, 0) AS maximum_height_requirement_inches,
|
|
r."location" AS coordinates,
|
|
a.queue_time AS queue_time_sec,
|
|
w.temperature_2m AS temperature_f,
|
|
w.apparent_temperature AS feels_like_f,
|
|
w.relative_humidity_2m AS humidity_pct,
|
|
w.precipitation AS precipitation_in,
|
|
w.rain AS rain_in,
|
|
w.weather_code AS wmo_weather_code,
|
|
CASE w.weather_code
|
|
WHEN 0 THEN 'Clear sky'
|
|
WHEN 1 THEN 'Mainly clear'
|
|
WHEN 2 THEN 'Partly cloudy'
|
|
WHEN 3 THEN 'Overcast'
|
|
WHEN 45 THEN 'Fog'
|
|
WHEN 48 THEN 'Depositing rime fog'
|
|
WHEN 51 THEN 'Light drizzle'
|
|
WHEN 53 THEN 'Moderate drizzle'
|
|
WHEN 55 THEN 'Dense drizzle'
|
|
WHEN 56 THEN 'Light freezing drizzle'
|
|
WHEN 57 THEN 'Dense freezing drizzle'
|
|
WHEN 61 THEN 'Slight rain'
|
|
WHEN 63 THEN 'Moderate rain'
|
|
WHEN 65 THEN 'Heavy rain'
|
|
WHEN 66 THEN 'Light freezing rain'
|
|
WHEN 67 THEN 'Heavy freezing rain'
|
|
WHEN 71 THEN 'Slight snowfall'
|
|
WHEN 73 THEN 'Moderate snowfall'
|
|
WHEN 75 THEN 'Heavy snowfall'
|
|
WHEN 77 THEN 'Snow grains'
|
|
WHEN 80 THEN 'Slight rain showers'
|
|
WHEN 81 THEN 'Moderate rain showers'
|
|
WHEN 82 THEN 'Violent rain showers'
|
|
WHEN 85 THEN 'Slight snow showers'
|
|
WHEN 86 THEN 'Heavy snow showers'
|
|
WHEN 95 THEN 'Thunderstorm'
|
|
WHEN 96 THEN 'Thunderstorm with slight hail'
|
|
WHEN 99 THEN 'Thunderstorm with heavy hail'
|
|
END AS weather_description,
|
|
w.cloud_cover AS cloud_cover_pct,
|
|
w.wind_speed_10m AS wind_mph,
|
|
w.wind_gusts_10m AS wind_gust_mph
|
|
FROM knoebels."LZ_attractions_io" a
|
|
JOIN knoebels."LZ_attractions_io_poi" r
|
|
ON r._id = a._id
|
|
JOIN knoebels."LZ_attractions_io_categories" c
|
|
ON r.category = c._id
|
|
LEFT JOIN knoebels."LZ_open_meteo_hourly" w
|
|
ON w."time" = date_trunc('hour', a.time_stamp)
|
|
WHERE a.queue_time IS NOT NULL;
|