mirror of https://github.com/eyhc1/rendercv.git
show timespan in experience entries
This commit is contained in:
parent
920cf379cf
commit
0107906480
|
@ -3,11 +3,7 @@ This module is a script to run the RenderCV and generate a CV as a PDF.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
|
|
||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
|
|
||||||
|
|
|
@ -121,38 +121,38 @@ def compute_time_span_string(start_date: Date, end_date: Date) -> str:
|
||||||
str: The time span string.
|
str: The time span string.
|
||||||
"""
|
"""
|
||||||
# calculate the number of days between start_date and end_date:
|
# calculate the number of days between start_date and end_date:
|
||||||
timeSpanInDays = (end_date - start_date).days
|
timespan_in_days = (end_date - start_date).days
|
||||||
|
|
||||||
# calculate the number of years between start_date and end_date:
|
# calculate the number of years between start_date and end_date:
|
||||||
howManyYears = timeSpanInDays // 365
|
how_many_years = timespan_in_days // 365
|
||||||
if howManyYears == 0:
|
if how_many_years == 0:
|
||||||
howManyYearsString = None
|
how_many_years_string = None
|
||||||
elif howManyYears == 1:
|
elif how_many_years == 1:
|
||||||
howManyYearsString = "1 year"
|
how_many_years_string = "1 year"
|
||||||
else:
|
else:
|
||||||
howManyYearsString = f"{howManyYears} years"
|
how_many_years_string = f"{how_many_years} years"
|
||||||
|
|
||||||
# calculate the number of months between start_date and end_date:
|
# calculate the number of months between start_date and end_date:
|
||||||
howManyMonths = round((timeSpanInDays % 365) / 30)
|
how_many_months = round((timespan_in_days % 365) / 30)
|
||||||
if howManyMonths == 0:
|
if how_many_months == 0:
|
||||||
howManyMonths = 1
|
how_many_months = 1
|
||||||
|
|
||||||
if howManyMonths == 0:
|
if how_many_months == 0:
|
||||||
howManyMonthsString = None
|
how_many_months_string = None
|
||||||
elif howManyMonths == 1:
|
elif how_many_months == 1:
|
||||||
howManyMonthsString = "1 month"
|
how_many_months_string = "1 month"
|
||||||
else:
|
else:
|
||||||
howManyMonthsString = f"{howManyMonths} months"
|
how_many_months_string = f"{how_many_months} months"
|
||||||
|
|
||||||
# combine howManyYearsString and howManyMonthsString:
|
# combine howManyYearsString and howManyMonthsString:
|
||||||
if howManyYearsString is None:
|
if how_many_years_string is None:
|
||||||
timeSpanString = howManyMonthsString
|
timespan_string = how_many_months_string
|
||||||
elif howManyMonthsString is None:
|
elif how_many_months_string is None:
|
||||||
timeSpanString = howManyYearsString
|
timespan_string = how_many_years_string
|
||||||
else:
|
else:
|
||||||
timeSpanString = f"{howManyYearsString} {howManyMonthsString}"
|
timespan_string = f"{how_many_years_string} {how_many_months_string}"
|
||||||
|
|
||||||
return timeSpanString
|
return timespan_string
|
||||||
|
|
||||||
|
|
||||||
def format_date(date: Date) -> str:
|
def format_date(date: Date) -> str:
|
||||||
|
@ -362,6 +362,15 @@ class ClassicThemeOptions(BaseModel):
|
||||||
examples=["1.35 cm", "1 in", "12 pt", "14 mm", "2 ex", "3 em"],
|
examples=["1.35 cm", "1 in", "12 pt", "14 mm", "2 ex", "3 em"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
show_timespan_in_experience_entries: bool = Field(
|
||||||
|
default=True,
|
||||||
|
title="Show Time Span in Experience Entries",
|
||||||
|
description=(
|
||||||
|
"If this option is set to true, then the time span of the experience"
|
||||||
|
" entries will be shown in the date and location column."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
margins: ClassicThemeMargins = Field(
|
margins: ClassicThemeMargins = Field(
|
||||||
default=ClassicThemeMargins(),
|
default=ClassicThemeMargins(),
|
||||||
title="Margins",
|
title="Margins",
|
||||||
|
@ -496,7 +505,7 @@ class Event(BaseModel):
|
||||||
|
|
||||||
@computed_field
|
@computed_field
|
||||||
@cached_property
|
@cached_property
|
||||||
def date_and_location_strings(self) -> list[str]:
|
def date_and_location_strings_with_timespan(self) -> list[str]:
|
||||||
date_and_location_strings = []
|
date_and_location_strings = []
|
||||||
|
|
||||||
if self.location is not None:
|
if self.location is not None:
|
||||||
|
@ -537,18 +546,19 @@ class Event(BaseModel):
|
||||||
|
|
||||||
@computed_field
|
@computed_field
|
||||||
@cached_property
|
@cached_property
|
||||||
def date_and_location_strings_without_time_span(self) -> list[str]:
|
def date_and_location_strings_without_timespan(self) -> list[str]:
|
||||||
strings_without_time_span = self.date_and_location_strings
|
# use copy() to avoid modifying the original list
|
||||||
for string in strings_without_time_span:
|
date_and_location_strings = self.date_and_location_strings_with_timespan.copy()
|
||||||
|
for string in date_and_location_strings:
|
||||||
if (
|
if (
|
||||||
"years" in string
|
"years" in string
|
||||||
or "months" in string
|
or "months" in string
|
||||||
or "year" in string
|
or "year" in string
|
||||||
or "month" in string
|
or "month" in string
|
||||||
):
|
):
|
||||||
strings_without_time_span.remove(string)
|
date_and_location_strings.remove(string)
|
||||||
|
|
||||||
return strings_without_time_span
|
return date_and_location_strings
|
||||||
|
|
||||||
@computed_field
|
@computed_field
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -794,13 +804,11 @@ class Section(BaseModel):
|
||||||
),
|
),
|
||||||
examples=["view on GitHub", "view on LinkedIn"],
|
examples=["view on GitHub", "view on LinkedIn"],
|
||||||
)
|
)
|
||||||
entries: list[NormalEntry | OneLineEntry | ExperienceEntry | EducationEntry | PublicationEntry] = (
|
entries: list[
|
||||||
Field(
|
NormalEntry | OneLineEntry | ExperienceEntry | EducationEntry | PublicationEntry
|
||||||
title="Entries",
|
] = Field(
|
||||||
description=(
|
title="Entries",
|
||||||
"The entries of the section. The format depends on the entry type."
|
description="The entries of the section. The format depends on the entry type.",
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -950,7 +958,7 @@ class CurriculumVitae(BaseModel):
|
||||||
"Test Scores",
|
"Test Scores",
|
||||||
"Certificates",
|
"Certificates",
|
||||||
"Extracurricular Activities",
|
"Extracurricular Activities",
|
||||||
"Publications"
|
"Publications",
|
||||||
]
|
]
|
||||||
if self.custom_sections is not None:
|
if self.custom_sections is not None:
|
||||||
# If the user specified custom sections, then add them to the end of the
|
# If the user specified custom sections, then add them to the end of the
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
"""This module implements LaTeX file generation and LaTeX runner utilities for RenderCV.
|
"""This module implements LaTeX file generation and LaTeX runner utilities for RenderCV.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader, PackageLoader
|
from jinja2 import Environment, PackageLoader
|
||||||
|
|
||||||
import rendercv.templates
|
|
||||||
|
|
||||||
|
|
||||||
def markdown_to_latex(markdown_string: str) -> str:
|
def markdown_to_latex(markdown_string: str) -> str:
|
||||||
|
@ -127,7 +121,7 @@ def make_it_bold(value: str, match_str: str) -> str:
|
||||||
raise ValueError("The string to match should be a string!")
|
raise ValueError("The string to match should be a string!")
|
||||||
|
|
||||||
if match_str in value:
|
if match_str in value:
|
||||||
value.replace(match_str, "\\textbf{" + match_str + "}")
|
value = value.replace(match_str, "\\textbf{" + match_str + "}")
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
@ -182,22 +176,22 @@ def render_template(data):
|
||||||
return output_file_path
|
return output_file_path
|
||||||
|
|
||||||
|
|
||||||
def run_latex(latexFilePath):
|
def run_latex(latex_file_path):
|
||||||
"""
|
"""
|
||||||
Run TinyTeX with the given LaTeX file and generate a PDF.
|
Run TinyTeX with the given LaTeX file and generate a PDF.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
latexFilePath (str): The path to the LaTeX file to compile.
|
latexFilePath (str): The path to the LaTeX file to compile.
|
||||||
"""
|
"""
|
||||||
latexFilePath = os.path.normpath(latexFilePath)
|
latex_file_path = os.path.normpath(latex_file_path)
|
||||||
latexFile = os.path.basename(latexFilePath)
|
latex_file = os.path.basename(latex_file_path)
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
# remove all files except the .tex file
|
# remove all files except the .tex file
|
||||||
for file in os.listdir(os.path.dirname(latexFilePath)):
|
for file in os.listdir(os.path.dirname(latex_file_path)):
|
||||||
if file.endswith(".tex"):
|
if file.endswith(".tex"):
|
||||||
continue
|
continue
|
||||||
os.remove(os.path.join(os.path.dirname(latexFilePath), file))
|
os.remove(os.path.join(os.path.dirname(latex_file_path), file))
|
||||||
|
|
||||||
tinytexPath = os.path.join(
|
tinytexPath = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
|
@ -211,12 +205,12 @@ def run_latex(latexFilePath):
|
||||||
f"{tinytexPath}\\latexmk.exe",
|
f"{tinytexPath}\\latexmk.exe",
|
||||||
"-lualatex",
|
"-lualatex",
|
||||||
# "-c",
|
# "-c",
|
||||||
f"{latexFile}",
|
f"{latex_file}",
|
||||||
"-synctex=1",
|
"-synctex=1",
|
||||||
"-interaction=nonstopmode",
|
"-interaction=nonstopmode",
|
||||||
"-file-line-error",
|
"-file-line-error",
|
||||||
],
|
],
|
||||||
cwd=os.path.dirname(latexFilePath),
|
cwd=os.path.dirname(latex_file_path),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print("Only Windows is supported for now.")
|
print("Only Windows is supported for now.")
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
((* import "components/classic/entry.tex.j2" as entry with context *))
|
((* import "components/classic/entry.tex.j2" as entry with context *))
|
||||||
|
|
||||||
((* macro section_contents(entries, entry_type, link_text=none, disableTimeSpan=True)*))
|
((* macro section_contents(entries, entry_type, link_text=none)*))
|
||||||
((* for value in entries *))
|
((* for value in entries *))
|
||||||
((* if disableTimeSpan *))
|
((* set date_and_location_strings = value.date_and_location_strings_without_timespan *))
|
||||||
((* set date_and_location_strings = value.date_and_location_strings_without_time_span *))
|
|
||||||
((* else *))
|
|
||||||
((* set date_and_location_strings = value.date_and_location_strings *))
|
|
||||||
((* endif *))
|
|
||||||
((* if entry_type == "EducationEntry" *))
|
((* if entry_type == "EducationEntry" *))
|
||||||
<<entry["education"](
|
<<entry["education"](
|
||||||
study_type=value.study_type,
|
study_type=value.study_type,
|
||||||
|
@ -16,6 +12,9 @@
|
||||||
date_and_location_strings=date_and_location_strings
|
date_and_location_strings=date_and_location_strings
|
||||||
)|indent(4)>>
|
)|indent(4)>>
|
||||||
((* elif entry_type == "ExperienceEntry" *))
|
((* elif entry_type == "ExperienceEntry" *))
|
||||||
|
((* if design.show_timespan_in_experience_entries *))
|
||||||
|
((* set date_and_location_strings = value.date_and_location_strings_with_timespan *))
|
||||||
|
((* endif *))
|
||||||
<<entry["experience"](
|
<<entry["experience"](
|
||||||
company=value.company,
|
company=value.company,
|
||||||
position=value.position,
|
position=value.position,
|
||||||
|
|
Loading…
Reference in New Issue