Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ NEO4J_USER=neo4j
NEO4J_PASSWORD=change-me-neo4j

# Compose-only: neo4j container auth, in user/password form.
# NOTE: keep NEO4J_AUTH's password in sync with NEO4J_PASSWORD above — the server
# reads this one, clients read the other; rotating only one fails auth at graph-run time.
NEO4J_AUTH=neo4j/change-me-neo4j

# =============================================================================
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ jobs:
tests/test_connector_weather.py
tests/test_readapi.py

graph-gate:
name: Entity graph gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: entity-graph suite (offline; fake driver, no Neo4j service)
run: >
uv run --extra graph --extra storage --extra quality --extra service
--extra orchestration --extra dev pytest -q
tests/test_graph_vocab.py
tests/test_graph_plan.py
tests/test_graph_sync.py
tests/test_graph_asset.py
tests/test_graph_api.py

secrets:
name: Secret scan (gitleaks)
runs-on: ubuntu-latest
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ sources ──> Connector ──> quality gate ──> ArcticDB (MinIO)
> **yfinance is dev-only and unscheduled.** Yahoo frequently blocks programmatic
> access, so a schedule would only fire failing runs. The asset stays manual.

An optional **Neo4j** entity graph references these instruments by symbol but never
owns the numbers.
An optional **Neo4j entity graph** mirrors the *identity* layer — instruments,
balancing authorities, ERCOT hubs and load zones, NOAA regions, commodities, and
observed fuel types — with a daily idempotent MERGE sync (`entity_graph` asset). It
references instruments by symbol but never owns the numbers, and it powers the
read API's `/graph/entities` and `/graph/related` discovery endpoints. See the
[entity graph docs](website/docs/entity-graph.md).

## Quickstart

Expand All @@ -94,7 +98,7 @@ Then open:
- **Dagster UI** — http://localhost:3000 (assets, schedules, run history, backfills)
- **MinIO console** — http://localhost:9001 (the ArcticDB object store)
- **Neo4j browser** — http://localhost:7474
- **Read API (S2)** — http://localhost:8000 (`/series`, `/curve`, `/symbols`, `/libraries`, `/healthz`)
- **Read API (S2)** — http://localhost:8000 (`/series`, `/curve`, `/symbols`, `/libraries`, `/graph/entities`, `/graph/related`, `/healthz`)

Four schedules run by default and keep the store current with no manual intervention:
EIA gas storage (Thursday), EIA petroleum status (Wednesday), FRED spot (weekday
Expand Down
7 changes: 6 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
build:
context: .
args:
EXTRAS: "--extra service --extra storage"
EXTRAS: "--extra service --extra storage --extra graph"
image: ghcr.io/oldhero5/energex:api
platform: linux/amd64
container_name: energex-api
Expand All @@ -24,6 +24,11 @@ services:
# Scoped ArcticDB service account created by minio-init (NOT MinIO root); read-only API.
MINIO_ACCESS_KEY: ${ARCTIC_ACCESS_KEY:?set ARCTIC_ACCESS_KEY in .env}
MINIO_SECRET_KEY: ${ARCTIC_SECRET_KEY:?set ARCTIC_SECRET_KEY in .env}
# Entity graph (optional): /graph/* endpoints 503 until the `full` profile's
# neo4j is reachable; the api starts and serves series data regardless.
NEO4J_URI: bolt://neo4j:7687
NEO4J_USER: neo4j
NEO4J_PASSWORD: ${NEO4J_PASSWORD:?set NEO4J_PASSWORD in .env}
TZ: UTC
ports:
- "8000:8001"
Expand Down
6 changes: 6 additions & 0 deletions src/energex/core/connectors/ercot.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
)


def settlement_points() -> frozenset[str]:
"""The canonical tradeable settlement points (hubs + load zones) this
connector ingests. Public so the entity graph never reaches into privates."""
return _SETTLEMENT_POINTS


def _is_retryable(exc: BaseException) -> bool:
"""Retry only transient failures: transport/timeout errors and 5xx responses. Never retry
4xx (bad creds, bad request) — retrying those just hammers ERCOT's auth/API and risks
Expand Down
4 changes: 4 additions & 0 deletions src/energex/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,9 @@ class SymbologyError(EnergexError):
"""Raised when an instrument_id cannot be resolved or its mode is inconsistent."""


class GraphError(EnergexError):
"""Raised on Neo4j entity-graph connection or sync failures."""


class PartitionError(EnergexError):
"""Raised when a Dagster partition key cannot be mapped to a valid_time range."""
Loading
Loading