Module ezcv.filters

This is a module that provides additional jinja filters to help with theme development

Functions

inject_filters() -> jinja2.Environment: Takes in a jinja environment and injects the filters from this module + functions fom the extra_filters parameter

split_to_sublists() -> List[list]: Takes a list and splits it into sublists of size n

get_image_path() -> str: Takes in the path to an image and returns it in usable format to use in img tags as src attribute

get_filename_without_extension() -> str: Takes in path and returns filename without extension

pretty_datetime() -> str: A utility function for pretty printing dates provided for jobs/getting a degree/volunteering etc

pretty_defaultdict() -> str: Returns a prettyprinted form of a defaultdict

Expand source code
"""This is a module that provides additional jinja filters to help with theme development

Functions
---------
inject_filters() -> jinja2.Environment:
    Takes in a jinja environment and injects the filters from this module + functions fom the extra_filters parameter

split_to_sublists() -> List[list]:
    Takes a list and splits it into sublists of size n

get_image_path() -> str:
    Takes in the path to an image and returns it in usable format to use in img tags as src attribute

get_filename_without_extension() -> str:
    Takes in path and returns filename without extension

pretty_datetime() -> str:
    A utility function for pretty printing dates provided for jobs/getting a degree/volunteering etc

pretty_defaultdict() -> str:
    Returns a prettyprinted form of a defaultdict
"""
# Standard library dependencies
from pprint import pformat
from typing import Callable, DefaultDict, List # Used to typehint accurately for documentation

# Third Party Dependencies
import jinja2           # Used mostly to typehint and allow for autocompletion in the file


def inject_filters(env:jinja2.Environment, extra_filters:List[Callable] = []) -> jinja2.Environment:
    """Takes in a jinja environment and injects the filters from this module + functions fom the extra_filters parameter

    Parameters
    ----------
    env : jinja2.Environment
        The existing environment you want to inject the filters into

    extra_filters : List[Callable], optional
        An optional set of method objects containing additional filter functions you want to use

    Returns
    -------
    jinja2.Environment
        The input environment with the filters injected
    """
    filters = [split_to_sublists, get_image_path, get_filename_without_extension, pretty_datetime, pretty_defaultdict]

    if extra_filters:
        for filter in extra_filters:
            filters.append(filter)

    for filter in filters:
        env.filters[filter.__name__] = filter

    return env


def split_to_sublists(initial_list:list, n:int, strict:bool=True) -> List[list]:
    """Takes a list and splits it into sublists of size n

    Parameters
    ----------
    initial_list : list
        The initial list to split into sublists

    n : int
        The size of each sublist

    strict: bool
        Whether to force an error if the length of the initial list is not divisible by n (split into even groups), default True

    Returns
    -------
    List[list]
        A list of lists of size n (unless strict is False, then the last list may be > n)

    Examples
    --------

    ### Split gallery images into sublists of 3

    #### JINJA USAGE
    ```jinja2
    {% if gallery|length % 3 == 0 %}
    {% for sublist in gallery|split_to_sublists(3) %}
        <div class="row">

        <div class="col-md-4">
            <img src="{{ sublist.0[0]['file_path'] }}" alt="{{ sublist.0[0]['file_path'].split()[-1] }}">
        </div>

        <div class="col-md-4">
            <img src="{{ sublist.1[0]['file_path'] }}" alt="{{ sublist.1[0]['file_path'].split()[-1]}}">
        </div>

        <div class="col-md-4">
            <img src="{{ sublist.2[0]['file_path'] }}" alt="{{ sublist.2[0]['file_path'].split()[-1] }}">
        </div>

        </div>
    {% endfor %}
    {% endif }
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    gallery = ["image 1" , "image 2", "image 3", "image 4" , "image 5", "image 6"]

    if len(images) % 3 == 0:
        for sublist in split_to_sublists(gallery, 3): # Returns [["image 1" , "image 2", "image 3"], ["image 4" , "image 5", "image 6"]]
        ... # Do stuff with each sublist
    ```
    """
    if strict:
        if not len(initial_list) % n == 0:
            raise ValueError(f"Provided list was not of correct size: \n\tList: {initial_list}\n\tSegment size {n}")

    result = []

    for i in range(0, len(initial_list), n): # Create sublists up to size n 
        result.append( initial_list[i:i + n])

    return result

