mirror of https://github.com/eyhc1/rendercv.git
add rendering.py tests
This commit is contained in:
parent
fab94208f2
commit
f90357e68e
|
@ -4,10 +4,17 @@ import subprocess
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
from datetime import date
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from rendercv.data_model import RenderCVDataModel
|
||||||
|
|
||||||
from jinja2 import Environment, PackageLoader
|
from jinja2 import Environment, PackageLoader
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def markdown_to_latex(markdown_string: str) -> str:
|
def markdown_to_latex(markdown_string: str) -> str:
|
||||||
"""Convert a markdown string to LaTeX.
|
"""Convert a markdown string to LaTeX.
|
||||||
|
|
||||||
|
@ -66,14 +73,14 @@ def markdown_to_latex(markdown_string: str) -> str:
|
||||||
return latex_string
|
return latex_string
|
||||||
|
|
||||||
|
|
||||||
def markdown_url_to_url(value: str) -> bool:
|
def markdown_link_to_url(value: str) -> bool:
|
||||||
"""Convert a markdown link to a normal string URL.
|
"""Convert a markdown link to a normal string URL.
|
||||||
|
|
||||||
This function is used as a Jinja2 filter.
|
This function is used as a Jinja2 filter.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
markdown_url_to_url("[Google](https://google.com)")
|
markdown_link_to_url("[Google](https://google.com)")
|
||||||
```
|
```
|
||||||
|
|
||||||
will return:
|
will return:
|
||||||
|
@ -92,13 +99,49 @@ def markdown_url_to_url(value: str) -> bool:
|
||||||
link = re.search(r"\[(.*)\]\((.*?)\)", value)
|
link = re.search(r"\[(.*)\]\((.*?)\)", value)
|
||||||
if link is not None:
|
if link is not None:
|
||||||
url = link.groups()[1]
|
url = link.groups()[1]
|
||||||
|
if url == "":
|
||||||
|
raise ValueError(f"The markdown link {value} is empty!")
|
||||||
return url
|
return url
|
||||||
else:
|
else:
|
||||||
raise ValueError("markdown_url_to_url should only be used on markdown links!")
|
raise ValueError("markdown_link_to_url should only be used on markdown links!")
|
||||||
|
|
||||||
|
|
||||||
def make_it_bold(value: str, match_str: str) -> str:
|
def make_it_something(value: str, something: str, match_str: str = None) -> str:
|
||||||
"""Make the matched parts of the string bold.
|
"""Make the matched parts of the string something. If the match_str is None, the
|
||||||
|
whole string will be made something.
|
||||||
|
|
||||||
|
Warning:
|
||||||
|
This function shouldn't be used directly. Use
|
||||||
|
(make_it_bold)[#rendercv.rendering.make_it_bold],
|
||||||
|
(make_it_underlined)[#rendercv.rendering.make_it_underlined], or
|
||||||
|
(make_it_italic)[#rendercv.rendering.make_it_italic] instead.
|
||||||
|
"""
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise ValueError(f"{something} should only be used on strings!")
|
||||||
|
|
||||||
|
if match_str is not None and not isinstance(match_str, str):
|
||||||
|
raise ValueError("The string to match should be a string!")
|
||||||
|
|
||||||
|
if something == "make_it_bold":
|
||||||
|
keyword = "textbf"
|
||||||
|
elif something == "make_it_underlined":
|
||||||
|
keyword = "underline"
|
||||||
|
elif something == "make_it_italic":
|
||||||
|
keyword = "textit"
|
||||||
|
|
||||||
|
if match_str is None:
|
||||||
|
return f"\\{keyword}{{{value}}}"
|
||||||
|
|
||||||
|
if match_str in value:
|
||||||
|
value = value.replace(match_str, f"\\{keyword}{{{match_str}}}")
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def make_it_bold(value: str, match_str: str = None) -> str:
|
||||||
|
"""Make the matched parts of the string bold. If the match_str is None, the whole
|
||||||
|
string will be made bold.
|
||||||
|
|
||||||
This function is used as a Jinja2 filter.
|
This function is used as a Jinja2 filter.
|
||||||
|
|
||||||
|
@ -115,21 +158,12 @@ def make_it_bold(value: str, match_str: str) -> str:
|
||||||
value (str): The string to make bold.
|
value (str): The string to make bold.
|
||||||
match_str (str): The string to match.
|
match_str (str): The string to match.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, str):
|
return make_it_something(value, "make_it_bold", match_str)
|
||||||
raise ValueError("make_it_bold_if should only be used on strings!")
|
|
||||||
|
|
||||||
if not isinstance(match_str, str):
|
|
||||||
raise ValueError("The string to match should be a string!")
|
|
||||||
|
|
||||||
if match_str in value:
|
|
||||||
value = value.replace(match_str, "\\textbf{" + match_str + "}")
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def make_it_underlined(value: str, match_str: str) -> str:
|
def make_it_underlined(value: str, match_str: str = None) -> str:
|
||||||
"""Make the matched parts of the string underlined.
|
"""Make the matched parts of the string underlined. If the match_str is None, the
|
||||||
|
whole string will be made underlined.
|
||||||
|
|
||||||
This function is used as a Jinja2 filter.
|
This function is used as a Jinja2 filter.
|
||||||
|
|
||||||
|
@ -146,21 +180,12 @@ def make_it_underlined(value: str, match_str: str) -> str:
|
||||||
value (str): The string to make underlined.
|
value (str): The string to make underlined.
|
||||||
match_str (str): The string to match.
|
match_str (str): The string to match.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, str):
|
return make_it_something(value, "make_it_underlined", match_str)
|
||||||
raise ValueError("make_it_underlined_if should only be used on strings!")
|
|
||||||
|
|
||||||
if not isinstance(match_str, str):
|
|
||||||
raise ValueError("The string to match should be a string!")
|
|
||||||
|
|
||||||
if match_str in value:
|
|
||||||
value = value.replace(match_str, "\\underline{" + match_str + "}")
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def make_it_italic(value: str, match_str: str) -> str:
|
def make_it_italic(value: str, match_str: str = None) -> str:
|
||||||
"""Make the matched parts of the string italic.
|
"""Make the matched parts of the string italic. If the match_str is None, the whole
|
||||||
|
string will be made italic.
|
||||||
|
|
||||||
This function is used as a Jinja2 filter.
|
This function is used as a Jinja2 filter.
|
||||||
|
|
||||||
|
@ -177,24 +202,14 @@ def make_it_italic(value: str, match_str: str) -> str:
|
||||||
value (str): The string to make italic.
|
value (str): The string to make italic.
|
||||||
match_str (str): The string to match.
|
match_str (str): The string to match.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, str):
|
return make_it_something(value, "make_it_italic", match_str)
|
||||||
raise ValueError("make_it_italic_if should only be used on strings!")
|
|
||||||
|
|
||||||
if not isinstance(match_str, str):
|
|
||||||
raise ValueError("The string to match should be a string!")
|
|
||||||
|
|
||||||
if match_str in value:
|
|
||||||
value = value.replace(match_str, "\\textit{" + match_str + "}")
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def divide_length_by(length: str, divider: float) -> str:
|
def divide_length_by(length: str, divider: float) -> str:
|
||||||
# r"""Divide a length by a number.
|
r"""Divide a length by a number.
|
||||||
|
|
||||||
# Length is a string with the following regex pattern: `\d+\.?\d* *(cm|in|pt|mm|ex|em)`
|
Length is a string with the following regex pattern: `\d+\.?\d* *(cm|in|pt|mm|ex|em)`
|
||||||
# """
|
"""
|
||||||
# Get the value as a float and the unit as a string:
|
# Get the value as a float and the unit as a string:
|
||||||
value = re.search(r"\d+\.?\d*", length).group()
|
value = re.search(r"\d+\.?\d*", length).group()
|
||||||
unit = re.findall(r"[^\d\.\s]+", length)[0]
|
unit = re.findall(r"[^\d\.\s]+", length)[0]
|
||||||
|
@ -208,7 +223,6 @@ def get_today() -> str:
|
||||||
Returns:
|
Returns:
|
||||||
str: Today's date.
|
str: Today's date.
|
||||||
"""
|
"""
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
today = date.today()
|
today = date.today()
|
||||||
return today.strftime("%B %d, %Y")
|
return today.strftime("%B %d, %Y")
|
||||||
|
@ -223,7 +237,7 @@ def get_path_to_font_directory(font_name: str) -> str:
|
||||||
return os.path.join(os.path.dirname(__file__), "templates", "fonts", font_name)
|
return os.path.join(os.path.dirname(__file__), "templates", "fonts", font_name)
|
||||||
|
|
||||||
|
|
||||||
def render_template(data):
|
def render_template(data: RenderCVDataModel, output_path: str = None):
|
||||||
"""Render the template using the given data.
|
"""Render the template using the given data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -254,7 +268,7 @@ def render_template(data):
|
||||||
|
|
||||||
# add custom filters:
|
# add custom filters:
|
||||||
environment.filters["markdown_to_latex"] = markdown_to_latex
|
environment.filters["markdown_to_latex"] = markdown_to_latex
|
||||||
environment.filters["markdown_url_to_url"] = markdown_url_to_url
|
environment.filters["markdown_link_to_url"] = markdown_link_to_url
|
||||||
environment.filters["make_it_bold"] = make_it_bold
|
environment.filters["make_it_bold"] = make_it_bold
|
||||||
environment.filters["make_it_underlined"] = make_it_underlined
|
environment.filters["make_it_underlined"] = make_it_underlined
|
||||||
environment.filters["make_it_italic"] = make_it_italic
|
environment.filters["make_it_italic"] = make_it_italic
|
||||||
|
@ -272,7 +286,12 @@ def render_template(data):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create an output file and write the rendered LaTeX code to it:
|
# Create an output file and write the rendered LaTeX code to it:
|
||||||
output_file_path = os.path.join(os.getcwd(), "tests", "outputs", "test.tex")
|
if output_path is None:
|
||||||
|
output_path = os.getcwd()
|
||||||
|
|
||||||
|
output_folder = os.path.join(output_path, "output")
|
||||||
|
file_name = data.cv.name.replace(" ", "_") + "_CV.tex"
|
||||||
|
output_file_path = os.path.join(output_folder, file_name)
|
||||||
os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
|
os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
|
||||||
with open(output_file_path, "w") as file:
|
with open(output_file_path, "w") as file:
|
||||||
file.write(output_latex_file)
|
file.write(output_latex_file)
|
||||||
|
@ -308,9 +327,10 @@ def run_latex(latex_file_path):
|
||||||
for file in os.listdir(os.path.dirname(latex_file_path)):
|
for file in os.listdir(os.path.dirname(latex_file_path)):
|
||||||
if file.endswith(".tex") or file == "fonts":
|
if file.endswith(".tex") or file == "fonts":
|
||||||
continue
|
continue
|
||||||
|
# remove the file:
|
||||||
os.remove(os.path.join(os.path.dirname(latex_file_path), file))
|
os.remove(os.path.join(os.path.dirname(latex_file_path), file))
|
||||||
|
|
||||||
tinytexPath = os.path.join(
|
tinytex_path = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
"vendor",
|
"vendor",
|
||||||
"TinyTeX",
|
"TinyTeX",
|
||||||
|
@ -320,7 +340,7 @@ def run_latex(latex_file_path):
|
||||||
print("PDF generatation started!")
|
print("PDF generatation started!")
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
f"{tinytexPath}\\latexmk.exe",
|
f"{tinytex_path}\\latexmk.exe",
|
||||||
"-lualatex",
|
"-lualatex",
|
||||||
# "-c",
|
# "-c",
|
||||||
f"{latex_file}",
|
f"{latex_file}",
|
||||||
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
from datetime import date
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from rendercv import rendering, data_model
|
||||||
|
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class TestDataModel(unittest.TestCase):
|
||||||
|
def test_markdown_to_latex(self):
|
||||||
|
input = "[link](www.example.com)"
|
||||||
|
expected = r"\hrefExternal{www.example.com}{link}"
|
||||||
|
output = rendering.markdown_to_latex(input)
|
||||||
|
with self.subTest(msg="only one link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "[link](www.example.com) and [link2](www.example2.com)"
|
||||||
|
expected = (
|
||||||
|
r"\hrefExternal{www.example.com}{link} and"
|
||||||
|
r" \hrefExternal{www.example2.com}{link2}"
|
||||||
|
)
|
||||||
|
output = rendering.markdown_to_latex(input)
|
||||||
|
with self.subTest(msg="two links"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "[**link**](www.example.com)"
|
||||||
|
expected = r"\hrefExternal{www.example.com}{\textbf{link}}"
|
||||||
|
output = rendering.markdown_to_latex(input)
|
||||||
|
with self.subTest(msg="bold link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "[*link*](www.example.com)"
|
||||||
|
expected = r"\hrefExternal{www.example.com}{\textit{link}}"
|
||||||
|
output = rendering.markdown_to_latex(input)
|
||||||
|
with self.subTest(msg="italic link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "[*link*](www.example.com) and [**link2**](www.example2.com)"
|
||||||
|
expected = (
|
||||||
|
r"\hrefExternal{www.example.com}{\textit{link}} and"
|
||||||
|
r" \hrefExternal{www.example2.com}{\textbf{link2}}"
|
||||||
|
)
|
||||||
|
output = rendering.markdown_to_latex(input)
|
||||||
|
with self.subTest(msg="italic and bold links"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "**bold**, *italic*, and [link](www.example.com)"
|
||||||
|
expected = (
|
||||||
|
r"\textbf{bold}, \textit{italic}, and"
|
||||||
|
r" \hrefExternal{www.example.com}{link}"
|
||||||
|
)
|
||||||
|
output = rendering.markdown_to_latex(input)
|
||||||
|
with self.subTest(msg="bold, italic, and link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
# invalid input:
|
||||||
|
input = 20
|
||||||
|
with self.subTest(msg="float input"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.markdown_to_latex(input)
|
||||||
|
|
||||||
|
def test_markdown_link_to_url(self):
|
||||||
|
input = "[link](www.example.com)"
|
||||||
|
expected = "www.example.com"
|
||||||
|
output = rendering.markdown_link_to_url(input)
|
||||||
|
with self.subTest(msg="only one link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "[**link**](www.example.com)"
|
||||||
|
expected = "www.example.com"
|
||||||
|
output = rendering.markdown_link_to_url(input)
|
||||||
|
with self.subTest(msg="bold link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "[*link*](www.example.com)"
|
||||||
|
expected = "www.example.com"
|
||||||
|
output = rendering.markdown_link_to_url(input)
|
||||||
|
with self.subTest(msg="italic link"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
# invalid input:
|
||||||
|
input = 20
|
||||||
|
with self.subTest(msg="float input"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.markdown_link_to_url(input)
|
||||||
|
|
||||||
|
input = "not a markdown link"
|
||||||
|
with self.subTest(msg="invalid input"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.markdown_link_to_url(input)
|
||||||
|
|
||||||
|
input = "[]()"
|
||||||
|
with self.subTest(msg="empty link"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.markdown_link_to_url(input)
|
||||||
|
|
||||||
|
def test_make_it_bold(self):
|
||||||
|
input = "some text"
|
||||||
|
expected = r"\textbf{some text}"
|
||||||
|
output = rendering.make_it_bold(input)
|
||||||
|
with self.subTest(msg="without match_str input"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "some text"
|
||||||
|
match_str = "text"
|
||||||
|
expected = r"some \textbf{text}"
|
||||||
|
output = rendering.make_it_bold(input, match_str)
|
||||||
|
with self.subTest(msg="with match_str input"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = 20
|
||||||
|
with self.subTest(msg="float input"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.make_it_bold(input)
|
||||||
|
|
||||||
|
def test_make_it_underlined(self):
|
||||||
|
input = "some text"
|
||||||
|
expected = r"\underline{some text}"
|
||||||
|
output = rendering.make_it_underlined(input)
|
||||||
|
with self.subTest(msg="without match_str input"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "some text"
|
||||||
|
match_str = "text"
|
||||||
|
expected = r"some \underline{text}"
|
||||||
|
output = rendering.make_it_underlined(input, match_str)
|
||||||
|
with self.subTest(msg="with match_str input"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = 20
|
||||||
|
with self.subTest(msg="float input"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.make_it_underlined(input)
|
||||||
|
|
||||||
|
def test_make_it_italic(self):
|
||||||
|
input = "some text"
|
||||||
|
expected = r"\textit{some text}"
|
||||||
|
output = rendering.make_it_italic(input)
|
||||||
|
with self.subTest(msg="without match_str input"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = "some text"
|
||||||
|
match_str = "text"
|
||||||
|
expected = r"some \textit{text}"
|
||||||
|
output = rendering.make_it_italic(input, match_str)
|
||||||
|
with self.subTest(msg="with match_str input"):
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
input = 20
|
||||||
|
with self.subTest(msg="float input"):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
rendering.make_it_italic(input)
|
||||||
|
|
||||||
|
def test_divide_length_by(self):
|
||||||
|
lengths = [
|
||||||
|
"10cm",
|
||||||
|
"10.24in",
|
||||||
|
"10 pt",
|
||||||
|
"10.24 mm",
|
||||||
|
"10.24 em",
|
||||||
|
"1024 ex",
|
||||||
|
]
|
||||||
|
divider = 10
|
||||||
|
expected = [
|
||||||
|
"1.0 cm",
|
||||||
|
"1.024 in",
|
||||||
|
"1.0 pt",
|
||||||
|
"1.024 mm",
|
||||||
|
"1.024 em",
|
||||||
|
"102.4 ex",
|
||||||
|
]
|
||||||
|
for length, exp in zip(lengths, expected):
|
||||||
|
with self.subTest(length=length):
|
||||||
|
self.assertEqual(rendering.divide_length_by(length, divider), exp)
|
||||||
|
|
||||||
|
def test_get_today(self):
|
||||||
|
expected = date.today().strftime("%B %d, %Y")
|
||||||
|
result = rendering.get_today()
|
||||||
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
def test_get_path_to_font_directory(self):
|
||||||
|
font_name = "test"
|
||||||
|
expected = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(__file__)),
|
||||||
|
"rendercv",
|
||||||
|
"templates",
|
||||||
|
"fonts",
|
||||||
|
font_name,
|
||||||
|
)
|
||||||
|
result = rendering.get_path_to_font_directory(font_name)
|
||||||
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
def test_render_template(self):
|
||||||
|
test_input = {
|
||||||
|
"cv": {
|
||||||
|
"academic_projects": [
|
||||||
|
{
|
||||||
|
"date": "Spring 2022",
|
||||||
|
"highlights": ["Test 1", "Test 2"],
|
||||||
|
"location": "Istanbul, Turkey",
|
||||||
|
"name": "Academic Project 1",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"highlights": ["Test 1", "Test 2"],
|
||||||
|
"name": "Academic Project 2",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end_date": "2022-05-01",
|
||||||
|
"highlights": ["Test 1", "Test 2"],
|
||||||
|
"location": "Istanbul, Turkey",
|
||||||
|
"name": "Academic Project 3",
|
||||||
|
"start_date": "2022-02-01",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"certificates": [{"name": "Certificate 1"}],
|
||||||
|
"education": [
|
||||||
|
{
|
||||||
|
"area": "Mechanical Engineering",
|
||||||
|
"end_date": "1985-01-01",
|
||||||
|
"gpa": "3.80/4.00",
|
||||||
|
"highlights": ["Test 1", "Test 2"],
|
||||||
|
"institution": "Bogazici University",
|
||||||
|
"location": "Istanbul, Turkey",
|
||||||
|
"start_date": "1980-09-01",
|
||||||
|
"study_type": "BS",
|
||||||
|
"transcript_url": "https://example.com/",
|
||||||
|
"url": "https://boun.edu.tr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"area": "Mechanical Engineering, Student Exchange Program",
|
||||||
|
"end_date": "2022-01-15",
|
||||||
|
"institution": "The University of Texas at Austin",
|
||||||
|
"location": "Austin, TX, USA",
|
||||||
|
"start_date": "2021-08-01",
|
||||||
|
"url": "https://utexas.edu",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"email": "john@doe.com",
|
||||||
|
"extracurricular_activities": [
|
||||||
|
{
|
||||||
|
"company": "Test Company 1",
|
||||||
|
"highlights": [
|
||||||
|
"Lead and train members for intercollegiate alpine ski"
|
||||||
|
" races in Turkey and organize skiing events."
|
||||||
|
],
|
||||||
|
"position": "Test Position 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"company": "Test Company 1",
|
||||||
|
"date": "Summer 2019 and 2020",
|
||||||
|
"highlights": ["Test 1", "Test 2", "Test 3"],
|
||||||
|
"location": "Izmir, Turkey",
|
||||||
|
"position": "Test Position 1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Engineer at CERN",
|
||||||
|
"location": "Geneva, Switzerland",
|
||||||
|
"name": "John Doe",
|
||||||
|
"personal_projects": [{"name": "Personal Project 1"}],
|
||||||
|
"phone": "+905413769286",
|
||||||
|
"publications": [
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"Cetin Yilmaz",
|
||||||
|
"Gregory M Hulbert",
|
||||||
|
"Noboru Kikuchi",
|
||||||
|
],
|
||||||
|
"cited_by": 243,
|
||||||
|
"date": "2007-08-01",
|
||||||
|
"doi": "10.1103/PhysRevB.76.054309",
|
||||||
|
"journal": "Physical Review B",
|
||||||
|
"title": (
|
||||||
|
"Phononic band gaps induced by inertial amplification in"
|
||||||
|
" periodic media"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"section_order": [
|
||||||
|
"Education",
|
||||||
|
"Work Experience",
|
||||||
|
"Academic Projects",
|
||||||
|
"Certificates",
|
||||||
|
"Personal Projects",
|
||||||
|
"Skills",
|
||||||
|
"Test Scores",
|
||||||
|
"Extracurricular Activities",
|
||||||
|
"Publications",
|
||||||
|
],
|
||||||
|
"skills": [
|
||||||
|
{
|
||||||
|
"details": "C++, C, Python, JavaScript, MATLAB, Lua, LaTeX",
|
||||||
|
"name": "Programming",
|
||||||
|
},
|
||||||
|
{"details": "GMSH, GetDP, CalculiX", "name": "CAE"},
|
||||||
|
],
|
||||||
|
"social_networks": [
|
||||||
|
{"network": "LinkedIn", "username": "dummy"},
|
||||||
|
{"network": "GitHub", "username": "sinaatalay"},
|
||||||
|
],
|
||||||
|
"test_scores": [
|
||||||
|
{"date": "2022-10-01", "details": "120/120", "name": "TOEFL"},
|
||||||
|
{
|
||||||
|
"details": "9.0/9.0",
|
||||||
|
"name": "IELTS",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"website": "https://example.com",
|
||||||
|
"work_experience": [
|
||||||
|
{
|
||||||
|
"company": "Company 1",
|
||||||
|
"end_date": "present",
|
||||||
|
"highlights": ["Test 1", "Test 2", "Test 3"],
|
||||||
|
"location": "Geneva, Switzerland",
|
||||||
|
"position": "Position 1",
|
||||||
|
"start_date": "2023-02-01",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"company": "Company 2",
|
||||||
|
"end_date": "2023-02-01",
|
||||||
|
"highlights": ["Test 1", "Test 2", "Test 3"],
|
||||||
|
"location": "Geneva, Switzerland",
|
||||||
|
"position": "Position 2",
|
||||||
|
"start_date": "1986-02-01",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"design": {
|
||||||
|
"font": "EBGaramond",
|
||||||
|
"options": {
|
||||||
|
"date_and_location_width": "3.6 cm",
|
||||||
|
"margins": {
|
||||||
|
"entry_area": {
|
||||||
|
"left": "0.2 cm",
|
||||||
|
"right": "0.2 cm",
|
||||||
|
"vertical_between": "0.12 cm",
|
||||||
|
},
|
||||||
|
"highlights_area": {
|
||||||
|
"left": "0.6 cm",
|
||||||
|
"top": "0.12 cm",
|
||||||
|
"vertical_between_bullet_points": "0.07 cm",
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"bottom": "1.35 cm",
|
||||||
|
"left": "1.35 cm",
|
||||||
|
"right": "1.35 cm",
|
||||||
|
"top": "1.35 cm",
|
||||||
|
},
|
||||||
|
"section_title": {"bottom": "0.13 cm", "top": "0.13 cm"},
|
||||||
|
},
|
||||||
|
"primary_color": "rgb(0,79,144)",
|
||||||
|
"show_last_updated_date": True,
|
||||||
|
"show_timespan_in_experience_entries": True,
|
||||||
|
},
|
||||||
|
"theme": "classic",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data = data_model.RenderCVDataModel(**test_input)
|
||||||
|
rendering.render_template(data=data, output_path=os.path.dirname(__file__))
|
||||||
|
|
||||||
|
# Check if the output file exists:
|
||||||
|
output_folder_path = os.path.join(os.path.dirname(__file__), "output")
|
||||||
|
output_file_path = os.path.join(output_folder_path, "John_Doe_CV.tex")
|
||||||
|
self.assertTrue(os.path.exists(output_file_path))
|
||||||
|
|
||||||
|
# Compare the output file with the reference file:
|
||||||
|
reference_file_path = os.path.join(
|
||||||
|
os.path.dirname(__file__), "reference_files", "John_Doe_CV.tex"
|
||||||
|
)
|
||||||
|
with open(output_file_path, "r") as file:
|
||||||
|
output = file.read()
|
||||||
|
with open(reference_file_path, "r") as file:
|
||||||
|
reference = file.read()
|
||||||
|
|
||||||
|
self.assertEqual(output, reference)
|
||||||
|
|
||||||
|
# Check if the font directory exists:
|
||||||
|
font_directory_path = os.path.join(output_folder_path, "fonts")
|
||||||
|
self.assertTrue(os.path.exists(font_directory_path))
|
||||||
|
|
||||||
|
required_files = [
|
||||||
|
"EBGaramond-Italic.ttf",
|
||||||
|
"EBGaramond-Regular.ttf",
|
||||||
|
"EBGaramond-Bold.ttf",
|
||||||
|
"EBGaramond-BoldItalic.ttf",
|
||||||
|
]
|
||||||
|
font_files = os.listdir(font_directory_path)
|
||||||
|
for required_file in required_files:
|
||||||
|
with self.subTest(required_file=required_file):
|
||||||
|
self.assertIn(required_file, font_files)
|
||||||
|
|
||||||
|
# Remove the output directory:
|
||||||
|
shutil.rmtree(output_folder_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue