|
|
|
@ -1048,11 +1048,17 @@ async def get_forecast_trending(
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return EAF/EFOR/SOF/EDH for the last `limit` completed weekly or monthly
|
|
|
|
Return EAF/EFOR/SOF/EDH for the last `limit` completed weekly or monthly
|
|
|
|
forecast simulations, oldest first (for chronological chart rendering).
|
|
|
|
forecast simulations, oldest first (for chronological chart rendering).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Strategy:
|
|
|
|
|
|
|
|
1. Try simulations named Simulation_weekly_* / Simulation_monthly_* (scheduled forecasts).
|
|
|
|
|
|
|
|
2. If fewer than `limit` found, fall back to any completed simulation, grouped
|
|
|
|
|
|
|
|
by week or month bucket (latest sim per bucket), so the chart is populated
|
|
|
|
|
|
|
|
even before automated forecast simulations exist.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
name_prefix = f"Simulation_{time_range}_"
|
|
|
|
|
|
|
|
plant_node_name = "- TJB - Unit 3 -"
|
|
|
|
plant_node_name = "- TJB - Unit 3 -"
|
|
|
|
|
|
|
|
name_prefix = f"Simulation_{time_range}_"
|
|
|
|
|
|
|
|
|
|
|
|
query = (
|
|
|
|
base_join = (
|
|
|
|
select(AerosSimulation, AerosSimulationCalcResult)
|
|
|
|
select(AerosSimulation, AerosSimulationCalcResult)
|
|
|
|
.join(
|
|
|
|
.join(
|
|
|
|
AerosSimulationCalcResult,
|
|
|
|
AerosSimulationCalcResult,
|
|
|
|
@ -1060,24 +1066,56 @@ async def get_forecast_trending(
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.join(AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id)
|
|
|
|
.join(AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id)
|
|
|
|
.where(AerosSimulation.status == "completed")
|
|
|
|
.where(AerosSimulation.status == "completed")
|
|
|
|
.where(AerosSimulation.simulation_name.like(f"{name_prefix}%"))
|
|
|
|
|
|
|
|
.where(AerosNode.node_name == plant_node_name)
|
|
|
|
.where(AerosNode.node_name == plant_node_name)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# First pass: named forecast simulations
|
|
|
|
|
|
|
|
named_q = (
|
|
|
|
|
|
|
|
base_join
|
|
|
|
|
|
|
|
.where(AerosSimulation.simulation_name.like(f"{name_prefix}%"))
|
|
|
|
.order_by(AerosSimulation.completed_at.desc())
|
|
|
|
.order_by(AerosSimulation.completed_at.desc())
|
|
|
|
.limit(limit)
|
|
|
|
.limit(limit)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
result = await db_session.execute(named_q)
|
|
|
|
|
|
|
|
named_rows = result.all()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _format(rows):
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"date": sim.completed_at,
|
|
|
|
|
|
|
|
"simulation_id": str(sim.id),
|
|
|
|
|
|
|
|
"simulation_name": sim.simulation_name,
|
|
|
|
|
|
|
|
"eaf": calc.eaf,
|
|
|
|
|
|
|
|
"efor": calc.efor,
|
|
|
|
|
|
|
|
"sof": calc.sof,
|
|
|
|
|
|
|
|
"edh": calc.derating_hours,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for sim, calc in rows
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(named_rows) >= limit:
|
|
|
|
|
|
|
|
return _format(reversed(named_rows))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Fallback: any completed sim, group by week/month bucket (latest per bucket)
|
|
|
|
|
|
|
|
fallback_q = (
|
|
|
|
|
|
|
|
base_join
|
|
|
|
|
|
|
|
.order_by(AerosSimulation.completed_at.desc())
|
|
|
|
|
|
|
|
.limit(limit * 20)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
result = await db_session.execute(fallback_q)
|
|
|
|
|
|
|
|
all_rows = result.all()
|
|
|
|
|
|
|
|
|
|
|
|
result = await db_session.execute(query)
|
|
|
|
buckets: dict = {}
|
|
|
|
rows = result.all()
|
|
|
|
for sim, calc in all_rows:
|
|
|
|
|
|
|
|
ts = sim.completed_at
|
|
|
|
return [
|
|
|
|
if ts is None:
|
|
|
|
{
|
|
|
|
continue
|
|
|
|
"date": sim.completed_at,
|
|
|
|
if time_range == "weekly":
|
|
|
|
"simulation_id": str(sim.id),
|
|
|
|
bucket = ts.strftime("%G-W%V") # ISO week
|
|
|
|
"simulation_name": sim.simulation_name,
|
|
|
|
else:
|
|
|
|
"eaf": calc.eaf,
|
|
|
|
bucket = ts.strftime("%Y-%m")
|
|
|
|
"efor": calc.efor,
|
|
|
|
if bucket not in buckets:
|
|
|
|
"sof": calc.sof,
|
|
|
|
buckets[bucket] = (sim, calc)
|
|
|
|
"edh": calc.derating_hours,
|
|
|
|
|
|
|
|
}
|
|
|
|
sorted_items = sorted(buckets.items(), key=lambda x: x[0])[-limit:]
|
|
|
|
for sim, calc in reversed(rows) # oldest → newest for chart x-axis
|
|
|
|
return _format((sim, calc) for _, (sim, calc) in sorted_items)
|
|
|
|
]
|
|
|
|
|
|
|
|
|