15 Commits
Author SHA1 Message Date
wesandClaude Opus 4.8 94a91e29ad knb_stats: validate PC interpretations + rank-3 reconstruction + writeup PC sections
pca_svd: exclude torn-down powersurge from the PCA, trim attractions.io
category names (trailing-space gotcha), time-side validation of PC1-PC3
interpretations, per-ride R^2 of the rank-3 reconstruction, blog-ready
PC2xPC3 scatter. writeup: PC1/PC2/PC3 narrative sections.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:22:37 -04:00
wesandClaude Opus 4.8 5c09528df7 Add Open-Meteo hourly weather pipeline + queue/weather view
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>
2026-06-01 10:38:01 -04:00
wesandClaude Opus 4.7 f7e0042ef8 Add "How Busy?" tab: 4 learned tiers + dual logistic regression
New dashboard tab that rates current park crowding:

- Tiers: k-means on park-wide mean wait yields 4 ordinal levels
  ("Walk right on" / "Pleasant" / "Packed" / "What were you
  thinking?!"), with the rare 0.6% zoo tail isolated as its own
  tier rather than buried in a quartile.
- Board-reading model: multinomial LR on per-ride waits -> tier.
  Drives the live gauge (current avg wait + predicted tier +
  confidence + class-probability bar). Near-perfect by construction
  -- honest framing baked into the model card.
- Calendar-forecast model: LR on hour/dow/month/weekend -> tier,
  evaluated with GroupKFold by date to avoid same-day leakage.
  Drives the "expected vs actual" verdict for the current moment.
- Bellwether rides bar (top tier coefficients) and a confusion-
  matrix expander tell the honest story: forecast barely beats
  baseline on everyday tiers, but pins the zoo tier nearly 100% --
  because Knoebels' worst crowds are locked to specific October
  festival weekends. "The chaos has a schedule."

Implementation notes: trained models cached via st.cache_resource
(6h), live snapshot fetched separately on a 5-min ttl. Context
features use a fixed column set so live inference always aligns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 23:38:23 -04:00
wesandClaude Opus 4.7 ad163cb996 Expose PC4 and make the PCA behavioral map configurable
- compute_pca now fits 4 components; PC4 added to PC_LABELS/PC_BLURB
  ("off-peak spread vs headliner pull", ~5% variance, framed as
  diminishing returns).
- "Read an axis" loadings explorer gains PC4.
- The behavioral-map scatter becomes interactive: pick any two
  components for the X/Y axes, and color by category tag, spatial
  area, or any component's loading (continuous RdBu, symmetric).
- Headline caption pinned to evr[:3] so "3 axes = 60%" stays true;
  the by-hour clock stays PC1-3 to preserve the three-axes story.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:54:52 -04:00
wesandClaude Opus 4.7 e65d1c796c Name the GPS k-means areas by their marquee ride
Replace the "Area N" fallbacks with derived names: Kiddieland
(lowest mean PC2) plus The Woodies / The Grand Midway / Flying
Turns Grove / Giant Wheel Corner, each keyed off the headline
ride that lands in the cluster so the labels survive re-clustering
as the wait history grows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:35:42 -04:00
wesandClaude Opus 4.7 53109bc7fe Add interactive 3D PCA views to dashboard Park Mind tab
Two spin/zoom Plotly scatter_3d sub-tabs in the PCA tab:
- Rides in PC1xPC2xPC3 (loadings), color by category / GPS
  k-means area / PC1 busyness. Adds GPS k-means areas to
  compute_pca(), auto-labeling the lowest-PC2 cluster Kiddieland.
- Moments in PC1xPC2xPC3 (per-snapshot scores), color by hour /
  day-of-week / weekend / month, sampled to 15k points if larger.

Pin numpy in requirements (now imported directly).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 18:53:55 -04:00
wesandClaude Opus 4.7 ade774f5aa Add Streamlit app + knb_stats analysis notebooks
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 10:06:19 -04:00
wesandClaude Opus 4.7 dd6ff18b04 Ignore APK/XAPK drift-check artifacts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 07:52:44 -04:00
wesandClaude Opus 4.7 c247e15a4e Fetch POI records from attractions.io API and upsert
Replace the local records.json read with a two-step Occasio fetch
(install + data zip), then upsert Items and Categories into the
knoebels schema with ON CONFLICT (_id) DO UPDATE so re-runs refresh
in place. Also brings DB credentials in line with attractions_api_logger.py
(env vars, default host 192.168.88.9), drops dead helpers, and uses a
single shared connection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 07:52:03 -04:00
wesandClaude Opus 4.7 7a92834c8b Share one DB connection per run + cleanups
Open one psycopg2 connection in main() and pass it through pull_data_and_log
and log_to_database. Previously each landing-zone insert opened its own
connection (3-4 handshakes per invocation); now everything runs in a single
session and the three LZ writes share one transaction.

Other cleanups:
- Replace bare `except:` blocks around attraction-field reads with .get()
- Drop the unused col_names computation in the old generic_query
- Move module-level driver code under `if __name__ == "__main__":`
- Parse park_opens / park_closes once instead of three times
- Name columns explicitly in the park-hours query (was SELECT *)
- Use `datestamp` (date column) for the today-freshness check rather
  than reading .date() off the open timestamp

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 06:36:31 -04:00
wes 10bdee8e1f Containerize scraper and externalize credentials
Move DB host/user/password, Discord webhook, and log path to env vars
so the script can be deployed via Docker compose. Discord notifications
are now skipped silently when DISCORD_WEBHOOK is unset (the old
hardcoded webhook is removed pending rotation). Adds Dockerfile,
docker-compose.yml, requirements.txt, .env.example, and .gitignore for
the cyrion deployment.
2026-05-06 23:20:12 -04:00
wes b9864b36b4 catch-up commit + csv with 2024 data 2024-12-17 09:57:05 -05:00
wes 9b86647b1c Forgot to save code for main datamart 2024-05-20 13:37:29 -04:00
wes 1678a2f5a6 Changed join to price table to a LEFT JOIN so that I can also keep track of the Alamo restaurant operating hours with the same tables 2024-05-20 13:35:46 -04:00
wes 0ce13cf8a4 first commit 2024-05-20 12:20:38 -04:00