#!/usr/bin/python3 import sys import json import attrs import pathlib import argparse class MyParser(argparse.ArgumentParser): def error(self, message): sys.stderr.write('error: %s\n' % message) self.print_help() sys.exit(2) @attrs.define class Template(): name: str alias: str language: str version: str author: str brief: str path: str mainfile: str files: list[str] = attrs.Factory(list) messages: list[str] = attrs.Factory(list) def compact_info(self, col_width=25): info = (f"\t{self.alias}{(col_width - len(self.alias)) * ' '}{self.name}{(col_width - len(self.name)) * ' '}{self.version}{(col_width - len(self.version)) * ' '}{self.language}") return info def full_info(self): print(f"{self.name} (alias {self.alias}) \t form version: {self.version}") print(f"{self.brief}") print(f"Language: {self.language}") print(f"Template by: {self.author}") print(f"Template location:{self.path}") print(f"Template files:") for f in self.files: print(f'\t{f}') print(f"Messages:") for i, m in enumerate(self.messages): print(f'\t{i}: {m}') def patch_make(self, source, dest, new_name): lines = [] with open(source, "r") as f: for l in f.readlines(): if l.startswith("BASENAME"): l = f"BASENAME={new_name}\n" lines.append(l) else: lines.append(l) with open(dest, "w") as f: f.writelines(lines) def copy_files(self, destination, project_name): project_name = project_name.replace(" ", "_") dest = pathlib.Path(destination).joinpath(project_name) if dest.exists() and dest.is_dir(): if len(list(dest.glob("*.*"))) > 0: raise ValueError(f"Destination folder ({str(dest.absolute())}) already exists and is not empty") dest.mkdir(exist_ok=True) src_path = pathlib.Path(self.path) # copy files for f in self.files: source_file = src_path.joinpath(f) dest_file = dest.joinpath(f) if f == self.mainfile: # rename main file to match project name dest_file = dest.joinpath(source_file.with_stem(project_name).name) dest_file.write_bytes(source_file.read_bytes()) elif f == "Makefile": self.patch_make(source_file, dest_file, project_name) else: dest_file.write_bytes(source_file.read_bytes()) # report print(f"Created new project in {dest}") print(f"... copied {len(self.files)} files") print(f"... patched Makefile") print(f"... template messages:") for i, m in enumerate(self.messages): print(f"\t{i}: {m}") print("... done.") def find_templates(): templates = {} for file_path in pathlib.Path.cwd().glob("*"): if pathlib.Path.is_dir(file_path): if pathlib.Path.joinpath(file_path, "info.json").exists(): with open(pathlib.Path.joinpath(file_path, "info.json")) as f: data = json.load(f) if "templates" in data: for t in data["templates"]: if t["alias"] in templates: print(UserWarning(f"A template with the alias {t['alias']} already exists! Ignoring template in path {str(file_path)}.")) continue templates[t["alias"]] = (Template(**t, path=str(file_path))) return templates def print_templates(templates, col_width=25): print("Available templates: ") print(f"\talias{(col_width - len('alias')) * ' '}name{(col_width - len('name')) * ' '}version{(col_width - len('language')) * ' '}language") for t in templates.keys(): print(templates[t].compact_info(col_width)) def copy_template(args, templates): t = templates.get(args.template) if t is not None: t.copy_files(args.destination, args.name) def create_parser(): parser = MyParser( description="Creates a new DFG document according to latex templates.", prog='DFGdocs', epilog='By Jan Grewe, 2023, Free software, absolutely no warranty!') parser.add_argument("-l", "--list", action="store_true", help="List the available templates.") parser.add_argument("-d", "--destination", type=str, default=".", help="The destination folder as absolute or relative path.") parser.add_argument("-i", "--info", action="store_true", help="More info on a specific template.") parser.add_argument("template", nargs='?', type=str, help="The alias of the DFG document template (use -l or --list argument to get a list of templates).") parser.add_argument("name", nargs='?', type=str, help="The project name") return parser def args_check(args, parser, templates): if args.list: print_templates(templates) sys.exit(0) if args.template is None: print(f"Error using DFGdocs. TEMPLATE argument is not defined!") parser.print_help() sys.exit(2) if args.info and args.template is not None: templates[args.template].full_info() sys.exit(0) if args.name is None: print(f"Error using DFGdocs. NAME argument is not defined!") parser.print_help() sys.exit(2) if args.template not in templates: print(f"Error using DFGdocs. Template {args.template} is not defined!") print_templates(templates) sys.exit(0) def main(): parser = create_parser() args = parser.parse_args() templates = find_templates() args_check(args, parser, templates) copy_template(args, templates) if __name__ == "__main__": main()