43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""Shared numeric formatting utilities."""
|
|
from __future__ import annotations
|
|
|
|
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
|
from typing import Any
|
|
|
|
|
|
def decimal_string(value: Any) -> str:
|
|
"""Format a numeric value as a clean decimal string.
|
|
|
|
- None / empty → ""
|
|
- Whole numbers → no decimal point (e.g. "5")
|
|
- Decimals → trailing zeros stripped (e.g. "3.14")
|
|
- Unparseable → raw str()
|
|
"""
|
|
if value is None or value == "":
|
|
return ""
|
|
try:
|
|
d = Decimal(str(value))
|
|
except (InvalidOperation, ValueError):
|
|
return str(value)
|
|
n = d.normalize()
|
|
if n == n.to_integral():
|
|
return str(n.quantize(Decimal("1")))
|
|
return format(n, "f")
|
|
|
|
|
|
def fixed_decimal_string(value: Any, places: int = 2) -> str:
|
|
"""Format a numeric value with a fixed number of decimal places."""
|
|
if value is None or value == "":
|
|
return ""
|
|
try:
|
|
d = Decimal(str(value))
|
|
except (InvalidOperation, ValueError):
|
|
return str(value)
|
|
quantum = Decimal("1").scaleb(-places)
|
|
return format(d.quantize(quantum, rounding=ROUND_HALF_UP), f".{places}f")
|
|
|
|
|
|
def fixed_decimal_number(value: Any, places: int = 2) -> float:
|
|
"""Round a numeric value to a fixed precision for JSON number payloads."""
|
|
return float(fixed_decimal_string(value, places))
|