Coverage for cookbook/integration/nextcloud_cookbook.py: 13%
141 statements
« prev ^ index » next coverage.py v7.4.0, created at 2023-12-29 00:47 +0100
« prev ^ index » next coverage.py v7.4.0, created at 2023-12-29 00:47 +0100
1import json
2import re
3from io import BytesIO, StringIO
4from zipfile import ZipFile
6from PIL import Image
8from cookbook.helper.image_processing import get_filetype
9from cookbook.helper.ingredient_parser import IngredientParser
10from cookbook.helper.recipe_url_import import iso_duration_to_minutes
11from cookbook.integration.integration import Integration
12from cookbook.models import Ingredient, Keyword, NutritionInformation, Recipe, Step
15class NextcloudCookbook(Integration):
17 def import_file_name_filter(self, zip_info_object):
18 return zip_info_object.filename.endswith('.json')
20 def get_recipe_from_file(self, file):
21 recipe_json = json.loads(file.getvalue().decode("utf-8"))
23 description = '' if len(recipe_json['description'].strip()) > 500 else recipe_json['description'].strip()
25 recipe = Recipe.objects.create(
26 name=recipe_json['name'].strip(), description=description,
27 created_by=self.request.user, internal=True,
28 servings=recipe_json['recipeYield'], space=self.request.space)
30 try:
31 recipe.working_time = iso_duration_to_minutes(recipe_json['prepTime'])
32 recipe.waiting_time = iso_duration_to_minutes(recipe_json['cookTime'])
33 except Exception:
34 pass
36 if 'url' in recipe_json:
37 recipe.source_url = recipe_json['url'].strip()
39 if 'recipeCategory' in recipe_json:
40 try:
41 recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=recipe_json['recipeCategory'])[0])
42 except Exception:
43 pass
45 if 'keywords' in recipe_json:
46 try:
47 for x in recipe_json['keywords'].split(','):
48 if x.strip() != '':
49 recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=x)[0])
50 except Exception:
51 pass
53 ingredients_added = False
54 for s in recipe_json['recipeInstructions']:
55 if 'text' in s:
56 step = Step.objects.create(
57 instruction=s['text'], name=s['name'], space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,
58 )
59 else:
60 step = Step.objects.create(
61 instruction=s, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,
62 )
63 if not ingredients_added:
64 if len(recipe_json['description'].strip()) > 500:
65 step.instruction = recipe_json['description'].strip() + '\n\n' + step.instruction
67 ingredients_added = True
69 ingredient_parser = IngredientParser(self.request, True)
70 for ingredient in recipe_json['recipeIngredient']:
71 amount, unit, food, note = ingredient_parser.parse(ingredient)
72 f = ingredient_parser.get_food(food)
73 u = ingredient_parser.get_unit(unit)
74 step.ingredients.add(Ingredient.objects.create(
75 food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space,
76 ))
77 recipe.steps.add(step)
79 if 'nutrition' in recipe_json:
80 nutrition = {}
81 try:
82 if 'calories' in recipe_json['nutrition']:
83 nutrition['calories'] = int(re.search(r'\d+', recipe_json['nutrition']['calories']).group())
84 if 'proteinContent' in recipe_json['nutrition']:
85 nutrition['proteins'] = int(re.search(r'\d+', recipe_json['nutrition']['proteinContent']).group())
86 if 'fatContent' in recipe_json['nutrition']:
87 nutrition['fats'] = int(re.search(r'\d+', recipe_json['nutrition']['fatContent']).group())
88 if 'carbohydrateContent' in recipe_json['nutrition']:
89 nutrition['carbohydrates'] = int(re.search(r'\d+', recipe_json['nutrition']['carbohydrateContent']).group())
91 if nutrition != {}:
92 recipe.nutrition = NutritionInformation.objects.create(**nutrition, space=self.request.space)
93 recipe.save()
94 except Exception:
95 pass
97 for f in self.files:
98 if '.zip' in f['name']:
99 import_zip = ZipFile(f['file'])
100 for z in import_zip.filelist:
101 if re.match(f'^(.)+{recipe.name}/full.jpg$', z.filename):
102 self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))
104 return recipe
106 def formatTime(self, min):
107 h = min // 60
108 m = min % 60
109 return f'PT{h}H{m}M0S'
111 def get_file_from_recipe(self, recipe):
113 export = {}
114 export['name'] = recipe.name
115 export['description'] = recipe.description
116 export['url'] = recipe.source_url
117 export['prepTime'] = self.formatTime(recipe.working_time)
118 export['cookTime'] = self.formatTime(recipe.waiting_time)
119 export['totalTime'] = self.formatTime(recipe.working_time + recipe.waiting_time)
120 export['recipeYield'] = recipe.servings
121 export['image'] = f'/Recipes/{recipe.name}/full.jpg'
122 export['imageUrl'] = f'/Recipes/{recipe.name}/full.jpg'
124 recipeKeyword = []
125 for k in recipe.keywords.all():
126 recipeKeyword.append(k.name)
128 export['keywords'] = recipeKeyword
130 recipeInstructions = []
131 recipeIngredient = []
132 for s in recipe.steps.all():
133 recipeInstructions.append(s.instruction)
135 for i in s.ingredients.all():
136 recipeIngredient.append(f'{float(i.amount)} {i.unit} {i.food}')
138 export['recipeIngredient'] = recipeIngredient
139 export['recipeInstructions'] = recipeInstructions
141 return "recipe.json", json.dumps(export)
143 def get_files_from_recipes(self, recipes, el, cookie):
144 export_zip_stream = BytesIO()
145 export_zip_obj = ZipFile(export_zip_stream, 'w')
147 for recipe in recipes:
148 if recipe.internal and recipe.space == self.request.space:
150 recipe_stream = StringIO()
151 filename, data = self.get_file_from_recipe(recipe)
152 recipe_stream.write(data)
153 export_zip_obj.writestr(f'{recipe.name}/{filename}', recipe_stream.getvalue())
154 recipe_stream.close()
156 try:
157 imageByte = recipe.image.file.read()
158 export_zip_obj.writestr(f'{recipe.name}/full.jpg', self.getJPEG(imageByte))
159 export_zip_obj.writestr(f'{recipe.name}/thumb.jpg', self.getThumb(171, imageByte))
160 export_zip_obj.writestr(f'{recipe.name}/thumb16.jpg', self.getThumb(16, imageByte))
161 except ValueError:
162 pass
164 el.exported_recipes += 1
165 el.msg += self.get_recipe_processed_msg(recipe)
166 el.save()
168 export_zip_obj.close()
170 return [[self.get_export_file_name(), export_zip_stream.getvalue()]]
172 def getJPEG(self, imageByte):
173 image = Image.open(BytesIO(imageByte))
174 image = image.convert('RGB')
176 bytes = BytesIO()
177 image.save(bytes, "JPEG")
178 return bytes.getvalue()
180 def getThumb(self, size, imageByte):
181 image = Image.open(BytesIO(imageByte))
183 w, h = image.size
184 m = min(w, h)
186 image = image.crop(((w - m) // 2, (h - m) // 2, (w + m) // 2, (h + m) // 2))
187 image = image.resize([size, size], Image.Resampling.LANCZOS)
188 image = image.convert('RGB')
190 bytes = BytesIO()
191 image.save(bytes, "JPEG")
192 return bytes.getvalue()