feat(added command to retrieve stock overview):

This commit is contained in:
Aerex 2019-03-03 19:29:38 -06:00
commit a5f290d0e7
14 changed files with 279 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
venv
grocy_cli.egg-info/

1
README.rst Normal file
View File

@ -0,0 +1 @@
.. readme

78
grocy/__init__.py Normal file
View File

@ -0,0 +1,78 @@
import requests
import json
class RestService(object):
API_KEY_HEADER = 'GROCY-API-KEY'
def __init__(self, api_url, json=False):
self.api_url = api_url
self.headers = {}
self.json = json
def get(self, path, id=None):
# TODO: change this to a single pattern assume a pattern then stick with it
if self.api_url.endswith('/'):
url = '{0}{1}'.format(self.api_url, path[1:])
else:
url = '{0}{1}'.format(self.api_url, path)
if id:
url = '{0}/{1}'.format(url, id)
r = requests.get(url, headers=self.headers)
if r.raise_for_status():
logger.error(r.raise_for_status())
raise r.raise_for_status()
if self.json:
return r.json()
return r.content
def delete(self, path, id):
api_url = self.api_url
if api_url.endswith('/'):
url = '{0}{1}'.format(api_url, path[1:])
else:
url = '{0}/{1}'.format(api_url, path)
r = requests.get(url, headers=self.headers)
if r.raise_for_status():
logger.error(r.raise_for_status())
raise r.raise_for_status()
if self.json:
return r.json()
return r.content
def post(self, path, payload):
api_url = self.api_url
if self.api_url.endswith('/'):
api_url = api_url[1:]
if path.startswith('/'):
url ='{0}{1}'.format(api_url, path)
else:
url = '{0}/{1}'.format(api_url, path)
print('{}'.format(url))
r = requests.post(url, data=json.dumps(payload), headers=self.headers)
#if r.raise_for_status():
# logger.error(r.raise_for_status())
# raise r.raise_for_status()
if self.json:
return r.json()
return r.content
def addHeader(self, type, value):
self.headers[type] = value
def addToken(self, value):
self.headers[RestService.API_KEY_HEADER] = value;

Binary file not shown.

Binary file not shown.

Binary file not shown.

75
grocy/cli.py Normal file
View File

@ -0,0 +1,75 @@
import click
from grocy import RestService
from grocy.commands import Stock
from pkg_resources import iter_entry_points
import yaml
from sys import exit
from shutil import copy
from os import path, chmod, makedirs
from taskw import TaskWarriorShellout
import logging
logger = logging.getLogger(__name__)
APP_NAME = 'grocy-cli'
SAMPLE_CONFIG_FILE = 'sample.config.yml'
CONFIG_FILE = 'config.yml'
CONFIG_DIR = click.get_app_dir(APP_NAME)
PROJ_DIR = path.join(path.dirname(path.realpath(__file__)))
def __create_config_file():
user_cfg_file = path.join(CONFIG_DIR, CONFIG_FILE)
sample_cfg_file = path.join(PROJ_DIR, '..', SAMPLE_CONFIG_FILE)
if not path.exists(CONFIG_DIR):
click.echo('Config {} director does not exist, create...'.format(CONFIG_DIR))
makedirs(CONFIG_DIR)
copy(sample_cfg_file, user_cfg_file)
click.echo('Copying sample config to {}'.format(sample_cfg_file, user_cfg_file))
chmod(user_cfg_file, 0o664)
return user_cfg_file
@click.group()
def main():
pass
def get_config_file():
no_config_msg = 'A config file was not found'
no_config_msg+= ' and will be created under {}. Is that Ok?'
no_config_msg = no_config_msg.format(click.get_app_dir(APP_NAME))
cfg_file = path.join(click.get_app_dir(APP_NAME), 'config.yml')
if not path.exists(cfg_file):
create_config_app_dir = click.confirm(no_config_msg)
if create_config_app_dir:
cfg_file = __create_config_file()
exit(0)
fd = open(cfg_file)
parse_cfg_file = yaml.safe_load(fd)
return parse_cfg_file
@main.command()
def stock():
cfg = get_config_file()
# Get logger
if 'logger' in cfg:
log_cfg = cfg['logger']
else:
log_cfg = path.join(click.get_app_dir(APP_NAME), 'grocy.log')
log_level = 'DEBUG' if 'level' not in log_cfg else log_cfg['level']
log_filename = 'log' if 'file_location' not in log_cfg else log_cfg['file_location']
logging.basicConfig(level=log_level, filename=log_filename)
# Validate token
if hasattr(cfg, 'token') or cfg['token'] is None:
click.echo('No token was found. Please add your token to the config file', err=True)
logger.error('No token was found. Please add your token')
exit(1)
stock = Stock(**cfg)
stock_entries = stock.get_entries()
click.echo(stock_entries)

