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

Modeling Debts and Advances #29

Merged
merged 91 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
37b9b03
notes
apromessi Nov 17, 2023
6bd6af2
update requirements
countvajhula Sep 22, 2023
3852b18
commit recent wip around debts, "advances", etc.
countvajhula Sep 22, 2023
1a6108f
Implement logic for writing debts
countvajhula Oct 6, 2023
ac907fa
commit wip
countvajhula Nov 17, 2023
c511e95
commit wip from last time
countvajhula Nov 30, 2023
fe18131
WIP transitioning to the new simplified advances scheme (in the doc)
countvajhula Dec 14, 2023
0c6b3be
draw down advances and collect them into the redistribution pot
countvajhula Dec 22, 2023
986afe2
notes
apromessi Jan 15, 2024
c47e544
implement redistribute_pot
apromessi Jan 15, 2024
c4f8364
doc string for redistribute_pot
apromessi Jan 19, 2024
28a257b
delete alternate function names
apromessi Jan 19, 2024
f5516b1
update docstrings
apromessi Jan 26, 2024
21a066e
update naming to split amounts_owed into amounts_payable and amounts_…
apromessi Jan 26, 2024
167241f
create new equity transactions from running tallies in amounts_payable
apromessi Mar 1, 2024
74018f0
return newly created advances from distribute payment function, add n…
apromessi Mar 1, 2024
09105ea
add note upon a note
apromessi Mar 1, 2024
aefa6d5
Address todos
countvajhula Mar 8, 2024
9c03638
Record any changes to advances after execution
countvajhula Mar 8, 2024
29d7704
debugging..
countvajhula Mar 8, 2024
4db66a2
remove the override that we had for debugging for some reason
countvajhula Mar 8, 2024
7074aae
Use abe folder as ABE_ROOT
countvajhula Mar 8, 2024
b16e169
use correct field name
countvajhula Mar 8, 2024
d2369db
mutate dict defined in calling context
countvajhula Mar 8, 2024
e86a993
import Advance model
countvajhula Mar 8, 2024
4d73b51
actually open file to be written
countvajhula Mar 8, 2024
4c07d8c
Actually read unpayable contributors file
countvajhula Mar 15, 2024
b109d29
Also commit updated debts after running job
countvajhula Mar 15, 2024
091eca4
Strategically re-enable error forwarding to fail on python errors
countvajhula Mar 15, 2024
4cac2d3
Use bash instead of sh
countvajhula Mar 15, 2024
8c10ded
try with quotes
countvajhula Mar 15, 2024
12c70b9
remove -u option to avoid unbound PYTHONPATH
countvajhula Mar 15, 2024
109555c
import missing Debt object
countvajhula Mar 15, 2024
dbf2c0d
don't create advances that are effectively for zero amounts
countvajhula Mar 15, 2024
b978b08
handle missing debts file
countvajhula Mar 15, 2024
61beb81
handle missing advances file
countvajhula Mar 15, 2024
6c981ae
don't redistribute pot if there's nothing in there
countvajhula Mar 15, 2024
125ce55
Add aggregate advances to outstanding balances issue for testing
countvajhula Mar 22, 2024
66a0952
parse amounts as `Decimal`
countvajhula Mar 22, 2024
c5087c9
add a comment for future refactoring
countvajhula Mar 22, 2024
aa6f4b3
only draw down advances for contributors in the relevant attributions…
countvajhula Mar 22, 2024
71cf203
parse ItemizedPayment amounts as Decimal
countvajhula Mar 22, 2024
0715c37
support partial drawdown of advances
countvajhula Mar 22, 2024
d424cb9
don't pay debts unless contributor is in the relevant attributions
countvajhula Mar 30, 2024
32009ef
print statements for debugging
countvajhula Mar 30, 2024
53f31d3
add a couple more debug statements
countvajhula Mar 30, 2024
5f3fe14
more debug statements
countvajhula Mar 30, 2024
e522554
more debug statements
countvajhula Mar 30, 2024
92c756b
don't double-deduct paid off debts from available payment amount
countvajhula Mar 30, 2024
d62acd3
Start on integration tests using pyfakefs
countvajhula Apr 12, 2024
25d6c49
a couple of starter integration tests
countvajhula Apr 19, 2024
a788cd3
handle missing input files
countvajhula Apr 19, 2024
16fe144
declare `time_machine` dependency for tests
countvajhula Apr 19, 2024
1b7eee0
a couple of working integration tests
countvajhula Apr 26, 2024
8aba0ea
Organize integration tests into classes and add a couple more
countvajhula Apr 26, 2024
b6c5430
Set convenient decimal precision in another test
countvajhula May 3, 2024
1ce8abb
More integration tests
countvajhula May 3, 2024
74ba995
Remove nested context manager for opening debts file
countvajhula May 3, 2024
0489c4f
Integration tests for remaining cases we identified
countvajhula May 10, 2024
636ac58
create new files for constants and utils
apromessi May 15, 2024
298bac0
fix syntax
apromessi May 15, 2024
f5e475a
fix syntax
apromessi May 15, 2024
72e4d01
fix syntax
apromessi May 15, 2024
b0559a1
fix syntax
apromessi May 15, 2024
48d9937
use money_in value of ABE_ROOT for constants
apromessi May 15, 2024
33d2411
standardize file construction for constants
apromessi May 15, 2024
8910c71
fix white space in test files, fix attributions file name, move attri…
apromessi May 15, 2024
1d6bf13
put utils into separate modules
apromessi May 17, 2024
3f59bbc
Update development docs
countvajhula Jun 21, 2024
575576c
move logic into a function for integration testing purposes
countvajhula Jul 12, 2024
907cc32
Expand integration tests to include "money out" functionality
countvajhula Jul 12, 2024
d2447be
Jair's refactor
jairtrejo Aug 24, 2024
59075eb
remove outdated money_in unit tests
countvajhula Sep 27, 2024
acaea38
update docs to include debts and advances, add logo
apromessi Oct 2, 2024
2330b88
Merge pull request #33 from apromessi/debts-and-advances-docs
countvajhula Oct 2, 2024
cdbd15a
whitelist .github path for grep search
countvajhula Oct 2, 2024
2e3af3a
try bumping python in workflows to fix GA build failure
countvajhula Oct 2, 2024
d5cbcf3
use python 3.11 to avoid setuptools issue
countvajhula Oct 2, 2024
c34cff5
blacken..
countvajhula Oct 2, 2024
98ec6e8
fix remaining lint errors
countvajhula Oct 2, 2024
6cc1d95
lint..
countvajhula Oct 2, 2024
1cda375
remove pypy from the matrix for now
countvajhula Oct 2, 2024
60fda20
remove deprecated `tests_require`
countvajhula Oct 2, 2024
edb1928
try python 3.9 with ubuntu
countvajhula Oct 2, 2024
47e3ed1
remove tests_require ... again
countvajhula Oct 2, 2024
4e61aba
add pypy back
countvajhula Oct 2, 2024
484b725
revert to macos to find original error
countvajhula Oct 2, 2024
a20dba6
try ARM64
countvajhula Oct 2, 2024
ab116a3
remove pypy
countvajhula Oct 2, 2024
8b6ec57
use `pytest` instead of `tox`
countvajhula Oct 2, 2024
bc50f2c
use python 3.11
countvajhula Oct 2, 2024
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
15 changes: 7 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ jobs:
strategy:
matrix:
py:
- "3.9"
- "pypy3.9"
- "3.11"
os:
- "macos-latest"
- "ubuntu-latest"
architecture:
- x64
name: "Python: ${{ matrix.py }}-${{ matrix.architecture }} on ${{ matrix.os }}"
Expand All @@ -29,9 +28,9 @@ jobs:
- name: Install dependencies
run: make build-for-test
- name: Run tests
run: make test-matrix
run: pytest
coverage:
runs-on: macos-latest
runs-on: ubuntu-latest
name: Report coverage
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
Expand All @@ -42,21 +41,21 @@ jobs:
- name: Setup python
uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: 3.11
architecture: x64
- name: Install dependencies
run: make build-for-test
- name: Report coverage
run: make cover-coveralls
lint:
runs-on: macos-latest
runs-on: ubuntu-latest
name: Lint the package
steps:
- uses: actions/checkout@v3
- name: Setup python
uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: 3.11
architecture: x64
- name: Install dependencies
run: make build
Expand Down
1 change: 1 addition & 0 deletions .ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!/.github/
34 changes: 31 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,50 @@ You could do this in a virtual environment or at the system / user level if you
$ make build
```

You may also need to run:

```
$ make build-for-test
```

Together, these should ensure that you have all development dependencies so that the rest of the `make` targets should work.

# Running Tests

## Unit Tests

```
$ make test
```

## Debugging
## Integration Tests

```
$ pytest tests/integration
```

NOTE: We do have a `make` target for this:

```
$ make test-integration
```

But this does not work even though it runs a command identical to the above, due to some weird dependency issue with pytest. For now, just use the earlier command in the shell directly.

# Debugging

To debug a test execution using a step debugger, put this in the body of the test you'd like to debug:

```
import pudb; pudb.set_trace()
```

or simply,

```
import pudb; pu.db
```

Now, when you run `make test` (for example), it will put you in the debugger, allowing you to step through the execution of the code. Here are some things you can do:

* `n` - next
Expand All @@ -32,8 +62,6 @@ Now, when you run `make test` (for example), it will put you in the debugger, al

There's more handy stuff that you can do like setting breakpoints and visiting other modules. Press `?` to see all the options.

*Note*: the official docs for `pudb` say that we could also use `pu.db()` instead of `pudb.set_trace()`, but this doesn't seem to work.

# Linting

Linter:
Expand Down
22 changes: 0 additions & 22 deletions DEV.md

This file was deleted.

11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DOCS-PATH=docs

export PYTEST_DISABLE_PLUGIN_AUTOLOAD = 1
UNIT_TESTS_PATH = tests/unit
INTEGRATION_TESTS_PATH = tests/integration

help:
@echo "clean - remove all build, test, coverage and Python artifacts"
Expand All @@ -23,6 +24,7 @@ help:
@echo "lint - alias for lint-source"
@echo "black - run black auto-formatting on all code"
@echo "test-unit - run unit tests"
@echo "test-integration - run integration tests"
@echo "test - run specified tests, e.g.:"
@echo " make test DEST=tests/unit/my_module.py"
@echo " (defaults to unit tests if none specified)"
Expand Down Expand Up @@ -93,7 +95,12 @@ black:
test-unit:
python setup.py test --addopts $(UNIT_TESTS_PATH)

test-all: clean-test test-unit
# NOTE: does not work! Only works when this identical
# command is run directly at the command line
test-integration:
pytest $(INTEGRATION_TESTS_PATH)

test-all: clean-test test-unit test-integration

test:
ifdef DEST
Expand Down Expand Up @@ -146,4 +153,4 @@ sdist: clean
python setup.py sdist
ls -l dist

.PHONY: help build build-for-test docs clean clean-build clean-pyc clean-test lint-source lint-tests lint-all lint black test-unit test-all test test-stop test-debug test-matrix test-tldr test-wiki debug coverage cover-coveralls sdist
.PHONY: help build build-for-test docs clean clean-build clean-pyc clean-test lint-source lint-tests lint-all lint black test-unit test-integration test-all test test-stop test-debug test-matrix test-tldr test-wiki debug coverage cover-coveralls sdist
Binary file added docs/assets/img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 16 additions & 64 deletions docs/oldabe.scrbl
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#lang scribble/manual

@require[racket/runtime-path]

@title{Old Abe: The Accountant for All of your ABE Needs}

@(define-runtime-path logo-path "assets/img/logo.png")
@(if (file-exists? logo-path)
(image logo-path #:scale 1.0)
(printf "[WARNING] No ~a file found!~%" logo-path))

@table-of-contents[]

@section{Intro}
Expand All @@ -25,6 +32,8 @@ Old Abe considers precisely three inputs in doing all of its accounting, and it

@item{Attributions -- an association of contributor to percentage of value allocated from the value represented by the project as a whole.}

@item{Instruments -- an association of an instrument to percentage of value allocated from the value represented by the project as a whole.}

@item{Price -- a generic "fair market value" provided by the project to its users (Like many concepts, this concept named price has a distinct role in ABE from its traditional role in capitalism).}

@item{Valuation -- the assessed present value of the project as a whole.}
Expand All @@ -34,81 +43,24 @@ All of these are determined through the process of Dialectical Inheritance Attri

@section{Accounting Flows}

There are several actions ("accountable actions") pertaining to a project that trigger accounting by Old Abe. These are:
Current accounting flows are a mix of manual and automated actions. Old Abe is not directly connected to any financial systems, so its primary role is to run the accounting logic, keep track of any project investors, and tell the maintainer how much to pay project contributors. It is up to the maintainer to record incoming payments (@code{abe/payments/}) and outgoing payouts (@code{abe/payouts/}).

@itemlist[
#:style 'ordered

@item{Work is done for the project.}
@item{Recording a Payment - triggers a GitHub Action that runs the accounting logic (details below) and produces a report of all Outstanding Balances as a GitHub Issue. The maintainer can refer to the Issue to find out how much to pay.}

@item{A financial contribution is made to the project.}

@item{An appointed project representative fulfills a payout to a contributor.}

@item{A "fiat" change is made to one of the inputs, i.e. either attributions, price or valuation, which is typically a resolution by DIA.}
@item{Recording a Payout - triggers a GitHub Action that simply updates the Outstanding Balances issue to reflect the updated amounts owed.}

]

We will learn more about each of these, in turn.

@subsection{Work}

Work done could be either labor, capital, or ideas, as defined in the @hyperlink["https://github.com/drym-org/finance/blob/main/finance.md"]{ABE financial model}. Regardless of what kind of work it is, its appraisal takes the form of an "incoming attribution," which is an association of a set of contributors to percentage of value contributed, as judged in related to existing attribution allocations in the project.

Old Abe will account this by "renormalizing" the attributions to total to 100% after incorporating the fresh values.

TODO: flesh out

@subsection{Payment}

When a payment comes in, we first pay out any @tech{instruments}. Then, with the remaining amount, we pay project contributors.

TODO: flesh out

@subsection{Payout}

TODO: flesh out

@subsection{Fiat Change in Inputs}

TODO: flesh out, including backpropagation

@section{Modules}

The accounting flows mentioned earlier correspond to distinct modules that handle them.

@subsection{Money In}

This module handles incoming payments.

First it finds all payments that have not already been processed, that
is, which do not appear in the transactions file.

For each of these payments, it consults the current attributions for
the project, and does three things.

First, it figures out how much each person in the attributions file is
owed from this fresh payment, generating a transaction for each
stakeholder.

Second, it determines how much of the incoming payment can be
considered an "investment" by comparing the project price with the
total amount paid by this payer up to this point -- the excess, if
any, is investment.
When someone makes a payment to a project, Old Abe allocates portions of that payment to project contributors and creates a report that tells maintainers how much money the project owes to each individual contributor. We'll get deep into the weeds of how it does that in a moment. If the incoming payment represents an investment (that is, it brings the payer's total amount paid above the project price), the payer is considered a project contributor. The system adds them to the attributions file with a share equal to their investment (or increases their pre-existing attributive share). The project valuation is increased by the investment amount and all existing attributive shares are diluted so that percentage shares still add up 100%.

Third, it increases the current valuation by the investment amount
determined, and, at the same time, "dilutes" the attributions by
making the payer an attributive stakeholder with a share proportionate
to their incoming investment amount (or if the payer is already a
stakeholder, increases their existing share) in relation to the
valuation.
Now, let's talk about how Old Abe allocates an incoming payment.

@subsection{Money Out}
First, we pay off any processing fees (found in @code{instruments.txt}). These fees have fixed percentages that apply to every incoming payment and do not get diluted by investments. They are somewhat analogous to credit card fees. They go towards the Old Abe system itself and to those who have contributed to the DIA process for this project.

This module determines outstanding balances owed.
Next, we divide the remainder among the contributors in the attributions file. Ideally, this is as simple as dividing the amount according to each contributor's attributive share. However, sometimes certain contributors are temporarily unpayable (e.g. they might not have provided their payment information yet, etc.). In that case, we record the amount owed to that contributor as a "debt" so that the project can pay them later. To avoid having money sitting around in maintainers' accounts, we divide any amount left over among payable contributors, according to attributive share. Any amount we pay someone in excess of what we owed them originally, we record as an "advance." The idea here is that when someone eventually becomes payable, we can prioritize paying off the debt we owe them by allocating money to them first whenever a new payment comes in. Anyone who has been accumulating advances will receive a little less than their attributive share until their total advance amount has been "drawn down" and the scales have been balanced between debts and advances.

First, it reads all generated transactions from payments that have come in to
determine the amount owed to each contributor. Then, it looks at all recorded
payouts to see the total amounts that have already been paid out. It then
reports the difference of these values, by contributor, as the balance still
owed.
14 changes: 5 additions & 9 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/sh -l
#!/bin/bash

# ensure that any errors encountered cause immediate
# termination with a non-zero exit code
set -e
set -eo pipefail

echo "PWD is: "
echo $(pwd)
Expand All @@ -23,21 +23,17 @@ echo "... done."

# Note that running this locally would cause your global
# git config to be modified
echo "Committing updated transactions and attributions back to repo..."
echo "Committing updated accounting records back to repo..."
git config --global user.email "[email protected]"
git config --global user.name "Old Abe"
git add abe/transactions.txt abe/attributions.txt abe/valuation.txt abe/itemized_payments.txt
git add abe/transactions.txt abe/attributions.txt abe/valuation.txt abe/itemized_payments.txt abe/advances.txt abe/debts.txt

set +e

git commit -m "Updated transactions and attributions"
git commit -m "Updated accounting records"
git fetch
git rebase origin/`git remote set-head origin -a | cut -d' ' -f4`
git push origin `git remote set-head origin -a | cut -d' ' -f4`
echo "... done."

set -e

echo "Running money_out script..."
echo balances=$(python -m oldabe.money_out) >> $GITHUB_OUTPUT
echo "... done."
34 changes: 34 additions & 0 deletions oldabe/accounting_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from decimal import Decimal

ROUNDING_TOLERANCE = Decimal("0.000001")


def get_rounding_difference(attributions):
"""
Get the difference of the total of the attributions from 1, which is
expected to occur due to finite precision. If the difference exceeds the
expected error tolerance, an error is signaled.
"""
total = _get_attributions_total(attributions)
difference = total - Decimal("1")
assert abs(difference) <= ROUNDING_TOLERANCE
return difference


def correct_rounding_error(attributions, incoming_attribution):
"""Due to finite precision, the Decimal module will round up or down
on the last decimal place. This could result in the aggregate value not
quite totaling to 1. This corrects that total by either adding or
subtracting the difference from the incoming attribution (by convention).
"""
difference = get_rounding_difference(attributions)
attributions[incoming_attribution.email] -= difference


def assert_attributions_normalized(attributions):
print(_get_attributions_total(attributions))
assert _get_attributions_total(attributions) == Decimal("1")


def _get_attributions_total(attributions):
return sum(attributions.values())
23 changes: 23 additions & 0 deletions oldabe/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
from decimal import Decimal

ACCOUNTING_ZERO = Decimal("0.01")

ABE_ROOT = './abe'
PAYOUTS_DIR = os.path.join(ABE_ROOT, 'payouts')
PAYMENTS_DIR = os.path.join(ABE_ROOT, 'payments')
NONATTRIBUTABLE_PAYMENTS_DIR = os.path.join(
ABE_ROOT, 'payments', 'nonattributable'
)

TRANSACTIONS_FILE = os.path.join(ABE_ROOT, 'transactions.txt')
DEBTS_FILE = os.path.join(ABE_ROOT, 'debts.txt')
ADVANCES_FILE = os.path.join(ABE_ROOT, 'advances.txt')
UNPAYABLE_CONTRIBUTORS_FILE = os.path.join(
ABE_ROOT, 'unpayable_contributors.txt'
)
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 = os.path.join(ABE_ROOT, 'attributions.txt')
INSTRUMENTS_FILE = os.path.join(ABE_ROOT, 'instruments.txt')
Loading
Loading