def get_image_path(path:str) -> str:
    """Takes in the path to an image and returns it in usable format to use in img tags as src attribute

    Parameters
    ----------
    path : str
        The raw image path from metadata

    Returns
    -------
    str
        The string corresponding to the correct path to use in img tags src attributes

    Examples
    --------
    ### Passing in an image path from a project in the projects section:

    #### JINJA USAGE
    ```jinja2
    {% for project in projects %}
        {% if project[0]["image"] %}
            <img src="{{ project[0]['image'] | get_image_path }}" alt="{{ project[0]['image'] | get_filename_without_extension }}" />
        {% endif %}
    {% endfor %}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    project = [{"image": "image.jpg"}, ["other stuff"]]

    if project[0]["image"]:
        print(get_image_path(project[0]['image'])) # Prints /images/image.jpg which is a usable path

    project = [{"image": "https://example.com/img/image.jpg"}, ["other stuff"]]

    if project[0]["image"]:
        print(get_image_path(project[0]['image'])) # Prints https://example.com/img/image.jpg which is a usable path
    ```
    """

    if path.startswith("http"):
        return path

    elif path.startswith("images"):
        return f"{path}"

    else:
        return f"images/{path}"

def get_filename_without_extension(path:str) -> str:
    """Takes in path and returns filename without extension

    Parameters
    ----------
    path : str
        The original path to file

    Returns
    -------
    str
        Then name without the extension on the end

    Examples
    --------
    ### Take in an image path and return the filename without extension to use for alt tag

    #### JINJA USAGE
    ```jinja2
    {% for project in projects %}
        {% if project[0]["image"] %}
            <img src="{{ project[0]['image'] | get_image_path }}" alt="{{ project[0]['image'] | get_filename_without_extension }}" />
        {% endif %}
    {% endfor %}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    project = [{"image": "/path/to/John Doe.jpg"}, ["other stuff"]]

    if project[0]["image"]:
        print(get_filename_without_extension(project[0]['image'])) # Prints "John Doe"
    ```
    """
    return str(path.split("/")[-1].split(".")[0])

def pretty_datetime(month_started:str, year_started:str, month_ended:str, year_ended:str, current:bool) -> str:
    """A utility function for pretty printing dates provided for jobs/getting a degree/volunteering etc

    Parameters
    ----------
    month_started : str
        The month started i.e. October

    year_started : str
        The year started i.e. 2013

    month_ended : str
        The month ended i.e. December

    year_ended : str
        The year ended i.e. 2017

    current : bool
        A boolean describing if this is somewhere you are currently working/studying/volunteering at

    Returns
    -------
    str
        A pretty string of the date i.e. 'October 2013 - December 2017'

    Examples
    --------
    ### Printing the date details of a degree in the `education` section:

    #### JINJA USAGE
    ```jinja2
    {% for experience in education %}
        {{ experience[0]["month_started"] | pretty_datetime(experience[0]["year_started"], experience[0]["month_ended"], experience[0]["year_ended"], experience[0]["current"]) }}
    {%endfor%}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python

    month_started = "October"
    year_started = "2013"

    month_ended = "December"
    year_ended = "2017 

    current = False

    print(pretty_datetime(month_started, year_started, month_ended, year_ended, current)) # October 2013 - December 2017
    ```
    """

    
    if month_started or year_started:
        if month_started and year_started:
            beginning = f"{month_started} {year_started}"
        elif month_started:
            beginning = f"{month_started}"
        elif year_started:
            beginning = f"{year_started}"
        sep = " - "
    else:
        sep = ""

    if current:
        end = "Present"
    
    elif month_ended or year_ended:
        if month_ended and year_ended:
            end = f"{month_ended} {year_ended}"
        elif month_ended:
            end = f"{month_ended}"
        elif year_ended:
            end = f"{year_ended}"
    else:
        end = ""

    return f"{beginning}{sep}{end}"


def pretty_defaultdict(ugly_dict:DefaultDict) -> str:
    """Returns a prettyprinted form of a defaultdict

    Parameters
    ----------
    ugly_dict : DefaultDict
        A defaultdictionary to pretty print

    Notes
    -----
    Needs to be used with the safe filter to work properly

    Returns
    -------
    str
        The 

    Examples
    --------
    ### Pretty printing the `config` defaultdictionary:

    #### JINJA USAGE
    ```jinja2
    {{ config | pretty_defaultdict | safe }}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    from ezcv.core import get_site_config

    config = get_site_config()

    print(pretty_defaultdict(config)) # Prints config dict in pretty form
    ```
    """
    return pformat(dict(ugly_dict)).replace("\n", "<br>").replace("{", "{<br>").replace("}", "<br>    }")

