# -*- coding: utf-8 -*-
from . import SubCommands
from jsonica import errorout, refactor_check
import os, sys, json, argparse, contextlib
from functools import reduce
from util import Util
output_formats = ['csv', 'tsv']
output_delimiters = [',', '\t']
SP_FILE = '-'
[docs]class Generate(SubCommands):
""" generate command """
VERSION = '0.1.0'
__aliases = ['gen', 'g']
__help = 'generate analyzed files as TEXT from META descritor file. (e.g., Excel)'
__DEBUG = True
DEBUG = not (os.getenv('TRAVIS', not __DEBUG))
def __init__(self):
super().__init__()
@property
def command_names(self): return [self.command_name] + self.aliases
@property
def aliases(self): return self.__aliases
@property
def help(self): return self.__help
def __run__(self, **kwargs):
args = kwargs['args']
# default option 対策 seelaso AnalyzeXSeparatedOutPath
# https://github.com/setminami/Jsonica/issues/47
refactor_check(args.__class__.__name__ == 'Namespace')
if not args.output_format:
args.output_format = ('tsv', output_delimiters[1], './output/')
self.args = args
fileloc = os.path.abspath(os.path.expanduser(args.input))
workpath = Generate.__treatFileTypes(fileloc)
from xlsx import XLSX
self._print('Analyzing... %s'%fileloc)
x = XLSX(workpath, args.encoding, args.root_sheet, args.output_format)
# sys.setrecursionlimit(1024 * 8)
j = x.generate_json()
with wild_open(args.output, encoding=args.encoding) as f:
try:
print(json.dumps(j, sort_keys=True, indent=args.human_readable) \
if args.human_readable > 0 else json.dumps(j), file=f)
except:
errorout(6, args.output)
else:
self._print('Output json Success ➡️ %s'%args.output)
Util.sprint('XXX %s XXX'%x.piled_schema, self.DEBUG)
[docs] def make_argparse(self, subparser):
myparser = super().make_argparse(subparser)
outs = reduce(lambda l, r: '{} | {}'.format(l, r), output_formats)
myparser.add_argument('-i', '--input',
nargs='?', type=str, default='./Samples/cheatsheet.xlsx',
metavar='path/to/inputfile',
help='Set path/to/input xlsx filename.')
myparser.add_argument('-hr', '--human_readable',
type=int, default=0, metavar='tabsize',
help='set indent size by numeric value, Output humanreadable json files.')
myparser.add_argument('-r', '--root_sheet',
nargs='?', type=str, default='root', # Default root sheet name
metavar='sheetname',
help='set a sheetname in xlsx book have. \nconstruct json tree from the sheet as root item. "root" is Default root sheet name.')
# reserve
# myparser.add_argument('-s', '--schema',
# nargs='?', type=str,
# metavar='schema url',
# help='http://json-schema.org/draft-04/schema#')
myparser.add_argument('-o', '--output',
nargs='?', type=str, action=AnalyzeJSONOutPath,
metavar='path/to/outputfile(.json)',
help='Output interpreted json. If this set which endswith ".json" as set full filename, output jsonfile treated as the name. But when not set ".json", adopt original xlsx filename, like path/to/outputfile/[source_METAFile_name].json\
(-o has special filename "{0}" as STDOUT, and when set like "-o {0}", all other stdout messages were masked.)'.format(SP_FILE))
myparser.add_argument('-of', '--output_format',
nargs='?', type=str, action=AnalyzeXSeparatedOutPath, # (xsv, path)になるので注意
metavar='(%s):path/to/outputdir'%outs,
help='''Output with the format, If you set this, output formfiles to path/to/[source_METAFile_name].xlsx/[sheetname.?sv]s It\'ll be recommended,
if you want to have communication with non Tech team without any gitconfiging.''')
@classmethod
def __treatFileTypes(cls, file):
if file.endswith('.xlsx'):
return file
else:
errorout(7, '%s format is not supported yet.'%file)
def _print(self, msg):
if not (self.args.output == SP_FILE): print(msg)
# argparse actions
[docs]class AnalyzeJSONOutPath(argparse.Action):
""" 正当な出力json名を推測する """
def __call__(self, parser, namespace, values, option_string=None):
path, fileloc = values, namespace.input
if path.endswith('.json'):
# 指定されたファイル名まま
jsonfilename = os.path.expanduser(path)
elif path == SP_FILE:
jsonfilename = path
else:
p = os.path.expanduser(path)
if not os.path.isdir(p):
os.path.makedirs(p, exist_ok=True)
# Excelと同名
fname = os.path.splitext(os.path.basename(fileloc))[0]
jsonfilename = os.path.join(p, r'%s.json'%fname)
namespace.output = jsonfilename
[docs]class AnalyzeXSeparatedOutPath(argparse.Action):
""" sv出力先と形式を解析する """
__DEBUG = False
DEBUG = not (os.getenv('TRAVIS', not __DEBUG))
def __call__(self, parser, namespace, values, option_string=None):
if self.DEBUG:
print('{} called: {}'.format(self.__class__.__name__, values))
args = values.split(':')
if len(args) != 2:
raise argparse.ArgumentTypeError('''{} {} have to separate ?sv and outputpath with ":"
e.g., tsv:path/to/output'''.format(option_string ,values))
elif not (args[0] in output_formats):
raise argparse.ArgumentTypeError('%s %s have to be picked from %s'%(option_string, args[0], output_formats))
o = output_formats.index(args[0])
namespace.output_format = (args[0], output_delimiters[o], args[1])
[docs]@contextlib.contextmanager
def wild_open(filename=None, encoding='utf-8'):
if filename and filename != SP_FILE:
fh = open(filename, 'w', encoding=encoding)
else:
fh = sys.stdout
try:
yield fh
finally:
if fh is not sys.stdout:
fh.close()