Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| import pandas as pd | |
| from config.db import get_collection | |
| from utils.text_cleaner import clean_text | |
| from utils.search import rank_results | |
| st.set_page_config(page_title="Expense Search System", layout="wide") | |
| st.title("π Expense Search Dashboard") | |
| # --- 1οΈβ£ Input/Search Layer --- | |
| user_input = st.text_input("Search (Date / Company / Remark / Description / Amount)") | |
| if user_input: | |
| clean_input = clean_text(user_input) | |
| collection = get_collection() | |
| # --- Build query WITHOUT $text to avoid MongoDB error --- | |
| query = { | |
| "$or": [ | |
| {"date": {"$regex": clean_input, "$options": "i"}}, # Date search | |
| {"home company": {"$regex": clean_input, "$options": "i"}}, # Company search | |
| {"description": {"$regex": clean_input, "$options": "i"}}, # Description search | |
| {"remarks": {"$regex": clean_input, "$options": "i"}}, # Remarks search | |
| {"description_clean": {"$regex": clean_input, "$options": "i"}}, # Cleaned description | |
| ] | |
| } | |
| # Try to match amount if input is numeric | |
| try: | |
| amount_value = float(clean_input.replace(",", "")) | |
| query["$or"].append({"amount": amount_value}) | |
| except ValueError: | |
| pass # Not a number, skip amount matching | |
| # --- Fetch ALL matching records from MongoDB --- | |
| docs = list(collection.find( | |
| query, | |
| { | |
| "date": 1, | |
| "description": 1, | |
| "remarks": 1, | |
| "amount": 1, | |
| "home company": 1, | |
| "description_clean": 1 | |
| } | |
| )) # Removed limit to fetch ALL records | |
| if docs: | |
| # --- Rank using fuzzy search --- | |
| ranked = rank_results(clean_input, docs) | |
| df = pd.DataFrame(ranked) | |
| df = df[['date', 'description', 'remarks', 'home company', 'amount']] | |
| df.rename(columns={"home company": "Company"}, inplace=True) | |
| # --- 2οΈβ£ Quick Summary Metrics --- | |
| total_transactions = len(df) | |
| total_amount = df['amount'].sum() | |
| inward_total = df[df['remarks'].str.lower().str.contains("inward", na=False)]['amount'].sum() | |
| outward_total = df[df['remarks'].str.lower().str.contains("outward", na=False)]['amount'].sum() | |
| net_total = inward_total - outward_total | |
| st.subheader("π Quick Summary") | |
| c1, c2, c3, c4, c5 = st.columns(5) | |
| c1.metric("Transactions", total_transactions) | |
| c2.metric("Total Amount", f"{total_amount:,.2f}") | |
| c3.metric("Inward", f"{inward_total:,.2f}") | |
| c4.metric("Outward", f"{outward_total:,.2f}") | |
| c5.metric("Net Total", f"{net_total:,.2f}") | |
| # --- 3οΈβ£ Matched Transactions Table --- | |
| st.subheader("π§Ύ Matched Transactions") | |
| st.dataframe(df, use_container_width=True) | |
| # --- 4οΈβ£ Remarks Breakup --- | |
| st.subheader("π·οΈ Remarks Breakup") | |
| remarks_summary = df.groupby('remarks')['amount'].agg(['count', 'sum']).reset_index() | |
| remarks_summary.rename(columns={"count": "Transactions", "sum": "Total Amount", "remarks": "Remark"}, inplace=True) | |
| st.table(remarks_summary) | |
| # --- 5οΈβ£ Company Involvement --- | |
| st.subheader("π’ Company Summary") | |
| company_summary = df.groupby('Company')['amount'].agg(['count', 'sum']).reset_index() | |
| company_summary.rename(columns={"count": "Transactions", "sum": "Total Amount"}, inplace=True) | |
| st.table(company_summary) | |
| # --- 6οΈβ£ Date Summary (if multiple dates present) --- | |
| st.subheader("π Date-wise Summary") | |
| date_summary = df.groupby('date')['amount'].agg(['count', 'sum']).reset_index() | |
| date_summary.rename(columns={"count": "Transactions", "sum": "Total Amount", "date": "Date"}, inplace=True) | |
| st.table(date_summary) | |
| # --- 7οΈβ£ Optional Visuals --- | |
| st.subheader("π Visual Insights") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("**π₯§ Inward vs Outward Pie Chart**") | |
| pie_data = pd.DataFrame({ | |
| "Type": ["Inward", "Outward"], | |
| "Amount": [inward_total, outward_total] | |
| }) | |
| st.bar_chart(pie_data.set_index("Type")) | |
| with col2: | |
| st.markdown("**π Amount by Company**") | |
| st.bar_chart(company_summary.set_index("Company")["Total Amount"]) | |
| else: | |
| st.warning("No confident match found") |