feat: adding edit feature for recipe

This commit is contained in:
Aerex 2019-04-01 00:16:32 -05:00
parent 3d8b209fd8
commit e5e9ddd836
10 changed files with 205 additions and 7 deletions

12
file.txt Normal file
View File

@ -0,0 +1,12 @@
1. Preheat oven to 425 degrees F (220 degrees C). Lightly oil a large roasting pan.
2. Place chicken pieces in large bowl. Season with salt, oregano, pepper, rosemary, and cayenne pepper. Add fresh lemon juice, olive oil, and garlic. Place potatoes in bowl with the chicken; stir together until chicken and potatoes are evenly coated with marinade.
3. Transfer chicken pieces, skin side up, to prepared roasting pan, reserving marinade. Distribute potato pieces among chicken thighs. Drizzle with 2/3 cup chicken broth. Spoon remainder of marinade over chicken and potatoes.
4. Place in preheated oven. Bake in the preheated oven for 20 minutes. Toss chicken and potatoes, keeping chicken skin side up; continue baking until chicken is browned and cooked through, about 25 minutes more. An instant-read thermometer inserted near the bone should read 165 degrees F (74 degrees C). Transfer chicken to serving platter and keep warm.
5. Set oven to broil or highest heat setting. Toss potatoes once again in pan juices. Place pan under broiler and broil until potatoes are caramelized, about 3 minutes. Transfer potatoes to serving platter with chicken.
6. Place roasting pan on stove over medium heat. Add a splash of broth and stir up browned bits from the bottom of the pan. Strain; spoon juices over chicken and potatoes. Top with chopped oregano.
[](/"https://www.allrecipes.com/recipe/242352/greek-lemon-chicken-and-
potatoes//")

View File

@ -1,4 +1,6 @@
import click
from jinja2 import Environment, FileSystemLoader
from uuid import uuid4
from grocy import RestService
from grocy.commands import *
from pkg_resources import iter_entry_points
@ -13,9 +15,12 @@ logger = logging.getLogger(__name__)
APP_NAME = 'grocy-cli'
SAMPLE_CONFIG_FILE = 'sample.config.yml'
TMP_DIR = '/tmp/grocy'
TEMPLATE_DIR = '../templates'
CONFIG_FILE = 'config.yml'
CONFIG_DIR = click.get_app_dir(APP_NAME)
PROJ_DIR = path.join(path.dirname(path.realpath(__file__)))
TEMPLATE_LOADER = Environment(loader = FileSystemLoader(TEMPLATE_DIR), trim_blocks=True, lstrip_blocks=True)
def __validate_token(cfg):
# Validate token
@ -36,6 +41,20 @@ def __create_config_file():
chmod(user_cfg_file, 0o664)
return user_cfg_file
def open_editor(template, data):
if not path.exists(TMP_DIR):
makedirs(TMP_DIR)
tmp_filename_template = '{}/grocy-{}'.format(TMP_DIR, uuid())
open_editork k
@click.group()
@click.pass_context
@ -49,6 +68,7 @@ def main(ctx):
log_level = 'DEBUG' if 'level' not in log_cfg else log_cfg['level']
log_filename = 'log' if 'file_location' not in log_cfg else log_cfg['file_location']
logging.basicConfig(level=log_level, filename=log_filename)
cfg['logger'] = log_cfg
__validate_token(cfg)
ctx.ensure_object(dict)
@ -89,7 +109,8 @@ def shopping(ctx):
shopping_list = shopping.get_list()
click.echo(shopping_list)
@main.command()
" Recipe Commands "
@main.group()
@click.pass_context
def recipe(ctx):
cfg = ctx.obj['cfg']
@ -98,6 +119,28 @@ def recipe(ctx):
recipes = receipe.get_list()
click.echo(recipes)
@recipe.command('edit')
@click.argument('recipe_id')
@click.pass_context
def edit(ctx):
cfg = ctx.obj['cfg']
logger = cfg['logger']
try:
if recipe_id:
receipe = Recipe(id=receipe_id, **cfg)
loaded_recipe = recipe.get(include_products=True)
loaded_template = TEMPLATE_LOADER.get_template('recipe.yml')
edited_recipe = click.edit(loaded_template.render(loaded_recipe))
if edited_recipe is not None:
updated_recipe = Recipe(id=receipe_id, **edited_recipe)
updated_recipe.update()
except Exception as e:
logger.error(e)
logger.error('Could not edit recipe {}'.format(recipe_id)
" Recipe Commands "
@main.command()
@click.pass_context
def chore(ctx):

View File

@ -3,3 +3,4 @@ from grocy.commands.recipe import Recipe
from grocy.commands.chore import Chore
from grocy.commands.task import Task
from grocy.commands.shopping import Shopping
from grocy.commands.battery import Battery

64
grocy/commands/battery.py Normal file
View File

@ -0,0 +1,64 @@
import re
from datetime import datetime
from grocy import RestService
from tabulate import tabulate
from os import path
class Battery(object):
GET_CURRENT_CHORES = '/chores/get-current'
GET_CHORE_BY_ID = '/get-object/chores/{0}'
def __init__(self, **entries):
self.__dict__.update(entries)
self._init_rest_service()
#self._set_default_table_formats()
#if not hasattr('tablefmt', self):
# self.tablefmt = None
#if not hasattr('colalign', self):
# self.colalign = None
def _set_default_table_formats(self):
if not hasattr('formats', self):
self.tablefmt = None
self.colalign = None
elif not hasattr('table', self.formats):
self.tableformat = None
elif not hasattr('col', self.formats):
self.colalign = None
def _init_rest_service(self):
if self.api.startswith == '/':
self.api = self.api[1:]
if self.api.endswith == '/':
self.api = self.api[1:-1]
self.rest_service = RestService(self.api, json=True)
self.rest_service.addHeader('Content-Type', 'application/json')
self.rest_service.addToken(self.token)
def get_list(self):
try:
get_current_chores = self.rest_service.get(Chore.GET_CURRENT_CHORES)
table_headers = ['Name', 'Due']
table_entries = []
for chore in get_current_chores:
path = Chore.GET_CHORE_BY_ID.format(chore['chore_id'])
chore_info = self.rest_service.get(path)
if chore.get('next_estimated_execution_time') is None:
due_date = 'None'
elif re.match('2999',chore.get('next_estimated_execution_time')):
due_date = 'None'
else:
due_date = datetime.strptime(chore.get('next_estimated_execution_time'), '%Y-%m-%d')
table_entry = [chore_info.get('name'), due_date]
table_entries.append(table_entry)
except Exception as e:
raise e
# Generate stock overview table
return tabulate(table_entries, headers=table_headers)

View File

@ -5,7 +5,7 @@ from tabulate import tabulate
from os import path
class Chore(object):
GET_CURRENT_CHORES = '/chores/get-current'
GET_BATTERIES = '/chores/get-current'
GET_CHORE_BY_ID = '/get-object/chores/{0}'
def __init__(self, **entries):
self.__dict__.update(entries)

View File

@ -0,0 +1,3 @@
class Product(object):
def __init__(self, **entries):
self.__dict__.update(entries)

View File

@ -1,13 +1,18 @@
from grocy import RestService
from grocy.commands import products
from tabulate import tabulate
from os import path
class Recipe(object):
GET_RECIPES = '/get-objects/recipes'
GET_PRODUCT_BY_ID = '/get-object/products/{0}'
def __init__(self, **entries):
GET_RECIPE = '/get-object/recipes/{0}'
GET_PRODUCT = '/get-object/products/{0}'
GET_RECIPES_POS = '/get-object/recipes_pos'
def __init__(self, id, **entries):
self.id = id
self.__dict__.update(entries)
self._init_rest_service()
self.products = []
#self._set_default_table_formats()
#if not hasattr('tablefmt', self):
# self.tablefmt = None
@ -16,6 +21,17 @@ class Recipe(object):
def _get_products_by_recipe_id(self):
recipe_products = self.rest_service.get(Recipe.GET_RECIPES_POS)
products_for_recipe = []
for recipe_product in recipe_products:
if recipe_product.get('recipe_id') == self.id:
## TODO: need to find a better way to run a batch call to get only products for recipe
product = self.rest_service.get(Recipe.GET_PRODUCT.format(recipe_product.get('product_id'))
## combined dict into single dict
product_recipie_info = {k: v for combined_dict in [product, recipe_product] for k, v in combined_dict.items()}
self.products.append(product_recipie_info)
def _set_default_table_formats(self):
if not hasattr('formats', self):
self.tablefmt = None
@ -50,3 +66,16 @@ class Recipe(object):
raise e
# Generate stock overview table
return tabulate(table_entries, headers=table_headers)
def get(self, include_products=False):
try:
recipe = self.rest_service.get(Recipe.GET_RECIPE.format(self.id))
if include_products:
self.products = self._get_products_by_recipe_id()
except Exception as e:
raise e
return recipe.to_json()

View File

@ -1,4 +1,6 @@
click
markdown
html2text
colorama
pyyaml
requests

26
sample.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div class="\&quot;directions--section__steps\&quot;" style="\&quot;color:">
<ol>
<li style="\&quot;float:"><span class="\&quot;recipe-directions__list--item\&quot;" style="\&quot;display:">Preheat oven to 425 degrees F (220 degrees C). Lightly oil a large roasting pan.</span></li>
<li class="\&quot;step\&quot;" style="\&quot;float:"><span class="\&quot;recipe-directions__list--item\&quot;" style="\&quot;display:">Place chicken pieces in large bowl. Season with salt, oregano, pepper, rosemary, and cayenne pepper. Add fresh lemon juice, olive oil, and garlic. Place potatoes in bowl with the chicken; stir together until chicken and potatoes are evenly coated with marinade.</span></li>
<li class="\&quot;step\&quot;" style="\&quot;float:"><span class="\&quot;recipe-directions__list--item\&quot;" style="\&quot;display:">Transfer chicken pieces, skin side up, to prepared roasting pan, reserving marinade. Distribute potato pieces among chicken thighs. Drizzle with 2/3 cup chicken broth. Spoon remainder of marinade over chicken and potatoes.</span></li>
<li class="\&quot;step\&quot;" style="\&quot;float:"><span class="\&quot;recipe-directions__list--item\&quot;" style="\&quot;display:">Place in preheated oven. Bake in the preheated oven for 20 minutes. Toss chicken and potatoes, keeping chicken skin side up; continue baking until chicken is browned and cooked through, about 25 minutes more. An instant-read thermometer inserted near the bone should read 165 degrees F (74 degrees C). Transfer chicken to serving platter and keep warm.</span></li>
<li class="\&quot;step\&quot;" style="\&quot;float:"><span class="\&quot;recipe-directions__list--item\&quot;" style="\&quot;display:">Set oven to broil or highest heat setting. Toss potatoes once again in pan juices. Place pan under broiler and broil until potatoes are caramelized, about 3 minutes. Transfer potatoes to serving platter with chicken.</span></li>
<li class="\&quot;step\&quot;" style="\&quot;float:"><span class="\&quot;recipe-directions__list--item\&quot;" style="\&quot;display:">Place roasting pan on stove over medium heat. Add a splash of broth and stir up browned bits from the bottom of the pan. Strain; spoon juices over chicken and potatoes. Top with chopped oregano.</span></li>
</ol><a aria-label="\&quot;Add" href="/&quot;https://www.allrecipes.com/recipe/242352/greek-lemon-chicken-and-potatoes//&quot;" style="\&quot;outline:"></a>
<div id="\&quot;karma-lazy-seriesDetails\&quot;" style="\&quot;outline:"></div>
</div>
<div class="\&quot;directions--section__right-side\&quot;" style="\&quot;color:">
<div class="\&quot;directions--section__tipsAndTricks\&quot;" style="\&quot;outline:">
<div class="\&quot;directions--section__tipsAndTricks__title\&quot;" style="\&quot;outline:">
<span class="\&quot;directions--section__tipsAndTricks__title__font\&quot;" style="\&quot;outline:">&nbsp;</span>
</div>
</div>
</div>
</body>
</html>

18
templates/recipe.yml Normal file
View File

@ -0,0 +1,18 @@
name: {{ name }}
description: {{ description }}
recipe_id: {{ recipe_id }
picture_file_name: {{ picture_file_name | null }}
base_serving: {{ base_serving | "1" }}
desired_serving: {{ desired_serving | "1" }}
not_checking_shopping_list: {{ not_checking_shopping_list | "1" }}
{% for product in products }
products:
- id: {{ product.id }}
amount: {{ product.amount }}
note: {{ product.note }}
qu_id:
only_check_single_unit_in_stock: {{ product.only_check_single_unit_in_stock }}
ingredient_group: {{ product.ingredient_group | null }}
not_checking_shopping_list: {{ product.not_checking_shopping_list }}
{% endfor }