Status: PROTOTYPE
- Analytics + reporting for BirdNET-GO (nightly-20260118).
- Current focus: reproducible rollups and a small web dashboard.
What this is:
- A small analytics layer over the BirdNET-GO SQLite DB.
- Scripts + (optional) web UI for exploring detection patterns over time.
Why it exists:
- Make it easy to answer questions like “who sings at dawn?” without bespoke SQL every time.
- Build repeatable, comparable rollups (hourly/daily counts, trends, QA).
Non-goals (for now):
- Replacing BirdNET-GO’s storage.
This project is agentic LLM code directed by Dan Hon.
Notes:
- Expect fast iteration and occasional rough edges.
- Prefer filing an issue with repro steps over guessing intent.
- If something seems surprising, assume it’s a bug or an unfinished idea.
Prereqs:
- Docker + docker compose
Setup:
cp .env.example .env
# edit .env and set BIRDNET_DB_HOST_PATH=/absolute/path/to/birdnet.dbRun (daemonized):
docker compose up --build -dWhat you should see:
- A running container (
docker compose ps) - A local server at http://127.0.0.1:8787/
Update to latest main:
git pull
docker compose up --build -dLogs:
docker compose logs -fHealth:
curl -fsS http://127.0.0.1:8787/health || echo "unhealthy"
docker compose psStop:
docker compose downThis repo uses uv.lock for reproducible installs.
To upgrade dependencies (intentional, reviewable change):
uv lock --upgrade
uv syncThen rebuild the container:
docker compose up --build -dPrereqs:
- Python + uv
Setup:
cd birdnet-analytics
uv venv
uv syncRun a script (schema print):
uv run python scripts/print_schema.py /path/to/birdnet.dbRun the web dashboard locally:
export BIRDNET_DB_PATH=/path/to/birdnet.db
export BIRDNET_ANALYTICS_TZ=America/Los_Angeles
uv run uvicorn birdnet_analytics.web:app --reload --port 8787Project layout:
src/birdnet_analytics/— library codescripts/— runnable entrypoints (ETL, backfills, reports)configs/— deployment/runtime configs (paths, DB locations)docs/— notes
Assumptions / invariants:
- BirdNET-GO’s SQLite DB is the source of truth.
These screenshots are examples from the current dashboard UI.
Shows the distribution of detections across the day. Includes a percentile baseline (P10/P50/P90) and an overlay for “today”, plus “% days active” to show how consistently that hour has detections.
Buckets detections into 15‑minute slices in a configurable window around sunrise (e.g. 90 minutes before → 150 minutes after). Useful for comparing dawn‑chorus intensity day to day.
A 100% stacked view of the dawn window: each day is normalized to sum to 100%, so you can compare the shape of activity around sunrise even when absolute totals differ.
Heatmap version of the dawn window: columns are days, rows are 15‑minute offsets from sunrise, color indicates relative intensity.
Splits each day into fixed clock-time “day parts” (00–06, 06–12, 12–18, 18–24) and shows detections per part. Overlays unique species/day and daily precipitation (mm) to spot basic weather correlations.
Weekly rollup of total detections and unique species, to track broader trends.
For each day, shows what fraction of detections were from the top 3 species (with everything else grouped as “Other”). Useful for spotting days dominated by a few species.
- 2026-02-20 — Start keeping feature/stats ideas in README so collaborators can see the direction.
Source(s) of truth:
- BirdNET-GO SQLite database (
birdnet.db).
Refresh / update cadence:
- Depends on the BirdNET-GO instance (out of scope here).
Known quirks / limitations:
- Raw detections can overcount repeated calls; most analytics should collapse into “events”.
Top 5:
- Implement per-species hourly activity profiles (normalized).
- Morning vs afternoon index per species.
- Hour × month heatmaps per species.
- Event-collapsing rules (dedupe adjacent detections) as a first-class primitive.
- QA: detector drift / mis-ID sentinel checks.
Time-of-day / daily rhythms
- Per-species hourly activity profile (normalized) + peak hour.
- Morning vs afternoon index per species.
- Dawn-chorus concentration (% of detections in a window around sunrise).
- Crepuscular/nocturnal flags (sunset/overnight concentration).
- Weekend vs weekday pattern differences.
- Seasonal shift in peak hour.
Sunrise/sunset aware
- Activity vs solar time (minutes since sunrise/sunset) for season-robust comparisons.
- Pre-dawn vs post-dawn ratios.
- Photoperiod sensitivity (activity window vs day length).
Presence/absence & “show up” patterns
- First/last detection dates per year (arrival/departure proxy).
- Cumulative arrival/departure curves; year-over-year comparisons.
- Persistence: consecutive-day “run length” distributions (resident vs transient).
- Rarity spikes / bursty appearances (outliers for review).
Seasonality (multi-scale)
- Hour × month heatmap per species.
- Breeding-season proxy: spring-morning singing increases.
- Inter-annual trends (weekly counts with smoothing).
Weather correlations (if we join weather)
- Temperature/wind/rain effects (who goes quiet in wind, who pops after rain).
- Pressure-change/front effects.
- Extreme-day comparisons (hottest/coolest 5%).
Soundscape / detection ecology
- Noise-floor proxy vs detection rates (if audio metadata available).
- Confidence distribution per species (IDs that are consistently low-confidence).
- Co-detection networks (species that tend to appear together by hour/day).
- “Top species by hour block” through the year.
Spatial (multi-recorder)
- Site specialization and seasonal changes.
- Turnover between sites (present here/not there).
- Habitat gradients (e.g., distance-to-water / urban) vs detections (if site metadata exists).
Behavior-ish proxies from detections
- Calling-bout structure (gaps/clustering within mornings).
- Schedule consistency (“punctual” vs “all day”).
- Diversity by hour (species richness/entropy).
Data hygiene (make the archive trustworthy)
- Collapse adjacent duplicate detections into “events” to avoid overcounting.
- Detector drift checks (counts jump after model/recorder changes).
- Mis-ID sentinel list (out-of-range/season detections → review queue).
Done:
- Captured an initial feature/stats backlog in the README.
Next:
- Confirm BirdNET-GO SQLite DB path (out of this repo’s scope, but needed for real data).
- Capture schema (
.tables,.schema) and commit sample outputs underdocs/.
- The dashboard loads but shows no data
- Cause:
BIRDNET_DB_PATHpoints at the wrong file. - Fix: set
BIRDNET_DB_PATHto a realbirdnet.db.
uvcommands fail / wrong Python
- Cause: missing uv or an unexpected Python version.
- Fix: install uv and recreate the venv (
uv venv && uv sync).
- Counts look inflated
- Cause: repeated detections of the same individual are being counted as separate events.
- Fix: use event-collapsing (dedupe) before aggregating.






