Merge branch 'develop' of https://github.com/vabene1111/recipes into develop

This commit is contained in:
smilerz
2021-02-16 07:45:22 -06:00
96 changed files with 13251 additions and 10154 deletions

View File

@@ -5,26 +5,26 @@ DEBUG=0
# hosts the application can run under e.g. recipes.mydomain.com,cooking.mydomain.com,... # hosts the application can run under e.g. recipes.mydomain.com,cooking.mydomain.com,...
ALLOWED_HOSTS=* ALLOWED_HOSTS=*
# random secret key, use for example base64 /dev/urandom | head -c50 to generate one # random secret key, use for example `base64 /dev/urandom | head -c50` to generate one
SECRET_KEY= SECRET_KEY=
# your default timezone # your default timezone See https://timezonedb.com/time-zones for a list of timezones
TIMEZONE=Europe/Berlin TIMEZONE=Europe/Berlin
# add only a database password if you want to run with the default postgres, otherwise change settings accordingly # add only a database password if you want to run with the default postgres, otherwise change settings accordingly
DB_ENGINE=django.db.backends.postgresql DB_ENGINE=django.db.backends.postgresql
POSTGRES_HOST=db_recipes POSTGRES_HOST=db_recipes
POSTGRES_PORT=5432 POSTGRES_PORT=5432
POSTGRES_USER=djangodb POSTGRES_USER=djangouser
POSTGRES_PASSWORD= POSTGRES_PASSWORD=
POSTGRES_DB=djangodb POSTGRES_DB=djangodb
# the default value for the user preference 'fractions' (enable/disable fraction support) # the default value for the user preference 'fractions' (enable/disable fraction support)
# when unset: 0 (disabled) # default: disabled=0
FRACTION_PREF_DEFAULT=0 FRACTION_PREF_DEFAULT=0
# the default value for the user preference 'comments' (enable/disable commenting system) # the default value for the user preference 'comments' (enable/disable commenting system)
# when unset: 1 (true) # default comments enabled=1
COMMENT_PREF_DEFAULT=1 COMMENT_PREF_DEFAULT=1
# Users can set a amount of time after which the shopping list is refreshed when they are in viewing mode # Users can set a amount of time after which the shopping list is refreshed when they are in viewing mode
@@ -47,13 +47,13 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL=5
# when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate # when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate
GUNICORN_MEDIA=0 GUNICORN_MEDIA=0
# allow authentication via reverse proxy (e.g. authelia), leave of if you dont know what you are doing # allow authentication via reverse proxy (e.g. authelia), leave off if you dont know what you are doing
# see docs for more information https://vabene1111.github.io/recipes/features/authentication/ # see docs for more information https://vabene1111.github.io/recipes/features/authentication/
# when unset: 0 (false) # when unset: 0 (false)
REVERSE_PROXY_AUTH=0 REVERSE_PROXY_AUTH=0
# allows you to setup o auth providers # allows you to setup OAuth providers
# see docs for more information https://vabene1111.github.io/recipes/features/authentication/ # see docs for more information https://vabene1111.github.io/recipes/features/authentication/
# SOCIAL_PROVIDERS = allauth.socialaccount.providers.github, allauth.socialaccount.providers.nextcloud, # SOCIAL_PROVIDERS = allauth.socialaccount.providers.github, allauth.socialaccount.providers.nextcloud,

View File

@@ -2,9 +2,12 @@
<dictionary name="vabene1111-PC"> <dictionary name="vabene1111-PC">
<words> <words>
<w>autosync</w> <w>autosync</w>
<w>chowdown</w>
<w>csrftoken</w> <w>csrftoken</w>
<w>gunicorn</w> <w>gunicorn</w>
<w>ical</w> <w>ical</w>
<w>mealie</w>
<w>safron</w>
<w>traefik</w> <w>traefik</w>
</words> </words>
</dictionary> </dictionary>

View File

