fix enf load
All checks were successful
test / smoke-test (push) Successful in 3s
deploy / deploy (push) Successful in 14s

This commit is contained in:
Dusan Vojacek
2026-04-12 22:31:18 +02:00
parent 3da738e7e9
commit 3b33594354

View File

@@ -5,6 +5,14 @@ Ekonomický screening velikosti baterie (15min, jednobusová energie).
Typicky BA81: fixní nákup + prodej spot, limity výkonu z baterie min(0,5C, střídač),
export/import podle připojení. Načte OTE z Postgres (stejná DB jako EMS) nebo z CSV.
Připojení k DB (deploy / Docker):
- Postgres v compose poslouchá na ``EMS_DB_BIND:5432`` (výchozí 127.0.0.1). ``connection refused``
= služba ``db`` neběží, nebo je port vázaný jen na jinou IP (WireGuard) → nastav stejný host.
- Skript načte první nalezené ``.env`` z ``…/ems-deploy/.env`` nebo ``…/app/.env`` (není-li
``--no-auto-env``) a doplní ``PGUSER``/``PGPASSWORD`` z ``DB_USER``/``DB_PASSWORD``.
- Nebo ``DATABASE_URL`` / ``postgresql://USER:PASS@HOST:5432/ems`` (na hostu HOST=127.0.0.1
nebo EMS_DB_BIND, ne ``db`` — to je jen uvnitř Docker sítě).
Příklad:
python3 scripts/analysis/battery_sizing_screen.py \\
--db \\
@@ -31,6 +39,7 @@ import os
import sys
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from pathlib import Path
from typing import Iterable, Sequence
try:
@@ -93,6 +102,41 @@ def effective_sell_kc_kwh(raw_ote: float, margin_fixed: float, margin_pct: float
return raw_ote + margin_fixed + (raw_ote * margin_pct / 100.0)
def load_env_file(path: Path) -> None:
if not path.is_file():
return
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, _, v = line.partition("=")
k, v = k.strip(), v.strip().strip('"').strip("'")
if not k:
continue
if k not in os.environ or os.environ.get(k, "") == "":
os.environ[k] = v
def apply_auto_env_files() -> None:
"""Na produkci: /opt/ems-deploy/.env (když tam je docker-compose), pak app/.env nebo kořen repa."""
script = Path(__file__).resolve()
if len(script.parents) >= 3:
deploy_root = script.parents[2]
if (deploy_root / "docker-compose.yml").is_file():
load_env_file(deploy_root / ".env")
if len(script.parents) >= 2:
load_env_file(script.parents[1] / ".env")
def sync_pg_env_from_db_vars() -> None:
if not os.environ.get("PGUSER") and os.environ.get("DB_USER"):
os.environ["PGUSER"] = os.environ["DB_USER"]
if not os.environ.get("PGPASSWORD") and os.environ.get("DB_PASSWORD"):
os.environ["PGPASSWORD"] = os.environ["DB_PASSWORD"]
if not os.environ.get("PGDATABASE"):
os.environ["PGDATABASE"] = "ems"
def load_prices_csv(path: str) -> list[tuple[datetime, float]]:
out: list[tuple[datetime, float]] = []
with open(path, newline="", encoding="utf-8") as f:
@@ -118,6 +162,16 @@ def load_prices_db(date_from: date, date_to: date) -> list[tuple[datetime, float
t0 = datetime.combine(date_from, datetime.min.time(), tzinfo=prg).astimezone(timezone.utc)
t1 = datetime.combine(date_to, datetime.min.time(), tzinfo=prg).astimezone(timezone.utc)
dsn = (
os.environ.get("DATABASE_URL")
or os.environ.get("EMS_DATABASE_URL")
or os.environ.get("POSTGRES_URL")
)
if dsn:
if dsn.startswith("postgres://"):
dsn = "postgresql://" + dsn[len("postgres://") :]
conn = psycopg2.connect(dsn)
else:
conn = psycopg2.connect(
host=os.environ.get("PGHOST", "127.0.0.1"),
port=int(os.environ.get("PGPORT", "5432")),
@@ -270,7 +324,19 @@ def simulate_year(
def main() -> None:
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--db", action="store_true", help="Načti OTE z Postgres (env PG* / DB_*)")
ap.add_argument("--db", action="store_true", help="Načti OTE z Postgres (env PG* / DB_* / DATABASE_URL)")
ap.add_argument(
"--no-auto-env",
action="store_true",
help="Nenačítej automaticky .env z ems-deploy/ ani app/",
)
ap.add_argument("--env-file", type=str, default="", help="Dodatečný soubor .env (po auto-env)")
ap.add_argument(
"--pg-host",
type=str,
default="",
help="Přepíše PGHOST (např. stejná IP jako EMS_DB_BIND ve compose)",
)
ap.add_argument("--price-csv", type=str, default="", help="CSV: interval_start, sell_raw_price_czk_kwh")
ap.add_argument("--date-from", type=str, required=True)
ap.add_argument("--date-to", type=str, required=True)
@@ -291,6 +357,13 @@ def main() -> None:
d0 = date.fromisoformat(args.date_from)
d1 = date.fromisoformat(args.date_to)
if args.db:
if not args.no_auto_env:
apply_auto_env_files()
if args.env_file:
load_env_file(Path(args.env_file))
sync_pg_env_from_db_vars()
if args.pg_host:
os.environ["PGHOST"] = args.pg_host
series = load_prices_db(d0, d1)
elif args.price_csv:
series = load_prices_csv(args.price_csv)