import tempfile import pathlib import importlib import importlib.machinery import importlib.util import io import os import sys import shutil from typing import Any import pdfCropMargins import ruamel.yaml import pypdfium2 # Import rendercv. I import the data_models and renderer modules like this instead # of using `import rendercv` because in order for that to work, the current working # directory must be the root of the project. To make it convenient for the user, I # import the modules using the full path of the files. repository_root = pathlib.Path(__file__).parent.parent rendercv_path = repository_root / "rendercv" image_assets_directory = pathlib.Path(__file__).parent / "assets" / "images" def import_a_module(module_name: str, file_path: pathlib.Path): """Imports a module from a file. Args: module_name (str): The name of the module. file_path (pathlib.Path): The path to the file. Returns: Any: The imported module. """ spec = importlib.util.spec_from_file_location(module_name, file_path) module = importlib.util.module_from_spec(spec) # type: ignore sys.modules[module_name] = module spec.loader.exec_module(module) # type: ignore return module # Import rendercv: rendercv = import_a_module("rendercv", rendercv_path / "__init__.py") # Import the rendercv.data_models as dm: dm = import_a_module("rendercv.data_models", rendercv_path / "data_models.py") # Import the rendercv.renderer as r: r = import_a_module("rendercv.renderer", rendercv_path / "renderer.py") # Import the rendercv.cli as cli: cli = import_a_module("rendercv.cli", rendercv_path / "cli.py") # The entries below will be pasted into the documentation as YAML, and their # corresponding figures will be generated with this script. education_entry = { "institution": "Boğaziçi University", "location": "Istanbul, Turkey", "degree": "BS", "area": "Mechanical Engineering", "start_date": "2015-09", "end_date": "2020-06", "highlights": [ "GPA: 3.24/4.00 ([Transcript](https://example.com))", "Awards: Dean's Honor List, Sportsperson of the Year", ], } experience_entry = { "company": "Some Company", "location": "TX, USA", "position": "Software Engineer", "start_date": "2020-07", "end_date": "2021-08-12", "highlights": [ ( "Developed an [IOS application](https://example.com) that has received" " more than **100,000 downloads**." ), "Managed a team of **5** engineers.", ], } normal_entry = { "name": "Some Project", "location": "Remote", "date": "2021-09", "highlights": [ "Developed a web application with **React** and **Django**.", "Implemented a **RESTful API**", ], } publication_entry = { "title": ( "Magneto-Thermal Thin Shell Approximation for 3D Finite Element Analysis of" " No-Insulation Coils" ), "authors": ["J. Doe", "***H. Tom***", "S. Doe", "A. Andsurname"], "date": "2021-12-08", "journal": "IEEE Transactions on Applied Superconductivity", "doi": "10.1109/TASC.2023.3340648", } one_line_entry = { "label": "Programming", "details": "Python, C++, JavaScript, MATLAB", } bullet_entry = { "bullet": "This is a bullet entry.", } text_entry = ( "This is a *TextEntry*. It is only a text and can be useful for sections like" " **Summary**. To showcase the TextEntry completely, this sentence is added, but it" " doesn't contain any information." ) def dictionary_to_yaml(dictionary: dict[str, Any]): """Converts a dictionary to a YAML string. Args: dictionary (dict[str, Any]): The dictionary to be converted to YAML. Returns: str: The YAML string. """ yaml_object = ruamel.yaml.YAML() yaml_object.width = 60 yaml_object.indent(mapping=2, sequence=4, offset=2) with io.StringIO() as string_stream: yaml_object.dump(dictionary, string_stream) yaml_string = string_stream.getvalue() return yaml_string def define_env(env): # see https://mkdocs-macros-plugin.readthedocs.io/en/latest/macros/ entries = [ "education_entry", "experience_entry", "normal_entry", "publication_entry", "one_line_entry", "bullet_entry", "text_entry", ] entries_showcase = dict() for entry in entries: proper_entry_name = entry.replace("_", " ").title() entries_showcase[proper_entry_name] = { "yaml": dictionary_to_yaml(eval(entry)), "figures": [ { "path": f"assets/images/{theme}/{entry}.png", "alt_text": f"{proper_entry_name} in {theme}", "theme": theme, } for theme in dm.available_themes ], } env.variables["showcase_entries"] = entries_showcase def generate_entry_figures(): """Generate an image for each entry type and theme.""" # Generate PDF figures for each entry type and theme entries = { "education_entry": dm.EducationEntry(**education_entry), "experience_entry": dm.ExperienceEntry(**experience_entry), "normal_entry": dm.NormalEntry(**normal_entry), "publication_entry": dm.PublicationEntry(**publication_entry), "one_line_entry": dm.OneLineEntry(**one_line_entry), "text_entry": f"{text_entry}", "bullet_entry": dm.BulletEntry(**bullet_entry), } themes = dm.available_themes with tempfile.TemporaryDirectory() as temporary_directory: # create a temporary directory: temporary_directory_path = pathlib.Path(temporary_directory) for theme in themes: for entry_type, entry in entries.items(): design_dictionary = { "theme": theme, "disable_page_numbering": True, "show_last_updated_date": False, } if theme == "moderncv": # moderncv theme does not support these options: del design_dictionary["disable_page_numbering"] del design_dictionary["show_last_updated_date"] # Create the data model with only one section and one entry data_model = dm.RenderCVDataModel( **{ "cv": dm.CurriculumVitae(sections={entry_type: [entry]}), "design": design_dictionary, } ) # Render: latex_file_path = r.generate_latex_file_and_copy_theme_files( data_model, temporary_directory_path ) pdf_file_path = r.latex_to_pdf(latex_file_path) # Prepare the output directory and file path: output_directory = image_assets_directory / theme output_directory.mkdir(parents=True, exist_ok=True) output_pdf_file_path = output_directory / f"{entry_type}.pdf" # Remove the file if it exists: if output_pdf_file_path.exists(): output_pdf_file_path.unlink() # Crop the margins pdfCropMargins.crop( argv_list=[ "-p4", "100", "0", "100", "0", "-a4", "0", "-30", "0", "-30", "-o", str(output_pdf_file_path.absolute()), str(pdf_file_path.absolute()), ] ) # Convert pdf to an image image_name = output_pdf_file_path.with_suffix(".png") pdf = pypdfium2.PdfDocument(str(output_pdf_file_path.absolute())) page = pdf[0] image = page.render(scale=5).to_pil() # If the image exists, remove it if image_name.exists(): image_name.unlink() image.save(image_name) pdf.close() # Remove the pdf file output_pdf_file_path.unlink() def generate_examples(): """Generate example YAML and PDF files.""" examples_directory_path = pathlib.Path(__file__).parent.parent / "examples" os.chdir(examples_directory_path) themes = dm.available_themes for theme in themes: cli.cli_command_new("John Doe", theme) yaml_file_path = examples_directory_path / "John_Doe_CV.yaml" # Rename John_Doe_CV.yaml: proper_theme_name = theme.replace("_", " ").title() + "Theme" new_yaml_file_path = ( examples_directory_path / f"John_Doe_{proper_theme_name}_CV.yaml" ) if new_yaml_file_path.exists(): new_yaml_file_path.unlink() yaml_file_path.rename(new_yaml_file_path) yaml_file_path = new_yaml_file_path # Generate the PDF file: cli.cli_command_render(yaml_file_path) output_pdf_file = ( examples_directory_path / "rendercv_output" / "John_Doe_CV.pdf" ) # Move pdf file to the examples directory: new_pdf_file_path = examples_directory_path / f"{yaml_file_path.stem}.pdf" if new_pdf_file_path.exists(): new_pdf_file_path.unlink() output_pdf_file.rename(new_pdf_file_path) # Remove the rendercv_output directory: rendercv_output_directory = examples_directory_path / "rendercv_output" shutil.rmtree(rendercv_output_directory) # convert first page of the pdf to an image: pdf = pypdfium2.PdfDocument(str(new_pdf_file_path.absolute())) page = pdf[0] image = page.render(scale=2).to_pil() image_path = image_assets_directory / f"{theme}.png" image.save(image_path) def generate_schema(): """Generate the schema.""" json_schema_file_path = repository_root / "schema.json" dm.generate_json_schema_file(json_schema_file_path) def update_index(): """Update the index.md file by copying the README.md file.""" index_file_path = repository_root / "docs" / "index.md" readme_file_path = repository_root / "README.md" shutil.copy(readme_file_path, index_file_path) if __name__ == "__main__": generate_entry_figures() generate_examples() generate_schema() update_index()