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

Documentation #12

Merged
merged 18 commits into from
Apr 20, 2018
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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017 Robert RObinson
Copyright (c) 2017 Robert Robinson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# GNUCash Reporting

A simple python library for generating reports from GNUCash data files. This repository provides a framework and application. The output is a collection of JSON files that can be displayed using a viewer application (similar to: [GNUCash Reporting Viewer](https://github.com/MeerkatLabs/gnucash-reporting-view)).
A simple python library for generating reports from a GNUCash database. This repository provides a framework and
application. The output is a collection of JSON files that can be displayed using a viewer application (similar to:
[GNUCash Reporting Viewer](https://github.com/MeerkatLabs/gnucash-reporting-view)).

## Requirements

* GNUCash Python Bindings
* Piecash
* PyYaml
* SimpleJSON
* NumPy
Expand All @@ -17,8 +19,8 @@ gnucash_reports -c configuration.yaml

## Documentation

Stored in the documentation folder of this project.
`TODO`

## License

BSD3 Clause License
MIT License
1 change: 0 additions & 1 deletion gnucash_reports/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
__author__ = 'rerobins'
1 change: 0 additions & 1 deletion gnucash_reports/collate/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
__author__ = 'rerobins'
52 changes: 43 additions & 9 deletions gnucash_reports/collate/bucket.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Data structures that will be used to sort data into buckets and return the results. These are essentially helpers for
dictionaries that will execute a hashing function and update the contents of the hash value with the new data.
"""
from dateutil.rrule import MONTHLY

import gnucash_reports.collate.bucket_generation as generator
Expand All @@ -7,15 +11,15 @@
class BucketCollate(object):
"""
Wrapper that will collate a collection of data based on the hash_method and store_methods provided.
Also provides functionality for generation of the buckets as well.
"""

def __init__(self, bucket_generation, hash_method, store_function):
"""
Create a bucket sort function.
:param bucket_generation:
:param hash_method:
:param store_function:
:param bucket_generation: function that creates a list of buckets (dictionary) for data to be stored in.
:param hash_method: hash method that will be used to convert values into keys if keys are not defined when
storing values.
:param store_function: function that defines how the content will be stored in the bucket.
"""
self._bucket_generation = bucket_generation
self._hash_method = hash_method
Expand All @@ -24,9 +28,11 @@ def __init__(self, bucket_generation, hash_method, store_function):
self._container = self._bucket_generation()

def reinitialize(self):
"""Clear the containers with brand new content."""
self._container = self._bucket_generation()

def store_value(self, value, key=None):
"""Store a value ine the container using the store function defined during initialization."""
if key is None:
key = self._hash_method(value)
bucket = self._container[key]
Expand All @@ -35,33 +41,61 @@ def store_value(self, value, key=None):

@property
def container(self):
"""Dictionary that contains the buckets."""
return self._container


class PeriodCollate(BucketCollate):
"""
Bucket Collation based on collecting data based on a period of dates.
"""
def __init__(self, start, end, default_value_generator, store_function, frequency=MONTHLY, interval=1):
super(PeriodCollate, self).__init__(generator.monthly_buckets(start, end,
default_value_generator=default_value_generator,
frequency=frequency, interval=interval),
"""
Initializer.
:param start: start date
:param end: end date.
:param default_value_generator: method that will generate the default values for all of the buckets.
:param store_function: function that will be used to update the contents of the bucket when storing a value.
:param frequency: how large are the buckets (Monthly, daily, yearly) should be a dateutil.rrule enumeration.
:param interval: how many frequencies are stored in a bucket.
"""
super(PeriodCollate, self).__init__(generator.frequency_buckets(start, end,
default_value_generator=default_value_generator,
frequency=frequency, interval=interval),
keys.period(start, end, frequency=frequency, interval=interval),
store_function)


class CategoryCollate(BucketCollate):
"""
Collage all of the splits into buckets based on the category that their account is defined in.
Collate incoming splits into buckets based on the category that their account is defined in. Categories are based
on the configuration values defined in gnucash_reports.configuration.expense_categories
"""

def __init__(self, default_value_generator, store_function):
"""
Initializer
:param default_value_generator: function that creates a default value for each of the buckets.
:param store_function: function that will be used to update the content of the bucket when storing a split
value.
"""
super(CategoryCollate, self).__init__(generator.category_buckets(default_value_generator),
keys.category_key_fetcher,
store_function)


class AccountCollate(BucketCollate):
"""
Collage all of the splits into buckets based on the category that their account is defined in.
Collate all of the splits into buckets based on the last account name in the split's account tree.
"""

def __init__(self, default_value_generator, store_function):
"""
Initializer.
:param default_value_generator: function that creates a default value for each of the buckets.
:param store_function: function that will be used to update the content of the bucket when storing a split
value.
"""
super(AccountCollate, self).__init__(generator.category_buckets(default_value_generator),
keys.account_key_fetcher,
store_function)
23 changes: 19 additions & 4 deletions gnucash_reports/collate/bucket_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,31 @@


def decimal_generator():
"""Generates a single bucket containing a decimal object initialized to 0.0."""
return Decimal('0.0')


def integer_generator():
"""Generates a single bucket that contains a 0 integer."""
return int()


def debit_credit_generator():
"""Create a single bucket that contains a dictionary containing values for debit and credit values."""
return dict(debit=Decimal('0.0'), credit=Decimal('0.0'))


def monthly_buckets(start, end, frequency=MONTHLY, interval=1, default_value_generator=decimal_generator):
def frequency_buckets(start, end, frequency=MONTHLY, interval=1, default_value_generator=decimal_generator):
"""
Configure a method that will define all of the buckets based on stepping frequency distance between the start and
end dates provided.
:param start: start date for the collator.
:param end: end date for the collator.
:param frequency: how large of a step will the buckets contain (must be a dateutil.rrule enumeration)
:param interval: how many steps in a bucket.
:param default_value_generator: function used to generate the initialized value for the bucket.
:return: function that will generate the buckets based on the period definition details created.
"""

def generate_buckets():
results = dict()
Expand All @@ -30,9 +43,11 @@ def generate_buckets():

def category_buckets(default_value_generator):
"""
Create a default dictionary that will generate the buckets if they are missing.
:param default_value_generator: value generator.
:return:
Method generator that will create the collator collection. Uses a default dictionary that will retrieve its
initial value from the default value generator method provided.
:param default_value_generator: function that creates the default value for the bucket.
:return: function that will create a default dictionary using default value generator method to generate default
values.
"""
def generate_buckets():
return defaultdict(default_value_generator)
Expand Down
31 changes: 14 additions & 17 deletions gnucash_reports/collate/key_generator.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
from datetime import date

import re
from dateutil.rrule import rrule, MONTHLY

from gnucash_reports.configuration.expense_categories import get_category_for_account
from gnucash_reports.utilities import clean_account_name


def monthly(data_key):
def period(start, end, frequency=MONTHLY, interval=1):
"""
Returns the bucket that the hash value should be stored into based on the data key that is provided.
:param data_key: data key value.
:return: hash key value.
Defines a key generation method that will hash a split's transaction's post date into the appropriate bucket key
for the period bucket that was defined.
:param start: start date of all buckets
:param end: end date of all buckets
:param frequency: size of the bucket using dateutil.rrule enumeration
:param interval: how many frequencies in a bucket
:return: a method that will hash the incoming split value into a date key for the collator.
"""
split_date = data_key.transaction.post_date.replace(tzinfo=None, microsecond=0)
return date(split_date.year, split_date.month, 1)


def period(start, end, frequency=MONTHLY, interval=1):

intervals = rrule(frequency, start, interval=interval, until=end)

Expand All @@ -30,17 +27,17 @@ def method(data_key):

def category_key_fetcher(data_key):
"""
Look up the category that is associated with the account defined in the split.
:param data_key: split
:return:
Find the category that the account name belongs to.
:param data_key: transaction split
:return: string
"""
return get_category_for_account(clean_account_name(data_key.account.fullname))


def account_key_fetcher(data_key):
"""
Look up the category that is associated with the account defined in the split.
:param data_key: split
:return:
Return the last account name in the tree. Account names are split on both : and . characters.
:param data_key: transaction split
:return: string
"""
return re.split('[:.]', data_key.account.fullname)[-1]
25 changes: 11 additions & 14 deletions gnucash_reports/collate/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@

def split_summation(bucket, value):
"""
sum the new value to the value in the bucket.
:param bucket: Decimal object
:param value: Split object
:return: new bucket value
Add value into the contents of the bucket.
:param bucket: Summation of all splits that have been stored in this bucket.
:param value: Split object to add to bucket
:return: new bucket value as a decimal.
"""
bucket += value.value
return bucket


def store_credit_debit(bucket, value):
"""
Store the value of the split into the buckets value. If the decimal is positive it is considered a credit,
otherwise it is a debit.
:param bucket: dictionary containing debits and credits.
:param value: a decimal or a split
:return:
"""
if isinstance(value, Decimal):
decimal_value = value
else:
Expand All @@ -24,13 +31,3 @@ def store_credit_debit(bucket, value):
bucket['credit'] += decimal_value

return bucket


def store_summation(bucket, value):
bucket += value
return bucket


def count(bucket, value):
bucket += 1
return bucket
19 changes: 8 additions & 11 deletions gnucash_reports/commands/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
import argparse
import glob
import logging
import sys

import os
import os.path
import simplejson as json

from gnucash_reports.wrapper import initialize
from gnucash_reports.reports import run_report
from gnucash_reports.configuration import configure_application
from gnucash_reports.utilities import load_plugins
from datetime import datetime

from yaml import load

try:
Expand All @@ -20,14 +25,6 @@

logger = logging.getLogger(__name__)

sys.path.insert(0, os.path.abspath(os.getcwd()))

from gnucash_reports.wrapper import initialize
from gnucash_reports.reports import run_report
from gnucash_reports.configuration import configure_application
from gnucash_reports.utilities import load_plugins
from datetime import datetime


def main():
"""
Expand Down Expand Up @@ -77,7 +74,7 @@ def main():

for report_definition in report_configuration['definitions']:

result = run_report(report_definition)
result = run_report(**report_definition)

if result:
result_definition['reports'].append(result)
Expand Down
3 changes: 2 additions & 1 deletion gnucash_reports/commands/update_prices.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def main():
if value is None:
continue

print 'Setting value of: %s to %s %s for date: %s' % (commodity.mnemonic, value, currency.get_currency(), quote_date)
print 'Setting value of: %s to %s %s for date: %s' % (commodity.mnemonic, value, currency.get_currency(),
quote_date)

Price(currency=currency.get_currency(),
commodity=commodity,
Expand Down
2 changes: 1 addition & 1 deletion gnucash_reports/configuration/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .base import configure_application
from gnucash_reports.configuration.base import configure_application
24 changes: 15 additions & 9 deletions gnucash_reports/configuration/alphavantage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,31 @@
import requests

_API_KEY = None
BASE_URL='https://www.alphavantage.co/query'
FUNCTION='TIME_SERIES_DAILY'
BASE_URL = 'https://www.alphavantage.co/query'
FUNCTION = 'TIME_SERIES_DAILY'

META_DATA_KEY='Meta Data'
LAST_REFRESHED='3. Last Refreshed'
META_DATA_KEY = 'Meta Data'
LAST_REFRESHED = '3. Last Refreshed'

DATA_KEY='Time Series (Daily)'
CLOSE_KEY='4. close'
DATA_KEY = 'Time Series (Daily)'
CLOSE_KEY = '4. close'

DATE_FORMAT = '%Y-%m-%d'


def configure(json_configuration):
def configure(configuration):
"""Read the alpha vantage key out of the configuration object and store it in the api key global."""
global _API_KEY
_API_KEY = json_configuration.get('alpha_vantage', dict()).get('api_key', None)
_API_KEY = configuration.get('alpha_vantage', dict()).get('api_key', None)


def get_price_information(symbol, date=None):

"""
Accessor for retrieving the price information from alphavantage web service.
:param symbol: ticker symbol to look up.
:param date: date to look up content for. If None, will return the data for the last updated object.
:return: tuple containing the date and the closing value for the symbol.
"""
payload = {
'function': FUNCTION,
'apikey': _API_KEY,
Expand Down
Loading