Spaces:
Sleeping
Sleeping
File size: 8,128 Bytes
39a23e4 3c822c9 0f92cf6 3c822c9 0f92cf6 39a23e4 3c822c9 39a23e4 3c822c9 0f92cf6 3c822c9 0f92cf6 39a23e4 70235da 39a23e4 3c822c9 39a23e4 0f92cf6 3c822c9 0f92cf6 3c822c9 0f92cf6 39a23e4 3c822c9 39a23e4 0f92cf6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
import gradio as gr
import pandas as pd
import numpy as np
# Global variables
expenses = []
participants_set = set()
def add_participant(participant):
global participants_set
if not participant or not participant.strip():
raise gr.Error("Participant name cannot be empty")
clean_name = participant.strip()
participants_set.add(clean_name)
participants_list = sorted(list(participants_set)) # Sort for consistent display
participants_text = "\n".join(participants_list)
# Return updated participant list and new Dropdown instances
return (
participants_text,
gr.Dropdown(choices=participants_list, label="Payer", interactive=True),
gr.Dropdown(choices=participants_list, label="Participants", multiselect=True, interactive=True)
)
def remove_participant(participant):
global participants_set
participant = participant.strip()
if participant in participants_set:
participants_set.remove(participant)
participants_list = sorted(list(participants_set)) # Sort for consistent display
participants_text = "\n".join(participants_list)
# Return updated participant list and new Dropdown instances
return (
participants_text,
gr.Dropdown(choices=participants_list, label="Payer", interactive=True),
gr.Dropdown(choices=participants_list, label="Participants", multiselect=True, interactive=True)
)
# Add expenses
def add_expense(description, amount, payer, participants_list):
global expenses
# Validate inputs
if not description or not description.strip():
raise gr.Error("Description cannot be empty")
if amount <= 0:
raise gr.Error("Amount must be a positive number")
if not payer:
raise gr.Error("Payer cannot be empty")
if not participants_list:
raise gr.Error("Participants cannot be empty")
# Ensure all are unique
unique_participants = list(set(participants_list + [payer]))
amount = float(amount)
expense = {
"Description": description,
"Amount": amount,
"Payer": payer,
"Participants": ", ".join(unique_participants),
"Split Amount": round(amount / len(unique_participants), 2),
}
expenses.append(expense)
return pd.DataFrame(expenses)
# Optimize Balances
def optimize_balances():
global expenses
# Create a comprehensive balance sheet
balances = {}
for expense in expenses:
payer = expense["Payer"]
participants_list = expense["Participants"].split(", ")
total_amount = expense["Amount"]
split_amount = total_amount / len(participants_list)
# Payer gets credit for the entire amount
balances[payer] = balances.get(payer, 0) + total_amount
# Participants owe their share
for participant in participants_list:
if participant != payer:
balances[participant] = balances.get(participant, 0) - split_amount
# Simplify debts
def simplify_debts(balances):
# Round balances to avoid floating-point errors
rounded_balances = {k: round(v, 2) for k, v in balances.items()}
# Separate creditors and debtors
debtors = {k: v for k, v in rounded_balances.items() if v < -0.01}
creditors = {k: v for k, v in rounded_balances.items() if v > 0.01}
transactions = []
# Continue until all debts are settled
while debtors and creditors:
# Find the most negative debtor and the largest creditor
debtor = min(debtors, key=debtors.get)
creditor = max(creditors, key=creditors.get)
# Amount to settle is the minimum of absolute debt and credit
settle_amount = min(abs(debtors[debtor]), creditors[creditor])
# Round to 2 decimal places
settle_amount = round(settle_amount, 2)
# Add transaction
transactions.append(f"{debtor} pays {creditor} ${settle_amount:.2f}")
# Update balances
debtors[debtor] += settle_amount
creditors[creditor] -= settle_amount
# Remove if balance is effectively zero
if abs(debtors[debtor]) < 0.01:
del debtors[debtor]
if abs(creditors[creditor]) < 0.01:
del creditors[creditor]
return transactions if transactions else ["No transactions needed"]
return "\n".join(simplify_debts(balances))
# Reset App
def reset():
global expenses, participants_set
expenses = []
participants_set = set()
participants_list = []
participants_text = ""
return (
pd.DataFrame(expenses),
"",
participants_text,
gr.Dropdown(choices=participants_list, label="Payer", interactive=True),
gr.Dropdown(choices=participants_list, label="Participants", multiselect=True, interactive=True)
)
# Gradio Interface
with gr.Blocks(theme='soft') as app:
gr.Markdown("# Expense Splitter App")
# Participant Management
with gr.Row():
with gr.Column():
participant_input = gr.Textbox(label="Participant Name", placeholder="Enter a participant name")
with gr.Row():
add_participant_btn = gr.Button("Add Participant")
remove_participant_btn = gr.Button("Remove Participant")
participants_display = gr.Textbox(
label="Current Participants",
lines=10,
interactive=False,
placeholder="Participants will appear here..."
)
# Expense Adding
with gr.Row():
with gr.Column():
description = gr.Textbox(label="Description", placeholder="e.g., Dinner")
amount = gr.Number(label="Amount", value=0, precision=2)
payer = gr.Dropdown(
label="Payer",
choices=[],
interactive=True
)
participants = gr.Dropdown(
label="Participants",
multiselect=True,
choices=[],
interactive=True
)
add_btn = gr.Button("Add Expense")
with gr.Column():
expense_table = gr.Dataframe(
headers=["Description", "Amount", "Payer", "Participants", "Split Amount"],
datatype=["str", "number", "str", "str", "number"],
type="pandas"
)
# Button Interactions
add_participant_btn.click(
add_participant,
inputs=participant_input,
outputs=[participants_display, payer, participants]
)
remove_participant_btn.click(
remove_participant,
inputs=participant_input,
outputs=[participants_display, payer, participants]
)
add_btn.click(
add_expense,
inputs=[description, amount, payer, participants],
outputs=expense_table
)
with gr.Row():
optimize_btn = gr.Button("Optimize Balances")
result = gr.Textbox(label="Transactions", lines=5)
reset_btn = gr.Button("Reset")
optimize_btn.click(optimize_balances, inputs=[], outputs=result)
reset_btn.click(
reset,
inputs=[],
outputs=[expense_table, result, participants_display, payer, participants]
)
# Launch the app
if __name__ == "__main__":
app.launch(share=True) |