Functions

def get_filename_without_extension(path: str) ‑> str

Takes in path and returns filename without extension

Parameters

path : str
The original path to file

Returns

str
Then name without the extension on the end

Examples

Take in an image path and return the filename without extension to use for alt tag

JINJA USAGE

{% for project in projects %}
    {% if project[0]["image"] %}
        <img src="{{ project[0]['image'] | get_image_path }}" alt="{{ project[0]['image'] | get_filename_without_extension }}" />
    {% endif %}
{% endfor %}

The above jinja is roughly equivalent to something like this in pure python:

project = [{"image": "/path/to/John Doe.jpg"}, ["other stuff"]]

if project[0]["image"]:
    print(get_filename_without_extension(project[0]['image'])) # Prints "John Doe"
Expand source code
def get_filename_without_extension(path:str) -> str:
    """Takes in path and returns filename without extension

    Parameters
    ----------
    path : str
        The original path to file

    Returns
    -------
    str
        Then name without the extension on the end

    Examples
    --------
    ### Take in an image path and return the filename without extension to use for alt tag

    #### JINJA USAGE
    ```jinja2
    {% for project in projects %}
        {% if project[0]["image"] %}
            <img src="{{ project[0]['image'] | get_image_path }}" alt="{{ project[0]['image'] | get_filename_without_extension }}" />
        {% endif %}
    {% endfor %}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    project = [{"image": "/path/to/John Doe.jpg"}, ["other stuff"]]

    if project[0]["image"]:
        print(get_filename_without_extension(project[0]['image'])) # Prints "John Doe"
    ```
    """
    return str(path.split("/")[-1].split(".")[0])
def get_image_path(path: str) ‑> str

Takes in the path to an image and returns it in usable format to use in img tags as src attribute

Parameters

path : str
The raw image path from metadata

Returns

str
The string corresponding to the correct path to use in img tags src attributes

Examples

Passing in an image path from a project in the projects section:

JINJA USAGE

{% for project in projects %}
    {% if project[0]["image"] %}
        <img src="{{ project[0]['image'] | get_image_path }}" alt="{{ project[0]['image'] | get_filename_without_extension }}" />
    {% endif %}
{% endfor %}

The above jinja is roughly equivalent to something like this in pure python:

project = [{"image": "image.jpg"}, ["other stuff"]]

if project[0]["image"]:
    print(get_image_path(project[0]['image'])) # Prints /images/image.jpg which is a usable path

project = [{"image": "https://example.com/img/image.jpg"}, ["other stuff"]]

if project[0]["image"]:
    print(get_image_path(project[0]['image'])) # Prints https://example.com/img/image.jpg which is a usable path
Expand source code
def get_image_path(path:str) -> str:
    """Takes in the path to an image and returns it in usable format to use in img tags as src attribute

    Parameters
    ----------
    path : str
        The raw image path from metadata

    Returns
    -------
    str
        The string corresponding to the correct path to use in img tags src attributes

    Examples
    --------
    ### Passing in an image path from a project in the projects section:

    #### JINJA USAGE
    ```jinja2
    {% for project in projects %}
        {% if project[0]["image"] %}
            <img src="{{ project[0]['image'] | get_image_path }}" alt="{{ project[0]['image'] | get_filename_without_extension }}" />
        {% endif %}
    {% endfor %}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    project = [{"image": "image.jpg"}, ["other stuff"]]

    if project[0]["image"]:
        print(get_image_path(project[0]['image'])) # Prints /images/image.jpg which is a usable path

    project = [{"image": "https://example.com/img/image.jpg"}, ["other stuff"]]

    if project[0]["image"]:
        print(get_image_path(project[0]['image'])) # Prints https://example.com/img/image.jpg which is a usable path
    ```
    """

    if path.startswith("http"):
        return path

    elif path.startswith("images"):
        return f"{path}"

    else:
        return f"images/{path}"
def inject_filters(env: jinja2.environment.Environment, extra_filters: List[Callable] = []) ‑> jinja2.environment.Environment

Takes in a jinja environment and injects the filters from this module + functions fom the extra_filters parameter

Parameters

env : jinja2.Environment
The existing environment you want to inject the filters into
extra_filters : List[Callable], optional
An optional set of method objects containing additional filter functions you want to use

