diff --git a/_tools/move_rst_files.py b/_tools/move_rst_files.py new file mode 100644 index 000000000..f51d23b95 --- /dev/null +++ b/_tools/move_rst_files.py @@ -0,0 +1,108 @@ +import re +from argparse import ArgumentParser +from os.path import isfile, isdir, join, realpath, split, dirname, relpath +import os +import shutil + + +def parse_and_get_arguments(): + """Creates and returns an object with parsed arguments, using argparse.""" + parser: ArgumentParser = ArgumentParser( + prog="move_rst_files", + description="Moves reST documents and their dependencies from one folder to another. Outputs required redirections in the ReadTheDocs backend.", + ) + parser.add_argument( + "documents", nargs="+", help="Paths of documents to move.", + ) + parser.add_argument( + "output_path", help="Path to the target output directory.", + ) + return parser.parse_args() + + +def find_project_root_path(document_path): + """Returns the path to the repository's root directory by looking for the file conf.py, starting +from the path of any file in the project.""" + full_path = realpath(document_path) + dirpath = split(full_path)[0] + + root_path = "" + current = dirpath + iterations = 0 + while root_path == "": + if isfile(join(current, "conf.py")): + root_path = current + else: + current = split(current)[0] + if current == "": + break + iterations += 1 + if iterations > 20: + break + return root_path + + +def find_images(document): + """Returns the list of image filepaths used by the `document`.""" + images = [] + for line in document: + match = re.match(r"\.\. image::\s+(img\/.+)", line) + if match: + images.append(match[1]) + return list(set(images)) + + +def find_document_dependencies(documents): + """For each document path in `documents`, finds all pictures it depends on and returns a dict with the form { document: [images] }.""" + data = {} + for path in documents: + with open(path, "r") as rst_file: + images = find_images(rst_file) + data[path] = images + return data + + +def move_documents(paths, output_path): + """Moves .rst files and all their image dependencies to `output_path`""" + data = find_document_dependencies(paths) + for path in data: + directory = dirname(path) + shutil.move(path, output_path) + for image in data[path]: + image_in_path = join(directory, image) + image_out_path = join(output_path, image) + image_out_dirpath = dirname(image_out_path) + if not isdir(image_out_dirpath): + os.makedirs(image_out_dirpath) + shutil.move(image_in_path, image_out_path) + + +def print_redirects(paths): + """Prints redirects we need to make on the ReadTheDocs backend with the form "input -> output". + Moving the file /learning/features/viewports/viewports.rst to /tutorials/viewports/viewports.rst + Requires the following redirect: + /learning/features/viewports/viewports.html -> /tutorials/viewports/viewports.html + """ + redirects = "" + project_root_path = find_project_root_path(paths[0]) + out_path_relative = relpath(args.output_path, project_root_path) + for document in paths: + in_path_relative = relpath(document, project_root_path) + in_directory, filename_rst = split(in_path_relative) + filename_html = filename_rst.rsplit(".rst", 1)[0] + ".html" + + in_path = join(in_directory, filename_html) + out_path = join(out_path_relative, filename_html) + redirects += in_path + " -> " + out_path + "\n" + print(redirects) + + +if __name__ == "__main__": + args = parse_and_get_arguments() + assert isdir(args.output_path) + + documents = [ + path for path in args.documents if isfile(path) and path.endswith(".rst") + ] + move_documents(documents, args.output_path) + print_redirects(documents)