Commit
·
6a4c6a3
1
Parent(s):
21a5b7f
Fix UnboundLocalError: calculate monthly statistics from budgets when data variable doesn't exist
Browse files- app/smart_recommendation.py +84 -3
app/smart_recommendation.py
CHANGED
|
@@ -416,9 +416,90 @@ class SmartBudgetRecommender:
|
|
| 416 |
std_dev = avg_expense * 0.05 # Assume 5% typical variation for new budgets
|
| 417 |
months_analyzed = 1 # Only one month of data (the provided budget_amount)
|
| 418 |
else:
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
data = {
|
| 424 |
"average_monthly": avg_expense,
|
|
|
|
| 416 |
std_dev = avg_expense * 0.05 # Assume 5% typical variation for new budgets
|
| 417 |
months_analyzed = 1 # Only one month of data (the provided budget_amount)
|
| 418 |
else:
|
| 419 |
+
# We have historical budget data - calculate statistics from budgets
|
| 420 |
+
# Try to get monthly values from budgets by analyzing dates
|
| 421 |
+
monthly_values = []
|
| 422 |
+
try:
|
| 423 |
+
# Get budgets again to calculate monthly statistics
|
| 424 |
+
try:
|
| 425 |
+
category_objid = ObjectId(category_id) if len(category_id) == 24 and all(c in '0123456789abcdefABCDEF' for c in category_id) else category_id
|
| 426 |
+
except (ValueError, TypeError):
|
| 427 |
+
category_objid = category_id
|
| 428 |
+
|
| 429 |
+
user_query = {
|
| 430 |
+
"$or": [
|
| 431 |
+
{"createdBy": ObjectId(user_id) if len(user_id) == 24 and all(c in '0123456789abcdefABCDEF' for c in user_id) else user_id},
|
| 432 |
+
{"createdBy": user_id},
|
| 433 |
+
{"user_id": ObjectId(user_id) if len(user_id) == 24 and all(c in '0123456789abcdefABCDEF' for c in user_id) else user_id},
|
| 434 |
+
{"user_id": user_id}
|
| 435 |
+
]
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
category_query = {
|
| 439 |
+
"$or": [
|
| 440 |
+
{"category": category_objid},
|
| 441 |
+
{"categoryId": category_objid},
|
| 442 |
+
{"headCategory": category_objid},
|
| 443 |
+
{"headCategories.headCategory": category_objid},
|
| 444 |
+
{"headCategories.categories.category": category_objid}
|
| 445 |
+
]
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
budget_query = {"$and": [user_query, category_query]}
|
| 449 |
+
budgets = list(self.db.budgets.find(budget_query).sort("createdAt", -1).limit(12))
|
| 450 |
+
|
| 451 |
+
# Group by month and calculate monthly totals
|
| 452 |
+
monthly_totals = defaultdict(float)
|
| 453 |
+
monthly_counts = defaultdict(int)
|
| 454 |
+
|
| 455 |
+
for budget in budgets:
|
| 456 |
+
try:
|
| 457 |
+
# Get date from budget
|
| 458 |
+
budget_date = budget.get("createdAt") or budget.get("date") or budget.get("startDate")
|
| 459 |
+
if budget_date:
|
| 460 |
+
if isinstance(budget_date, str):
|
| 461 |
+
budget_date = datetime.fromisoformat(budget_date.replace('Z', '+00:00'))
|
| 462 |
+
elif not isinstance(budget_date, datetime):
|
| 463 |
+
continue
|
| 464 |
+
|
| 465 |
+
month_key = f"{budget_date.year}-{budget_date.month:02d}"
|
| 466 |
+
|
| 467 |
+
max_amount = float(budget.get("maxAmount", 0) or budget.get("max_amount", 0) or budget.get("amount", 0) or 0)
|
| 468 |
+
spend_amount = float(budget.get("spendAmount", 0) or budget.get("spend_amount", 0) or budget.get("spent", 0) or 0)
|
| 469 |
+
budget_amount_val = float(budget.get("budget", 0) or budget.get("budgetAmount", 0) or 0)
|
| 470 |
+
|
| 471 |
+
base_amount = spend_amount if spend_amount > 0 else (max_amount if max_amount > 0 else budget_amount_val)
|
| 472 |
+
if base_amount > 0:
|
| 473 |
+
monthly_totals[month_key] += base_amount
|
| 474 |
+
monthly_counts[month_key] += 1
|
| 475 |
+
except (ValueError, TypeError, AttributeError):
|
| 476 |
+
continue
|
| 477 |
+
|
| 478 |
+
# Convert to monthly values list
|
| 479 |
+
if monthly_totals:
|
| 480 |
+
# Sort by month key and calculate averages
|
| 481 |
+
sorted_months = sorted(monthly_totals.keys())
|
| 482 |
+
monthly_values = [monthly_totals[month] / monthly_counts[month] for month in sorted_months]
|
| 483 |
+
months_analyzed = len(monthly_values)
|
| 484 |
+
|
| 485 |
+
# Calculate std_dev
|
| 486 |
+
if len(monthly_values) > 1:
|
| 487 |
+
mean = sum(monthly_values) / len(monthly_values)
|
| 488 |
+
variance = sum((x - mean) ** 2 for x in monthly_values) / len(monthly_values)
|
| 489 |
+
std_dev = variance ** 0.5
|
| 490 |
+
else:
|
| 491 |
+
std_dev = avg_expense * 0.05 # Default 5% variation
|
| 492 |
+
else:
|
| 493 |
+
# Fallback: use avg_expense as single data point
|
| 494 |
+
monthly_values = [avg_expense]
|
| 495 |
+
std_dev = avg_expense * 0.05
|
| 496 |
+
months_analyzed = 1
|
| 497 |
+
except Exception as e:
|
| 498 |
+
print(f"Error calculating monthly statistics: {e}")
|
| 499 |
+
# Fallback: use avg_expense as single data point
|
| 500 |
+
monthly_values = [avg_expense]
|
| 501 |
+
std_dev = avg_expense * 0.05
|
| 502 |
+
months_analyzed = 1
|
| 503 |
|
| 504 |
data = {
|
| 505 |
"average_monthly": avg_expense,
|