Update BASE_REFERENCE_DATE from January 15, 2025 to November 25, 2025 to ensure demo data appears recent and relevant. This fixes the "11 meses" (11 months) display issue in GlanceableHealthHero where lastOrchestrationRun was showing an 11-month-old timestamp. The BASE_REFERENCE_DATE is used by all demo seeding scripts to calculate relative dates. With the updated reference date, the most recent orchestration runs will show as "hace 2 días" or similar instead of "hace 11 meses". Impact: - All demo seed data timestamps will be relative to Nov 25, 2025 - Dashboard will show "Last run: 2 days ago" instead of "11 months ago" - Expiration dates, delivery dates, and other temporal data remain properly offset from the new reference Fixes: Issue #6 - "11 meses" data bug 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
212 lines
6.7 KiB
Python
212 lines
6.7 KiB
Python
"""
|
|
Demo Date Offset Utilities
|
|
Provides functions for adjusting dates during demo session cloning
|
|
to ensure all temporal data is relative to the demo session creation time
|
|
"""
|
|
|
|
from datetime import datetime, timezone, timedelta
|
|
from typing import Optional
|
|
|
|
|
|
# Base reference date for all demo seed data
|
|
# All seed scripts should use this as the "logical seed date"
|
|
# Updated to November 2025 to show recent orchestration runs
|
|
BASE_REFERENCE_DATE = datetime(2025, 11, 25, 12, 0, 0, tzinfo=timezone.utc)
|
|
|
|
|
|
def adjust_date_for_demo(
|
|
original_date: Optional[datetime],
|
|
session_created_at: datetime,
|
|
base_reference_date: datetime = BASE_REFERENCE_DATE
|
|
) -> Optional[datetime]:
|
|
"""
|
|
Adjust a date from seed data to be relative to demo session creation time
|
|
|
|
This ensures that demo data appears fresh and relevant regardless of when
|
|
the demo session is created. For example, expiration dates that were "15 days
|
|
from seed date" will become "15 days from session creation date".
|
|
|
|
Args:
|
|
original_date: The original date from the seed data (or None)
|
|
session_created_at: When the demo session was created
|
|
base_reference_date: The logical date when seed data was created (default: 2025-01-15)
|
|
|
|
Returns:
|
|
Adjusted date relative to session creation, or None if original_date was None
|
|
|
|
Example:
|
|
# Seed data created on 2025-01-15
|
|
# Stock expiration: 2025-01-30 (15 days from seed date)
|
|
# Demo session created: 2025-10-16
|
|
# Result: 2025-10-31 (15 days from session date)
|
|
|
|
>>> original = datetime(2025, 1, 30, 12, 0, tzinfo=timezone.utc)
|
|
>>> session = datetime(2025, 10, 16, 10, 0, tzinfo=timezone.utc)
|
|
>>> adjusted = adjust_date_for_demo(original, session)
|
|
>>> print(adjusted)
|
|
2025-10-31 10:00:00+00:00
|
|
"""
|
|
if original_date is None:
|
|
return None
|
|
|
|
# Ensure timezone-aware datetimes
|
|
if original_date.tzinfo is None:
|
|
original_date = original_date.replace(tzinfo=timezone.utc)
|
|
if session_created_at.tzinfo is None:
|
|
session_created_at = session_created_at.replace(tzinfo=timezone.utc)
|
|
if base_reference_date.tzinfo is None:
|
|
base_reference_date = base_reference_date.replace(tzinfo=timezone.utc)
|
|
|
|
# Calculate offset from base reference
|
|
offset = original_date - base_reference_date
|
|
|
|
# Apply offset to session creation date
|
|
return session_created_at + offset
|
|
|
|
|
|
def adjust_date_relative_to_now(
|
|
days_offset: int,
|
|
hours_offset: int = 0,
|
|
reference_time: Optional[datetime] = None
|
|
) -> datetime:
|
|
"""
|
|
Create a date relative to now (or a reference time) with specified offset
|
|
|
|
Useful for creating dates during cloning without needing to store seed dates.
|
|
|
|
Args:
|
|
days_offset: Number of days to add (negative for past dates)
|
|
hours_offset: Number of hours to add (negative for past times)
|
|
reference_time: Reference datetime (defaults to now)
|
|
|
|
Returns:
|
|
Calculated datetime
|
|
|
|
Example:
|
|
>>> # Create a date 7 days in the future
|
|
>>> future = adjust_date_relative_to_now(days_offset=7)
|
|
>>> # Create a date 3 days in the past
|
|
>>> past = adjust_date_relative_to_now(days_offset=-3)
|
|
"""
|
|
if reference_time is None:
|
|
reference_time = datetime.now(timezone.utc)
|
|
elif reference_time.tzinfo is None:
|
|
reference_time = reference_time.replace(tzinfo=timezone.utc)
|
|
|
|
return reference_time + timedelta(days=days_offset, hours=hours_offset)
|
|
|
|
|
|
def calculate_expiration_date(
|
|
received_date: datetime,
|
|
shelf_life_days: int
|
|
) -> datetime:
|
|
"""
|
|
Calculate expiration date based on received date and shelf life
|
|
|
|
Args:
|
|
received_date: When the product was received
|
|
shelf_life_days: Number of days until expiration
|
|
|
|
Returns:
|
|
Calculated expiration datetime
|
|
"""
|
|
if received_date.tzinfo is None:
|
|
received_date = received_date.replace(tzinfo=timezone.utc)
|
|
|
|
return received_date + timedelta(days=shelf_life_days)
|
|
|
|
|
|
def get_days_until_expiration(
|
|
expiration_date: datetime,
|
|
reference_date: Optional[datetime] = None
|
|
) -> int:
|
|
"""
|
|
Calculate number of days until expiration
|
|
|
|
Args:
|
|
expiration_date: The expiration datetime
|
|
reference_date: Reference datetime (defaults to now)
|
|
|
|
Returns:
|
|
Number of days until expiration (negative if already expired)
|
|
"""
|
|
if reference_date is None:
|
|
reference_date = datetime.now(timezone.utc)
|
|
elif reference_date.tzinfo is None:
|
|
reference_date = reference_date.replace(tzinfo=timezone.utc)
|
|
|
|
if expiration_date.tzinfo is None:
|
|
expiration_date = expiration_date.replace(tzinfo=timezone.utc)
|
|
|
|
delta = expiration_date - reference_date
|
|
return delta.days
|
|
|
|
|
|
def is_expiring_soon(
|
|
expiration_date: datetime,
|
|
threshold_days: int = 3,
|
|
reference_date: Optional[datetime] = None
|
|
) -> bool:
|
|
"""
|
|
Check if a product is expiring soon
|
|
|
|
Args:
|
|
expiration_date: The expiration datetime
|
|
threshold_days: Number of days to consider as "soon" (default: 3)
|
|
reference_date: Reference datetime (defaults to now)
|
|
|
|
Returns:
|
|
True if expiring within threshold_days, False otherwise
|
|
"""
|
|
days_until = get_days_until_expiration(expiration_date, reference_date)
|
|
return 0 <= days_until <= threshold_days
|
|
|
|
|
|
def is_expired(
|
|
expiration_date: datetime,
|
|
reference_date: Optional[datetime] = None
|
|
) -> bool:
|
|
"""
|
|
Check if a product is expired
|
|
|
|
Args:
|
|
expiration_date: The expiration datetime
|
|
reference_date: Reference datetime (defaults to now)
|
|
|
|
Returns:
|
|
True if expired, False otherwise
|
|
"""
|
|
days_until = get_days_until_expiration(expiration_date, reference_date)
|
|
return days_until < 0
|
|
|
|
|
|
def adjust_multiple_dates(
|
|
dates_dict: dict,
|
|
session_created_at: datetime,
|
|
base_reference_date: datetime = BASE_REFERENCE_DATE
|
|
) -> dict:
|
|
"""
|
|
Adjust multiple dates in a dictionary
|
|
|
|
Args:
|
|
dates_dict: Dictionary with datetime values to adjust
|
|
session_created_at: When the demo session was created
|
|
base_reference_date: The logical date when seed data was created
|
|
|
|
Returns:
|
|
Dictionary with adjusted dates (preserves None values)
|
|
|
|
Example:
|
|
>>> dates = {
|
|
... 'expiration_date': datetime(2025, 1, 30, tzinfo=timezone.utc),
|
|
... 'received_date': datetime(2025, 1, 15, tzinfo=timezone.utc),
|
|
... 'optional_date': None
|
|
... }
|
|
>>> session = datetime(2025, 10, 16, tzinfo=timezone.utc)
|
|
>>> adjusted = adjust_multiple_dates(dates, session)
|
|
"""
|
|
return {
|
|
key: adjust_date_for_demo(value, session_created_at, base_reference_date)
|
|
for key, value in dates_dict.items()
|
|
}
|