2001muhammadumair commited on
Commit
615e9ba
Β·
verified Β·
1 Parent(s): 0071fcf

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +160 -465
streamlit_app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  # budget_tracker_with_voice_ocr.py
2
  import streamlit as st
3
  import pandas as pd
@@ -21,17 +22,9 @@ from transformers import pipeline
21
  import torch
22
  import pytesseract
23
  import os
24
- from langchain_groq import ChatGroq
25
- from langchain.prompts import PromptTemplate
26
- from langchain_core.messages import HumanMessage, SystemMessage
27
- import time
28
- warnings.filterwarnings('ignore')
29
- import os
30
 
31
- # Force Streamlit to use local writable directory for config
32
- os.environ["STREAMLIT_HOME"] = os.getcwd()
33
- os.environ["XDG_CONFIG_HOME"] = os.getcwd()
34
 
 
35
 
36
  # Set Tesseract path (update this path according to your system)
37
  # For Windows: r"C:\Program Files\Tesseract-OCR\tesseract.exe"
@@ -39,45 +32,13 @@ os.environ["XDG_CONFIG_HOME"] = os.getcwd()
39
  # For Linux: "/usr/bin/tesseract"
40
  try:
41
  # You can set your Tesseract path here
42
- TESSERACT_PATH = os.getenv("TESSERACT_PATH", r'"/usr/bin/tesseract"') # Default for Linux
43
  pytesseract.pytesseract.tesseract_cmd = TESSERACT_PATH
44
  except:
45
  pass # Use default path
46
- import os
47
- import warnings
48
- import logging
49
-
50
- # Optional: Hide LangSmith warnings
51
- warnings.filterwarnings("ignore", category=UserWarning)
52
-
53
- # --- Logging Setup ---
54
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
55
-
56
- from dotenv import load_dotenv
57
- load_dotenv() # This loads the .env file into environment variables
58
-
59
- # --- API Keys ---
60
-
61
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
62
-
63
 
64
- os.environ["GROQ_API_KEY"] = GROQ_API_KEY
65
 
66
 
67
- # Initialize Groq LLM
68
- @st.cache_resource
69
- def get_llm():
70
- try:
71
- llm = ChatGroq(
72
- model="llama3-70b-8192", # Using a more stable model
73
- temperature=0.3,
74
- max_tokens=1000,
75
- )
76
- return llm
77
- except Exception as e:
78
- st.error(f"Error initializing LLM: {str(e)}")
79
- return None
80
-
81
  # Initialize session state for data persistence
82
  def initialize_session_state():
83
  """Initialize all session state variables"""
@@ -92,8 +53,6 @@ def initialize_session_state():
92
  st.session_state.notifications = []
93
  if 'whisper_model' not in st.session_state:
94
  st.session_state.whisper_model = None
95
- if 'financial_insights' not in st.session_state:
96
- st.session_state.financial_insights = []
97
  return True
98
  except Exception as e:
99
  st.error(f"Error initializing session state: {str(e)}")
@@ -121,6 +80,7 @@ def transcribe_audio_with_whisper(audio_file_path):
121
  model = load_whisper_model()
122
  if model is None:
123
  return None
 
124
  with st.spinner("Transcribing audio..."):
