Initial commit

This commit is contained in:
2014-04-19 23:47:56 +01:00
commit 39c24efc74
9 changed files with 259 additions and 0 deletions

56
.gitignore vendored Normal file
View File

@@ -0,0 +1,56 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
# Sphinx documentation
docs/_build/
# PyCharm
.idea/

6
AUTHORS Normal file
View File

@@ -0,0 +1,6 @@
Andrew Williams <andy@tensixtyone.com>
Inspiration and file format information from YNAB4 Alfred Workflow add-on, created by:
James Seward
"ppiixx"

12
LICENSE Normal file
View File

@@ -0,0 +1,12 @@
Copyright (c) Andrew Williams, 2014
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

26
README.md Normal file
View File

@@ -0,0 +1,26 @@
pYNAB
=====
```pYNAB``` is a read-only YNAB4 budget file reader for Python, with support for Python 2.7 and 3.2+
Why?
----
YNAB reports are OK, to give you a quick overview of your spending but outside that you'll need to export your information and put it into Excel or another application to manually work through. Python has amazing libraries like Pandas available to really chew through data, so the library was designed to scratch these itches.
Installation
------------
pYNAB is a standard Python package, installation can be done via ```setup.py```
Usage
-----
At the moment only very limited module documentation exists, in the near future we'll have some Sphinx documentation
Testing
-------
We have extremely basic testing at the moment, which can be executed via ```setup.py``` using ```python setup.py test```

4
pynab/__init__.py Normal file
View File

@@ -0,0 +1,4 @@
__author__ = 'Andrew Williams <andy@tensixtyone.com>'
__version__ = '0.1'
from pynab.budget import Budget

103
pynab/budget.py Normal file
View File

@@ -0,0 +1,103 @@
import os
from json import load
import locale
from pynab.exceptions import InvalidBudget
class Category(object):
pass
class Account(dict):
@property
def id(self):
return self['entityId']
@property
def name(self):
return self['name']
@property
def account_type(self):
return self['accountType']
@property
def note(self):
return self['note']
@property
def hidden(self):
return self['hidden']
@property
def on_budget(self):
return self['onBudget']
def __unicode__(self):
return u'{} ({})'.format(self['accountName'], self['accountType'])
def __repr__(self):
return u'<{}>'.format(self.__unicode__())
class Budget(object):
def __init__(self, filename=None):
self._data = None
if filename:
self.load(filename)
def load(self, filename):
# Load the budget's metadata
if not os.path.exists(os.path.join(filename, 'Budget.ymeta')):
raise InvalidBudget('{} is a invalid YNAB budget')
with open(os.path.join(filename, 'Budget.ymeta'), 'r') as f:
meta = load(f)
# Find the relative folder name
data_folder = os.path.join(filename, meta['relativeDataFolderName'])
# Check the devices, and see which is tagged with full knowledge
devices_folder = os.path.join(data_folder, 'devices')
for device in os.listdir(devices_folder):
with open(os.path.join(devices_folder, device)) as f:
device_info = load(f)
if device_info['hasFullKnowledge']:
target_folder = device_info['deviceGUID']
break
else:
raise InvalidBudget('No device has full budget data')
# Load the full budget file
with open(os.path.join(data_folder, target_folder, 'Budget.yfull')) as f:
self._data = load(f)
@property
def budget_type(self):
"""
Returnt the budget type
"""
if self._data:
return self._data['budgetMetaData']['budgetType']
@property
def currency_locale(self):
"""
Returns the budget's currency locale
"""
if self._data:
return self._data['budgetMetaData']['currencyLocale']
@property
def date_locale(self):
"""
Returns the budget's date locale
"""
if self._data:
return self._data['budgetMetaData']['dateLocale']
@property
def accounts(self):
return [Account(account) for account in self._data['accounts']]

8
pynab/exceptions.py Normal file
View File

@@ -0,0 +1,8 @@
class InvalidBudget(Exception):
def __init__(self, msg):
self.msg = msg
def __unicode__(self):
return self.msg

1
pynab/reader.py Normal file
View File

@@ -0,0 +1 @@

43
setup.py Normal file
View File

@@ -0,0 +1,43 @@
from __future__ import print_function
from setuptools import setup
import io
import os
import pynab
here = os.path.abspath(os.path.dirname(__file__))
def read(*filenames, **kwargs):
encoding = kwargs.get('encoding', 'utf-8')
sep = kwargs.get('sep', '\n')
buf = []
for filename in filenames:
with io.open(filename, encoding=encoding) as f:
buf.append(f.read())
return sep.join(buf)
long_description = read('README.md')
setup(
name='pynab',
version=pynab.__version__,
url='http://github.com/nikdoof/pynab/',
license='BSD',
author='Andrew Williams',
author_email='andy@tensixtyone.com',
description='YNAB4 budget parser for Python',
long_description=long_description,
packages=['pynab'],
platforms='any',
classifiers = [
'Programming Language :: Python',
'rogramming Language :: Python :: 3',
'Development Status :: 2 - Pre-Alpha',
'Natural Language :: English',
'Intended Audience :: Developers',
'icense :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Topic :: Software Development :: Libraries :: Python Modules',
],
)