Update main.py
Browse files
main.py
CHANGED
|
@@ -749,8 +749,8 @@ def admin_remove_member_from_org(org_id, member_uid):
|
|
| 749 |
@app.route('/api/admin/dashboard/stats', methods=['GET'])
|
| 750 |
def get_admin_dashboard_stats():
|
| 751 |
"""
|
| 752 |
-
Retrieves global statistics for the admin dashboard, including leaderboards
|
| 753 |
-
|
| 754 |
"""
|
| 755 |
try:
|
| 756 |
verify_admin_and_get_uid(request.headers.get('Authorization'))
|
|
@@ -759,11 +759,10 @@ def get_admin_dashboard_stats():
|
|
| 759 |
all_users_docs = list(db.collection('users').stream())
|
| 760 |
all_orgs_docs = list(db.collection('organizations').stream())
|
| 761 |
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
phone_to_user_map = {} # Helper to get user info from phone
|
| 767 |
|
| 768 |
# --- First Pass: Get user info and list of approved phones ---
|
| 769 |
pending_approvals, approved_users, admin_count = 0, 0, 0
|
|
@@ -787,7 +786,7 @@ def get_admin_dashboard_stats():
|
|
| 787 |
user_stats = {'total': len(all_users_docs), 'admins': admin_count, 'approvedForBot': approved_users, 'pendingApproval': pending_approvals}
|
| 788 |
org_stats = {'total': len(all_orgs_docs)}
|
| 789 |
|
| 790 |
-
# --- Second Pass: Aggregate financial data
|
| 791 |
total_sales_revenue, total_cogs, total_expenses, sales_count = 0, 0, 0, 0
|
| 792 |
|
| 793 |
for phone in approved_phone_numbers:
|
|
@@ -795,7 +794,6 @@ def get_admin_dashboard_stats():
|
|
| 795 |
bot_data_id = phone.lstrip('+')
|
| 796 |
bot_user_ref = db.collection('users').document(bot_data_id)
|
| 797 |
|
| 798 |
-
# Initialize user's entry for leaderboard tracking
|
| 799 |
user_sales_data[phone] = {'total_revenue': 0, 'item_sales': {}}
|
| 800 |
|
| 801 |
# Process Sales
|
|
@@ -808,12 +806,10 @@ def get_admin_dashboard_stats():
|
|
| 808 |
item_name = details.get('item', 'Unknown Item')
|
| 809 |
sale_revenue = price * quantity
|
| 810 |
|
| 811 |
-
# Aggregate for global system stats
|
| 812 |
total_sales_revenue += sale_revenue
|
| 813 |
total_cogs += cost * quantity
|
| 814 |
sales_count += 1
|
| 815 |
|
| 816 |
-
# Aggregate for leaderboards
|
| 817 |
user_sales_data[phone]['total_revenue'] += sale_revenue
|
| 818 |
user_sales_data[phone]['item_sales'][item_name] = user_sales_data[phone]['item_sales'].get(item_name, 0) + sale_revenue
|
| 819 |
global_item_revenue[item_name] = global_item_revenue.get(item_name, 0) + sale_revenue
|
|
@@ -823,7 +819,11 @@ def get_admin_dashboard_stats():
|
|
| 823 |
for expense_doc in expenses_docs:
|
| 824 |
details = expense_doc.to_dict().get('details', {})
|
| 825 |
amount = float(details.get('amount', 0))
|
| 826 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 827 |
|
| 828 |
total_expenses += amount
|
| 829 |
global_expense_totals[category] = global_expense_totals.get(category, 0) + amount
|
|
@@ -839,7 +839,6 @@ def get_admin_dashboard_stats():
|
|
| 839 |
top_users_by_revenue = []
|
| 840 |
for phone, data in sorted_users[:5]:
|
| 841 |
user_info = phone_to_user_map.get(phone, {})
|
| 842 |
-
# Find this user's top selling item
|
| 843 |
top_item = max(data['item_sales'], key=data['item_sales'].get) if data['item_sales'] else 'N/A'
|
| 844 |
top_users_by_revenue.append({
|
| 845 |
'displayName': user_info.get('displayName'),
|
|
@@ -852,7 +851,7 @@ def get_admin_dashboard_stats():
|
|
| 852 |
sorted_items = sorted(global_item_revenue.items(), key=lambda item: item[1], reverse=True)
|
| 853 |
top_selling_items = [{'item': name, 'totalRevenue': round(revenue, 2)} for name, revenue in sorted_items[:5]]
|
| 854 |
|
| 855 |
-
# Rank Top Expenses Globally
|
| 856 |
sorted_expenses = sorted(global_expense_totals.items(), key=lambda item: item[1], reverse=True)
|
| 857 |
top_expenses = [{'category': name, 'totalAmount': round(amount, 2)} for name, amount in sorted_expenses[:5]]
|
| 858 |
|
|
@@ -872,7 +871,7 @@ def get_admin_dashboard_stats():
|
|
| 872 |
'userStats': user_stats,
|
| 873 |
'organizationStats': org_stats,
|
| 874 |
'systemStats': system_stats,
|
| 875 |
-
'leaderboards': {
|
| 876 |
'topUsersByRevenue': top_users_by_revenue,
|
| 877 |
'topSellingItems': top_selling_items,
|
| 878 |
'topExpenses': top_expenses
|
|
|
|
| 749 |
@app.route('/api/admin/dashboard/stats', methods=['GET'])
|
| 750 |
def get_admin_dashboard_stats():
|
| 751 |
"""
|
| 752 |
+
Retrieves global statistics for the admin dashboard, including leaderboards.
|
| 753 |
+
**FIXED**: Correctly groups expenses by the 'description' field for the leaderboard.
|
| 754 |
"""
|
| 755 |
try:
|
| 756 |
verify_admin_and_get_uid(request.headers.get('Authorization'))
|
|
|
|
| 759 |
all_users_docs = list(db.collection('users').stream())
|
| 760 |
all_orgs_docs = list(db.collection('organizations').stream())
|
| 761 |
|
| 762 |
+
user_sales_data = {}
|
| 763 |
+
global_item_revenue = {}
|
| 764 |
+
global_expense_totals = {} # This will be populated correctly now
|
| 765 |
+
phone_to_user_map = {}
|
|
|
|
| 766 |
|
| 767 |
# --- First Pass: Get user info and list of approved phones ---
|
| 768 |
pending_approvals, approved_users, admin_count = 0, 0, 0
|
|
|
|
| 786 |
user_stats = {'total': len(all_users_docs), 'admins': admin_count, 'approvedForBot': approved_users, 'pendingApproval': pending_approvals}
|
| 787 |
org_stats = {'total': len(all_orgs_docs)}
|
| 788 |
|
| 789 |
+
# --- Second Pass: Aggregate financial data ---
|
| 790 |
total_sales_revenue, total_cogs, total_expenses, sales_count = 0, 0, 0, 0
|
| 791 |
|
| 792 |
for phone in approved_phone_numbers:
|
|
|
|
| 794 |
bot_data_id = phone.lstrip('+')
|
| 795 |
bot_user_ref = db.collection('users').document(bot_data_id)
|
| 796 |
|
|
|
|
| 797 |
user_sales_data[phone] = {'total_revenue': 0, 'item_sales': {}}
|
| 798 |
|
| 799 |
# Process Sales
|
|
|
|
| 806 |
item_name = details.get('item', 'Unknown Item')
|
| 807 |
sale_revenue = price * quantity
|
| 808 |
|
|
|
|
| 809 |
total_sales_revenue += sale_revenue
|
| 810 |
total_cogs += cost * quantity
|
| 811 |
sales_count += 1
|
| 812 |
|
|
|
|
| 813 |
user_sales_data[phone]['total_revenue'] += sale_revenue
|
| 814 |
user_sales_data[phone]['item_sales'][item_name] = user_sales_data[phone]['item_sales'].get(item_name, 0) + sale_revenue
|
| 815 |
global_item_revenue[item_name] = global_item_revenue.get(item_name, 0) + sale_revenue
|
|
|
|
| 819 |
for expense_doc in expenses_docs:
|
| 820 |
details = expense_doc.to_dict().get('details', {})
|
| 821 |
amount = float(details.get('amount', 0))
|
| 822 |
+
|
| 823 |
+
# --- THE FIX IS HERE ---
|
| 824 |
+
# Use the 'description' field for grouping, as seen in Firestore.
|
| 825 |
+
category = details.get('description', 'Uncategorized')
|
| 826 |
+
# --- END OF FIX ---
|
| 827 |
|
| 828 |
total_expenses += amount
|
| 829 |
global_expense_totals[category] = global_expense_totals.get(category, 0) + amount
|
|
|
|
| 839 |
top_users_by_revenue = []
|
| 840 |
for phone, data in sorted_users[:5]:
|
| 841 |
user_info = phone_to_user_map.get(phone, {})
|
|
|
|
| 842 |
top_item = max(data['item_sales'], key=data['item_sales'].get) if data['item_sales'] else 'N/A'
|
| 843 |
top_users_by_revenue.append({
|
| 844 |
'displayName': user_info.get('displayName'),
|
|
|
|
| 851 |
sorted_items = sorted(global_item_revenue.items(), key=lambda item: item[1], reverse=True)
|
| 852 |
top_selling_items = [{'item': name, 'totalRevenue': round(revenue, 2)} for name, revenue in sorted_items[:5]]
|
| 853 |
|
| 854 |
+
# Rank Top Expenses Globally (This will now be correct)
|
| 855 |
sorted_expenses = sorted(global_expense_totals.items(), key=lambda item: item[1], reverse=True)
|
| 856 |
top_expenses = [{'category': name, 'totalAmount': round(amount, 2)} for name, amount in sorted_expenses[:5]]
|
| 857 |
|
|
|
|
| 871 |
'userStats': user_stats,
|
| 872 |
'organizationStats': org_stats,
|
| 873 |
'systemStats': system_stats,
|
| 874 |
+
'leaderboards': {
|
| 875 |
'topUsersByRevenue': top_users_by_revenue,
|
| 876 |
'topSellingItems': top_selling_items,
|
| 877 |
'topExpenses': top_expenses
|