Akshayram1 commited on
Commit
139af8c
·
verified ·
1 Parent(s): 8d30781

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +330 -0
app.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import seaborn as sns
5
+ import altair as alt
6
+ import google.generativeai as genai
7
+ from datetime import datetime
8
+ import os
9
+ import re
10
+ import json
11
+
12
+ # App title and configuration
13
+ st.set_page_config(page_title="Expense Tracker", layout="wide")
14
+
15
+ # Initialize session state
16
+ if 'expenses' not in st.session_state:
17
+ st.session_state.expenses = []
18
+ if 'df' not in st.session_state:
19
+ st.session_state.df = pd.DataFrame(columns=['Date', 'Category', 'Amount', 'Description'])
20
+ if 'chat_history' not in st.session_state:
21
+ st.session_state.chat_history = []
22
+
23
+ # Load Gemini API key from secrets
24
+ def configure_genai():
25
+ # For local development, use st.secrets
26
+ # For Hugging Face deployment, use environment variables
27
+ if 'GEMINI_API_KEY' in st.secrets:
28
+ api_key = st.secrets['GEMINI_API_KEY']
29
+ else:
30
+ api_key = os.environ.get('GEMINI_API_KEY')
31
+
32
+ if not api_key:
33
+ st.error("Gemini API key not found. Please add it to the secrets or environment variables.")
34
+ st.stop()
35
+
36
+ genai.configure(api_key=api_key)
37
+ return genai.GenerativeModel('gemini-2.0-flash')
38
+
39
+ model = configure_genai()
40
+
41
+ # Function to extract expense data using Gemini
42
+ def extract_expense_data(text):
43
+ prompt = f"""
44
+ Extract expense information from the following text.
45
+ Return a JSON object with these fields:
46
+ - date: in YYYY-MM-DD format (use today's date if not specified)
47
+ - category: the expense category (e.g., food, transport, entertainment)
48
+ - amount: the numerical amount (just the number, no currency symbol)
49
+ - description: brief description of the expense
50
+
51
+ Example output format:
52
+ {{
53
+ "date": "2025-03-19",
54
+ "category": "food",
55
+ "amount": 25.50,
56
+ "description": "lunch at cafe"
57
+ }}
58
+
59
+ If multiple expenses are mentioned, return an array of such objects.
60
+
61
+ Text: {text}
62
+ """
63
+
64
+ try:
65
+ response = model.generate_content(prompt)
66
+ response_text = response.text
67
+
68
+ # Extract JSON from the response
69
+ json_match = re.search(r'```json\n(.*?)```', response_text, re.DOTALL)
70
+ if json_match:
71
+ json_str = json_match.group(1)
72
+ else:
73
+ # If no code block, try to find JSON directly
74
+ json_str = response_text
75
+
76
+ # Parse the JSON
77
+ data = json.loads(json_str)
78
+ return data
79
+ except Exception as e:
80
+ st.error(f"Error extracting expense data: {e}")
81
+ return None
82
+
83
+ # Function to add expenses to the dataframe
84
+ def add_expense_to_df(expense_data):
85
+ if isinstance(expense_data, list):
86
+ # Handle multiple expenses
87
+ for expense in expense_data:
88
+ add_single_expense(expense)
89
+ else:
90
+ # Handle single expense
91
+ add_single_expense(expense_data)
92
+
93
+ # Sort by date
94
+ st.session_state.df = st.session_state.df.sort_values(by='Date', ascending=False)
95
+
96
+ def add_single_expense(expense):
97
+ # Convert amount to float
98
+ try:
99
+ amount = float(expense['amount'])
100
+ except:
101
+ amount = 0.0
102
+
103
+ # Create a new row
104
+ new_row = pd.DataFrame({
105
+ 'Date': [expense.get('date', datetime.now().strftime('%Y-%m-%d'))],
106
+ 'Category': [expense.get('category', 'Other')],
107
+ 'Amount': [amount],
108
+ 'Description': [expense.get('description', '')]
109
+ })
110
+
111
+ # Append to the dataframe
112
+ st.session_state.df = pd.concat([st.session_state.df, new_row], ignore_index=True)
113
+
114
+ # Function to get AI insights about expenses
115
+ def get_expense_insights(query):
116
+ if st.session_state.df.empty:
117
+ return "No expense data available yet. Please add some expenses first."
118
+
119
+ # Convert dataframe to string representation
120
+ df_str = st.session_state.df.to_string()
121
+
122
+ prompt = f"""
123
+ Here is a dataset of expenses:
124
+ {df_str}
125
+
126
+ User query: {query}
127
+
128
+ Please analyze this expense data and answer the query.
129
+ Provide your analysis in a clear and concise way.
130
+ If the query is about visualizations, describe what kind of chart would be helpful.
131
+ """
132
+
133
+ try:
134
+ response = model.generate_content(prompt)
135
+ return response.text
136
+ except Exception as e:
137
+ return f"Error getting insights: {e}"
138
+
139
+ # Function to create visualizations
140
+ def create_visualizations():
141
+ if st.session_state.df.empty:
142
+ st.info("Add some expenses to see visualizations")
143
+ return
144
+
145
+ # Create a copy of the dataframe for visualization
146
+ df = st.session_state.df.copy()
147
+
148
+ # Ensure Date is datetime
149
+ df['Date'] = pd.to_datetime(df['Date'])
150
+
151
+ # Create tabs for different visualizations
152
+ tab1, tab2, tab3 = st.tabs(["Expenses by Category", "Expenses Over Time", "Recent Expenses"])
153
+
154
+ with tab1:
155
+ st.subheader("Expenses by Category")
156
+ category_totals = df.groupby('Category')['Amount'].sum().reset_index()
157
+
158
+ # Create a pie chart
159
+ fig, ax = plt.subplots(figsize=(8, 8))
160
+ ax.pie(category_totals['Amount'], labels=category_totals['Category'], autopct='%1.1f%%')
161
+ ax.set_title('Expenses by Category')
162
+ st.pyplot(fig)
163
+
164
+ # Create a bar chart
165
+ category_chart = alt.Chart(category_totals).mark_bar().encode(
166
+ x=alt.X('Category:N', sort='-y'),
167
+ y=alt.Y('Amount:Q'),
168
+ color='Category:N'
169
+ ).properties(
170
+ title='Total Expenses by Category'
171
+ )
172
+ st.altair_chart(category_chart, use_container_width=True)
173
+
174
+ with tab2:
175
+ st.subheader("Expenses Over Time")
176
+ # Group by date and sum amounts
177
+ daily_totals = df.groupby(df['Date'].dt.date)['Amount'].sum().reset_index()
178
+
179
+ # Create a line chart
180
+ time_chart = alt.Chart(daily_totals).mark_line(point=True).encode(
181
+ x='Date:T',
182
+ y='Amount:Q',
183
+ tooltip=['Date:T', 'Amount:Q']
184
+ ).properties(
185
+ title='Daily Expenses Over Time'
186
+ )
187
+ st.altair_chart(time_chart, use_container_width=True)
188
+
189
+ with tab3:
190
+ st.subheader("Recent Expenses")
191
+ # Sort by date and get the last 10 expenses
192
+ recent = df.sort_values('Date', ascending=False).head(10)
193
+
194
+ # Create a bar chart
195
+ recent_chart = alt.Chart(recent).mark_bar().encode(
196
+ x=alt.X('Description:N', sort='-y'),
197
+ y='Amount:Q',
198
+ color='Category:N',
199
+ tooltip=['Date:T', 'Category:N', 'Amount:Q', 'Description:N']
200
+ ).properties(
201
+ title='Most Recent Expenses'
202
+ )
203
+ st.altair_chart(recent_chart, use_container_width=True)
204
+
205
+ # App layout
206
+ st.title("💰 Expense Tracker with AI")
207
+
208
+ # Sidebar for app navigation
209
+ page = st.sidebar.radio("Navigation", ["Add Expenses", "View & Analyze", "Chat with your Data"])
210
+
211
+ if page == "Add Expenses":
212
+ st.header("Add Your Expenses")
213
+ st.write("Describe your expenses in natural language, and AI will extract the details.")
214
+
215
+ with st.form("expense_form"):
216
+ user_input = st.text_area(
217
+ "Enter your expenses:",
218
+ height=100,
219
+ placeholder="Example: I spent $25 on lunch today, $15 on transport yesterday, and $50 on groceries on March 15th"
220
+ )
221
+ submit_button = st.form_submit_button("Add Expenses")
222
+
223
+ if submit_button and user_input:
224
+ with st.spinner("Processing your expenses..."):
225
+ expense_data = extract_expense_data(user_input)
226
+
227
+ if expense_data:
228
+ add_expense_to_df(expense_data)
229
+ st.success("Expenses added successfully!")
230
+ st.write("Extracted information:")
231
+ st.json(expense_data)
232
+ else:
233
+ st.error("Failed to extract expense data. Please try again with a clearer description.")
234
+
235
+ # Show the current expenses
236
+ if not st.session_state.df.empty:
237
+ st.subheader("Your Recent Expenses")
238
+ st.dataframe(st.session_state.df.sort_values(by='Date', ascending=False), use_container_width=True)
239
+
240
+ elif page == "View & Analyze":
241
+ st.header("Your Expense Data")
242
+
243
+ # Show the current expenses as a table
244
+ if not st.session_state.df.empty:
245
+ st.dataframe(st.session_state.df.sort_values(by='Date', ascending=False), use_container_width=True)
246
+
247
+ # Add download button
248
+ csv = st.session_state.df.to_csv(index=False)
249
+ st.download_button(
250
+ label="Download CSV",
251
+ data=csv,
252
+ file_name="expenses.csv",
253
+ mime="text/csv"
254
+ )
255
+
256
+ # Show summary statistics
257
+ st.subheader("Summary Statistics")
258
+ col1, col2, col3 = st.columns(3)
259
+ with col1:
260
+ st.metric("Total Expenses", f"${st.session_state.df['Amount'].sum():.2f}")
261
+ with col2:
262
+ st.metric("Average Expense", f"${st.session_state.df['Amount'].mean():.2f}")
263
+ with col3:
264
+ st.metric("Number of Expenses", f"{len(st.session_state.df)}")
265
+
266
+ # Create visualizations
267
+ st.subheader("Visualizations")
268
+ create_visualizations()
269
+ else:
270
+ st.info("No expense data available yet. Please add some expenses first.")
271
+
272
+ elif page == "Chat with your Data":
273
+ st.header("Chat with Your Expense Data")
274
+
275
+ if st.session_state.df.empty:
276
+ st.info("No expense data available yet. Please add some expenses first.")
277
+ else:
278
+ st.write("Ask questions about your expenses to get insights.")
279
+
280
+ # Display chat history
281
+ for message in st.session_state.chat_history:
282
+ with st.chat_message(message["role"]):
283
+ st.write(message["content"])
284
+
285
+ # Get user input
286
+ user_query = st.chat_input("Ask about your expenses...")
287
+
288
+ if user_query:
289
+ # Add user message to chat history
290
+ st.session_state.chat_history.append({"role": "user", "content": user_query})
291
+
292
+ # Display user message
293
+ with st.chat_message("user"):
294
+ st.write(user_query)
295
+
296
+ # Get AI response
297
+ with st.spinner("Thinking..."):
298
+ response = get_expense_insights(user_query)
299
+
300
+ # Add AI response to chat history
301
+ st.session_state.chat_history.append({"role": "assistant", "content": response})
302
+
303
+ # Display AI response
304
+ with st.chat_message("assistant"):
305
+ st.write(response)
306
+
307
+ # Add instructions for Hugging Face deployment in the sidebar
308
+ with st.sidebar.expander("Deployment Instructions"):
309
+ st.write("""
310
+ ### How to deploy to Hugging Face:
311
+
312
+ 1. Save this code as `app.py`
313
+ 2. Create a `requirements.txt` file with these dependencies:
314
+ ```
315
+ streamlit
316
+ pandas
317
+ matplotlib
318
+ seaborn
319
+ altair
320
+ google-generativeai
321
+ ```
322
+ 3. Create a `README.md` file describing your app
323
+ 4. Add your Gemini API key to your Hugging Face Space secrets with the name `GEMINI_API_KEY`
324
+ 5. Push your code to a GitHub repository
325
+ 6. Create a new Hugging Face Space, select Streamlit as the SDK, and connect your GitHub repository
326
+ """)
327
+
328
+ # Bottom credits
329
+ st.sidebar.markdown("---")
330
+ st.sidebar.caption("Built with Streamlit and Gemini AI")