Initial commit
This commit is contained in:
commit
da872e8aab
4 changed files with 267 additions and 0 deletions
111
.gitignore
vendored
Normal file
111
.gitignore
vendored
Normal 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
12
Pipfile
Normal 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
57
Pipfile.lock
generated
Normal 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
87
tab
Executable 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)
|
Loading…
Reference in a new issue