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č), 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. 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: Příklad:
python3 scripts/analysis/battery_sizing_screen.py \\ python3 scripts/analysis/battery_sizing_screen.py \\
--db \\ --db \\
@@ -31,6 +39,7 @@ import os
import sys import sys
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from pathlib import Path
from typing import Iterable, Sequence from typing import Iterable, Sequence
try: 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) 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]]: def load_prices_csv(path: str) -> list[tuple[datetime, float]]:
out: list[tuple[datetime, float]] = [] out: list[tuple[datetime, float]] = []
with open(path, newline="", encoding="utf-8") as f: with open(path, newline="", encoding="utf-8") as f:
@@ -118,13 +162,23 @@ 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) 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) t1 = datetime.combine(date_to, datetime.min.time(), tzinfo=prg).astimezone(timezone.utc)
conn = psycopg2.connect( dsn = (
host=os.environ.get("PGHOST", "127.0.0.1"), os.environ.get("DATABASE_URL")
port=int(os.environ.get("PGPORT", "5432")), or os.environ.get("EMS_DATABASE_URL")
dbname=os.environ.get("PGDATABASE", "ems"), or os.environ.get("POSTGRES_URL")
user=os.environ.get("PGUSER", os.environ.get("DB_USER", "ems_user")),
password=os.environ.get("PGPASSWORD", os.environ.get("DB_PASSWORD", "")),
) )
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")),
dbname=os.environ.get("PGDATABASE", "ems"),
user=os.environ.get("PGUSER", os.environ.get("DB_USER", "ems_user")),
password=os.environ.get("PGPASSWORD", os.environ.get("DB_PASSWORD", "")),
)
cur = conn.cursor() cur = conn.cursor()
cur.execute( cur.execute(
""" """
@@ -270,7 +324,19 @@ def simulate_year(
def main() -> None: def main() -> None:
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 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("--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-from", type=str, required=True)
ap.add_argument("--date-to", 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) d0 = date.fromisoformat(args.date_from)
d1 = date.fromisoformat(args.date_to) d1 = date.fromisoformat(args.date_to)
if args.db: 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) series = load_prices_db(d0, d1)
elif args.price_csv: elif args.price_csv:
series = load_prices_csv(args.price_csv) series = load_prices_csv(args.price_csv)