feat: Added configurable user templates
This commit is contained in:
parent
0a1ecddf3d
commit
1f7a061600
98
grocy/cli.py
98
grocy/cli.py
@ -21,14 +21,6 @@ PROJ_DIR = path.join(path.dirname(path.realpath(__file__)))
|
|||||||
TEMPLATE_LOADER = Environment(loader=FileSystemLoader('templates'), trim_blocks=True, lstrip_blocks=True)
|
TEMPLATE_LOADER = Environment(loader=FileSystemLoader('templates'), trim_blocks=True, lstrip_blocks=True)
|
||||||
|
|
||||||
|
|
||||||
def __validate_token(cfg):
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
def main():
|
def main():
|
||||||
cfg = Configuration()
|
cfg = Configuration()
|
||||||
@ -65,15 +57,49 @@ def stock(ctx):
|
|||||||
logger = logging.getLogger('cli.stock')
|
logger = logging.getLogger('cli.stock')
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
try:
|
try:
|
||||||
entity = Stock()
|
stock = Stock()
|
||||||
stocks = entity.get()
|
table = Table(stocks=stock.products)
|
||||||
table = Table(stocks=stocks)
|
|
||||||
click.echo(table.stock)
|
click.echo(table.stock)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
@main.group(invoke_without_command=True)
|
||||||
|
@click.pass_context
|
||||||
|
@click.argument('product_id', required=False)
|
||||||
|
def product(ctx, product_id):
|
||||||
|
logger = logging.getLogger('cli.product')
|
||||||
|
try:
|
||||||
|
if product_id:
|
||||||
|
stock = Stock()
|
||||||
|
product = stock.get_product(product_id)
|
||||||
|
# Need to get quantity_unit
|
||||||
|
table = Table(entry=product)
|
||||||
|
click.echo(table.product)
|
||||||
|
else:
|
||||||
|
click.echo(ctx.get_help())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#@product.command('list')
|
||||||
|
#@click.argument('query', required=False)
|
||||||
|
#def view(query, product_id):
|
||||||
|
# logger = logging.getLogger('cli.product.view')
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# if product_id:
|
||||||
|
# entity = Entity(name='product')
|
||||||
|
# product = entity.get(id=product_id)
|
||||||
|
# table = Table(product=product)
|
||||||
|
# click.echo(table.product)
|
||||||
|
# else:
|
||||||
|
# except Exception as e:
|
||||||
|
# logger.error(e)
|
||||||
|
# raise e
|
||||||
|
|
||||||
|
|
||||||
@main.command()
|
@main.command()
|
||||||
def shopping():
|
def shopping():
|
||||||
logger = logging.getLogger('cli.shopping')
|
logger = logging.getLogger('cli.shopping')
|
||||||
@ -88,34 +114,55 @@ def shopping():
|
|||||||
|
|
||||||
|
|
||||||
@main.group()
|
@main.group()
|
||||||
def ingredient(ctx):
|
def ingredient():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ingredient.command('add')
|
@ingredient.command('add')
|
||||||
@click.option('-r', '--recipe_id')
|
@click.argument('query')
|
||||||
def add(ctx, recipe_id):
|
@click.argument('recipe_id')
|
||||||
|
@click.option('--amount', '-a', 'amount', default=1, type=int)
|
||||||
|
@click.option('--group', '-g', type=str, default='')
|
||||||
|
@click.option('--variable-amount', '--va', 'variable_amount', default=None, type=float)
|
||||||
|
@click.option('--in-stock', '--is', 'in_stock', default=False)
|
||||||
|
@click.option('--disable-fulfillment', '--df', 'disable_fulfillment', default=False)
|
||||||
|
@click.option('--note', '-n', 'note', multiple=True, default='', type=str)
|
||||||
|
@click.option('--no-edit', '--ne', 'no_edit', default=False)
|
||||||
|
def add(query, recipe_id, amount, group, variable_amount, in_stock, disable_fulfillment, note, no_edit):
|
||||||
logger = logging.getLogger('cli.ingredient.add')
|
logger = logging.getLogger('cli.ingredient.add')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
loaded_template = TEMPLATE_LOADER.get_template('ingredient_add.yml')
|
loaded_template = TEMPLATE_LOADER.get_template('ingredient_add.yml')
|
||||||
|
new_ingredient = {}
|
||||||
|
|
||||||
if recipe_id:
|
|
||||||
entity = Entity(name='recipes')
|
entity = Entity(name='recipes')
|
||||||
recipe = entity.get(id=recipe_id)
|
recipe = entity.get(id=recipe_id)
|
||||||
new_ingredient = click.edit(loaded_template.render(recipe))
|
|
||||||
parsed_new_ingredient = yaml.safe_load(new_ingredient)
|
|
||||||
else:
|
|
||||||
new_ingredient = click.edit(loaded_template.render())
|
|
||||||
parsed_new_ingredient = yaml.safe_load(new_ingredient)
|
|
||||||
if parsed_new_ingredient['recipe_id']:
|
|
||||||
raise Exception('Recipe id is not defined')
|
|
||||||
|
|
||||||
if new_ingredient:
|
if not recipe:
|
||||||
entity = Entity(name='ingredients', **parsed_new_ingredient)
|
raise click.BadParameter(message='recipe {id} does not exist', param='recipe_id',
|
||||||
entity.create()
|
param_hint='Use `grocy recipes ls` to get a list of recipes')
|
||||||
|
|
||||||
|
entity = Entity(name='products')
|
||||||
|
product = entity.findOne(query)
|
||||||
|
new_ingredient['product_id'] = product['id']
|
||||||
|
|
||||||
|
new_ingredient['amount'] = amount
|
||||||
|
new_ingredient['group'] = group
|
||||||
|
new_ingredient['variable_amount'] = variable_amount
|
||||||
|
new_ingredient['only_check_single_unit_in_stock'] = "1" if in_stock else "0"
|
||||||
|
new_ingredient['not_check_stock_fulfillment'] = "1" if disable_fulfillment else "0"
|
||||||
|
new_ingredient['note'] = note
|
||||||
|
|
||||||
|
if not no_edit:
|
||||||
|
new_ingredient = click.edit(loaded_template.render(new_ingredient))
|
||||||
|
|
||||||
|
parsed_new_ingredient = yaml.safe_load(new_ingredient)
|
||||||
|
entity = Entity(name='recipes_pos')
|
||||||
|
#entity.create(parsed_new_ingredient)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@main.group()
|
@main.group()
|
||||||
@ -138,6 +185,7 @@ def ls():
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@recipe.command('edit')
|
@recipe.command('edit')
|
||||||
@click.argument('recipe_id')
|
@click.argument('recipe_id')
|
||||||
def edit(recipe_id):
|
def edit(recipe_id):
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
from os import path, chmod, makedirs
|
from os import path, chmod, makedirs
|
||||||
|
from shutil import copy
|
||||||
from yaml import safe_load, dump
|
from yaml import safe_load, dump
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
class Configuration(object):
|
class Configuration(object):
|
||||||
# TODO: Figure out how to handle windows config
|
# TODO: Figure out how to handle windows config
|
||||||
CONFIG_DIR = path.expanduser('~/.config/grocy')
|
CONFIG_DIR = path.expanduser('~/.config/grocy')
|
||||||
|
USER_TEMPLATE_DIR = path.expanduser('~/.config/grocy/templates')
|
||||||
CONFIG_FILE = CONFIG_DIR + '/config.yml'
|
CONFIG_FILE = CONFIG_DIR + '/config.yml'
|
||||||
|
PROJ_DIR = path.join(path.dirname(path.realpath(__file__)))
|
||||||
|
PROJ_TEMPLATE_DIR = '{}/templates'.format(PROJ_DIR)
|
||||||
API_KEY_HEADER = 'GROCY-API-KEY'
|
API_KEY_HEADER = 'GROCY-API-KEY'
|
||||||
DEFAULT_CFG = {
|
DEFAULT_CFG = {
|
||||||
'logger': {
|
'logger': {
|
||||||
@ -72,6 +77,11 @@ class Configuration(object):
|
|||||||
fd.write(dump_cfg)
|
fd.write(dump_cfg)
|
||||||
chmod(self.CONFIG_DIR, 0o755)
|
chmod(self.CONFIG_DIR, 0o755)
|
||||||
|
|
||||||
|
# Create template directory
|
||||||
|
makedirs(self.USER_TEMPLATE_DIR)
|
||||||
|
copy(self.PROJ_TEMPLATE_DIR, self.USER_TEMPLATE_DIR)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def exists(self):
|
def exists(self):
|
||||||
return path.exists(self.CONFIG_FILE)
|
return path.exists(self.CONFIG_FILE)
|
||||||
@ -83,3 +93,12 @@ class Configuration(object):
|
|||||||
self.update(data)
|
self.update(data)
|
||||||
else:
|
else:
|
||||||
self.create()
|
self.create()
|
||||||
|
|
||||||
|
def templates(self, name):
|
||||||
|
try:
|
||||||
|
TEMPLATE_LOADER = Environment(loader=FileSystemLoader([self.USER_TEMPLATE_DIR, self.PROJ_TEMPLATE_DIR]),
|
||||||
|
trim_blocks=True, lstrip_blocks=True)
|
||||||
|
return TEMPLATE_LOADER.get_template(name)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class Entity(object):
|
|||||||
logger = logging.getLogger('entity.add')
|
logger = logging.getLogger('entity.add')
|
||||||
url = self.RESOURCE_URL_TEMPLATE
|
url = self.RESOURCE_URL_TEMPLATE
|
||||||
|
|
||||||
request = Request('post', url)
|
request = Request('post', url, entity)
|
||||||
try:
|
try:
|
||||||
return request.send()
|
return request.send()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -5,14 +5,13 @@ import logging
|
|||||||
|
|
||||||
class Stock(object):
|
class Stock(object):
|
||||||
GET_STOCK_URL_TEMPLATE = '{api}/stock'
|
GET_STOCK_URL_TEMPLATE = '{api}/stock'
|
||||||
|
GET_STOCK_PRODUCT_DETAIL_TEMPLATE = '{api}/stock/products/{product_id}'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.conf = Configuration()
|
self.conf = Configuration()
|
||||||
self.conf.load()
|
self.conf.load()
|
||||||
|
|
||||||
def get(self):
|
def __get_resources(self, url, logger):
|
||||||
logger = logging.getLogger('stock.get')
|
|
||||||
url = self.GET_STOCK_URL_TEMPLATE.format(api=self.conf.api)
|
|
||||||
request = Request('get', url)
|
request = Request('get', url)
|
||||||
try:
|
try:
|
||||||
return request.send()
|
return request.send()
|
||||||
@ -20,3 +19,13 @@ class Stock(object):
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def products(self):
|
||||||
|
logger = logging.getLogger('stock.products')
|
||||||
|
url = self.GET_STOCK_URL_TEMPLATE.format(api=self.conf.api)
|
||||||
|
return self.__get_resources(url, logger)
|
||||||
|
|
||||||
|
def get_product(self, product_id):
|
||||||
|
logger = logging.getLogger('stock.get_product')
|
||||||
|
url = self.GET_STOCK_PRODUCT_DETAIL_TEMPLATE.format(api=self.conf.api, product_id=product_id)
|
||||||
|
return self.__get_resources(url, logger)
|
||||||
|
@ -2,6 +2,7 @@ from grocy.entity import Entity
|
|||||||
import re
|
import re
|
||||||
from grocy.conf import Configuration
|
from grocy.conf import Configuration
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
|
|
||||||
@ -15,6 +16,13 @@ class Table(object):
|
|||||||
self.conf = Configuration()
|
self.conf = Configuration()
|
||||||
self.conf.load()
|
self.conf.load()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def product(self):
|
||||||
|
if not self.entry:
|
||||||
|
raise Exception('Missing product')
|
||||||
|
loaded_template = self.conf.templates('single_product')
|
||||||
|
return loaded_template.render(self.entry)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stock(self):
|
def stock(self):
|
||||||
logger = logging.getLogger('table.stock')
|
logger = logging.getLogger('table.stock')
|
||||||
|
13
templates/single_product
Normal file
13
templates/single_product
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
Name: {{product.name}}
|
||||||
|
|
||||||
|
Stock Quantity Unit: {{quantity_unit_stock.name}}
|
||||||
|
|
||||||
|
Last Purchased: {{last_purchased}}
|
||||||
|
|
||||||
|
Last Used: {{last_used}}
|
||||||
|
|
||||||
|
Last Price: {{last_price}}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user