Initial commit

This commit is contained in:
Midgard 2019-06-21 12:58:16 +02:00
commit da872e8aab
Signed by: midgard
GPG key ID: 511C112F1331BBB4
4 changed files with 267 additions and 0 deletions

111
.gitignore vendored Normal file
View file

@ -0,0 +1,111 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# backup files
*~
bin/
include/
pip-selfcheck.json
# visual studio code
.vscode/

12
Pipfile Normal file
View file

@ -0,0 +1,12 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
requests = "*"
[dev-packages]
[requires]
python_version = "3.7"

57
Pipfile.lock generated Normal file
View file

@ -0,0 +1,57 @@
{
"_meta": {
"hash": {
"sha256": "bb57e0d7853b45999e47c163c46b95bc2fde31c527d8d7b5b5539dc979444a6d"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
],
"version": "==2019.6.16"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"index": "pypi",
"version": "==2.22.0"
},
"urllib3": {
"hashes": [
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
],
"version": "==1.25.3"
}
},
"develop": {}
}

87
tab Executable file
View file

@ -0,0 +1,87 @@
#!/usr/bin/env python3
import itertools
import sys
import os
import requests
ME = os.getenv("TAB_ME")
TOKEN = os.getenv("TAB_TOKEN")
TAP_ACCOUNT = os.getenv("LEDGER_TAP_ACCOUNT", "Expenses:Zeuskelder:Drinks and snacks")
FOOD_ACCOUNT = os.getenv("LEDGER_FOOD_ACCOUNT", "Expenses:Zeuskelder:Food")
def formatted_price(cents):
return f"€ {cents / 100}"
def balance_assertion(balance):
return \
f"= {formatted_price(balance)}" \
if balance is not None else ""
def formatted_transaction(transaction, me, balance_after=None):
issuer = transaction["issuer"]
debtor = transaction["debtor"]
date = transaction["time"][:10]
amount = formatted_price(transaction["amount"])
message = (
transaction["message"].replace("1 ", "").replace(" and ", " en ")
if issuer == "Tap" else
transaction["message"]
).strip()
header = f"{date} {message}"
assertion = balance_assertion(balance_after)
if debtor == me:
# If the issuer wasn't Tap, we assume the money we gave was for food manual change to the
# output required if it wasn't
to_account = TAP_ACCOUNT if issuer == "Tap" else FOOD_ACCOUNT
to_line = f"{to_account} {amount}"
from_line = f"tab {assertion}".rstrip()
else:
to_line = f"tab {amount} {assertion}"
from_line = f"; {debtor}"
return "\n".join([
header,
"\t" + to_line,
"\t" + from_line
])
def formatted_transactions(transactions, final_balance, me):
all_but_last = itertools.islice(transactions, len(transactions) - 1)
return [
*(formatted_transaction(t, me) for t in all_but_last),
formatted_transaction(transactions[-1], me, final_balance)
]
def main(me, token, file=sys.stdout):
def get(path):
return requests.get(
f"https://tab.zeus.gent{path}",
headers={"Accept": "application/json", "Authorization": f"Token token={token}"}
).json()
balance = get(f"/users/{me}")["balance"]
transactions = get(f"/users/{me}/transactions")
print(
"\n\n".join(formatted_transactions(transactions, balance, me)),
file=file
)
if __name__ == "__main__":
main(ME, TOKEN)