fix: robust error handling in goal value fetcher
Prevents crashes when: - Goal types have NULL source_table/column (lean_mass, inactive placeholders) - Old goals reference inactive goal types - SQL queries fail for any reason Changes: - Guard clause checks table/column before SQL - try-catch wraps all aggregation queries - Returns None gracefully instead of crashing endpoint - Logs warnings for debugging Fixes: Goals page not loading due to /api/goals/list crash
This commit is contained in:
parent
1e758696fd
commit
1f4ee5021e
|
|
@ -245,82 +245,92 @@ def _fetch_by_aggregation_method(
|
||||||
- min_30d: Minimum value in last 30 days
|
- min_30d: Minimum value in last 30 days
|
||||||
- max_30d: Maximum value in last 30 days
|
- max_30d: Maximum value in last 30 days
|
||||||
"""
|
"""
|
||||||
|
# Guard: source_table/column required for simple aggregation
|
||||||
|
if not table or not column:
|
||||||
|
print(f"[WARNING] Missing source_table or source_column for aggregation")
|
||||||
|
return None
|
||||||
|
|
||||||
cur = get_cursor(conn)
|
cur = get_cursor(conn)
|
||||||
|
|
||||||
if method == 'latest':
|
try:
|
||||||
cur.execute(f"""
|
if method == 'latest':
|
||||||
SELECT {column} FROM {table}
|
cur.execute(f"""
|
||||||
WHERE profile_id = %s AND {column} IS NOT NULL
|
SELECT {column} FROM {table}
|
||||||
ORDER BY date DESC LIMIT 1
|
WHERE profile_id = %s AND {column} IS NOT NULL
|
||||||
""", (profile_id,))
|
ORDER BY date DESC LIMIT 1
|
||||||
row = cur.fetchone()
|
""", (profile_id,))
|
||||||
return float(row[column]) if row else None
|
row = cur.fetchone()
|
||||||
|
return float(row[column]) if row else None
|
||||||
|
|
||||||
elif method == 'avg_7d':
|
elif method == 'avg_7d':
|
||||||
days_ago = date.today() - timedelta(days=7)
|
days_ago = date.today() - timedelta(days=7)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT AVG({column}) as avg_value FROM {table}
|
SELECT AVG({column}) as avg_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['avg_value']) if row and row['avg_value'] is not None else None
|
return float(row['avg_value']) if row and row['avg_value'] is not None else None
|
||||||
|
|
||||||
elif method == 'avg_30d':
|
elif method == 'avg_30d':
|
||||||
days_ago = date.today() - timedelta(days=30)
|
days_ago = date.today() - timedelta(days=30)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT AVG({column}) as avg_value FROM {table}
|
SELECT AVG({column}) as avg_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['avg_value']) if row and row['avg_value'] is not None else None
|
return float(row['avg_value']) if row and row['avg_value'] is not None else None
|
||||||
|
|
||||||
elif method == 'sum_30d':
|
elif method == 'sum_30d':
|
||||||
days_ago = date.today() - timedelta(days=30)
|
days_ago = date.today() - timedelta(days=30)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT SUM({column}) as sum_value FROM {table}
|
SELECT SUM({column}) as sum_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['sum_value']) if row and row['sum_value'] is not None else None
|
return float(row['sum_value']) if row and row['sum_value'] is not None else None
|
||||||
|
|
||||||
elif method == 'count_7d':
|
elif method == 'count_7d':
|
||||||
days_ago = date.today() - timedelta(days=7)
|
days_ago = date.today() - timedelta(days=7)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT COUNT(*) as count_value FROM {table}
|
SELECT COUNT(*) as count_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s
|
WHERE profile_id = %s AND date >= %s
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['count_value']) if row else 0.0
|
return float(row['count_value']) if row else 0.0
|
||||||
|
|
||||||
elif method == 'count_30d':
|
elif method == 'count_30d':
|
||||||
days_ago = date.today() - timedelta(days=30)
|
days_ago = date.today() - timedelta(days=30)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT COUNT(*) as count_value FROM {table}
|
SELECT COUNT(*) as count_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s
|
WHERE profile_id = %s AND date >= %s
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['count_value']) if row else 0.0
|
return float(row['count_value']) if row else 0.0
|
||||||
|
|
||||||
elif method == 'min_30d':
|
elif method == 'min_30d':
|
||||||
days_ago = date.today() - timedelta(days=30)
|
days_ago = date.today() - timedelta(days=30)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT MIN({column}) as min_value FROM {table}
|
SELECT MIN({column}) as min_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['min_value']) if row and row['min_value'] is not None else None
|
return float(row['min_value']) if row and row['min_value'] is not None else None
|
||||||
|
|
||||||
elif method == 'max_30d':
|
elif method == 'max_30d':
|
||||||
days_ago = date.today() - timedelta(days=30)
|
days_ago = date.today() - timedelta(days=30)
|
||||||
cur.execute(f"""
|
cur.execute(f"""
|
||||||
SELECT MAX({column}) as max_value FROM {table}
|
SELECT MAX({column}) as max_value FROM {table}
|
||||||
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL
|
||||||
""", (profile_id, days_ago))
|
""", (profile_id, days_ago))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return float(row['max_value']) if row and row['max_value'] is not None else None
|
return float(row['max_value']) if row and row['max_value'] is not None else None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"[WARNING] Unknown aggregation method: {method}")
|
print(f"[WARNING] Unknown aggregation method: {method}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Failed to fetch value from {table}.{column} using {method}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user