125
  output = model(
126
  audio_file_path,
@@ -153,9 +113,11 @@ def voice_expense_recording():
153
  """
154
  try:
155
  st.subheader("🎀 Voice Expense Recording")
 
156
  # Audio input options
157
  audio_option = st.radio("Choose audio input method:",
158
  ["Microphone (Real-time)", "Upload Audio File"])
 
159
  if audio_option == "Microphone (Real-time)":
160
  # Check if microphone is available
161
  try:
@@ -164,6 +126,7 @@ def voice_expense_recording():
164
  except:
165
  mic_available = False
166
  st.warning("Microphone not available. Please check your device settings.")
 
167
  if mic_available and st.button("πŸŽ™οΈ Start Voice Recording"):
168
  try:
169
  with sr.Microphone() as source:
@@ -171,22 +134,27 @@ def voice_expense_recording():
171
  # Adjust for ambient noise
172
  recognizer.adjust_for_ambient_noise(source, duration=1)
173
  audio = recognizer.listen(source, timeout=10)
 
174
  # Save audio to temporary file for processing
175
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_file:
176
  with open(tmp_file.name, "wb") as f:
177
  f.write(audio.get_wav_data())
178
  temp_filename = tmp_file.name
 
179
  # Try Whisper first, fallback to Google
180
  text = transcribe_audio_with_whisper(temp_filename)
181
  if text is None:
182
  text = transcribe_audio_with_google(temp_filename)
 
183
  # Clean up temporary file
184
  os.unlink(temp_filename)
 
185
  if text:
186
  st.success(f"βœ… Recognized: {text}")
187
  process_voice_text(text)
188
  else:
189
  st.error("❌ Failed to transcribe audio")
 
190
  except sr.WaitTimeoutError:
191
  st.error("⏰ Timeout: No speech detected within 10 seconds")
192
  except sr.UnknownValueError:
@@ -195,8 +163,10 @@ def voice_expense_recording():
195
  st.error(f"🌐 Could not request results: {e}")
196
  except Exception as e:
197
  st.error(f"❌ Error processing voice input: {str(e)}")
 
198
  else: # Upload Audio File
199
  uploaded_audio = st.file_uploader("Upload Audio File", type=['wav', 'mp3', 'm4a'])
 
200
  if uploaded_audio is not None:
201
  if st.button("πŸ”Š Process Audio File"):
202
  try:
@@ -204,23 +174,29 @@ def voice_expense_recording():
204
  with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_audio.name)[1]) as tmp_file:
205
  tmp_file.write(uploaded_audio.getvalue())
206
  temp_filename = tmp_file.name
 
207
  # Process audio file
208
  with st.spinner("Processing audio file..."):
209
  # Try Whisper first, fallback to Google
210
  text = transcribe_audio_with_whisper(temp_filename)
211
  if text is None:
212
  text = transcribe_audio_with_google(temp_filename)
 
213
  # Clean up temporary file
214
  os.unlink(temp_filename)
 
215
  if text:
216
  st.success(f"βœ… Transcribed: {text}")
217
  process_voice_text(text)
218
  else:
219
  st.error("❌ Failed to transcribe audio file")
 
220
  except Exception as e:
221
  st.error(f"❌ Error processing audio file: {str(e)}")
 
222
  # Instructions
223
  st.info("πŸ’‘ Tip: Say something like 'I spent 500 rupees on groceries at Big Bazaar'")
 
224
  except Exception as e:
225
  st.error(f"❌ Critical error in voice recording: {str(e)}")
226
 
@@ -233,44 +209,27 @@ def process_voice_text(text):
233
  category = "Other"
234
  description = text
235
 
236
- # Use LLM for intelligent categorization
237
- llm = get_llm()
238
- if llm:
239
- try:
240
- prompt = f"""
241
- Based on this expense description: "{text}"
242
- Please categorize it into one of these categories:
243
- Food, Transport, Shopping, Entertainment, Bills, Health, Education, Other
244
-
245
- Respond with only the category name.
246
- """
247
- response = llm.invoke([SystemMessage(content="You are a financial assistant that categorizes expenses."),
248
- HumanMessage(content=prompt)])
249
- llm_category = response.content.strip()
250
- if llm_category in ["Food", "Transport", "Shopping", "Entertainment", "Bills", "Health", "Education", "Other"]:
251
- category = llm_category
252
- except Exception as e:
253
- st.warning(f"LLM categorization failed, using rule-based: {str(e)}")
254
 
255
- # Fallback to rule-based categorization if LLM fails
256
- if category == "Other":
257
- categories = {
258
- 'Food': ['food', 'groceries', 'restaurant', 'cafe', 'meal', 'lunch', 'dinner', 'breakfast', 'dhaba', 'hotel'],
259
- 'Transport': ['transport', 'travel', 'taxi', 'uber', 'ola', 'bus', 'train', 'flight', 'fuel', 'petrol', 'diesel', 'auto'],
260
- 'Shopping': ['shopping', 'clothes', 'electronics', 'purchase', 'buy', 'mall', 'store', 'market'],
261
- 'Entertainment': ['entertainment', 'movie', 'cinema', 'game', 'fun', 'party', 'netflix', 'spotify'],
262
- 'Bills': ['bill', 'electricity', 'water', 'internet', 'phone', 'rent', 'insurance', 'subscription'],
263
- 'Health': ['medicine', 'doctor', 'hospital', 'pharmacy', 'health', 'medical'],
264
- 'Education': ['education', 'school', 'college', 'books', 'course', 'tuition', 'study']
265
- }
266
- text_lower = text.lower()
267
- for cat, keywords in categories.items():
268
- if any(keyword in text_lower for keyword in keywords):
269
- category = cat
270
- break
271
 
272
  # Extract numbers for amount using regex
273
  amount_pattern = r'(?:$|\$|rs|rupees?|dollars?)\s*(\d+(?:\.\d+)?)|(\d+(?:\.\d+)?)\s*(?:$|\$|rs|rupees?|dollars?)'
 
 
274
  matches = re.findall(amount_pattern, text_lower)
275
  if matches:
276
  for match in matches:
@@ -297,13 +256,11 @@ def process_voice_text(text):
297
  })
298
  st.session_state.expenses = pd.concat([st.session_state.expenses, new_expense], ignore_index=True)
299
  st.success(f"βœ… Expense logged: ${amount:.2f} for {category}")
 
300
 
301
  # Check budget alerts
302
  check_budget_alerts(amount, category)
303
 
304
- # Generate financial insight for this expense
305
- generate_financial_insight_for_expense(new_expense.iloc[0])
306
-
307
  except Exception as e:
308
  st.error(f"❌ Error processing voice text: {str(e)}")
309
 
@@ -333,6 +290,7 @@ class OCRExtractor:
333
  # Extract text with multiple languages support
334
  custom_config = r'--oem 3 --psm 6 -l eng'
335
  text = pytesseract.image_to_string(image, config=custom_config)
 
336
  return text.strip()
337
  except Exception as e:
338
  st.error(f"OCR Error: {e}")
@@ -380,108 +338,73 @@ def ocr_receipt_processing():
380
  """
381
  try:
382
  st.subheader("πŸ“Έ Receipt OCR Processing")
 
383
  uploaded_file = st.file_uploader("Upload Receipt Image", type=['jpg', 'jpeg', 'png'])
 
384
  if uploaded_file is not None:
385
  try:
386
  image = Image.open(uploaded_file)
387
  st.image(image, caption="πŸ“Έ Uploaded Receipt", use_container_width=True)
 
388
  if st.button("πŸ” Process Receipt"):
389
  # Initialize OCR extractor
390
  ocr_extractor = OCRExtractor()
 
391
  # Use Tesseract OCR
392
  try:
393
  # Preprocess image for better results
394
  processed_image = ocr_extractor.preprocess_image(image)
 
395
  # Extract text
396
  extracted_text = ocr_extractor.extract_text_from_image(processed_image)
 
397
  if extracted_text:
398
  st.text_area("πŸ“„ Extracted Text", extracted_text, height=200)
399
 
400
- # Use LLM to parse receipt data intelligently
401
- llm = get_llm()
402
  amount = 0
403
  category = "Other"
404
  description = "Receipt expense"
405
 
406
- if llm:
407
- try:
408
- # Extract amount using LLM
409
- amount_prompt = f"""
410
- From this receipt text, extract the total amount paid:
411
- {extracted_text}
412
-
413
- Respond with only the numeric amount (e.g., 549.50).
414
- If you cannot find it, respond with "0".
415
- """
416
- amount_response = llm.invoke([
417
- SystemMessage(content="You are a receipt parsing assistant that extracts payment amounts."),
418
- HumanMessage(content=amount_prompt)
419
- ])
420
- try:
421
- amount = float(amount_response.content.strip())
422
- except:
423
- amount = 0
424
-
425
- # Extract category using LLM
426
- category_prompt = f"""
427
- Based on this receipt content, categorize the expense into one of these categories:
428
- Food, Transport, Shopping, Entertainment, Bills, Health, Education, Other
429
-
430
- Receipt content: {extracted_text}
431
-
432
- Respond with only the category name.
433
- """
434
- category_response = llm.invoke([
435
- SystemMessage(content="You are a financial assistant that categorizes expenses from receipts."),
436
- HumanMessage(content=category_prompt)
437
- ])
438
- llm_category = category_response.content.strip()
439
- if llm_category in ["Food", "Transport", "Shopping", "Entertainment", "Bills", "Health", "Education", "Other"]:
440
- category = llm_category
441
-
442
- except Exception as e:
443
- st.warning(f"LLM receipt parsing failed: {str(e)}")
444
 
445
- # Fallback parsing if LLM fails
446
- if amount == 0:
447
- # Extract amount with multiple patterns
448
- amount_patterns = [
449
- r'[$$€£]\s*(\d+(?:\.\d+)?)',
450
- r'(\d+(?:\.\d+)?)\s*[$$€£]',
451
- r'(?:total|amount|paid|grand total).*?(\d+(?:\.\d+)?)',
452
- r'(?:bill|invoice).*?(\d+(?:\.\d+)?)'
453
- ]
454
- for pattern in amount_patterns:
455
- matches = re.findall(pattern, extracted_text.lower(), re.IGNORECASE)
456
- if matches:
457
- for match in matches:
458
- if isinstance(match, tuple):
459
- for group in match:
460
- if group and (group.replace('.', '').isdigit()):
461
- amount = float(group)
462
- break
463
- elif match.replace('.', '').isdigit():
464
- amount = float(match)
465
- break
466
- if amount > 0:
467
  break
 
 
468
 
469
  # Enhanced category detection
470
- if category == "Other":
471
- categories_keywords = {
472
- 'Food': ['restaurant', 'cafe', 'grocery', 'food', 'meal', 'supermarket', 'big bazaar', 'dmart', 'walmart'],
473
- 'Transport': ['taxi', 'uber', 'ola', 'fuel', 'petrol', 'bus', 'train', 'airport', 'parking'],
474
- 'Shopping': ['mall', 'store', 'shop', 'purchase', 'clothes', 'electronics', 'amazon', 'flipkart'],
475
- 'Entertainment': ['movie', 'cinema', 'game', 'entertainment', 'theatre', 'netflix'],
476
- 'Bills': ['electricity', 'water', 'internet', 'phone', 'rent', 'subscription', 'bill'],
477
- 'Health': ['pharmacy', 'medicine', 'doctor', 'hospital', 'medical', 'apollo', 'apollo'],
478
- 'Education': ['school', 'college', 'books', 'stationery', 'tution', 'course']
479
- }
480
- text_lower = extracted_text.lower()
481
- for cat, keywords in categories_keywords.items():
482
- if any(keyword in text_lower for keyword in keywords):
483
- category = cat
484
- break
485
 
486
  # Save to expenses with image data
487
  image_data = f"data:image/png;base64,{base64.b64encode(uploaded_file.getvalue()).decode()}"
@@ -497,29 +420,30 @@ def ocr_receipt_processing():
497
 
498
  # Check budget alerts
499
  check_budget_alerts(amount, category)
500
-
501
- # Generate financial insight for this expense
502
- generate_financial_insight_for_expense(new_expense.iloc[0])
503
-
504
  else:
505
  st.error("❌ Could not extract text from image. Please try a clearer image.")
 
506
  except Exception as e:
507
  st.error(f"❌ OCR processing failed: {str(e)}")
508
  st.info("πŸ’‘ Make sure Tesseract OCR is properly installed on your system")
 
509
  except Exception as e:
510
  st.error(f"❌ Error processing image: {str(e)}")
511
  else:
512
  st.info("πŸ“€ Please upload a receipt image (JPG, JPEG, PNG)")
 
513
  except Exception as e:
514
  st.error(f"❌ Critical error in OCR processing: {str(e)}")
515
 
516
  def create_budget():
517
  """
518
  Function to create and manage budgets
519
- LLM Needed: YES - For intelligent budget recommendations
 
520
  """
521
  try:
522
  st.subheader("πŸ’° Create Budget")
 
523
  col1, col2 = st.columns(2)
524
  with col1:
525
  predefined_categories = ["Food", "Transport", "Shopping", "Entertainment", "Bills", "Health", "Education", "Other"]
@@ -528,45 +452,11 @@ def create_budget():
528
  category = st.selectbox("Category", predefined_categories)
529
  else:
530
  category = st.text_input("Enter custom category")
 
531
  with col2:
532
  budget_amount = st.number_input("Budget Amount ($)", min_value=0.0, step=100.0, value=1000.0)
533
  period = st.selectbox("Period", ["Monthly", "Weekly", "Custom"])
534
 
535
- # AI-powered budget recommendations
536
- if st.button("πŸ€– Get AI Budget Recommendation"):
537
- llm = get_llm()
538
- if llm and not st.session_state.expenses.empty:
539
- try:
540
- # Prepare spending history for LLM
541
- spending_summary = st.session_state.expenses.groupby('category')['amount'].sum().to_dict()
542
- spending_text = "\n".join([f"{cat}: ${amt:.2f}" for cat, amt in spending_summary.items()])
543
-
544
- prompt = f"""
545
- Based on this user's spending history:
546
- {spending_text}
547
-
548
- Suggest an optimal budget allocation for the category '{category}'.
549
- Consider typical spending patterns and financial best practices.
550
- Respond with a recommended budget amount in rupees.
551
- """
552
-
553
- response = llm.invoke([
554
- SystemMessage(content="You are a financial advisor that recommends optimal budget allocations."),
555
- HumanMessage(content=prompt)
556
- ])
557
-
558
- try:
559
- # Extract number from response
560
- recommendation = re.findall(r'\d+(?:\.\d+)?', response.content)
561
- if recommendation:
562
- recommended_amount = float(recommendation[0])
563
- st.info(f"πŸ€– AI Recommendation: ${recommended_amount:.2f} for {category}")
564
- except:
565
- st.info("πŸ€– AI Recommendation: " + response.content)
566
-
567
- except Exception as e:
568
- st.error(f"Error getting AI recommendation: {str(e)}")
569
-
570
  if st.button("πŸ“Š Set Budget"):
571
  if category and budget_amount > 0:
572
  try:
@@ -574,6 +464,7 @@ def create_budget():
574
  existing_budget = st.session_state.budgets[
575
  st.session_state.budgets['category'] == category
576
  ]
 
577
  if not existing_budget.empty:
578
  # Update existing budget
579
  st.session_state.budgets.loc[
@@ -613,16 +504,19 @@ def create_budget():
613
  st.success(f"βœ… Budget for {budget_to_delete} deleted")
614
  else:
615
  st.info("πŸ“ No budgets set yet. Create your first budget!")
 
616
  except Exception as e:
617
  st.error(f"❌ Critical error in budget creation: {str(e)}")
618
 
619
  def set_savings_goals():
620
  """
621
  Function to set and track savings goals
622
- LLM Needed: YES - For personalized savings recommendations
 
623
  """
624
  try:
625
  st.subheader("🎯 Savings Goals")
 
626
  col1, col2, col3 = st.columns(3)
627
  with col1:
628
  goal_name = st.text_input("Goal Name", placeholder="e.g., Vacation, Emergency Fund")
@@ -632,40 +526,6 @@ def set_savings_goals():
632
  target_date = st.date_input("Target Date",
633
  value=datetime.now() + timedelta(days=30))
634
 
635
- # AI-powered savings goal recommendations
636
- if st.button("πŸ€– Get AI Savings Recommendation"):
637
- llm = get_llm()
638
- if llm:
639
- try:
640
- # Get user's financial situation
641
- total_income = st.session_state.budgets['budget_amount'].sum() if not st.session_state.budgets.empty else 0
642
- total_spending = st.session_state.expenses['amount'].sum() if not st.session_state.expenses.empty else 0
643
- current_savings = st.session_state.savings_goals['current_amount'].sum() if not st.session_state.savings_goals.empty else 0
644
-
645
- prompt = f"""
646
- Based on this financial situation:
647
- - Monthly income (budgeted): ${total_income:.2f}
648
- - Monthly spending: ${total_spending:.2f}
649
- - Current savings: ${current_savings:.2f}
650
-
651
- For a savings goal named '{goal_name}', suggest:
652
- 1. A realistic target amount
653
- 2. A reasonable timeline
654
- 3. Weekly/monthly savings recommendations
655
-
656
- Keep response concise and actionable.
657
- """
658
-
659
- response = llm.invoke([
660
- SystemMessage(content="You are a financial advisor that recommends savings strategies."),
661
- HumanMessage(content=prompt)
662
- ])
663
-
664
- st.info("πŸ€– AI Savings Recommendation:\n" + response.content)
665
-
666
- except Exception as e:
667
- st.error(f"Error getting AI recommendation: {str(e)}")
668
-
669
  if st.button("🎯 Set Goal"):
670
  if goal_name and target_amount > 0:
671
  try:
@@ -686,10 +546,12 @@ def set_savings_goals():
686
  # Display existing goals
687
  if not st.session_state.savings_goals.empty:
688
  st.subheader("πŸ† Current Goals")
 
689
  for idx, goal in st.session_state.savings_goals.iterrows():
690
  try:
691
  progress = (goal['current_amount'] / goal['target_amount']) * 100 if goal['target_amount'] > 0 else 0
692
  days_left = (goal['target_date'] - datetime.now().date()).days
 
693
  st.write(f"**{goal['goal_name']}**")
694
  st.progress(min(progress/100, 1.0))
695
  st.write(f"πŸ’° ${goal['current_amount']:.2f} / ${goal['target_amount']:.2f} ({progress:.1f}%)")
@@ -703,21 +565,25 @@ def set_savings_goals():
703
  st.session_state.savings_goals.at[idx, 'current_amount'] += add_amount
704
  st.success(f"βœ… Added ${add_amount:.2f} to {goal['goal_name']}")
705
  st.rerun()
 
706
  st.write("---")
707
  except Exception as e:
708
  st.error(f"❌ Error displaying goal: {str(e)}")
709
  else:
710
  st.info("πŸ“ No savings goals set yet. Create your first goal!")
 
711
  except Exception as e:
712
  st.error(f"❌ Critical error in savings goals: {str(e)}")
713
 
714
  def spending_categorization():
715
  """
716
  Function to categorize and review spending
717
- LLM Needed: YES - For intelligent expense analysis
 
718
  """
719
  try:
720
  st.subheader("🏷️ Spending Categorization")
 
721
  if not st.session_state.expenses.empty:
722
  # Display expenses that need categorization
723
  uncategorized = st.session_state.expenses[st.session_state.expenses['category'] == 'Other']
@@ -782,44 +648,14 @@ def spending_categorization():
782
  total_spent = filtered_expenses['amount'].sum()
783
  avg_spent = filtered_expenses['amount'].mean()
784
  st.metric("Total Spent", f"${total_spent:.2f}")
 
785
  st.metric("Average Expense", f"${avg_spent:.2f}")
786
-
787
- # AI-powered spending analysis
788
- if st.button("πŸ€– Analyze Spending Patterns"):
789
- llm = get_llm()
790
- if llm:
791
- try:
792
- # Prepare spending data for analysis
793
- category_spending = filtered_expenses.groupby('category')['amount'].sum().to_dict()
794
- spending_text = "\n".join([f"{cat}: ${amt:.2f}" for cat, amt in category_spending.items()])
795
-
796
- prompt = f"""
797
- Analyze this spending pattern and provide insights:
798
- {spending_text}
799
-
800
- Please provide:
801
- 1. Which categories are the highest spending?
802
- 2. Any concerning spending patterns?
803
- 3. Recommendations to optimize spending
804
- 4. Potential savings opportunities
805
-
806
- Keep response concise and actionable.
807
- """
808
-
809
- with st.spinner("Analyzing spending patterns..."):
810
- response = llm.invoke([
811
- SystemMessage(content="You are a financial analyst that provides spending insights."),
812
- HumanMessage(content=prompt)
813
- ])
814
-
815
- st.info("πŸ€– Spending Analysis:\n" + response.content)
816
-
817
- except Exception as e:
818
- st.error(f"Error analyzing spending: {str(e)}")
819
  else:
820
  st.info("πŸ” No expenses match the current filters")
821
  else:
822
  st.info("πŸ“ No expenses recorded yet. Start by adding expenses through voice or receipt scanning!")
 
823
  except Exception as e:
824
  st.error(f"❌ Critical error in spending categorization: {str(e)}")
825
 
@@ -838,45 +674,24 @@ def check_budget_alerts(amount, category):
838
 
839
  if current_spending > budget_amount:
840
  alert_msg = f"🚨 OVERSPENT: {category} - ${current_spending:.2f}/${budget_amount:.2f}"
 
 
841
  if alert_msg not in st.session_state.notifications:
842
  st.session_state.notifications.append(alert_msg)
843
  elif current_spending > budget_amount * 0.8: # 80% threshold
844
  alert_msg = f"⚠️ WARNING: {category} - ${current_spending:.2f}/${budget_amount:.2f} ({((current_spending/budget_amount)*100):.1f}%)"
 
 
845
  if alert_msg not in st.session_state.notifications:
846
  st.session_state.notifications.append(alert_msg)
847
  except Exception as e:
848
  st.error(f"❌ Error checking budget alerts: {str(e)}")
849
 
850
- def generate_financial_insight_for_expense(expense):
851
- """Generate AI-powered insight for a new expense"""
852
- try:
853
- llm = get_llm()
854
- if llm:
855
- prompt = f"""
856
- For this expense:
857
- - Amount: ${expense['amount']:.2f}
858
- - Category: {expense['category']}
859
- - Description: {expense['description']}
860
-
861
- Provide a brief, helpful insight or tip related to this type of spending.
862
- Keep it under 100 words and make it actionable.
863
- """
864
-
865
- response = llm.invoke([
866
- SystemMessage(content="You are a financial advisor providing personalized spending insights."),
867
- HumanMessage(content=prompt)
868
- ])
869
-
870
- insight = f"πŸ’‘ Insight for {expense['category']}: {response.content}"
871
- st.session_state.financial_insights.append(insight)
872
-
873
- except Exception as e:
874
- pass # Silently fail if insight generation fails
875
-
876
  def alerts_and_notifications():
877
  """
878
  Function to check and display budget alerts
879
- LLM Needed: YES - For personalized alert messages
 
880
  """
881
  try:
882
  st.subheader("πŸ”” Budget Alerts & Notifications")
@@ -892,16 +707,22 @@ def alerts_and_notifications():
892
  try:
893
  # Calculate spending by category
894
  spending_by_category = st.session_state.expenses.groupby('category')['amount'].sum().reset_index()
 
895
  alerts = []
896
  for _, budget in st.session_state.budgets.iterrows():
897
  category_spending = spending_by_category[spending_by_category['category'] == budget['category']]
898
  if not category_spending.empty:
899
  spent = category_spending.iloc[0]['amount']
900
  budget_amount = budget['budget_amount']
 
901
  if spent > budget_amount:
902
  alerts.append(f"🚨 OVERSPENT: {budget['category']} - ${spent:.2f}/${budget_amount:.2f} ({((spent/budget_amount)*100):.1f}%)")
 
 
903
  elif spent > budget_amount * 0.8: # 80% threshold
904
  alerts.append(f"⚠️ WARNING: {budget['category']} - ${spent:.2f}/${budget_amount:.2f} ({((spent/budget_amount)*100):.1f}%)")
 
 
905
 
906
  # Display alerts
907
  if alerts:
@@ -911,6 +732,7 @@ def alerts_and_notifications():
911
  st.session_state.notifications.append(alert)
912
  else:
913
  st.success("βœ… All budgets are within limits!")
 
914
  except Exception as e:
915
  st.error(f"❌ Error calculating alerts: {str(e)}")
916
  else:
@@ -924,26 +746,25 @@ def alerts_and_notifications():
924
  else:
925
  st.info("πŸ“­ No notifications yet")
926
 
927
- # Display AI-generated financial insights
928
- if st.session_state.financial_insights:
929
- st.subheader("πŸ’‘ Financial Insights")
930
- for insight in reversed(st.session_state.financial_insights[-5:]): # Show last 5
931
- st.info(insight)
932
  except Exception as e:
933
  st.error(f"❌ Critical error in alerts system: {str(e)}")
934
 
935
  def visualizations_and_summaries():
936
  """
937
  Function to create charts and summaries
938
- LLM Needed: YES - For intelligent financial summaries
 
939
  """
940
  try:
941
  st.subheader("πŸ“Š Financial Visualizations")
 
942
  if not st.session_state.expenses.empty:
943
  try:
944
  # Spending by category pie chart
945
  spending_by_category = st.session_state.expenses.groupby('category')['amount'].sum()
 
946
  col1, col2 = st.columns(2)
 
947
  with col1:
948
  st.write("πŸ’° Spending by Category")
949
  if len(spending_by_category) > 0:
@@ -952,6 +773,7 @@ def visualizations_and_summaries():
952
  st.plotly_chart(fig1, use_container_width=True)
953
  else:
954
  st.info("No spending data to visualize")
 
955
  with col2:
956
  st.write("πŸ“ˆ Spending Trend")
957
  daily_spending = st.session_state.expenses.groupby('date')['amount'].sum().reset_index()
@@ -969,17 +791,21 @@ def visualizations_and_summaries():
969
  monthly_expenses = st.session_state.expenses[
970
  st.session_state.expenses['date'].str.startswith(current_month)
971
  ]
 
972
  if not monthly_expenses.empty:
973
  total_spent = monthly_expenses['amount'].sum()
974
  st.metric("Total Monthly Spending", f"${total_spent:.2f}")
 
 
975
  category_summary = monthly_expenses.groupby('category')['amount'].sum().reset_index()
976
  fig3 = px.bar(category_summary, x='category', y='amount',
977
  title='Monthly Spending by Category')
978
  st.plotly_chart(fig3, use_container_width=True)
 
979
  st.dataframe(category_summary)
980
  else:
981
  st.info("No expenses recorded this month.")
982
-
983
  # Budget vs Actual comparison
984
  if not st.session_state.budgets.empty:
985
  st.subheader("βš–οΈ Budget vs Actual Comparison")
@@ -994,76 +820,35 @@ def visualizations_and_summaries():
994
  'Actual': actual_spent,
995
  'Difference': budget['budget_amount'] - actual_spent
996
  })
 
