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

1import json 

2import re 

3from io import BytesIO, StringIO 

4from zipfile import ZipFile 

5 

6from PIL import Image 

7 

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 

13 

14 

15class NextcloudCookbook(Integration): 

16 

17 def import_file_name_filter(self, zip_info_object): 

18 return zip_info_object.filename.endswith('.json') 

19 

20 def get_recipe_from_file(self, file): 

21 recipe_json = json.loads(file.getvalue().decode("utf-8")) 

22 

23 description = '' if len(recipe_json['description'].strip()) > 500 else recipe_json['description'].strip() 

24 

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) 

29 

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 

35 

36 if 'url' in recipe_json: 

37 recipe.source_url = recipe_json['url'].strip() 

38 

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 

44 

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 

52 

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 

66 

67 ingredients_added = True 

68 

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) 

78 

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()) 

90 

91 if nutrition != {}: 

92 recipe.nutrition = NutritionInformation.objects.create(**nutrition, space=self.request.space) 

93 recipe.save() 

94 except Exception: 

95 pass 

96 

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)) 

103 

104 return recipe 

105 

106 def formatTime(self, min): 

107 h = min // 60 

108 m = min % 60 

109 return f'PT{h}H{m}M0S' 

110 

111 def get_file_from_recipe(self, recipe): 

112 

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' 

123 

124 recipeKeyword = [] 

125 for k in recipe.keywords.all(): 

126 recipeKeyword.append(k.name) 

127 

128 export['keywords'] = recipeKeyword 

129 

130 recipeInstructions = [] 

131 recipeIngredient = [] 

132 for s in recipe.steps.all(): 

133 recipeInstructions.append(s.instruction) 

134 

135 for i in s.ingredients.all(): 

136 recipeIngredient.append(f'{float(i.amount)} {i.unit} {i.food}') 

137 

138 export['recipeIngredient'] = recipeIngredient 

139 export['recipeInstructions'] = recipeInstructions 

140 

141 return "recipe.json", json.dumps(export) 

142 

143 def get_files_from_recipes(self, recipes, el, cookie): 

144 export_zip_stream = BytesIO() 

145 export_zip_obj = ZipFile(export_zip_stream, 'w') 

146 

147 for recipe in recipes: 

148 if recipe.internal and recipe.space == self.request.space: 

149 

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() 

155 

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 

163 

164 el.exported_recipes += 1 

165 el.msg += self.get_recipe_processed_msg(recipe) 

166 el.save() 

167 

168 export_zip_obj.close() 

169 

170 return [[self.get_export_file_name(), export_zip_stream.getvalue()]] 

171 

172 def getJPEG(self, imageByte): 

173 image = Image.open(BytesIO(imageByte)) 

174 image = image.convert('RGB') 

175 

176 bytes = BytesIO() 

177 image.save(bytes, "JPEG") 

178 return bytes.getvalue() 

179 

180 def getThumb(self, size, imageByte): 

181 image = Image.open(BytesIO(imageByte)) 

182 

183 w, h = image.size 

184 m = min(w, h) 

185 

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') 

189 

190 bytes = BytesIO() 

191 image.save(bytes, "JPEG") 

192 return bytes.getvalue()