Merge pull request 'implementation of the revenue sharing model' (#2) from dachary/accountant:wip-share into master
Reviewed-on: https://gitea.hostea.org/Hostea/accountant/pulls/2pull/3/head
commit
4c56b2008d
|
@ -1,2 +1,3 @@
|
||||||
venv
|
venv
|
||||||
*.csv
|
*.csv
|
||||||
|
*~
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
# py.test-3 --log-cli-level=DEBUG -v -k test_share_income share.py
|
||||||
|
|
||||||
|
EXPENSE = 1
|
||||||
|
|
||||||
|
#
|
||||||
|
# For a given income, return a list of the members who will not be paid in full
|
||||||
|
# and how much will remain in their expense balance.
|
||||||
|
#
|
||||||
|
# See https://forum.hostea.org/t/decision-revenue-sharing-model/92
|
||||||
|
#
|
||||||
|
# The members list given in argument is a list of pairs of
|
||||||
|
#
|
||||||
|
# ['member', EXPENSE]
|
||||||
|
#
|
||||||
|
# Where 'member' is the unique id of the member and EXPENSE is a
|
||||||
|
# positive integer.
|
||||||
|
#
|
||||||
|
# The income argument must be an integer lower or equal to the sum of
|
||||||
|
# all members EXPENSE.
|
||||||
|
#
|
||||||
|
# The returned member list is a subset of the members list given in
|
||||||
|
# argument and only contains the members that cannot be paid in full
|
||||||
|
# with the income given in argument. For instance:
|
||||||
|
#
|
||||||
|
# share_income(3, [['a', 2], ['b', 2]]) == [['b', 1]]
|
||||||
|
#
|
||||||
|
# Means that with an income of 3, only member 'a' can be paid in full and
|
||||||
|
# the remaining expense for member 'b' is 1.
|
||||||
|
#
|
||||||
|
def share_income(income, members):
|
||||||
|
if income <= 0:
|
||||||
|
#
|
||||||
|
# Recursion termination: no more income to share, none of the
|
||||||
|
# remaining members get anything
|
||||||
|
#
|
||||||
|
return members
|
||||||
|
if income <= len(members):
|
||||||
|
#
|
||||||
|
# The income is an integer number: when it cannot be divided
|
||||||
|
# among members (2/3 == 0), some of them get 1 and the others
|
||||||
|
# get nothing. If the income is consistently very low some
|
||||||
|
# members to never get paid but the amount of money involved
|
||||||
|
# would then be so low that no member would care.
|
||||||
|
#
|
||||||
|
share = 1
|
||||||
|
count = income
|
||||||
|
else:
|
||||||
|
#
|
||||||
|
# The share that each member will get at this stage of the
|
||||||
|
# recursion is the lowest expense. If there is not enough
|
||||||
|
# income to give each member the lowest expense, it is divided
|
||||||
|
# evenly.
|
||||||
|
#
|
||||||
|
share = min(int(income / len(members)), min(members, key=lambda m: m[EXPENSE])[EXPENSE])
|
||||||
|
count = len(members)
|
||||||
|
#
|
||||||
|
# remaining is the list of members that cannot be paid in full
|
||||||
|
# with the share.
|
||||||
|
#
|
||||||
|
remaining = []
|
||||||
|
for i in range(count):
|
||||||
|
m = members[i]
|
||||||
|
m[EXPENSE] -= share
|
||||||
|
if m[EXPENSE] > 0:
|
||||||
|
#
|
||||||
|
# If the member needs more to be paid in full, they are
|
||||||
|
# elligible to participate in the next recursion round.
|
||||||
|
#
|
||||||
|
remaining.append(m)
|
||||||
|
#
|
||||||
|
# This is the border case when income <= len(members): some
|
||||||
|
# members cannot get their share because there is not enough
|
||||||
|
# income to provide an equal share of 1 to everyone and were
|
||||||
|
# excluded from the loop above. They are added because they could
|
||||||
|
# not be paid in full.
|
||||||
|
#
|
||||||
|
remaining.extend(members[count:])
|
||||||
|
#
|
||||||
|
# Now that each member got an equal share (except for the border
|
||||||
|
# case above) recurse, but only with the members that expect to be
|
||||||
|
# paid more.
|
||||||
|
#
|
||||||
|
return share_income(income - share * count, remaining)
|
||||||
|
|
||||||
|
def test_share_income():
|
||||||
|
assert share_income(1, [['a', 1], ['b', 1]]) == [['b', 1]]
|
||||||
|
assert share_income(5, [['a', 10], ['b', 2]]) == [['a', 7]]
|
||||||
|
assert share_income(5, [['a', 2], ['b', 10], ['c', 1]]) == [['b', 8]]
|
||||||
|
assert share_income(5, [['a', 2], ['b', 10], ['c', 1], ['d', 40]]) == [['b', 9], ['d', 39]]
|
||||||
|
assert share_income(5, [['a', 2], ['b', 10], ['c', 1], ['d', 40], ['e', 3]]) == [['a', 1], ['b', 9], ['d', 39], ['e', 2]]
|
||||||
|
assert share_income(5, [['a', 2], ['b', 10], ['c', 1], ['d', 40], ['e', 3], ['f', 1]]) == [['a', 1], ['b', 9], ['d', 39], ['e', 2], ['f', 1]]
|
Loading…
Reference in New Issue