mirror of https://github.com/eyhc1/rendercv.git
320 lines
10 KiB
Python
320 lines
10 KiB
Python
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()
|