diff --git a/backend/goal_utils.py b/backend/goal_utils.py index e4aee5e..1178005 100644 --- a/backend/goal_utils.py +++ b/backend/goal_utils.py @@ -245,82 +245,92 @@ def _fetch_by_aggregation_method( - min_30d: Minimum 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) - if method == 'latest': - cur.execute(f""" - SELECT {column} FROM {table} - WHERE profile_id = %s AND {column} IS NOT NULL - ORDER BY date DESC LIMIT 1 - """, (profile_id,)) - row = cur.fetchone() - return float(row[column]) if row else None + try: + if method == 'latest': + cur.execute(f""" + SELECT {column} FROM {table} + WHERE profile_id = %s AND {column} IS NOT NULL + ORDER BY date DESC LIMIT 1 + """, (profile_id,)) + row = cur.fetchone() + return float(row[column]) if row else None - elif method == 'avg_7d': - days_ago = date.today() - timedelta(days=7) - cur.execute(f""" - SELECT AVG({column}) as avg_value FROM {table} - WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['avg_value']) if row and row['avg_value'] is not None else None + elif method == 'avg_7d': + days_ago = date.today() - timedelta(days=7) + cur.execute(f""" + SELECT AVG({column}) as avg_value FROM {table} + WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['avg_value']) if row and row['avg_value'] is not None else None - elif method == 'avg_30d': - days_ago = date.today() - timedelta(days=30) - cur.execute(f""" - SELECT AVG({column}) as avg_value FROM {table} - WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['avg_value']) if row and row['avg_value'] is not None else None + elif method == 'avg_30d': + days_ago = date.today() - timedelta(days=30) + cur.execute(f""" + SELECT AVG({column}) as avg_value FROM {table} + WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['avg_value']) if row and row['avg_value'] is not None else None - elif method == 'sum_30d': - days_ago = date.today() - timedelta(days=30) - cur.execute(f""" - SELECT SUM({column}) as sum_value FROM {table} - WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['sum_value']) if row and row['sum_value'] is not None else None + elif method == 'sum_30d': + days_ago = date.today() - timedelta(days=30) + cur.execute(f""" + SELECT SUM({column}) as sum_value FROM {table} + WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['sum_value']) if row and row['sum_value'] is not None else None - elif method == 'count_7d': - days_ago = date.today() - timedelta(days=7) - cur.execute(f""" - SELECT COUNT(*) as count_value FROM {table} - WHERE profile_id = %s AND date >= %s - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['count_value']) if row else 0.0 + elif method == 'count_7d': + days_ago = date.today() - timedelta(days=7) + cur.execute(f""" + SELECT COUNT(*) as count_value FROM {table} + WHERE profile_id = %s AND date >= %s + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['count_value']) if row else 0.0 - elif method == 'count_30d': - days_ago = date.today() - timedelta(days=30) - cur.execute(f""" - SELECT COUNT(*) as count_value FROM {table} - WHERE profile_id = %s AND date >= %s - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['count_value']) if row else 0.0 + elif method == 'count_30d': + days_ago = date.today() - timedelta(days=30) + cur.execute(f""" + SELECT COUNT(*) as count_value FROM {table} + WHERE profile_id = %s AND date >= %s + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['count_value']) if row else 0.0 - elif method == 'min_30d': - days_ago = date.today() - timedelta(days=30) - cur.execute(f""" - SELECT MIN({column}) as min_value FROM {table} - WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['min_value']) if row and row['min_value'] is not None else None + elif method == 'min_30d': + days_ago = date.today() - timedelta(days=30) + cur.execute(f""" + SELECT MIN({column}) as min_value FROM {table} + WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['min_value']) if row and row['min_value'] is not None else None - elif method == 'max_30d': - days_ago = date.today() - timedelta(days=30) - cur.execute(f""" - SELECT MAX({column}) as max_value FROM {table} - WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL - """, (profile_id, days_ago)) - row = cur.fetchone() - return float(row['max_value']) if row and row['max_value'] is not None else None + elif method == 'max_30d': + days_ago = date.today() - timedelta(days=30) + cur.execute(f""" + SELECT MAX({column}) as max_value FROM {table} + WHERE profile_id = %s AND date >= %s AND {column} IS NOT NULL + """, (profile_id, days_ago)) + row = cur.fetchone() + return float(row['max_value']) if row and row['max_value'] is not None else None - else: - print(f"[WARNING] Unknown aggregation method: {method}") + else: + 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