997
  if budget_comparison:
998
  comparison_df = pd.DataFrame(budget_comparison)
999
  st.dataframe(comparison_df)
 
1000
  # Visualization
1001
  fig4 = go.Figure()
1002
  fig4.add_trace(go.Bar(name='Budget', x=comparison_df['Category'], y=comparison_df['Budget']))
1003
  fig4.add_trace(go.Bar(name='Actual', x=comparison_df['Category'], y=comparison_df['Actual']))
1004
  fig4.update_layout(title="Budget vs Actual Spending", barmode='group')
1005
  st.plotly_chart(fig4, use_container_width=True)
1006
-
1007
- # AI-powered financial summary
1008
- if st.button("πŸ€– Generate Financial Summary"):
1009
- llm = get_llm()
1010
- if llm:
1011
- try:
1012
- # Prepare data for summary
1013
- total_expenses = st.session_state.expenses['amount'].sum()
1014
- category_spending = st.session_state.expenses.groupby('category')['amount'].sum().to_dict()
1015
- spending_text = "\n".join([f"{cat}: ${amt:.2f}" for cat, amt in category_spending.items()])
1016
-
1017
- # Get budget data
1018
- total_budget = st.session_state.budgets['budget_amount'].sum()
1019
- budget_text = "\n".join([f"{row['category']}: ${row['budget_amount']:.2f}"
1020
- for _, row in st.session_state.budgets.iterrows()])
1021
-
1022
- prompt = f"""
1023
- Based on this financial data:
1024
-
1025
- Total Expenses: ${total_expenses:.2f}
1026
-
1027
- Spending by Category:
1028
- {spending_text}
1029
-
1030
- Budget Allocations:
1031
- {budget_text if budget_text else "No budgets set"}
1032
-
1033
- Please provide:
1034
- 1. Overall financial health assessment
1035
- 2. Top 3 spending categories and insights
1036
- 3. Budget adherence analysis (if budgets exist)
1037
- 4. Personalized recommendations for improvement
1038
-
1039
- Keep response concise, structured, and actionable.
1040
- """
1041
-
1042
- with st.spinner("Generating financial summary..."):
1043
- response = llm.invoke([
1044
- SystemMessage(content="You are a financial advisor providing comprehensive financial summaries."),
1045
- HumanMessage(content=prompt)
1046
- ])
1047
-
1048
- st.info("πŸ€– Financial Summary:\n" + response.content)
1049
-
1050
- except Exception as e:
1051
- st.error(f"Error generating summary: {str(e)}")
1052
-
1053
  except Exception as e:
