feat: Added ability to edit multiple products
- refactor: Changed templates to use yml extension
This commit is contained in:
parent
1f7a061600
commit
d479b4f621
348
grocy/cli.py
348
grocy/cli.py
@ -2,6 +2,7 @@ import click
|
|||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from grocy.conf import Configuration
|
from grocy.conf import Configuration
|
||||||
|
from grocy.util import Util
|
||||||
from grocy.recipe import Recipe
|
from grocy.recipe import Recipe
|
||||||
from grocy.table import Table
|
from grocy.table import Table
|
||||||
from grocy.entity import Entity
|
from grocy.entity import Entity
|
||||||
@ -20,6 +21,13 @@ CONFIG_DIR = click.get_app_dir(APP_NAME)
|
|||||||
PROJ_DIR = path.join(path.dirname(path.realpath(__file__)))
|
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)
|
||||||
|
|
||||||
|
class GrocyGroup(click.Group):
|
||||||
|
def parse_args(self, ctx, args):
|
||||||
|
is_subcommand = True if args[0] in self.commands else False
|
||||||
|
if is_subcommand:
|
||||||
|
ctx.forward(self.commands[args[0]], args[1:])
|
||||||
|
super(GrocyGroup, self).parse_args(ctx, args)
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
def main():
|
def main():
|
||||||
@ -65,16 +73,20 @@ def stock(ctx):
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@main.group(invoke_without_command=True)
|
@main.group()
|
||||||
|
def product():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@product.command()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
@click.argument('product_id', required=False)
|
@click.argument('product_id', required=False)
|
||||||
def product(ctx, product_id):
|
def view(ctx, product_id):
|
||||||
logger = logging.getLogger('cli.product')
|
logger = logging.getLogger('cli.product')
|
||||||
try:
|
try:
|
||||||
if product_id:
|
if product_id:
|
||||||
stock = Stock()
|
stock = Stock()
|
||||||
product = stock.get_product(product_id)
|
product = stock.get_product(product_id)
|
||||||
# Need to get quantity_unit
|
|
||||||
table = Table(entry=product)
|
table = Table(entry=product)
|
||||||
click.echo(table.product)
|
click.echo(table.product)
|
||||||
else:
|
else:
|
||||||
@ -83,148 +95,203 @@ def product(ctx, product_id):
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
#@product.command('list')
|
@product.command()
|
||||||
#@click.argument('query', required=False)
|
@click.option('--name', '-n', 'name')
|
||||||
#def view(query, product_id):
|
@click.argument('product_id', required=False)
|
||||||
# logger = logging.getLogger('cli.product.view')
|
def edit(product_id, name):
|
||||||
|
logger = logging.getLogger('cli.product.edit')
|
||||||
|
try:
|
||||||
|
cfg = Configuration()
|
||||||
|
util = Util(cfg=cfg)
|
||||||
|
cfg.load()
|
||||||
|
loaded_template = cfg.templates('product_edit')
|
||||||
|
entity = Entity(name='products')
|
||||||
|
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
|
||||||
|
|
||||||
|
parsed_edited_products = Util.load_yaml(edited_products)
|
||||||
|
schema = entity.schema
|
||||||
|
|
||||||
|
for index, edited_product in enumerate(parsed_edited_products):
|
||||||
|
edited_product['id'] = products[index]['id']
|
||||||
|
Util.verify_integrity(edited_product, schema)
|
||||||
|
entity.update(edited_product, id=products[index]['id'])
|
||||||
|
else:
|
||||||
|
raise click.BadParameter('Missing PRODUCT_ID or QUERY')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
@product.command()
|
||||||
|
@click.option('--name', '-n', 'name')
|
||||||
|
def list(name):
|
||||||
|
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')
|
||||||
|
try:
|
||||||
|
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})
|
||||||
|
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)
|
||||||
|
click.echo(table.products)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#@main.command()
|
||||||
|
#def shopping():
|
||||||
|
# logger = logging.getLogger('cli.shopping')
|
||||||
# try:
|
# try:
|
||||||
# if product_id:
|
# entity = Entity(name='shopping_list')
|
||||||
# entity = Entity(name='product')
|
# shopping_list = entity.get()
|
||||||
# product = entity.get(id=product_id)
|
# table = Table(shopping_list=shopping_list)
|
||||||
# table = Table(product=product)
|
# click.echo(table.shopping_list)
|
||||||
# click.echo(table.product)
|
|
||||||
# else:
|
|
||||||
# except Exception as e:
|
# except Exception as e:
|
||||||
# logger.error(e)
|
# logger.error(e)
|
||||||
# raise e
|
# raise e
|
||||||
|
#
|
||||||
|
#
|
||||||
@main.command()
|
#@main.command()
|
||||||
def shopping():
|
#@main.group()
|
||||||
logger = logging.getLogger('cli.shopping')
|
#def ingredient():
|
||||||
try:
|
# pass
|
||||||
entity = Entity(name='shopping_list')
|
#
|
||||||
shopping_list = entity.get()
|
#
|
||||||
table = Table(shopping_list=shopping_list)
|
#@ingredient.command('add')
|
||||||
click.echo(table.shopping_list)
|
#@click.argument('query')
|
||||||
except Exception as e:
|
#@click.argument('recipe_id')
|
||||||
logger.error(e)
|
#@click.option('--amount', '-a', 'amount', default=1, type=int)
|
||||||
raise e
|
#@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)
|
||||||
@main.group()
|
#@click.option('--disable-fulfillment', '--df', 'disable_fulfillment', default=False)
|
||||||
def ingredient():
|
#@click.option('--note', '-n', 'note', multiple=True, default='', type=str)
|
||||||
pass
|
#@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')
|
||||||
@ingredient.command('add')
|
#
|
||||||
@click.argument('query')
|
# try:
|
||||||
@click.argument('recipe_id')
|
# loaded_template = TEMPLATE_LOADER.get_template('ingredient_add.yml')
|
||||||
@click.option('--amount', '-a', 'amount', default=1, type=int)
|
# new_ingredient = {}
|
||||||
@click.option('--group', '-g', type=str, default='')
|
#
|
||||||
@click.option('--variable-amount', '--va', 'variable_amount', default=None, type=float)
|
# entity = Entity(name='recipes')
|
||||||
@click.option('--in-stock', '--is', 'in_stock', default=False)
|
# recipe = entity.get(id=recipe_id)
|
||||||
@click.option('--disable-fulfillment', '--df', 'disable_fulfillment', default=False)
|
#
|
||||||
@click.option('--note', '-n', 'note', multiple=True, default='', type=str)
|
# if not recipe:
|
||||||
@click.option('--no-edit', '--ne', 'no_edit', default=False)
|
# raise click.BadParameter(message='recipe {id} does not exist', param='recipe_id',
|
||||||
def add(query, recipe_id, amount, group, variable_amount, in_stock, disable_fulfillment, note, no_edit):
|
# param_hint='Use `grocy recipes ls` to get a list of recipes')
|
||||||
logger = logging.getLogger('cli.ingredient.add')
|
#
|
||||||
|
# entity = Entity(name='products')
|
||||||
try:
|
# product = entity.findOne(query)
|
||||||
loaded_template = TEMPLATE_LOADER.get_template('ingredient_add.yml')
|
# new_ingredient['product_id'] = product['id']
|
||||||
new_ingredient = {}
|
#
|
||||||
|
# new_ingredient['amount'] = amount
|
||||||
entity = Entity(name='recipes')
|
# new_ingredient['group'] = group
|
||||||
recipe = entity.get(id=recipe_id)
|
# new_ingredient['variable_amount'] = variable_amount
|
||||||
|
# new_ingredient['only_check_single_unit_in_stock'] = "1" if in_stock else "0"
|
||||||
if not recipe:
|
# new_ingredient['not_check_stock_fulfillment'] = "1" if disable_fulfillment else "0"
|
||||||
raise click.BadParameter(message='recipe {id} does not exist', param='recipe_id',
|
# new_ingredient['note'] = note
|
||||||
param_hint='Use `grocy recipes ls` to get a list of recipes')
|
#
|
||||||
|
# if not no_edit:
|
||||||
entity = Entity(name='products')
|
# new_ingredient = click.edit(loaded_template.render(new_ingredient))
|
||||||
product = entity.findOne(query)
|
#
|
||||||
new_ingredient['product_id'] = product['id']
|
# parsed_new_ingredient = yaml.safe_load(new_ingredient)
|
||||||
|
# entity = Entity(name='recipes_pos')
|
||||||
new_ingredient['amount'] = amount
|
# #entity.create(parsed_new_ingredient)
|
||||||
new_ingredient['group'] = group
|
#
|
||||||
new_ingredient['variable_amount'] = variable_amount
|
# except Exception as e:
|
||||||
new_ingredient['only_check_single_unit_in_stock'] = "1" if in_stock else "0"
|
# logger.error(e)
|
||||||
new_ingredient['not_check_stock_fulfillment'] = "1" if disable_fulfillment else "0"
|
# raise e
|
||||||
new_ingredient['note'] = note
|
#
|
||||||
|
#
|
||||||
if not no_edit:
|
#@main.group()
|
||||||
new_ingredient = click.edit(loaded_template.render(new_ingredient))
|
#@click.pass_context
|
||||||
|
#def recipe(ctx):
|
||||||
parsed_new_ingredient = yaml.safe_load(new_ingredient)
|
# pass
|
||||||
entity = Entity(name='recipes_pos')
|
#
|
||||||
#entity.create(parsed_new_ingredient)
|
#
|
||||||
|
#@recipe.command('ls')
|
||||||
except Exception as e:
|
#def ls():
|
||||||
logger.error(e)
|
# logger = logging.getLogger('cli.recipe')
|
||||||
raise e
|
# try:
|
||||||
|
# entity = Entity(name='recipes')
|
||||||
|
# recipes = entity.get()
|
||||||
@main.group()
|
# recipe = Recipe()
|
||||||
@click.pass_context
|
# recipes_reqs = recipe.get_requirements()
|
||||||
def recipe(ctx):
|
# table = Table(recipes=recipes, recipes_reqs=recipes_reqs)
|
||||||
pass
|
# click.echo(table.recipe)
|
||||||
|
# except Exception as e:
|
||||||
|
# logger.error(e)
|
||||||
@recipe.command('ls')
|
# raise e
|
||||||
def ls():
|
#
|
||||||
logger = logging.getLogger('cli.recipe')
|
#
|
||||||
try:
|
#@recipe.command('edit')
|
||||||
entity = Entity(name='recipes')
|
#@click.argument('recipe_id')
|
||||||
recipes = entity.get()
|
#def edit(recipe_id):
|
||||||
recipe = Recipe()
|
# logger = logging.getLogger('cli.recipe.edit')
|
||||||
recipes_reqs = recipe.get_requirements()
|
# try:
|
||||||
table = Table(recipes=recipes, recipes_reqs=recipes_reqs)
|
# if recipe_id:
|
||||||
click.echo(table.recipe)
|
# entity = Entity(name='recipes')
|
||||||
except Exception as e:
|
# recipe = entity.get(id=recipe_id)
|
||||||
logger.error(e)
|
# loaded_template = TEMPLATE_LOADER.get_template('recipe_edit.yml')
|
||||||
raise e
|
# edited_recipe = click.edit(loaded_template.render(recipe))
|
||||||
|
# if edited_recipe is not None:
|
||||||
|
# parsed_edited_recipe = yaml.safe_load(edited_recipe)
|
||||||
@recipe.command('edit')
|
# parsed_edited_recipe['description'] = markdown(parsed_edited_recipe['description'])
|
||||||
@click.argument('recipe_id')
|
# recipe.__dict__.update(parsed_edited_recipe)
|
||||||
def edit(recipe_id):
|
# recipe.update()
|
||||||
logger = logging.getLogger('cli.recipe.edit')
|
# except Exception as e:
|
||||||
try:
|
# logger.error(e)
|
||||||
if recipe_id:
|
# logger.error('Could not edit recipe {}'.format(recipe_id))
|
||||||
entity = Entity(name='recipes')
|
# raise e
|
||||||
recipe = entity.get(id=recipe_id)
|
#
|
||||||
loaded_template = TEMPLATE_LOADER.get_template('recipe_edit.yml')
|
#
|
||||||
edited_recipe = click.edit(loaded_template.render(recipe))
|
#@recipe.command('create')
|
||||||
if edited_recipe is not None:
|
#@click.pass_context
|
||||||
parsed_edited_recipe = yaml.safe_load(edited_recipe)
|
#def create(ctx):
|
||||||
parsed_edited_recipe['description'] = markdown(parsed_edited_recipe['description'])
|
# logger = logging.getLogger('cli.recipe.create')
|
||||||
recipe.__dict__.update(parsed_edited_recipe)
|
#
|
||||||
recipe.update()
|
# try:
|
||||||
except Exception as e:
|
# recipe = Entity(name='recipes')
|
||||||
logger.error(e)
|
# loaded_template = TEMPLATE_LOADER.get_template('recipe_add.yml')
|
||||||
logger.error('Could not edit recipe {}'.format(recipe_id))
|
# new_recipe = click.edit(loaded_template.render())
|
||||||
raise e
|
# if new_recipe is not None:
|
||||||
|
# parsed_new_recipe = yaml.safe_load(new_recipe)
|
||||||
|
# parsed_new_recipe['description'] = markdown(parsed_new_recipe['description'])
|
||||||
@recipe.command('create')
|
# recipe.__dict__.update(parsed_new_recipe)
|
||||||
@click.pass_context
|
# recipe.create()
|
||||||
def create(ctx):
|
# except Exception as e:
|
||||||
logger = logging.getLogger('cli.recipe.create')
|
# logger.error(e)
|
||||||
|
# raise e
|
||||||
try:
|
#
|
||||||
recipe = Entity(name='recipes')
|
|
||||||
loaded_template = TEMPLATE_LOADER.get_template('recipe_add.yml')
|
|
||||||
new_recipe = click.edit(loaded_template.render())
|
|
||||||
if new_recipe is not None:
|
|
||||||
parsed_new_recipe = yaml.safe_load(new_recipe)
|
|
||||||
parsed_new_recipe['description'] = markdown(parsed_new_recipe['description'])
|
|
||||||
recipe.__dict__.update(parsed_new_recipe)
|
|
||||||
recipe.create()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(e)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
#@main.command()
|
#@main.command()
|
||||||
#@click.pass_context
|
#@click.pass_context
|
||||||
@ -243,3 +310,4 @@ def create(ctx):
|
|||||||
# task = Task(**cfg)
|
# task = Task(**cfg)
|
||||||
# tasks = task.get_list()
|
# tasks = task.get_list()
|
||||||
# click.echo(tasks)
|
# click.echo(tasks)
|
||||||
|
# click.echo(tasks)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from os import path, chmod, makedirs
|
from os import path, chmod, makedirs, pardir
|
||||||
|
from pathlib import Path
|
||||||
from shutil import copy
|
from shutil import copy
|
||||||
from yaml import safe_load, dump
|
from yaml import safe_load, dump
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
@ -10,7 +11,8 @@ class Configuration(object):
|
|||||||
CONFIG_DIR = path.expanduser('~/.config/grocy')
|
CONFIG_DIR = path.expanduser('~/.config/grocy')
|
||||||
USER_TEMPLATE_DIR = path.expanduser('~/.config/grocy/templates')
|
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__)))
|
TEMPLATE_EXT = 'yml'
|
||||||
|
PROJ_DIR = Path(__file__).resolve().parent.parent
|
||||||
PROJ_TEMPLATE_DIR = '{}/templates'.format(PROJ_DIR)
|
PROJ_TEMPLATE_DIR = '{}/templates'.format(PROJ_DIR)
|
||||||
API_KEY_HEADER = 'GROCY-API-KEY'
|
API_KEY_HEADER = 'GROCY-API-KEY'
|
||||||
DEFAULT_CFG = {
|
DEFAULT_CFG = {
|
||||||
@ -98,7 +100,8 @@ class Configuration(object):
|
|||||||
try:
|
try:
|
||||||
TEMPLATE_LOADER = Environment(loader=FileSystemLoader([self.USER_TEMPLATE_DIR, self.PROJ_TEMPLATE_DIR]),
|
TEMPLATE_LOADER = Environment(loader=FileSystemLoader([self.USER_TEMPLATE_DIR, self.PROJ_TEMPLATE_DIR]),
|
||||||
trim_blocks=True, lstrip_blocks=True)
|
trim_blocks=True, lstrip_blocks=True)
|
||||||
return TEMPLATE_LOADER.get_template(name)
|
|
||||||
|
return TEMPLATE_LOADER.get_template('{}.{}'.format(name, self.TEMPLATE_EXT))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from grocy.request import Request
|
from grocy.request import Request
|
||||||
|
import re
|
||||||
from grocy.conf import Configuration
|
from grocy.conf import Configuration
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -6,6 +7,11 @@ import logging
|
|||||||
class Entity(object):
|
class Entity(object):
|
||||||
RESOURCE_URL_TEMPLATE = '{api}/objects/{entity}/{objectId}'
|
RESOURCE_URL_TEMPLATE = '{api}/objects/{entity}/{objectId}'
|
||||||
COLLECTION_URL_TEMPLATE = '{api}/objects/{entity}'
|
COLLECTION_URL_TEMPLATE = '{api}/objects/{entity}'
|
||||||
|
SCHEMA_URL_TEMPLATE = '{api}/openapi/specification'
|
||||||
|
SCHEMA_MODEL_MAP = {
|
||||||
|
'products': 'Product',
|
||||||
|
'stock': 'StockEntry'
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, name, **props):
|
def __init__(self, name, **props):
|
||||||
self.conf = Configuration()
|
self.conf = Configuration()
|
||||||
@ -27,9 +33,28 @@ class Entity(object):
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
def find(self, query):
|
||||||
|
logger = logging.getLogger('entity.find')
|
||||||
|
found_entities = []
|
||||||
|
try:
|
||||||
|
entities = self.get()
|
||||||
|
for entity in entities:
|
||||||
|
for prop, value in query.items():
|
||||||
|
regex = re.compile(r'{}'.format(value))
|
||||||
|
if regex.search(entity[prop]):
|
||||||
|
found_entities.append(entity)
|
||||||
|
|
||||||
|
if len(found_entities) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return found_entities
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
def create(self, entity):
|
def create(self, entity):
|
||||||
logger = logging.getLogger('entity.add')
|
logger = logging.getLogger('entity.add')
|
||||||
url = self.RESOURCE_URL_TEMPLATE
|
url = self.COLLECTION_URL_TEMPLATE.format(api=self.conf.api, entity=self.name)
|
||||||
|
|
||||||
request = Request('post', url, entity)
|
request = Request('post', url, entity)
|
||||||
try:
|
try:
|
||||||
@ -37,3 +62,29 @@ class Entity(object):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
def update(self, entity, id=None):
|
||||||
|
if id is None:
|
||||||
|
raise Exception('id property is required to update entity')
|
||||||
|
logger = logging.getLogger('entity.update')
|
||||||
|
url = self.RESOURCE_URL_TEMPLATE.format(api=self.conf.api, entity=self.name, objectId=id)
|
||||||
|
|
||||||
|
request = Request('put', url, resource=entity)
|
||||||
|
try:
|
||||||
|
return request.send()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schema(self):
|
||||||
|
logger = logging.getLogger('entity.schema')
|
||||||
|
try:
|
||||||
|
url = self.SCHEMA_URL_TEMPLATE.format(api=self.conf.api)
|
||||||
|
request = Request('get', url)
|
||||||
|
response = request.send()
|
||||||
|
schema_name = self.SCHEMA_MODEL_MAP[self.name]
|
||||||
|
return response['components']['schemas'][schema_name]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
@ -43,7 +43,7 @@ class Stock(object):
|
|||||||
product_ids = [entry['product_id'] for entry in get_current_stock]
|
product_ids = [entry['product_id'] for entry in get_current_stock]
|
||||||
table_entries = []
|
table_entries = []
|
||||||
try:
|
try:
|
||||||
for index in range(len(product_ids)):
|
for index in range(0, len(product_ids)):
|
||||||
product_id = product_ids[index]
|
product_id = product_ids[index]
|
||||||
path = Stock.GET_PRODUCT_BY_ID.format(product_id)
|
path = Stock.GET_PRODUCT_BY_ID.format(product_id)
|
||||||
product = self.rest_service.get(path)
|
product = self.rest_service.get(path)
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
from grocy.conf import Configuration
|
from grocy.conf import Configuration
|
||||||
|
import json
|
||||||
from requests import request
|
from requests import request
|
||||||
|
import requests
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class Request(object):
|
class Request(object):
|
||||||
def __init__(self, method, url):
|
def __init__(self, method, url, resource=None):
|
||||||
self.conf = Configuration()
|
self.conf = Configuration()
|
||||||
self.conf.load()
|
self.conf.load()
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.resource = resource
|
||||||
self.method = method
|
self.method = method
|
||||||
self.headers = {
|
self.headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -18,8 +21,14 @@ class Request(object):
|
|||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
logger = logging.getLogger('request.send')
|
logger = logging.getLogger('request.send')
|
||||||
r = request(method=self.method, url=self.url, headers=self.headers)
|
if self.resource:
|
||||||
|
r = request(method=self.method, url=self.url, headers=self.headers, json=self.resource)
|
||||||
|
else:
|
||||||
|
r = request(method=self.method, url=self.url, headers=self.headers)
|
||||||
|
|
||||||
if r.raise_for_status():
|
if r.raise_for_status():
|
||||||
logger.error(r.raise_for_status())
|
logger.error(r.raise_for_status())
|
||||||
raise r.raise_for_status()
|
raise r.raise_for_status()
|
||||||
return r.json()
|
if r.status_code != 204:
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
@ -23,6 +23,40 @@ class Table(object):
|
|||||||
loaded_template = self.conf.templates('single_product')
|
loaded_template = self.conf.templates('single_product')
|
||||||
return loaded_template.render(self.entry)
|
return loaded_template.render(self.entry)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def products(self):
|
||||||
|
if not self.entries:
|
||||||
|
raise Exception('Missing entries')
|
||||||
|
product_groups_map = {product_group['id']: product_group['name']
|
||||||
|
for product_group in self.entries['product_groups']}
|
||||||
|
location_map = {location['id']: location['name']
|
||||||
|
for location in self.entries['locations']}
|
||||||
|
quantity_unit_map = {quantity_unit['id']: quantity_unit['name']
|
||||||
|
for quantity_unit in self.entries['quantity_units']}
|
||||||
|
table_entries = []
|
||||||
|
try:
|
||||||
|
for entry in self.entries['products']:
|
||||||
|
print('{}'.format(entry))
|
||||||
|
table_entry = []
|
||||||
|
|
||||||
|
product_group_name = '' if entry['product_group_id'] == '' else product_groups_map[entry['product_group_id']]
|
||||||
|
location_name = location_map[entry['location_id']]
|
||||||
|
quantity_unit_purchase_name = quantity_unit_map[entry['qu_id_purchase']]
|
||||||
|
quantity_unit_stock_name = quantity_unit_map[entry['qu_id_stock']]
|
||||||
|
table_entry.append(entry['id'])
|
||||||
|
table_entry.append(location_name)
|
||||||
|
table_entry.append(quantity_unit_purchase_name)
|
||||||
|
table_entry.append(quantity_unit_stock_name)
|
||||||
|
table_entry.append(product_group_name)
|
||||||
|
table_entry.append(entry['mini_stock_amount'])
|
||||||
|
table_entry.append(entry['qu_factor_purchase_to_stock'])
|
||||||
|
table_entries.append(table_entry)
|
||||||
|
table_headers = ['ID', 'Name', 'Location', 'Min Stock Amount',
|
||||||
|
'QU Purchase', 'QU Stock', 'QU Factor', 'Product Group']
|
||||||
|
return tabulate(table_entries, headers=table_headers)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stock(self):
|
def stock(self):
|
||||||
logger = logging.getLogger('table.stock')
|
logger = logging.getLogger('table.stock')
|
||||||
|
31
grocy/util.py
Normal file
31
grocy/util.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import yaml
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def _yaml_constructor(loader, node):
|
||||||
|
return node.value
|
||||||
|
|
||||||
|
|
||||||
|
class Util(object):
|
||||||
|
def __init__(self, cfg):
|
||||||
|
self.cfg = cfg
|
||||||
|
yaml.SafeLoader.add_constructor("tag:yaml.org,2002:python/unicode", _yaml_constructor)
|
||||||
|
|
||||||
|
|
||||||
|
def load_yaml(data):
|
||||||
|
generator = yaml.safe_load_all(data)
|
||||||
|
data_list = list(generator)
|
||||||
|
return data_list
|
||||||
|
|
||||||
|
|
||||||
|
def verify_integrity(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))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
21
templates/product_edit.yml
Normal file
21
templates/product_edit.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% for product in products %}
|
||||||
|
name: {{ product.name }}
|
||||||
|
description: |
|
||||||
|
{{ product.description }}
|
||||||
|
location_id: {{ product.location_id }}
|
||||||
|
qu_id_purchase: {{ product.qu_id_purchase }}
|
||||||
|
qu_id_stock: {{ product.qu_id_stock }}
|
||||||
|
qu_factor_purchase_to_stock: {{ product.qu_factor_purchase_to_stock }}
|
||||||
|
barcode: {{ product.barcode | default("") }}
|
||||||
|
min_stock_amount: {{ product.min_stock_amount | default("0") }}
|
||||||
|
default_best_before_days: {{ product.default_best_before_days | default("0") }}
|
||||||
|
product_group_id: {{ product.product_group_id }}
|
||||||
|
default_best_before_days_after_open: {{ product.default_best_before_days_after_open | default("0") }}
|
||||||
|
allow_partial_units_in_stock: {{ product.allow_partial_units_in_stock | default("0") }}
|
||||||
|
enable_tare_weight_handling: {{ product.enable_tare_weight_handling | default("0") }}
|
||||||
|
tare_weight: {{ product.tare_weight | default("0.0") }}
|
||||||
|
not_check_stock_fulfillment_for_recipes: {{ product.not_check_stock_fulfillment_for_recipes | default("0") }}
|
||||||
|
{% if loop.nextitem %}
|
||||||
|
---
|
||||||
|
{%endif%}
|
||||||
|
{%endfor %}
|
Loading…
Reference in New Issue
Block a user