Skip to content

Commit

Permalink
Merge pull request #33 from meaningfy-ws/feature/lam-93
Browse files Browse the repository at this point in the history
latex template flavour
  • Loading branch information
costezki authored Jan 5, 2021
2 parents 8b993f9 + 6fe7bb6 commit 82729a2
Show file tree
Hide file tree
Showing 27 changed files with 346 additions and 3,343 deletions.
60 changes: 59 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ The command to run the report generation is `mkreport`
The command line interface has three arguments:
########################################################################################################

* **--target** (mandatory): the directory where eds4jinja2 will load the content from, for rendering a template; this directory has a mandatory layout (see below)
* **--target** (optional): the directory where eds4jinja2 will load the content from, for rendering a template; this directory has a mandatory layout (see below)
* **--output** (optional): the directory where eds4jinja2 will place the rendered output; if this parameter is not specified, it will be placed in an "output" subdirectory that will be created where the "--target" parameter points to
* **--config** (optional): the name of the configuration file from where eds4jinja2 will load the configuration that's passed to Jinja2; default "config.json"

Expand Down Expand Up @@ -156,15 +156,73 @@ Example:
{
"template": "main.html",
"template_flavour_syntax": "html",
"conf":
{
"default_endpoint" : "http://example.com/path/sparqlendpoint",
"title": "Pretty printed relevant information",
"type": "report",
"author": "Your name here"
"nexted_properties": {
"graph": "http://publications.europa.eu/resources/authority/lam/DocumentProperty"
},
}
}
Latex templates:
########################################################################################################

It is possible to write templates with LaTex documents.
To do so, first make sure you have specified the template flavour in the config file

.. code-block:: json
{
"template": "main.tex",
"template_flavour_syntax": "latex",
"conf":
{
"title": "Pretty printed relevant information",
...
Next write your templates using the following conventions:
* Blocks: ``\BLOCK{block block_name}\BLOCK{endblock}``
* Line statement: ``%- line instruction``
* Variables: ``\VAR{variable_name}``
* Comments (long form): ``\#{This is a long-form Jinja comment}``
* Comments (short form): ``%# This is a short-form Jinja comment``


An example ``jinja-test.tex`` is available below:

.. code-block:: latex

\documentclass{article}
\begin{document}
\section{Example}
An example document using \LaTeX, Python, and Jinja.

% This is a regular LaTeX comment
\section{\VAR{section1}}
\#{This is a long-form Jinja comment}
\begin{itemize}
\BLOCK{ for x in range(0, 3) }
\item Counting: \VAR{x}
\BLOCK{ endfor }
\end{itemize}

\section{\VAR{section2}}
%# This is a short-form Jinja comment
\begin{itemize}
%- for x in range(0, 3)
\item Counting: \VAR{x}
%- endfor
\end{itemize}

\end{document}


Indices and tables
===================================
Expand Down
8 changes: 8 additions & 0 deletions docs/srcdocs/eds4jinja2.adapters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ eds4jinja2.adapters.http\_ds module
:undoc-members:
:show-inheritance:

eds4jinja2.adapters.latex\_utils module
---------------------------------------

.. automodule:: eds4jinja2.adapters.latex_utils
:members:
:undoc-members:
:show-inheritance:

eds4jinja2.adapters.local\_sparql\_ds module
--------------------------------------------

Expand Down
4 changes: 2 additions & 2 deletions eds4jinja2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
__docformat__ = "restructuredtext en"

# The format of the __version__ line is matched by a regex in setup.py and /docs/conf.py
__version__ = "0.1.28"
__date__ = "2021-01-04"
__version__ = "0.1.29"
__date__ = "2021-01-05"

import logging

Expand Down
60 changes: 60 additions & 0 deletions eds4jinja2/adapters/latex_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/python3

# latex_utils.py
# Date: 05/01/2021
# Author: Eugeniu Costetchi
# Email: costezki.eugen@gmail.com

"""
Latex parsing functionality
This module provides functions to parse latex text
Credits: This module was adapted after the Samuel Roeca's latex repository (pappasam/latexbuild on github)
"""

import re

# Organize all latex escape characters in one list
# (EXCEPT FOR ( "\" ), which is handled separately)
# escaping those which are special characters in
# PERL regular expressions
ESCAPE_CHARS = [r'\&', '%', r'\$', '#', '_', r'\{', r'\}', '~', r'\^', ]

# For each latex escape character, create a regular expression
# that matches all of the following criteria
# 1) one or two characters
# 2) if two characters, the first character is NOT a backslash ( "\" )
# 3) if two characters, the second, if one, the first character
# is one of the latex escape characters
REGEX_ESCAPE_CHARS = [
(re.compile(r"(?<!\\)" + i), r"\\" + i.replace('\\', ''))
for i in ESCAPE_CHARS
]

# Place escape characters in [] for "match any character" regex
ESCAPE_CHARS_OR = r'[{}\\]'.format(''.join(ESCAPE_CHARS))

# For the back slash, create a regular expression
# that matches all of the following criteria
# 1) one, two, or three characters
# 2) the first character is not a backslash
# 3) the second character is a backslash
# 4) the third character is none of the ESCAPE_CHARS,
# and is also not a backslash
REGEX_BACKSLASH = re.compile(r'(?<!\\)\\(?!{})'.format(ESCAPE_CHARS_OR))


def escape_latex(value: str):
"""
Escape a latex string
:param value:
:return:
"""

if not isinstance(value, str):
return value
for regex, replace_text in REGEX_ESCAPE_CHARS:
value = re.sub(regex, replace_text, value)
value = re.sub(REGEX_BACKSLASH, r'\\\\', value)
return value
33 changes: 29 additions & 4 deletions eds4jinja2/builders/jinja_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import jinja2

from eds4jinja2.adapters.latex_utils import escape_latex
from eds4jinja2.adapters.local_sparql_ds import RDFFileDataSource
from eds4jinja2.adapters.tabular_utils import invert_dict, replace_strings_in_tabular, add_relative_figures
from eds4jinja2.adapters.file_ds import FileDataSource
Expand All @@ -30,24 +31,48 @@
percentage),
}

