Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ingredient images #1001

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Developers
* absolutely-not-bot - https://github.com/absolutely-not-bot
* Jaspreet Dhillon - https://github.com/jaspreetsd902
* Sofiko Alaverdashvili - https://github.com/sophiamartelli
* Oebele Lijzenga - https://github.com/olijzenga

Translators
-----------
Expand Down
13 changes: 13 additions & 0 deletions wger/nutrition/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from django.shortcuts import get_object_or_404

# Third Party
from easy_thumbnails.alias import aliases
from easy_thumbnails.files import get_thumbnailer
from rest_framework import viewsets
from rest_framework.decorators import (
action,
Expand Down Expand Up @@ -167,11 +169,22 @@ def search(request):
)

for ingredient in ingredients:
if hasattr(ingredient, 'image'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we just do if ingredient.image: here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this but in that case RelatedObjectDoesNotExist is raised when the image is not there. The recommended way by Django to work around this is by using hasattr (according to https://docs.djangoproject.com/en/4.0/topics/db/examples/one_to_one/).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right

image_obj = ingredient.image
image = image_obj.image.url
t = get_thumbnailer(image_obj.image)
thumbnail = t.get_thumbnail(aliases.get('micro_cropped')).url
else:
image = None
thumbnail = None

ingredient_json = {
'value': ingredient.name,
'data': {
'id': ingredient.id,
'name': ingredient.name,
'image': image,
'image_thumbnail': thumbnail
}
}
results.append(ingredient_json)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 *-*

# This file is part of wger Workout Manager.
#
# wger Workout Manager 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.
#
# wger Workout Manager 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 General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License

# Standard Library
import datetime

# Django
from django.conf import settings
from django.contrib.sites.models import Site
from django.core import mail
from django.core.management.base import BaseCommand
from django.template import loader
from django.utils import translation
from django.utils.translation import gettext_lazy as _

# wger
from wger.nutrition.models import MealItem


class Command(BaseCommand):
"""
Download images of all Open Food Facts ingredients that are used in a nutrition plan
"""

help = 'Download images of all Open Food Facts ingredients that are used in a nutrition plan'

def handle(self, **options):
# Make sure off downloads are enabled
settings.WGER_SETTINGS['DOWNLOAD_FROM_OFF'] = True

# Since each MealItem is linked to a NutritionPlan via a Meal we can skip accessing
# NutritionPlan and Meal itself and fetch all MealItems directly instead.
meal_items = MealItem.objects.all()
meal_item_counter = 0
download_counter = 0
for meal_item in meal_items:
if meal_item.ingredient.fetch_image():
download_counter += 1
meal_item_counter += 1

if meal_item_counter % 10 == 0:
self.stdout.write(
f'Processed {meal_item_counter} meal items, '
f'downloaded {download_counter} images'
)


self.stdout.write(
f'Processed {meal_item_counter} meal items, downloaded {download_counter} images'
)
self.stdout.write(f'Done')
14 changes: 14 additions & 0 deletions wger/nutrition/migrations/0011_merge_20220406_2021.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 3.2.12 on 2022-04-06 18:21

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('nutrition', '0010_logitem_meal'),
('nutrition', '0010_merge_0009_auto_20210802_1526_0009_meal_name'),
]

operations = [
]
27 changes: 18 additions & 9 deletions wger/nutrition/models/ingredient.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,18 +446,13 @@ def off_link(self):
if self.source_name == Source.OPEN_FOOD_FACTS.value:
return f'https://world.openfoodfacts.org/product/{self.code}/'

def get_image(self, request: HttpRequest):
def fetch_image(self):
"""
Returns the ingredient image
Fetches the ingredient image from Open Food Facts servers if it is not available locally

If it is not available locally, it is fetched from Open Food Facts servers
Returns the image if it was fetched
"""
try:
return self.image
except Ingredient.image.RelatedObjectDoesNotExist:
pass

if not request.user.is_authenticated:
if hasattr(self, 'image'):
return

if self.source_name != Source.OPEN_FOOD_FACTS.value:
Expand Down Expand Up @@ -507,3 +502,17 @@ def get_image(self, request: HttpRequest):
'size': len(downloaded_image.content)
}
return Image.from_json(self, downloaded_image, image_data, headers, generate_uuid=True)

def get_image(self, request: HttpRequest):
"""
Returns the ingredient image

If it is not available locally, it is fetched from Open Food Facts servers
"""
if hasattr(self, 'image'):
return self.image

if not request.user.is_authenticated:
return

return self.fetch_image()
9 changes: 9 additions & 0 deletions wger/nutrition/tests/test_ingredient.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,16 @@ def search_ingredient(self, fail=True):
result = json.loads(response.content.decode('utf8'))
self.assertEqual(len(result['suggestions']), 2)
self.assertEqual(result['suggestions'][0]['value'], 'Ingredient, test, 2, organic, raw')
self.assertEqual(result['suggestions'][0]['data']['id'], 2)
suggestion_0_name = 'Ingredient, test, 2, organic, raw'
self.assertEqual(result['suggestions'][0]['data']['name'], suggestion_0_name)
self.assertEqual(result['suggestions'][0]['data']['image'], None)
self.assertEqual(result['suggestions'][0]['data']['image_thumbnail'], None)
self.assertEqual(result['suggestions'][1]['value'], 'Test ingredient 1')
self.assertEqual(result['suggestions'][1]['data']['id'], 1)
self.assertEqual(result['suggestions'][1]['data']['name'], 'Test ingredient 1')
self.assertEqual(result['suggestions'][1]['data']['image'], None)
self.assertEqual(result['suggestions'][1]['data']['image_thumbnail'], None)

# Search for an ingredient pending review (0 hits, "Pending ingredient")
response = self.client.get(reverse('ingredient-search'), {'term': 'Pending'}, **kwargs)
Expand Down