From 029ba359451c8b228703c3339a79e5610023b413 Mon Sep 17 00:00:00 2001 From: Aerex Date: Wed, 25 Sep 2019 23:14:11 -0500 Subject: [PATCH] feat: Added add/remove ingredients sub commands to recipes - feat: Added list recipe commands - feat: Added sample add ingredient template for recipe --- grocy/cli.py | 69 ++++++++++++++++++++++++++--- grocy/entity.py | 18 ++++++-- grocy/recipe.py | 2 +- grocy/schema.py | 3 +- grocy/util.py | 21 ++++++--- templates/recipe/add-ingredient.yml | 8 ++++ templates/recipe/list.yml | 3 +- 7 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 templates/recipe/add-ingredient.yml diff --git a/grocy/cli.py b/grocy/cli.py index 65ec49e..515c45c 100644 --- a/grocy/cli.py +++ b/grocy/cli.py @@ -9,6 +9,7 @@ from grocy.recipe import Recipe from grocy.table import Table from grocy.entity import Entity from grocy.stock import Stock +from grocy.schema import get_schema import yaml from sys import exit from os import path @@ -131,7 +132,7 @@ def edit(product_id, name): for index, edited_product in enumerate(parsed_edited_products): edited_product['id'] = products[index]['id'] - Util.verify_integrity(edited_product, schema) + util.verify_integrity(edited_product, schema) entity.update(edited_product, id=products[index]['id']) else: raise click.BadParameter('Missing PRODUCT_ID or QUERY') @@ -332,6 +333,7 @@ def browse(fullscreen, recipe_id): logger.error(e) +# TODO: revist this command @recipe.command() @click.option('--name', '-n', 'name') @click.argument('recipe_id', required=False) @@ -342,11 +344,10 @@ def edit(recipe_id, name, template): cfg = Configuration() util = Util(cfg=cfg) cfg.load() - loaded_template = cfg.templates('recipe/edit') if template: loaded_template = cfg.templates(template) else: - loaded_template = cfg.templates('recipe/list') + loaded_template = cfg.templates('recipe/edit') entity = Entity(name='recipes') if recipe_id: @@ -368,6 +369,7 @@ def edit(recipe_id, name, template): click.echo('Could not find recipe') return + # Build hydrated recipe entity object for editing data = {'recipes': []} for recipe_entity in recipe_entities: entry = {'fields': recipe_entity, 'meta': []} @@ -386,17 +388,74 @@ def edit(recipe_id, name, template): if edited_recipes is None: return + 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) + parsed_edited_recipes = util.load_yaml(edited_recipes) for index, edited_recipe in enumerate(parsed_edited_recipes): + schema = entity.schema edited_recipe['id'] = recipe_entities[index]['id'] - # Util.verify_integrity(edited_recipe, schema) - entity.update(edited_recipe, id=recipe[index]['id']) + util.verify_integrity(edited_recipe, schema) + entity.update(edited_recipe, id=edited_recipe['id']) else: raise click.BadParameter('Missing RECIPE_ID or QUERY') except Exception as e: logger.error(e) raise e +@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 #@main.command() #def shopping(): diff --git a/grocy/entity.py b/grocy/entity.py index c49baa8..81c08af 100644 --- a/grocy/entity.py +++ b/grocy/entity.py @@ -14,7 +14,8 @@ class Entity(object): 'stock': 'StockEntry', 'product_groups':'ProductGroup', 'locations': 'Location', - 'quantity_units': 'QuantityUnit' + 'quantity_units': 'QuantityUnit', + 'recipes': 'Recipe' } def __init__(self, name, **props): @@ -65,8 +66,6 @@ class Entity(object): if type(value) == bool: entity[key] = '1' if value else '0' - print('{}'.format(entity)) - request = Request('post', url, resource=entity) try: return request.send() @@ -87,6 +86,19 @@ class Entity(object): logger.error(e) raise e + def delete(self, id=None): + if id is None: + raise Exception('id property is required to delete entity') + logger = logging.getLogger('entity.delete') + url = self.RESOURCE_URL_TEMPLATE.format(domain=self.conf.domain, entity=self.name, objectId=id) + + request = Request('delete', url) + try: + return request.send() + except Exception as e: + logger.error(e) + raise e + @property def schema(self): logger = logging.getLogger('entity.schema') diff --git a/grocy/recipe.py b/grocy/recipe.py index ddd764b..9c80d52 100644 --- a/grocy/recipe.py +++ b/grocy/recipe.py @@ -11,7 +11,6 @@ class Recipe(object): GET_RECIPES_FULFILLMENT_URL_TEMPLATE = '{domain}/api/recipes/fulfillment' GET_RECIPE_FULFILLMENT_URL_TEMPLATE = '{domain}/api/recipes/{recipeId}/fulfillment' GET_RECIPES_POS_FULFILLMENT_URL_TEMPLATE = '{domain}/api/recipes/{recipeId}/pos/fulfillment' - GET_RECIPE_POS_FULFILLMENT_URL_TEMPLATE = '{domain}/api/recipes/{recipeId}/pos/{recipePosId}/fulfillment' def __init__(self, id=None): self.conf = Configuration() @@ -104,3 +103,4 @@ class Recipe(object): except Exception as e: logger.error(e) raise e + diff --git a/grocy/schema.py b/grocy/schema.py index 42ea355..8a38efe 100644 --- a/grocy/schema.py +++ b/grocy/schema.py @@ -10,7 +10,8 @@ SCHEMA_MODEL_MAP = { 'product_groups': 'ProductGroup', 'locations': 'Location', 'quantity_units': 'QuantityUnit', - 'recipe_fulfillment': 'RecipeFulfillmentResponse' + 'recipe_fulfillment': 'RecipeFulfillmentResponse', + 'recipe_pos_fulfillment': 'RecipePosFulfillmentResponse' } diff --git a/grocy/util.py b/grocy/util.py index 6b19c2a..27e457a 100644 --- a/grocy/util.py +++ b/grocy/util.py @@ -16,15 +16,24 @@ class Util(object): data_list = list(generator) return data_list - - def verify_integrity(new_data, schema): + def verify_integrity(self, new_data, schema): logger = logging.getLogger('util.verify_integrity') try: # Verify that updated fields exist - schema_keys = schema['properties'].keys() - for prop in new_data.keys(): - if prop not in schema_keys: - raise Exception('{} is not a valid field'.format(prop)) + if type(schema) != list: + schemas = [schema] + else: + schemas = schema + + keys_not_found = [] + for current_schema in schemas: + schema_keys = current_schema['properties'].keys() + for prop in new_data.keys(): + if prop not in schema_keys: + keys_not_found.append(prop) + print('{}'.format(keys_not_found)) + if len(keys_not_found) > 0: + raise Exception('{} is not valid property'.format(keys_not_found.join(', '))) except Exception as e: logger.error(e) raise e diff --git a/templates/recipe/add-ingredient.yml b/templates/recipe/add-ingredient.yml new file mode 100644 index 0000000..73313d1 --- /dev/null +++ b/templates/recipe/add-ingredient.yml @@ -0,0 +1,8 @@ +product_id: +amount: +qu_id: +only_check_single_unit_in_stock: +not_check_stock_fulfilment: +variable_amount: +ingredient_group: +note: diff --git a/templates/recipe/list.yml b/templates/recipe/list.yml index c1182a5..768d7be 100644 --- a/templates/recipe/list.yml +++ b/templates/recipe/list.yml @@ -1,7 +1,8 @@ {% from 'recipe/macro.yml' import render_recipe_fulfilment %} {% for recipe in grocy.recipes %} +id: {{ recipe.fields.id }} name: {{ recipe.fields.name }} -servings: {{ recipe.fields.base_servings }} +base_servings: {{ recipe.fields.base_servings }} required_fulfilled: {{ render_recipe_fulfilment(recipe.fields.fulfillment, grocy.meta.fa_icons) }} description: |- {{ recipe.fields.description }}