Files
calminer/routes/template_filters.py
zwitschi d9fd82b2e3 feat: Implement initial capex calculation feature
- Added CapexComponentInput, CapexParameters, CapexCalculationRequest, CapexCalculationResult, and related schemas for capex calculations.
- Introduced calculate_initial_capex function to aggregate capex components and compute totals and timelines.
- Created ProjectCapexRepository and ScenarioCapexRepository for managing capex snapshots in the database.
- Developed capex.html template for capturing and displaying initial capex data.
- Registered common Jinja2 filters for formatting currency and percentages.
- Implemented unit and integration tests for capex calculation functionality.
- Updated unit of work to include new repositories for capex management.
2025-11-12 23:51:52 +01:00

96 lines
2.6 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
from typing import Any
from fastapi.templating import Jinja2Templates
def format_datetime(value: Any) -> str:
"""Render datetime values consistently for templates."""
if not isinstance(value, datetime):
return ""
if value.tzinfo is None:
value = value.replace(tzinfo=timezone.utc)
return value.strftime("%Y-%m-%d %H:%M UTC")
def currency_display(value: Any, currency_code: str | None) -> str:
"""Format numeric values with currency context."""
if value is None:
return ""
if isinstance(value, (int, float)):
formatted_value = f"{value:,.2f}"
else:
formatted_value = str(value)
if currency_code:
return f"{currency_code} {formatted_value}"
return formatted_value
def format_metric(value: Any, metric_name: str, currency_code: str | None = None) -> str:
"""Format metrics according to their semantic type."""
if value is None:
return ""
currency_metrics = {
"npv",
"inflows",
"outflows",
"net",
"total_inflows",
"total_outflows",
"total_net",
}
if metric_name in currency_metrics and currency_code:
return currency_display(value, currency_code)
percentage_metrics = {"irr", "payback_period"}
if metric_name in percentage_metrics:
if isinstance(value, (int, float)):
return f"{value:.2f}%"
return f"{value}%"
if isinstance(value, (int, float)):
return f"{value:,.2f}"
return str(value)
def percentage_display(value: Any) -> str:
"""Format numeric values as percentages."""
if value is None:
return ""
if isinstance(value, (int, float)):
return f"{value:.2f}%"
return f"{value}%"
def period_display(value: Any) -> str:
"""Format period values in years."""
if value is None:
return ""
if isinstance(value, (int, float)):
if value == int(value):
return f"{int(value)} years"
return f"{value:.1f} years"
return str(value)
def register_common_filters(templates: Jinja2Templates) -> None:
templates.env.filters["format_datetime"] = format_datetime
templates.env.filters["currency_display"] = currency_display
templates.env.filters["format_metric"] = format_metric
templates.env.filters["percentage_display"] = percentage_display
templates.env.filters["period_display"] = period_display
__all__ = [
"format_datetime",
"currency_display",
"format_metric",
"percentage_display",
"period_display",
"register_common_filters",
]