1054
  st.error(f"❌ Error creating visualizations: {str(e)}")
1055
  else:
1056
  st.info("πŸ“ No data to visualize yet. Start by recording expenses!")
 
1057
  except Exception as e:
1058
  st.error(f"❌ Critical error in visualizations: {str(e)}")
1059
 
1060
  def receipt_management():
1061
  """
1062
  Function to manage and view stored receipts
1063
- LLM Needed: YES - For receipt analysis and insights
 
1064
  """
1065
  try:
1066
  st.subheader("🧾 Receipt Management")
 
1067
  if not st.session_state.expenses.empty:
1068
  receipts = st.session_state.expenses[st.session_state.expenses['receipt_image'] != '']
1069
  if not receipts.empty:
@@ -1098,40 +883,12 @@ def receipt_management():
1098
  with cols[idx % 3]:
1099
  st.write(f"**πŸ“… {receipt['date']}**")
1100
  st.write(f"πŸ’° ${receipt['amount']:.2f}")
 
1101
  st.write(f"🏷️ {receipt['category']}")
1102
  if receipt['receipt_image'].startswith('data:image'):
1103
  # Display base64 image
1104
  st.image(receipt['receipt_image'], width=200)
1105
  st.write(f"πŸ“ {receipt['description'][:50]}...")
1106
-
1107
- # AI-powered receipt analysis
1108
- if st.button("πŸ€– Analyze Receipt", key=f"analyze_{idx}"):
1109
- llm = get_llm()
1110
- if llm:
1111
- try:
1112
- prompt = f"""
1113
- Analyze this receipt expense:
1114
- - Amount: ${receipt['amount']:.2f}
1115
- - Category: {receipt['category']}
1116
- - Date: {receipt['date']}
1117
-
1118
- Provide insights on:
1119
- 1. Whether this is a typical expense for this category
1120
- 2. Spending patterns related to this type of purchase
1121
- 3. Tips to optimize similar expenses
1122
-
1123
- Keep response concise and actionable.
1124
- """
1125
-
1126
- response = llm.invoke([
1127
- SystemMessage(content="You are a financial advisor analyzing individual receipts."),
1128
- HumanMessage(content=prompt)
1129
- ])
1130
-
1131
- st.info("πŸ€– Receipt Analysis:\n" + response.content)
1132
-
1133
- except Exception as e:
1134
- st.error(f"Error analyzing receipt: {str(e)}")
1135
  st.write("---")
1136
  except Exception as e:
1137
  st.error(f"❌ Error displaying receipt: {str(e)}")
@@ -1141,6 +898,7 @@ def receipt_management():
1141
  st.info("πŸ“ No receipts uploaded yet. Upload receipts through the OCR feature!")
1142
  else:
1143
  st.info("πŸ“ No expenses recorded yet. Start by recording expenses!")
 
1144
  except Exception as e:
1145
  st.error(f"❌ Critical error in receipt management: {str(e)}")
1146
 
