nexus-3/backend/core/utils/helpers.py
2026-01-26 10:30:49 -05:00

327 lines
7.8 KiB
Python

"""
Helper functions for the API.
Provides utility functions for data transformation, processing, and other common tasks.
"""
import uuid
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
import json
def generate_uuid() -> str:
"""
Generate a new UUID string.
Returns:
str: A new UUID string.
"""
return str(uuid.uuid4())
def format_date(date_obj: datetime) -> str:
"""
Format a datetime object as a date string (YYYY-MM-DD).
Args:
date_obj: The datetime object to format.
Returns:
str: The formatted date string.
"""
return date_obj.strftime('%Y-%m-%d')
def format_datetime(datetime_obj: datetime) -> str:
"""
Format a datetime object as an ISO string.
Args:
datetime_obj: The datetime object to format.
Returns:
str: The formatted datetime string.
"""
return datetime_obj.isoformat()
def parse_date(date_str: str) -> Optional[datetime]:
"""
Parse a date string into a datetime object.
Args:
date_str: The date string to parse (YYYY-MM-DD).
Returns:
Optional[datetime]: The parsed datetime or None if invalid.
"""
if not date_str:
return None
try:
return datetime.fromisoformat(date_str)
except ValueError:
return None
def parse_datetime(datetime_str: str) -> Optional[datetime]:
"""
Parse a datetime string into a datetime object.
Args:
datetime_str: The datetime string to parse.
Returns:
Optional[datetime]: The parsed datetime or None if invalid.
"""
if not datetime_str:
return None
try:
return datetime.fromisoformat(datetime_str)
except ValueError:
return None
def to_camel_case(snake_str: str) -> str:
"""
Convert a snake_case string to camelCase.
Args:
snake_str: The snake_case string to convert.
Returns:
str: The camelCase string.
"""
components = snake_str.split('_')
return components[0] + ''.join(x.title() for x in components[1:])
def to_snake_case(camel_str: str) -> str:
"""
Convert a camelCase string to snake_case.
Args:
camel_str: The camelCase string to convert.
Returns:
str: The snake_case string.
"""
import re
# Use regex to find all capital letters and insert underscore before them
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
def convert_keys_to_camel_case(data: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert all dictionary keys from snake_case to camelCase.
Args:
data: The dictionary with snake_case keys.
Returns:
Dict[str, Any]: A new dictionary with camelCase keys.
"""
if not isinstance(data, dict):
return data
result = {}
for key, value in data.items():
if isinstance(value, dict):
value = convert_keys_to_camel_case(value)
elif isinstance(value, list):
value = [
convert_keys_to_camel_case(item) if isinstance(item, dict) else item
for item in value
]
result[to_camel_case(key)] = value
return result
def convert_keys_to_snake_case(data: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert all dictionary keys from camelCase to snake_case.
Args:
data: The dictionary with camelCase keys.
Returns:
Dict[str, Any]: A new dictionary with snake_case keys.
"""
if not isinstance(data, dict):
return data
result = {}
for key, value in data.items():
if isinstance(value, dict):
value = convert_keys_to_snake_case(value)
elif isinstance(value, list):
value = [
convert_keys_to_snake_case(item) if isinstance(item, dict) else item
for item in value
]
result[to_snake_case(key)] = value
return result
def filter_none_values(data: Dict[str, Any]) -> Dict[str, Any]:
"""
Remove all None values from a dictionary.
Args:
data: The dictionary to filter.
Returns:
Dict[str, Any]: A new dictionary without None values.
"""
return {k: v for k, v in data.items() if v is not None}
def get_week_start_end(date: datetime) -> tuple:
"""
Get the start and end dates of the week containing the given date.
Args:
date: The date to get the week for.
Returns:
tuple: (start_date, end_date) of the week.
"""
# Monday is 0 and Sunday is 6
start = date - timedelta(days=date.weekday())
end = start + timedelta(days=6)
return start, end
def get_month_start_end(date: datetime) -> tuple:
"""
Get the start and end dates of the month containing the given date.
Args:
date: The date to get the month for.
Returns:
tuple: (start_date, end_date) of the month.
"""
start = date.replace(day=1)
# Get the last day by going to next month and subtracting one day
if date.month == 12:
end = datetime(date.year + 1, 1, 1) - timedelta(days=1)
else:
end = datetime(date.year, date.month + 1, 1) - timedelta(days=1)
return start, end
def get_date_range(start_date: str, end_date: str) -> List[str]:
"""
Get a list of date strings between start_date and end_date (inclusive).
Args:
start_date: The start date string (YYYY-MM-DD).
end_date: The end date string (YYYY-MM-DD).
Returns:
List[str]: List of date strings in the range.
"""
start = parse_date(start_date)
end = parse_date(end_date)
if not start or not end:
return []
if start > end:
return []
date_list = []
current = start
while current <= end:
date_list.append(format_date(current))
current += timedelta(days=1)
return date_list
def date_diff_in_days(start_date: str, end_date: str) -> int:
"""
Calculate the difference in days between two date strings.
Args:
start_date: The start date string (YYYY-MM-DD).
end_date: The end date string (YYYY-MM-DD).
Returns:
int: The number of days between the dates. Returns 0 if dates are invalid.
"""
start = parse_date(start_date)
end = parse_date(end_date)
if not start or not end:
return 0
return (end - start).days
def dict_to_json(data: Dict[str, Any]) -> str:
"""
Convert a dictionary to a JSON string.
Args:
data: The dictionary to convert.
Returns:
str: The JSON string.
"""
return json.dumps(data, default=str)
def json_to_dict(json_str: str) -> Dict[str, Any]:
"""
Convert a JSON string to a dictionary.
Args:
json_str: The JSON string to convert.
Returns:
Dict[str, Any]: The dictionary. Returns empty dict if JSON is invalid.
"""
try:
return json.loads(json_str)
except (json.JSONDecodeError, TypeError):
return {}
def paginate_results(items: List[Any], page: int = 1, page_size: int = 10) -> Dict[str, Any]:
"""
Paginate a list of items.
Args:
items: The list of items to paginate.
page: The page number (1-based).
page_size: The number of items per page.
Returns:
Dict[str, Any]: A dictionary with pagination info and results.
"""
if page < 1:
page = 1
if page_size < 1:
page_size = 10
total_items = len(items)
total_pages = (total_items + page_size - 1) // page_size
start_idx = (page - 1) * page_size
end_idx = min(start_idx + page_size, total_items)
return {
'page': page,
'page_size': page_size,
'total_items': total_items,
'total_pages': total_pages,
'has_previous': page > 1,
'has_next': page < total_pages,
'items': items[start_idx:end_idx]
}