Files
lektor-recipes/src/packages/helper/lektor_helper.py
2019-11-25 18:26:24 +01:00

220 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
from lektor.pluginsystem import Plugin, get_plugin
from lektor.databags import Databags
from markupsafe import Markup
from datetime import datetime
import unicodedata
# -------
# Sorting
def sortKeyInt(x):
return int(x[0]) if x[0] else 0
def sortKeyStr(x):
return noUmlaut(x[0]).lower()
def groupByDictSort(dic, sorter=None, reverse=False):
if type(sorter) == list: # sort by pre-defined, ordered list
return sorted(dic, reverse=bool(reverse), key=lambda x:
sorter.index(x[0]) if x[0] in sorter else 0)
fn = sortKeyInt if sorter == 'int' else sortKeyStr
return sorted(dic, reverse=bool(reverse), key=fn)
# -----------------------
# Pure text manupulations
def noUmlaut(text):
try:
text = unicode(text, 'utf-8')
except (TypeError, NameError):
pass
text = unicodedata.normalize('NFD', text)
text = text.encode('ascii', 'ignore')
text = text.decode("utf-8")
return str(text)
def replaceFractions(txt):
res = ''
for x in txt.split():
try:
i = ['1/2', '1/3', '2/3', '1/4', '3/4', '1/8'].index(x)
res += [u'½', u'', u'', u'¼', u'¾', u''][i]
except ValueError:
if x in u'-–—':
res += u' - '
else:
res += ' ' + x
return res.lstrip()
def numFillWithText(num, fill=u'', empty=u'', total=5):
num = int(num) if num else 0
return fill * num + empty * (total - num)
# ------------------
# Array manipulation
def updateSet_if(dic, parent, parentkey, value):
try:
key = parent[parentkey]
except KeyError:
return
if not key:
key = ''
try:
dic[key]
except KeyError:
dic[key] = set()
dic[key].add(value)
# --------------------
# Ingredient splitting
def splitIngredientLine(line):
state = 1
capture = False
indices = [0, len(line)]
for i, char in enumerate(line):
if char.isspace():
if capture:
capture = False
indices[state] = i
state += 1
continue
elif capture:
continue
elif state == 1 and char in u'0123456789-–—.,':
state -= 1
elif state > 1:
break
capture = True
return indices
def parseIngredientLine(line, measureList=[], rep_frac=False):
idx = splitIngredientLine(line)
val = line[:idx[0]]
if rep_frac:
val = replaceFractions(val)
measure = line[idx[0]:idx[1]].lstrip()
if measure.lower() in measureList:
name = line[idx[1]:].lstrip()
# if name.startswith('of '):
# measure += ' of'
# name = name[3:]
else:
measure = ''
name = line[idx[0]:].lstrip()
note = ''
name_note = name.split(',', 1)
if len(name_note) > 1:
name, note = [x.strip() for x in name_note]
return {'value': val, 'measure': measure, 'name': name, 'note': note}
def replace_atref_urls(text, label=None):
if '@' not in text:
return text
result = list()
for x in text.split():
if x[0] == '@':
x = x[1:]
result.append(u'<a href="{}">{}</a>'.format(x, label or x))
else:
result.append(x)
return Markup(' '.join(result))
# ----------------
# Main entry point
class HelperPlugin(Plugin):
name = u'Helper'
description = u'Some helper methods, filters, and templates.'
buildTime = None
settings = dict()
translations = dict()
# -----------
# Event hooks
# -----------
def processCLI(self, extra_flags):
useCache = bool(extra_flags.get('ENABLE_APPCACHE'))
plugin = get_plugin('force-update', self.env)
if plugin.enabled and not useCache:
plugin.enabled = False
print('AppCache: ' + ('ENABLED' if useCache else 'DISABLED'))
self.env.jinja_env.globals['ENABLE_APPCACHE'] = useCache
def processSettings(self):
bag = Databags(self.env)
pad = self.env.new_pad()
for alt in self.env.load_config().iter_alternatives():
set = pad.get('settings', alt=alt)
self.translations[alt] = bag.lookup('i18n+' + alt)
self.settings[alt] = {
'measures': set['measures'].lower().split(),
'replFrac': set['replace_frac']
}
def on_before_build_all(self, builder, **extra):
build_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print('Build time: ' + build_time)
self.env.jinja_env.globals['DATE_NOW'] = build_time
# update project settings once per build
self.processCLI(getattr(builder, 'extra_flags'))
self.processSettings()
# def on_process_template_context(self, context, **extra):
# pass
def on_setup_env(self, **extra):
def localizeDic(alt, partA, partB=None):
if alt not in self.translations:
raise RuntimeError(
'localize() expects first parameter to be an alternate')
if partB is None:
partA, partB = partA.split('.', 1)
return self.translations[alt][partA][partB]
def ingredientsForRecipe(recipe, alt='en'):
meaList = self.settings[alt]['measures']
repFrac = self.settings[alt]['replFrac']
for line in recipe['ingredients']:
line = line.strip()
if not line:
continue
elif line.endswith(':'):
yield {'group': line}
else:
yield parseIngredientLine(line, meaList, repFrac)
def groupByAttribute(recipeList, attribute, alt='en'):
groups = dict()
for recipe in recipeList:
if attribute == 'ingredients':
for ing in ingredientsForRecipe(recipe, alt):
updateSet_if(groups, ing, 'name', recipe)
else:
updateSet_if(groups, recipe, attribute, recipe)
# groups[undefinedKey].update(groups.pop('_undefined'))
return groups.items()
self.env.jinja_env.filters['rating'] = numFillWithText
self.env.jinja_env.filters['replaceFractions'] = replaceFractions
self.env.jinja_env.filters['enumIngredients'] = ingredientsForRecipe
self.env.jinja_env.filters['replaceAtRefURLs'] = replace_atref_urls
self.env.jinja_env.filters['groupByAttribute'] = groupByAttribute
self.env.jinja_env.filters['groupSort'] = groupByDictSort
self.env.jinja_env.globals['localize'] = localizeDic