@@ -1151,13 +909,16 @@ def data_security_and_privacy():
1151
  """
1152
  try:
1153
  st.subheader("πŸ”’ Data Security & Privacy")
 
1154
  st.write("πŸ›‘οΈ Your financial data is stored locally and never shared with third parties.")
1155
  st.write("πŸ” All data is encrypted and protected according to privacy regulations.")
1156
 
1157
  # Security settings
1158
  st.subheader("βš™οΈ Security Settings")
 
1159
  if st.checkbox("Enable Data Encryption", value=True):
1160
  st.success("βœ… Data encryption is enabled!")
 
1161
  if st.checkbox("Enable Automatic Backups"):
1162
  backup_frequency = st.selectbox("Backup Frequency", ["Daily", "Weekly", "Monthly"])
1163
  st.info(f"πŸ“… Automatic backups will run {backup_frequency.lower()}")
@@ -1171,6 +932,7 @@ def data_security_and_privacy():
1171
  'budgets': st.session_state.budgets.to_dict('records') if not st.session_state.budgets.empty else [],
1172
  'savings_goals': st.session_state.savings_goals.to_dict('records') if not st.session_state.savings_goals.empty else []
1173
  }
 
1174
  json_str = json.dumps(export_data, indent=2, default=str)
1175
  st.download_button(
1176
  label="πŸ“₯ Download Data as JSON",
@@ -1178,6 +940,7 @@ def data_security_and_privacy():
1178
  file_name=f"budget_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
1179
  mime="application/json"
1180
  )
 
1181
  # Also provide CSV export
1182
  if not st.session_state.expenses.empty:
1183
  st.download_button(
@@ -1197,73 +960,55 @@ def data_security_and_privacy():
1197
  - We collect only the financial data you enter
1198
  - No personal identification information is collected
1199
  - All data is stored locally on your device
 
1200
  **Data Usage:**
1201
  - Your data is used only for the functionality of this application
1202
  - We do not share your data with any third parties
1203
  - Data is not transmitted over the internet
 
1204
  **Data Security:**
1205
  - All data is encrypted at rest
1206
  - You have full control over your data
1207
  - You can export or delete your data at any time
1208
  """)
 
1209
  except Exception as e:
1210
  st.error(f"❌ Critical error in security section: {str(e)}")
1211
 
1212
  def bank_integration_placeholder():
1213
  """
1214
  Placeholder for bank integration feature
1215
- LLM Needed: YES - For natural language banking queries
 
1216
  """
1217
  try:
1218
  st.subheader("🏦 Bank Integration (Coming Soon)")
 
1219
  st.info("πŸš€ This feature will allow automatic syncing with your bank accounts!")
 
1220
  st.write("πŸ“‹ Planned Features:")
1221
  st.write("β€’ Automatic transaction import")
1222
  st.write("β€’ Real-time balance updates")
1223
  st.write("β€’ Bank statement analysis")
1224
  st.write("β€’ Automatic expense categorization")
1225
 
1226
- # AI-powered banking assistant
1227
- st.subheader("πŸ€– AI Banking Assistant (Preview)")
1228
- user_query = st.text_input("Ask about banking features:")
1229
- if user_query and st.button("Get Answer"):
1230
- llm = get_llm()
1231
- if llm:
1232
- try:
1233
- prompt = f"""
1234
- User wants to know about: {user_query}
1235
-
1236
- This is a budget tracking application that will integrate with banks.
1237
- Explain how this feature would work and its benefits.
1238
- Keep response helpful and informative.
1239
- """
1240
-
1241
- response = llm.invoke([
1242
- SystemMessage(content="You are a banking technology expert explaining features."),
1243
- HumanMessage(content=prompt)
1244
- ])
1245
-
1246
- st.info(response.content)
1247
-
1248
- except Exception as e:
1249
- st.error(f"Error processing query: {str(e)}")
1250
-
1251
  bank_name = st.selectbox("Select Bank", ["HDFC", "ICICI", "SBI", "Axis", "Kotak", "Other"])
1252
  if bank_name:
1253
  st.info(f"Bank integration for {bank_name} will be available soon!")
 
1254
  except Exception as e:
1255
  st.error(f"❌ Error in bank integration section: {str(e)}")
1256
 
1257
  def main_dashboard():
1258
  """
1259
  Main dashboard overview
1260
- LLM Needed: YES - For financial health score and insights
1261
  """
1262
  try:
1263
  st.subheader("🏠 Dashboard Overview")
1264
 
1265
  # Key metrics
1266
  col1, col2, col3, col4 = st.columns(4)
 
1267
  total_expenses = st.session_state.expenses['amount'].sum() if not st.session_state.expenses.empty else 0
1268
  total_budget = st.session_state.budgets['budget_amount'].sum() if not st.session_state.budgets.empty else 0
1269
  total_savings = st.session_state.savings_goals['current_amount'].sum() if not st.session_state.savings_goals.empty else 0
@@ -1271,10 +1016,13 @@ def main_dashboard():
1271
 
1272
  with col1:
1273
  st.metric("πŸ’° Total Expenses", f"${total_expenses:.2f}")
 
1274
  with col2:
1275
  st.metric("πŸ“Š Total Budget", f"${total_budget:.2f}")
 
1276
  with col3:
1277
  st.metric("πŸ† Total Savings", f"${total_savings:.2f}")
 
1278
  with col4:
1279
  st.metric("🧾 Expense Count", f"{expense_count}")
1280
 
@@ -1309,10 +1057,13 @@ def main_dashboard():
1309
  category_spending = st.session_state.expenses[
1310
  st.session_state.expenses['category'] == budget['category']
1311
  ]['amount'].sum()
 
1312
  progress = (category_spending / budget['budget_amount']) * 100 if budget['budget_amount'] > 0 else 0
1313
  st.write(f"**{budget['category']}**")
1314
  st.progress(min(progress/100, 1.0))
1315
  st.write(f"${category_spending:.2f} / ${budget['budget_amount']:.2f} ({progress:.1f}%)")
 
 
1316
 
1317
  # Savings goals progress
1318
  if not st.session_state.savings_goals.empty:
@@ -1322,54 +1073,9 @@ def main_dashboard():
1322
  st.write(f"**{goal['goal_name']}**")
1323
  st.progress(min(progress/100, 1.0))
1324
  st.write(f"${goal['current_amount']:.2f} / ${goal['target_amount']:.2f} ({progress:.1f}%)")
 
 
1325
 
1326
- # AI-powered financial health score
1327
- if st.button("πŸ€– Calculate Financial Health Score"):
1328
- llm = get_llm()
1329
- if llm and not st.session_state.expenses.empty:
1330
- try:
1331
- # Prepare financial data
1332
- total_income = total_budget
1333
- total_spent = total_expenses
1334
- savings_rate = (total_savings / total_income * 100) if total_income > 0 else 0
1335
-
1336
- # Category diversity (more categories = better)
1337
- category_count = len(st.session_state.expenses['category'].unique())
1338
-
1339
- prompt = f"""
1340
- Calculate a financial health score (0-100) based on:
1341
- - Total income/budget: ${total_income:.2f}
1342
- - Total spending: ${total_spent:.2f}
1343
- - Savings amount: ${total_savings:.2f}
1344
- - Savings rate: {savings_rate:.1f}%
1345
- - Spending category diversity: {category_count} categories
1346
-
1347
- Consider:
1348
- 1. Spending vs income ratio
1349
- 2. Savings rate
1350
- 3. Budget adherence
1351
- 4. Spending diversity
1352
- 5. Financial stability indicators
1353
-
1354
- Provide:
1355
- 1. Score out of 100
1356
- 2. Brief explanation of the score
1357
- 3. Top 3 improvement recommendations
1358
-
1359
- Keep response concise and actionable.
1360
- """
1361
-
1362
- with st.spinner("Calculating financial health score..."):
1363
- response = llm.invoke([
1364
- SystemMessage(content="You are a financial health assessment expert."),
1365
- HumanMessage(content=prompt)
1366
- ])
1367
-
1368
- st.info("πŸ€– Financial Health Assessment:\n" + response.content)
1369
-
1370
- except Exception as e:
1371
- st.error(f"Error calculating health score: {str(e)}")
1372
-
1373
  except Exception as e:
1374
  st.error(f"❌ Error in dashboard: {str(e)}")
1375
 
@@ -1411,7 +1117,7 @@ def main():
1411
 
1412
  # App title and description
1413
  st.title("πŸ’° Budget Tracker Pro")
1414
- st.markdown("*Your intelligent personal finance assistant powered by AI*")
1415
 
1416
  # Sidebar navigation
1417
  st.sidebar.title("🧭 Navigation")
@@ -1476,20 +1182,9 @@ def main():
1476
  st.sidebar.markdown("---")
1477
  st.sidebar.info("πŸ’‘ Tip: Use voice commands for quick expense logging!")
1478
 
1479
- # Initialize LLM on startup
1480
- if 'llm_initialized' not in st.session_state:
1481
- with st.sidebar:
1482
- with st.spinner("Initializing AI assistant..."):
1483
- llm = get_llm()
1484
- if llm:
1485
- st.session_state.llm_initialized = True
1486
- st.success("πŸ€– AI assistant ready!")
1487
- else:
1488
- st.warning("⚠️ AI features disabled - check Groq API key")
1489
-
1490
  except Exception as e:
1491
  st.error(f"❌ Critical application error: {str(e)}")
1492
  st.info("πŸ”„ Please refresh the page or contact support if the issue persists.")
1493
 
1494
  if __name__ == "__main__":
1495
- main()
 
1
+
2
  # budget_tracker_with_voice_ocr.py
3
  import streamlit as st
4
  import pandas as pd
 
22
  import torch
23
  import pytesseract
24
  import os
 
 
 
 
 
 
25
 
 
 
 
26
 
27
+ warnings.filterwarnings('ignore')
28
 
29
  # Set Tesseract path (update this path according to your system)
30
  # For Windows: r"C:\Program Files\Tesseract-OCR\tesseract.exe"
 
32
  # For Linux: "/usr/bin/tesseract"
33
  try:
34
  # You can set your Tesseract path here
35
+ TESSERACT_PATH = os.getenv("TESSERACT_PATH", r'/usr/bin/tesseract') # Default for Linux
36
  pytesseract.pytesseract.tesseract_cmd = TESSERACT_PATH