@@ -1,13 +1,27 @@
# Recipes <h1 align="center">
![CI](https://github.com/vabene1111/recipes/workflows/Continous%20Integration/badge.svg?branch=develop) <br>
![Stars](https://img.shields.io/github/stars/vabene1111/recipes) <a href="https://app.tandoor.dev"><img src="https://github.com/vabene1111/recipes/raw/develop/docs/logo_color.svg" height="256px" width="256px"></a>
![Forks](https://img.shields.io/github/forks/vabene1111/recipes) <br>
![Docker Pulls](https://img.shields.io/docker/pulls/vabene1111/recipes) Tandoor Recipes
<br>
</h1>
Recipes is a Django application to manage, tag and search recipes using either built-in models or <h4 align="center">The recipe manager that allows you to manage your ever growing collection of digital recipes.</h4>
external storage providers hosting PDF's, images or other files.
[Installation Instructions](https://vabene1111.github.io/recipes/install/docker/) - [Documentation](https://vabene1111.github.io/recipes/) - [More (slightly outdated) Screenshots](https://imgur.com/a/V01151p) <p align="center">
<img src="https://github.com/vabene1111/recipes/workflows/Continous%20Integration/badge.svg?branch=develop" >
<img src="https://img.shields.io/github/stars/vabene1111/recipes" >
<img src="https://img.shields.io/github/forks/vabene1111/recipes" >
<img src="https://img.shields.io/docker/pulls/vabene1111/recipes" >
</p>
<p align="center">
<a href="https://docs.tandoor.dev/install/docker/" target="_blank" rel="noopener noreferrer">Installation</a> •
<a href="https://docs.tandoor.dev/" target="_blank" rel="noopener noreferrer">Documentation</a> •
<a href="https://app.tandoor.dev/" target="_blank" rel="noopener noreferrer">Demo</a>
</p>
![Preview](docs/preview.png) ![Preview](docs/preview.png)
@@ -32,14 +46,10 @@ external storage providers hosting PDF's, images or other files.
This application is meant for people with a collection of recipes they want to share with family and friends or simply This application is meant for people with a collection of recipes they want to share with family and friends or simply
store them in a nicely organized way. A basic permission system exists but this application is not meant to be run as store them in a nicely organized way. A basic permission system exists but this application is not meant to be run as
a public page. a public page.
Documentation can be found [here](https://github.com/vabene1111/recipes/wiki). Documentation can be found [here](https://docs.tandoor.dev/).
While this application has been around for a while and is actively used by many (including myself), it is still considered While this application has been around for a while and is actively used by many (including myself), it is still considered
**beta** software that has a lot of rough edges and unpolished parts. **beta** software that has a lot of rough edges and unpolished parts.
## Documentation
Please refer to the [documentation](https://vabene1111.github.io/recipes/) for everything you need to know.
## License ## License
Beginning with version 0.10.0 the code in this repository is licensed under the [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.de.html) license with an Beginning with version 0.10.0 the code in this repository is licensed under the [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.de.html) license with an

View File

@@ -135,8 +135,14 @@ class ImportExportBase(forms.Form):
DEFAULT = 'DEFAULT' DEFAULT = 'DEFAULT'
PAPRIKA = 'PAPRIKA' PAPRIKA = 'PAPRIKA'
NEXTCLOUD = 'NEXTCLOUD' NEXTCLOUD = 'NEXTCLOUD'
MEALIE = 'MEALIE'
CHOWDOWN = 'CHOWDOWN'
SAFRON = 'SAFRON'
type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, _('Paprika')), (NEXTCLOUD, _('Nextcloud Cookbook')),)) type = forms.ChoiceField(choices=(
(DEFAULT, _('Default')), (PAPRIKA, 'Paprika'), (NEXTCLOUD, 'Nextcloud Cookbook'),
(MEALIE, 'Mealie'), (CHOWDOWN, 'Chowdown'), (SAFRON, 'Safron'),
))
class ImportForm(ImportExportBase): class ImportForm(ImportExportBase):

View File

@@ -28,7 +28,7 @@ def parse_amount(x):
and ( and (
x[end] in string.digits x[end] in string.digits
or ( or (
(x[end] == '.' or x[end] == ',') (x[end] == '.' or x[end] == ',' or x[end] == '/')
and end + 1 < len(x) and end + 1 < len(x)
and x[end + 1] in string.digits and x[end + 1] in string.digits
) )
@@ -36,7 +36,10 @@ def parse_amount(x):
): ):
end += 1 end += 1
if end > 0: if end > 0:
amount = float(x[:end].replace(',', '.')) if "/" in x[:end]:
amount = parse_fraction(x[:end])
else:
amount = float(x[:end].replace(',', '.'))
else: else:
amount = parse_fraction(x[0]) amount = parse_fraction(x[0])
end += 1 end += 1

View File

@@ -224,7 +224,7 @@ def find_recipe_json(ld_json, url):
ld_json['servings'] = int(re.findall(r'\b\d+\b', ld_json['recipeYield'])[0]) ld_json['servings'] = int(re.findall(r'\b\d+\b', ld_json['recipeYield'])[0])
except Exception as e: except Exception as e:
print(e) print(e)
ld_json['servings'] = 0 ld_json['servings'] = 1
for key in list(ld_json): for key in list(ld_json):
if key not in [ if key not in [

View File

@@ -0,0 +1,79 @@
import json
import re
from io import BytesIO
from zipfile import ZipFile
from cookbook.helper.ingredient_parser import parse
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
class Chowdown(Integration):
def import_file_name_filter(self, zip_info_object):
print("testing", zip_info_object.filename)
return re.match(r'^_recipes/([A-Za-z\d\s-])+.md$', zip_info_object.filename)
def get_recipe_from_file(self, file):
ingredient_mode = False
direction_mode = False
description_mode = False
ingredients = []
directions = []
descriptions = []
for fl in file.readlines():
line = fl.decode("utf-8")
if 'title:' in line:
title = line.replace('title:', '').replace('"', '').strip()
if 'image:' in line:
image = line.replace('image:', '').strip()
if 'tags:' in line:
tags = line.replace('tags:', '').strip()
if ingredient_mode:
if len(line) > 2 and 'directions:' not in line:
ingredients.append(line[2:])
if '---' in line and direction_mode:
direction_mode = False
description_mode = True
if direction_mode:
if len(line) > 2:
directions.append(line[2:])
if 'ingredients:' in line:
ingredient_mode = True
if 'directions:' in line:
ingredient_mode = False
direction_mode = True
if description_mode and len(line) > 3 and '---' not in line:
descriptions.append(line)
recipe = Recipe.objects.create(name=title, created_by=self.request.user, internal=True, )
for k in tags.split(','):
keyword, created = Keyword.objects.get_or_create(name=k.strip())
recipe.keywords.add(keyword)
step = Step.objects.create(
instruction='\n'.join(directions) + '\n\n' + '\n'.join(descriptions)
)
for ingredient in ingredients:
amount, unit, ingredient, note = parse(ingredient)
f, created = Food.objects.get_or_create(name=ingredient)
u, created = Unit.objects.get_or_create(name=unit)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note
))
recipe.steps.add(step)
for f in self.files:
if '.zip' in f.name:
import_zip = ZipFile(f.file)
for z in import_zip.filelist:
if re.match(f'^images/{image}$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)))
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

View File

@@ -0,0 +1,52 @@
import json
import re
from io import BytesIO
from zipfile import ZipFile
from cookbook.helper.ingredient_parser import parse
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient
class Mealie(Integration):
def import_file_name_filter(self, zip_info_object):
return re.match(r'^recipes/([A-Za-z\d-])+.json$', zip_info_object.filename)
def get_recipe_from_file(self, file):
recipe_json = json.loads(file.getvalue().decode("utf-8"))
recipe = Recipe.objects.create(
name=recipe_json['name'].strip(), description=recipe_json['description'].strip(),
created_by=self.request.user, internal=True)
# TODO parse times (given in PT2H3M )
ingredients_added = False
for s in recipe_json['recipeInstructions']:
step = Step.objects.create(
instruction=s['text']
)
if not ingredients_added:
ingredients_added = True
for ingredient in recipe_json['recipeIngredient']:
amount, unit, ingredient, note = parse(ingredient)
f, created = Food.objects.get_or_create(name=ingredient)
u, created = Unit.objects.get_or_create(name=unit)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note
))
recipe.steps.add(step)
for f in self.files:
if '.zip' in f.name:
import_zip = ZipFile(f.file)
for z in import_zip.filelist:
if re.match(f'^images/{recipe_json["slug"]}.jpg$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)))
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

View File

@@ -3,18 +3,14 @@ import re
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from zipfile import ZipFile
from rest_framework.renderers import JSONRenderer
from cookbook.helper.ingredient_parser import parse from cookbook.helper.ingredient_parser import parse
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient from cookbook.models import Recipe, Step, Food, Unit, Ingredient
from cookbook.serializer import RecipeExportSerializer
class NextcloudCookbook(Integration): class NextcloudCookbook(Integration):
def import_file_name_filter(self, zip_info_object): def import_file_name_filter(self, zip_info_object):
print("testing", zip_info_object.filename)
return re.match(r'^Recipes/([A-Za-z\d\s])+/recipe.json$', zip_info_object.filename) return re.match(r'^Recipes/([A-Za-z\d\s])+/recipe.json$', zip_info_object.filename)
def get_recipe_from_file(self, file): def get_recipe_from_file(self, file):
@@ -26,6 +22,7 @@ class NextcloudCookbook(Integration):
servings=recipe_json['recipeYield']) servings=recipe_json['recipeYield'])
# TODO parse times (given in PT2H3M ) # TODO parse times (given in PT2H3M )
# TODO parse keywords
ingredients_added = False ingredients_added = False
for s in recipe_json['recipeInstructions']: for s in recipe_json['recipeInstructions']:
@@ -54,6 +51,4 @@ class NextcloudCookbook(Integration):
return recipe return recipe
def get_file_from_recipe(self, recipe): def get_file_from_recipe(self, recipe):
export = RecipeExportSerializer(recipe).data raise NotImplementedError('Method not implemented in storage integration')
return 'recipe.json', JSONRenderer().render(export).decode("utf-8")

View File

@@ -1,6 +1,10 @@
import json import json
import re
from io import BytesIO
from zipfile import ZipFile
import microdata import microdata
from bs4 import BeautifulSoup
from cookbook.helper.recipe_url_import import find_recipe_json from cookbook.helper.recipe_url_import import find_recipe_json
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
@@ -9,6 +13,10 @@ from cookbook.models import Recipe, Step, Food, Ingredient, Unit
class Paprika(Integration): class Paprika(Integration):
def import_file_name_filter(self, zip_info_object):
print("testing", zip_info_object.filename)
return re.match(r'^Recipes/([A-Za-z\s])+.html$', zip_info_object.filename)
def get_file_from_recipe(self, recipe): def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration') raise NotImplementedError('Method not implemented in storage integration')
@@ -33,4 +41,16 @@ class Paprika(Integration):
)) ))
recipe.steps.add(step) recipe.steps.add(step)
soup = BeautifulSoup(html_text, "html.parser")
image = soup.find('img')
image_name = image.attrs['src'].strip().replace('Images/', '')
for f in self.files:
if '.zip' in f.name:
import_zip = ZipFile(f.file)
for z in import_zip.filelist:
if re.match(f'^Recipes/Images/{image_name}$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)))
return recipe return recipe

View File

@@ -0,0 +1,60 @@
from django.utils.translation import gettext as _
from cookbook.helper.ingredient_parser import parse
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient
class Safron(Integration):
def get_recipe_from_file(self, file):
ingredient_mode = False
direction_mode = False
ingredients = []
directions = []
for fl in file.readlines():
line = fl.decode("utf-8")
if 'Title:' in line:
title = line.replace('Title:', '').strip()
if 'Description:' in line:
description = line.replace('Description:', '').strip()
if 'Yield:' in line:
directions.append(_('Servings') + ' ' + line.replace('Yield:', '').strip() + '\n')
if 'Cook:' in line:
directions.append(_('Waiting time') + ' ' + line.replace('Cook:', '').strip() + '\n')
if 'Prep:' in line:
directions.append(_('Preparation Time') + ' ' + line.replace('Prep:', '').strip() + '\n')
if 'Cookbook:' in line:
directions.append(_('Cookbook') + ' ' + line.replace('Cookbook:', '').strip() + '\n')
if 'Section:' in line:
directions.append(_('Section') + ' ' + line.replace('Section:', '').strip() + '\n')
if ingredient_mode:
if len(line) > 2 and 'Instructions:' not in line:
ingredients.append(line.strip())
if direction_mode:
if len(line) > 2:
directions.append(line.strip())
if 'Ingredients:' in line:
ingredient_mode = True
if 'Instructions:' in line:
ingredient_mode = False
direction_mode = True
recipe = Recipe.objects.create(name=title, description=description, created_by=self.request.user, internal=True, )
step = Step.objects.create(instruction='\n'.join(directions))
for ingredient in ingredients:
amount, unit, ingredient, note = parse(ingredient)
f, created = Food.objects.get_or_create(name=ingredient)
u, created = Unit.objects.get_or_create(name=unit)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note
))
recipe.steps.add(step)
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Logo" transform="matrix(0.637323,0,0,0.637323,-243.095,-716.725)">
<g id="Kreis" transform="matrix(1.44936,0,0,1.50279,387.258,1039.34)">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584" style="fill:url(#_Linear1);"/>
<clipPath id="_clip2">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g id="Shadow" transform="matrix(1.10322,0,0,1.064,-5.58287,50.5786)">
<path d="M156.285,427.208L389.554,660.477L668.803,495.551L374.012,200.761L156.285,427.208Z" style="fill:rgb(22,22,22);"/>
<g transform="matrix(1,0,0,1,-4.22105,0.775864)">
<path d="M208.628,178.613L485.935,455.919L590.027,364.63L296.923,71.526L294.175,138.989L208.628,178.613Z" style="fill:rgb(22,22,22);"/>
</g>
<g transform="matrix(1,0,0,1,-85.3876,27.8512)">
<path d="M310.385,145.641L587.692,422.948L590.392,361.357L297.288,68.253L294.175,138.989L310.385,145.641Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
</g>
</g>
<g transform="matrix(1.471,0,0,1.471,406.537,1149.69)">
<path d="M256.049,220C286.222,219.994 312.656,207.31 329.388,194.134C346.35,180.754 370.899,183.406 384.611,200.1C407.129,227.376 420.598,261.944 420.598,299.53C420.598,361.08 382.604,437.101 329.764,463.706C307.035,475.15 283.466,480.586 256.098,480.599L256.098,480.599L256.049,480.599L256,480.599L256,480.599C228.632,480.586 205.063,475.15 182.334,463.706C129.494,437.101 91.5,361.08 91.5,299.53C91.5,261.944 104.969,227.376 127.487,200.1C141.199,183.406 165.748,180.754 182.71,194.134C199.442,207.31 225.876,219.994 256.049,220Z" style="fill:rgb(255,203,118);"/>
</g>
<g id="Flame-2" serif:id="Flame 2" transform="matrix(0.965725,0,0,0.89175,164.497,436.391)">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z" style="fill:rgb(255,111,0);"/>
<clipPath id="_clip3">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z"/>
</clipPath>
<g clip-path="url(#_clip3)">
<g transform="matrix(1.28784,-0.270602,0.285942,1.59598,247.349,825.209)">
<path d="M255.004,46.957C279.547,58.545 306,85.447 313.307,120.161C325.437,177.791 291.571,193.789 262.496,192.403C215.889,190.181 200.194,153.246 231.326,108.9C250.631,81.401 232.663,36.408 255.004,46.957Z" style="fill:rgb(255,209,0);"/>
</g>
</g>
</g>
<g id="Hut" transform="matrix(1.521,0,0,1.521,393.566,1149.06)">
<path d="M228.197,408.524C222.698,408.524 217.813,406.688 214.024,403.619C211.776,401.794 210.92,398.752 211.888,396.024C212.856,393.295 215.437,391.472 218.332,391.472C232.214,391.4 256.112,391.396 256.112,391.396C256.112,391.396 280.009,391.4 293.891,391.472C296.786,391.472 299.367,393.295 300.335,396.024C301.303,398.752 300.447,401.794 298.199,403.619C294.41,406.688 289.526,408.524 284.027,408.524L228.197,408.524ZM217.24,378.877C214.208,378.877 211.3,377.671 209.158,375.525C207.015,373.379 205.814,370.469 205.82,367.436C205.831,361.119 205.842,354.539 205.842,354.539C205.842,350.423 203.097,346.814 199.131,345.714C185.313,341.841 175.2,329.468 175.2,314.823C175.2,297.07 190.059,282.657 208.362,282.657C208.362,282.657 208.362,282.657 208.362,282.657C215.401,282.657 221.675,278.218 224.017,271.581C227.243,262.39 236.411,252.015 256,251.998L256,251.998L256.223,251.998L256.223,251.998C275.812,252.015 284.98,262.39 288.206,271.581C290.549,278.218 296.822,282.657 303.861,282.657C303.861,282.657 303.861,282.657 303.861,282.657C322.164,282.657 337.023,297.07 337.023,314.823C337.023,329.468 326.911,341.841 313.093,345.714C309.127,346.814 306.382,350.423 306.381,354.539C306.381,354.539 306.386,361.127 306.391,367.447C306.394,370.478 305.191,373.385 303.049,375.529C300.907,377.672 298.001,378.877 294.971,378.877C275.615,378.877 236.604,378.877 217.24,378.877Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2e-06,0,0,2e-06,3755.77,81.7179)"><stop offset="0" style="stop-color:rgb(39,39,39);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(108,108,108);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Logo" transform="matrix(0.637323,0,0,0.637323,-243.095,-716.725)">
<g id="Kreis" transform="matrix(1.44936,0,0,1.50279,387.258,1039.34)">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584" style="fill:url(#_Linear1);"/>
<clipPath id="_clip2">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g id="Shadow" transform="matrix(1.10322,0,0,1.064,-5.58287,50.5786)">
<path d="M156.285,427.208L389.554,660.477L668.803,495.551L374.012,200.761L156.285,427.208Z" style="fill:rgb(22,22,22);"/>
<g transform="matrix(1,0,0,1,-4.22105,0.775864)">
<path d="M208.628,178.613L485.935,455.919L590.027,364.63L296.923,71.526L294.175,138.989L208.628,178.613Z" style="fill:rgb(22,22,22);"/>
</g>
<g transform="matrix(1,0,0,1,-85.3876,27.8512)">
<path d="M310.385,145.641L587.692,422.948L590.392,361.357L297.288,68.253L294.175,138.989L310.385,145.641Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
</g>
</g>
<g transform="matrix(1.471,0,0,1.471,406.537,1149.69)">
<path d="M256.049,220C286.222,219.994 312.656,207.31 329.388,194.134C346.35,180.754 370.899,183.406 384.611,200.1C407.129,227.376 420.598,261.944 420.598,299.53C420.598,361.08 382.604,437.101 329.764,463.706C307.035,475.15 283.466,480.586 256.098,480.599L256.098,480.599L256.049,480.599L256,480.599L256,480.599C228.632,480.586 205.063,475.15 182.334,463.706C129.494,437.101 91.5,361.08 91.5,299.53C91.5,261.944 104.969,227.376 127.487,200.1C141.199,183.406 165.748,180.754 182.71,194.134C199.442,207.31 225.876,219.994 256.049,220Z" style="fill:rgb(255,203,118);"/>
</g>
<g id="Flame-2" serif:id="Flame 2" transform="matrix(0.965725,0,0,0.89175,164.497,436.391)">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z" style="fill:rgb(255,111,0);"/>
<clipPath id="_clip3">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z"/>
</clipPath>
<g clip-path="url(#_clip3)">
<g transform="matrix(1.28784,-0.270602,0.285942,1.59598,247.349,825.209)">
<path d="M255.004,46.957C279.547,58.545 306,85.447 313.307,120.161C325.437,177.791 291.571,193.789 262.496,192.403C215.889,190.181 200.194,153.246 231.326,108.9C250.631,81.401 232.663,36.408 255.004,46.957Z" style="fill:rgb(255,209,0);"/>
</g>
</g>
</g>
<g id="Hut" transform="matrix(1.521,0,0,1.521,393.566,1149.06)">
<path d="M228.197,408.524C222.698,408.524 217.813,406.688 214.024,403.619C211.776,401.794 210.92,398.752 211.888,396.024C212.856,393.295 215.437,391.472 218.332,391.472C232.214,391.4 256.112,391.396 256.112,391.396C256.112,391.396 280.009,391.4 293.891,391.472C296.786,391.472 299.367,393.295 300.335,396.024C301.303,398.752 300.447,401.794 298.199,403.619C294.41,406.688 289.526,408.524 284.027,408.524L228.197,408.524ZM217.24,378.877C214.208,378.877 211.3,377.671 209.158,375.525C207.015,373.379 205.814,370.469 205.82,367.436C205.831,361.119 205.842,354.539 205.842,354.539C205.842,350.423 203.097,346.814 199.131,345.714C185.313,341.841 175.2,329.468 175.2,314.823C175.2,297.07 190.059,282.657 208.362,282.657C208.362,282.657 208.362,282.657 208.362,282.657C215.401,282.657 221.675,278.218 224.017,271.581C227.243,262.39 236.411,252.015 256,251.998L256,251.998L256.223,251.998L256.223,251.998C275.812,252.015 284.98,262.39 288.206,271.581C290.549,278.218 296.822,282.657 303.861,282.657C303.861,282.657 303.861,282.657 303.861,282.657C322.164,282.657 337.023,297.07 337.023,314.823C337.023,329.468 326.911,341.841 313.093,345.714C309.127,346.814 306.382,350.423 306.381,354.539C306.381,354.539 306.386,361.127 306.391,367.447C306.394,370.478 305.191,373.385 303.049,375.529C300.907,377.672 298.001,378.877 294.971,378.877C275.615,378.877 236.604,378.877 217.24,378.877Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2e-06,0,0,2e-06,3755.77,81.7179)"><stop offset="0" style="stop-color:rgb(39,39,39);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(108,108,108);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="book" class="svg-inline--fa fa-book fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M128 152v-32c0-4.4 3.6-8 8-8h208c4.4 0 8 3.6 8 8v32c0 4.4-3.6 8-8 8H136c-4.4 0-8-3.6-8-8zm8 88h208c4.4 0 8-3.6 8-8v-32c0-4.4-3.6-8-8-8H136c-4.4 0-8 3.6-8 8v32c0 4.4 3.6 8 8 8zm299.1 159.7c-4.2 13-4.2 51.6 0 64.6 7.3 1.4 12.9 7.9 12.9 15.7v16c0 8.8-7.2 16-16 16H80c-44.2 0-80-35.8-80-80V80C0 35.8 35.8 0 80 0h352c8.8 0 16 7.2 16 16v368c0 7.8-5.5 14.2-12.9 15.7zm-41.1.3H80c-17.6 0-32 14.4-32 32 0 17.7 14.3 32 32 32h314c-2.7-17.3-2.7-46.7 0-64zm6-352H80c-17.7 0-32 14.3-32 32v278.7c9.8-4.3 20.6-6.7 32-6.7h320V48z"></path></svg>

Before

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -11,17 +11,17 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" type="image/x-icon" href="{% static 'favicon.png' %}"> <link rel="shortcut icon" type="image/x-icon" href="{% static 'assets/favicon.svg' %}">
<link rel="shortcut icon" href="{% static 'favicon.png' %}"> <link rel="shortcut icon" href="{% static 'assets/favicon.svg' %}">
<link rel="icon" type="image/png" href="{% static 'favicon.png' %}" sizes="32x32"> <link rel="icon" type="image/png" href="{% static 'assets/favicon.svg' %}" sizes="32x32">
<link rel="icon" type="image/png" href="{% static 'favicon.png' %}" sizes="96x96"> <link rel="icon" type="image/png" href="{% static 'assets/favicon.svg' %}" sizes="96x96">
<link rel="apple-touch-icon" href="{% static 'favicon.png' %}"> <link rel="apple-touch-icon" href="{% static 'assets/favicon.svg' %}">
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'favicon.png' %}"> <link rel="apple-touch-icon" sizes="180x180" href="{% static 'assets/favicon.svg' %}">
<link rel="apple-touch-icon" sizes="152x152" href="{% static 'favicon.png' %}"> <link rel="apple-touch-icon" sizes="152x152" href="{% static 'assets/favicon.svg' %}">
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'favicon.png' %}"> <link rel="apple-touch-icon" sizes="180x180" href="{% static 'assets/favicon.svg' %}">
<link rel="apple-touch-icon" sizes="167x167" href="{% static 'favicon.png' %}"> <link rel="apple-touch-icon" sizes="167x167" href="{% static 'assets/favicon.svg' %}">
<link rel="manifest" href="{% static 'manifest/webmanifest' %}"> <link rel="manifest" href="{% url 'web_manifest' %}">
<meta name="msapplication-TileColor" content="#ffffff"> <meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/mstile-144x144.png"> <meta name="msapplication-TileImage" content="/mstile-144x144.png">

