jinja2: replace a variable based on the value of a previous variable - python

I am using jinja2 to dynamically create a latex document. I am new to jinja2 and its syntax. I am using this jinja env in a python file:
latex_jinja_env = jinja2.Environment(
block_start_string = '((*',
block_end_string = '*))',
variable_start_string = '(((',
variable_end_string = ')))',
comment_start_string = '((=',
comment_end_string = '=))',
loader = jinja2.FileSystemLoader(os.path.abspath('.'))
)
if __name__ == '__main__':
template = latex_jinja_env.get_template('template.tex') # pass in the template file
render_template = template.render(blocks = blocks, **options) # pass in all jinja vars as a big dictionary
print render_template
In the template.tex file I have this situation:
((( var1 ))) ( ((( var2 ))) ) other text...
var1 can get replaced by a string that can be either big or small, var2 with an integer. I would like that var2 gets replaced only when var1 gets the value small. How can translate that into jinja2? thanks

Just a simple if statement (with the syntax defined in the latex_jinja_env var ) does the job:
((( var1 )))
((* if block13_var1 == "small" *))
( ((( var2 ))) )
((* endif *))

Related

django custom template filter with variable as argument

I'm using a Django custom template filter as described here to access entries in a 3D array in my template. I pass the 3D array swipeData from views.py to index.html.
index.html:
function getNearestHalfHourTimeString() {
var now = new Date();
var hour = now.getHours();
var minutes = now.getMinutes();
var ampm = "AM";
if (minutes < 15) {
minutes = "00";
} else if (minutes < 45){
minutes = "30";
} else {
minutes = "00";
++hour;
}
return("T" + hour + minutes);
}
var currentDay = (new Date()).getDay();
var currentTimeRounded = getNearestHalfHourTimeString();
{% block content %}
{% load hello_extras %}
var atrium = {{swipeData|bldIndex:'ATRIUMCAFE'|dayIndex:currentDay|timeIndex:currentTimeRounded }};
{% endblock %}
hello_extras.py:
from django import template
register = template.Library()
#register.filter
def bldIndex(List, strIndex):
dctBuild = {'ATRIUMCAFE':0, 'BUTLERCOLLEGE':1, 'CAFEVIVIAN':2, 'CENTERFORJEWISHLIFE':3, 'CHANCELLORGREEN':4, 'CHEMISTRYCAFE':5, 'CONCESSIONS_12':6, 'FORBESCOLLEGE':7, 'FRISTCSTORE':8, 'FRISTGALLERY1':9, 'FRISTGALLERY2':10, 'FRISTGALLERY3':11, 'FRISTGALLERY4':12, 'FRISTWITHERSPOONS':13, 'GRADUATECOLLEGE':14, 'LIBRARY_CART':15, 'MATHEYCOLLEGE':16, 'ROCKEFELLERCOLLEGE':17, 'STUDIO34BUTLEREMPORIUM':18, 'WHITMANCOLLEGE':19, 'WILSONCOLLEGE':20, 'WOODROWWILSONCAFE':21, 'FIRESTONELIBRARY':22}
i = int(dctBuild.get(strIndex))
return List[i]
#register.filter
def dayIndex(List, strIndex):
return List[int(strIndex)]
#register.filter
def timeIndex(List, strIndex):
dctTime = { "T0000":0, "T0030":1, "T0100":2, "T0130":3, "T0200":4, "T0230":5, "T0300":6, "T0330":7, "T0400":8, "T0430":9, "T0500":10, "T0530":11, "T0600":12, "T0630":13, "T0700":14, "T0730":15, "T0800":16, "T0830":17, "T0900":18, "T0930":19, "T1000":20, "T1030":21, "T1100":22, "T1130":23, "T1200":24, "T1230":25, "T1300":26, "T1330":27, "T1400":28, "T1430":29, "T1500":30, "T1530":31, "T1600":32, "T1630":33, "T1700":34, "T1730":35, "T1800":36, "T1830":37, "T1900":38, "T1930":39, "T2000":40, "T2030":41, "T2100":42, "T2130":43, "T2200":44, "T2230":45, "T2300":46, "T2330":47}
i = int(dctTime.get(strIndex))
return List[i]
I get the error
VariableDoesNotExist at /
Failed lookup for key [currentDay] in u"[{
How can I use a variable as an argument to my custom template filter? I've tried using a with block already.
Your problem is that you are trying to pass a JS var to a django filter, that it's wrong
currentDay and currentTimeRounded are JS Vars not Python vars, for this reason filter fails
Remember that you can get all necessary info in your view, and then render in your template
I suggest you that format your data in your view and in the template assign it to the respective JS var
some as follow
def some_view(request):
# here you can calculate, convert, create all values to your chart as you need
# if you need time or date can use datetime module ie
values_to_chart = ""
render(request, template_name, locals())
And in your template
var atrium = {{values_to_chart}}

