220 lines
6.6 KiB
Python
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
|