View File

@@ -1,51 +1,48 @@
{ {
"name": "Recipes", "name": "Tandoor Recipes",
"short_name" : "Tandoor",
"description": "Application to manage, tag and search recipes.", "description": "Application to manage, tag and search recipes.",
"icons": [ "icons": [
{ {
"src": "/static/manifest/icon-192.png", "src": "/static/assets/logo_color144.png",
"type": "image/png", "type": "image/png",
"sizes": "192x192" "sizes": "144x144"
}, },
{ {
"src": "/static/manifest/icon-512.png", "src": "/static/assets/logo_color512.png",
"type": "image/png", "type": "image/png",
"sizes": "512x512" "sizes": "512x512"
} }
], ],
"start_url": "/search", "start_url": "/search",
"background_color": "#18BC9C", "background_color": "#ffcb76",
"display": "standalone", "display": "standalone",
"scope": "/", "scope": "/",
"theme_color": "#18BC9C", "theme_color": "#ffcb76",
"shortcuts": [ "shortcuts": [
{ {
"name": "Plan", "name": "Plan",
"short_name": "Plan", "short_name": "Plan",
"description": "View your meal Plan", "description": "View your meal Plan",
"url": "/plan", "url": "/plan"
"icons": [{ "src": "/static/manifest/icon-192.png", "sizes": "192x192" }]
}, },
{ {
"name": "Books", "name": "Books",
"short_name": "Cookbooks", "short_name": "Cookbooks",
"description": "View your cookbooks", "description": "View your cookbooks",
"url": "/books", "url": "/books"
"icons": [{ "src": "/static/manifest/icon-192.png", "sizes": "192x192" }]
}, },
{ {
"name": "Shopping", "name": "Shopping",
"short_name": "Shopping", "short_name": "Shopping",
"description": "View your shopping lists", "description": "View your shopping lists",
"url": "/list/shopping-list/", "url": "/list/shopping-list/"
"icons": [{ "src": "/static/manifest/shopping-cart-192.png", "sizes": "192x192" }]
}, },
{ {
"name": "Latest Shopping List", "name": "Latest Shopping List",
"short_name": "Shopping List", "short_name": "Shopping List",
"description": "View the latest shopping list", "description": "View the latest shopping list",
"url": "/shopping/latest/", "url": "/shopping/latest/"
"icons": [{ "src": "/static/manifest/shopping-cart-192.png", "sizes": "192x192" }]
} }
] ]
} }

