Files
common/artdag_common/utils/formatting.py
giles fd97812e3d Initial artdag-common shared library
Shared components for L1 and L2 servers:
- Jinja2 template system with base template and components
- Middleware for auth and content negotiation
- Pydantic models for requests/responses
- Utility functions for pagination, media, formatting
- Constants for Tailwind/HTMX/Cytoscape CDNs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 07:07:59 +00:00

166 lines
3.7 KiB
Python

"""
Formatting utilities for display.
"""
from datetime import datetime
from typing import Optional, Union
def format_date(
value: Optional[Union[str, datetime]],
length: int = 10,
include_time: bool = False,
) -> str:
"""
Format a date/datetime for display.
Args:
value: Date string or datetime object
length: Length to truncate to (default 10 for YYYY-MM-DD)
include_time: Whether to include time portion
Returns:
Formatted date string
"""
if value is None:
return ""
if isinstance(value, str):
# Parse ISO format string
try:
if "T" in value:
dt = datetime.fromisoformat(value.replace("Z", "+00:00"))
else:
return value[:length]
except ValueError:
return value[:length]
else:
dt = value
if include_time:
return dt.strftime("%Y-%m-%d %H:%M")
return dt.strftime("%Y-%m-%d")
def format_size(size_bytes: Optional[int]) -> str:
"""
Format file size in human-readable form.
Args:
size_bytes: Size in bytes
Returns:
Human-readable size string (e.g., "1.5 MB")
"""
if size_bytes is None:
return "Unknown"
if size_bytes < 0:
return "Unknown"
if size_bytes == 0:
return "0 B"
units = ["B", "KB", "MB", "GB", "TB"]
unit_index = 0
size = float(size_bytes)
while size >= 1024 and unit_index < len(units) - 1:
size /= 1024
unit_index += 1
if unit_index == 0:
return f"{int(size)} {units[unit_index]}"
return f"{size:.1f} {units[unit_index]}"
def truncate_hash(value: str, length: int = 16, suffix: str = "...") -> str:
"""
Truncate a hash or long string with ellipsis.
Args:
value: String to truncate
length: Maximum length before truncation
suffix: Suffix to add when truncated
Returns:
Truncated string
"""
if not value:
return ""
if len(value) <= length:
return value
return f"{value[:length]}{suffix}"
def format_duration(seconds: Optional[float]) -> str:
"""
Format duration in human-readable form.
Args:
seconds: Duration in seconds
Returns:
Human-readable duration string (e.g., "2m 30s")
"""
if seconds is None or seconds < 0:
return "Unknown"
if seconds < 1:
return f"{int(seconds * 1000)}ms"
if seconds < 60:
return f"{seconds:.1f}s"
minutes = int(seconds // 60)
remaining_seconds = int(seconds % 60)
if minutes < 60:
if remaining_seconds:
return f"{minutes}m {remaining_seconds}s"
return f"{minutes}m"
hours = minutes // 60
remaining_minutes = minutes % 60
if remaining_minutes:
return f"{hours}h {remaining_minutes}m"
return f"{hours}h"
def format_count(count: int) -> str:
"""
Format a count with abbreviation for large numbers.
Args:
count: Number to format
Returns:
Formatted string (e.g., "1.2K", "3.5M")
"""
if count < 1000:
return str(count)
if count < 1000000:
return f"{count / 1000:.1f}K"
if count < 1000000000:
return f"{count / 1000000:.1f}M"
return f"{count / 1000000000:.1f}B"
def format_percentage(value: float, decimals: int = 1) -> str:
"""
Format a percentage value.
Args:
value: Percentage value (0-100 or 0-1)
decimals: Number of decimal places
Returns:
Formatted percentage string
"""
# Assume 0-1 if less than 1
if value <= 1:
value *= 100
if decimals == 0:
return f"{int(value)}%"
return f"{value:.{decimals}f}%"