ADDITIONAL_VARIABLE_FILTERS = {
"escape_latex": lambda value: escape_latex(value),
}


def build_eds_environment(external_data_source_builders={**DATA_SOURCE_BUILDERS, **TABULAR_HELPERS}, **kwargs):
def build_eds_environment(external_data_source_builders={**DATA_SOURCE_BUILDERS, **TABULAR_HELPERS},
external_filters=ADDITIONAL_VARIABLE_FILTERS, **kwargs):
"""
creates a JINJA environment and injects the global context with EDS functions
:param external_data_source_builders:
:param external_filters: additional filters to be make available in the templates
:param external_data_source_builders: additional instructions to be made available in the templates
:param kwargs:
:return:
"""
template_env = jinja2.Environment(**kwargs)
inject_environment_globals(template_env, external_data_source_builders)
inject_environment_filters(template_env, external_filters)
return template_env


def inject_environment_filters(jinja_environment: jinja2.Environment, filters: dict, update_existent=True):
"""
Inject the filters into JINJA2 environment making them globally available from any template.
Updates in place the `filter` environment dictionary by adding non existent keys from another dictionary.
If the dictionary keys exist then they are replaced depending whether the update_existent flag is set.
:param update_existent: whether the overlapping values shall be overwritten
:param filters: additional filters to be injected
:param jinja_environment: JINJA environment to be updated
:return:
"""
if update_existent:
jinja_environment.filters.update(filters)
else:
jinja_environment.filters.update(
{k: v for k, v in filters.items() if k not in jinja_environment.filters.keys()})


def inject_environment_globals(jinja_environment: jinja2.Environment, context: dict, update_existent=True):
"""
Inject the context into JINJA2 environment making it globally available from any template.
Update in place a dictionary by adding non existent keys from another dictionary.
If the dictionary keys exist then they are replaced depending whether the update_existent flag is set.
Updates in place the `global` environment dictionary by adding non existent keys from another dictionary.
If the keys exist then they are replaced depending whether the update_existent flag is set.
:param update_existent: whether the overlapping values shall be overwritten
:param context: additional context to be injected
:param jinja_environment: JINJA environment to be updated
Expand Down
58 changes: 51 additions & 7 deletions eds4jinja2/builders/report_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,52 @@
"""
import json
import pathlib
from typing import Union

