feat: Auto-adjust start_date to first available measurement
**User Feedback:** "Macht es nicht Sinn, den nächsten verfügbaren Wert
am oder nach dem Startdatum automatisch zu ermitteln und auch das
Startdatum dann automatisch auf den Wert zu setzen?"
**New Logic:**
1. User sets start_date: 2026-01-01
2. System finds FIRST measurement >= 2026-01-01 (e.g., 2026-01-15: 88 kg)
3. System auto-adjusts:
- start_date → 2026-01-15
- start_value → 88 kg
4. User sees: "Start: 88 kg (15.01.26)" ✓
**Benefits:**
- User doesn't need to know exact date of first measurement
- More user-friendly UX
- Automatically finds closest available data
**Implementation:**
- Changed query from "BETWEEN date ±7 days" to "WHERE date >= target_date"
- Returns dict with {'value': float, 'date': date}
- Both create_goal() and update_goal() now adjust start_date automatically
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
169dbba092
commit
e479627f0f
|
|
@ -395,11 +395,16 @@ def create_goal(data: GoalCreate, session: dict = Depends(require_auth)):
|
|||
start_value = data.start_value
|
||||
elif start_date < date.today():
|
||||
# Historical start date - try to get historical value
|
||||
start_value = _get_historical_value_for_goal_type(conn, pid, data.goal_type, start_date)
|
||||
if start_value is None:
|
||||
# No data on that date, fall back to current value
|
||||
historical_data = _get_historical_value_for_goal_type(conn, pid, data.goal_type, start_date)
|
||||
if historical_data is not None:
|
||||
# Use the actual measurement date and value
|
||||
start_date = historical_data['date']
|
||||
start_value = historical_data['value']
|
||||
print(f"[INFO] Auto-adjusted start_date to {start_date} (first measurement)")
|
||||
else:
|
||||
# No data found, fall back to current value and keep original date
|
||||
start_value = current_value
|
||||
print(f"[WARN] No historical data for {data.goal_type} on {start_date}, using current value")
|
||||
print(f"[WARN] No historical data for {data.goal_type} on or after {start_date}, using current value")
|
||||
else:
|
||||
# Start date is today, use current value
|
||||
start_value = current_value
|
||||
|
|
@ -514,15 +519,23 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
|
|||
goal_row = cur.fetchone()
|
||||
if goal_row:
|
||||
goal_type = goal_row['goal_type']
|
||||
print(f"[DEBUG] Looking up historical value for {goal_type} on {data.start_date}")
|
||||
historical_value = _get_historical_value_for_goal_type(conn, pid, goal_type, data.start_date)
|
||||
print(f"[DEBUG] Historical value result: {historical_value}")
|
||||
if historical_value is not None:
|
||||
print(f"[DEBUG] Looking up historical value for {goal_type} on or after {data.start_date}")
|
||||
historical_data = _get_historical_value_for_goal_type(conn, pid, goal_type, data.start_date)
|
||||
print(f"[DEBUG] Historical data result: {historical_data}")
|
||||
if historical_data is not None:
|
||||
# Update both start_date and start_value with actual measurement
|
||||
actual_date = historical_data['date']
|
||||
actual_value = historical_data['value']
|
||||
|
||||
# Replace the start_date in updates with the actual measurement date
|
||||
updates[-1] = "start_date = %s" # Update the last added start_date
|
||||
params[-1] = actual_date
|
||||
|
||||
updates.append("start_value = %s")
|
||||
params.append(historical_value)
|
||||
print(f"[INFO] Auto-populated start_value from {data.start_date}: {historical_value}")
|
||||
params.append(actual_value)
|
||||
print(f"[INFO] Auto-adjusted to first measurement: {actual_date} = {actual_value}")
|
||||
else:
|
||||
print(f"[WARN] No historical data found for {goal_type} around {data.start_date}")
|
||||
print(f"[WARN] No historical data found for {goal_type} on or after {data.start_date}")
|
||||
else:
|
||||
print(f"[ERROR] Could not find goal with id {goal_id}")
|
||||
|
||||
|
|
@ -683,19 +696,19 @@ def _get_current_value_for_goal_type(conn, profile_id: str, goal_type: str) -> O
|
|||
# Delegate to universal fetcher (Phase 1.5)
|
||||
return get_current_value_for_goal(conn, profile_id, goal_type)
|
||||
|
||||
def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, target_date: date) -> Optional[float]:
|
||||
def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, target_date: date) -> Optional[dict]:
|
||||
"""
|
||||
Get historical value for a goal type on a specific date.
|
||||
Looks for closest value within ±7 days window.
|
||||
Get historical value for a goal type on or after a specific date.
|
||||
Finds the FIRST available measurement >= target_date.
|
||||
|
||||
Args:
|
||||
conn: Database connection
|
||||
profile_id: User's profile ID
|
||||
goal_type: Goal type key (e.g., 'weight', 'body_fat')
|
||||
target_date: Date to query (can be historical)
|
||||
target_date: Desired start date (will find first measurement on or after this date)
|
||||
|
||||
Returns:
|
||||
Historical value or None if not found
|
||||
Dict with {'value': float, 'date': date} or None if not found
|
||||
"""
|
||||
from goal_utils import get_goal_type_config, get_cursor
|
||||
|
||||
|
|
@ -726,20 +739,16 @@ def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, t
|
|||
else:
|
||||
date_col = 'date'
|
||||
|
||||
# Find first measurement on or after target_date
|
||||
query = f"""
|
||||
SELECT {source_column}
|
||||
SELECT {source_column}, {date_col} as measurement_date
|
||||
FROM {source_table}
|
||||
WHERE profile_id = %s
|
||||
AND {date_col} BETWEEN %s AND %s
|
||||
ORDER BY ABS({date_col} - %s::date)
|
||||
AND {date_col} >= %s
|
||||
ORDER BY {date_col} ASC
|
||||
LIMIT 1
|
||||
"""
|
||||
params = (
|
||||
profile_id,
|
||||
target_date - timedelta(days=7),
|
||||
target_date + timedelta(days=7),
|
||||
target_date
|
||||
)
|
||||
params = (profile_id, target_date)
|
||||
|
||||
print(f"[DEBUG] Query: {query}")
|
||||
print(f"[DEBUG] Params: {params}")
|
||||
|
|
@ -751,12 +760,24 @@ def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, t
|
|||
|
||||
if row:
|
||||
value = row[source_column]
|
||||
measurement_date = row['measurement_date']
|
||||
|
||||
# Convert Decimal to float
|
||||
result = float(value) if value is not None else None
|
||||
print(f"[DEBUG] Returning value: {result}")
|
||||
result_value = float(value) if value is not None else None
|
||||
|
||||
# Handle different date types (date vs datetime)
|
||||
if hasattr(measurement_date, 'date'):
|
||||
# It's a datetime, extract date
|
||||
result_date = measurement_date.date()
|
||||
else:
|
||||
# It's already a date
|
||||
result_date = measurement_date
|
||||
|
||||
result = {'value': result_value, 'date': result_date}
|
||||
print(f"[DEBUG] Returning: {result}")
|
||||
return result
|
||||
|
||||
print(f"[DEBUG] No data found in date range")
|
||||
print(f"[DEBUG] No data found on or after {target_date}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to get historical value for {goal_type} on {target_date}: {e}")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user