libwedger 0.1.0 documentation

libwedger

Contents

Source code for libwedger

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#
# Copyright © 2013 Felix Crux <felixc@felixcrux.com> and the Authors.
# Released under the terms of the MIT License (Expat Version).
# See the LICENSE and AUTHORS files for further details.
#

"""Interfaces to read data from ledger."""


__author__ = "Felix Crux and the Authors"
__copyright__ = "2013 " + __author__
__license__ = "MIT License"
__title__ = "libwedger"
__version__ = "0.1.0"


import datetime
import os
import re
import subprocess


_DATE_FMT = "%Y-%m-%d"


[docs]def get_all_account_names(): """Retrieve the list of account names known to ledger. :return: Set of account names. :rtype: {str} """ full_account_names = subprocess.check_output( ["ledger", "accounts"], env=os.environ.copy() ).strip().decode().split("\n") def get_sub_accounts(account_name, accumulator): parts = account_name.rsplit(":", 1) if len(parts) == 1: return accumulator + parts return get_sub_accounts(parts[0], accumulator + [account_name]) all_accounts = [] for full_account in full_account_names: if full_account is "": continue all_accounts.extend(get_sub_accounts(full_account, [])) return set(all_accounts)
[docs]def get_account(account, start=None, end=None): """Get the balance of an account for the given period. :param str account: The name of the account. :param datetime.date start: Date from which to start counting the balance. If omitted, you'll get the overall balance of the account. :param datetime.date end: Date at which to stop counting the balance. Defaults to :py:meth:`datetime.date.today()` if omitted. :return: The account balance (including currency symbol or commodity name). :rtype: str """ if end is None: end = datetime.date.today() if start and end: period = "from {} to {}".format( start.strftime(_DATE_FMT), end.strftime(_DATE_FMT)) elif start: period = "from {}".format(start.strftime(_DATE_FMT)) else: period = "until {}".format(end.strftime(_DATE_FMT)) output = subprocess.check_output( ["ledger", "bal", "--market", "--flat", account, "--period", period], env=os.environ.copy() ).strip().decode() output_lines = output.split("\n") if len(output_lines) > 1: return output_lines[-1].strip() else: return output.split(" ")[0]
[docs]def get_transaction_register(): """Get the list of transactions.""" new_transaction_re = re.compile( "^" # Start of the line. "(?P<date>\d{4}-\d{2}-\d{2})" # Date in YYYY-MM-DD format. " " # A single space. "(?P<payee>\S(?:\S| (?! ))*)" # Payee name. "\s{2,}") # Two or more spaces. account_in_transaction_re = re.compile( "\s+" # Leading whitespace. "(?P<account>\S(?:\S| (?! ))*)" # Account name. "\s{2,}" # Two or more spaces. "(?P<balance>\S(?:\S| (?! ))*)" # Balance. "\s{2,}" # Two or more spaces. "\S(?:\S| (?! ))*" # Total within transaction (ignored). "$") # End of the line. # 1000 columns wide is an arbitrary limit we expect never to hit. # It prevents truncation of long payee names/account names. output = subprocess.check_output( ["ledger", "reg", "--columns", "1000", "--date-format", _DATE_FMT], env=os.environ.copy() ).strip().decode() output_lines = output.split("\n") transactions = [] cur_transaction = None for line in output_lines: new_tx = new_transaction_re.search(line) if new_tx: if cur_transaction: transactions.append(cur_transaction) tx_date = datetime.datetime.strptime( new_tx.group("date"), _DATE_FMT) tx_date = datetime.date(tx_date.year, tx_date.month, tx_date.day) cur_transaction = { "date": tx_date, "payee": new_tx.group("payee"), "accounts": [] } account = account_in_transaction_re.search(line) if account: cur_transaction["accounts"].append( {"account": account.group("account"), "amount": account.group("balance")}) if cur_transaction: transactions.append(cur_transaction) return transactions

Contents