Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
vabene1111 committed Nov 23, 2021
2 parents b72919d + 9d8c083 commit 69acca7
Show file tree
Hide file tree
Showing 49 changed files with 6,610 additions and 755 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ jobs:
- uses: actions/setup-python@v2
with:
python-version: 3.x
- run: pip install mkdocs-material
- run: pip install mkdocs-material mkdocs-include-markdown-plugin
- run: mkdocs gh-deploy --force
40 changes: 27 additions & 13 deletions cookbook/apps.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import traceback

from django.apps import AppConfig
from django.conf import settings
from django.db import OperationalError, ProgrammingError
from django_scopes import scopes_disabled

from recipes.settings import DEBUG


class CookbookConfig(AppConfig):
name = 'cookbook'

def ready(self):
# post_save signal is only necessary if using full-text search on postgres
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']:
import cookbook.signals # noqa

# when starting up run fix_tree to:
# a) make sure that nodes are sorted when switching between sort modes
# b) fix problems, if any, with tree consistency
with scopes_disabled():
try:
from cookbook.models import Keyword, Food
Keyword.fix_tree(fix_paths=True)
Food.fix_tree(fix_paths=True)
except OperationalError:
pass # if model does not exist there is no need to fix it
except ProgrammingError:
pass # if migration has not been run database cannot be fixed yet
if not settings.DISABLE_TREE_FIX_STARTUP:
# when starting up run fix_tree to:
# a) make sure that nodes are sorted when switching between sort modes
# b) fix problems, if any, with tree consistency
with scopes_disabled():
try:
from cookbook.models import Keyword, Food
Keyword.fix_tree(fix_paths=True)
Food.fix_tree(fix_paths=True)
except OperationalError:
if DEBUG:
traceback.print_exc()
pass # if model does not exist there is no need to fix it
except ProgrammingError:
if DEBUG:
traceback.print_exc()
pass # if migration has not been run database cannot be fixed yet
except Exception:
if DEBUG:
traceback.print_exc()
pass # dont break startup just because fix could not run, need to investigate cases when this happens
22 changes: 12 additions & 10 deletions cookbook/helper/image_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@
from io import BytesIO


def rescale_image_jpeg(image_object, base_width=720):
def rescale_image_jpeg(image_object, base_width=1020):
img = Image.open(image_object)
icc_profile = img.info.get('icc_profile') # remember color profile to not mess up colors
width_percent = (base_width / float(img.size[0]))
height = int((float(img.size[1]) * float(width_percent)))

img = img.resize((base_width, height), Image.ANTIALIAS)
img_bytes = BytesIO()
img.save(img_bytes, 'JPEG', quality=75, optimize=True, icc_profile=icc_profile)
img.save(img_bytes, 'JPEG', quality=90, optimize=True, icc_profile=icc_profile)

return img_bytes


def rescale_image_png(image_object, base_width=720):
basewidth = 720
wpercent = (basewidth / float(image_object.size[0]))
def rescale_image_png(image_object, base_width=1020):
image_object = Image.open(image_object)
wpercent = (base_width / float(image_object.size[0]))
hsize = int((float(image_object.size[1]) * float(wpercent)))
img = image_object.resize((basewidth, hsize), Image.ANTIALIAS)
img = image_object.resize((base_width, hsize), Image.ANTIALIAS)

im_io = BytesIO()
img.save(im_io, 'PNG', quality=70)
return img
img.save(im_io, 'PNG', quality=90)
return im_io


def get_filetype(name):
Expand All @@ -36,9 +36,11 @@ def get_filetype(name):
return '.jpeg'


# TODO this whole file needs proper documentation, refactoring, and testing
# TODO also add env variable to define which images sizes should be compressed
def handle_image(request, image_object, filetype='.jpeg'):
if sys.getsizeof(image_object) / 8 > 500:
if filetype == '.jpeg':
if (image_object.size / 1000) > 500: # if larger than 500 kb compress
if filetype == '.jpeg' or filetype == '.jpg':
return rescale_image_jpeg(image_object), filetype
if filetype == '.png':
return rescale_image_png(image_object), filetype
Expand Down
5 changes: 5 additions & 0 deletions cookbook/helper/recipe_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def search_recipes(request, queryset, params):
search_keywords = params.getlist('keywords', [])
search_foods = params.getlist('foods', [])
search_books = params.getlist('books', [])
search_steps = params.getlist('steps', [])
search_units = params.get('units', None)

# TODO I think default behavior should be 'AND' which is how most sites operate with facet/filters based on results
Expand Down Expand Up @@ -191,6 +192,10 @@ def search_recipes(request, queryset, params):
if search_units:
queryset = queryset.filter(steps__ingredients__unit__id=search_units)

# probably only useful in Unit list view, so keeping it simple
if search_steps:
queryset = queryset.filter(steps__id__in=search_steps)

if search_internal:
queryset = queryset.filter(internal=True)

Expand Down
12 changes: 12 additions & 0 deletions cookbook/helper/scope_middleware.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from django.urls import reverse
from django_scopes import scope, scopes_disabled
from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed

from cookbook.views import views

Expand Down Expand Up @@ -33,6 +36,15 @@ def __call__(self, request):
with scope(space=request.space):
return self.get_response(request)
else:
if request.path.startswith('/api/'):
try:
if auth := TokenAuthentication().authenticate(request):
request.space = auth[0].userpreference.space
with scope(space=request.space):
return self.get_response(request)
except AuthenticationFailed:
pass

