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 jinja2 import Environment, FileSystemLoader
|
||||
from grocy.conf import Configuration
|
||||
from grocy.util import Util
|
||||
from grocy.recipe import Recipe
|
||||
from grocy.table import Table
|
||||
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__)))
|
||||
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()
|
||||
def main():
|
||||
@ -65,16 +73,20 @@ def stock(ctx):
|
||||
raise e
|
||||
|
||||
|
||||
@main.group(invoke_without_command=True)
|
||||
@main.group()
|
||||
def product():
|
||||
pass
|
||||
|
||||
|
||||
@product.command()
|
||||
@click.pass_context
|
||||
@click.argument('product_id', required=False)
|
||||
def product(ctx, product_id):
|
||||
def view(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:
|
||||
@ -83,148 +95,203 @@ def product(ctx, product_id):
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
#@product.command('list')
|
||||
#@click.argument('query', required=False)
|
||||
#def view(query, product_id):
|
||||
# logger = logging.getLogger('cli.product.view')
|
||||
@product.command()
|
||||
@click.option('--name', '-n', 'name')
|
||||
@click.argument('product_id', required=False)
|
||||
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:
|
||||
# if product_id:
|
||||
# entity = Entity(name='product')
|
||||
# product = entity.get(id=product_id)
|
||||
# table = Table(product=product)
|
||||
# click.echo(table.product)
|
||||
# else:
|
||||
# entity = Entity(name='shopping_list')
|
||||
# shopping_list = entity.get()
|
||||
# table = Table(shopping_list=shopping_list)
|
||||
# click.echo(table.shopping_list)
|
||||
# except Exception as e:
|
||||
# logger.error(e)
|
||||
# raise e
|
||||
|
||||
|
||||
@main.command()
|
||||
def shopping():
|
||||
logger = logging.getLogger('cli.shopping')
|
||||
try:
|
||||
entity = Entity(name='shopping_list')
|
||||
shopping_list = entity.get()
|
||||
table = Table(shopping_list=shopping_list)
|
||||
click.echo(table.shopping_list)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
|
||||
@main.group()
|
||||
def ingredient():
|
||||
pass
|
||||
|
||||
|
||||
@ingredient.command('add')
|
||||
@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 = {}
|
||||
|
||||
entity = Entity(name='recipes')
|
||||
recipe = entity.get(id=recipe_id)
|
||||
|
||||
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()
|
||||
@click.pass_context
|
||||
def recipe(ctx):
|
||||
pass
|
||||
|
||||
|
||||
@recipe.command('ls')
|
||||
def ls():
|
||||
logger = logging.getLogger('cli.recipe')
|
||||
try:
|
||||
entity = Entity(name='recipes')
|
||||
recipes = entity.get()
|
||||
recipe = Recipe()
|
||||
recipes_reqs = recipe.get_requirements()
|
||||
table = Table(recipes=recipes, recipes_reqs=recipes_reqs)
|
||||
click.echo(table.recipe)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
|
||||
@recipe.command('edit')
|
||||
@click.argument('recipe_id')
|
||||
def edit(recipe_id):
|
||||
logger = logging.getLogger('cli.recipe.edit')
|
||||
try:
|
||||
if recipe_id:
|
||||
entity = Entity(name='recipes')
|
||||
recipe = entity.get(id=recipe_id)
|
||||
loaded_template = TEMPLATE_LOADER.get_template('recipe_edit.yml')
|
||||
edited_recipe = click.edit(loaded_template.render(recipe))
|
||||
if edited_recipe is not None:
|
||||
parsed_edited_recipe = yaml.safe_load(edited_recipe)
|
||||
parsed_edited_recipe['description'] = markdown(parsed_edited_recipe['description'])
|
||||
recipe.__dict__.update(parsed_edited_recipe)
|
||||
recipe.update()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
logger.error('Could not edit recipe {}'.format(recipe_id))
|
||||
raise e
|
||||
|
||||
|
||||
@recipe.command('create')
|
||||
@click.pass_context
|
||||
def create(ctx):
|
||||
logger = logging.getLogger('cli.recipe.create')
|
||||
|
||||
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.group()
|
||||
#def ingredient():
|
||||
# pass
|
||||
#
|
||||
#
|
||||
#@ingredient.command('add')
|
||||
#@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 = {}
|
||||
#
|
||||
# entity = Entity(name='recipes')
|
||||
# recipe = entity.get(id=recipe_id)
|
||||
#
|
||||
# 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()
|
||||
#@click.pass_context
|
||||
#def recipe(ctx):
|
||||
# pass
|
||||
#
|
||||
#
|
||||
#@recipe.command('ls')
|
||||
#def ls():
|
||||
# logger = logging.getLogger('cli.recipe')
|
||||
# try:
|
||||
# entity = Entity(name='recipes')
|
||||
# recipes = entity.get()
|
||||
# recipe = Recipe()
|
||||
# recipes_reqs = recipe.get_requirements()
|
||||
# table = Table(recipes=recipes, recipes_reqs=recipes_reqs)
|
||||
# click.echo(table.recipe)
|
||||
# except Exception as e:
|
||||
# logger.error(e)
|
||||
# raise e
|
||||
#
|
||||
#
|
||||
#@recipe.command('edit')
|
||||
#@click.argument('recipe_id')
|
||||
#def edit(recipe_id):
|
||||
# logger = logging.getLogger('cli.recipe.edit')
|
||||
# try:
|
||||
# if recipe_id:
|
||||
# entity = Entity(name='recipes')
|
||||
# recipe = entity.get(id=recipe_id)
|
||||
# loaded_template = TEMPLATE_LOADER.get_template('recipe_edit.yml')
|
||||
# edited_recipe = click.edit(loaded_template.render(recipe))
|
||||
# if edited_recipe is not None:
|
||||
# parsed_edited_recipe = yaml.safe_load(edited_recipe)
|
||||
# parsed_edited_recipe['description'] = markdown(parsed_edited_recipe['description'])
|
||||
# recipe.__dict__.update(parsed_edited_recipe)
|
||||
# recipe.update()
|
||||
# except Exception as e:
|
||||
# logger.error(e)
|
||||
# logger.error('Could not edit recipe {}'.format(recipe_id))
|
||||
# raise e
|
||||
#
|
||||
#
|
||||
#@recipe.command('create')
|
||||
#@click.pass_context
|
||||
#def create(ctx):
|
||||
# logger = logging.getLogger('cli.recipe.create')
|
||||
#
|
||||
# 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()
|
||||
#@click.pass_context
|
||||
@ -243,3 +310,4 @@ def create(ctx):
|
||||
# task = Task(**cfg)
|
||||
# tasks = task.get_list()
|
||||
# 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 yaml import safe_load, dump
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
@ -10,7 +11,8 @@ class Configuration(object):
|
||||
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__)))
|
||||
TEMPLATE_EXT = 'yml'
|
||||
PROJ_DIR = Path(__file__).resolve().parent.parent
|
||||
PROJ_TEMPLATE_DIR = '{}/templates'.format(PROJ_DIR)
|
||||
API_KEY_HEADER = 'GROCY-API-KEY'
|
||||
DEFAULT_CFG = {
|
||||
@ -98,7 +100,8 @@ class Configuration(object):
|
||||
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)
|
||||
|
||||
return TEMPLATE_LOADER.get_template('{}.{}'.format(name, self.TEMPLATE_EXT))
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from grocy.request import Request
|
||||
import re
|
||||
from grocy.conf import Configuration
|
||||
import logging
|
||||
|
||||
@ -6,6 +7,11 @@ import logging
|
||||
class Entity(object):
|
||||
RESOURCE_URL_TEMPLATE = '{api}/objects/{entity}/{objectId}'
|
||||
COLLECTION_URL_TEMPLATE = '{api}/objects/{entity}'
|
||||
SCHEMA_URL_TEMPLATE = '{api}/openapi/specification'
|
||||
SCHEMA_MODEL_MAP = {
|
||||
'products': 'Product',
|
||||
'stock': 'StockEntry'
|
||||
}
|
||||
|
||||
def __init__(self, name, **props):
|
||||
self.conf = Configuration()
|
||||
@ -27,9 +33,28 @@ class Entity(object):
|
||||
logger.error(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):
|
||||
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)
|
||||
try:
|
||||
@ -37,3 +62,29 @@ class Entity(object):
|
||||
except Exception as e:
|
||||
logger.error(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]
|
||||
table_entries = []
|
||||
try:
|
||||
for index in range(len(product_ids)):
|
||||
for index in range(0, len(product_ids)):
|
||||
product_id = product_ids[index]
|
||||
path = Stock.GET_PRODUCT_BY_ID.format(product_id)
|
||||
product = self.rest_service.get(path)
|
||||
|
@ -1,13 +1,16 @@
|
||||
from grocy.conf import Configuration
|
||||
import json
|
||||
from requests import request
|
||||
import requests
|
||||
import logging
|
||||
|
||||
|
||||
class Request(object):
|
||||
def __init__(self, method, url):
|
||||
def __init__(self, method, url, resource=None):
|
||||
self.conf = Configuration()
|
||||
self.conf.load()
|
||||
self.url = url
|
||||
self.resource = resource
|
||||
self.method = method
|
||||
self.headers = {
|
||||
'Content-Type': 'application/json',
|
||||
@ -18,8 +21,14 @@ class Request(object):
|
||||
|
||||
def send(self):
|
||||
logger = logging.getLogger('request.send')
|
||||
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():
|
||||
logger.error(r.raise_for_status())
|
||||
raise r.raise_for_status()
|
||||
if r.status_code != 204:
|
||||
return r.json()
|
||||
|
||||
|
@ -23,6 +23,40 @@ class Table(object):
|
||||
loaded_template = self.conf.templates('single_product')
|
||||
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
|
||||
def stock(self):
|
||||
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