Format body of recipe

This commit is contained in:
Benjamin 2023-03-25 12:55:52 +01:00
parent 41d346d56a
commit 4461a81ae6
3 changed files with 180 additions and 2 deletions

View file

@ -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 %}
<h1>{{ version.recipe.title }}{% if has_multiple_versions %} ({{ version.label }}){% endif %}</h1>
{% if has_multiple_versions %}
<p><a href="{{ recipe.get_absolute_url }}">Show all versions</a></p>
{% endif %}
<p><a href="{% url 'edit-recipe' recipe.slug %}">Edit recipe name</a></p>
<p><a href="{% url 'add-version' recipe.slug %}">Add version</a></p>
<p><a href="{% url 'edit-version' recipe.slug version.slug %}">Edit Version</a></p>
<article>
<h1>{{ version.recipe.title }}{% if has_multiple_versions %} ({{ version.label }}){% endif %}</h1>
<ul>
{% for i in ingredients %}
<li>{{ i.text }}</li>
{% endfor %}
</ul>
<p>{{ version.body }}</p>
{% if version.body %}{{ version.body|ndash_for_ranges|nbsp_for_amounts|unify_units|vulgar_fractions|to_sections }}{% endif %}
</article>
{% if version.img %}<img src="{{ version.img.url }}" alt="">{% endif %}
{% endblock %}

View file

View file

@ -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 = "&#189;" # 1/2
elif 3 == f.denominator:
fraction_string = "&#8531;" # 1/3
elif 4 == f.denominator:
fraction_string = "&#188;" # 1/4
elif 5 == f.denominator:
fraction_string = "&#8533;" # 1/5
elif 6 == f.denominator:
fraction_string = "&#8537;" # 1/6
elif 7 == f.denominator:
fraction_string = "&#8528;" # 1/7
elif 8 == f.denominator:
fraction_string = "&#8539;" # 1/8
elif 9 == f.denominator:
fraction_string = "&#8529;" # 1/9
elif 10 == f.denominator:
fraction_string = "&#8530;" # 1/10
elif 2 == f.numerator:
if 3 == f.denominator:
fraction_string = "&#8532;" # 2/3
elif 5 == f.denominator:
fraction_string = "&#8534;" # 2/5
elif 3 == f.numerator:
if 4 == f.denominator:
fraction_string = "&#190;" # 3/4
elif 5 == f.denominator:
fraction_string = "&#8535;" # 3/5
elif 8 == f.denominator:
fraction_string = "&#8540;" # 3/8
elif 4 == f.numerator:
if 5 == f.denominator:
fraction_string = "&#8536;" # 4/5
elif 5 == f.numerator:
if 6 == f.denominator:
fraction_string = "&#8538;" # 5/6
elif 8 == f.denominator:
fraction_string = "&#8541;" # 5/8
elif 7 == f.numerator:
if 8 == f.denominator:
fraction_string = "&#8542;" # 7/8
if "" == fraction_string:
fraction_string = "%i&frasl;%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)", '&nbsp;', 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 = '&#8288;&ndash;&#8288;' # 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 += '<section>\n'
if section['title']:
sections += '<h2>%s</h2>\n' % section['title']
sections += linebreaks(section['body'], autoescape=False)
sections += '\n</section>\n'
return mark_safe(sections)