refactor: Reanmed templates
- fix: Added browse subcommand for products
This commit is contained in:
parent
d479b4f621
commit
6af9bef334
132
grocy/cli.py
132
grocy/cli.py
@ -1,6 +1,8 @@
|
||||
import click
|
||||
from markdown import markdown
|
||||
from html2text import html2text
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from grocy.meta import Meta
|
||||
from grocy.conf import Configuration
|
||||
from grocy.util import Util
|
||||
from grocy.recipe import Recipe
|
||||
@ -104,7 +106,7 @@ def edit(product_id, name):
|
||||
cfg = Configuration()
|
||||
util = Util(cfg=cfg)
|
||||
cfg.load()
|
||||
loaded_template = cfg.templates('product_edit')
|
||||
loaded_template = cfg.templates('product/edit')
|
||||
entity = Entity(name='products')
|
||||
if product_id:
|
||||
product = entity.get(id=product_id)
|
||||
@ -138,7 +140,8 @@ def edit(product_id, name):
|
||||
|
||||
@product.command()
|
||||
@click.option('--name', '-n', 'name')
|
||||
def list(name):
|
||||
@click.option('-t', 'template')
|
||||
def list(name, template):
|
||||
logger = logging.getLogger('cli.product.list')
|
||||
cfg = Configuration()
|
||||
cfg.load()
|
||||
@ -160,12 +163,114 @@ def list(name):
|
||||
entries['locations'] = location_entity.get()
|
||||
entries['products'] = products
|
||||
table = Table(entries=entries)
|
||||
click.echo(table.products)
|
||||
click.echo_via_pager(table.products)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
|
||||
@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')
|
||||
def add(template):
|
||||
logger = logging.getLogger('cli.product.add')
|
||||
try:
|
||||
cfg = Configuration()
|
||||
cfg.load()
|
||||
logger = logging.getLogger('cli.product.add')
|
||||
meta = Meta()
|
||||
# Get product_groups
|
||||
meta.add(type='entities', name='product_groups')
|
||||
# Get locations
|
||||
meta.add(type='entities', name='locations')
|
||||
# Get quantity_units
|
||||
meta.add(type='entities', name='quantity_units')
|
||||
if template:
|
||||
loaded_template = cfg.templates(template)
|
||||
else:
|
||||
loaded_template = cfg.templates('product/add')
|
||||
new_product = click.edit(loaded_template.render(grocy=meta.generate()), extension='.yml')
|
||||
|
||||
if not new_product:
|
||||
return
|
||||
|
||||
parsed_new_product = yaml.safe_load(new_product)
|
||||
if template == 'debug':
|
||||
click.echo(parsed_new_product)
|
||||
return
|
||||
entity = Entity(name='products')
|
||||
entity.create(parsed_new_product)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
@main.group()
|
||||
@click.pass_context
|
||||
def recipe(ctx):
|
||||
pass
|
||||
|
||||
@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')
|
||||
data['fields'] = entity.get(id=recipe_id)
|
||||
|
||||
# Change html markup to plain text
|
||||
html_markup_description = data['fields']['description']
|
||||
plain_text_description = html2text(html_markup_description)
|
||||
data['fields']['description'] = plain_text_description
|
||||
|
||||
recipe = Recipe(id=recipe_id)
|
||||
data['fields']['fulfillment'] = recipe.get_fulfillments()
|
||||
|
||||
if template:
|
||||
loaded_template = cfg.templates(template)
|
||||
else:
|
||||
loaded_template = cfg.templates('recipe/view')
|
||||
|
||||
entity = Entity(name='recipes')
|
||||
click.echo(loaded_template.render(grocy=data))
|
||||
else:
|
||||
click.echo(ctx.get_help())
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
#@recipe.command()
|
||||
#def list():
|
||||
# logger = logging.getLogger('cli.recipe')
|
||||
# try:
|
||||
# entity = Entity(name='recipes')
|
||||
# recipes = entity.get()
|
||||
# recipe = Recipe(
|
||||
# ingredient_reqs = recipe.get_ingredient_requirements()
|
||||
# table = Table(recipes=recipes, recipes_reqs=recipes_reqs, ingredient_reqs=ingredient_reqs)
|
||||
# click.echo(table.recipe)
|
||||
# except Exception as e:
|
||||
# logger.error(e)
|
||||
# raise e
|
||||
##
|
||||
#
|
||||
#@main.command()
|
||||
#def shopping():
|
||||
# logger = logging.getLogger('cli.shopping')
|
||||
@ -232,27 +337,6 @@ def list(name):
|
||||
# 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):
|
||||
|
@ -20,7 +20,7 @@ class Configuration(object):
|
||||
'level': 'DEBUG',
|
||||
'file_location': None
|
||||
},
|
||||
'api': 'https://demo-en.grocy.info/api',
|
||||
'domain': 'https://demo-en.grocy.info',
|
||||
'token': None,
|
||||
'formats': {
|
||||
'col': 'center',
|
||||
@ -56,9 +56,9 @@ class Configuration(object):
|
||||
self.logger_file_location = user_cfg_options['logger_file_location']
|
||||
cfg_json['logger']['file_location'] = self.logger_file_location
|
||||
|
||||
if user_cfg_options['api']:
|
||||
self.api = user_cfg_options['api']
|
||||
cfg_json['api'] = self.api
|
||||
if user_cfg_options['domain']:
|
||||
self.domain = user_cfg_options['domain']
|
||||
cfg_json['domain'] = self.domain
|
||||
|
||||
if user_cfg_options['token']:
|
||||
self.token = user_cfg_options['token']
|
||||
@ -99,7 +99,7 @@ class Configuration(object):
|
||||
def templates(self, name):
|
||||
try:
|
||||
TEMPLATE_LOADER = Environment(loader=FileSystemLoader([self.USER_TEMPLATE_DIR, self.PROJ_TEMPLATE_DIR]),
|
||||
trim_blocks=True, lstrip_blocks=True)
|
||||
trim_blocks=False, lstrip_blocks=True)
|
||||
|
||||
return TEMPLATE_LOADER.get_template('{}.{}'.format(name, self.TEMPLATE_EXT))
|
||||
except Exception as e:
|
||||
|
@ -1,16 +1,21 @@
|
||||
from grocy.request import Request
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from grocy.conf import Configuration
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
class Entity(object):
|
||||
RESOURCE_URL_TEMPLATE = '{api}/objects/{entity}/{objectId}'
|
||||
COLLECTION_URL_TEMPLATE = '{api}/objects/{entity}'
|
||||
SCHEMA_URL_TEMPLATE = '{api}/openapi/specification'
|
||||
RESOURCE_URL_TEMPLATE = '{domain}/api/objects/{entity}/{objectId}'
|
||||
COLLECTION_URL_TEMPLATE = '{domain}/api/objects/{entity}'
|
||||
SCHEMA_URL_TEMPLATE = '{domain}/api/openapi/specification'
|
||||
SCHEMA_MODEL_MAP = {
|
||||
'products': 'Product',
|
||||
'stock': 'StockEntry'
|
||||
'stock': 'StockEntry',
|
||||
'product_groups':'ProductGroup',
|
||||
'locations': 'Location',
|
||||
'quantity_units': 'QuantityUnit'
|
||||
}
|
||||
|
||||
def __init__(self, name, **props):
|
||||
@ -19,12 +24,13 @@ class Entity(object):
|
||||
self.name = name
|
||||
self.__dict__.update(**props)
|
||||
|
||||
|
||||
def get(self, id=None):
|
||||
logger = logging.getLogger('entity.get')
|
||||
if id:
|
||||
url = self.RESOURCE_URL_TEMPLATE.format(api=self.conf.api, entity=self.name, objectId=id)
|
||||
url = self.RESOURCE_URL_TEMPLATE.format(domain=self.conf.domain, entity=self.name, objectId=id)
|
||||
else:
|
||||
url = self.COLLECTION_URL_TEMPLATE.format(api=self.conf.api, entity=self.name)
|
||||
url = self.COLLECTION_URL_TEMPLATE.format(domain=self.conf.domain, entity=self.name)
|
||||
|
||||
request = Request('get', url)
|
||||
try:
|
||||
@ -54,9 +60,15 @@ class Entity(object):
|
||||
|
||||
def create(self, entity):
|
||||
logger = logging.getLogger('entity.add')
|
||||
url = self.COLLECTION_URL_TEMPLATE.format(api=self.conf.api, entity=self.name)
|
||||
url = self.COLLECTION_URL_TEMPLATE.format(domain=self.conf.domain, entity=self.name)
|
||||
|
||||
request = Request('post', url, entity)
|
||||
for key, value in entity.items():
|
||||
if type(value) == bool:
|
||||
entity[key] = '1' if value else '0'
|
||||
|
||||
print('{}'.format(entity))
|
||||
|
||||
request = Request('post', url, resource=entity)
|
||||
try:
|
||||
return request.send()
|
||||
except Exception as e:
|
||||
@ -67,7 +79,7 @@ class Entity(object):
|
||||
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)
|
||||
url = self.RESOURCE_URL_TEMPLATE.format(domain=self.conf.domain, entity=self.name, objectId=id)
|
||||
|
||||
request = Request('put', url, resource=entity)
|
||||
try:
|
||||
@ -80,7 +92,7 @@ class Entity(object):
|
||||
def schema(self):
|
||||
logger = logging.getLogger('entity.schema')
|
||||
try:
|
||||
url = self.SCHEMA_URL_TEMPLATE.format(api=self.conf.api)
|
||||
url = self.SCHEMA_URL_TEMPLATE.format(domain=self.conf.domain)
|
||||
request = Request('get', url)
|
||||
response = request.send()
|
||||
schema_name = self.SCHEMA_MODEL_MAP[self.name]
|
||||
|
27
grocy/meta.py
Normal file
27
grocy/meta.py
Normal file
@ -0,0 +1,27 @@
|
||||
from grocy.entity import Entity
|
||||
from grocy.schema import get_schema
|
||||
from grocy.recipe import Recipe
|
||||
from fontawesome import icons as fa_icons
|
||||
|
||||
|
||||
class Meta(object):
|
||||
def __init__(self):
|
||||
self.meta = {'meta': {'entities': {}, 'fa_icons': fa_icons}}
|
||||
|
||||
def add(self, type, name):
|
||||
if type == 'entities':
|
||||
entity = Entity(name=name)
|
||||
resources = entity.get()
|
||||
schema = get_schema(name)
|
||||
elif type == 'recipes':
|
||||
recipe = Recipe()
|
||||
if name == 'fulfillments':
|
||||
resources = recipe.get_fulfillments()
|
||||
schema = get_schema(name='recipe_fulfilments')
|
||||
|
||||
self.meta['meta'][type][name] = {}
|
||||
self.meta['meta'][type][name]['properties'] = schema['properties']
|
||||
self.meta['meta'][type][name]['valid_values'] = resources
|
||||
|
||||
def generate(self):
|
||||
return self.meta
|
@ -1,18 +1,30 @@
|
||||
from grocy.request import Request
|
||||
from grocy.conf import Configuration
|
||||
from grocy.entity import Entity
|
||||
import logging
|
||||
|
||||
|
||||
class Recipe(object):
|
||||
GET_RECIPE_REQUIRMENTS_URL_TEMPLATE = '{api}/recipes/requirements'
|
||||
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/pos/fulfillment'
|
||||
GET_RECIPE_POS_FULFILLMENT_URL_TEMPLATE = '{domain}/api/recipes/{recipeId}/pos/{recipeId}/fulfillment'
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, id=None):
|
||||
self.conf = Configuration()
|
||||
self.conf.load()
|
||||
self.id = id
|
||||
|
||||
def get_requirements(self):
|
||||
def get_ingredient_requirements(self, recipe_id=None):
|
||||
logger = logging.getLogger('recipe.get_ingredient_requirements')
|
||||
|
||||
|
||||
def get_fulfillments(self):
|
||||
logger = logging.getLogger('recipe.get_requirements')
|
||||
url = self.GET_RECIPE_REQUIRMENTS_URL_TEMPLATE.format(api=self.conf.api)
|
||||
if self.id:
|
||||
url = self.GET_RECIPE_FULFILLMENT_URL_TEMPLATE.format(domain=self.conf.domain, recipeId=self.id)
|
||||
else:
|
||||
url = self.GET_RECIPES_FULFILLMENT_URL_TEMPLATE.format(domain=self.conf.domain)
|
||||
request = Request('get', url)
|
||||
try:
|
||||
return request.send()
|
||||
@ -20,3 +32,23 @@ class Recipe(object):
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
def get(self):
|
||||
# Get list of available ingredients
|
||||
if self.id:
|
||||
entity = Entity(name='recipes')
|
||||
recipe = entity.get(id=self.id)
|
||||
|
||||
if type(recipe) is list:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
from grocy.conf import Configuration
|
||||
import json
|
||||
from requests import request
|
||||
import requests
|
||||
from requests import session
|
||||
import logging
|
||||
import cachecontrol
|
||||
|
||||
sess = cachecontrol.CacheControl(session())
|
||||
|
||||
|
||||
class Request(object):
|
||||
|
||||
def __init__(self, method, url, resource=None):
|
||||
self.conf = Configuration()
|
||||
self.conf.load()
|
||||
@ -22,13 +24,16 @@ 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)
|
||||
r = sess.request(method=self.method, url=self.url, headers=self.headers, json=self.resource)
|
||||
print(r.text)
|
||||
else:
|
||||
r = request(method=self.method, url=self.url, headers=self.headers)
|
||||
r = sess.request(method=self.method, url=self.url, headers=self.headers)
|
||||
|
||||
if r.raise_for_status():
|
||||
logger.error(r.raise_for_status())
|
||||
logger.error(r.text)
|
||||
raise r.raise_for_status()
|
||||
|
||||
if r.status_code != 204:
|
||||
return r.json()
|
||||
|
||||
|
28
grocy/schema.py
Normal file
28
grocy/schema.py
Normal file
@ -0,0 +1,28 @@
|
||||
from grocy.request import Request
|
||||
from grocy.conf import Configuration
|
||||
import logging
|
||||
|
||||
SCHEMA_URL_TEMPLATE = '{domain}/api/openapi/specification'
|
||||
SCHEMA_MODEL_MAP = {
|
||||
'products': 'Product',
|
||||
'stock': 'StockEntry',
|
||||
'product_groups': 'ProductGroup',
|
||||
'locations': 'Location',
|
||||
'quantity_units': 'QuantityUnit',
|
||||
'recipe_requirements': 'RecipeFulfilmentResponse'
|
||||
}
|
||||
|
||||
|
||||
def get_schema(name):
|
||||
logger = logging.getLogger('schema')
|
||||
try:
|
||||
cfg = Configuration()
|
||||
cfg.load()
|
||||
url = SCHEMA_URL_TEMPLATE.format(domain=cfg.domain)
|
||||
request = Request('get', url)
|
||||
response = request.send()
|
||||
schema_name = SCHEMA_MODEL_MAP[name]
|
||||
return response['components']['schemas'][schema_name]
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
@ -4,8 +4,8 @@ import logging
|
||||
|
||||
|
||||
class Stock(object):
|
||||
GET_STOCK_URL_TEMPLATE = '{api}/stock'
|
||||
GET_STOCK_PRODUCT_DETAIL_TEMPLATE = '{api}/stock/products/{product_id}'
|
||||
GET_STOCK_URL_TEMPLATE = '{domain}/api/stock'
|
||||
GET_STOCK_PRODUCT_DETAIL_TEMPLATE = '{domain}/api/stock/products/{product_id}'
|
||||
|
||||
def __init__(self):
|
||||
self.conf = Configuration()
|
||||
@ -22,10 +22,10 @@ class Stock(object):
|
||||
@property
|
||||
def products(self):
|
||||
logger = logging.getLogger('stock.products')
|
||||
url = self.GET_STOCK_URL_TEMPLATE.format(api=self.conf.api)
|
||||
url = self.GET_STOCK_URL_TEMPLATE.format(domain=self.conf.domain)
|
||||
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)
|
||||
url = self.GET_STOCK_PRODUCT_DETAIL_TEMPLATE.format(domain=self.conf.domain, product_id=product_id)
|
||||
return self.__get_resources(url, logger)
|
||||
|
@ -20,7 +20,7 @@ class Table(object):
|
||||
def product(self):
|
||||
if not self.entry:
|
||||
raise Exception('Missing product')
|
||||
loaded_template = self.conf.templates('single_product')
|
||||
loaded_template = self.conf.templates('product/view')
|
||||
return loaded_template.render(self.entry)
|
||||
|
||||
@property
|
||||
@ -36,23 +36,26 @@ class Table(object):
|
||||
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']]
|
||||
product_group_name = 'N/A' if entry['product_group_id'] == '' else product_groups_map[entry['product_group_id']]
|
||||
mini_stock_amount = entry['mini_stock_amount'] if 'mini_stock_amount' in entry else 'N/A'
|
||||
qu_factor_purchase_to_stock = entry['qu_factor_purchase_to_stock'] if 'qu_factor_purchase_to_stock' in entry else 'N/A'
|
||||
location_name = location_map[entry['location_id']] if 'location_id' in entry else 'N/A'
|
||||
quantity_unit_purchase_name = quantity_unit_map[entry['qu_id_purchase']] if 'qu_id_purchase' in entry else 'N/A'
|
||||
quantity_unit_stock_name = quantity_unit_map[entry['qu_id_stock']] if 'qu_id_stock' in entry else 'N/A'
|
||||
|
||||
table_entry.append(entry['id'])
|
||||
table_entry.append(entry['name'])
|
||||
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_entry.append(mini_stock_amount)
|
||||
table_entry.append(quantity_unit_stock_name)
|
||||
table_entries.append(table_entry)
|
||||
table_headers = ['ID', 'Name', 'Location', 'Min Stock Amount',
|
||||
'QU Purchase', 'QU Stock', 'QU Factor', 'Product Group']
|
||||
table_headers = ['ID', 'Name', 'Location', 'Min\nStock Amount',
|
||||
'QU\nPurchase', 'QU\nStock', 'QU\nFactor', 'Product\nGroup']
|
||||
return tabulate(table_entries, headers=table_headers)
|
||||
except Exception as e:
|
||||
raise e
|
||||
@ -89,7 +92,49 @@ class Table(object):
|
||||
checkmark_glyph = ''
|
||||
times_glyph = ''
|
||||
exclamation_glyph = ''
|
||||
logger = logging.getLogger('table.recipes')
|
||||
logger = logging.getLogger('table.recipe')
|
||||
normal_recipes = [recipe for recipe in self.recipes if re.search(r'^normal', recipe['type'])]
|
||||
recipes_req_map = {recipe_reqs['recipe_id']: recipe_reqs for recipe_reqs in self.recipes_reqs}
|
||||
|
||||
try:
|
||||
table_entries = []
|
||||
try:
|
||||
for item in normal_recipes:
|
||||
table_entry = []
|
||||
table_entry.append(item['id'])
|
||||
table_entry.append(item['name'])
|
||||
table_entry.append(item['base_servings'])
|
||||
|
||||
recipe_reqs = recipes_req_map[item['id']]
|
||||
missing_amount = int(recipe_reqs['missing_products_count'])
|
||||
number_of_ingredients = 's' if missing_amount > 1 else ''
|
||||
if recipe_reqs['need_fulfilled'] == '1':
|
||||
table_entry.append(self.ENOUGH_IN_STOCK_MSG.format(glyph=checkmark_glyph))
|
||||
elif recipe_reqs['need_fulfilled_with_shopping_list'] == '1':
|
||||
missing_amount = recipe_reqs['missing_products_count']
|
||||
table_entry.append(self.NOT_ENOUGH_BUT_IN_SHOPPING_LIST_MSG.format(glyph=exclamation_glyph,
|
||||
missing_amount=missing_amount, s=number_of_ingredients))
|
||||
else:
|
||||
table_entry.append(self.NOT_ENOUGH_IN_STOCK_MSG.format(glyph=times_glyph,
|
||||
missing_amount=missing_amount, s=number_of_ingredients))
|
||||
table_entries.append(table_entry)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
|
||||
# Generate recipes overview table
|
||||
table_headers = ['Id', 'Name', 'Servings', 'Requirements Fulfilled']
|
||||
return tabulate(table_entries, headers=table_headers)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
raise e
|
||||
@property
|
||||
def recipes(self):
|
||||
checkmark_glyph = ''
|
||||
times_glyph = ''
|
||||
exclamation_glyph = ''
|
||||
logger = logging.getLogger('table.recipe')
|
||||
normal_recipes = [recipe for recipe in self.recipes if re.search(r'^normal', recipe['type'])]
|
||||
recipes_req_map = {recipe_reqs['recipe_id']: recipe_reqs for recipe_reqs in self.recipes_reqs}
|
||||
|
||||
@ -160,3 +205,5 @@ class Table(object):
|
||||
|
||||
# Generate stock overview table
|
||||
return tabulate(table_entries, headers=table_headers)
|
||||
|
||||
|
||||
|
39
models.json
Normal file
39
models.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"id": "3",
|
||||
"name": "Garbonza Beans",
|
||||
"description": "",
|
||||
"location": {
|
||||
"id": "3",
|
||||
"name": "Pantry",
|
||||
"description": "",
|
||||
"row_created_timestamp": "2018-10-21 03:24:25"
|
||||
},
|
||||
"quantity_unit": {
|
||||
"stock": {
|
||||
"id": "4",
|
||||
"name": "Ib",
|
||||
"description": "Pounds",
|
||||
"row_created_timestamp": "2018-10-21 03:24:11",
|
||||
"name_plural": "Ibs",
|
||||
"plural_forms": null
|
||||
},
|
||||
"purchase": {
|
||||
"id": "4",
|
||||
"name": "Ib",
|
||||
"description": "Pounds",
|
||||
"row_created_timestamp": "2018-10-21 03:24:11",
|
||||
"name_plural": "Ibs"
|
||||
}
|
||||
},
|
||||
"qu_factor_purchase_to_stock": "1.0",
|
||||
"barcode": "",
|
||||
"min_stock_amount": "0",
|
||||
"default_best_before_days": "0",
|
||||
"row_created_timestamp": "2018-10-21 03:39:42",
|
||||
"product_group_id": "",
|
||||
"picture_file_name": null,
|
||||
"default_best_before_days_after_open": "0",
|
||||
"allow_partial_units_in_stock": "0",
|
||||
"enable_tare_weight_handling": "0",
|
||||
"tare_weight": "0.0",
|
||||
"not_check_stock_fulfillment_for_recipes": "0"
|
2
setup.py
2
setup.py
@ -22,7 +22,7 @@ setup(
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
install_requires=['Click', 'pyyaml', 'requests', 'taskw', 'lockfile', 'tox', 'html2text', 'markdown'],
|
||||
install_requires=['Click', 'pyyaml', 'requests', 'taskw', 'lockfile', 'tox', 'html2text', 'markdown', 'cachecontrol', 'fontawesome'],
|
||||
long_description=read('README.rst'),
|
||||
tests_require=[
|
||||
"pytest_mock",
|
||||
|
1
templates/debug.yml
Normal file
1
templates/debug.yml
Normal file
@ -0,0 +1 @@
|
||||
{{ grocy | tojson(indent=2) | safe }}
|
20
templates/product/add.yml
Normal file
20
templates/product/add.yml
Normal file
@ -0,0 +1,20 @@
|
||||
# Legend:
|
||||
# Location Name (id): {% for valid_value in grocy.meta.entities.locations.valid_values %}{{ valid_value.name }}({{ valid_value.id}}){% if not loop.last %}{{ ', '}}{% endif %}{% endfor %}
|
||||
# Product Group (id): {% for valid_value in grocy.meta.entities.product_groups.valid_values %}{{ valid_value.name }}({{ valid_value.id}}){% if not loop.last %}{{ ', '}}{% endif %}{% endfor %}
|
||||
# Quanity Units (id): {% for valid_value in grocy.meta.entities.quantity_units.valid_values %}{{ valid_value.name }}({{ valid_value.id}}){% if not loop.last %}{{ ', '}}{% endif %}{% endfor %}
|
||||
|
||||
name:
|
||||
description: |-
|
||||
barcode:
|
||||
location_id:
|
||||
min_stock_amount: 0
|
||||
default_best_before_days: 0
|
||||
product_group_id:
|
||||
qu_id_purchase:
|
||||
qu_id_stock:
|
||||
default_best_before_days_after_open: 0
|
||||
qu_factor_purchase_to_stock: 0
|
||||
allow_partial_units_in_stock: False
|
||||
enable_tare_weight_handling: False
|
||||
tare_weight: 0
|
||||
not_check_stock_fulfillment_for_recipes: False
|
0
templates/product/table.yml
Normal file
0
templates/product/table.yml
Normal file
18
templates/recipe/edit.yml
Normal file
18
templates/recipe/edit.yml
Normal file
@ -0,0 +1,18 @@
|
||||
name: {{ grocy.fields.name }}
|
||||
description: |
|
||||
|
||||
{{ grocy.fields.description }}
|
||||
|
||||
base_servings: {{ grocy.fields.base_servings | default("1") }}
|
||||
desired_servings: {{ grocy.fields.desired_servings | default("1") }}
|
||||
not_check_shoppinglist: {{ grocy.fields.not_check_shoppinglist | default("1") }}
|
||||
products: {% for ingredient in grocy.fields.ingredientsp%}
|
||||
- id: {{ ingredient.id }}
|
||||
name: {{ ingredient.name }}
|
||||
description: {{ ingredient.description | default(null) }}
|
||||
note: {{ ingredient.note }}
|
||||
amount: {{ ingredient.amount }}
|
||||
qu_id: {{ ingredient.qu_id }}
|
||||
only_check_single_unit_in_stock: {{ ingredient.only_check_single_unit_in_stock }}
|
||||
ingredient_group: {{ ingredient.ingredient_group | default(null) }}
|
||||
not_check_stock_fulfillment: {{ ingredient.not_check_stock_fulfillment }}{% endfor %}
|
20
templates/recipe/view.yml
Normal file
20
templates/recipe/view.yml
Normal file
@ -0,0 +1,20 @@
|
||||
name: {{ grocy.fields.name }}
|
||||
servings: {{ grocy.fields.base_servings }}
|
||||
costs: {{ grocy.fields.fulfillment.costs }}
|
||||
ingredients: {% for ingredient in grocy.fields.ingredients %}
|
||||
- product_id: {{ ingredient.id }}
|
||||
name: {{ ingredient.name}}
|
||||
description: {{ ingredient.description | default(null) }}
|
||||
note: {{ ingredient.note }}
|
||||
amount: {{ ingredient.amount }}
|
||||
qu_id: {{ ingredient.qu_id }}
|
||||
only_check_single_unit_in_stock: {{ ingredient.only_check_single_unit_in_stock }}
|
||||
ingredient_group: {{ ingredient.ingredient_group | default(null) }}
|
||||
not_check_stock_fulfillment: {{ ingredient.not_check_stock_fulfillment }}{% endfor %}
|
||||
|
||||
description: |-
|
||||
{{ grocy.fields.description }}
|
||||
|
||||
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
name: {{ name }}
|
||||
description: |
|
||||
|
||||
{{ description }}
|
||||
|
||||
picture_file_name: {% if picture_file_name is not none %}
|
||||
{{picture_file_name}}
|
||||
{% else %}
|
||||
|
||||
{% endif %}
|
||||
base_servings: {{ base_servings | default("1") }}
|
||||
desired_servings: {{ desired_servings | default("1") }}
|
||||
not_check_shoppinglist: {{ not_check_shoppinglist | default("1") }}
|
||||
products: {% for product in products %}
|
||||
- id: {{ product.id }}
|
||||
name: {{ product.name }}
|
||||
description: {{ product.description | default(null) }}
|
||||
note: {{ recipes_pos[loop.index0].note }}
|
||||
amount: {{ recipes_pos[loop.index0].amount }}
|
||||
qu_id: {{ recipes_pos[loop.index0].qu_id }}
|
||||
only_check_single_unit_in_stock: {{ recipes_pos[loop.index0].only_check_single_unit_in_stock }}
|
||||
ingredient_group: {{ recipes_pos[loop.index0].ingredient_group | default(null) }}
|
||||
not_check_stock_fulfillment: {{ recipes_pos[loop.index0].not_check_stock_fulfillment }}{% endfor %}
|
||||
|
Loading…
Reference in New Issue
Block a user