grocy-cli/grocy/cli.py

167 lines
5.0 KiB
Python

import click
from markdown import markdown
from dataclasses import asdict, replace
from jinja2 import Environment, FileSystemLoader
from uuid import uuid4
from grocy import RestService
from grocy.models import (Stock,
Battery, Shopping, Recipe)
from pkg_resources import iter_entry_points
import yaml
from sys import exit
from shutil import copy
from os import path, chmod, makedirs
from taskw import TaskWarriorShellout
import logging
logger = logging.getLogger(__name__)
APP_NAME = 'grocy-cli'
SAMPLE_CONFIG_FILE = 'sample.config.yml'
TMP_DIR = '/tmp/grocy'
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('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)
def __create_config_file():
user_cfg_file = path.join(CONFIG_DIR, CONFIG_FILE)
sample_cfg_file = path.join(PROJ_DIR, '..', SAMPLE_CONFIG_FILE)
if not path.exists(CONFIG_DIR):
click.echo('Config {} director does not exist, create...'.format(CONFIG_DIR))
makedirs(CONFIG_DIR)
copy(sample_cfg_file, user_cfg_file)
click.echo('Copying sample config to {}'.format(sample_cfg_file, user_cfg_file))
chmod(user_cfg_file, 0o664)
return user_cfg_file
@click.group()
@click.pass_context
def main(ctx):
cfg = get_config_file()
# Get logger
if 'logger' in cfg:
log_cfg = cfg['logger']
else:
log_cfg = path.join(click.get_app_dir(APP_NAME), 'grocy.log')
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'] = logger
__validate_token(cfg)
ctx.ensure_object(dict)
ctx.obj['cfg'] = cfg
def get_config_file():
no_config_msg = 'A config file was not found'
no_config_msg+= ' and will be created under {}. Is that Ok?'
no_config_msg = no_config_msg.format(click.get_app_dir(APP_NAME))
cfg_file = path.join(click.get_app_dir(APP_NAME), 'config.yml')
if not path.exists(cfg_file):
create_config_app_dir = click.confirm(no_config_msg)
if create_config_app_dir:
cfg_file = __create_config_file()
exit(0)
fd = open(cfg_file)
parse_cfg_file = yaml.safe_load(fd)
return parse_cfg_file
@main.command()
@click.pass_context
def stock(ctx):
cfg = ctx.obj['cfg']
stock = Stock(**cfg)
stock_entries = stock.get_entries()
click.echo(stock_entries)
@main.command()
@click.pass_context
def shopping(ctx):
cfg = ctx.obj['cfg']
shopping = Shopping(**cfg)
shopping_list = shopping.get_list()
click.echo(shopping_list)
@main.group()
@click.pass_context
def recipe(ctx):
if ctx.invoked_subcommand is None:
cfg = ctx.obj['cfg']
receipe = Recipe(id=None, **cfg)
recipes = receipe.get_list()
click.echo(recipes)
@recipe.command('edit')
@click.argument('recipe_id')
@click.pass_context
def edit(ctx, recipe_id):
cfg = ctx.obj['cfg']
logger = cfg['logger']
try:
if recipe_id:
recipe = Recipe(id=recipe_id, **cfg)
recipe.get(include_products=True)
loaded_template = TEMPLATE_LOADER.get_template('recipe_edit.yml')
edited_recipe = click.edit(loaded_template.render(recipe.toJSON()))
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))
@recipe.command('create')
@click.pass_context
def create(ctx):
cfg = ctx.obj['cfg']
logger = cfg['logger']
try:
recipe = Recipe(**cfg)
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)
#@main.command()
#@click.pass_context
#def chore(ctx):
# cfg = ctx.obj['cfg']
#
# chore = Chore(**cfg)
# chores = chore.get_list()
# click.echo(chores)
#
#@main.command()
#@click.pass_context
#def task(ctx):
# cfg = ctx.obj['cfg']
#
# task = Task(**cfg)
# tasks = task.get_list()
# click.echo(tasks)