Returns

jinja2.Environment
The input environment with the filters injected
Expand source code
def inject_filters(env:jinja2.Environment, extra_filters:List[Callable] = []) -> jinja2.Environment:
    """Takes in a jinja environment and injects the filters from this module + functions fom the extra_filters parameter

    Parameters
    ----------
    env : jinja2.Environment
        The existing environment you want to inject the filters into

    extra_filters : List[Callable], optional
        An optional set of method objects containing additional filter functions you want to use

    Returns
    -------
    jinja2.Environment
        The input environment with the filters injected
    """
    filters = [split_to_sublists, get_image_path, get_filename_without_extension, pretty_datetime, pretty_defaultdict]

    if extra_filters:
        for filter in extra_filters:
            filters.append(filter)

    for filter in filters:
        env.filters[filter.__name__] = filter

    return env
def pretty_datetime(month_started: str, year_started: str, month_ended: str, year_ended: str, current: bool) ‑> str

A utility function for pretty printing dates provided for jobs/getting a degree/volunteering etc

Parameters

month_started : str
The month started i.e. October
year_started : str
The year started i.e. 2013
month_ended : str
The month ended i.e. December
year_ended : str
The year ended i.e. 2017
current : bool
A boolean describing if this is somewhere you are currently working/studying/volunteering at

Returns

str
A pretty string of the date i.e. 'October 2013 - December 2017'

Examples

Printing the date details of a degree in the education section:

JINJA USAGE

{% for experience in education %}
    {{ experience[0]["month_started"] | pretty_datetime(experience[0]["year_started"], experience[0]["month_ended"], experience[0]["year_ended"], experience[0]["current"]) }}
{%endfor%}

The above jinja is roughly equivalent to something like this in pure python:


month_started = "October"
year_started = "2013"

month_ended = "December"
year_ended = "2017 

current = False

print(pretty_datetime(month_started, year_started, month_ended, year_ended, current)) # October 2013 - December 2017
Expand source code
def pretty_datetime(month_started:str, year_started:str, month_ended:str, year_ended:str, current:bool) -> str:
    """A utility function for pretty printing dates provided for jobs/getting a degree/volunteering etc

    Parameters
    ----------
    month_started : str
        The month started i.e. October

    year_started : str
        The year started i.e. 2013

    month_ended : str
        The month ended i.e. December

    year_ended : str
        The year ended i.e. 2017

    current : bool
        A boolean describing if this is somewhere you are currently working/studying/volunteering at

    Returns
    -------
    str
        A pretty string of the date i.e. 'October 2013 - December 2017'

    Examples
    --------
    ### Printing the date details of a degree in the `education` section:

    #### JINJA USAGE
    ```jinja2
    {% for experience in education %}
        {{ experience[0]["month_started"] | pretty_datetime(experience[0]["year_started"], experience[0]["month_ended"], experience[0]["year_ended"], experience[0]["current"]) }}
    {%endfor%}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python

    month_started = "October"
    year_started = "2013"

    month_ended = "December"
    year_ended = "2017 

    current = False

    print(pretty_datetime(month_started, year_started, month_ended, year_ended, current)) # October 2013 - December 2017
    ```
    """

    
    if month_started or year_started:
        if month_started and year_started:
            beginning = f"{month_started} {year_started}"
        elif month_started:
            beginning = f"{month_started}"
        elif year_started:
            beginning = f"{year_started}"
        sep = " - "
    else:
        sep = ""

    if current:
        end = "Present"
    
    elif month_ended or year_ended:
        if month_ended and year_ended:
            end = f"{month_ended} {year_ended}"
        elif month_ended:
            end = f"{month_ended}"
        elif year_ended:
            end = f"{year_ended}"
    else:
        end = ""

    return f"{beginning}{sep}{end}"
def pretty_defaultdict(ugly_dict: DefaultDict) ‑> str

Returns a prettyprinted form of a defaultdict

Parameters

ugly_dict : DefaultDict
A defaultdictionary to pretty print

Notes

Needs to be used with the safe filter to work properly

Returns

str
The

Examples

Pretty printing the config defaultdictionary:

JINJA USAGE

{{ config | pretty_defaultdict | safe }}

The above jinja is roughly equivalent to something like this in pure python:

from ezcv.core import get_site_config

config = get_site_config()