View File

@ -0,0 +1 @@
from grocy.commands.stock import Stock

Binary file not shown.

Binary file not shown.

64
grocy/commands/stock.py Normal file
View File

@ -0,0 +1,64 @@
from grocy import RestService
from tabulate import tabulate
from os import path
class Stock(object):
GET_CURRENT_STOCK = '/stock/get-current-stock'
GET_PRODUCT_BY_ID = '/get-object/products/{0}'
def __init__(self, **entries):
self.__dict__.update(entries)
self._init_rest_service()
#self._set_default_table_formats()
#if not hasattr('tablefmt', self):
# self.tablefmt = None
#if not hasattr('colalign', self):
# self.colalign = None
def _set_default_table_formats(self):
if not hasattr('formats', self):
self.tablefmt = None
self.colalign = None
elif not hasattr('table', self.formats):
self.tableformat = None
elif not hasattr('col', self.formats):
self.colalign = None
def _init_rest_service(self):
if self.api.startswith == '/':
self.api = self.api[1:]
if self.api.endswith == '/':
self.api = self.api[1:-1]
self.rest_service = RestService(self.api, json=True)
self.rest_service.addHeader('Content-Type', 'application/json')
self.rest_service.addToken(self.token)
def get_entries(self):
try:
get_current_stock = self.rest_service.get(Stock.GET_CURRENT_STOCK)
# Get product names from ids and replace
product_ids = [entry['product_id'] for entry in get_current_stock]
table_entries = []
try:
for index in range(len(product_ids)):
product_id = product_ids[index]
path = Stock.GET_PRODUCT_BY_ID.format(product_id)
product = self.rest_service.get(path)
get_current_stock[index]['product_id'] = product['name']
table_entry = list(dict.values(get_current_stock[index]))
table_entries.append(table_entry)
except Exception as e:
raise e
# Generate stock overview table
table_headers = ['Product', 'Amount', 'Best Before Date', 'Amount Opened']
return tabulate(table_entries, headers=table_headers)
except Exception as e:
raise e

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
click
colorama
pyyaml
requests
lockfile
tox
tabulate

9
sample.config.yml Normal file
View File

@ -0,0 +1,9 @@
logger:
level: DEBUG
file_location: ''
api: ''
token: ''
formats:
col: 'center'
table: 'simple'

42
setup.py Normal file
View File

@ -0,0 +1,42 @@
import os
from setuptools import setup, find_packages
f = open('README.rst')
long_description = f.read().strip()
long_description = long_description.split('readme', 1)[1]
f.close()
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name='grocy-cli',
version='0.0.1',
author='Aerex',
author_email='aerex@aerex.me',
description=('A plugin to create, delete, and modify tasks across various services'),
keywords='taskwarrior, grocy',
url='http://packages.python.org/an_example_pypi_project',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=['Click', 'pyyaml', 'requests', 'taskw', 'lockfile', 'tox'],
long_description=read('README.rst'),
tests_require=[
"pytest_mock",
"pytest",
"mock"
],
classifiers=[
"Development Status :: 3 - Alpha",
"Topic :: Utilities",
"License :: OSI Approved :: BSD License",
],
entry_points='''
[console_scripts]
grocy=grocy.cli:main
''',
)