View File

@@ -129,7 +129,7 @@
[](https://github.com/vabene1111/recipes) [](https://github.com/vabene1111/recipes)
[GitHub](https://github.com/vabene1111/recipes) [GitHub](https://github.com/vabene1111/recipes)
![{% trans 'This will become an image' %}]({% static 'favicon.png' %}) ![{% trans 'This will become an image' %}]({% static 'assets/favicon.svg' %})
</code></pre> </code></pre>
<div style="text-align: center"> <div style="text-align: center">
@@ -142,7 +142,7 @@
<div class="card-body"> <div class="card-body">
<a href="https://github.com/vabene1111/recipes">https://github.com/vabene1111/recipes</a> <br/> <a href="https://github.com/vabene1111/recipes">https://github.com/vabene1111/recipes</a> <br/>
<a href="https://github.com/vabene1111/recipes">GitHub</a> <br/> <a href="https://github.com/vabene1111/recipes">GitHub</a> <br/>
<img src="{% static 'favicon.png' %}" class="img-fluid" alt="{% trans 'This will become an image' %}" <img src="{% static 'assets/favicon.svg' %}" class="img-fluid" alt="{% trans 'This will become an image' %}"
style="height: 3vw"> style="height: 3vw">
</div> </div>

View File

@@ -19,7 +19,7 @@
<img src=" {{ row.cells.image }}" alt="{% trans 'Recipe Image' %}" <img src=" {{ row.cells.image }}" alt="{% trans 'Recipe Image' %}"
class="card-img" style="object-fit: cover;height: 130px"> class="card-img" style="object-fit: cover;height: 130px">
{% else %} {% else %}
<img src="{% static 'recipe_no_image.svg' %}" <img src="{% static 'assets/recipe_no_image.svg' %}"
alt="{% trans 'Recipe Image' %}" alt="{% trans 'Recipe Image' %}"
class="card-img d-none d-md-block" class="card-img d-none d-md-block"
style="object-fit: cover; height: 130px"> style="object-fit: cover; height: 130px">

View File

@@ -681,7 +681,7 @@
Promise.allSettled(recipe_promises).then(() => { Promise.allSettled(recipe_promises).then(() => {
console.log("proceeding to update shopping list", this.shopping_list) console.log("proceeding to update shopping list", this.shopping_list)
if (this.shopping_list_id === null) { if (this.shopping_list.id === undefined) {
return this.$http.post("{% url 'api:shoppinglist-list' %}", this.shopping_list, {}).then((response) => { return this.$http.post("{% url 'api:shoppinglist-list' %}", this.shopping_list, {}).then((response) => {
console.log(response) console.log(response)
this.makeToast(gettext('Updated'), gettext('Object created successfully!'), 'success') this.makeToast(gettext('Updated'), gettext('Object created successfully!'), 'success')
@@ -697,7 +697,7 @@
this.loading = false this.loading = false
}) })
} else { } else {
return this.$http.put("{% url 'api:shoppinglist-detail' shopping_list_id %}", this.shopping_list, {}).then((response) => { return this.$http.put("{% url 'api:shoppinglist-detail' 123456 %}".replace('123456', this.shopping_list.id), this.shopping_list, {}).then((response) => {
console.log(response) console.log(response)
this.shopping_list = response.body this.shopping_list = response.body
this.makeToast(gettext('Updated'), gettext('Changes saved successfully!'), 'success') this.makeToast(gettext('Updated'), gettext('Changes saved successfully!'), 'success')

View File

@@ -86,6 +86,8 @@ def page_help(page_name):
help_pages = { help_pages = {
'edit_storage': 'https://vabene1111.github.io/recipes/features/external_recipes/', 'edit_storage': 'https://vabene1111.github.io/recipes/features/external_recipes/',
'view_shopping': 'https://vabene1111.github.io/recipes/features/shopping/', 'view_shopping': 'https://vabene1111.github.io/recipes/features/shopping/',
'view_import': 'https://vabene1111.github.io/recipes/features/import_export/',
'view_export': 'https://vabene1111.github.io/recipes/features/import_export/',
} }
link = help_pages.get(page_name, '') link = help_pages.get(page_name, '')

View File

@@ -33,6 +33,7 @@ class TestEditsRecipe(TestBase):
expectations = { expectations = {
"2¼ l Wasser": (2.25, "l", "Wasser", ""), "2¼ l Wasser": (2.25, "l", "Wasser", ""),
"2¼l Wasser": (2.25, "l", "Wasser", ""), "2¼l Wasser": (2.25, "l", "Wasser", ""),
"¼ l Wasser": (0.25, "l", "Wasser", ""),
"3l Wasser": (3, "l", "Wasser", ""), "3l Wasser": (3, "l", "Wasser", ""),
"4 l Wasser": (4, "l", "Wasser", ""), "4 l Wasser": (4, "l", "Wasser", ""),
"½l Wasser": (0.5, "l", "Wasser", ""), "½l Wasser": (0.5, "l", "Wasser", ""),
@@ -43,6 +44,10 @@ class TestEditsRecipe(TestBase):
"1 Zwiebel(n)": (1, "", "Zwiebel(n)", ""), "1 Zwiebel(n)": (1, "", "Zwiebel(n)", ""),
"4 1/2 Zwiebeln": (4.5, "", "Zwiebeln", ""), "4 1/2 Zwiebeln": (4.5, "", "Zwiebeln", ""),
"4 ½ Zwiebeln": (4.5, "", "Zwiebeln", ""), "4 ½ Zwiebeln": (4.5, "", "Zwiebeln", ""),
"1/2 EL Mehl": (0.5, "EL", "Mehl", ""),
"1/2 Zwiebel": (0.5, "", "Zwiebel", ""),
"1/5g Mehl, gesiebt": (0.2, "g", "Mehl", "gesiebt"),
"1/2 Zitrone, ausgepresst": (0.5, "", "Zitrone", "ausgepresst"),
"etwas Mehl": (0, "", "etwas Mehl", ""), "etwas Mehl": (0, "", "etwas Mehl", ""),
"Öl zum Anbraten": (0, "", "Öl zum Anbraten", ""), "Öl zum Anbraten": (0, "", "Öl zum Anbraten", ""),
"n. B. Knoblauch, zerdrückt": (0, "", "n. B. Knoblauch", "zerdrückt"), "n. B. Knoblauch, zerdrückt": (0, "", "n. B. Knoblauch", "zerdrückt"),

View File

@@ -105,8 +105,10 @@ urlpatterns = [
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('offline/', views.offline, name='view_offline'), path('offline/', views.offline, name='view_offline'),
path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript', )), name='service_worker'),
path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript', )), name='service_worker'),
path('manifest.json', (TemplateView.as_view(template_name="manifest.json", content_type='application/json', )), name='web_manifest'),
] ]
generic_models = ( generic_models = (

View File

@@ -6,9 +6,12 @@ from django.utils.translation import gettext as _
from cookbook.forms import ExportForm, ImportForm, ImportExportBase from cookbook.forms import ExportForm, ImportForm, ImportExportBase
from cookbook.helper.permission_helper import group_required from cookbook.helper.permission_helper import group_required
from cookbook.integration.chowdown import Chowdown
from cookbook.integration.default import Default from cookbook.integration.default import Default
from cookbook.integration.mealie import Mealie
from cookbook.integration.nextcloud_cookbook import NextcloudCookbook from cookbook.integration.nextcloud_cookbook import NextcloudCookbook
from cookbook.integration.paprika import Paprika from cookbook.integration.paprika import Paprika
from cookbook.integration.safron import Safron
from cookbook.models import Recipe from cookbook.models import Recipe
@@ -19,6 +22,12 @@ def get_integration(request, export_type):
return Paprika(request) return Paprika(request)
if export_type == ImportExportBase.NEXTCLOUD: if export_type == ImportExportBase.NEXTCLOUD:
return NextcloudCookbook(request) return NextcloudCookbook(request)
if export_type == ImportExportBase.MEALIE:
return Mealie(request)
if export_type == ImportExportBase.CHOWDOWN:
return Chowdown(request)
if export_type == ImportExportBase.SAFRON:
return Safron(request)
@group_required('user') @group_required('user')

View File

@@ -30,9 +30,7 @@ def keyword(request):
@group_required('admin') @group_required('admin')
def sync_log(request): def sync_log(request):
table = ImportLogTable( table = ImportLogTable(
SyncLog.objects.all().order_by( SyncLog.objects.all().order_by('-created_at')
Lower('created_at').desc()
)
) )
RequestConfig(request, paginate={'per_page': 25}).configure(table) RequestConfig(request, paginate={'per_page': 25}).configure(table)

1
docs/CNAME Normal file
View File

@@ -0,0 +1 @@
docs.tandoor.dev

View File

@@ -7,12 +7,12 @@ integrations to allow you to both import and export your recipes into whatever f
Feel like there is an important integration missing ? Just take a look at the [integration issues](https://github.com/vabene1111/recipes/issues?q=is%3Aissue+is%3Aopen+label%3Aintegration) or open a new one Feel like there is an important integration missing ? Just take a look at the [integration issues](https://github.com/vabene1111/recipes/issues?q=is%3Aissue+is%3Aopen+label%3Aintegration) or open a new one
if your favorite one is missing. if your favorite one is missing.
!!! warning "WIP" !!! info "Export"
Please note that this feature is relatively new and many integrations are missing. I strongly believe in everyone's right to use their data as they please and therefore want to give you
Additionally, many recipe applications provide formats that are not structured in an easily machine-readable way the most possible flexibility with your recipes.
and thus require a lot of work to integrate even tough the module is very versatile. That said for most of the people getting this application running with their recipes is the biggest priority.
If you are good at writing parsers feel free to add new integrations for your favorite services. Because of this importing as many formats as possible is prioritized over exporting.
Exporter for the different formats will follow over time.
## Default ## Default
The default integration is the build in (and preferred) way to import and export recipes. The default integration is the build in (and preferred) way to import and export recipes.
@@ -36,7 +36,7 @@ You will get a `Recipes.zip` file. Simply upload the file and choose the Nextclo
!!! warning "Folder Structure" !!! warning "Folder Structure"
Importing only works if the folder structure is correct. If you do not use the standard path or create the Importing only works if the folder structure is correct. If you do not use the standard path or create the
zip file in any other way make sure the strucutre is as follows zip file in any other way make sure the structure is as follows
``` ```
Recipes.zip/ Recipes.zip/
└── Recipes/ └── Recipes/
@@ -48,12 +48,57 @@ You will get a `Recipes.zip` file. Simply upload the file and choose the Nextclo
└── full.jpg └── full.jpg
``` ```
## Mealie
Mealie provides structured data similar to nextcloud.
To migrate your recipes
1. Go to you Mealie settings and create a new Backup
2. Download the backup by clicking on it and pressing download (this wasn't working for me, so I had to manually pull it from the server)
3. Upload the entire `.zip` file to the importer page and import everything
## Chowdown
Chowdown stores all your recipes in plain text markdown files in a directory called `_recipes`.
Images are saved in a directory called `images`.
In order to import your Chowdown recipes simply create a `.zip` file from those two folders and import them.
The folder structure should look as follows
```
Recipes.zip/
├── _recipes/
│ ├── recipe one.md
│ ├── recipe two.md
│ └── ...
└── images/
├── image-name.jpg
├── second-image-name.jpg
└── ...
```
## Safron
Go to you safron settings page and export your recipes.
Then simply upload the entire `.zip` file to the importer.
!!! warning "Images"
Safron exports do not contain any images. They will be lost during import.
## Paprika ## Paprika
Paprika can create two types of export. The first is a proprietary `.paprikarecipes` file in some kind of binarized format. Paprika can create two types of export. The first is a proprietary `.paprikarecipes` file in some kind of binarized format.
The second one is HTML files containing at least a bit of microdata. The second one is HTML files containing at least a bit of microdata.
If you want to import your Paprika recipes create a html export. Then import the individual recipes HTML files. If you want to import your Paprika recipes follow these steps
Due to the lack of structure not all fields can be imported.
Even tough images are present in the export they cannot be imported atm. This is technically possible and might be 1. create a html export
added in the future. 2. Create a `.zip` file from the `Recipes` folder (next to the `index.html`) the structure should look like this
```
Recipes.zip/
└── Recipes/
├── recipe one.html
├── recipe two.thml
└── Images/
├── 5D5E09CD-8F88-4F61-8121-0727DD3E0E89/
│ └── 5D5E09CD-8F88-4F61-8121-0727DD3E0E89.jpg
└── 7CEE2AC6-DF60-4464-9B61-4F5E347EB90C/
└── 7CEE2AC6-DF60-4464-9B61-4F5E347EB90C.jpg
```
3. Upload the zip file in the import module and import it

View File

@@ -1,6 +1,27 @@
# Welcome to Recipes Documentation <h1 align="center">
<br>
<a href="https://app.tandoor.dev"><img src="https://github.com/vabene1111/recipes/raw/develop/docs/logo_color.svg" height="256px" width="256px"></a>
<br>
Tandoor Recipes
<br>
</h1>
The recipe manager that allows you to manage your ever growing collection of digital recipes. <h4 align="center">The recipe manager that allows you to manage your ever growing collection of digital recipes.</h4>
<p align="center">
<img src="https://github.com/vabene1111/recipes/workflows/Continous%20Integration/badge.svg?branch=develop" >
<img src="https://img.shields.io/github/stars/vabene1111/recipes" >
<img src="https://img.shields.io/github/forks/vabene1111/recipes" >
<img src="https://img.shields.io/docker/pulls/vabene1111/recipes" >
</p>
<p align="center">
<a href="https://docs.tandoor.dev/install/docker/" target="_blank" rel="noopener noreferrer">Installation</a> •
<a href="https://docs.tandoor.dev/" target="_blank" rel="noopener noreferrer">Documentation</a> •
<a href="https://app.tandoor.dev/" target="_blank" rel="noopener noreferrer">Demo</a>
</p>
![Preview](preview.png) ![Preview](preview.png)

43
docs/logo_color.svg Normal file
View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Logo" transform="matrix(0.637323,0,0,0.637323,-243.095,-716.725)">
<g id="Kreis" transform="matrix(1.44936,0,0,1.50279,387.258,1039.34)">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584" style="fill:url(#_Linear1);"/>
<clipPath id="_clip2">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g id="Shadow" transform="matrix(1.10322,0,0,1.064,-5.58287,50.5786)">
<path d="M156.285,427.208L389.554,660.477L668.803,495.551L374.012,200.761L156.285,427.208Z" style="fill:rgb(22,22,22);"/>
<g transform="matrix(1,0,0,1,-4.22105,0.775864)">
<path d="M208.628,178.613L485.935,455.919L590.027,364.63L296.923,71.526L294.175,138.989L208.628,178.613Z" style="fill:rgb(22,22,22);"/>
</g>
<g transform="matrix(1,0,0,1,-85.3876,27.8512)">
<path d="M310.385,145.641L587.692,422.948L590.392,361.357L297.288,68.253L294.175,138.989L310.385,145.641Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
</g>
</g>
<g transform="matrix(1.471,0,0,1.471,406.537,1149.69)">
<path d="M256.049,220C286.222,219.994 312.656,207.31 329.388,194.134C346.35,180.754 370.899,183.406 384.611,200.1C407.129,227.376 420.598,261.944 420.598,299.53C420.598,361.08 382.604,437.101 329.764,463.706C307.035,475.15 283.466,480.586 256.098,480.599L256.098,480.599L256.049,480.599L256,480.599L256,480.599C228.632,480.586 205.063,475.15 182.334,463.706C129.494,437.101 91.5,361.08 91.5,299.53C91.5,261.944 104.969,227.376 127.487,200.1C141.199,183.406 165.748,180.754 182.71,194.134C199.442,207.31 225.876,219.994 256.049,220Z" style="fill:rgb(255,203,118);"/>
</g>
<g id="Flame-2" serif:id="Flame 2" transform="matrix(0.965725,0,0,0.89175,164.497,436.391)">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z" style="fill:rgb(255,111,0);"/>
<clipPath id="_clip3">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z"/>
</clipPath>
<g clip-path="url(#_clip3)">
<g transform="matrix(1.28784,-0.270602,0.285942,1.59598,247.349,825.209)">
<path d="M255.004,46.957C279.547,58.545 306,85.447 313.307,120.161C325.437,177.791 291.571,193.789 262.496,192.403C215.889,190.181 200.194,153.246 231.326,108.9C250.631,81.401 232.663,36.408 255.004,46.957Z" style="fill:rgb(255,209,0);"/>
</g>
</g>
</g>
<g id="Hut" transform="matrix(1.521,0,0,1.521,393.566,1149.06)">
<path d="M228.197,408.524C222.698,408.524 217.813,406.688 214.024,403.619C211.776,401.794 210.92,398.752 211.888,396.024C212.856,393.295 215.437,391.472 218.332,391.472C232.214,391.4 256.112,391.396 256.112,391.396C256.112,391.396 280.009,391.4 293.891,391.472C296.786,391.472 299.367,393.295 300.335,396.024C301.303,398.752 300.447,401.794 298.199,403.619C294.41,406.688 289.526,408.524 284.027,408.524L228.197,408.524ZM217.24,378.877C214.208,378.877 211.3,377.671 209.158,375.525C207.015,373.379 205.814,370.469 205.82,367.436C205.831,361.119 205.842,354.539 205.842,354.539C205.842,350.423 203.097,346.814 199.131,345.714C185.313,341.841 175.2,329.468 175.2,314.823C175.2,297.07 190.059,282.657 208.362,282.657C208.362,282.657 208.362,282.657 208.362,282.657C215.401,282.657 221.675,278.218 224.017,271.581C227.243,262.39 236.411,252.015 256,251.998L256,251.998L256.223,251.998L256.223,251.998C275.812,252.015 284.98,262.39 288.206,271.581C290.549,278.218 296.822,282.657 303.861,282.657C303.861,282.657 303.861,282.657 303.861,282.657C322.164,282.657 337.023,297.07 337.023,314.823C337.023,329.468 326.911,341.841 313.093,345.714C309.127,346.814 306.382,350.423 306.381,354.539C306.381,354.539 306.386,361.127 306.391,367.447C306.394,370.478 305.191,373.385 303.049,375.529C300.907,377.672 298.001,378.877 294.971,378.877C275.615,378.877 236.604,378.877 217.24,378.877Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2e-06,0,0,2e-06,3755.77,81.7179)"><stop offset="0" style="stop-color:rgb(39,39,39);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(108,108,108);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

View File

@@ -0,0 +1,19 @@
:root {
--md-primary-fg-color: #ffcb76;
--md-accent-fg-color: #FF6F00;
--md-primary-fg-color--light: #ffcb76;
/* not working part, has no effect */
--md-primary-bg-color: #272727;
--md-default-bg-color: #272727;
--md-default-bg-color--light: #272727;
--md-default-bg-color--lighter: #272727;
--md-default-bg-color--lightest: #272727;
}
/*
flame dark orange #FF6F00
background dark #272727
tandoor #ffcb76
*/

View File

@@ -1,3 +1,3 @@
CALL venv\Scripts\activate.bat CALL venv\Scripts\activate.bat
python manage.py makemessages -i venv -a python manage.py makemessages -i venv -l ca -l de -l en -l es -l fr -l hu_HU -l it -l lv -l nl -l pt -l rn -l tr -l zh_CN
python manage.py makemessages -i venv -a -l de -d djangojs python manage.py makemessages -i venv -a -l de -d djangojs

View File

@@ -1,18 +1,20 @@
site_name: Recipes site_name: Tandoor Recipes
site_description: Documentation for Recipes site_description: Tandoor Recipe Documentation
site_author: vabene1111 site_author: vabene1111
repo_url: https://github.com/vabene1111/recipes repo_url: https://github.com/vabene1111/recipes
edit_uri: https://github.com/vabene1111/recipes/tree/develop/docs edit_uri: https://github.com/vabene1111/recipes/tree/develop/docs
extra_css:
- stylesheets/extra.css
theme: theme:
name: material name: material
repo: fontawesome/brands/github
logo: logo_color.svg
favicon: logo_color.svg
palette: palette:
scheme: slate scheme: slate
primary: green
accent: deep orange
logo: cookbook/static/favicon.png
favicon: cookbook/static/favicon.ico
icon:
repo: fontawesome/brands/github
markdown_extensions: markdown_extensions:
- admonition - admonition
@@ -29,7 +31,7 @@ nav:
- Manual: install/manual.md - Manual: install/manual.md
- Other setups: install/other.md - Other setups: install/other.md
- Features: - Features:
- Tempalating: features/templating.md - Templating: features/templating.md
- Shopping: features/shopping.md - Shopping: features/shopping.md
- Authentication: features/authentication.md - Authentication: features/authentication.md
- Storages and Sync: features/external_recipes.md - Storages and Sync: features/external_recipes.md

View File

@@ -10,7 +10,7 @@ server {
} }
# pass requests for dynamic content to gunicorn # pass requests for dynamic content to gunicorn
location / { location / {
proxy_set_header Host $host; proxy_set_header Host $http_host;
proxy_pass http://web_recipes:8080; proxy_pass http://web_recipes:8080;
} }
} }

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,30 +17,35 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175
#: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-13 23:31+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,34 +18,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: .\recipes\settings.py:196 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: .\recipes\settings.py:197 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: .\recipes\settings.py:198 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:199 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: .\recipes\settings.py:200 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: .\recipes\settings.py:201 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: .\recipes\settings.py:202 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:203 #: .\recipes\settings.py:221
msgid "Latvian" msgid "Latvian"
msgstr "" msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,30 +18,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,30 +17,35 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175
#: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,30 +18,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,30 +17,34 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,30 +17,35 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175
#: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -2,13 +2,13 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-31 18:41+0200\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,34 +19,34 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : "
"2);\n" "2);\n"
#: .\recipes\settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: .\recipes\settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: .\recipes\settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: .\recipes\settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:182 #: .\recipes\settings.py:221
msgid "Latvian" msgid "Latvian"
msgstr "" msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,30 +18,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,30 +18,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,30 +17,34 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,30 +18,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,30 +17,34 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: recipes/settings.py:175 #: .\recipes\settings.py:214
msgid "English" msgid "English"
msgstr "" msgstr ""
#: recipes/settings.py:176 #: .\recipes\settings.py:215
msgid "German" msgid "German"
msgstr "" msgstr ""
#: recipes/settings.py:177 #: .\recipes\settings.py:216
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: recipes/settings.py:178 #: .\recipes\settings.py:217
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179 #: .\recipes\settings.py:218
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: recipes/settings.py:180 #: .\recipes\settings.py:219
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: recipes/settings.py:181 #: .\recipes\settings.py:220
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: .\recipes\settings.py:221
msgid "Latvian"
msgstr ""

View File

@@ -219,14 +219,15 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
LANGUAGES = [ LANGUAGES = [
('en', _('English')),
('de', _('German')),
('nl', _('Dutch')),
('fr', _('French')),
('ca', _('Catalan')), ('ca', _('Catalan')),
('es', _('Spanish')), ('cs', _('Czech')),
('nl', _('Dutch')),
('en', _('English')),
('fr', _('French')),
('de', _('German')),
('it', _('Italian')), ('it', _('Italian')),
('lv', _('Latvian')), ('lv', _('Latvian')),
('es', _('Spanish')),
] ]
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)