Bold text with asterisks

In my Django project I want to make text bold if asterisks * are there at the start and end of text, the same feature we have here on Stack Overflow. Although I convert ** to <b>, due to output escaping it becomes <b>. What is the right approach to achieve this?
template file contains {{ anidea.description|format_text}}
format_text is custom template filter
code..
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
#register.filter(name='format_text')
def custom_formating(value):
for word in value.split():
start = word[:2]
end = word[-2:]
if start == '**' and end == '**':
word = word[2:-2]
word = '<b>' +word+ '</b>'
mark_safe(word)
return value
if you want the full suite of all markdown features, go with an existing markdown library.
if you just want <b> to print directly to the source code w/o escaping, use
{{ some_var|safe }}
I did it in following way.
views.py
i.description = i.description.split() #use of split()
template file (format_text is custom template filter)
{% for text in anidea.description %}
{{ text|format_text }}
{% endfor %}
filter
#register.filter(name='format_text')
def custom_formating(value):
start = value[:2]
end = value[-2:]
if start == '**' and end == '**':
value = value[2:-2]
value = '<b>' +value+ '</b>'
return mark_safe(value)
else:
return value
with this way I can achieve output escaping for description and desired text formatting.

How to use jinja2 i18n with bottle (with babel)

I believe to use jinja2 with bottle one simply uses jinja2_template instead of template:
e.g. bottle.jinja2_template("mytemplate", dict(name=value, name2=value2))
However if one needs the i18n jinja extension how is that best specified to also do
....install_gettext_translations(
? Is that done automatically with
bottle.jinja2_template("mytemplate", dict(name=value, name2=value2), template_lookup=['templates'],'template_settings'= {'extensions':['jinja2.ext.i18n'],'autoescape': True }))
? Thanks.
Upon further reflection, I think I may need to overide the prepare method in class Jinja2Template to add the env.install_gettext_translations( ???
More info ,if I were doing ....install_gettext_translations( manually, perhaps:
tenv = Environment(extensions=['jinja2.ext.i18n'])
tenv.install_gettext_translations(gettransobj())
import gettext
import locale
def gettransobj():
loc = locale.getlocale()
# change to reflect where your mo files are
mofilename = "res/messages_%s.mo" % locale.getdefaultlocale()[0][0:2]
try:
trans = gettext.GNUTranslations(open( mofilename, "rb" ) )
except IOError:
trans = gettext.NullTranslations()
return trans
OR for babel translations obj something like
.....install_gettext_translations(gettransobj(),newstyle=True)
import babel
import locale
def gettransobj():
loc = locale.getlocale()
mofilename = "res/messages_%s.mo" % locale.getdefaultlocale()[0][0:2]
trans = babel.support.Translations(open( mofilename, "rb" ) )
If this code is somewhat correct, not sure where to put it? Not very familiar with jinja2. Just once at top of program or per bottle.jinja2_template call.
On a different note, if someone needs to do extraction using babel, see jinja2.ext.babel_extract
Another approach is getting trans obj with something like:
return gettext.translation(domain, localedir=localedir,languages=languages, codeset='utf-8')
from jinja2 import FileSystemBytecodeCache, Environment
bcc = FileSystemBytecodeCache('/tmp', '%s.cache')
template_settings = {'filters': {
'tojson': json_util.dumps
},
'bytecode_cache': bcc,
'extensions': ['jinja2.ext.i18n'],
'languages': ['en_US']
}
import bottle
from bottle import Jinja2Template
from babel.support import Translations
TEMPLATE_PATH = bottle.TEMPLATE_PATH
DEBUG = bottle.DEBUG
TEMPLATES = {}
class Jinja2BabelTemplate(Jinja2Template):
def prepare(self, filters=None, tests=None,
languages=['en_US'],
globals={}, **kwargs):
from jinja2 import Environment, FunctionLoader
self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
#BABEL: this is where we load and install our translations from babel
translations = Translations.load('translations', languages)
self.env.install_gettext_translations(translations)
if filters: self.env.filters.update(filters)
if tests: self.env.tests.update(tests)
if globals: self.env.globals.update(globals)
if self.source:
self.tpl = self.env.from_string(self.source)
else:
self.tpl = self.env.get_template(self.filename)
def jinja_template(*args, **kwargs):
tpl = args[0] if args else None
adapter = Jinja2BabelTemplate
lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
languages = kwargs.pop('languages', None)
tplid = (id(languages), tpl,)
if tplid not in TEMPLATES or DEBUG:
settings = kwargs.pop('template_settings', {})
if languages: settings.update({'languages': languages})
if isinstance(tpl, adapter):
TEMPLATES[tplid] = tpl
if settings: TEMPLATES[tplid].prepare(**settings)
elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)
else:
TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings)
if not TEMPLATES[tplid]:
abort(500, 'Template (%s) not found' % tpl)
for dictarg in args[1:]: kwargs.update(dictarg)
return TEMPLATES[tplid].render(kwargs)
template = functools.partial(jinja_template,
template_settings=template_settings)
#and now you should be able to use the template function above with different languages
#get('/home')
def home():
data = {}
return template('home.html', data, languages=['en_US'])

