Download app.py from arkapravac366/Eminem-Data-Entry: direct link, hf CLI and curl.
- Browser
- Download file 36.6 kB
-
https://huggingface.co/arkapravac366/Eminem-Data-Entry/resolve/main/app.py
- Command line
-
hf download hf://arkapravac366/Eminem-Data-Entry/app.py
-
curl -L -o app.py https://huggingface.co/arkapravac366/Eminem-Data-Entry/resolve/main/app.py
36.6 kB
| 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>', configure_scroll) | |
| # Configure canvas resize | |
| def on_canvas_configure(event): | |
| canvas.itemconfig(canvas.find_withtag('all')[0], width=event.width-40) | |
| canvas.bind('<Configure>', 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('<Button-3>', 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('<Return>', 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() |