Files
labFusion/services/service-adapters/utils/time_formatter.py
GSRN 7373ccfa1d
Some checks failed
Integration Tests / integration-tests (push) Failing after 20s
Integration Tests / performance-tests (push) Has been skipped
Service Adapters (Python FastAPI) / test (3.11) (push) Failing after 23s
Frontend (React) / test (20) (push) Failing after 1m3s
Frontend (React) / build (push) Has been skipped
Frontend (React) / lighthouse (push) Has been skipped
Service Adapters (Python FastAPI) / test (3.12) (push) Failing after 23s
Service Adapters (Python FastAPI) / test (3.13) (push) Failing after 20s
Service Adapters (Python FastAPI) / build (push) Has been skipped
feat: Enhance frontend loading experience and service status handling
### Summary of Changes
- Removed proxy configuration in `rsbuild.config.js` as the API Gateway is not running.
- Added smooth transitions and gentle loading overlays in CSS for improved user experience during data loading.
- Updated `Dashboard` component to conditionally display loading spinner and gentle loading overlay based on data fetching state.
- Enhanced `useOfflineAwareServiceStatus` and `useOfflineAwareSystemData` hooks to manage loading states and service status more effectively.
- Increased refresh intervals for service status and system data to reduce API call frequency.

### Expected Results
- Improved user experience with smoother loading transitions and better feedback during data refreshes.
- Enhanced handling of service status checks, providing clearer information when services are unavailable.
- Streamlined code for managing loading states, making it easier to maintain and extend in the future.
2025-09-18 11:09:51 +02:00

200 lines
5.6 KiB
Python

"""
Time Formatting Utilities
This module provides utilities for formatting time durations and timestamps
into human-readable formats for the frontend.
"""
import re
from datetime import datetime, timezone
from typing import Optional, Union
def format_uptime_for_frontend(uptime_value: Optional[str]) -> str:
"""
Format uptime value for frontend display in "Xd Xh Xm" format.
Args:
uptime_value: Raw uptime value (timestamp, epoch, duration string, etc.)
Returns:
Formatted uptime string like "2d 5h 30m" or "0d 0h" if invalid
"""
if not uptime_value:
return "0d 0h"
try:
# Try to parse as timestamp (ISO format)
if _is_timestamp(uptime_value):
return _format_timestamp_uptime(uptime_value)
# Try to parse as epoch timestamp
if _is_epoch(uptime_value):
return _format_epoch_uptime(uptime_value)
# Try to parse as duration string (e.g., "2h 30m", "5d 2h 15m")
if _is_duration_string(uptime_value):
return _format_duration_string(uptime_value)
# Try to parse as numeric seconds
if _is_numeric_seconds(uptime_value):
return _format_seconds_uptime(float(uptime_value))
# If none of the above, return as-is or default
return uptime_value if len(uptime_value) < 50 else "0d 0h"
except Exception:
return "0d 0h"
def _is_timestamp(value: str) -> bool:
"""Check if value is an ISO timestamp."""
try:
datetime.fromisoformat(value.replace('Z', '+00:00'))
return True
except (ValueError, AttributeError):
return False
def _is_epoch(value: str) -> bool:
"""Check if value is an epoch timestamp."""
try:
float(value)
return len(value) >= 10 and float(value) > 1000000000 # Reasonable epoch range
except (ValueError, TypeError):
return False
def _is_duration_string(value: str) -> bool:
"""Check if value is a duration string like '2h 30m' or '5d 2h 15m'."""
# Look for patterns like "2h 30m", "5d 2h 15m", "1d 2h 3m 4s"
pattern = r'^\d+[dhms]\s*(\d+[dhms]\s*)*$'
return bool(re.match(pattern, value.strip()))
def _is_numeric_seconds(value: str) -> bool:
"""Check if value is numeric seconds."""
try:
float(value)
return True
except (ValueError, TypeError):
return False
def _format_timestamp_uptime(timestamp: str) -> str:
"""Format timestamp uptime (time since timestamp)."""
try:
# Parse timestamp
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
# Calculate time difference
now = datetime.now(timezone.utc)
diff = now - dt
return _format_timedelta(diff)
except Exception:
return "0d 0h"
def _format_epoch_uptime(epoch_str: str) -> str:
"""Format epoch timestamp uptime."""
try:
epoch = float(epoch_str)
dt = datetime.fromtimestamp(epoch, tz=timezone.utc)
now = datetime.now(timezone.utc)
diff = now - dt
return _format_timedelta(diff)
except Exception:
return "0d 0h"
def _format_duration_string(duration: str) -> str:
"""Format duration string to standardized format."""
try:
# Parse duration string like "2h 30m" or "5d 2h 15m"
total_seconds = _parse_duration_string(duration)
return _format_seconds_uptime(total_seconds)
except Exception:
return "0d 0h"
def _format_seconds_uptime(seconds: float) -> str:
"""Format seconds to "Xd Xh Xm" format."""
return _format_timedelta_from_seconds(seconds)
def _parse_duration_string(duration: str) -> float:
"""Parse duration string to total seconds."""
total_seconds = 0
# Extract days
days_match = re.search(r'(\d+)d', duration)
if days_match:
total_seconds += int(days_match.group(1)) * 86400
# Extract hours
hours_match = re.search(r'(\d+)h', duration)
if hours_match:
total_seconds += int(hours_match.group(1)) * 3600
# Extract minutes
minutes_match = re.search(r'(\d+)m', duration)
if minutes_match:
total_seconds += int(minutes_match.group(1)) * 60
# Extract seconds
seconds_match = re.search(r'(\d+)s', duration)
if seconds_match:
total_seconds += int(seconds_match.group(1))
return total_seconds
def _format_timedelta(td) -> str:
"""Format timedelta to "Xd Xh Xm" format."""
total_seconds = int(td.total_seconds())
return _format_timedelta_from_seconds(total_seconds)
def _format_timedelta_from_seconds(total_seconds: Union[int, float]) -> str:
"""Format total seconds to "Xd Xh Xm" format."""
if total_seconds < 0:
return "0d 0h"
# Convert to int to avoid decimal places
total_seconds = int(total_seconds)
days = total_seconds // 86400
hours = (total_seconds % 86400) // 3600
minutes = (total_seconds % 3600) // 60
# Only show days if > 0
if days > 0:
return f"{days}d {hours}h {minutes}m"
elif hours > 0:
return f"{hours}h {minutes}m"
else:
return f"{minutes}m"
def format_response_time(seconds: Optional[float]) -> str:
"""
Format response time for display.
Args:
seconds: Response time in seconds
Returns:
Formatted response time string
"""
if seconds is None:
return "N/A"
if seconds < 1:
return f"{seconds * 1000:.0f}ms"
else:
return f"{seconds:.2f}s"