sql first refactor
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import date
|
||||
from typing import Annotated, Any
|
||||
|
||||
@@ -9,6 +10,7 @@ import asyncpg
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.db_json import fetch_json
|
||||
from app.deps import get_pg_pool
|
||||
|
||||
router = APIRouter(
|
||||
@@ -16,6 +18,7 @@ router = APIRouter(
|
||||
tags=["energy-flows"],
|
||||
)
|
||||
|
||||
|
||||
class DailyEnergyFlows(BaseModel):
|
||||
day: date
|
||||
interval_count: int
|
||||
@@ -65,12 +68,6 @@ def _num(val: Any) -> float:
|
||||
return float(val)
|
||||
|
||||
|
||||
def _wh_to_kwh(val: Any) -> float | None:
|
||||
if val is None:
|
||||
return None
|
||||
return round(float(val) / 1000.0, 4)
|
||||
|
||||
|
||||
async def _check_site(conn: asyncpg.Connection, site_id: int) -> None:
|
||||
ok = await conn.fetchval(
|
||||
"SELECT EXISTS(SELECT 1 FROM ems.site WHERE id = $1)", site_id
|
||||
@@ -79,28 +76,16 @@ async def _check_site(conn: asyncpg.Connection, site_id: int) -> None:
|
||||
raise HTTPException(status_code=404, detail="Site not found")
|
||||
|
||||
|
||||
def _row_to_daily(r: Any) -> DailyEnergyFlows:
|
||||
return DailyEnergyFlows(
|
||||
day=r["day_local"],
|
||||
interval_count=int(r["interval_count"] or 0),
|
||||
pv_production_kwh=_num(r["pv_production_kwh"]),
|
||||
grid_import_kwh=_num(r["grid_import_kwh"]),
|
||||
grid_export_kwh=_num(r["grid_export_kwh"]),
|
||||
batt_charge_kwh=_num(r["batt_charge_kwh"]),
|
||||
batt_discharge_kwh=_num(r["batt_discharge_kwh"]),
|
||||
load_kwh=_num(r["load_kwh"]),
|
||||
pv_to_load_kwh=_num(r["pv_to_load_kwh"]),
|
||||
pv_to_batt_kwh=_num(r["pv_to_batt_kwh"]),
|
||||
pv_to_grid_kwh=_num(r["pv_to_grid_kwh"]),
|
||||
batt_to_load_kwh=_num(r["batt_to_load_kwh"]),
|
||||
batt_to_grid_kwh=_num(r["batt_to_grid_kwh"]),
|
||||
grid_to_load_kwh=_num(r["grid_to_load_kwh"]),
|
||||
grid_to_batt_kwh=_num(r["grid_to_batt_kwh"]),
|
||||
grid_import_cashflow_czk=_num(r["grid_import_cashflow_czk"]),
|
||||
grid_export_revenue_czk=_num(r["grid_export_revenue_czk"]),
|
||||
grid_to_load_cost_czk=_num(r["grid_to_load_cost_czk"]),
|
||||
grid_to_batt_cost_czk=_num(r["grid_to_batt_cost_czk"]),
|
||||
)
|
||||
def _parse_day(val: Any) -> date:
|
||||
from datetime import datetime as _dt
|
||||
|
||||
if isinstance(val, _dt):
|
||||
return val.date()
|
||||
if isinstance(val, date):
|
||||
return val
|
||||
if isinstance(val, str):
|
||||
return date.fromisoformat(val[:10])
|
||||
raise ValueError(val)
|
||||
|
||||
|
||||
@router.get("/daily", response_model=DailyEnergyFlowsResponse)
|
||||
@@ -125,84 +110,44 @@ async def get_energy_flows_daily(
|
||||
|
||||
async with db.acquire() as conn:
|
||||
await _check_site(conn, site_id)
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT
|
||||
(date_trunc('day', ai.interval_start AT TIME ZONE 'Europe/Prague'))::date
|
||||
AS day_local,
|
||||
COUNT(*)::int AS interval_count,
|
||||
ROUND(SUM(COALESCE(ai.actual_pv_production_wh, 0)) / 1000, 3)
|
||||
AS pv_production_kwh,
|
||||
ROUND(SUM(COALESCE(ai.actual_grid_import_wh, 0)) / 1000, 3)
|
||||
AS grid_import_kwh,
|
||||
ROUND(SUM(COALESCE(ai.actual_grid_export_wh, 0)) / 1000, 3)
|
||||
AS grid_export_kwh,
|
||||
ROUND(SUM(COALESCE(ai.actual_batt_charge_wh, 0)) / 1000, 3)
|
||||
AS batt_charge_kwh,
|
||||
ROUND(SUM(COALESCE(ai.actual_batt_discharge_wh, 0)) / 1000, 3)
|
||||
AS batt_discharge_kwh,
|
||||
ROUND(SUM(COALESCE(ai.actual_load_consumption_wh, 0)) / 1000, 3)
|
||||
AS load_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_pv_to_load_wh, 0)) / 1000, 3)
|
||||
AS pv_to_load_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_pv_to_batt_wh, 0)) / 1000, 3)
|
||||
AS pv_to_batt_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_pv_to_grid_wh, 0)) / 1000, 3)
|
||||
AS pv_to_grid_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_batt_to_load_wh, 0)) / 1000, 3)
|
||||
AS batt_to_load_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_batt_to_grid_wh, 0)) / 1000, 3)
|
||||
AS batt_to_grid_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_grid_to_load_wh, 0)) / 1000, 3)
|
||||
AS grid_to_load_kwh,
|
||||
ROUND(SUM(COALESCE(ai.flow_grid_to_batt_wh, 0)) / 1000, 3)
|
||||
AS grid_to_batt_kwh,
|
||||
ROUND(
|
||||
SUM(
|
||||
COALESCE(ai.actual_grid_import_wh, 0) / 1000.0
|
||||
* COALESCE(ep.effective_buy_price_czk_kwh, 0)
|
||||
),
|
||||
2
|
||||
) AS grid_import_cashflow_czk,
|
||||
ROUND(
|
||||
SUM(
|
||||
COALESCE(ai.actual_grid_export_wh, 0) / 1000.0
|
||||
* COALESCE(ep.effective_sell_price_czk_kwh, 0)
|
||||
),
|
||||
2
|
||||
) AS grid_export_revenue_czk,
|
||||
ROUND(
|
||||
SUM(
|
||||
COALESCE(ai.flow_grid_to_load_wh, 0) / 1000.0
|
||||
* COALESCE(ep.effective_buy_price_czk_kwh, 0)
|
||||
),
|
||||
2
|
||||
) AS grid_to_load_cost_czk,
|
||||
ROUND(
|
||||
SUM(
|
||||
COALESCE(ai.flow_grid_to_batt_wh, 0) / 1000.0
|
||||
* COALESCE(ep.effective_buy_price_czk_kwh, 0)
|
||||
),
|
||||
2
|
||||
) AS grid_to_batt_cost_czk
|
||||
FROM ems.audit_interval ai
|
||||
LEFT JOIN ems.vw_site_effective_price ep
|
||||
ON ep.site_id = ai.site_id
|
||||
AND ep.interval_start = ai.interval_start
|
||||
WHERE ai.site_id = $1
|
||||
AND (date_trunc('day', ai.interval_start AT TIME ZONE 'Europe/Prague'))::date
|
||||
>= $2
|
||||
AND (date_trunc('day', ai.interval_start AT TIME ZONE 'Europe/Prague'))::date
|
||||
< $3
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
""",
|
||||
raw = await fetch_json(
|
||||
conn,
|
||||
"select ems.fn_energy_flows_daily_month($1::int, $2::date, $3::date)",
|
||||
site_id,
|
||||
month_start,
|
||||
month_end,
|
||||
)
|
||||
|
||||
return DailyEnergyFlowsResponse(days=[_row_to_daily(r) for r in rows])
|
||||
if not isinstance(raw, dict):
|
||||
raw = json.loads(raw)
|
||||
rows = raw.get("days") or []
|
||||
days: list[DailyEnergyFlows] = []
|
||||
for r in rows:
|
||||
if not isinstance(r, dict):
|
||||
continue
|
||||
days.append(
|
||||
DailyEnergyFlows(
|
||||
day=_parse_day(r.get("day")),
|
||||
interval_count=int(r.get("interval_count") or 0),
|
||||
pv_production_kwh=_num(r.get("pv_production_kwh")),
|
||||
grid_import_kwh=_num(r.get("grid_import_kwh")),
|
||||
grid_export_kwh=_num(r.get("grid_export_kwh")),
|
||||
batt_charge_kwh=_num(r.get("batt_charge_kwh")),
|
||||
batt_discharge_kwh=_num(r.get("batt_discharge_kwh")),
|
||||
load_kwh=_num(r.get("load_kwh")),
|
||||
pv_to_load_kwh=_num(r.get("pv_to_load_kwh")),
|
||||
pv_to_batt_kwh=_num(r.get("pv_to_batt_kwh")),
|
||||
pv_to_grid_kwh=_num(r.get("pv_to_grid_kwh")),
|
||||
batt_to_load_kwh=_num(r.get("batt_to_load_kwh")),
|
||||
batt_to_grid_kwh=_num(r.get("batt_to_grid_kwh")),
|
||||
grid_to_load_kwh=_num(r.get("grid_to_load_kwh")),
|
||||
grid_to_batt_kwh=_num(r.get("grid_to_batt_kwh")),
|
||||
grid_import_cashflow_czk=_num(r.get("grid_import_cashflow_czk")),
|
||||
grid_export_revenue_czk=_num(r.get("grid_export_revenue_czk")),
|
||||
grid_to_load_cost_czk=_num(r.get("grid_to_load_cost_czk")),
|
||||
grid_to_batt_cost_czk=_num(r.get("grid_to_batt_cost_czk")),
|
||||
)
|
||||
)
|
||||
return DailyEnergyFlowsResponse(days=days)
|
||||
|
||||
|
||||
@router.get("/daily/{day}/intervals", response_model=list[IntervalEnergyFlows])
|
||||
@@ -213,48 +158,35 @@ async def get_energy_flows_intervals(
|
||||
) -> list[IntervalEnergyFlows]:
|
||||
async with db.acquire() as conn:
|
||||
await _check_site(conn, site_id)
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT
|
||||
interval_start,
|
||||
actual_pv_production_wh,
|
||||
actual_grid_import_wh,
|
||||
actual_grid_export_wh,
|
||||
actual_batt_charge_wh,
|
||||
actual_batt_discharge_wh,
|
||||
actual_load_consumption_wh,
|
||||
flow_pv_to_load_wh,
|
||||
flow_pv_to_batt_wh,
|
||||
flow_pv_to_grid_wh,
|
||||
flow_batt_to_load_wh,
|
||||
flow_batt_to_grid_wh,
|
||||
flow_grid_to_load_wh,
|
||||
flow_grid_to_batt_wh
|
||||
FROM ems.audit_interval
|
||||
WHERE site_id = $1
|
||||
AND (date_trunc('day', interval_start AT TIME ZONE 'Europe/Prague'))::date = $2
|
||||
ORDER BY interval_start
|
||||
""",
|
||||
rows = await fetch_json(
|
||||
conn,
|
||||
"select ems.fn_energy_flows_intervals_day($1::int, $2::date)",
|
||||
site_id,
|
||||
day,
|
||||
)
|
||||
|
||||
return [
|
||||
IntervalEnergyFlows(
|
||||
interval_start=r["interval_start"].isoformat(),
|
||||
pv_production_kwh=_wh_to_kwh(r["actual_pv_production_wh"]),
|
||||
grid_import_kwh=_wh_to_kwh(r["actual_grid_import_wh"]),
|
||||
grid_export_kwh=_wh_to_kwh(r["actual_grid_export_wh"]),
|
||||
batt_charge_kwh=_wh_to_kwh(r["actual_batt_charge_wh"]),
|
||||
batt_discharge_kwh=_wh_to_kwh(r["actual_batt_discharge_wh"]),
|
||||
load_kwh=_wh_to_kwh(r["actual_load_consumption_wh"]),
|
||||
pv_to_load_kwh=_wh_to_kwh(r["flow_pv_to_load_wh"]),
|
||||
pv_to_batt_kwh=_wh_to_kwh(r["flow_pv_to_batt_wh"]),
|
||||
pv_to_grid_kwh=_wh_to_kwh(r["flow_pv_to_grid_wh"]),
|
||||
batt_to_load_kwh=_wh_to_kwh(r["flow_batt_to_load_wh"]),
|
||||
batt_to_grid_kwh=_wh_to_kwh(r["flow_batt_to_grid_wh"]),
|
||||
grid_to_load_kwh=_wh_to_kwh(r["flow_grid_to_load_wh"]),
|
||||
grid_to_batt_kwh=_wh_to_kwh(r["flow_grid_to_batt_wh"]),
|
||||
if not isinstance(rows, list):
|
||||
rows = json.loads(rows) if isinstance(rows, str) else []
|
||||
out: list[IntervalEnergyFlows] = []
|
||||
for r in rows:
|
||||
if not isinstance(r, dict):
|
||||
continue
|
||||
ist = r.get("interval_start")
|
||||
out.append(
|
||||
IntervalEnergyFlows(
|
||||
interval_start=ist if isinstance(ist, str) else str(ist),
|
||||
pv_production_kwh=r.get("pv_production_kwh"),
|
||||
grid_import_kwh=r.get("grid_import_kwh"),
|
||||
grid_export_kwh=r.get("grid_export_kwh"),
|
||||
batt_charge_kwh=r.get("batt_charge_kwh"),
|
||||
batt_discharge_kwh=r.get("batt_discharge_kwh"),
|
||||
load_kwh=r.get("load_kwh"),
|
||||
pv_to_load_kwh=r.get("pv_to_load_kwh"),
|
||||
pv_to_batt_kwh=r.get("pv_to_batt_kwh"),
|
||||
pv_to_grid_kwh=r.get("pv_to_grid_kwh"),
|
||||
batt_to_load_kwh=r.get("batt_to_load_kwh"),
|
||||
batt_to_grid_kwh=r.get("batt_to_grid_kwh"),
|
||||
grid_to_load_kwh=r.get("grid_to_load_kwh"),
|
||||
grid_to_batt_kwh=r.get("grid_to_batt_kwh"),
|
||||
)
|
||||
)
|
||||
for r in rows
|
||||
]
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user