Information Technology Grimoire

Version .0.0.1

IT Notes from various projects because I forget, and hopefully they help you too.

Python3 IRA Calculator

yes, calculators are boring. I wanted one anyway.

Demo

Demo

Python Source Code

import tkinter as tk
from tkinter import ttk
import pandas as pd
from decimal import Decimal, ROUND_HALF_UP

def calculate_growth():
    try:
        # User inputs - convert to Decimal for precise calculation
        monthly_contribution = Decimal(monthly_contribution_entry.get())
        overhead_rate = Decimal(overhead_rate_entry.get()) / Decimal('100')
        annual_rate = Decimal(interest_rate_entry.get()) / Decimal('100')
        monthly_rate = annual_rate / Decimal('12')
        years = int(years_entry.get())
        
        # Variables for calculations
        table_data = []  # Yearly summary
        monthly_detail = []  # Monthly breakdown
        total_contribution = Decimal('0')
        total_overhead = Decimal('0')
        cash_value = Decimal('0')
        
        # Monthly compounding logic with detailed tracking
        for year in range(1, years + 1):
            yearly_interest = Decimal('0')
            
            for month in range(1, 13):
                # Add monthly contribution
                total_contribution += monthly_contribution
                
                # Calculate and add overhead
                monthly_overhead = monthly_contribution * overhead_rate
                total_overhead += monthly_overhead
                
                # Calculate cash value addition (contribution minus overhead)
                cash_addition = monthly_contribution - monthly_overhead
                cash_value += cash_addition
                
                # Calculate interest on cash value
                month_interest = (cash_value * monthly_rate).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
                
                # Add interest to cash value
                cash_value += month_interest
                
                # Track yearly interest
                yearly_interest += month_interest
                
                # Track monthly details
                monthly_detail.append({
                    'Year': year,
                    'Month': month,
                    'Total_Contribution': float(total_contribution),
                    'Total_Overhead': float(total_overhead),
                    'Cash_Value': float(cash_value),
                    'Monthly_Interest': float(month_interest),
                    'Interest_Rate': float(annual_rate * 100)
                })
            
            # Add year's summary to table
            table_data.append((
                year,
                f"${float(total_contribution):,.2f}",
                f"${float(total_overhead):,.2f}",
                f"${float(cash_value):,.2f}",
                f"${float(yearly_interest):,.2f}"
            ))
        
        # Update Treeview
        for row in tree.get_children():
            tree.delete(row)
        for row_data in table_data:
            tree.insert("", "end", values=row_data)
            
        # Save both summary and detailed data for copying
        global clipboard_data, monthly_detail_data
        clipboard_data = pd.DataFrame(
            table_data, 
            columns=["Year", "Total Contribution", "Total Overhead", "Cash Value", "Yearly Interest"]
        )
        monthly_detail_data = pd.DataFrame(monthly_detail)
        
        # Show first month's calculation in info label
        first_month = monthly_detail[0]
        detail_text = (
            f"First Month Example:\n"
            f"Contribution: ${monthly_contribution:.2f}\n"
            f"Overhead: ${monthly_contribution * overhead_rate:.2f}\n"
            f"Cash Value: ${first_month['Cash_Value']:.2f}\n"
            f"Interest Earned: ${first_month['Monthly_Interest']:.2f}"
        )
        error_label.config(text=detail_text)
        
    except ValueError as e:
        error_label.config(text=f"Please enter valid numbers. Error: {str(e)}")

def copy_to_clipboard():
    if clipboard_data is not None:
        root.clipboard_clear()
        clipboard_data.to_clipboard(index=False, header=True, sep="\t")
        error_label.config(text="Yearly summary copied to clipboard!")
    else:
        error_label.config(text="No data to copy!")

def copy_monthly_detail():
    if monthly_detail_data is not None:
        monthly_detail_data.to_clipboard(index=False, header=True, sep="\t")
        error_label.config(text="Monthly detail copied to clipboard!")
    else:
        error_label.config(text="No monthly detail available!")

# Create the main window
root = tk.Tk()
root.title("Life Insurance Policy Calculator")

# Initialize data variables
clipboard_data = None
monthly_detail_data = None

# Create and configure a main frame with padding
main_frame = ttk.Frame(root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

# Input fields with improved layout
input_frame = ttk.LabelFrame(main_frame, text="Policy Parameters", padding="5")
input_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=5)

ttk.Label(input_frame, text="Monthly Contribution ($):").grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
monthly_contribution_entry = ttk.Entry(input_frame)
monthly_contribution_entry.insert(0, "100")  # Default value
monthly_contribution_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)

ttk.Label(input_frame, text="Overhead Rate (%):").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2)
overhead_rate_entry = ttk.Entry(input_frame)
overhead_rate_entry.insert(0, "50")  # Default value
overhead_rate_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)

ttk.Label(input_frame, text="Annual Interest Rate (%):").grid(row=2, column=0, sticky=tk.W, padx=5, pady=2)
interest_rate_entry = ttk.Entry(input_frame)
interest_rate_entry.insert(0, "11")  # Default value
interest_rate_entry.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)

ttk.Label(input_frame, text="Number of Years:").grid(row=3, column=0, sticky=tk.W, padx=5, pady=2)
years_entry = ttk.Entry(input_frame)
years_entry.insert(0, "25")  # Default value
years_entry.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)

# Buttons frame
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=1, column=0, pady=5)

calculate_button = ttk.Button(button_frame, text="Calculate", command=calculate_growth)
calculate_button.grid(row=0, column=0, padx=5)

copy_button = ttk.Button(button_frame, text="Copy Yearly Summary", command=copy_to_clipboard)
copy_button.grid(row=0, column=1, padx=5)

copy_detail_button = ttk.Button(button_frame, text="Copy Monthly Detail", command=copy_monthly_detail)
copy_detail_button.grid(row=0, column=2, padx=5)

# Error/info label with more space
error_label = ttk.Label(main_frame, text="", foreground="blue", wraplength=400)
error_label.grid(row=2, column=0, pady=5)

# Treeview for displaying yearly results
tree = ttk.Treeview(main_frame, 
                    columns=("Year", "Total Contribution", "Total Overhead", "Cash Value", "Yearly Interest"), 
                    show="headings", 
                    height=25)  # Increased height to show all 25 years

# Configure column headings
for col in tree["columns"]:
    tree.heading(col, text=col, anchor="w")
    tree.column(col, anchor="w")

tree.grid(row=3, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)

# Add scrollbar
scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=tree.yview)
scrollbar.grid(row=3, column=1, sticky=(tk.N, tk.S))
tree.configure(yscrollcommand=scrollbar.set)

# Configure grid weights for resizing
main_frame.columnconfigure(0, weight=1)
root.columnconfigure(0, weight=1)

# Automatically calculate on startup
root.after(100, calculate_growth)

# Run the application
root.mainloop()

Requirements

numpy==2.2.0
pandas==2.2.3
python-dateutil==2.9.0.post0
pytz==2024.2
six==1.17.0
tzdata==2024.2
Last updated on 16 Dec 2023
Published on 16 Dec 2023