feat:v2 #12
@@ -3,6 +3,7 @@
|
|||||||
## 2025-11-12
|
## 2025-11-12
|
||||||
|
|
||||||
- Diagnosed admin bootstrap failure caused by legacy `roles` schema, added Alembic migration `20251112_00_add_roles_metadata_columns.py` to backfill `display_name`, `description`, `created_at`, and `updated_at`, and verified the migration via full pytest run in the activated `.venv`.
|
- Diagnosed admin bootstrap failure caused by legacy `roles` schema, added Alembic migration `20251112_00_add_roles_metadata_columns.py` to backfill `display_name`, `description`, `created_at`, and `updated_at`, and verified the migration via full pytest run in the activated `.venv`.
|
||||||
|
- Resolved Ruff E402 warnings by moving module docstrings ahead of `from __future__ import annotations` across currency and pricing service modules, dropped the unused `HTTPException` import in `monitoring/__init__.py`, and confirmed a clean `ruff check .` run.
|
||||||
|
|
||||||
## 2025-11-11
|
## 2025-11-11
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
import time
|
import time
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from fastapi import BackgroundTasks, Request, Response
|
from fastapi import Request, Response
|
||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
from monitoring.metrics import observe_request
|
from monitoring.metrics import observe_request
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from sqlalchemy import Column, DateTime, Float, Integer, String
|
from sqlalchemy import Column, DateTime, Float, Integer, String
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
"""Database models for persisted pricing configuration settings."""
|
"""Database models for persisted pricing configuration settings."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
from fastapi import APIRouter, Depends, Query, Response
|
||||||
from prometheus_client import CONTENT_TYPE_LATEST, generate_latest
|
from prometheus_client import CONTENT_TYPE_LATEST, generate_latest
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@@ -94,8 +94,9 @@ async def detailed_health(db: Session = Depends(get_db)) -> dict:
|
|||||||
for m in request_metrics:
|
for m in request_metrics:
|
||||||
if m.duration_seconds is not None:
|
if m.duration_seconds is not None:
|
||||||
durations.append(m.duration_seconds)
|
durations.append(m.duration_seconds)
|
||||||
if m.status_code is not None and m.status_code >= 400:
|
if m.status_code is not None:
|
||||||
error_count += 1
|
if m.status_code >= 400:
|
||||||
|
error_count += 1
|
||||||
total_requests = len(request_metrics)
|
total_requests = len(request_metrics)
|
||||||
|
|
||||||
avg_duration = sum(durations) / len(durations) if durations else 0
|
avg_duration = sum(durations) / len(durations) if durations else 0
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from urllib.parse import urlencode
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
"""Utilities for currency normalization within pricing and financial workflows."""
|
"""Utilities for currency normalization within pricing and financial workflows."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
"""Financial calculation helpers for project evaluation metrics."""
|
"""Financial calculation helpers for project evaluation metrics."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from math import isclose, isfinite
|
from math import isclose, isfinite
|
||||||
@@ -151,7 +151,8 @@ def internal_rate_of_return(
|
|||||||
|
|
||||||
amounts = [amount for amount, _ in flows]
|
amounts = [amount for amount, _ in flows]
|
||||||
if not any(amount < 0 for amount in amounts) or not any(amount > 0 for amount in amounts):
|
if not any(amount < 0 for amount in amounts) or not any(amount > 0 for amount in amounts):
|
||||||
raise ValueError("cash_flows must include both negative and positive values")
|
raise ValueError(
|
||||||
|
"cash_flows must include both negative and positive values")
|
||||||
|
|
||||||
def _npv_with_flows(rate: float) -> float:
|
def _npv_with_flows(rate: float) -> float:
|
||||||
periodic_rate = rate / float(compounds_per_year)
|
periodic_rate = rate / float(compounds_per_year)
|
||||||
@@ -170,7 +171,8 @@ def internal_rate_of_return(
|
|||||||
derivative = 0.0
|
derivative = 0.0
|
||||||
for amount, periods in flows:
|
for amount, periods in flows:
|
||||||
factor = (1.0 + periodic_rate) ** (-periods - 1.0)
|
factor = (1.0 + periodic_rate) ** (-periods - 1.0)
|
||||||
derivative += -amount * periods * factor / float(compounds_per_year)
|
derivative += -amount * periods * \
|
||||||
|
factor / float(compounds_per_year)
|
||||||
return derivative
|
return derivative
|
||||||
|
|
||||||
rate = float(guess)
|
rate = float(guess)
|
||||||
@@ -199,7 +201,8 @@ def internal_rate_of_return(
|
|||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
if lower_value * upper_value > 0:
|
if lower_value * upper_value > 0:
|
||||||
raise ConvergenceError("IRR could not be bracketed within default bounds")
|
raise ConvergenceError(
|
||||||
|
"IRR could not be bracketed within default bounds")
|
||||||
|
|
||||||
for _ in range(max_iterations * 2):
|
for _ in range(max_iterations * 2):
|
||||||
midpoint = (lower_bound + upper_bound) / 2.0
|
midpoint = (lower_bound + upper_bound) / 2.0
|
||||||
@@ -245,4 +248,5 @@ def payback_period(
|
|||||||
cumulative = next_cumulative
|
cumulative = next_cumulative
|
||||||
previous_period = periods
|
previous_period = periods
|
||||||
|
|
||||||
raise PaybackNotReachedError("Cumulative cash flow never becomes non-negative")
|
raise PaybackNotReachedError(
|
||||||
|
"Cumulative cash flow never becomes non-negative")
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from typing import Any, Dict, Optional
|
|||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from config.database import get_db
|
|
||||||
from models.performance_metric import PerformanceMetric
|
from models.performance_metric import PerformanceMetric
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
"""Pricing service implementing commodity revenue calculations.
|
"""Pricing service implementing commodity revenue calculations.
|
||||||
|
|
||||||
This module exposes data models and helpers for computing product pricing
|
This module exposes data models and helpers for computing product pricing
|
||||||
@@ -9,6 +7,8 @@ calculation steps (payable metal, penalties, net revenue) and is intended to be
|
|||||||
composed within broader scenario evaluation workflows.
|
composed within broader scenario evaluation workflows.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
"""Reporting service layer aggregating deterministic and simulation metrics."""
|
"""Reporting service layer aggregating deterministic and simulation metrics."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import date
|
from datetime import date
|
||||||
import math
|
import math
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
"""Scenario evaluation services including pricing integration."""
|
"""Scenario evaluation services including pricing integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ def run_monte_carlo(
|
|||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
# Record failed simulation
|
# Record failed simulation
|
||||||
duration = time.time() - start_time
|
duration = time.time() - start_time
|
||||||
observe_simulation(
|
observe_simulation(
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import pytest
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from models import Project, Scenario, FinancialInput
|
from models import Project, Scenario
|
||||||
from models.metadata import CostBucket, ResourceType
|
|
||||||
from services.reporting import (
|
from services.reporting import (
|
||||||
ReportingService,
|
ReportingService,
|
||||||
ReportFilters,
|
ReportFilters,
|
||||||
@@ -244,7 +242,6 @@ class TestReportingRoutes:
|
|||||||
def test_unauthorized_access(self, client: TestClient):
|
def test_unauthorized_access(self, client: TestClient):
|
||||||
# Create a new client without authentication
|
# Create a new client without authentication
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from routes.reports import router as reports_router
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
app.include_router(reports_router)
|
app.include_router(reports_router)
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|||||||
Reference in New Issue
Block a user