Spaces:
Running
Running
add new get visualize endpoint
Browse files
main.py
CHANGED
|
@@ -686,6 +686,106 @@ async def not_found_handler(request: Request, exc):
|
|
| 686 |
async def internal_error_handler(request: Request, exc):
|
| 687 |
return ORJSONResponse(status_code=500, content={"error": "Internal server error: " + str(exc)})
|
| 688 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 689 |
@app.post("/analyze", response_class=ORJSONResponse)
|
| 690 |
async def analyze(input: TextInput):
|
| 691 |
start_time = time.time() # ⏱️ start
|
|
|
|
| 686 |
async def internal_error_handler(request: Request, exc):
|
| 687 |
return ORJSONResponse(status_code=500, content={"error": "Internal server error: " + str(exc)})
|
| 688 |
|
| 689 |
+
@app.get("/visualyse/{user_id}", response_class=ORJSONResponse)
|
| 690 |
+
async def visualyse_dashboard(user_id: str):
|
| 691 |
+
try:
|
| 692 |
+
conn = psycopg2.connect(DATABASE_URL)
|
| 693 |
+
cur = conn.cursor()
|
| 694 |
+
# Fetch all entries for the user
|
| 695 |
+
cur.execute("SELECT * FROM user_entries WHERE user_id = %s", (user_id,))
|
| 696 |
+
rows = cur.fetchall()
|
| 697 |
+
columns = [desc[0] for desc in cur.description]
|
| 698 |
+
entries = [dict(zip(columns, row)) for row in rows]
|
| 699 |
+
cur.close()
|
| 700 |
+
conn.close()
|
| 701 |
+
except Exception as e:
|
| 702 |
+
return ORJSONResponse(status_code=500, content={"error": str(e)})
|
| 703 |
+
|
| 704 |
+
# Section 1: Expense Overview
|
| 705 |
+
expenses = [e for e in entries if e["type"] == "expense"]
|
| 706 |
+
total_expense = sum(a["value"] for e in expenses for a in (e["amounts"] or []))
|
| 707 |
+
expense_count = len(expenses)
|
| 708 |
+
expense_by_category = {}
|
| 709 |
+
for e in expenses:
|
| 710 |
+
cat = e.get("expense_type", "miscellaneous")
|
| 711 |
+
amt = sum(a["value"] for a in (e["amounts"] or []))
|
| 712 |
+
expense_by_category[cat] = expense_by_category.get(cat, 0) + amt
|
| 713 |
+
|
| 714 |
+
# Monthly/Weekly Trends
|
| 715 |
+
monthly_trends = {}
|
| 716 |
+
for e in expenses:
|
| 717 |
+
key = f"{e['month']}-{e['year']}"
|
| 718 |
+
amt = sum(a["value"] for a in (e["amounts"] or []))
|
| 719 |
+
monthly_trends[key] = monthly_trends.get(key, 0) + amt
|
| 720 |
+
|
| 721 |
+
# Section 2: Top Stores & Categories
|
| 722 |
+
store_stats = {}
|
| 723 |
+
for e in expenses:
|
| 724 |
+
for s in (e["stores"] or []):
|
| 725 |
+
store = s.get("store", "unknown")
|
| 726 |
+
amt = sum(a["value"] for a in (e["amounts"] or []))
|
| 727 |
+
if store not in store_stats:
|
| 728 |
+
store_stats[store] = {"count": 0, "total": 0}
|
| 729 |
+
store_stats[store]["count"] += 1
|
| 730 |
+
store_stats[store]["total"] += amt
|
| 731 |
+
top_categories = sorted(expense_by_category.items(), key=lambda x: x[1], reverse=True)
|
| 732 |
+
|
| 733 |
+
# Section 3: Recent Expenses
|
| 734 |
+
recent_expenses = sorted(expenses, key=lambda e: e.get("created_at", ""), reverse=True)[:10]
|
| 735 |
+
|
| 736 |
+
# Section 4: Mood Trends
|
| 737 |
+
mood_dist = {}
|
| 738 |
+
for e in entries:
|
| 739 |
+
mood = e.get("mood", "neutral")
|
| 740 |
+
mood_dist[mood] = mood_dist.get(mood, 0) + 1
|
| 741 |
+
|
| 742 |
+
# Section 5: Tags & Keywords
|
| 743 |
+
tag_freq = {}
|
| 744 |
+
for e in entries:
|
| 745 |
+
for tag in (e["tags"] or []):
|
| 746 |
+
tag_freq[tag] = tag_freq.get(tag, 0) + 1
|
| 747 |
+
top_tags = sorted(tag_freq.items(), key=lambda x: x[1], reverse=True)[:10]
|
| 748 |
+
|
| 749 |
+
# Section 6: Time Analysis
|
| 750 |
+
day_stats = {}
|
| 751 |
+
hour_stats = {}
|
| 752 |
+
for e in expenses:
|
| 753 |
+
day = e.get("day_of_week", "unknown")
|
| 754 |
+
hour = e.get("hour_of_day", 0)
|
| 755 |
+
amt = sum(a["value"] for a in (e["amounts"] or []))
|
| 756 |
+
day_stats[day] = day_stats.get(day, 0) + amt
|
| 757 |
+
hour_stats[hour] = hour_stats.get(hour, 0) + amt
|
| 758 |
+
|
| 759 |
+
# Section 7: Meta Info
|
| 760 |
+
entry_count = len(entries)
|
| 761 |
+
type_dist = {}
|
| 762 |
+
for e in entries:
|
| 763 |
+
t = e.get("type", "other")
|
| 764 |
+
type_dist[t] = type_dist.get(t, 0) + 1
|
| 765 |
+
|
| 766 |
+
dashboard = {
|
| 767 |
+
"expense_overview": {
|
| 768 |
+
"total_expense": total_expense,
|
| 769 |
+
"expense_count": expense_count,
|
| 770 |
+
"expense_by_category": expense_by_category,
|
| 771 |
+
"monthly_trends": monthly_trends
|
| 772 |
+
},
|
| 773 |
+
"top_stores": store_stats,
|
| 774 |
+
"top_categories": top_categories,
|
| 775 |
+
"recent_expenses": recent_expenses,
|
| 776 |
+
"mood_distribution": mood_dist,
|
| 777 |
+
"top_tags": top_tags,
|
| 778 |
+
"time_analysis": {
|
| 779 |
+
"by_day": day_stats,
|
| 780 |
+
"by_hour": hour_stats
|
| 781 |
+
},
|
| 782 |
+
"meta_info": {
|
| 783 |
+
"entry_count": entry_count,
|
| 784 |
+
"type_distribution": type_dist
|
| 785 |
+
}
|
| 786 |
+
}
|
| 787 |
+
return ORJSONResponse(content=dashboard)
|
| 788 |
+
|
| 789 |
@app.post("/analyze", response_class=ORJSONResponse)
|
| 790 |
async def analyze(input: TextInput):
|
| 791 |
start_time = time.time() # ⏱️ start
|