with scopes_disabled():
request.space = None
return self.get_response(request)
9 changes: 4 additions & 5 deletions cookbook/integration/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from django_scopes import scope

from cookbook.forms import ImportExportBase
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.image_processing import get_filetype, handle_image
from cookbook.models import Keyword, Recipe
from recipes.settings import DATABASES, DEBUG

Expand Down Expand Up @@ -52,7 +52,7 @@ def __init__(self, request, export_type):
icon=icon,
space=request.space
)
except IntegrityError: # in case, for whatever reason, the name does exist append UUID to it. Not nice but works for now.
except IntegrityError: # in case, for whatever reason, the name does exist append UUID to it. Not nice but works for now.
self.keyword = parent.add_child(
name=f'{name} {str(uuid.uuid4())[0:8]}',
description=description,
Expand Down Expand Up @@ -229,15 +229,14 @@ def handle_duplicates(self, recipe, import_duplicates):
self.ignored_recipes.append(recipe.name)
recipe.delete()

@staticmethod
def import_recipe_image(recipe, image_file, filetype='.jpeg'):
def import_recipe_image(self, recipe, image_file, filetype='.jpeg'):
"""
Adds an image to a recipe naming it correctly
:param recipe: Recipe object
:param image_file: ByteIO stream containing the image
:param filetype: type of file to write bytes to, default to .jpeg if unknown
"""
recipe.image = File(image_file, name=f'{uuid.uuid4()}_{recipe.pk}{filetype}')
recipe.image = File(handle_image(self.request, File(image_file, name='image'), filetype=filetype)[0], name=f'{uuid.uuid4()}_{recipe.pk}{filetype}')
recipe.save()

def get_recipe_from_file(self, file):
Expand Down
2 changes: 1 addition & 1 deletion cookbook/integration/mealmaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def split_recipe_file(self, file):
current_recipe = ''

for fl in file.readlines():
line = fl.decode("ANSI")
line = fl.decode("windows-1250")
if (line.startswith('MMMMM') or line.startswith('-----')) and 'meal-master' in line.lower():
if current_recipe != '':
recipe_list.append(current_recipe)
Expand Down
35 changes: 30 additions & 5 deletions cookbook/integration/nextcloud_cookbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.recipe_url_import import iso_duration_to_minutes
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Ingredient
from cookbook.models import Recipe, Step, Ingredient, Keyword


class NextcloudCookbook(Integration):
Expand All @@ -24,9 +25,24 @@ def get_recipe_from_file(self, file):
created_by=self.request.user, internal=True,
servings=recipe_json['recipeYield'], space=self.request.space)

# TODO parse times (given in PT2H3M )
# @vabene check recipe_url_import.iso_duration_to_minutes I think it does what you are looking for
# TODO parse keywords
try:
recipe.working_time = iso_duration_to_minutes(recipe_json['prepTime'])
recipe.waiting_time = iso_duration_to_minutes(recipe_json['cookTime'])
except Exception:
pass

if 'recipeCategory' in recipe_json:
try:
recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=recipe_json['recipeCategory'])[0])
except Exception:
pass

if 'keywords' in recipe_json:
try:
for x in recipe_json['keywords'].split(','):
recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=x)[0])
except Exception:
pass

ingredients_added = False
for s in recipe_json['recipeInstructions']:
Expand All @@ -49,11 +65,20 @@ def get_recipe_from_file(self, file):
))
recipe.steps.add(step)

if 'nutrition' in recipe_json:
try:
recipe.nutrition.calories = recipe_json['nutrition']['calories'].replace(' kcal', '').replace(' ', '')
recipe.nutrition.proteins = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
recipe.nutrition.fats = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
recipe.nutrition.carbohydrates = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
except Exception:
pass

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'^{recipe.name}/full.jpg$', z.filename):
if re.match(f'^(.)+{recipe.name}/full.jpg$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))

return recipe
Expand Down
2 changes: 1 addition & 1 deletion cookbook/integration/plantoeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def split_recipe_file(self, file):
current_recipe = ''

for fl in file.readlines():
line = fl.decode("ANSI")
line = fl.decode("windows-1250")
if line.startswith('--------------'):
if current_recipe != '':
recipe_list.append(current_recipe)
Expand Down
12 changes: 4 additions & 8 deletions cookbook/locale/de/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
"PO-Revision-Date: 2021-11-07 17:14+0000\n"
"Last-Translator: Kaibu <[email protected]>\n"
"Language-Team: German <http://translate.tandoor.dev/projects/tandoor/recipes-"
"backend/de/>\n"
"PO-Revision-Date: 2021-11-12 20:06+0000\n"
"Last-Translator: A. L. <[email protected]>\n"
"Language-Team: German <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
Expand Down Expand Up @@ -261,10 +261,6 @@ msgid "Email address already taken!"
msgstr "Email-Adresse ist bereits vergeben!"

#: .\cookbook\forms.py:367
#, fuzzy
#| msgid ""
#| "An email address is not required but if present the invite link will be "
#| "send to the user."
msgid ""
"An email address is not required but if present the invite link will be sent "
"to the user."
Expand Down
Loading

0 comments on commit 69acca7

Please sign in to comment.