2001muhammadumair commited on
Commit
8daec1b
Β·
verified Β·
1 Parent(s): 805308a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1190 -0
app.py ADDED
@@ -0,0 +1,1190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # budget_tracker_with_voice_ocr.py
3
+ import streamlit as st
4
+ import pandas as pd
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ import seaborn as sns
8
+ import plotly.express as px
9
+ import plotly.graph_objects as go
10
+ from datetime import datetime, timedelta
11
+ import pytesseract
12
+ from PIL import Image
13
+ import speech_recognition as sr
14
+ import io
15
+ import base64
16
+ import warnings
17
+ import re
18
+ import json
19
+ import os
20
+ import tempfile
21
+ from transformers import pipeline
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"
31
+ # For Mac: "/usr/local/bin/tesseract"
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"""
45
+ try:
46
+ if 'expenses' not in st.session_state:
47
+ st.session_state.expenses = pd.DataFrame(columns=['date', 'amount', 'category', 'description', 'receipt_image'])
48
+ if 'budgets' not in st.session_state:
49
+ st.session_state.budgets = pd.DataFrame(columns=['category', 'budget_amount', 'period'])
50
+ if 'savings_goals' not in st.session_state:
51
+ st.session_state.savings_goals = pd.DataFrame(columns=['goal_name', 'target_amount', 'current_amount', 'target_date'])
52
+ if 'notifications' not in st.session_state:
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)}")
59
+ return False
60
+
61
+ # Voice Recognition with Whisper (Corrected Implementation)
62
+ def load_whisper_model():
63
+ """Load Whisper model for speech recognition"""
64
+ try:
65
+ if st.session_state.whisper_model is None:
66
+ with st.spinner("Loading Whisper model... This may take a moment."):
67
+ st.session_state.whisper_model = pipeline(
68
+ "automatic-speech-recognition",
69
+ model="openai/whisper-tiny", # Using tiny model for faster loading
70
+ chunk_length_s=30,
71
+ )
72
+ return st.session_state.whisper_model
73
+ except Exception as e:
74
+ st.error(f"Error loading Whisper model: {str(e)}")
75
+ return None
76
+
77
+ def transcribe_audio_with_whisper(audio_file_path):
78
+ """Transcribe audio using Whisper model"""
79
+ try:
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,
87
+ generate_kwargs={"task": "transcribe"},
88
+ batch_size=8,
89
+ return_timestamps=False,
90
+ )
91
+ return output["text"]
92
+ except Exception as e:
93
+ st.error(f"Error in transcription: {str(e)}")
94
+ return None
95
+
96
+ def transcribe_audio_with_google(audio_file_path):
97
+ """Fallback: Transcribe audio using Google Speech Recognition"""
98
+ try:
99
+ recognizer = sr.Recognizer()
100
+ with sr.AudioFile(audio_file_path) as source:
101
+ audio = recognizer.record(source)
102
+ text = recognizer.recognize_google(audio)
103
+ return text
104
+ except Exception as e:
105
+ st.error(f"Google Speech Recognition error: {str(e)}")
106
+ return None
107
+
108
+ def voice_expense_recording():
109
+ """
110
+ Function to record expense using voice input
111
+ LLM Needed: NO - Uses Whisper/GSR for speech recognition
112
+ Could use LLM for better natural language understanding
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:
124
+ recognizer = sr.Recognizer()
125
+ mic_available = True
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:
133
+ st.info("🎀 Listening... Please speak your expense (e.g., 'I spent 500 rupees on groceries')")
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:
161
+ st.error("πŸ€” Could not understand audio. Please try again.")
162
+ except sr.RequestError as e:
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:
173
+ # Save uploaded file temporarily
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
+
203
+ def process_voice_text(text):
204
+ """Process transcribed voice text to extract expense details"""
205
+ try:
206
+ # Enhanced parsing logic
207
+ st.info("πŸ”„ Processing voice input...")
208
+ amount = 0
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:
236
+ for group in match:
237
+ if group and (group.replace('.', '').isdigit()):
238
+ amount = float(group)
239
+ break
240
+ if amount > 0:
241
+ break
242
+
243
+ # Fallback: look for any number
244
+ if amount == 0:
245
+ numbers = re.findall(r'\d+(?:\.\d+)?', text)
246
+ if numbers:
247
+ amount = float(numbers[0])
248
+
249
+ # Save to expenses
250
+ new_expense = pd.DataFrame({
251
+ 'date': [datetime.now().strftime('%Y-%m-%d')],
252
+ 'amount': [amount],
253
+ 'category': [category],
254
+ 'description': [description],
255
+ 'receipt_image': ['']
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
+
267
+ # OCR Processing (Corrected Implementation)
268
+ class OCRExtractor:
269
+ def __init__(self):
270
+ pass
271
+
272
+ def extract_text_from_image(self, image_input):
273
+ """Extract text from image using Tesseract OCR"""
274
+ try:
275
+ # Handle different input types
276
+ if hasattr(image_input, 'read'):
277
+ # Uploaded file
278
+ image = Image.open(image_input)
279
+ elif isinstance(image_input, str):
280
+ # File path
281
+ image = Image.open(image_input)
282
+ else:
283
+ # PIL Image
284
+ image = image_input
285
+
286
+ # Convert to RGB if necessary
287
+ if image.mode != 'RGB':
288
+ image = image.convert('RGB')
289
+
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}")
297
+ return None
298
+
299
+ def extract_structured_data(self, image_input):
300
+ """Extract structured data from receipt image"""
301
+ try:
302
+ text = self.extract_text_from_image(image_input)
303
+ if not text:
304
+ return None
305
+
306
+ # Basic structure extraction
307
+ lines = [line.strip() for line in text.split('\n') if line.strip()]
308
+
309
+ return {
310
+ 'raw_text': text,
311
+ 'lines': lines,
312
+ 'extracted_at': str(pd.Timestamp.now())
313
+ }
314
+ except Exception as e:
315
+ st.error(f"Error extracting structured data: {e}")
316
+ return None
317
+
318
+ def preprocess_image(self, image):
319
+ """Preprocess image for better OCR results"""
320
+ try:
321
+ # Convert to grayscale
322
+ if image.mode != 'L':
323
+ image = image.convert('L')
324
+
325
+ # You can add more preprocessing steps here
326
+ # like noise reduction, contrast enhancement, etc.
327
+
328
+ return image
329
+ except Exception as e:
330
+ st.error(f"Error preprocessing image: {e}")
331
+ return image
332
+
333
+ def ocr_receipt_processing():
334
+ """
335
+ Function to process receipt images using OCR
336
+ LLM Needed: NO - Uses Tesseract OCR for text extraction
337
+ Could use LLM for better data parsing and categorization
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()}"
411
+ new_expense = pd.DataFrame({
412
+ 'date': [datetime.now().strftime('%Y-%m-%d')],
413
+ 'amount': [amount],
414
+ 'category': [category],
415
+ 'description': [description],
416
+ 'receipt_image': [image_data]
417
+ })
418
+ st.session_state.expenses = pd.concat([st.session_state.expenses, new_expense], ignore_index=True)
419
+ st.success(f"βœ… Receipt processed successfully: ${amount:.2f} for {category}")
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"]
450
+ category_type = st.radio("Category Type", ["Predefined", "Custom"])
451
+ if category_type == "Predefined":
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:
463
+ # Check if budget already exists for this category
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[
471
+ st.session_state.budgets['category'] == category, 'budget_amount'
472
+ ] = budget_amount
473
+ st.session_state.budgets.loc[
474
+ st.session_state.budgets['category'] == category, 'period'
475
+ ] = period
476
+ st.success(f"πŸ”„ Budget updated: ${budget_amount:.2f} for {category}")
477
+ else:
478
+ # Add new budget
479
+ new_budget = pd.DataFrame({
480
+ 'category': [category],
481
+ 'budget_amount': [budget_amount],
482
+ 'period': [period]
483
+ })
484
+ st.session_state.budgets = pd.concat([st.session_state.budgets, new_budget], ignore_index=True)
485
+ st.success(f"βœ… Budget set: ${budget_amount:.2f} for {category}")
486
+ except Exception as e:
487
+ st.error(f"❌ Error setting budget: {str(e)}")
488
+ else:
489
+ st.error("⚠️ Please enter valid category and amount")
490
+
491
+ # Display existing budgets
492
+ if not st.session_state.budgets.empty:
493
+ st.subheader("πŸ“Š Current Budgets")
494
+ st.dataframe(st.session_state.budgets)
495
+
496
+ # Option to delete budgets
497
+ if st.checkbox("πŸ—‘οΈ Show delete options"):
498
+ budget_to_delete = st.selectbox("Select budget to delete",
499
+ st.session_state.budgets['category'].tolist())
500
+ if st.button("πŸ—‘οΈ Delete Budget"):
501
+ st.session_state.budgets = st.session_state.budgets[
502
+ st.session_state.budgets['category'] != budget_to_delete
503
+ ]
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")
523
+ with col2:
524
+ target_amount = st.number_input("Target Amount ($)", min_value=0.0, step=1000.0, value=10000.0)
525
+ with col3:
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:
532
+ new_goal = pd.DataFrame({
533
+ 'goal_name': [goal_name],
534
+ 'target_amount': [target_amount],
535
+ 'current_amount': [0.0],
536
+ 'target_date': [target_date]
537
+ })
538
+ st.session_state.savings_goals = pd.concat([st.session_state.savings_goals, new_goal], ignore_index=True)
539
+ st.success(f"βœ… Savings goal '{goal_name}' set!")
540
+ st.balloons()
541
+ except Exception as e:
542
+ st.error(f"❌ Error setting goal: {str(e)}")
543
+ else:
544
+ st.error("⚠️ Please enter valid goal name and target amount")
545
+
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}%)")
558
+ st.write(f"πŸ“… Target Date: {goal['target_date']} ({days_left} days left)")
559
+
560
+ # Add to current savings
561
+ add_amount = st.number_input(f"Add to {goal['goal_name']}",
562
+ min_value=0.0, step=100.0, key=f"add_{idx}")
563
+ if st.button(f"βž• Add to {goal['goal_name']}", key=f"btn_{idx}"):
564
+ if add_amount > 0:
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']
590
+ if not uncategorized.empty:
591
+ st.write("πŸ“ Uncategorized Expenses:")
592
+ for idx, expense in uncategorized.iterrows():
593
+ try:
594
+ st.write(f"πŸ“… {expense['date']} | ${expense['amount']:.2f} | {expense['description']}")
595
+ predefined_categories = ["Food", "Transport", "Shopping", "Entertainment", "Bills", "Health", "Education", "Other"]
596
+ new_category = st.selectbox(f"Re-categorize", predefined_categories,
597
+ key=f"cat_{idx}")
598
+ if st.button(f"πŸ”„ Update Category {idx}"):
599
+ if new_category and new_category != 'Other':
600
+ st.session_state.expenses.at[idx, 'category'] = new_category
601
+ st.success(f"βœ… Category updated to {new_category}!")
602
+ st.rerun()
603
+ except Exception as e:
604
+ st.error(f"❌ Error updating category: {str(e)}")
605
+ st.write("---")
606
+
607
+ # Show all expenses with filter options
608
+ st.subheader("πŸ“‹ All Expenses")
609
+
610
+ # Filters
611
+ col1, col2, col3 = st.columns(3)
612
+ with col1:
613
+ category_filter = st.selectbox("Filter by Category",
614
+ ["All"] + list(st.session_state.expenses['category'].unique()))
615
+ with col2:
616
+ date_filter = st.selectbox("Date Range", ["All", "Last 7 days", "Last 30 days", "This month"])
617
+ with col3:
618
+ min_amount = st.number_input("Min Amount", min_value=0.0, step=10.0)
619
+ max_amount = st.number_input("Max Amount", min_value=0.0,
620
+ value=float(st.session_state.expenses['amount'].max()) if not st.session_state.expenses.empty else 10000.0)
621
+
622
+ # Apply filters
623
+ filtered_expenses = st.session_state.expenses.copy()
624
+
625
+ if category_filter != "All":
626
+ filtered_expenses = filtered_expenses[filtered_expenses['category'] == category_filter]
627
+
628
+ if date_filter == "Last 7 days":
629
+ cutoff_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
630
+ filtered_expenses = filtered_expenses[filtered_expenses['date'] >= cutoff_date]
631
+ elif date_filter == "Last 30 days":
632
+ cutoff_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
633
+ filtered_expenses = filtered_expenses[filtered_expenses['date'] >= cutoff_date]
634
+ elif date_filter == "This month":
635
+ current_month = datetime.now().strftime('%Y-%m')
636
+ filtered_expenses = filtered_expenses[filtered_expenses['date'].str.startswith(current_month)]
637
+
638
+ filtered_expenses = filtered_expenses[
639
+ (filtered_expenses['amount'] >= min_amount) &
640
+ (filtered_expenses['amount'] <= max_amount)
641
+ ]
642
+
643
+ if not filtered_expenses.empty:
644
+ st.dataframe(filtered_expenses[['date', 'amount', 'category', 'description']])
645
+
646
+ # Summary statistics
647
+ st.subheader("πŸ“Š Summary")
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
+
662
+ def check_budget_alerts(amount, category):
663
+ """
664
+ Helper function to check budget alerts
665
+ """
666
+ try:
667
+ if not st.session_state.budgets.empty:
668
+ category_budget = st.session_state.budgets[st.session_state.budgets['category'] == category]
669
+ if not category_budget.empty:
670
+ budget_amount = category_budget.iloc[0]['budget_amount']
671
+ current_spending = st.session_state.expenses[
672
+ st.session_state.expenses['category'] == category
673
+ ]['amount'].sum()
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")
698
+
699
+ # Clear notifications button
700
+ if st.session_state.notifications:
701
+ if st.button("🧹 Clear All Notifications"):
702
+ st.session_state.notifications = []
703
+ st.rerun()
704
+
705
+ # Check for new alerts
706
+ if not st.session_state.expenses.empty and not st.session_state.budgets.empty:
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:
729
+ for alert in alerts:
730
+ st.warning(alert)
731
+ if alert not in st.session_state.notifications:
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:
739
+ st.info("πŸ“ Set up budgets and record expenses to see alerts")
740
+
741
+ # Display all notifications
742
+ if st.session_state.notifications:
743
+ st.subheader("πŸ“œ Recent Notifications")
744
+ for notification in reversed(st.session_state.notifications[-10:]): # Show last 10
745
+ st.info(notification)
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:
771
+ fig1 = px.pie(values=spending_by_category.values, names=spending_by_category.index,
772
+ title="Expense Distribution by Category")
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()
780
+ if len(daily_spending) > 1:
781
+ fig2 = px.line(daily_spending, x='date', y='amount',
782
+ title='Daily Spending Trend')
783
+ fig2.update_xaxes(type='category')
784
+ st.plotly_chart(fig2, use_container_width=True)
785
+ else:
786
+ st.info("Need more data points for trend analysis")
787
+
788
+ # Monthly summary
789
+ st.subheader("πŸ“‹ Monthly Summary")
790
+ current_month = datetime.now().strftime('%Y-%m')
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")
812
+ budget_comparison = []
813
+ for _, budget in st.session_state.budgets.iterrows():
814
+ actual_spent = st.session_state.expenses[
815
+ st.session_state.expenses['category'] == budget['category']
816
+ ]['amount'].sum()
817
+ budget_comparison.append({
818
+ 'Category': budget['category'],
819
+ 'Budget': budget['budget_amount'],
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:
855
+ st.write(f"πŸ“ Found {len(receipts)} receipts")
856
+
857
+ # Search functionality
858
+ search_term = st.text_input("οΏ½οΏ½οΏ½οΏ½ Search receipts by description or category")
859
+ if search_term:
860
+ receipts = receipts[
861
+ receipts['description'].str.contains(search_term, case=False) |
862
+ receipts['category'].str.contains(search_term, case=False)
863
+ ]
864
+ st.write(f"πŸ” Found {len(receipts)} matching receipts")
865
+
866
+ # Sort options
867
+ sort_option = st.selectbox("Sort by", ["Date (Newest)", "Date (Oldest)", "Amount (High to Low)", "Amount (Low to High)"])
868
+
869
+ if sort_option == "Date (Newest)":
870
+ receipts = receipts.sort_values('date', ascending=False)
871
+ elif sort_option == "Date (Oldest)":
872
+ receipts = receipts.sort_values('date', ascending=True)
873
+ elif sort_option == "Amount (High to Low)":
874
+ receipts = receipts.sort_values('amount', ascending=False)
875
+ elif sort_option == "Amount (Low to High)":
876
+ receipts = receipts.sort_values('amount', ascending=True)
877
+
878
+ # Display receipts in a grid
879
+ if not receipts.empty:
880
+ cols = st.columns(3)
881
+ for idx, (i, receipt) in enumerate(receipts.iterrows()):
882
+ try:
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)}")
895
+ else:
896
+ st.info("πŸ” No receipts match your search criteria")
897
+ else:
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
+
905
+ def data_security_and_privacy():
906
+ """
907
+ Function to handle data security (simulated)
908
+ LLM Needed: NO - Just UI for privacy settings
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()}")
925
+
926
+ # Data export
927
+ st.subheader("πŸ“€ Data Export")
928
+ if st.button("πŸ’Ύ Export All Data"):
929
+ try:
930
+ export_data = {
931
+ 'expenses': st.session_state.expenses.to_dict('records') if not st.session_state.expenses.empty else [],
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",
939
+ data=json_str,
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(
947
+ label="πŸ“Š Download Expenses CSV",
948
+ data=st.session_state.expenses.to_csv(index=False).encode('utf-8'),
949
+ file_name=f"expenses_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
950
+ mime="text/csv"
951
+ )
952
+ except Exception as e:
953
+ st.error(f"❌ Error exporting data: {str(e)}")
954
+
955
+ # Privacy policy
956
+ st.subheader("πŸ“œ Privacy Policy")
957
+ with st.expander("Read Privacy Policy"):
958
+ st.write("""
959
+ **Data Collection:**
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
1015
+ expense_count = len(st.session_state.expenses)
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
+
1029
+ # Quick actions
1030
+ st.subheader("⚑ Quick Actions")
1031
+ col1, col2, col3 = st.columns(3)
1032
+ with col1:
1033
+ if st.button("🎀 Record Voice Expense"):
1034
+ st.session_state.current_page = "🎀 Voice Expense"
1035
+ st.rerun()
1036
+ with col2:
1037
+ if st.button("πŸ“Έ Scan Receipt"):
1038
+ st.session_state.current_page = "πŸ“Έ OCR Receipts"
1039
+ st.rerun()
1040
+ with col3:
1041
+ if st.button("πŸ“Š View Analytics"):
1042
+ st.session_state.current_page = "πŸ“Š Visualizations"
1043
+ st.rerun()
1044
+
1045
+ # Recent activity
1046
+ if not st.session_state.expenses.empty:
1047
+ st.subheader("πŸ“… Recent Expenses")
1048
+ recent_expenses = st.session_state.expenses.tail(5)
1049
+ st.dataframe(recent_expenses[['date', 'amount', 'category', 'description']])
1050
+ else:
1051
+ st.info("πŸ“ No recent expenses. Start by adding your first expense!")
1052
+
1053
+ # Budget status
1054
+ if not st.session_state.budgets.empty:
1055
+ st.subheader("πŸ“Š Budget Status")
1056
+ for _, budget in st.session_state.budgets.iterrows():
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:
1070
+ st.subheader("🎯 Savings Goals Progress")
1071
+ for _, goal in st.session_state.savings_goals.iterrows():
1072
+ progress = (goal['current_amount'] / goal['target_amount']) * 100 if goal['target_amount'] > 0 else 0
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
+
1082
+ def main():
1083
+ """
1084
+ Main application function with error handling
1085
+ """
1086
+ try:
1087
+ # Initialize session state
1088
+ if not initialize_session_state():
1089
+ st.error("❌ Failed to initialize application. Please refresh the page.")
1090
+ return
1091
+
1092
+ # Set page config
1093
+ st.set_page_config(
1094
+ page_title="πŸ’° Budget Tracker Pro",
1095
+ page_icon="πŸ’°",
1096
+ layout="wide",
1097
+ initial_sidebar_state="expanded"
1098
+ )
1099
+
1100
+ # Custom CSS for better UI
1101
+ st.markdown("""
1102
+ <style>
1103
+ .stApp {
1104
+ background-color: #f0f2f6;
1105
+ }
1106
+ .stMetric {
1107
+ background-color: white;
1108
+ padding: 10px;
1109
+ border-radius: 10px;
1110
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
1111
+ }
1112
+ .css-1d391kg {
1113
+ background-color: #262730;
1114
+ }
1115
+ </style>
1116
+ """, unsafe_allow_html=True)
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")
1124
+
1125
+ # Initialize current page in session state
1126
+ if 'current_page' not in st.session_state:
1127
+ st.session_state.current_page = "🏠 Dashboard"
1128
+
1129
+ menu = [
1130
+ "🏠 Dashboard",
1131
+ "🎀 Voice Expense",
1132
+ "πŸ“Έ OCR Receipts",
1133
+ "πŸ’° Budget",
1134
+ "🎯 Savings Goals",
1135
+ "🏷️ Categorization",
1136
+ "πŸ”” Alerts",
1137
+ "πŸ“Š Visualizations",
1138
+ "🧾 Receipts",
1139
+ "πŸ”’ Security",
1140
+ "🏦 Bank Integration"
1141
+ ]
1142
+
1143
+ # Page selection
1144
+ choice = st.sidebar.selectbox("Choose a section", menu,
1145
+ index=menu.index(st.session_state.current_page))
1146
+ st.session_state.current_page = choice
1147
+
1148
+ # Display notifications in sidebar
1149
+ if st.session_state.notifications:
1150
+ st.sidebar.subheader("πŸ”” Notifications")
1151
+ for notification in st.session_state.notifications[-3:]: # Show last 3
1152
+ if "🚨" in notification:
1153
+ st.sidebar.error(notification)
1154
+ elif "⚠️" in notification:
1155
+ st.sidebar.warning(notification)
1156
+
1157
+ # Route to appropriate function
1158
+ if choice == "🏠 Dashboard":
1159
+ main_dashboard()
1160
+ elif choice == "🎀 Voice Expense":
1161
+ voice_expense_recording()
1162
+ elif choice == "πŸ“Έ OCR Receipts":
1163
+ ocr_receipt_processing()
1164
+ elif choice == "πŸ’° Budget":
1165
+ create_budget()
1166
+ elif choice == "🎯 Savings Goals":
1167
+ set_savings_goals()
1168
+ elif choice == "🏷️ Categorization":
1169
+ spending_categorization()
1170
+ elif choice == "πŸ”” Alerts":
1171
+ alerts_and_notifications()
1172
+ elif choice == "πŸ“Š Visualizations":
1173
+ visualizations_and_summaries()
1174
+ elif choice == "🧾 Receipts":
1175
+ receipt_management()
1176
+ elif choice == "πŸ”’ Security":
1177
+ data_security_and_privacy()
1178
+ elif choice == "🏦 Bank Integration":
1179
+ bank_integration_placeholder()
1180
+
1181
+ # Footer
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()