2019-10-13 15:27:10 -05:00
|
|
|
import logging
|
2019-03-03 19:29:38 -06:00
|
|
|
import click
|
2019-04-28 16:12:05 -05:00
|
|
|
from markdown import markdown
|
2019-07-07 13:58:01 -05:00
|
|
|
from html2text import html2text
|
2019-04-01 00:16:32 -05:00
|
|
|
from jinja2 import Environment, FileSystemLoader
|
2019-07-07 13:58:01 -05:00
|
|
|
from grocy.meta import Meta
|
2019-06-16 23:54:10 -05:00
|
|
|
from grocy.conf import Configuration
|
2019-06-25 00:52:17 -05:00
|
|
|
from grocy.util import Util
|
2019-06-16 23:54:10 -05:00
|
|
|
from grocy.recipe import Recipe
|
|
|
|
from grocy.table import Table
|
|
|
|
from grocy.entity import Entity
|
2019-11-02 00:41:09 -05:00
|
|
|
from grocy.chore import Chore
|
2019-06-16 23:54:10 -05:00
|
|
|
from grocy.stock import Stock
|
2019-09-25 23:14:11 -05:00
|
|
|
from grocy.schema import get_schema
|
2019-10-29 23:48:29 -05:00
|
|
|
from grocy.shoppinglist import ShoppingList
|
2019-03-03 19:29:38 -06:00
|
|
|
import yaml
|
|
|
|
from sys import exit
|
2019-06-16 23:54:10 -05:00
|
|
|
from os import path
|
2019-03-03 19:29:38 -06:00
|
|
|
|
|
|
|
|
|
|
|
APP_NAME = 'grocy-cli'
|
|
|
|
SAMPLE_CONFIG_FILE = 'sample.config.yml'
|
2019-04-01 00:16:32 -05:00
|
|
|
TMP_DIR = '/tmp/grocy'
|
2019-03-03 19:29:38 -06:00
|
|
|
CONFIG_FILE = 'config.yml'
|
|
|
|
CONFIG_DIR = click.get_app_dir(APP_NAME)
|
|
|
|
PROJ_DIR = path.join(path.dirname(path.realpath(__file__)))
|
2019-06-16 23:54:10 -05:00
|
|
|
TEMPLATE_LOADER = Environment(loader=FileSystemLoader('templates'), trim_blocks=True, lstrip_blocks=True)
|
|
|
|
|
2019-08-04 23:59:28 -05:00
|
|
|
|
2019-06-25 00:52:17 -05:00
|
|
|
class GrocyGroup(click.Group):
|
|
|
|
def parse_args(self, ctx, args):
|
2019-10-30 00:33:59 -05:00
|
|
|
is_subcommand = True if args[0] in self.commands else False
|
2019-06-25 00:52:17 -05:00
|
|
|
if is_subcommand:
|
|
|
|
ctx.forward(self.commands[args[0]], args[1:])
|
|
|
|
super(GrocyGroup, self).parse_args(ctx, args)
|
|
|
|
|
2019-03-03 19:29:38 -06:00
|
|
|
|
|
|
|
@click.group()
|
2019-06-16 23:54:10 -05:00
|
|
|
def main():
|
|
|
|
cfg = Configuration()
|
|
|
|
if not cfg.exists:
|
|
|
|
no_config_msg = 'A config file was not found. A sample configuration file'
|
|
|
|
no_config_msg += ' will be created under {}. Is that Ok?'.format(cfg.CONFIG_DIR)
|
2019-03-03 19:29:38 -06:00
|
|
|
create_config_app_dir = click.confirm(no_config_msg)
|
2019-06-16 23:54:10 -05:00
|
|
|
user_cfg_options = {}
|
2019-03-03 19:29:38 -06:00
|
|
|
if create_config_app_dir:
|
2019-06-16 23:54:10 -05:00
|
|
|
user_cfg_options['logger_level'] = click.prompt('Enter logger level',
|
|
|
|
default='DEBUG')
|
|
|
|
user_cfg_options['logger_file_location'] = click.prompt('Enter location for logger',
|
|
|
|
default=path.expanduser('~/.config/grocy/log'))
|
|
|
|
user_cfg_options['api'] = click.prompt('Enter the grocy api url',
|
|
|
|
default='https://demo-en.grocy.info/api')
|
|
|
|
user_cfg_options['token'] = click.prompt('Enter the grocy token ',
|
|
|
|
default='')
|
|
|
|
user_cfg_options['col_format'] = click.prompt('Enter the col position for rendering tables',
|
|
|
|
default='col')
|
|
|
|
user_cfg_options['table_format'] = click.prompt('Enter the table format',
|
|
|
|
default='simple')
|
|
|
|
cfg.create(user_cfg_options)
|
|
|
|
else:
|
2019-03-03 19:29:38 -06:00
|
|
|
exit(0)
|
2019-06-16 23:54:10 -05:00
|
|
|
else:
|
|
|
|
cfg.load()
|
2019-03-09 19:02:42 -06:00
|
|
|
|
2019-06-16 23:54:10 -05:00
|
|
|
logging.basicConfig(level=cfg.logger_level, filename=cfg.logger_file_location)
|
2019-03-03 19:29:38 -06:00
|
|
|
|
|
|
|
|
|
|
|
@main.command()
|
2019-08-04 23:59:28 -05:00
|
|
|
@click.pass_context
|
2019-03-09 19:02:42 -06:00
|
|
|
def stock(ctx):
|
2019-06-16 23:54:10 -05:00
|
|
|
logger = logging.getLogger('cli.stock')
|
|
|
|
if ctx.invoked_subcommand is None:
|
|
|
|
try:
|
2019-06-22 02:21:23 -05:00
|
|
|
stock = Stock()
|
|
|
|
table = Table(stocks=stock.products)
|
2019-06-16 23:54:10 -05:00
|
|
|
click.echo(table.stock)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
2019-03-03 19:29:38 -06:00
|
|
|
|
2019-03-09 19:02:42 -06:00
|
|
|
|
2019-06-25 00:52:17 -05:00
|
|
|
@main.group()
|
|
|
|
def product():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@product.command()
|
2019-06-22 02:21:23 -05:00
|
|
|
@click.pass_context
|
|
|
|
@click.argument('product_id', required=False)
|
2019-06-25 00:52:17 -05:00
|
|
|
def view(ctx, product_id):
|
2019-06-22 02:21:23 -05:00
|
|
|
logger = logging.getLogger('cli.product')
|
|
|
|
try:
|
|
|
|
if product_id:
|
|
|
|
stock = Stock()
|
|
|
|
product = stock.get_product(product_id)
|
|
|
|
table = Table(entry=product)
|
|
|
|
click.echo(table.product)
|
|
|
|
else:
|
|
|
|
click.echo(ctx.get_help())
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
2019-08-04 23:59:28 -05:00
|
|
|
|
2019-06-25 00:52:17 -05:00
|
|
|
@product.command()
|
|
|
|
@click.option('--name', '-n', 'name')
|
|
|
|
@click.argument('product_id', required=False)
|
|
|
|
def edit(product_id, name):
|
|
|
|
logger = logging.getLogger('cli.product.edit')
|
2019-06-16 23:54:10 -05:00
|
|
|
try:
|
2019-06-25 00:52:17 -05:00
|
|
|
cfg = Configuration()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
cfg.load()
|
2019-07-07 13:58:01 -05:00
|
|
|
loaded_template = cfg.templates('product/edit')
|
2019-06-22 02:21:23 -05:00
|
|
|
entity = Entity(name='products')
|
2019-06-25 00:52:17 -05:00
|
|
|
if product_id:
|
|
|
|
product = entity.get(id=product_id)
|
|
|
|
edited_product = click.edit(loaded_template.render(product))
|
|
|
|
edited_product.update()
|
|
|
|
elif name:
|
|
|
|
# Convert name args to a single string
|
|
|
|
string_name_arg = ' '.join(name) if type(name) == list else name
|
|
|
|
products = entity.find({'name': string_name_arg})
|
|
|
|
if products is None:
|
|
|
|
click.echo('Could not find product')
|
|
|
|
return
|
|
|
|
|
|
|
|
edited_products = click.edit(loaded_template.render(products=products), extension='.yml')
|
|
|
|
if edited_products is None:
|
|
|
|
return
|
|
|
|
|
2019-08-04 23:59:28 -05:00
|
|
|
parsed_edited_products = util.load_yaml(edited_products)
|
2019-06-25 00:52:17 -05:00
|
|
|
schema = entity.schema
|
|
|
|
|
|
|
|
for index, edited_product in enumerate(parsed_edited_products):
|
|
|
|
edited_product['id'] = products[index]['id']
|
2019-09-25 23:14:11 -05:00
|
|
|
util.verify_integrity(edited_product, schema)
|
2019-06-25 00:52:17 -05:00
|
|
|
entity.update(edited_product, id=products[index]['id'])
|
|
|
|
else:
|
|
|
|
raise click.BadParameter('Missing PRODUCT_ID or QUERY')
|
2019-06-16 23:54:10 -05:00
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
2019-03-09 19:50:42 -06:00
|
|
|
|
2019-06-22 02:21:23 -05:00
|
|
|
|
2019-06-25 00:52:17 -05:00
|
|
|
@product.command()
|
|
|
|
@click.option('--name', '-n', 'name')
|
2019-07-07 13:58:01 -05:00
|
|
|
@click.option('-t', 'template')
|
|
|
|
def list(name, template):
|
2019-06-25 00:52:17 -05:00
|
|
|
logger = logging.getLogger('cli.product.list')
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
product_entity = Entity(name='products')
|
|
|
|
qu_entity = Entity(name='quantity_units')
|
|
|
|
location_entity = Entity(name='locations')
|
|
|
|
product_group_entity = Entity(name='product_groups')
|
2019-04-01 00:16:32 -05:00
|
|
|
try:
|
2019-06-25 00:52:17 -05:00
|
|
|
if name:
|
|
|
|
# Convert name args to a single string
|
|
|
|
string_name_arg = ' '.join(name) if type(name) == list else name
|
|
|
|
products = product_entity.find({'name': string_name_arg})
|
2019-08-04 23:59:28 -05:00
|
|
|
if len(products) == 0:
|
|
|
|
return
|
2019-06-25 00:52:17 -05:00
|
|
|
else:
|
|
|
|
products = product_entity.get()
|
|
|
|
|
|
|
|
entries = {}
|
|
|
|
entries['quantity_units'] = qu_entity.get()
|
|
|
|
entries['product_groups'] = product_group_entity.get()
|
|
|
|
entries['locations'] = location_entity.get()
|
|
|
|
entries['products'] = products
|
|
|
|
table = Table(entries=entries)
|
2019-07-07 13:58:01 -05:00
|
|
|
click.echo_via_pager(table.products)
|
2019-04-01 00:16:32 -05:00
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
2019-06-16 23:54:10 -05:00
|
|
|
raise e
|
2019-04-13 01:28:43 -05:00
|
|
|
|
|
|
|
|
2019-07-07 13:58:01 -05:00
|
|
|
@product.command()
|
|
|
|
@click.argument('product_id')
|
|
|
|
def browse(product_id):
|
|
|
|
logger = logging.getLogger('cli.product.browse')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
url = '{domain}/product/{product_id}'.format(domain=cfg.domain, product_id=product_id)
|
|
|
|
click.launch(url, wait=False)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
@product.command()
|
|
|
|
@click.option('-t', 'template')
|
2019-11-09 00:38:39 -06:00
|
|
|
def create(template):
|
|
|
|
logger = logging.getLogger('cli.product.create')
|
2019-07-07 13:58:01 -05:00
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
meta = Meta()
|
|
|
|
# Get product_groups
|
2019-08-04 23:59:28 -05:00
|
|
|
entity = Entity(name='product_groups')
|
|
|
|
product_groups = entity.get()
|
|
|
|
meta.add(type='entities', name='product_groups', valid_values=product_groups)
|
2019-07-07 13:58:01 -05:00
|
|
|
# Get locations
|
2019-08-04 23:59:28 -05:00
|
|
|
entity = Entity(name='locations')
|
|
|
|
locations = entity.get()
|
|
|
|
meta.add(type='entities', name='locations', valid_values=locations)
|
2019-07-07 13:58:01 -05:00
|
|
|
# Get quantity_units
|
2019-08-04 23:59:28 -05:00
|
|
|
entity = Entity(name='quantity_units')
|
|
|
|
quantity_units = entity.get()
|
|
|
|
meta.add(type='entities', name='quantity_units', valid_values=quantity_units)
|
|
|
|
data = { 'meta': meta.generate() }
|
2019-07-07 13:58:01 -05:00
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
2019-11-09 00:38:39 -06:00
|
|
|
loaded_template = cfg.templates('product/create')
|
|
|
|
created_product = click.edit(loaded_template.render(grocy=data), extension='.yml')
|
2019-07-07 13:58:01 -05:00
|
|
|
|
2019-11-09 00:38:39 -06:00
|
|
|
if not created_product:
|
2019-07-07 13:58:01 -05:00
|
|
|
return
|
|
|
|
|
2019-11-09 00:38:39 -06:00
|
|
|
parsed_created_product = yaml.safe_load(created_product)
|
2019-07-07 13:58:01 -05:00
|
|
|
if template == 'debug':
|
2019-11-09 00:38:39 -06:00
|
|
|
click.echo(parsed_created_product)
|
2019-07-07 13:58:01 -05:00
|
|
|
return
|
|
|
|
entity = Entity(name='products')
|
2019-11-09 00:38:39 -06:00
|
|
|
entity.create(parsed_created_product)
|
2019-07-07 13:58:01 -05:00
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
2019-10-30 00:33:59 -05:00
|
|
|
|
2019-07-07 13:58:01 -05:00
|
|
|
@main.group()
|
|
|
|
@click.pass_context
|
|
|
|
def recipe(ctx):
|
|
|
|
pass
|
2019-08-04 23:59:28 -05:00
|
|
|
|
|
|
|
|
2019-11-09 00:38:39 -06:00
|
|
|
@recipe.command()
|
|
|
|
def create(template):
|
|
|
|
logger = logging.getLogger('cli.recipe.create')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
recipe = Entity(name='recipes')
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('recipe/create')
|
|
|
|
created_recipe = click.edit(loaded_template.render(),
|
|
|
|
extension='.yml')
|
|
|
|
if created_recipe is None:
|
|
|
|
return
|
|
|
|
parsed_created_recipe = util.load_yaml(created_recipe)[0]
|
|
|
|
|
|
|
|
if template == 'debug':
|
|
|
|
click.echo(parsed_created_recipe)
|
|
|
|
return
|
|
|
|
shopping_lists.create(parsed_created_recipe)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
2019-07-07 13:58:01 -05:00
|
|
|
@recipe.command()
|
|
|
|
@click.pass_context
|
|
|
|
@click.argument('recipe_id', required=False)
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def view(ctx, recipe_id, template):
|
|
|
|
logger = logging.getLogger('cli.recipes.view')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
data = {'fields': {}}
|
|
|
|
if recipe_id:
|
|
|
|
entity = Entity(name='recipes')
|
2019-08-04 23:59:28 -05:00
|
|
|
meta = Meta()
|
2019-07-07 13:58:01 -05:00
|
|
|
data['fields'] = entity.get(id=recipe_id)
|
2019-08-04 23:59:28 -05:00
|
|
|
recipe = Recipe(id=recipe_id)
|
|
|
|
data['fields']['fulfillment'] = recipe.fulfillment
|
2019-07-07 13:58:01 -05:00
|
|
|
|
|
|
|
# Change html markup to plain text
|
2019-08-04 23:59:28 -05:00
|
|
|
html_markup_description = data['fields']['description']
|
2019-07-07 13:58:01 -05:00
|
|
|
plain_text_description = html2text(html_markup_description)
|
2019-08-04 23:59:28 -05:00
|
|
|
data['fields']['description'] = plain_text_description
|
2019-07-07 13:58:01 -05:00
|
|
|
|
|
|
|
recipe = Recipe(id=recipe_id)
|
2019-08-04 23:59:28 -05:00
|
|
|
meta.add(type='recipes', name='ingredients', valid_values=recipe.ingredients)
|
|
|
|
|
|
|
|
data['meta'] = meta.generate()
|
2019-07-07 13:58:01 -05:00
|
|
|
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('recipe/view')
|
|
|
|
|
|
|
|
click.echo(loaded_template.render(grocy=data))
|
|
|
|
else:
|
|
|
|
click.echo(ctx.get_help())
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
2019-08-04 23:59:28 -05:00
|
|
|
|
2019-10-30 00:33:59 -05:00
|
|
|
|
2019-08-04 23:59:28 -05:00
|
|
|
@recipe.command()
|
|
|
|
@click.option('--name', '-n', 'name')
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def list(name, template):
|
|
|
|
logger = logging.getLogger('cli.recipe.list')
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
try:
|
|
|
|
entity = Entity(name='recipes')
|
|
|
|
if name:
|
|
|
|
# Convert name args to a single string
|
|
|
|
string_name_arg = ' '.join(name) if type(name) == list else name
|
|
|
|
recipe_entities = entity.find({'name': string_name_arg})
|
|
|
|
if len(recipe_entities) == 0:
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
recipe_entities = entity.get()
|
|
|
|
|
|
|
|
data = {'recipes': []}
|
|
|
|
for recipe_entity in recipe_entities:
|
|
|
|
entry = {'fields': recipe_entity, 'meta': []}
|
|
|
|
meta = Meta(include_fa_icons=False)
|
|
|
|
recipe = Recipe(id=recipe_entity['id'])
|
|
|
|
entry['fields']['fulfillment'] = recipe.fulfillment
|
|
|
|
entry['fields']['description'] = recipe.generate_plain_text_description(recipe_entity['description'])
|
|
|
|
|
|
|
|
meta.add(type='recipes', name='ingredients', valid_values=recipe.ingredients)
|
|
|
|
entry['meta'].append(meta.generate())
|
|
|
|
data['recipes'].append(entry)
|
|
|
|
|
|
|
|
meta = Meta()
|
|
|
|
data['meta'] = meta.generate()
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('recipe/list')
|
|
|
|
|
|
|
|
click.echo(loaded_template.render(grocy=data))
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
@recipe.command()
|
|
|
|
@click.option('--fullscreen', '-f', 'fullscreen', is_flag=True)
|
|
|
|
@click.argument('recipe_id')
|
|
|
|
def browse(fullscreen, recipe_id):
|
|
|
|
logger = logging.getLogger('cli.recipe.browse')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
url = '{domain}/recipes?recipe={recipe_id}'.format(domain=cfg.domain, recipe_id=recipe_id)
|
|
|
|
if fullscreen:
|
|
|
|
url += '#fullscreen'
|
|
|
|
click.launch(url, wait=False)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
|
|
|
|
|
2019-09-25 23:14:11 -05:00
|
|
|
# TODO: revist this command
|
2019-08-04 23:59:28 -05:00
|
|
|
@recipe.command()
|
|
|
|
@click.option('--name', '-n', 'name')
|
|
|
|
@click.argument('recipe_id', required=False)
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def edit(recipe_id, name, template):
|
|
|
|
logger = logging.getLogger('cli.recipe.edit')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
cfg.load()
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
2019-09-25 23:14:11 -05:00
|
|
|
loaded_template = cfg.templates('recipe/edit')
|
2019-08-04 23:59:28 -05:00
|
|
|
|
|
|
|
entity = Entity(name='recipes')
|
|
|
|
if recipe_id:
|
|
|
|
recipe_entity = entity.get(id=recipe_id)
|
|
|
|
recipe = Recipe(id=recipe_id)
|
|
|
|
entry = {'fields': recipe_entity}
|
|
|
|
meta = Meta()
|
|
|
|
entry['fields']['fulfillment'] = recipe.fulfillment
|
|
|
|
entry['fields']['description'] = recipe.generate_plain_text_description(entity['description'])
|
|
|
|
entry['meta'] = meta.generate()
|
|
|
|
edited_recipe = click.edit(loaded_template.render(grocy=entry))
|
|
|
|
edited_recipe.update()
|
|
|
|
return
|
|
|
|
elif name:
|
|
|
|
# Convert name args to a single string
|
|
|
|
string_name_arg = ' '.join(name) if type(name) == list else name
|
|
|
|
recipe_entities = entity.find({'name': string_name_arg})
|
|
|
|
if len(recipe_entities) == 0:
|
|
|
|
click.echo('Could not find recipe')
|
|
|
|
return
|
|
|
|
|
2019-09-25 23:14:11 -05:00
|
|
|
# Build hydrated recipe entity object for editing
|
2019-08-04 23:59:28 -05:00
|
|
|
data = {'recipes': []}
|
|
|
|
for recipe_entity in recipe_entities:
|
|
|
|
entry = {'fields': recipe_entity, 'meta': []}
|
|
|
|
meta = Meta(include_fa_icons=False)
|
|
|
|
recipe = Recipe(id=recipe_entity['id'])
|
|
|
|
entry['fields']['fulfillment'] = recipe.fulfillment
|
|
|
|
entry['fields']['description'] = recipe.generate_plain_text_description(recipe_entity['description'])
|
|
|
|
|
|
|
|
meta.add(type='recipes', name='ingredients', valid_values=recipe.ingredients)
|
|
|
|
entry['meta'].append(meta.generate())
|
|
|
|
data['recipes'].append(entry)
|
|
|
|
|
|
|
|
meta = Meta()
|
|
|
|
data['meta'] = meta.generate()
|
|
|
|
edited_recipes = click.edit(loaded_template.render(grocy=data))
|
|
|
|
if edited_recipes is None:
|
|
|
|
return
|
|
|
|
|
2019-09-25 23:14:11 -05:00
|
|
|
schema = []
|
|
|
|
schema.append(Entity(name='products').schema)
|
|
|
|
schema.append(Entity(name='locations').schema)
|
|
|
|
schema.append(Entity(name='quantity_units').schema)
|
|
|
|
schema.append(Entity(name='recipes').schema)
|
|
|
|
|
2019-08-04 23:59:28 -05:00
|
|
|
parsed_edited_recipes = util.load_yaml(edited_recipes)
|
|
|
|
for index, edited_recipe in enumerate(parsed_edited_recipes):
|
2019-09-25 23:14:11 -05:00
|
|
|
schema = entity.schema
|
2019-08-04 23:59:28 -05:00
|
|
|
edited_recipe['id'] = recipe_entities[index]['id']
|
2019-09-25 23:14:11 -05:00
|
|
|
util.verify_integrity(edited_recipe, schema)
|
|
|
|
entity.update(edited_recipe, id=edited_recipe['id'])
|
2019-08-04 23:59:28 -05:00
|
|
|
|
|
|
|
else:
|
|
|
|
raise click.BadParameter('Missing RECIPE_ID or QUERY')
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
2019-10-30 00:33:59 -05:00
|
|
|
|
|
|
|
|
2019-09-25 23:14:11 -05:00
|
|
|
@recipe.command()
|
|
|
|
@click.argument('recipe_id', required=True)
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def add_ingredient(recipe_id, template):
|
|
|
|
logger = logging.getLogger('cli.recipe.add_ingredient')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
cfg.load()
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('recipe/add-ingredient')
|
|
|
|
|
|
|
|
# Verify that recipe exist
|
|
|
|
try:
|
|
|
|
entity = Entity(name='recipes')
|
|
|
|
entity.get(id=recipe_id)
|
|
|
|
except Exception:
|
|
|
|
raise Exception('Could not find recipe {}'.format(recipe_id))
|
|
|
|
|
|
|
|
# TODO: Meta has to be added to validate against know fields/properties
|
|
|
|
# Add quantity_units to meta
|
|
|
|
ingredient = click.edit(loaded_template.render(), extension='.yml')
|
|
|
|
if ingredient is None:
|
|
|
|
return
|
|
|
|
parsed_new_ingredient = util.load_yaml(ingredient)[0]
|
|
|
|
parsed_new_ingredient['recipe_id'] = recipe_id
|
|
|
|
|
|
|
|
if template == 'debug':
|
|
|
|
click.echo(parsed_new_ingredient)
|
|
|
|
return
|
|
|
|
entity = Entity(name='recipes_pos')
|
|
|
|
entity.create(parsed_new_ingredient)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
@recipe.command()
|
|
|
|
@click.argument('recipe_id', required=True)
|
|
|
|
@click.argument('ingredient_id', required=True)
|
|
|
|
def remove_ingredient(recipe_id, ingredient_id):
|
|
|
|
logger = logging.getLogger('cli.recipe.remove_ingredient')
|
|
|
|
try:
|
|
|
|
entity = Entity(name='recipes_pos')
|
|
|
|
entity.delete(ingredient_id)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
2019-08-04 23:59:28 -05:00
|
|
|
|
2019-11-02 00:41:09 -05:00
|
|
|
|
2019-10-13 15:27:10 -05:00
|
|
|
@main.group()
|
|
|
|
@click.pass_context
|
|
|
|
def shopping(ctx):
|
|
|
|
pass
|
|
|
|
|
2019-10-29 23:48:29 -05:00
|
|
|
|
2019-10-13 15:27:10 -05:00
|
|
|
@shopping.command()
|
|
|
|
@click.pass_context
|
|
|
|
@click.argument('shopping_id', required=False)
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def view(ctx, shopping_id, template):
|
|
|
|
logger = logging.getLogger('cli.shopping.view')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
data = {'fields': {'shopping_list_items': []}}
|
|
|
|
if shopping_id:
|
|
|
|
entity = Entity(name='shopping_list')
|
|
|
|
all_shopping_list_items = entity.get()
|
|
|
|
shopping_list_items = [s for s in all_shopping_list_items if s['shopping_list_id'] == shopping_id]
|
|
|
|
|
|
|
|
if shopping_list_items is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
entity = Entity(name='products')
|
|
|
|
products = entity.get()
|
|
|
|
products_map = {product['id']: product for product in products}
|
2019-10-29 23:48:29 -05:00
|
|
|
for shopping_list_item in shopping_list_items:
|
2019-10-13 15:27:10 -05:00
|
|
|
shopping_product_item = products_map[shopping_list_item['product_id']]
|
|
|
|
shopping_list_item['product'] = shopping_product_item
|
|
|
|
data['fields']['shopping_list_items'].append(shopping_list_item)
|
|
|
|
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('shopping/view')
|
|
|
|
|
|
|
|
click.echo(loaded_template.render(grocy=data))
|
|
|
|
else:
|
|
|
|
click.echo(ctx.get_help())
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
2019-10-29 23:48:29 -05:00
|
|
|
|
2019-10-13 15:27:10 -05:00
|
|
|
@shopping.command()
|
|
|
|
@click.option('--name', '-n', 'name')
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def list(name, template):
|
|
|
|
logger = logging.getLogger('cli.shopping.list')
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
try:
|
|
|
|
entity = Entity(name='shopping_lists')
|
|
|
|
if name:
|
|
|
|
# Convert name args to a single string
|
|
|
|
string_name_arg = ' '.join(name) if type(name) == list else name
|
|
|
|
shopping_list_entities = entity.find({'name': string_name_arg})
|
|
|
|
if len(shopping_list_entities) == 0:
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
shopping_list_entities = entity.get()
|
|
|
|
|
2019-10-29 23:48:29 -05:00
|
|
|
data = {'fields': {'shopping_lists': shopping_list_entities}}
|
2019-10-13 15:27:10 -05:00
|
|
|
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('shopping/list')
|
|
|
|
|
|
|
|
click.echo(loaded_template.render(grocy=data))
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
2019-10-29 23:48:29 -05:00
|
|
|
@shopping.command()
|
|
|
|
@click.argument('shopping_id')
|
|
|
|
def browse(shopping_id):
|
|
|
|
logger = logging.getLogger('cli.shopping.browse')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
url = '{domain}/shoppinglist?list={shopping_id}'.format(domain=cfg.domain, shopping_id=shopping_id)
|
|
|
|
click.launch(url, wait=False)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
|
|
|
|
|
|
|
|
@shopping.command()
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def create(template):
|
|
|
|
logger = logging.getLogger('cli.shopping.create')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
shopping_lists = Entity(name='shopping_lists')
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('shopping/create')
|
|
|
|
created_shopping_list = click.edit(loaded_template.render(),
|
|
|
|
extension='.yml')
|
|
|
|
if created_shopping_list is None:
|
|
|
|
return
|
|
|
|
parsed_created_shopping_list = util.load_yaml(created_shopping_list)[0]
|
|
|
|
|
|
|
|
if template == 'debug':
|
|
|
|
click.echo(parsed_created_shopping_list)
|
|
|
|
return
|
|
|
|
shopping_lists.create(parsed_created_shopping_list)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
2019-10-30 00:32:21 -05:00
|
|
|
@shopping.command()
|
|
|
|
@click.argument('shopping_id')
|
|
|
|
def delete(shopping_id):
|
|
|
|
logger = logging.getLogger('cli.shopping.delete')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
shopping_lists = Entity(name='shopping_lists')
|
|
|
|
shopping_lists.delete(shopping_id)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
@shopping.command()
|
|
|
|
@click.argument('shopping_id')
|
|
|
|
def clear(shopping_id):
|
|
|
|
logger = logging.getLogger('cli.shopping.clear')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
|
|
|
|
# Validate that shopping list exists
|
|
|
|
entity = Entity(name='shopping_lists')
|
|
|
|
found_shopping_list = entity.get(id=shopping_id)
|
|
|
|
if found_shopping_list is None:
|
|
|
|
raise Exception('Shopping list does not exist')
|
|
|
|
|
|
|
|
shopping_list = ShoppingList(id=shopping_id)
|
|
|
|
shopping_list.clear()
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
2019-10-29 23:48:29 -05:00
|
|
|
@shopping.command()
|
|
|
|
@click.argument('shopping_id')
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def add_product(shopping_id, template):
|
|
|
|
logger = logging.getLogger('cli.shopping.add_product')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
util = Util(cfg=cfg)
|
2019-10-30 00:32:21 -05:00
|
|
|
|
|
|
|
# Validate that shopping list exists
|
|
|
|
entity = Entity(name='shopping_lists')
|
|
|
|
found_shopping_list = entity.get(id=shopping_id)
|
|
|
|
if found_shopping_list is None:
|
|
|
|
raise Exception('Shopping list does not exist')
|
|
|
|
|
2019-10-29 23:48:29 -05:00
|
|
|
shopping_list = ShoppingList(id=shopping_id)
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('shopping/add_product')
|
|
|
|
|
|
|
|
added_product = click.edit(loaded_template.render(), extension='.yml')
|
|
|
|
if added_product is None:
|
|
|
|
return
|
|
|
|
parsed_added_product = util.load_yaml(added_product)[0]
|
|
|
|
|
|
|
|
if template == 'debug':
|
2019-10-30 00:32:21 -05:00
|
|
|
# TODO: Reserve for meta
|
2019-10-29 23:48:29 -05:00
|
|
|
return
|
|
|
|
shopping_list.add_product(parsed_added_product)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
2019-10-13 15:27:10 -05:00
|
|
|
|
2019-10-30 00:32:21 -05:00
|
|
|
|
|
|
|
@shopping.command()
|
|
|
|
@click.argument('shopping_id')
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def remove_product(shopping_id, template):
|
|
|
|
logger = logging.getLogger('cli.shopping.remove_product')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
util = Util(cfg=cfg)
|
|
|
|
shopping_list = ShoppingList(id=shopping_id)
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('shopping/remove_product')
|
|
|
|
|
|
|
|
remove_product = click.edit(loaded_template.render(), extension='.yml')
|
|
|
|
if remove_product is None:
|
|
|
|
return
|
|
|
|
parsed_removed_product = util.load_yaml(remove_product)[0]
|
|
|
|
|
|
|
|
if template == 'debug':
|
|
|
|
# TODO: Reserve for meta
|
|
|
|
return
|
|
|
|
shopping_list.remove_product(parsed_removed_product)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: Figure out why this command is not working for new shopping lists
|
2019-11-02 00:41:09 -05:00
|
|
|
#@shopping.command()
|
|
|
|
#@click.argument('shopping_id')
|
|
|
|
#@click.option('-t', 'template')
|
|
|
|
#@click.option('recipe_id', 'r')
|
|
|
|
#def add_missing_products(shopping_id, recipe_id, template):
|
|
|
|
# logger = logging.getLogger('cli.shopping.add_missing_products')
|
|
|
|
# try:
|
|
|
|
# cfg = Configuration()
|
|
|
|
# cfg.load()
|
|
|
|
# util = Util(cfg=cfg)
|
|
|
|
#
|
|
|
|
# # Validate that shopping list exists
|
|
|
|
# entity = Entity(name='shopping_lists')
|
|
|
|
# found_shopping_list = entity.get(id=shopping_id)
|
|
|
|
# if found_shopping_list is None:
|
|
|
|
# raise Exception('Shopping list does not exist')
|
|
|
|
#
|
|
|
|
# shopping_list = ShoppingList(id=shopping_id)
|
|
|
|
# except Exception as e:
|
|
|
|
# raise e
|
|
|
|
|
|
|
|
@main.group()
|
|
|
|
def chore():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@chore.command()
|
|
|
|
@click.option('--name', '-n', 'name')
|
2019-10-30 00:32:21 -05:00
|
|
|
@click.option('-t', 'template')
|
2019-11-02 00:41:09 -05:00
|
|
|
def list(name, template):
|
|
|
|
logger = logging.getLogger('cli.shopping.list')
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
2019-10-30 00:32:21 -05:00
|
|
|
try:
|
2019-11-02 00:41:09 -05:00
|
|
|
entity = Entity(name='chores')
|
|
|
|
if name:
|
|
|
|
chores = entity.find_by_name(name)
|
|
|
|
if len(chores) == 0:
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
chores = entity.get()
|
2019-10-30 00:32:21 -05:00
|
|
|
|
2019-11-02 00:41:09 -05:00
|
|
|
data = {'chores': []}
|
|
|
|
for chore in chores:
|
|
|
|
chore_details = Chore(chore['id'])
|
|
|
|
chore.update(chore_details.execution_times)
|
|
|
|
data['chores'].append({'fields': chore})
|
2019-10-30 00:32:21 -05:00
|
|
|
|
2019-11-02 00:41:09 -05:00
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('chore/list')
|
|
|
|
|
|
|
|
click.echo(loaded_template.render(grocy=data))
|
2019-10-30 00:32:21 -05:00
|
|
|
except Exception as e:
|
2019-11-02 00:41:09 -05:00
|
|
|
logger.error(e)
|
2019-10-30 00:32:21 -05:00
|
|
|
raise e
|
2019-11-09 00:38:39 -06:00
|
|
|
|
|
|
|
|
|
|
|
@chore.command()
|
|
|
|
@click.option('-t', 'template')
|
|
|
|
def create(template):
|
|
|
|
logger = logging.getLogger('cli.chore.create')
|
|
|
|
try:
|
|
|
|
cfg = Configuration()
|
|
|
|
cfg.load()
|
|
|
|
entity = Entity(name='chores')
|
|
|
|
if template:
|
|
|
|
loaded_template = cfg.templates(template)
|
|
|
|
else:
|
|
|
|
loaded_template = cfg.templates('chore/create')
|
|
|
|
created_chore = click.edit(loaded_template.render(), extension='.yml')
|
|
|
|
|
|
|
|
if not created_chore:
|
|
|
|
return
|
|
|
|
|
|
|
|
parsed_created_chore = yaml.safe_load(created_chore)
|
|
|
|
if template == 'debug':
|
|
|
|
click.echo(parsed_created_chore)
|
|
|
|
return
|
|
|
|
entity.create(parsed_created_chore)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
|
|
|
raise e
|