37
  except:
38
  pass # Use default path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
 
40
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # Initialize session state for data persistence
43
  def initialize_session_state():
44
  """Initialize all session state variables"""
 
53
  st.session_state.notifications = []
54
  if 'whisper_model' not in st.session_state:
55
  st.session_state.whisper_model = None
 
 
56
  return True
57
  except Exception as e:
58
  st.error(f"Error initializing session state: {str(e)}")
 
80
  model = load_whisper_model()
81
  if model is None:
82
  return None
83
+
84
  with st.spinner("Transcribing audio..."):
85
  output = model(
86
  audio_file_path,
 
113
  """
114
  try:
115
  st.subheader("🎀 Voice Expense Recording")
116
+
117
  # Audio input options
118
  audio_option = st.radio("Choose audio input method:",
119
  ["Microphone (Real-time)", "Upload Audio File"])
120
+
121
  if audio_option == "Microphone (Real-time)":
122
  # Check if microphone is available
123
  try:
 
126
  except:
127
  mic_available = False
128
  st.warning("Microphone not available. Please check your device settings.")
129
+
130
  if mic_available and st.button("πŸŽ™οΈ Start Voice Recording"):
131
  try:
132
  with sr.Microphone() as source:
 
134
  # Adjust for ambient noise
135
  recognizer.adjust_for_ambient_noise(source, duration=1)
136
  audio = recognizer.listen(source, timeout=10)
137
+
138
  # Save audio to temporary file for processing
139
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_file:
140
  with open(tmp_file.name, "wb") as f:
141
  f.write(audio.get_wav_data())
142
  temp_filename = tmp_file.name
143
+
144
  # Try Whisper first, fallback to Google
145
  text = transcribe_audio_with_whisper(temp_filename)
146
  if text is None:
147
  text = transcribe_audio_with_google(temp_filename)
148
+
149
  # Clean up temporary file
150
  os.unlink(temp_filename)
151
+
152
  if text:
153
  st.success(f"βœ… Recognized: {text}")
154
  process_voice_text(text)
155
  else:
156
  st.error("❌ Failed to transcribe audio")
157
+
158
  except sr.WaitTimeoutError:
159
  st.error("⏰ Timeout: No speech detected within 10 seconds")
160
  except sr.UnknownValueError:
 
163
  st.error(f"🌐 Could not request results: {e}")
164
  except Exception as e:
165
  st.error(f"❌ Error processing voice input: {str(e)}")
166
+
167
  else: # Upload Audio File
168
  uploaded_audio = st.file_uploader("Upload Audio File", type=['wav', 'mp3', 'm4a'])
169
+
170
  if uploaded_audio is not None:
171
  if st.button("πŸ”Š Process Audio File"):
172
  try:
 
174
  with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_audio.name)[1]) as tmp_file:
175
  tmp_file.write(uploaded_audio.getvalue())
176
  temp_filename = tmp_file.name
177
+
178
  # Process audio file
179
  with st.spinner("Processing audio file..."):
180
  # Try Whisper first, fallback to Google
181
  text = transcribe_audio_with_whisper(temp_filename)
182
  if text is None:
183
  text = transcribe_audio_with_google(temp_filename)
184
+
185
  # Clean up temporary file
186
  os.unlink(temp_filename)
187
+
188
  if text:
189
  st.success(f"βœ… Transcribed: {text}")
190
  process_voice_text(text)
191
  else:
192
  st.error("❌ Failed to transcribe audio file")
193
+
194
  except Exception as e:
195
  st.error(f"❌ Error processing audio file: {str(e)}")
196
+
197
  # Instructions
198
  st.info("πŸ’‘ Tip: Say something like 'I spent 500 rupees on groceries at Big Bazaar'")
199
+
200
  except Exception as e:
201
  st.error(f"❌ Critical error in voice recording: {str(e)}")
202
 
 
209
  category = "Other"
210
  description = text
211
 
212
+ # Enhanced category detection
213
+ categories = {
214
+ 'Food': ['food', 'groceries', 'restaurant', 'cafe', 'meal', 'lunch', 'dinner', 'breakfast', 'dhaba', 'hotel'],
215
+ 'Transport': ['transport', 'travel', 'taxi', 'uber', 'ola', 'bus', 'train', 'flight', 'fuel', 'petrol', 'diesel', 'auto'],
216
+ 'Shopping': ['shopping', 'clothes', 'electronics', 'purchase', 'buy', 'mall', 'store', 'market'],
217
+ 'Entertainment': ['entertainment', 'movie', 'cinema', 'game', 'fun', 'party', 'netflix', 'spotify'],
218
+ 'Bills': ['bill', 'electricity', 'water', 'internet', 'phone', 'rent', 'insurance', 'subscription'],
219
+ 'Health': ['medicine', 'doctor', 'hospital', 'pharmacy', 'health', 'medical'],
220
+ 'Education': ['education', 'school', 'college', 'books', 'course', 'tuition', 'study']
221
+ }
 
 
 
 
 
 
 
 
222
 
223
+ text_lower = text.lower()
224
+ for cat, keywords in categories.items():
225
+ if any(keyword in text_lower for keyword in keywords):
226
+ category = cat
227
+ break
 
 
 
 
 
 
 
 
 
 
 
228
 
229
  # Extract numbers for amount using regex
230
  amount_pattern = r'(?:$|\$|rs|rupees?|dollars?)\s*(\d+(?:\.\d+)?)|(\d+(?:\.\d+)?)\s*(?:$|\$|rs|rupees?|dollars?)'
231
+
232
+ # amount_pattern = r'(?:$|rs|rupees?)\s*(\d+(?:\.\d+)?)|(\d+(?:\.\d+)?)\s*(?:$|rs|rupees?)'
233
  matches = re.findall(amount_pattern, text_lower)
234
  if matches:
235
  for match in matches:
 
256
  })
257
  st.session_state.expenses = pd.concat([st.session_state.expenses, new_expense], ignore_index=True)
258
  st.success(f"βœ… Expense logged: ${amount:.2f} for {category}")
259
+
260
 
261
  # Check budget alerts
262
  check_budget_alerts(amount, category)
263
 
 
 
 
264
  except Exception as e:
265
  st.error(f"❌ Error processing voice text: {str(e)}")
266
 
 
290
  # Extract text with multiple languages support
291
  custom_config = r'--oem 3 --psm 6 -l eng'
292
  text = pytesseract.image_to_string(image, config=custom_config)
293
+
294
  return text.strip()
295
  except Exception as e:
296
  st.error(f"OCR Error: {e}")
 
338
  """
339
  try:
340
  st.subheader("πŸ“Έ Receipt OCR Processing")
341
+
342
  uploaded_file = st.file_uploader("Upload Receipt Image", type=['jpg', 'jpeg', 'png'])
343
+
344
  if uploaded_file is not None:
345
  try:
346
  image = Image.open(uploaded_file)
347
  st.image(image, caption="πŸ“Έ Uploaded Receipt", use_container_width=True)
348
+
349
  if st.button("πŸ” Process Receipt"):
350
  # Initialize OCR extractor
351
  ocr_extractor = OCRExtractor()
352
+
353
  # Use Tesseract OCR
354
  try:
355
  # Preprocess image for better results
356
  processed_image = ocr_extractor.preprocess_image(image)
357
+
358
  # Extract text
359
  extracted_text = ocr_extractor.extract_text_from_image(processed_image)
360
+
361
  if extracted_text:
362
  st.text_area("πŸ“„ Extracted Text", extracted_text, height=200)
363
 
364
+ # Parse receipt data
 
365
  amount = 0
366
  category = "Other"
367
  description = "Receipt expense"
368
 
369
+ # Extract amount with multiple patterns
370
+ amount_patterns = [
371
+ r'[$$€£]\s*(\d+(?:\.\d+)?)',
372
+ r'(\d+(?:\.\d+)?)\s*[$$€£]',
373
+ r'(?:total|amount|paid|grand total).*?(\d+(?:\.\d+)?)',
374
+ r'(?:bill|invoice).*?(\d+(?:\.\d+)?)'
375
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
 
377
+ for pattern in amount_patterns:
378
+ matches = re.findall(pattern, extracted_text.lower(), re.IGNORECASE)
379
+ if matches:
380
+ for match in matches:
381
+ if isinstance(match, tuple):
382
+ for group in match:
383
+ if group and (group.replace('.', '').isdigit()):
384
+ amount = float(group)
385
+ break
386
+ elif match.replace('.', '').isdigit():
387
+ amount = float(match)
 
 
 
 
 
 
 
 
 
 
 
388
  break
389
+ if amount > 0:
390
+ break
391
 
392
  # Enhanced category detection
393
+ categories_keywords = {
394
+ 'Food': ['restaurant', 'cafe', 'grocery', 'food', 'meal', 'supermarket', 'big bazaar', 'dmart', 'walmart'],
395
+ 'Transport': ['taxi', 'uber', 'ola', 'fuel', 'petrol', 'bus', 'train', 'airport', 'parking'],
396
+ 'Shopping': ['mall', 'store', 'shop', 'purchase', 'clothes', 'electronics', 'amazon', 'flipkart'],
397
+ 'Entertainment': ['movie', 'cinema', 'game', 'entertainment', 'theatre', 'netflix'],
398
+ 'Bills': ['electricity', 'water', 'internet', 'phone', 'rent', 'subscription', 'bill'],
399
+ 'Health': ['pharmacy', 'medicine', 'doctor', 'hospital', 'medical', 'apollo', 'apollo'],
400
+ 'Education': ['school', 'college', 'books', 'stationery', 'tution', 'course']
401
+ }
402
+
403
+ text_lower = extracted_text.lower()
404
+ for cat, keywords in categories_keywords.items():
405
+ if any(keyword in text_lower for keyword in keywords):
406
+ category = cat
407
+ break
408
 
409
  # Save to expenses with image data
410
  image_data = f"data:image/png;base64,{base64.b64encode(uploaded_file.getvalue()).decode()}"
 
420
 
421
  # Check budget alerts
422
  check_budget_alerts(amount, category)
 
 
 
 
423
  else:
424
  st.error("❌ Could not extract text from image. Please try a clearer image.")
425
+
426
  except Exception as e:
427
  st.error(f"❌ OCR processing failed: {str(e)}")
428
  st.info("πŸ’‘ Make sure Tesseract OCR is properly installed on your system")
429
+
430
  except Exception as e:
431
  st.error(f"❌ Error processing image: {str(e)}")
432
  else:
433
  st.info("πŸ“€ Please upload a receipt image (JPG, JPEG, PNG)")
434
+
435
  except Exception as e:
436
  st.error(f"❌ Critical error in OCR processing: {str(e)}")
437
 
438
  def create_budget():
439
  """
440
  Function to create and manage budgets
441
+ LLM Needed: NO - Simple form-based input
442
+ Could use LLM for budget recommendations based on spending patterns
443
  """
444
  try:
445
  st.subheader("πŸ’° Create Budget")
446
+
447
  col1, col2 = st.columns(2)
448
  with col1:
449
  predefined_categories = ["Food", "Transport", "Shopping", "Entertainment", "Bills", "Health", "Education", "Other"]
 
452
  category = st.selectbox("Category", predefined_categories)
453
  else:
454
  category = st.text_input("Enter custom category")
455
+
456
  with col2:
457
  budget_amount = st.number_input("Budget Amount ($)", min_value=0.0, step=100.0, value=1000.0)
458
  period = st.selectbox("Period", ["Monthly", "Weekly", "Custom"])
459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  if st.button("πŸ“Š Set Budget"):
461
  if category and budget_amount > 0:
462
  try:
 
464
  existing_budget = st.session_state.budgets[
465
  st.session_state.budgets['category'] == category
466
  ]
467
+
468
  if not existing_budget.empty:
469
  # Update existing budget
470
  st.session_state.budgets.loc[
 
504
  st.success(f"βœ… Budget for {budget_to_delete} deleted")
505
  else:
506
  st.info("πŸ“ No budgets set yet. Create your first budget!")
507
+
508
  except Exception as e:
509
  st.error(f"❌ Critical error in budget creation: {str(e)}")
510
 
511
  def set_savings_goals():
512
  """
513
  Function to set and track savings goals
514
+ LLM Needed: NO - Simple goal tracking
515
+ Could use LLM for personalized savings recommendations
516
  """
517
  try:
518
  st.subheader("🎯 Savings Goals")
519
+
520
  col1, col2, col3 = st.columns(3)
521
  with col1:
522
  goal_name = st.text_input("Goal Name", placeholder="e.g., Vacation, Emergency Fund")
 
526
  target_date = st.date_input("Target Date",
527
  value=datetime.now() + timedelta(days=30))
528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
  if st.button("🎯 Set Goal"):
530
  if goal_name and target_amount > 0:
531
  try:
 
546
  # Display existing goals
547
  if not st.session_state.savings_goals.empty:
548
  st.subheader("πŸ† Current Goals")
549
+
550
  for idx, goal in st.session_state.savings_goals.iterrows():
551
  try:
552
  progress = (goal['current_amount'] / goal['target_amount']) * 100 if goal['target_amount'] > 0 else 0
553
  days_left = (goal['target_date'] - datetime.now().date()).days
554
+
555
  st.write(f"**{goal['goal_name']}**")
556
  st.progress(min(progress/100, 1.0))
557
  st.write(f"πŸ’° ${goal['current_amount']:.2f} / ${goal['target_amount']:.2f} ({progress:.1f}%)")
 
565
  st.session_state.savings_goals.at[idx, 'current_amount'] += add_amount
566
  st.success(f"βœ… Added ${add_amount:.2f} to {goal['goal_name']}")
567
  st.rerun()
568
+
569
  st.write("---")
570
  except Exception as e:
571
  st.error(f"❌ Error displaying goal: {str(e)}")
572
  else:
573
  st.info("πŸ“ No savings goals set yet. Create your first goal!")
574
+
575
  except Exception as e:
576
  st.error(f"❌ Critical error in savings goals: {str(e)}")
577
 
578
  def spending_categorization():
579
  """
580
  Function to categorize and review spending
581
+ LLM Needed: NO - Rule-based categorization
582
+ Could use LLM for smarter automatic categorization
583
  """
584
  try:
585
  st.subheader("🏷️ Spending Categorization")
586
+
587
  if not st.session_state.expenses.empty:
588
  # Display expenses that need categorization
589
  uncategorized = st.session_state.expenses[st.session_state.expenses['category'] == 'Other']
 
648
  total_spent = filtered_expenses['amount'].sum()
649
  avg_spent = filtered_expenses['amount'].mean()
650
  st.metric("Total Spent", f"${total_spent:.2f}")
651
+
652
  st.metric("Average Expense", f"${avg_spent:.2f}")
653
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  else:
655
  st.info("πŸ” No expenses match the current filters")
656
  else:
657
  st.info("πŸ“ No expenses recorded yet. Start by adding expenses through voice or receipt scanning!")
658
+
659
  except Exception as e:
660
  st.error(f"❌ Critical error in spending categorization: {str(e)}")
661
 
 
674
 
675
  if current_spending > budget_amount:
676
  alert_msg = f"🚨 OVERSPENT: {category} - ${current_spending:.2f}/${budget_amount:.2f}"
677
+
678
+
679
  if alert_msg not in st.session_state.notifications:
680
  st.session_state.notifications.append(alert_msg)
681
  elif current_spending > budget_amount * 0.8: # 80% threshold
682
  alert_msg = f"⚠️ WARNING: {category} - ${current_spending:.2f}/${budget_amount:.2f} ({((current_spending/budget_amount)*100):.1f}%)"
683
+
684
+
685
  if alert_msg not in st.session_state.notifications:
686
  st.session_state.notifications.append(alert_msg)
687
  except Exception as e:
688
  st.error(f"❌ Error checking budget alerts: {str(e)}")
689
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
  def alerts_and_notifications():
691
  """
692
  Function to check and display budget alerts
693
+ LLM Needed: NO - Simple threshold checking
694
+ Could use LLM for personalized alert messages
695
  """
696
  try:
697
  st.subheader("πŸ”” Budget Alerts & Notifications")
 
707
  try:
708
  # Calculate spending by category
709
  spending_by_category = st.session_state.expenses.groupby('category')['amount'].sum().reset_index()
710
+
711
  alerts = []
712
  for _, budget in st.session_state.budgets.iterrows():
713
  category_spending = spending_by_category[spending_by_category['category'] == budget['category']]
714
  if not category_spending.empty:
715
  spent = category_spending.iloc[0]['amount']
716
  budget_amount = budget['budget_amount']
717
+
718
  if spent > budget_amount:
719
  alerts.append(f"🚨 OVERSPENT: {budget['category']} - ${spent:.2f}/${budget_amount:.2f} ({((spent/budget_amount)*100):.1f}%)")
720
+
721
+
722
  elif spent > budget_amount * 0.8: # 80% threshold
723
  alerts.append(f"⚠️ WARNING: {budget['category']} - ${spent:.2f}/${budget_amount:.2f} ({((spent/budget_amount)*100):.1f}%)")
724
+
725
+
726
 
727
  # Display alerts
728
  if alerts:
 
732
  st.session_state.notifications.append(alert)
733
  else:
734
  st.success("βœ… All budgets are within limits!")
735
+
736
  except Exception as e:
737
  st.error(f"❌ Error calculating alerts: {str(e)}")
738
  else:
 
746
  else:
747
  st.info("πŸ“­ No notifications yet")
748
 
 
 
 
 
 
749
  except Exception as e:
750
  st.error(f"❌ Critical error in alerts system: {str(e)}")
751
 
752
  def visualizations_and_summaries():
753
  """
754
  Function to create charts and summaries
755
+ LLM Needed: NO - Standard data visualization
756
+ Could use LLM for generating insights and summaries
757
  """
758
  try:
759
  st.subheader("πŸ“Š Financial Visualizations")
760
+
761
  if not st.session_state.expenses.empty:
762
  try:
763
  # Spending by category pie chart
764
  spending_by_category = st.session_state.expenses.groupby('category')['amount'].sum()
765
+
766
  col1, col2 = st.columns(2)
767
+
768
  with col1:
769
  st.write("πŸ’° Spending by Category")
770
  if len(spending_by_category) > 0:
 
773
  st.plotly_chart(fig1, use_container_width=True)
774
  else:
775
  st.info("No spending data to visualize")
776
+
777
  with col2:
778
  st.write("πŸ“ˆ Spending Trend")
779
  daily_spending = st.session_state.expenses.groupby('date')['amount'].sum().reset_index()
 
791
  monthly_expenses = st.session_state.expenses[
792
  st.session_state.expenses['date'].str.startswith(current_month)
793
  ]
794
+
795
  if not monthly_expenses.empty:
796
  total_spent = monthly_expenses['amount'].sum()
797
  st.metric("Total Monthly Spending", f"${total_spent:.2f}")
798
+
799
+
800
  category_summary = monthly_expenses.groupby('category')['amount'].sum().reset_index()
801
  fig3 = px.bar(category_summary, x='category', y='amount',
802
  title='Monthly Spending by Category')
803
  st.plotly_chart(fig3, use_container_width=True)
804
+
805
  st.dataframe(category_summary)
806
  else:
807
  st.info("No expenses recorded this month.")
808
+
809
  # Budget vs Actual comparison
810
  if not st.session_state.budgets.empty:
811
  st.subheader("βš–οΈ Budget vs Actual Comparison")
 
820
  'Actual': actual_spent,
821
  'Difference': budget['budget_amount'] - actual_spent
822
  })
823
+
824
  if budget_comparison:
825
  comparison_df = pd.DataFrame(budget_comparison)
826
  st.dataframe(comparison_df)
827
+
828
  # Visualization
829
  fig4 = go.Figure()
830
  fig4.add_trace(go.Bar(name='Budget', x=comparison_df['Category'], y=comparison_df['Budget']))
831
  fig4.add_trace(go.Bar(name='Actual', x=comparison_df['Category'], y=comparison_df['Actual']))
832
  fig4.update_layout(title="Budget vs Actual Spending", barmode='group')
833
  st.plotly_chart(fig4, use_container_width=True)
834
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
835
  except Exception as e:
836
  st.error(f"❌ Error creating visualizations: {str(e)}")
837
  else:
838
  st.info("πŸ“ No data to visualize yet. Start by recording expenses!")
839
+
840
  except Exception as e:
841
  st.error(f"❌ Critical error in visualizations: {str(e)}")
842
 
843
  def receipt_management():
844
  """
845
  Function to manage and view stored receipts
846
+ LLM Needed: NO - Simple storage and retrieval
847
+ Could use LLM for receipt categorization and insights
848
  """
849
  try:
850
  st.subheader("🧾 Receipt Management")
851
+
852
  if not st.session_state.expenses.empty:
853
  receipts = st.session_state.expenses[st.session_state.expenses['receipt_image'] != '']
854
  if not receipts.empty:
 
883
  with cols[idx % 3]:
884
  st.write(f"**πŸ“… {receipt['date']}**")
885
  st.write(f"πŸ’° ${receipt['amount']:.2f}")
886
+
887
  st.write(f"🏷️ {receipt['category']}")
888
  if receipt['receipt_image'].startswith('data:image'):
889
  # Display base64 image
890
  st.image(receipt['receipt_image'], width=200)
891
  st.write(f"πŸ“ {receipt['description'][:50]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
892
  st.write("---")
893
  except Exception as e:
894
  st.error(f"❌ Error displaying receipt: {str(e)}")
 
898
  st.info("πŸ“ No receipts uploaded yet. Upload receipts through the OCR feature!")
899
  else:
900
  st.info("πŸ“ No expenses recorded yet. Start by recording expenses!")
901
+
902
  except Exception as e:
903
  st.error(f"❌ Critical error in receipt management: {str(e)}")
904
 
 
909
  """
910
  try:
911
  st.subheader("πŸ”’ Data Security & Privacy")
912
+
913
  st.write("πŸ›‘οΈ Your financial data is stored locally and never shared with third parties.")
914
  st.write("πŸ” All data is encrypted and protected according to privacy regulations.")
915
 
916
  # Security settings
917
  st.subheader("βš™οΈ Security Settings")
918
+
919
  if st.checkbox("Enable Data Encryption", value=True):
920
  st.success("βœ… Data encryption is enabled!")
921
+
922
  if st.checkbox("Enable Automatic Backups"):
923
  backup_frequency = st.selectbox("Backup Frequency", ["Daily", "Weekly", "Monthly"])
924
  st.info(f"πŸ“… Automatic backups will run {backup_frequency.lower()}")
 
932
  'budgets': st.session_state.budgets.to_dict('records') if not st.session_state.budgets.empty else [],
933
  'savings_goals': st.session_state.savings_goals.to_dict('records') if not st.session_state.savings_goals.empty else []
934
  }
935
+
936
  json_str = json.dumps(export_data, indent=2, default=str)
937
  st.download_button(
938
  label="πŸ“₯ Download Data as JSON",
 
940
  file_name=f"budget_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
941
  mime="application/json"
942
  )
943
+
944
  # Also provide CSV export
945
  if not st.session_state.expenses.empty:
946
  st.download_button(
 
960
  - We collect only the financial data you enter
961
  - No personal identification information is collected
962
  - All data is stored locally on your device
963
+
964
  **Data Usage:**
965
  - Your data is used only for the functionality of this application
966
  - We do not share your data with any third parties
967
  - Data is not transmitted over the internet
968
+
969
  **Data Security:**
970
  - All data is encrypted at rest
971
  - You have full control over your data
972
  - You can export or delete your data at any time
973
  """)
974
+
975
  except Exception as e:
976
  st.error(f"❌ Critical error in security section: {str(e)}")
977
 
978
  def bank_integration_placeholder():
979
  """
980
  Placeholder for bank integration feature
981
+ LLM Needed: NO - Just UI placeholder
982
+ Would need LLM for natural language banking queries
983
  """
984
  try:
985
  st.subheader("🏦 Bank Integration (Coming Soon)")
986
+
987
  st.info("πŸš€ This feature will allow automatic syncing with your bank accounts!")
988
+
989
  st.write("πŸ“‹ Planned Features:")
990
  st.write("β€’ Automatic transaction import")
991
  st.write("β€’ Real-time balance updates")
992
  st.write("β€’ Bank statement analysis")
993
  st.write("β€’ Automatic expense categorization")
994
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
995
  bank_name = st.selectbox("Select Bank", ["HDFC", "ICICI", "SBI", "Axis", "Kotak", "Other"])
996
  if bank_name:
997
  st.info(f"Bank integration for {bank_name} will be available soon!")
998
+
999
  except Exception as e:
1000
  st.error(f"❌ Error in bank integration section: {str(e)}")
1001
 
1002
  def main_dashboard():
1003
  """
1004
  Main dashboard overview
 
1005
  """
1006
  try:
1007
  st.subheader("🏠 Dashboard Overview")
1008
 
1009
  # Key metrics
1010
  col1, col2, col3, col4 = st.columns(4)
1011
+
1012
  total_expenses = st.session_state.expenses['amount'].sum() if not st.session_state.expenses.empty else 0
1013
  total_budget = st.session_state.budgets['budget_amount'].sum() if not st.session_state.budgets.empty else 0
1014
  total_savings = st.session_state.savings_goals['current_amount'].sum() if not st.session_state.savings_goals.empty else 0
 
1016
 
1017
  with col1:
1018
  st.metric("πŸ’° Total Expenses", f"${total_expenses:.2f}")
1019
+
1020
  with col2:
1021
  st.metric("πŸ“Š Total Budget", f"${total_budget:.2f}")
1022
+
1023
  with col3:
1024
  st.metric("πŸ† Total Savings", f"${total_savings:.2f}")
1025
+
1026
  with col4:
1027
  st.metric("🧾 Expense Count", f"{expense_count}")
1028
 
 
1057
  category_spending = st.session_state.expenses[
1058
  st.session_state.expenses['category'] == budget['category']
1059
  ]['amount'].sum()
1060
+
1061
  progress = (category_spending / budget['budget_amount']) * 100 if budget['budget_amount'] > 0 else 0
1062
  st.write(f"**{budget['category']}**")
1063
  st.progress(min(progress/100, 1.0))
1064
  st.write(f"${category_spending:.2f} / ${budget['budget_amount']:.2f} ({progress:.1f}%)")
1065
+
1066
+
1067
 
1068
  # Savings goals progress
1069
  if not st.session_state.savings_goals.empty:
 
1073
  st.write(f"**{goal['goal_name']}**")
1074
  st.progress(min(progress/100, 1.0))
1075
  st.write(f"${goal['current_amount']:.2f} / ${goal['target_amount']:.2f} ({progress:.1f}%)")
1076
+
1077
+
1078
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1079
  except Exception as e:
1080
  st.error(f"❌ Error in dashboard: {str(e)}")
1081
 
 
1117
 
1118
  # App title and description
1119
  st.title("πŸ’° Budget Tracker Pro")
1120
+ st.markdown("*Your intelligent personal finance assistant*")
1121
 
1122
  # Sidebar navigation
1123
  st.sidebar.title("🧭 Navigation")
 
1182
  st.sidebar.markdown("---")
1183
  st.sidebar.info("πŸ’‘ Tip: Use voice commands for quick expense logging!")
1184
 
 
 
 
 
 
 
 
 
 
 
 
1185
  except Exception as e:
1186
  st.error(f"❌ Critical application error: {str(e)}")
1187
  st.info("πŸ”„ Please refresh the page or contact support if the issue persists.")
1188
 
1189
  if __name__ == "__main__":
1190
+ main()