Revamp CLI and add add command

This commit is contained in:
Tim Van Baak 2019-12-16 23:40:46 -08:00
parent ecd9d67881
commit 69627d3fda
4 changed files with 153 additions and 73 deletions

View File

@ -1,42 +1,43 @@
# Standard library imports
import argparse
import os
import logging
import sys
# Application imports
import cli
# Globals
logger = logging.getLogger("inquisitor.__main__")
import configs
def parse_args(valid_commands):
command_descs = "\n".join([
"- {0}: {1}".format(name, func.__doc__)
for name, func in valid_commands.items()])
parser = argparse.ArgumentParser(description="Available commands:\n{}\n".format(command_descs), formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("command", default="help", help="The command to execute", choices=valid_commands, metavar="COMMAND")
parser.add_argument("--srcdir", help="Path to sources folder (default ./sources)", default="./sources")
parser.add_argument("--dungeon", help="Path to item cache folder (default ./dungeon)", default="./dungeon")
parser.add_argument("--sources", help="Sources to update, by name", nargs="*")
parser.add_argument("--log", default="INFO", help="Set the log level (default: INFO)")
parser = argparse.ArgumentParser(
description="Available commands:\n{}\n".format(command_descs),
formatter_class=argparse.RawDescriptionHelpFormatter,
add_help=False)
parser.add_argument("command",
nargs="?",
default="help",
help="The command to execute",
choices=valid_commands,
metavar="command")
parser.add_argument("args",
nargs=argparse.REMAINDER,
help="Command arguments",
metavar="args")
parser.add_argument("-v",
action="store_true",
dest="verbose",
help="Enable debug logging")
args = parser.parse_args()
global print_usage
print_usage = parser.print_help
return args
def run_flask_server(args):
"""Run the default flask server serving from the specified dungeon."""
try:
from app import app
app.run()
return 0
except Exception as e:
logger.error(e)
return (-1)
return parser.parse_args()
def command_help(args):
"""Print this help message and exit."""
print_usage()
return 0
def main():
# Enumerate valid commands.
@ -44,21 +45,19 @@ def main():
name[8:] : func
for name, func in vars(cli).items()
if name.startswith("command_")}
commands["run"] = run_flask_server
commands['help'] = command_help
args = parse_args(commands)
# Configure logging.
if args.command != 'run':
loglevel = getattr(logging, args.log.upper())
if not isinstance(loglevel, int):
raise ValueError("Invalid log level: {}".format(args.log))
logging.basicConfig(format='[%(levelname)s:%(filename)s:%(lineno)d] %(message)s', level=loglevel)
if args.verbose:
configs.log_verbose()
# Execute command.
if args.command:
return commands[args.command](args)
return commands[args.command](args.args)
if __name__ == "__main__":
import sys
sys.exit(main())

View File

