import tkinter as tk from tkinter import ttk, messagebox import sqlite3 from datetime import datetime from chatbot import ChatBot import os import pandas as pd from style import apply_modern_style, create_custom_font, style_text_widget from PIL import Image, ImageTk class DonationTracker: def __init__(self): self.root = tk.Tk() self.root.title("GiveFlow AI : brought to you by Arkaprava") self.root.geometry("800x600") # Create splash screen self.splash = tk.Toplevel(self.root) self.splash.title("") self.splash.geometry("800x600") self.splash.overrideredirect(True) # Remove window decorations # Load and display splash image img = Image.open("img.png") img = img.resize((800, 600), Image.Resampling.LANCZOS) self.splash_img = ImageTk.PhotoImage(img) splash_label = tk.Label(self.splash, image=self.splash_img) splash_label.pack(fill='both', expand=True) # Center splash screen self.splash.update_idletasks() width = self.splash.winfo_width() height = self.splash.winfo_height() x = (self.splash.winfo_screenwidth() // 2) - (width // 2) y = (self.splash.winfo_screenheight() // 2) - (height // 2) self.splash.geometry(f"{width}x{height}+{x}+{y}") # Hide main window initially self.root.withdraw() # Schedule splash screen removal and app initialization self.root.after(5000, self._remove_splash) def _remove_splash(self): self.splash.destroy() self.root.deiconify() self.loading_label = ttk.Label(self.root, text="Initializing application...") self.loading_label.pack(expand=True) self.root.after(100, self._initialize_app) def _initialize_app(self): # Initialize database in background import threading db_thread = threading.Thread(target=self.init_database) db_thread.start() # Initialize chatbot self.chatbot = ChatBot() # Apply modern styling apply_modern_style(self.root) self.custom_fonts = create_custom_font() # Create main container self.main_container = ttk.Notebook(self.root, style='Modern.TNotebook') # Create tabs self.donation_frame = ttk.Frame(self.main_container, style='Modern.TFrame') self.chat_frame = ttk.Frame(self.main_container, style='Modern.TFrame') self.reports_frame = ttk.Frame(self.main_container, style='Modern.TFrame') # Remove loading label self.loading_label.destroy() # Setup UI self.main_container.pack(expand=True, fill='both', padx=20, pady=10) self.main_container.add(self.donation_frame, text='Donations') self.main_container.add(self.chat_frame, text='Chat Assistant') self.main_container.add(self.reports_frame, text='Reports') # Setup UI components self.setup_donation_ui() self.setup_chat_ui() self.setup_reports_ui() def init_database(self): # Create database and tables if they don't exist with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS donations ( id INTEGER PRIMARY KEY AUTOINCREMENT, donor_name TEXT NOT NULL, amount REAL NOT NULL, category TEXT NOT NULL, date TEXT NOT NULL, notes TEXT, is_recurring BOOLEAN DEFAULT 0, recurring_interval TEXT, next_donation_date TEXT ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS donor_profiles ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT, phone TEXT, address TEXT, preferred_category TEXT, total_donations REAL DEFAULT 0, last_donation_date TEXT, notification_preferences TEXT ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS donation_goals ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, target_amount REAL NOT NULL, current_amount REAL DEFAULT 0, start_date TEXT NOT NULL, end_date TEXT, status TEXT DEFAULT 'active' ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS chat_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_message TEXT NOT NULL, bot_response TEXT NOT NULL, timestamp TEXT NOT NULL ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS email_notifications ( id INTEGER PRIMARY KEY AUTOINCREMENT, donor_id INTEGER, type TEXT NOT NULL, message TEXT NOT NULL, status TEXT DEFAULT 'pending', created_at TEXT NOT NULL, sent_at TEXT, FOREIGN KEY (donor_id) REFERENCES donor_profiles(id) ) ''') conn.commit() def setup_donation_ui(self): # Create canvas for scrollable content canvas = tk.Canvas(self.donation_frame) canvas.pack(fill='both', expand=True, padx=20, pady=10) # Add scrollbar to canvas scrollbar = ttk.Scrollbar(self.donation_frame, orient='vertical', command=canvas.yview) scrollbar.pack(side='right', fill='y') # Configure canvas canvas.configure(yscrollcommand=scrollbar.set) # Create main frame inside canvas main_frame = ttk.Frame(canvas) canvas.create_window((0, 0), window=main_frame, anchor='nw', width=canvas.winfo_width()) # Donation entry form entry_frame = ttk.LabelFrame(main_frame, text="New Donation", padding=15, style='Modern.TLabelframe') entry_frame.pack(fill='x', padx=20, pady=10) # Donor Information donor_info_frame = ttk.LabelFrame(entry_frame, text="Donor Information", padding=10) donor_info_frame.grid(row=0, column=0, columnspan=2, sticky='ew', padx=5, pady=5) # Donor Name ttk.Label(donor_info_frame, text="Name:").grid(row=0, column=0, padx=5, pady=3, sticky='e') self.donor_name = ttk.Entry(donor_info_frame, style='Modern.TEntry') self.donor_name.grid(row=0, column=1, padx=5, pady=3, sticky='ew') # Donor Email ttk.Label(donor_info_frame, text="Email:").grid(row=0, column=2, padx=5, pady=3, sticky='e') self.donor_email = ttk.Entry(donor_info_frame, style='Modern.TEntry') self.donor_email.grid(row=0, column=3, padx=5, pady=3, sticky='ew') # Donor Phone ttk.Label(donor_info_frame, text="Phone:").grid(row=1, column=0, padx=5, pady=3, sticky='e') self.donor_phone = ttk.Entry(donor_info_frame, style='Modern.TEntry') self.donor_phone.grid(row=1, column=1, padx=5, pady=3, sticky='ew') # Donor Address ttk.Label(donor_info_frame, text="Address:").grid(row=1, column=2, padx=5, pady=3, sticky='e') self.donor_address = ttk.Entry(donor_info_frame, style='Modern.TEntry') self.donor_address.grid(row=1, column=3, padx=5, pady=3, sticky='ew') # Donation Details donation_details_frame = ttk.LabelFrame(entry_frame, text="Donation Details", padding=10) donation_details_frame.grid(row=1, column=0, columnspan=2, sticky='ew', padx=5, pady=5) # Amount ttk.Label(donation_details_frame, text="Amount:").grid(row=0, column=0, padx=5, pady=3, sticky='e') self.amount = ttk.Entry(donation_details_frame, style='Modern.TEntry') self.amount.grid(row=0, column=1, padx=5, pady=3, sticky='ew') # Category ttk.Label(donation_details_frame, text="Category:").grid(row=0, column=2, padx=5, pady=3, sticky='e') self.category = ttk.Combobox(donation_details_frame, values=['General', 'Project', 'Emergency', 'Other'], style='Modern.TCombobox') self.category.grid(row=0, column=3, padx=5, pady=3, sticky='ew') # Recurring Donation self.is_recurring = tk.BooleanVar() ttk.Checkbutton(donation_details_frame, text="Recurring Donation", variable=self.is_recurring, command=self.toggle_recurring_options).grid(row=1, column=0, columnspan=2, padx=5, pady=3, sticky='w') # Recurring Options Frame (initially hidden) self.recurring_frame = ttk.Frame(donation_details_frame) self.recurring_frame.grid(row=2, column=0, columnspan=4, sticky='ew') self.recurring_frame.grid_remove() # Interval ttk.Label(self.recurring_frame, text="Interval:").grid(row=0, column=0, padx=5, pady=3, sticky='e') self.recurring_interval = ttk.Combobox(self.recurring_frame, values=['Weekly', 'Monthly', 'Quarterly', 'Yearly'], style='Modern.TCombobox') self.recurring_interval.grid(row=0, column=1, padx=5, pady=3, sticky='ew') # Notes ttk.Label(entry_frame, text="Notes:").grid(row=2, column=0, padx=10, pady=8, sticky='ne') self.notes = tk.Text(entry_frame, height=3, width=30) self.notes.grid(row=2, column=1, padx=10, pady=8, sticky='ew') style_text_widget(self.notes) # Submit button with improved visibility submit_btn = ttk.Button(entry_frame, text="Submit Donation", command=self.submit_donation, style='Modern.TButton') submit_btn.grid(row=4, column=0, columnspan=2, pady=15, sticky='ew') submit_btn.configure(padding=(20, 10)) # Increase button size # Configure grid columns entry_frame.columnconfigure(1, weight=1) # Configure canvas scrolling def configure_scroll(event): canvas.configure(scrollregion=canvas.bbox('all')) main_frame.bind('', configure_scroll) # Configure canvas resize def on_canvas_configure(event): canvas.itemconfig(canvas.find_withtag('all')[0], width=event.width-40) canvas.bind('', on_canvas_configure) # Recent donations list list_frame = ttk.LabelFrame(self.donation_frame, text="Recent Donations", padding=15, style='Modern.TLabelframe') list_frame.pack(fill='both', expand=True, padx=20, pady=10) # Treeview for donations self.donation_tree = ttk.Treeview(list_frame, columns=('Donor', 'Amount', 'Category', 'Date', 'Notes'), show='headings', style='Modern.Treeview') self.donation_tree.heading('Donor', text='Donor') self.donation_tree.heading('Amount', text='Amount') self.donation_tree.heading('Category', text='Category') self.donation_tree.heading('Date', text='Date') self.donation_tree.heading('Notes', text='Notes') # Add scrollbar scrollbar = ttk.Scrollbar(list_frame, orient='vertical', command=self.donation_tree.yview) self.donation_tree.configure(yscrollcommand=scrollbar.set) # Pack treeview and scrollbar self.donation_tree.pack(side='left', fill='both', expand=True) scrollbar.pack(side='right', fill='y') # Add Delete button delete_btn = ttk.Button(list_frame, text="Delete Selected", command=self.delete_donation, style='Modern.TButton') delete_btn.pack(pady=10) # Add right-click menu self.context_menu = tk.Menu(self.root, tearoff=0) self.context_menu.add_command(label="Delete", command=self.delete_donation) self.donation_tree.bind('', self.show_context_menu) def setup_chat_ui(self): # Chat interface chat_container = ttk.Frame(self.chat_frame, style='Modern.TFrame') chat_container.pack(fill='both', expand=True, padx=20, pady=10) self.chat_history = tk.Text(chat_container, wrap=tk.WORD, state='disabled') style_text_widget(self.chat_history) self.chat_history.pack(fill='both', expand=True, pady=(0, 10)) # Message entry entry_frame = ttk.Frame(chat_container, style='Modern.TFrame') entry_frame.pack(fill='x') self.message_entry = ttk.Entry(entry_frame, style='Modern.TEntry') self.message_entry.pack(side='left', fill='x', expand=True, padx=(0, 10)) send_btn = ttk.Button(entry_frame, text="Send", command=self.send_message, style='Modern.TButton') send_btn.pack(side='right') # Bind Enter key to send message self.message_entry.bind('', lambda e: self.send_message()) def setup_reports_ui(self): # Reports interface controls_frame = ttk.Frame(self.reports_frame, style='Modern.TFrame') controls_frame.pack(fill='x', padx=20, pady=10) # Filters frame filters_frame = ttk.LabelFrame(controls_frame, text="Filters", padding=10) filters_frame.pack(fill='x', padx=5, pady=5) # Date range ttk.Label(filters_frame, text="Date Range:").grid(row=0, column=0, padx=5, pady=3) self.date_range = ttk.Combobox(filters_frame, values=['Last 7 Days', 'Last 30 Days', 'Last 90 Days', 'This Year', 'All Time'], style='Modern.TCombobox') self.date_range.grid(row=0, column=1, padx=5, pady=3) self.date_range.set('All Time') # Category filter ttk.Label(filters_frame, text="Category:").grid(row=0, column=2, padx=5, pady=3) self.category_filter = ttk.Combobox(filters_frame, values=['All Categories', 'General', 'Project', 'Emergency', 'Other'], style='Modern.TCombobox') self.category_filter.grid(row=0, column=3, padx=5, pady=3) self.category_filter.set('All Categories') # Donor filter ttk.Label(filters_frame, text="Donor:").grid(row=0, column=4, padx=5, pady=3) self.donor_filter = ttk.Entry(filters_frame, style='Modern.TEntry') self.donor_filter.grid(row=0, column=5, padx=5, pady=3) # Buttons frame button_frame = ttk.Frame(controls_frame, style='Modern.TFrame') button_frame.pack(fill='x', padx=5, pady=5) ttk.Button(button_frame, text="Generate Report", command=self.generate_report, style='Modern.TButton').pack(side='left', padx=5) ttk.Button(button_frame, text="Export to Excel", command=self.export_to_excel, style='Modern.TButton').pack(side='left', padx=5) ttk.Button(button_frame, text="Donor Analytics", command=self.show_donor_analytics, style='Modern.TButton').pack(side='left', padx=5) ttk.Button(button_frame, text="Donation Trends", command=self.show_donation_trends, style='Modern.TButton').pack(side='left', padx=5) # Create notebook for different views self.report_notebook = ttk.Notebook(self.reports_frame, style='Modern.TNotebook') self.report_notebook.pack(fill='both', expand=True, padx=20, pady=10) # Summary tab self.summary_frame = ttk.Frame(self.report_notebook) self.report_notebook.add(self.summary_frame, text='Summary') self.report_text = tk.Text(self.summary_frame, wrap=tk.WORD, state='disabled') style_text_widget(self.report_text) self.report_text.pack(fill='both', expand=True) # Trends tab self.trends_frame = ttk.Frame(self.report_notebook) self.report_notebook.add(self.trends_frame, text='Trends') # Analytics tab self.analytics_frame = ttk.Frame(self.report_notebook) self.report_notebook.add(self.analytics_frame, text='Analytics') def toggle_recurring_options(self): if self.is_recurring.get(): self.recurring_frame.grid() else: self.recurring_frame.grid_remove() def submit_donation(self): try: # Get donor information donor = self.donor_name.get() email = self.donor_email.get() phone = self.donor_phone.get() address = self.donor_address.get() # Get donation details amount = float(self.amount.get()) category = self.category.get() notes = self.notes.get('1.0', 'end-1c') date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Get recurring donation information is_recurring = self.is_recurring.get() recurring_interval = self.recurring_interval.get() if is_recurring else None # Validate required fields if not donor or not amount or not category: messagebox.showerror("Error", "Please fill in all required fields") return # Validate recurring donation fields if is_recurring and not recurring_interval: messagebox.showerror("Error", "Please select a recurring interval") return # Calculate next donation date for recurring donations next_donation_date = None if is_recurring: next_date = datetime.now() if recurring_interval == 'Weekly': next_date = next_date.replace(day=next_date.day + 7) elif recurring_interval == 'Monthly': if next_date.month == 12: next_date = next_date.replace(year=next_date.year + 1, month=1) else: next_date = next_date.replace(month=next_date.month + 1) elif recurring_interval == 'Quarterly': if next_date.month > 9: next_date = next_date.replace(year=next_date.year + 1, month=(next_date.month + 3) % 12) else: next_date = next_date.replace(month=next_date.month + 3) elif recurring_interval == 'Yearly': next_date = next_date.replace(year=next_date.year + 1) next_donation_date = next_date.strftime('%Y-%m-%d %H:%M:%S') # Save to database with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() # Update or create donor profile cursor.execute(''' INSERT OR REPLACE INTO donor_profiles (name, email, phone, address, preferred_category, last_donation_date) VALUES (?, ?, ?, ?, ?, ?) ''', (donor, email, phone, address, category, date)) # Get donor_id for the notification donor_id = cursor.lastrowid # Insert donation with next_donation_date cursor.execute(''' INSERT INTO donations (donor_name, amount, category, date, notes, is_recurring, recurring_interval, next_donation_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', (donor, amount, category, date, notes, is_recurring, recurring_interval, next_donation_date)) # Create notification for large donations if amount >= 1000: # Threshold for significant donations cursor.execute(''' INSERT INTO email_notifications (donor_id, type, message, created_at) VALUES (?, ?, ?, ?) ''', (donor_id, 'large_donation', f'Large donation received: ${amount:.2f} from {donor}', date)) conn.commit() # Clear form self.donor_name.delete(0, 'end') self.donor_email.delete(0, 'end') self.donor_phone.delete(0, 'end') self.donor_address.delete(0, 'end') self.amount.delete(0, 'end') self.category.set('') self.notes.delete('1.0', 'end') self.is_recurring.set(False) self.recurring_interval.set('') self.toggle_recurring_options() # Update donation list self.update_donation_list() messagebox.showinfo("Success", "Donation recorded successfully!") except ValueError: messagebox.showerror("Error", "Please enter a valid amount") except Exception as e: messagebox.showerror("Error", f"An error occurred: {str(e)}") def update_donation_list(self): # Clear current items for item in self.donation_tree.get_children(): self.donation_tree.delete(item) # Fetch and display recent donations with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() cursor.execute(''' SELECT donor_name, amount, category, date, notes FROM donations ORDER BY date DESC LIMIT 50 ''') for row in cursor.fetchall(): self.donation_tree.insert('', 'end', values=row) def send_message(self): message = self.message_entry.get().strip() if not message: return # Clear message entry self.message_entry.delete(0, 'end') # Add user message to chat history self.chat_history.configure(state='normal') self.chat_history.insert('end', f"You: {message}\n") # Get bot response response = self.chatbot.get_response(message) # Add bot response to chat history self.chat_history.insert('end', f"Assistant: {response}\n\n") self.chat_history.configure(state='disabled') self.chat_history.see('end') # Save chat history to database with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() cursor.execute(''' INSERT INTO chat_history (user_message, bot_response, timestamp) VALUES (?, ?, ?) ''', (message, response, datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) conn.commit() def generate_report(self): try: with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() # Get total donations cursor.execute("SELECT COUNT(*), SUM(amount) FROM donations") count, total = cursor.fetchone() # Get category breakdown cursor.execute(''' SELECT category, COUNT(*), SUM(amount) FROM donations GROUP BY category ''') categories = cursor.fetchall() # Generate report text report = f"Donation Summary Report\n" report += f"Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n" report += f"Total Donations: {count if count else 0}\n" total_amount = float(total) if total else 0.00 report += f"Total Amount: ${total_amount:.2f}\n\n" report += "Category Breakdown:\n" for category, cat_count, cat_total in categories: cat_total = float(cat_total) if cat_total else 0.00 report += f"{category}:\n" report += f" Count: {cat_count}\n" report += f" Total: ${cat_total:.2f}\n" # Update report text widget self.report_text.configure(state='normal') self.report_text.delete('1.0', 'end') self.report_text.insert('1.0', report) self.report_text.configure(state='disabled') except Exception as e: messagebox.showerror("Error", f"Failed to generate report: {str(e)}") def show_donor_analytics(self): try: with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() # Get donor statistics cursor.execute(''' SELECT donor_name, COUNT(*) as donation_count, SUM(amount) as total_amount, AVG(amount) as avg_amount, MAX(date) as last_donation FROM donations GROUP BY donor_name ORDER BY total_amount DESC ''') donor_stats = cursor.fetchall() # Generate analytics report report = "Donor Analytics Report\n\n" report += "Top Donors:\n" for donor, count, total, avg, last_date in donor_stats: report += f"\nDonor: {donor}\n" report += f"Total Donations: {count}\n" report += f"Total Amount: ${total:.2f}\n" report += f"Average Donation: ${avg:.2f}\n" report += f"Last Donation: {last_date}\n" # Show in analytics tab analytics_text = tk.Text(self.analytics_frame, wrap=tk.WORD) style_text_widget(analytics_text) analytics_text.pack(fill='both', expand=True) analytics_text.insert('1.0', report) analytics_text.configure(state='disabled') except Exception as e: messagebox.showerror("Error", f"Failed to generate donor analytics: {str(e)}") def show_donation_trends(self): try: with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() # Get monthly trends cursor.execute(''' SELECT strftime('%Y-%m', date) as month, COUNT(*) as donation_count, SUM(amount) as total_amount, category FROM donations GROUP BY month, category ORDER BY month DESC ''') trends = cursor.fetchall() # Generate trends report report = "Donation Trends Report\n\n" report += "Monthly Breakdown:\n" current_month = None for month, count, total, category in trends: if month != current_month: current_month = month report += f"\n{month}:\n" report += f" {category}: ${total:.2f} ({count} donations)\n" # Show in trends tab trends_text = tk.Text(self.trends_frame, wrap=tk.WORD) style_text_widget(trends_text) trends_text.pack(fill='both', expand=True) trends_text.insert('1.0', report) trends_text.configure(state='disabled') except Exception as e: messagebox.showerror("Error", f"Failed to generate donation trends: {str(e)}") def export_to_excel(self): try: with sqlite3.connect('donations.db') as conn: # Create a pandas DataFrame from the donations table df = pd.read_sql_query(''' SELECT donor_name as 'Donor Name', amount as 'Amount', category as 'Category', date as 'Date', notes as 'Notes' FROM donations ORDER BY date DESC ''', conn) # Convert date column to datetime df['Date'] = pd.to_datetime(df['Date']) # Calculate summary statistics total_donations = df['Amount'].sum() total_count = len(df) category_summary = df.groupby('Category').agg({ 'Amount': ['count', 'sum'] }).round(2) category_summary.columns = ['Count', 'Total Amount'] # Create visualizations import matplotlib.pyplot as plt import io # 1. Category Distribution Pie Chart plt.figure(figsize=(10, 6)) plt.pie(category_summary['Total Amount'], labels=category_summary.index, autopct='%1.1f%%') plt.title('Donation Distribution by Category') category_pie = io.BytesIO() plt.savefig(category_pie) plt.close() # 2. Monthly Trends Line Graph monthly_data = df.groupby(df['Date'].dt.to_period('M')).agg({ 'Amount': 'sum' }).reset_index() monthly_data['Date'] = monthly_data['Date'].astype(str) plt.figure(figsize=(12, 6)) plt.plot(monthly_data['Date'], monthly_data['Amount'], marker='o') plt.title('Monthly Donation Trends') plt.xticks(rotation=45) plt.tight_layout() trends_line = io.BytesIO() plt.savefig(trends_line) plt.close() # 3. Top Donors Bar Chart top_donors = df.groupby('Donor Name')['Amount'].sum().nlargest(10) plt.figure(figsize=(12, 6)) plt.bar(top_donors.index, top_donors.values) plt.title('Top 10 Donors') plt.xticks(rotation=45) plt.tight_layout() donors_bar = io.BytesIO() plt.savefig(donors_bar) plt.close() # Get current timestamp for filename timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') filename = f'donation_report_{timestamp}.xlsx' # Create Excel writer object with pd.ExcelWriter(filename, engine='openpyxl') as writer: # Write main donation data df.to_excel(writer, sheet_name='Donations', index=False) # Write summary sheet summary_df = pd.DataFrame({ 'Metric': ['Total Donations', 'Total Amount'], 'Value': [total_count, f'${total_donations:.2f}'] }) summary_df.to_excel(writer, sheet_name='Summary', index=False) # Write category breakdown category_summary.to_excel(writer, sheet_name='Category Breakdown') # Add visualizations to the workbook workbook = writer.book # Add graphs worksheet graphs_sheet = workbook.create_sheet('Graphs') # Insert the charts from openpyxl.drawing.image import Image # Category Distribution Pie Chart img_pie = Image(category_pie) graphs_sheet.add_image(img_pie, 'A1') # Monthly Trends Line Graph img_line = Image(trends_line) graphs_sheet.add_image(img_line, 'A20') # Top Donors Bar Chart img_bar = Image(donors_bar) graphs_sheet.add_image(img_bar, 'A40') # Auto-adjust column widths for sheet_name in writer.sheets: worksheet = writer.sheets[sheet_name] if sheet_name != 'Graphs': # Skip graphs sheet for column in worksheet.columns: max_length = 0 column = [cell for cell in column] for cell in column: try: if len(str(cell.value)) > max_length: max_length = len(str(cell.value)) except: pass adjusted_width = (max_length + 2) worksheet.column_dimensions[column[0].column_letter].width = adjusted_width messagebox.showinfo("Success", f"Report exported successfully as '{filename}'") except Exception as e: messagebox.showerror("Error", f"Failed to export report: {str(e)}") def run(self): # Start the application self.root.mainloop() def delete_donation(self): selected_items = self.donation_tree.selection() if not selected_items: messagebox.showwarning("Warning", "Please select a donation to delete.") return if not messagebox.askyesno("Confirm Delete", "Are you sure you want to delete the selected donation(s)?"): return try: with sqlite3.connect('donations.db') as conn: cursor = conn.cursor() for item in selected_items: # Get the date from the selected item values = self.donation_tree.item(item)['values'] date = values[3] # Date is the fourth column # Delete from database cursor.execute('DELETE FROM donations WHERE date = ?', (date,)) # Remove from treeview self.donation_tree.delete(item) conn.commit() messagebox.showinfo("Success", "Selected donation(s) deleted successfully!") except Exception as e: messagebox.showerror("Error", f"Failed to delete donation(s): {str(e)}") def show_context_menu(self, event): # Show context menu on right-click try: self.context_menu.tk_popup(event.x_root, event.y_root) finally: self.context_menu.grab_release() if __name__ == "__main__": app = DonationTracker() app.run() def main(): print("Welcome to the Donation Management System!") print("Type 'exit' to quit the application.") chatbot = ChatBot() while True: try: user_input = input("\nYou: ").strip() if user_input.lower() == 'exit': print("\nThank you for using the Donation Management System. Goodbye!") break if not user_input: continue response = chatbot.get_response(user_input) print(f"\nAssistant: {response}") except KeyboardInterrupt: print("\n\nExiting the application...") break except Exception as e: print(f"\nAn error occurred: {str(e)}") print("Please try again.") if __name__ == '__main__': main()