feat: Added configurable user templates
This commit is contained in:
		
							
								
								
									
										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) | ||||
|  | ||||
|  | ||||
| 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() | ||||
| def main(): | ||||
|     cfg = Configuration() | ||||
| @@ -65,15 +57,49 @@ def stock(ctx): | ||||
|     logger = logging.getLogger('cli.stock') | ||||
|     if ctx.invoked_subcommand is None: | ||||
|         try: | ||||
|             entity = Stock() | ||||
|             stocks = entity.get() | ||||
|             table = Table(stocks=stocks) | ||||
|             stock = Stock() | ||||
|             table = Table(stocks=stock.products) | ||||
|             click.echo(table.stock) | ||||
|         except Exception as e: | ||||
|             logger.error(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() | ||||
| def shopping(): | ||||
|     logger = logging.getLogger('cli.shopping') | ||||
| @@ -88,34 +114,55 @@ def shopping(): | ||||
|  | ||||
|  | ||||
| @main.group() | ||||
| def ingredient(ctx): | ||||
| def ingredient(): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @ingredient.command('add') | ||||
| @click.option('-r', '--recipe_id') | ||||
| def add(ctx, recipe_id): | ||||
| @click.argument('query') | ||||
| @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') | ||||
|  | ||||
|     try: | ||||
|         loaded_template = TEMPLATE_LOADER.get_template('ingredient_add.yml') | ||||
|         new_ingredient = {} | ||||
|  | ||||
|         if recipe_id: | ||||
|         entity = Entity(name='recipes') | ||||
|         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: | ||||
|                 entity = Entity(name='ingredients', **parsed_new_ingredient) | ||||
|                 entity.create() | ||||
|         if not recipe: | ||||
|             raise click.BadParameter(message='recipe {id} does not exist', param='recipe_id', | ||||
|                     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: | ||||
|         logger.error(e) | ||||
|         raise e | ||||
|  | ||||
|  | ||||
| @main.group() | ||||
| @@ -138,6 +185,7 @@ def ls(): | ||||
|         logger.error(e) | ||||
|         raise e | ||||
|  | ||||
|  | ||||
| @recipe.command('edit') | ||||
| @click.argument('recipe_id') | ||||
| def edit(recipe_id): | ||||
|   | ||||
| @@ -1,12 +1,17 @@ | ||||
| from os import path, chmod, makedirs | ||||
| from shutil import copy | ||||
| from yaml import safe_load, dump | ||||
| from jinja2 import Environment, FileSystemLoader | ||||
| from copy import deepcopy | ||||
|  | ||||
|  | ||||
| class Configuration(object): | ||||
|     # TODO: Figure out how to handle windows config | ||||
|     CONFIG_DIR = path.expanduser('~/.config/grocy') | ||||
|     USER_TEMPLATE_DIR = path.expanduser('~/.config/grocy/templates') | ||||
|     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' | ||||
|     DEFAULT_CFG = { | ||||
|         'logger': { | ||||
| @@ -72,6 +77,11 @@ class Configuration(object): | ||||
|             fd.write(dump_cfg) | ||||
|         chmod(self.CONFIG_DIR, 0o755) | ||||
|  | ||||
|         # Create template directory | ||||
|         makedirs(self.USER_TEMPLATE_DIR) | ||||
|         copy(self.PROJ_TEMPLATE_DIR, self.USER_TEMPLATE_DIR) | ||||
|  | ||||
|  | ||||
|     @property | ||||
|     def exists(self): | ||||
|         return path.exists(self.CONFIG_FILE) | ||||
| @@ -83,3 +93,12 @@ class Configuration(object): | ||||
|                 self.update(data) | ||||
|         else: | ||||
|             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') | ||||
|         url = self.RESOURCE_URL_TEMPLATE | ||||
|  | ||||
|         request = Request('post', url) | ||||
|         request = Request('post', url, entity) | ||||
|         try: | ||||
|             return request.send() | ||||
|         except Exception as e: | ||||
|   | ||||
| @@ -5,14 +5,13 @@ import logging | ||||
|  | ||||
| class Stock(object): | ||||
|     GET_STOCK_URL_TEMPLATE = '{api}/stock' | ||||
|     GET_STOCK_PRODUCT_DETAIL_TEMPLATE = '{api}/stock/products/{product_id}' | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.conf = Configuration() | ||||
|         self.conf.load() | ||||
|  | ||||
|     def get(self): | ||||
|         logger = logging.getLogger('stock.get') | ||||
|         url = self.GET_STOCK_URL_TEMPLATE.format(api=self.conf.api) | ||||
|     def __get_resources(self, url, logger): | ||||
|         request = Request('get', url) | ||||
|         try: | ||||
|             return request.send() | ||||
| @@ -20,3 +19,13 @@ class Stock(object): | ||||
|             logger.error(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 | ||||
| from grocy.conf import Configuration | ||||
| import logging | ||||
|  | ||||
| from tabulate import tabulate | ||||
|  | ||||
|  | ||||
| @@ -15,6 +16,13 @@ class Table(object): | ||||
|         self.conf = Configuration() | ||||
|         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 | ||||
|     def stock(self): | ||||
|         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}}  | ||||
|  | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user