import jinja2

from eds4jinja2.builders import deep_update
from eds4jinja2.builders.jinja_builder import build_eds_environment, inject_environment_globals

# aims to be close to the J2 default env syntax definition, but explicitly specified
HTML_TEMPLATE_SYNTAX = {'block_start_string': '{%',
'block_end_string': '%}',
'variable_start_string': '{{',
'variable_end_string': '}}',
'comment_start_string': '{#',
'comment_end_string': '#}',
'line_statement_prefix': '#',
'line_comment_prefix': '##',
'trim_blocks': False, }

# do not change anything
DEFAULT_TEMPLATE_SYNTAX = {}

######################################################################
# Jinja2 Environment ARGS
# Constant borrowed from Marc Brinkmann's latex repository (mbr/latex on github)
# Reused also by Samuel Roeca's in his latex repository (pappasam/latexbuild on github)
######################################################################
LATEX_TEMPLATE_SYNTAX = {'block_start_string': r'\BLOCK{',
'block_end_string': '}',
'variable_start_string': r'\VAR{',
'variable_end_string': '}',
'comment_start_string': r'\#{',
'comment_end_string': '}',
'line_statement_prefix': '%-',
'line_comment_prefix': '%#',
'trim_blocks': True,
'autoescape': False, }


class ReportBuilder:
"""
generic report builder that takes templates and configuration as input and produces an output report
"""
configuration_context: dict = {}

def __init__(self, target_path, config_file="config.json",
output_path=None, additional_config: dict = {}, ):
def __init__(self, target_path: Union[str, pathlib.Path], config_file: str = "config.json",
output_path: str = None, additional_config: dict = {}):
"""
Instantiates builders form a template providing an optional configuration context.
Expand All @@ -36,6 +68,7 @@ def __init__(self, target_path, config_file="config.json",

with open(pathlib.Path(target_path) / config_file, encoding='utf-8') as configFile:
self.configuration_context = json.loads(configFile.read())
deep_update(self.configuration_context, additional_config)

template_path = str(pathlib.Path(target_path) / "templates")
static_folder = pathlib.Path(target_path) / "static"
Expand All @@ -50,16 +83,27 @@ def __init__(self, target_path, config_file="config.json",
self.configuration_context["template_path"] = template_path
self.configuration_context["static_folder"] = str(static_folder)
self.configuration_context["output_folder"] = str(output_folder)

deep_update(self.configuration_context, additional_config)
self.configuration_context["conf"]["template_path"] = template_path
self.template = self.configuration_context["template"]
self.configuration_context = self.configuration_context

# Provides a configuration of the templating syntax flavour. By default it is configured
# for HTML but could be adapted as necessary. For LaTex, LATEX_TEMPLATE_SYNTAX is available.
# TODO: this code might need to be externalised in the next refactoring
if "template_flavour_syntax" in self.configuration_context:
if str.lower(self.configuration_context["template_flavour_syntax"]) in ("latex", "tex"):
self.template_flavour_syntax_spec = LATEX_TEMPLATE_SYNTAX
elif str.lower(self.configuration_context["template_flavour_syntax"]) in ("xml", "html", "xhtml"):
self.template_flavour_syntax_spec = HTML_TEMPLATE_SYNTAX
else:
self.template_flavour_syntax_spec = DEFAULT_TEMPLATE_SYNTAX
else:
self.template_flavour_syntax_spec = DEFAULT_TEMPLATE_SYNTAX

template_loader = jinja2.FileSystemLoader(searchpath=template_path)
self.template_env = build_eds_environment(loader=template_loader)
self.configuration_context["conf"]["template_path"] = template_path
self.template_env = build_eds_environment(loader=template_loader, **self.template_flavour_syntax_spec)
inject_environment_globals(self.template_env, {'conf': self.configuration_context["conf"]},
False if self.configuration_context is None else True)

self.__before_rendering_listeners = []
self.__after_rendering_listeners = []

Expand Down

This file was deleted.

Binary file not shown.
Loading

0 comments on commit 82729a2

Please sign in to comment.