diff --git a/web/src/beanflows/analytics.py b/web/src/beanflows/analytics.py index f146bed..cc706d7 100644 --- a/web/src/beanflows/analytics.py +++ b/web/src/beanflows/analytics.py @@ -14,19 +14,19 @@ COFFEE_COMMODITY_CODE = 711100 # Metrics safe for user-facing queries (prevents SQL injection in dynamic column refs) ALLOWED_METRICS = frozenset({ - "Production", - "Imports", - "Exports", - "Total_Distribution", - "Ending_Stocks", - "Beginning_Stocks", - "Total_Supply", - "Domestic_Consumption", - "Net_Supply", - "Trade_Balance", - "Supply_Demand_Balance", - "Stock_to_Use_Ratio_pct", - "Production_YoY_pct", + "production", + "imports", + "exports", + "total_distribution", + "ending_stocks", + "beginning_stocks", + "total_supply", + "domestic_consumption", + "net_supply", + "trade_balance", + "supply_demand_balance", + "stock_to_use_ratio_pct", + "production_yoy_pct", }) _conn: duckdb.DuckDBPyConnection | None = None @@ -152,7 +152,7 @@ async def get_stock_to_use_trend(commodity_code: int) -> list[dict]: """Global stock-to-use ratio over time.""" return await fetch_analytics( """ - SELECT market_year, Stock_to_Use_Ratio_pct + SELECT market_year, stock_to_use_ratio_pct FROM serving.commodity_metrics WHERE commodity_code = ? AND country_name = 'Global' @@ -166,7 +166,7 @@ async def get_supply_demand_balance(commodity_code: int) -> list[dict]: """Global supply-demand balance trend.""" return await fetch_analytics( """ - SELECT market_year, Production, Total_Distribution, Supply_Demand_Balance + SELECT market_year, production, total_distribution, supply_demand_balance FROM serving.commodity_metrics WHERE commodity_code = ? AND country_name = 'Global' @@ -189,13 +189,13 @@ async def get_production_yoy_by_country( WHERE commodity_code = ? AND country_code IS NOT NULL ) SELECT country_name, country_code, market_year, - Production, Production_YoY_pct + production, production_yoy_pct FROM serving.commodity_metrics, latest WHERE commodity_code = ? AND country_code IS NOT NULL AND market_year = latest.max_year - AND Production > 0 - ORDER BY ABS(Production_YoY_pct) DESC + AND production > 0 + ORDER BY ABS(production_yoy_pct) DESC LIMIT ? """, [commodity_code, commodity_code, limit], diff --git a/web/src/beanflows/dashboard/routes.py b/web/src/beanflows/dashboard/routes.py index a3e1927..bd6b200 100644 --- a/web/src/beanflows/dashboard/routes.py +++ b/web/src/beanflows/dashboard/routes.py @@ -104,9 +104,9 @@ async def index(): time_series, top_producers, stu_trend, balance, yoy = await asyncio.gather( analytics.get_global_time_series( analytics.COFFEE_COMMODITY_CODE, - ["Production", "Exports", "Imports", "Ending_Stocks", "Total_Distribution"], + ["production", "exports", "imports", "ending_stocks", "total_distribution"], ), - analytics.get_top_countries(analytics.COFFEE_COMMODITY_CODE, "Production", limit=10), + analytics.get_top_countries(analytics.COFFEE_COMMODITY_CODE, "production", limit=10), analytics.get_stock_to_use_trend(analytics.COFFEE_COMMODITY_CODE), analytics.get_supply_demand_balance(analytics.COFFEE_COMMODITY_CODE), analytics.get_production_yoy_by_country(analytics.COFFEE_COMMODITY_CODE, limit=15), @@ -147,11 +147,11 @@ async def countries(): plan = (g.get("subscription") or {}).get("plan", "free") # Get available countries for coffee - all_countries = await analytics.get_top_countries(analytics.COFFEE_COMMODITY_CODE, "Production", limit=50) + all_countries = await analytics.get_top_countries(analytics.COFFEE_COMMODITY_CODE, "production", limit=50) # Parse query params selected_codes = request.args.getlist("country") - metric = request.args.get("metric", "Production") + metric = request.args.get("metric", "production") comparison_data = [] if selected_codes: diff --git a/web/src/beanflows/dashboard/templates/countries.html b/web/src/beanflows/dashboard/templates/countries.html index bb323c1..0e3f9ca 100644 --- a/web/src/beanflows/dashboard/templates/countries.html +++ b/web/src/beanflows/dashboard/templates/countries.html @@ -21,7 +21,7 @@
diff --git a/web/src/beanflows/dashboard/templates/index.html b/web/src/beanflows/dashboard/templates/index.html index d57d975..9c08dc8 100644 --- a/web/src/beanflows/dashboard/templates/index.html +++ b/web/src/beanflows/dashboard/templates/index.html @@ -18,7 +18,7 @@
Global Production (latest year)
-
{{ "{:,.0f}".format(latest.get("Production", 0)) }}
+
{{ "{:,.0f}".format(latest.get("production", 0)) }}
1,000 60-kg bags
@@ -26,7 +26,7 @@
Stock-to-Use Ratio
{% if stu_trend %} - {{ "{:.1f}".format(stu_trend[-1].get("Stock_to_Use_Ratio_pct", 0)) }}% + {{ "{:.1f}".format(stu_trend[-1].get("stock_to_use_ratio_pct", 0)) }}% {% else %} -- {% endif %} @@ -36,7 +36,7 @@
Trade Balance
-
{{ "{:,.0f}".format(latest.get("Exports", 0) - latest.get("Imports", 0)) }}
+
{{ "{:,.0f}".format(latest.get("exports", 0) - latest.get("imports", 0)) }}
Exports minus imports
@@ -90,10 +90,10 @@ {% for row in yoy %} {{ row.country_name }} - {{ "{:,.0f}".format(row.Production) }} - - {% if row.Production_YoY_pct is not none %} - {{ "{:+.1f}%".format(row.Production_YoY_pct) }} + {{ "{:,.0f}".format(row.production) }} + + {% if row.production_yoy_pct is not none %} + {{ "{:+.1f}%".format(row.production_yoy_pct) }} {% else %} -- {% endif %} @@ -163,11 +163,11 @@ if (tsData.length > 0) { data: { labels: tsData.map(r => r.market_year), datasets: [ - {label: 'Production', data: tsData.map(r => r.Production), borderColor: CHART_PALETTE[0], tension: 0.3}, - {label: 'Exports', data: tsData.map(r => r.Exports), borderColor: CHART_PALETTE[1], tension: 0.3}, - {label: 'Imports', data: tsData.map(r => r.Imports), borderColor: CHART_PALETTE[2], tension: 0.3}, - {label: 'Ending Stocks', data: tsData.map(r => r.Ending_Stocks), borderColor: CHART_PALETTE[3], tension: 0.3}, - {label: 'Total Distribution', data: tsData.map(r => r.Total_Distribution), borderColor: CHART_PALETTE[4], tension: 0.3}, + {label: 'Production', data: tsData.map(r => r.production), borderColor: CHART_PALETTE[0], tension: 0.3}, + {label: 'Exports', data: tsData.map(r => r.exports), borderColor: CHART_PALETTE[1], tension: 0.3}, + {label: 'Imports', data: tsData.map(r => r.imports), borderColor: CHART_PALETTE[2], tension: 0.3}, + {label: 'Ending Stocks', data: tsData.map(r => r.ending_stocks), borderColor: CHART_PALETTE[3], tension: 0.3}, + {label: 'Total Distribution', data: tsData.map(r => r.total_distribution), borderColor: CHART_PALETTE[4], tension: 0.3}, ] }, options: { @@ -187,7 +187,7 @@ if (stuData.length > 0) { labels: stuData.map(r => r.market_year), datasets: [{ label: 'Stock-to-Use Ratio (%)', - data: stuData.map(r => r.Stock_to_Use_Ratio_pct), + data: stuData.map(r => r.stock_to_use_ratio_pct), borderColor: CHART_COLORS.copper, backgroundColor: 'rgba(180, 83, 9, 0.08)', fill: true, @@ -211,7 +211,7 @@ if (topData.length > 0) { labels: topData.map(r => r.country_name), datasets: [{ label: 'Production', - data: topData.map(r => r.Production), + data: topData.map(r => r.production), backgroundColor: CHART_COLORS.copper }] },