Multiple renders of jinja2 templates?

Is there any way to do this with jinja2?
template = Template("{{ var1 }}{{ var2 }}")
rendered1 = template.render(var1=5) # "5-{{ var2 }}"
rendered2 = Template(rendered1).render(var2=6) # "5-6"
basically, I want to be able to do multiple passes on a template. When the template engine finds a variable in the template that is not in the context, instead of replacing it with nothing, keep the template variable intact? If not jinja2, is there any other python template library that can do this?
You can use DebugUndefined, which keeps the failed lookups, as your Undefined Type for the undefined parameter of the Template environment:
>>> from jinja2 import Template, DebugUndefined
>>> template = Template("{{ var1 }}-{{ var2 }}", undefined=DebugUndefined)
>>> rendered1 = template.render(var1=5) # "5-{{ var2 }}"
>>> print(rendered1)
5-{{ var2 }}
>>> rendered2 = Template(rendered1).render(var2=6) # "5-6"
>>> print(rendered2)
5-6

Jinja2 filter to convert custom markup to html

Having the autoescape property on (I want to keep it that way), I want user to be able to enter some custom markup, to have the opportunity to format text. For example, [s][/s] will be translated into <strong></strong>. I believe the right way to do this is to write the custom Jinja2 filter. But the following doesn't work:
#app.template_filter()
#evalcontextfilter
def mark2html(eval_ctx, value):
result = escape(value).replace('[s]','<strong>')
if eval_ctx.autoescape:
result = Markup(result)
return result
When applied to text like
<div>{{ custom_markup_text|mark2html }}</div>
When [s] is encountered in the string, stored in custom_markup_text, it should be converted to <strong> tag. AFAIK, Markup() function ensures that we trust this particular string, so that HTML is not escaped there. The filter is successfully applied, [s] is replaced by <strong>, but it's still escaped.
Obviously, the autoescaping is done after this custom filter. On the other hand, example filter from Jinja2 documentation works perfectly:
#app.template_filter()
#evalcontextfilter
def nl2br(eval_ctx, value):
result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n') \
for p in _paragraph_re.split(escape(value)))
if eval_ctx.autoescape:
result = Markup(result)
return result
What am I doing wrong?
Problem found. It's double escaping the string - rather silly.
This code works flawlessly:
#app.template_filter()
#evalcontextfilter
def mark2html(eval_ctx, value):
result = value.replace('[s]',u'<strong>')
result = result.replace('[/s]',u'</strong>')
if eval_ctx.autoescape:
result = Markup(result)
return result
Note, value shouldn't be escaped, as autoescape property is on.

Categories