From 4461a81ae627fe6a71dea54bb2a8a5262cea5177 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sat, 25 Mar 2023 12:55:52 +0100 Subject: [PATCH] Format body of recipe --- recipes/templates/version.html | 7 +- recipes/templatetags/__init__.py | 0 recipes/templatetags/version_markup.py | 175 +++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 recipes/templatetags/__init__.py create mode 100644 recipes/templatetags/version_markup.py diff --git a/recipes/templates/version.html b/recipes/templates/version.html index 886c80d..b52c676 100644 --- a/recipes/templates/version.html +++ b/recipes/templates/version.html @@ -17,20 +17,23 @@ This program comes with a copy of the GNU Affero General Public License file at the root of this project. {% endcomment %} +{% load version_markup %} {% block title %}{{ version.recipe.title }}{% if has_multiple_versions %} ({{ version.label }}){% endif %}{% endblock %} {% block main %} -

{{ version.recipe.title }}{% if has_multiple_versions %} ({{ version.label }}){% endif %}

{% if has_multiple_versions %}

Show all versions

{% endif %}

Edit recipe name

Add version

Edit Version

+
+

{{ version.recipe.title }}{% if has_multiple_versions %} ({{ version.label }}){% endif %}

-

{{ version.body }}

+ {% if version.body %}{{ version.body|ndash_for_ranges|nbsp_for_amounts|unify_units|vulgar_fractions|to_sections }}{% endif %} +
{% if version.img %}{% endif %} {% endblock %} diff --git a/recipes/templatetags/__init__.py b/recipes/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/recipes/templatetags/version_markup.py b/recipes/templatetags/version_markup.py new file mode 100644 index 0000000..10d5e97 --- /dev/null +++ b/recipes/templatetags/version_markup.py @@ -0,0 +1,175 @@ +""" +Barn Web App - A collection of web-apps for my family's personal use, +including a recipe database. +Copyright © 2023 Benjamin Stadlbauer + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +General Public License for more details. + +This program comes with a copy of the GNU Affero General Public License +file at the root of this project. +""" + +from django import template +import re +from fractions import Fraction +from django.template.defaultfilters import stringfilter +from django.utils.html import conditional_escape, linebreaks +from django.utils.safestring import mark_safe +from django.utils.text import normalize_newlines + +register = template.Library() + +@register.filter(name='unify_units', is_safe=True, needs_autoescape=True) +@stringfilter +def unify_units_filter(value, autoescape=True): + if autoescape: + value = conditional_escape(value) + + value = re.sub(r"\b[lL](iter)?\b", "Liter", value) + value = re.sub(r"\b[eE](ss|ß)?[lL]?(öffel)?\b", "Esslöffel", value) + value = re.sub(r"\b[tT](ee)?[lL]\.?(öffel)?\b", "Teelöffel", value) + value = re.sub(r"\b[dD]a?[gG]\b", "dag", value) + value = re.sub(r"\b([kK][iI][lL][oO]|[kK](ilo)?[gG]\.?(ramm)?)\b", "kg", value) + value = re.sub(r"(\b[Gg](rad)? ?|\u00B0 ?)?[Cc](elsius)?\b", "°C", value) + value = re.sub(r"\b[Mm](in)?\.?(uten)?\b", "min", value) + value = re.sub(r"\b[sS]\.?(ek)?\.?(unden)?\b", "Sekunden", value) + + return mark_safe(value) + +@register.filter(name='vulgar_fractions', is_safe=True, needs_autoescape=True) +@stringfilter +def fractions_to_vuglar_fractions_filter(value, autoescape=True): + fraction_re = re.compile(r"(\d{1,2})( ?\/ ?)(\d{1,2})") + if autoescape: + value = conditional_escape(value) + + match = fraction_re.search(value) + + while (match): + f = Fraction(int(match.group(1)), int(match.group(3))) + fraction_string = "" + + if 1 == f.numerator: + if 2 == f.denominator: + fraction_string = "½" # 1/2 + elif 3 == f.denominator: + fraction_string = "⅓" # 1/3 + elif 4 == f.denominator: + fraction_string = "¼" # 1/4 + elif 5 == f.denominator: + fraction_string = "⅕" # 1/5 + elif 6 == f.denominator: + fraction_string = "⅙" # 1/6 + elif 7 == f.denominator: + fraction_string = "⅐" # 1/7 + elif 8 == f.denominator: + fraction_string = "⅛" # 1/8 + elif 9 == f.denominator: + fraction_string = "⅑" # 1/9 + elif 10 == f.denominator: + fraction_string = "⅒" # 1/10 + elif 2 == f.numerator: + if 3 == f.denominator: + fraction_string = "⅔" # 2/3 + elif 5 == f.denominator: + fraction_string = "⅖" # 2/5 + elif 3 == f.numerator: + if 4 == f.denominator: + fraction_string = "¾" # 3/4 + elif 5 == f.denominator: + fraction_string = "⅗" # 3/5 + elif 8 == f.denominator: + fraction_string = "⅜" # 3/8 + elif 4 == f.numerator: + if 5 == f.denominator: + fraction_string = "⅘" # 4/5 + elif 5 == f.numerator: + if 6 == f.denominator: + fraction_string = "⅚" # 5/6 + elif 8 == f.denominator: + fraction_string = "⅝" # 5/8 + elif 7 == f.numerator: + if 8 == f.denominator: + fraction_string = "⅞" # 7/8 + if "" == fraction_string: + fraction_string = "%i⁄%i" % (f.numerator, f.denominator) + + prefix = value[:match.start()] + suffix = value[match.end():] + value = prefix + fraction_string + suffix + match = fraction_re.search(value) + + return mark_safe(value) + +@register.filter(name='nbsp_for_amounts', is_safe=True, needs_autoescape=True) +@stringfilter +def nbsp_for_amounts_filter(value, autoescape=True): + if autoescape: + value = conditional_escape(value) + + return mark_safe(re.sub(r"(?<=\d) *(?=\b[^\d\W]+\b)", ' ', value)) # replaces optional spaces between digits and non-digit word characters with non-breaking spaces + +@register.filter(name='ndash_for_ranges', is_safe=True, needs_autoescape=True) +@stringfilter +def ndash_for_ranges_filter(value, autoescape=True): + if autoescape: + value = conditional_escape(value) + + en_dash = '⁠–⁠' # word joiner (U+2060) en dash (U+2013) word joiner (U+2060) + return mark_safe(re.sub(r"(?<=\d) ?-+ ?(?=\d)", en_dash, value)) # replaces dashes between digits with en-dashes (and omits optional spaces and adds word joiner) + +def line_is_section_heading(text): + return len(text) < 30 and not re.search(r"\. *$", text) + +@register.filter(name='to_sections', is_safe=True, needs_autoescape=True) +@stringfilter +def body_to_sections_filter(value, autoescape=True): + if autoescape: + value = conditional_escape(value) + + value = normalize_newlines(value) + + section_objects = [] + section_object = { + 'title': '', + 'body': '', + } + + for line in re.split('\n{2,}', value): + if line_is_section_heading(line): + if section_object['body']: + section_objects += [section_object] + + section_object = { + 'title': line, + 'body': '', + } + else: + if section_object['body']: + section_object['body'] += '\n\n' + line + else: + section_object['body'] = line + + section_objects += [section_object] + + sections = '' + + for section in section_objects: + sections += '
\n' + + if section['title']: + sections += '

%s

\n' % section['title'] + + sections += linebreaks(section['body'], autoescape=False) + sections += '\n
\n' + + + return mark_safe(sections)