Module ezcv.themes
Contains utilities related to theme management, discovery and creation including:
- Section template discovery and creation
- Remote repo management
- Theme discovery & updating
Expand source code
"""Contains utilities related to theme management, discovery and creation including:
- Section template discovery and creation
- Remote repo management
- Theme discovery & updating
"""
# Standard Library Dependencies
import os # Used for path validation and manipulation
import shutil # Used to make copying and deletion of paths easier
import tempfile # Used to generate temporary folders for downloads
from zipfile import ZipFile # Used to extract all directories from zip archives
# Third Party Depenencies
import requests # Used to access remote files
from tqdm import tqdm # Used to generate progress bars during iteration
THEMES_FOLDER = os.path.join(os.path.dirname(__file__), "themes")
#TODO: Add a way to update themes from CLI, will require theme metadata to implement
def get_theme_section_directories(theme_folder:str, sections:list = []) -> list:
"""Gets a list of the available sections for a theme
Explanation
-----------
Essentially this function goes into a theme folder (full path to a theme), looks for a folder
called sections and returns a list of all the .jinja files available stripped of the extension
so i.e. if `<theme folder>/sections` had 3 files `education.jinja`, `work_experience.jinja` and
`volunteering_experience.jinja` this function would return ['education', 'work_experience', 'volunteering_experience']
Parameters
----------
sections : (list, optional)
A list of sections names, or an empty list if they need to be searched for
theme_folder : str
The full path to the theme folder (typically from calling locate_theme_directory() )
Returns
-------
list
The name(s) of the section templates that exist within the sections list without extensions
"""
if sections:
return sections
if not sections and os.path.exists(os.path.join(theme_folder, "sections")):
for section in os.listdir(os.path.join(theme_folder, "sections")):
if section.endswith(".jinja"):
section = section.replace(".jinja", "")
sections.append(section)
return sections
def setup_remote_theme(name: str, url: str):
"""downloads a remote theme (zip file) and extracts it to the THEMES_FOLDER
Parameters
----------
name : str
The name of the theme (will be the folder name for the theme also)
url : str
The URL to the .zip file
"""
theme_folder_path = os.path.join(THEMES_FOLDER, name)
if os.path.exists(theme_folder_path): # If theme folder already exists
return # Exit function
else: # Download remote theme
# Setting up necessary download variables
file_stream = requests.get(url, stream=True) # The open http request for the file
chunk_size = 1024 # Setting the progress bar chunk size to measure in kb
total_length = int(file_stream.headers.get('content-length')) # Getting file size
# Setting up the download progress bar
progress_bar = tqdm(total=total_length, unit='iB', unit_scale=True)
progress_bar.set_description(f"Download progress for {name}:")
# Write the incoming data stream to a file and update progress bar as it downloads
with tempfile.TemporaryDirectory() as temp_dir:
zip_folder_path = os.path.join(temp_dir, f"{name}.zip")
with open(zip_folder_path, 'wb') as download_file:
for chunk in file_stream.iter_content(chunk_size):
if chunk:
progress_bar.update(len(chunk))
download_file.write(chunk)
print(f"file Downloaded to {zip_folder_path}")
progress_bar.close()
# Extract zip file to theme folder
with ZipFile(zip_folder_path, "r") as archive:
archive.extractall(theme_folder_path) # Extract to theme folder
print(f"Extracted theme from {zip_folder_path} to folder {theme_folder_path}")
# If theme is at THEME_FOLDER/<name>/<name> move code to THEME_FOLDER/<name>
if os.path.exists(os.path.join(theme_folder_path, name)):
print(f"moving {os.path.join(theme_folder_path, name)} to {theme_folder_path}")
for current_file in os.listdir(os.path.join(theme_folder_path, name)):
shutil.move(os.path.join(theme_folder_path, name, current_file), os.path.join(theme_folder_path))
shutil.rmtree(os.path.join(theme_folder_path, name))
def locate_theme_directory(theme:str, site_context:dict) -> str:
"""Preprocess theme folder, and find correct full path to a theme
Parameters
----------
theme : str
The name of the theme to locate
site_context : dict
The site context with the site configuration
Returns
-------
str
The path to the theme directory
Raises
------
FileNotFoundError
If no theme folder exists, or remote is defined
"""
if os.path.exists(os.path.abspath(theme)):
theme_folder = os.path.abspath(theme)
elif os.path.exists(os.path.abspath(os.path.join("themes", theme))):
theme_folder = os.path.abspath(os.path.join("themes", theme))
elif os.path.exists(os.path.abspath(os.path.join(THEMES_FOLDER, theme))):
theme_folder = os.path.abspath(os.path.join(THEMES_FOLDER, theme))
elif theme in site_context["config"]["remotes"]:
setup_remote_theme(theme, site_context["config"]["remotes"][theme])
theme_folder = os.path.abspath(os.path.join(THEMES_FOLDER, theme))
else:
raise FileNotFoundError(f"Theme {theme} does not exist")
return theme_folder
def get_remote_themes(remotes_file_path:str = os.path.join(THEMES_FOLDER, "remotes.yml")) -> dict:
"""Takes in the path to a yml/YAML file and returns a dict of name:url pairs to download themes
Parameters
----------
remotes_file_path : str, optional
The path to the yml/YAML file that defines remote themes, by default os.path.join(THEMES_FOLDER, "remotes.yml")
Returns
-------
dict
A key-value pair of name to url of themes
"""
import yaml
if os.path.exists(remotes_file_path):
with open(remotes_file_path, "r") as remotes_file:
remotes = yaml.safe_load(remotes_file)
return remotes
Functions
def get_remote_themes(remotes_file_path: str = '/opt/hostedtoolcache/Python/3.9.6/x64/lib/python3.9/site-packages/ezcv/themes/remotes.yml') ‑> dict
-
Takes in the path to a yml/YAML file and returns a dict of name:url pairs to download themes
Parameters
remotes_file_path
:str
, optional- The path to the yml/YAML file that defines remote themes, by default os.path.join(THEMES_FOLDER, "remotes.yml")
Returns
dict
- A key-value pair of name to url of themes
Expand source code
def get_remote_themes(remotes_file_path:str = os.path.join(THEMES_FOLDER, "remotes.yml")) -> dict: """Takes in the path to a yml/YAML file and returns a dict of name:url pairs to download themes Parameters ---------- remotes_file_path : str, optional The path to the yml/YAML file that defines remote themes, by default os.path.join(THEMES_FOLDER, "remotes.yml") Returns ------- dict A key-value pair of name to url of themes """ import yaml if os.path.exists(remotes_file_path): with open(remotes_file_path, "r") as remotes_file: remotes = yaml.safe_load(remotes_file) return remotes
def get_theme_section_directories(theme_folder: str, sections: list = []) ‑> list
-
Gets a list of the available sections for a theme
Explanation
Essentially this function goes into a theme folder (full path to a theme), looks for a folder called sections and returns a list of all the .jinja files available stripped of the extension so i.e. if
<theme folder>/sections
had 3 fileseducation.jinja
,work_experience.jinja
andvolunteering_experience.jinja
this function would return ['education', 'work_experience', 'volunteering_experience']Parameters
sections
:(list
, optional)
- A list of sections names, or an empty list if they need to be searched for
theme_folder
:str
- The full path to the theme folder (typically from calling locate_theme_directory() )
Returns
list
- The name(s) of the section templates that exist within the sections list without extensions
Expand source code
def get_theme_section_directories(theme_folder:str, sections:list = []) -> list: """Gets a list of the available sections for a theme Explanation ----------- Essentially this function goes into a theme folder (full path to a theme), looks for a folder called sections and returns a list of all the .jinja files available stripped of the extension so i.e. if `<theme folder>/sections` had 3 files `education.jinja`, `work_experience.jinja` and `volunteering_experience.jinja` this function would return ['education', 'work_experience', 'volunteering_experience'] Parameters ---------- sections : (list, optional) A list of sections names, or an empty list if they need to be searched for theme_folder : str The full path to the theme folder (typically from calling locate_theme_directory() ) Returns ------- list The name(s) of the section templates that exist within the sections list without extensions """ if sections: return sections if not sections and os.path.exists(os.path.join(theme_folder, "sections")): for section in os.listdir(os.path.join(theme_folder, "sections")): if section.endswith(".jinja"): section = section.replace(".jinja", "") sections.append(section) return sections
def locate_theme_directory(theme: str, site_context: dict) ‑> str
-
Preprocess theme folder, and find correct full path to a theme
Parameters
theme
:str
- The name of the theme to locate
site_context
:dict
- The site context with the site configuration
Returns
str
- The path to the theme directory
Raises
FileNotFoundError
- If no theme folder exists, or remote is defined
Expand source code
def locate_theme_directory(theme:str, site_context:dict) -> str: """Preprocess theme folder, and find correct full path to a theme Parameters ---------- theme : str The name of the theme to locate site_context : dict The site context with the site configuration Returns ------- str The path to the theme directory Raises ------ FileNotFoundError If no theme folder exists, or remote is defined """ if os.path.exists(os.path.abspath(theme)): theme_folder = os.path.abspath(theme) elif os.path.exists(os.path.abspath(os.path.join("themes", theme))): theme_folder = os.path.abspath(os.path.join("themes", theme)) elif os.path.exists(os.path.abspath(os.path.join(THEMES_FOLDER, theme))): theme_folder = os.path.abspath(os.path.join(THEMES_FOLDER, theme)) elif theme in site_context["config"]["remotes"]: setup_remote_theme(theme, site_context["config"]["remotes"][theme]) theme_folder = os.path.abspath(os.path.join(THEMES_FOLDER, theme)) else: raise FileNotFoundError(f"Theme {theme} does not exist") return theme_folder
def setup_remote_theme(name: str, url: str)
-
downloads a remote theme (zip file) and extracts it to the THEMES_FOLDER
Parameters
name
:str
- The name of the theme (will be the folder name for the theme also)
url
:str
- The URL to the .zip file
Expand source code
def setup_remote_theme(name: str, url: str): """downloads a remote theme (zip file) and extracts it to the THEMES_FOLDER Parameters ---------- name : str The name of the theme (will be the folder name for the theme also) url : str The URL to the .zip file """ theme_folder_path = os.path.join(THEMES_FOLDER, name) if os.path.exists(theme_folder_path): # If theme folder already exists return # Exit function else: # Download remote theme # Setting up necessary download variables file_stream = requests.get(url, stream=True) # The open http request for the file chunk_size = 1024 # Setting the progress bar chunk size to measure in kb total_length = int(file_stream.headers.get('content-length')) # Getting file size # Setting up the download progress bar progress_bar = tqdm(total=total_length, unit='iB', unit_scale=True) progress_bar.set_description(f"Download progress for {name}:") # Write the incoming data stream to a file and update progress bar as it downloads with tempfile.TemporaryDirectory() as temp_dir: zip_folder_path = os.path.join(temp_dir, f"{name}.zip") with open(zip_folder_path, 'wb') as download_file: for chunk in file_stream.iter_content(chunk_size): if chunk: progress_bar.update(len(chunk)) download_file.write(chunk) print(f"file Downloaded to {zip_folder_path}") progress_bar.close() # Extract zip file to theme folder with ZipFile(zip_folder_path, "r") as archive: archive.extractall(theme_folder_path) # Extract to theme folder print(f"Extracted theme from {zip_folder_path} to folder {theme_folder_path}") # If theme is at THEME_FOLDER/<name>/<name> move code to THEME_FOLDER/<name> if os.path.exists(os.path.join(theme_folder_path, name)): print(f"moving {os.path.join(theme_folder_path, name)} to {theme_folder_path}") for current_file in os.listdir(os.path.join(theme_folder_path, name)): shutil.move(os.path.join(theme_folder_path, name, current_file), os.path.join(theme_folder_path)) shutil.rmtree(os.path.join(theme_folder_path, name))