Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fancy dicts #9

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 12 additions & 26 deletions abrechnung/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import random, copy, time
import transaction as trans
import operator
from utils import NormalizingDict

class Group:
"""
Expand All @@ -10,51 +11,39 @@ class Group:
distributes the cost to the participants.
"""
def __init__(self, group_id):
self.accounts = []
self.accounts = NormalizingDict()
self.events = []
self.transactions = []
self.group_id = group_id

def add_account(self, account):
"""Add an account to the group"""
self.accounts.append(account)
self.accounts[account.name] = account

def add_event(self, event):
"""Add an event to this group and calculate the new balance"""

for participant in event.participants:
found = False
for acc in self.accounts:
if acc.name.lower() == participant.lower():
found = True
if not found:
if participant not in self.accounts:
raise GroupError("Participant: " + participant + " not found.\n" + "Event will not be added!")

# Only use whole payments and randomly put the remainder onto a account
remainder = event.cost_in_cents % len(event.participants)
cost_per_person = ( event.cost_in_cents - remainder) / len(event.participants)

# The payer gets all credits to his account
for acc in self.accounts:
if acc.name.lower() == event.payer.lower():
acc.balance += event.cost_in_cents
self.accounts[event.payer].balance += event.cost_in_cents

# All participants get the cost_per_person to his account
for participant in event.participants:
for acc in self.accounts:
#Use edit-distance here
if acc.name.lower() == participant.lower():
acc.balance -= cost_per_person
break
self.accounts[participant].balance -= cost_per_person

# Randomly add remainder costs to a participant
if remainder != 0:
rand_person = random.choice(event.participants)
event.add_remainder_person(rand_person)
print("[add_event] - Extra remainder goes to: " + rand_person)
for acc in self.accounts:
if rand_person.lower() == acc.name.lower():
acc.balance -= remainder
self.accounts[rand_person].balance -= remainder

add_event_text = "[add_event] - cost_per_person: {cost_per_person}, remainder: {remainder}"
print(event)
Expand All @@ -64,21 +53,18 @@ def add_event(self, event):

def do_transaction(self, transaction):
"""Transfer money from one persion to another"""
src, dst = None, None
for acc in self.accounts:
if acc.name.lower() == transaction.source.lower():
src = acc
if acc.name.lower() == transaction.destination.lower():
dst = acc

if not src or not dst:
try:
src = self.accounts[transaction.source]
dst = self.accounts[transaction.destination]
except KeyError:
raise GroupError("Participant not found. Can not do the transaction.")

print(transaction)

# source gets balance decreased, destination gets balance increased
src.balance -= transaction.amount_in_cents
dst.balance += transaction.amount_in_cents

self.transactions.append(transaction)

def __repr__(self):
Expand Down
60 changes: 59 additions & 1 deletion abrechnung/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,62 @@ def levenshtein(s1, s2):
current_row.append(min(insertions, deletions, substitutions))
previous_row = current_row

return previous_row[-1]
return previous_row[-1]

class FuzzyDict:
def __init__(self, items=[], threshold=5):
self.data = dict(items)
self.threshold = threshold

def __setitem__(self, key, value):
self.data[key] = value

def __getitem__(self, key):
dist, match = min((levenshtein(key, match), match) for match in self.data)
if dist<self.threshold:
return self.data[match]
else:
raise KeyError(key + " does not match any value")

def __contains__(self, key):
try:
v = self[key]
return True
except KeyError:
return False

def __iter__(self):
yield from self.data.values()

def __repr__(self):
return "FuzzyDict({data!r}, threshold={threshold})".format(**self.__dict__)

def __str__(self):
return str(self.data)

class NormalizingDict:
def __init__(self, items=[]):
self.data = {}
for k,v in items:
self[k] = v

def __setitem__(self, key, value):
self.data[self.normalize(key)] = value

def __getitem__(self, key):
return self.data[self.normalize(key)]

def __contains__(self, key):
return self.normalize(key) in self.data

def normalize(self, key):
return key.lower()

def __iter__(self):
yield from self.data.values()

def __repr__(self):
return "NormalizingDict({data!r})".format(**self.__dict__)

def __str__(self):
return str(self.data)