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

Jair refactor II #32

Merged
merged 1 commit into from
Sep 27, 2024
Merged
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
1 change: 1 addition & 0 deletions oldabe/accounting_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def correct_rounding_error(attributions, incoming_attribution):


def assert_attributions_normalized(attributions):
print(_get_attributions_total(attributions))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugging?

assert _get_attributions_total(attributions) == Decimal("1")


Expand Down
7 changes: 5 additions & 2 deletions oldabe/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import os
from decimal import Decimal

ACCOUNTING_ZERO = Decimal("0.01")

ABE_ROOT = './abe'
PAYOUTS_DIR = os.path.join(ABE_ROOT, 'payouts')
Expand All @@ -14,5 +17,5 @@
ITEMIZED_PAYMENTS_FILE = os.path.join(ABE_ROOT, 'itemized_payments.txt')
PRICE_FILE = os.path.join(ABE_ROOT, 'price.txt')
VALUATION_FILE = os.path.join(ABE_ROOT, 'valuation.txt')
ATTRIBUTIONS_FILE = 'attributions.txt'
INSTRUMENTS_FILE = 'instruments.txt'
ATTRIBUTIONS_FILE = os.path.join(ABE_ROOT, 'attributions.txt')
INSTRUMENTS_FILE = os.path.join(ABE_ROOT, 'instruments.txt')
48 changes: 48 additions & 0 deletions oldabe/distribution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from decimal import Decimal
from typing import Set


class Distribution(dict[str | None, Decimal]):
"""
A dictionary of shareholders to proportions

None can be used as a shareholder to omit a percentage from distribution
and enumeration.
"""

def _normalized(self) -> "Distribution":
"""
Return a distribution with the same proportions that adds up to 1
"""
total = sum(self.values())

return Distribution(
{
shareholder: share * (Decimal("1") / total)
for shareholder, share in self.items()
}
)

def without(self, exclude: Set[str]) -> "Distribution":
"""
Return a distribution without the shareholders in exclude

Other shareholders retain their relative proportions.
"""
return Distribution(
{
shareholder: share
for shareholder, share in self.items()
if shareholder not in exclude
}
)

def distribute(self, amount: Decimal) -> dict[str, Decimal]:
"""
Distribute an amount amongst shareholders
"""
return {
shareholder: amount * share
for shareholder, share in self._normalized().items()
if shareholder is not None
}
3 changes: 3 additions & 0 deletions oldabe/git.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import subprocess
from functools import cache


@cache
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nifty

def get_git_revision_short_hash() -> str:
"""From https://stackoverflow.com/a/21901260"""
return (
Expand Down
80 changes: 53 additions & 27 deletions oldabe/models.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
from datetime import datetime
from dataclasses import dataclass, field
from datetime import datetime
from decimal import Decimal

from oldabe.git import get_git_revision_short_hash
from oldabe.parsing import parse_percentage


# Wrapping so that tests can mock it
def default_commit_hash():
return get_git_revision_short_hash()


@dataclass
class Transaction:
email: str = None
amount: Decimal = 0
payment_file: str = None
commit_hash: str = None
email: str
amount: Decimal
payment_file: str
commit_hash: str = field(default_factory=lambda: default_commit_hash())
created_at: datetime = field(default_factory=datetime.utcnow)


@dataclass
class Payout:
name: str
email: str
amount: Decimal
created_at: datetime = field(default_factory=datetime.utcnow)


@dataclass
class Debt:
email: str = None
amount: Decimal = None
email: str
amount: Decimal
# amount_paid is a running tally of how much of this debt has been paid
# in future will link to Transaction objects instead
amount_paid: Decimal = 0
payment_file: str = None
commit_hash: str = None
amount_paid: Decimal
payment_file: str
commit_hash: str = field(default_factory=lambda: default_commit_hash())
created_at: datetime = field(default_factory=datetime.utcnow)

def key(self):
Expand All @@ -32,26 +48,35 @@ def is_fulfilled(self):
def amount_remaining(self):
return self.amount - self.amount_paid

# These are not recorded (yet?), they just represent an intention to record a payment
@dataclass
class DebtPayment:
debt: Debt
amount: Decimal


# Individual advances can have a positive or negative amount (to
# indicate an actual advance payment, or a drawn down advance).
# To find the current advance amount for a given contributor,
# sum all of their existing Advance objects.
@dataclass
class Advance:
email: str = None
amount: Decimal = 0
payment_file: str = None
commit_hash: str = None
email: str
amount: Decimal
payment_file: str
commit_hash: str = field(default_factory=lambda: default_commit_hash())
created_at: datetime = field(default_factory=datetime.utcnow)


@dataclass
class Payment:
email: str = None
amount: Decimal = 0
email: str
name: str
amount: Decimal
# Should move this, but it will invalidate existing files
created_at: datetime = field(default_factory=datetime.utcnow)
attributable: bool = True
file: str = None
file: str = ''


# ItemizedPayment acts as a proxy for a Payment object that keeps track
Expand All @@ -60,17 +85,18 @@ class Payment:
# avoid mutating Payment records.
@dataclass
class ItemizedPayment:
email: str = None
fee_amount: Decimal = 0 # instruments
project_amount: Decimal = 0 # attributions
attributable: bool = True
payment_file: str = (
None # acts like a foreign key to original payment object
)
email: str
fee_amount: Decimal # instruments
project_amount: Decimal # attributions
attributable: bool
payment_file: str # acts like a foreign key to original payment object


@dataclass
class Attribution:
email: str = None
share: Decimal = 0
dilutable: bool = True
email: str
share: str

@property
def decimal_share(self):
return parse_percentage(self.share)
Loading
Loading