print(pretty_defaultdict(config)) # Prints config dict in pretty form
Expand source code
def pretty_defaultdict(ugly_dict:DefaultDict) -> str:
    """Returns a prettyprinted form of a defaultdict

    Parameters
    ----------
    ugly_dict : DefaultDict
        A defaultdictionary to pretty print

    Notes
    -----
    Needs to be used with the safe filter to work properly

    Returns
    -------
    str
        The 

    Examples
    --------
    ### Pretty printing the `config` defaultdictionary:

    #### JINJA USAGE
    ```jinja2
    {{ config | pretty_defaultdict | safe }}
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    from ezcv.core import get_site_config

    config = get_site_config()

    print(pretty_defaultdict(config)) # Prints config dict in pretty form
    ```
    """
    return pformat(dict(ugly_dict)).replace("\n", "<br>").replace("{", "{<br>").replace("}", "<br>    }")
def split_to_sublists(initial_list: list, n: int, strict: bool = True) ‑> List[list]

Takes a list and splits it into sublists of size n

Parameters

initial_list : list
The initial list to split into sublists
n : int
The size of each sublist
strict : bool
Whether to force an error if the length of the initial list is not divisible by n (split into even groups), default True

Returns

List[list]
A list of lists of size n (unless strict is False, then the last list may be > n)

Examples

JINJA USAGE

{% if gallery|length % 3 == 0 %}
{% for sublist in gallery|split_to_sublists(3) %}
    <div class="row">

    <div class="col-md-4">
        <img src="{{ sublist.0[0]['file_path'] }}" alt="{{ sublist.0[0]['file_path'].split()[-1] }}">
    </div>

    <div class="col-md-4">
        <img src="{{ sublist.1[0]['file_path'] }}" alt="{{ sublist.1[0]['file_path'].split()[-1]}}">
    </div>

    <div class="col-md-4">
        <img src="{{ sublist.2[0]['file_path'] }}" alt="{{ sublist.2[0]['file_path'].split()[-1] }}">
    </div>

    </div>
{% endfor %}
{% endif }

The above jinja is roughly equivalent to something like this in pure python:

gallery = ["image 1" , "image 2", "image 3", "image 4" , "image 5", "image 6"]

if len(images) % 3 == 0:
    for sublist in split_to_sublists(gallery, 3): # Returns [["image 1" , "image 2", "image 3"], ["image 4" , "image 5", "image 6"]]
    ... # Do stuff with each sublist
Expand source code
def split_to_sublists(initial_list:list, n:int, strict:bool=True) -> List[list]:
    """Takes a list and splits it into sublists of size n

    Parameters
    ----------
    initial_list : list
        The initial list to split into sublists

    n : int
        The size of each sublist

    strict: bool
        Whether to force an error if the length of the initial list is not divisible by n (split into even groups), default True

    Returns
    -------
    List[list]
        A list of lists of size n (unless strict is False, then the last list may be > n)

    Examples
    --------

    ### Split gallery images into sublists of 3

    #### JINJA USAGE
    ```jinja2
    {% if gallery|length % 3 == 0 %}
    {% for sublist in gallery|split_to_sublists(3) %}
        <div class="row">

        <div class="col-md-4">
            <img src="{{ sublist.0[0]['file_path'] }}" alt="{{ sublist.0[0]['file_path'].split()[-1] }}">
        </div>

        <div class="col-md-4">
            <img src="{{ sublist.1[0]['file_path'] }}" alt="{{ sublist.1[0]['file_path'].split()[-1]}}">
        </div>

        <div class="col-md-4">
            <img src="{{ sublist.2[0]['file_path'] }}" alt="{{ sublist.2[0]['file_path'].split()[-1] }}">
        </div>

        </div>
    {% endfor %}
    {% endif }
    ```

    The above jinja is roughly equivalent to something like this in pure python:

    ```python
    gallery = ["image 1" , "image 2", "image 3", "image 4" , "image 5", "image 6"]

    if len(images) % 3 == 0:
        for sublist in split_to_sublists(gallery, 3): # Returns [["image 1" , "image 2", "image 3"], ["image 4" , "image 5", "image 6"]]
        ... # Do stuff with each sublist
    ```
    """
    if strict:
        if not len(initial_list) % n == 0:
            raise ValueError(f"Provided list was not of correct size: \n\tList: {initial_list}\n\tSegment size {n}")

    result = []

    for i in range(0, len(initial_list), n): # Create sublists up to size n 
        result.append( initial_list[i:i + n])

    return result