176 lines
5.5 KiB
Python
176 lines
5.5 KiB
Python
import argparse
|
|
import json
|
|
import os
|
|
|
|
from .template import DocxTemplate, TemplateError
|
|
|
|
TEMPLATE_ARG = "template_path"
|
|
JSON_ARG = "json_path"
|
|
OUTPUT_ARG = "output_filename"
|
|
OVERWRITE_ARG = "overwrite"
|
|
QUIET_ARG = "quiet"
|
|
|
|
|
|
def make_arg_parser():
|
|
parser = argparse.ArgumentParser(
|
|
usage="python -m docxtpl [-h] [-o] [-q] {} {} {}".format(
|
|
TEMPLATE_ARG, JSON_ARG, OUTPUT_ARG
|
|
),
|
|
description="Make docx file from existing template docx and json data.",
|
|
)
|
|
parser.add_argument(
|
|
TEMPLATE_ARG, type=str, help="The path to the template docx file."
|
|
)
|
|
parser.add_argument(
|
|
JSON_ARG, type=str, help="The path to the json file with the data."
|
|
)
|
|
parser.add_argument(
|
|
OUTPUT_ARG, type=str, help="The filename to save the generated docx."
|
|
)
|
|
parser.add_argument(
|
|
"-" + OVERWRITE_ARG[0],
|
|
"--" + OVERWRITE_ARG,
|
|
action="store_true",
|
|
help="If output file already exists, overwrites without asking for confirmation",
|
|
)
|
|
parser.add_argument(
|
|
"-" + QUIET_ARG[0],
|
|
"--" + QUIET_ARG,
|
|
action="store_true",
|
|
help="Do not display unnecessary messages",
|
|
)
|
|
return parser
|
|
|
|
|
|
def get_args(parser):
|
|
try:
|
|
parsed_args = vars(parser.parse_args())
|
|
return parsed_args
|
|
# Argument errors raise a SystemExit with code 2. Normal usage of the
|
|
# --help or -h flag raises a SystemExit with code 0.
|
|
except SystemExit as e:
|
|
if e.code == 0:
|
|
raise SystemExit
|
|
else:
|
|
raise RuntimeError(
|
|
"Correct usage is:\n{parser.usage}".format(parser=parser)
|
|
)
|
|
|
|
|
|
def is_argument_valid(arg_name, arg_value, overwrite):
|
|
# Basic checks for the arguments
|
|
if arg_name == TEMPLATE_ARG:
|
|
return os.path.isfile(arg_value) and arg_value.endswith(".docx")
|
|
elif arg_name == JSON_ARG:
|
|
return os.path.isfile(arg_value) and arg_value.endswith(".json")
|
|
elif arg_name == OUTPUT_ARG:
|
|
return arg_value.endswith(".docx") and check_exists_ask_overwrite(
|
|
arg_value, overwrite
|
|
)
|
|
elif arg_name in [OVERWRITE_ARG, QUIET_ARG]:
|
|
return arg_value in [True, False]
|
|
|
|
|
|
def check_exists_ask_overwrite(arg_value, overwrite):
|
|
# If output file does not exist or command was run with overwrite option,
|
|
# returns True, else asks for overwrite confirmation. If overwrite is
|
|
# confirmed returns True, else raises OSError.
|
|
if os.path.exists(arg_value) and not overwrite:
|
|
try:
|
|
msg = (
|
|
"File %s already exists, would you like to overwrite the existing file? "
|
|
"(y/n)" % arg_value
|
|
)
|
|
if input(msg).lower() == "y":
|
|
return True
|
|
else:
|
|
raise OSError
|
|
except OSError:
|
|
raise RuntimeError(
|
|
"File %s already exists, please choose a different name." % arg_value
|
|
)
|
|
else:
|
|
return True
|
|
|
|
|
|
def validate_all_args(parsed_args):
|
|
overwrite = parsed_args[OVERWRITE_ARG]
|
|
# Raises AssertionError if any of the arguments is not validated
|
|
try:
|
|
for arg_name, arg_value in parsed_args.items():
|
|
if not is_argument_valid(arg_name, arg_value, overwrite):
|
|
raise AssertionError
|
|
except AssertionError:
|
|
raise RuntimeError(
|
|
'The specified {arg_name} "{arg_value}" is not valid.'.format(
|
|
arg_name=arg_name, arg_value=arg_value
|
|
)
|
|
)
|
|
|
|
|
|
def get_json_data(json_path):
|
|
with open(json_path) as file:
|
|
try:
|
|
json_data = json.load(file)
|
|
return json_data
|
|
except json.JSONDecodeError as e:
|
|
print(
|
|
"There was an error on line {e.lineno}, column {e.colno} while trying "
|
|
"to parse file {json_path}".format(e=e, json_path=json_path)
|
|
)
|
|
raise RuntimeError("Failed to get json data.")
|
|
|
|
|
|
def make_docxtemplate(template_path):
|
|
try:
|
|
return DocxTemplate(template_path)
|
|
except TemplateError:
|
|
raise RuntimeError("Could not create docx template.")
|
|
|
|
|
|
def render_docx(doc, json_data):
|
|
try:
|
|
doc.render(json_data)
|
|
return doc
|
|
except TemplateError:
|
|
raise RuntimeError("An error ocurred while trying to render the docx")
|
|
|
|
|
|
def save_file(doc, parsed_args):
|
|
try:
|
|
output_path = parsed_args[OUTPUT_ARG]
|
|
doc.save(output_path)
|
|
if not parsed_args[QUIET_ARG]:
|
|
print(
|
|
"Document successfully generated and saved at {output_path}".format(
|
|
output_path=output_path
|
|
)
|
|
)
|
|
except OSError as e:
|
|
print("{e.strerror}. Could not save file {e.filename}.".format(e=e))
|
|
raise RuntimeError("Failed to save file.")
|
|
|
|
|
|
def main():
|
|
parser = make_arg_parser()
|
|
# Everything is in a try-except block that catches a RuntimeError that is
|
|
# raised if any of the individual functions called cause an error
|
|
# themselves, terminating the main function.
|
|
parsed_args = get_args(parser)
|
|
try:
|
|
validate_all_args(parsed_args)
|
|
json_data = get_json_data(os.path.abspath(parsed_args[JSON_ARG]))
|
|
doc = make_docxtemplate(os.path.abspath(parsed_args[TEMPLATE_ARG]))
|
|
doc = render_docx(doc, json_data)
|
|
save_file(doc, parsed_args)
|
|
except RuntimeError as e:
|
|
print("Error: " + e.__str__())
|
|
return
|
|
finally:
|
|
if not parsed_args[QUIET_ARG]:
|
|
print("Exiting program!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|