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")