@ -1,60 +1,131 @@
# Standard library imports
import argparse
import json
import logging
import os
import random
# Application imports
import dungeon as dungeonlib
# Globals
logger = logging.getLogger("inquisitor.cli")
from configs import logger, DUNGEON_PATH, SOURCES_PATH
def command_update(args):
"""Fetch and store new items from the specified sources."""
if not os.path.isdir(args.srcdir):
print("update: Error: srcdir must be a directory")
return (-1)
if not os.path.isdir(args.dungeon):
logger.error("update: Error: dungeon must be a directory")
return (-1)
if not args.sources:
logger.error("update: Error: No sources specified")
return (-1)
parser = argparse.ArgumentParser(
prog="inquisitor update",
description=command_update.__doc__,
add_help=False)
parser.add_argument("source",
nargs="*",
help="Sources to update.")
args = parser.parse_args(args)
# Initialize dungeon.
dungeon = dungeonlib.Dungeon(args.dungeon)
# Process each source argument.
for source_arg in args.sources:
dungeon.update(source_arg, args)
if len(args.source) == 0:
parser.print_help()
return 0
if not os.path.isdir(DUNGEON_PATH):
logger.error("Couldn't find dungeon. Set INQUISITOR_DUNGEON or cd to parent folder of ./dungeon")
return -1
if not os.path.isdir(SOURCES_PATH):
logger.error("Couldn't find sources. Set INQUISITOR_SOURCES or cd to parent folder of ./sources")
# Update sources
from importer import update_sources
update_sources(*args.source)
return 0
def command_deactivate(args):
"""Deactivate all items in the specified dungeon cells."""
if not os.path.isdir(args.dungeon):
logger.error("deactivate: Error: dungeon must be a directory")
return (-1)
if not args.sources:
logger.error("deactivate: Error: No sources specified")
return (-1)
parser = argparse.ArgumentParser(
prog="inquisitor deactivate",
description=command_deactivate.__doc__,
add_help=False)
parser.add_argument("source",
nargs="*",
help="Cells to deactivate.")
args = parser.parse_args(args)
# Initialize dungeon.
dungeon = dungeonlib.Dungeon(args.dungeon)
if len(args.source) == 0:
parser.print_help()
return 0
if not os.path.isdir(DUNGEON_PATH):
logger.error("Couldn't find dungeon. Set INQUISITOR_DUNGEON or cd to parent folder of ./dungeon")
return -1
# Deactivate all items in each source.
for source_name in args.sources:
if source_name not in dungeon:
print("Error: No source named '{}'".format(source_name))
print("Valid source names are: " + " ".join([s for s in dungeon]))
continue
cell = dungeon[source_name]
from loader import load_items
for source_name in args.source:
path = os.path.join(DUNGEON_PATH, source_name)
if not os.path.isdir(path):
logger.warning("'{}' is not an extant source".format(source_name))
count = 0
for item_id in cell:
item = cell[item_id]
items, _ = load_items(source_name)
for item in items.values():
if item['active']:
item.deactivate()
item['active'] = False
count += 1
logger.info("Deactivated {} items in '{}'".format(count, source_name))
return 0
def command_add(args):
"""Creates an item."""
parser = argparse.ArgumentParser(
prog="inquisitor add",
description=command_add.__doc__,
add_help=False)
parser.add_argument("--id", help="String")
parser.add_argument("--source", help="String")
parser.add_argument("--title", help="String")
parser.add_argument("--link", help="URL")
parser.add_argument("--time", type=int, help="Unix timestmap")
parser.add_argument("--author", help="String")
parser.add_argument("--body", help="HTML")
parser.add_argument("--tags", help="Comma-separated list")
parser.add_argument("--ttl", type=int, help="Cleanup protection in seconds")
args = parser.parse_args(args)
if not args.title:
parser.print_help()
return 0
if not os.path.isdir(DUNGEON_PATH):
logger.error("Couldn't find dungeon. Set INQUISITOR_DUNGEON or cd to parent folder of ./dungeon")
return -1
from importer import populate_new
item = {
'id': '{:x}'.format(random.getrandbits(16 * 4)),
'source': 'inquisitor'
}
if args.id: item['id'] = str(args.id)
if args.source: item['source'] = str(args.source)
if args.title: item['title'] = str(args.title)
if args.link: item['link'] = str(args.link)
if args.time: item['time'] = int(args.time)
if args.author: item['author'] = str(args.author)
if args.body: item['body'] = str(args.body)
if args.tags: item['tags'] = [str(tag) for tag in args.tags]
if args.ttl: item['ttl'] = int(args.ttl)
populate_new(item)
s = json.dumps(item, indent=2)
path = os.path.join(DUNGEON_PATH, item['source'], item['id'] + '.item')
with open(path, 'w', encoding='utf8') as f:
f.write(s)
logger.info(item)
# def command_run(args):
# """Run the default Flask server."""
# pass
# def run_flask_server(args):
# """Run the default flask server serving from the specified dungeon."""
# try:
# from app import app
# app.run()
# return 0
# except Exception as e:
# logger.error(e)
# return (-1)

View File

@ -5,9 +5,19 @@ DUNGEON_PATH = os.path.abspath(os.environ.get("INQUISITOR_DUNGEON") or "./dungeo
SOURCES_PATH = os.path.abspath(os.environ.get("INQUISITOR_SOURCES") or "./sources")
logger = logging.getLogger("inquisitor")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
formatter = logging.Formatter('[{levelname}] {message}', style="{")
handler.setFormatter(formatter)
logger.addHandler(handler)
def log_normal():
logger.setLevel(logging.INFO)
handler.setLevel(logging.INFO)
formatter = logging.Formatter('[{levelname}] {message}', style="{")
handler.setFormatter(formatter)
def log_verbose():
logger.setLevel(logging.DEBUG)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('[{asctime}] [{levelname}:{filename}:{lineno}] {message}', style="{")
handler.setFormatter(formatter)
log_normal()

View File

@ -74,7 +74,7 @@ def update_source(source_name, fetch_new):
# If the item is new, write it.
new_count += 1
s = json.dumps(item)
path = os.path.join(DUNGEON_PATH, item['source'], item['id'])
path = os.path.join(DUNGEON_PATH, item['source'], item['id'] + ".item")
with open(path, 'w', encoding="utf8") as f:
f.write(s)