From 8dd615cdcf451231750479aacf3b14fff777cab0 Mon Sep 17 00:00:00 2001 From: flauschtrud Date: Fri, 13 Dec 2024 18:04:25 +0100 Subject: [PATCH 1/3] As a user I can add nutritional information to my recipes #187 --- .../3.json | 250 ++++++++++++++++++ .../CreateAndEditRecipeActivityTest.java | 3 + .../broccoli/util/RecipeTestUtil.java | 2 + .../broccoli/BroccoliDatabase.java | 5 +- .../broccoli/recipe/CoreRecipe.java | 10 + .../flauschcode/broccoli/recipe/Recipe.java | 8 + .../sharing/ShareableRecipeBuilder.java | 9 +- app/src/main/res/drawable/ic_fire_24dp.xml | 16 ++ .../main/res/layout/activity_new_recipe.xml | 16 ++ .../res/layout/content_recipe_details.xml | 23 ++ app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../sharing/ShareableRecipeBuilderTest.java | 5 + 14 files changed, 347 insertions(+), 3 deletions(-) create mode 100644 app/schemas/com.flauschcode.broccoli.BroccoliDatabase/3.json create mode 100644 app/src/main/res/drawable/ic_fire_24dp.xml diff --git a/app/schemas/com.flauschcode.broccoli.BroccoliDatabase/3.json b/app/schemas/com.flauschcode.broccoli.BroccoliDatabase/3.json new file mode 100644 index 00000000..d82691a7 --- /dev/null +++ b/app/schemas/com.flauschcode.broccoli.BroccoliDatabase/3.json @@ -0,0 +1,250 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "d7cb3f8620655a8a3eeb770e4e8fdf10", + "entities": [ + { + "tableName": "recipes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`recipeId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `imageName` TEXT, `description` TEXT, `servings` TEXT, `preparationTime` TEXT, `source` TEXT, `ingredients` TEXT, `directions` TEXT, `nutritionalValues` TEXT, `notes` TEXT, `favorite` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "recipeId", + "columnName": "recipeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "imageName", + "columnName": "imageName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "servings", + "columnName": "servings", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "preparationTime", + "columnName": "preparationTime", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ingredients", + "columnName": "ingredients", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directions", + "columnName": "directions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "nutritionalValues", + "columnName": "nutritionalValues", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "recipeId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT)", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "categoryId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recipes_with_categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`recipeId` INTEGER NOT NULL, `categoryId` INTEGER NOT NULL, PRIMARY KEY(`recipeId`, `categoryId`), FOREIGN KEY(`recipeId`) REFERENCES `recipes`(`recipeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`categoryId`) REFERENCES `categories`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "recipeId", + "columnName": "recipeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "recipeId", + "categoryId" + ] + }, + "indices": [ + { + "name": "index_recipes_with_categories_recipeId", + "unique": false, + "columnNames": [ + "recipeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_recipes_with_categories_recipeId` ON `${TABLE_NAME}` (`recipeId`)" + }, + { + "name": "index_recipes_with_categories_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_recipes_with_categories_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + } + ], + "foreignKeys": [ + { + "table": "recipes", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "recipeId" + ], + "referencedColumns": [ + "recipeId" + ] + }, + { + "table": "categories", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "ftsVersion": "FTS4", + "ftsOptions": { + "tokenizer": "unicode61", + "tokenizerArgs": [ + "tokenchars=#" + ], + "contentTable": "recipes", + "languageIdColumnName": "", + "matchInfo": "FTS4", + "notIndexedColumns": [], + "prefixSizes": [], + "preferredOrder": "ASC" + }, + "contentSyncTriggers": [ + "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_recipes_fts_BEFORE_UPDATE BEFORE UPDATE ON `recipes` BEGIN DELETE FROM `recipes_fts` WHERE `docid`=OLD.`rowid`; END", + "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_recipes_fts_BEFORE_DELETE BEFORE DELETE ON `recipes` BEGIN DELETE FROM `recipes_fts` WHERE `docid`=OLD.`rowid`; END", + "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_recipes_fts_AFTER_UPDATE AFTER UPDATE ON `recipes` BEGIN INSERT INTO `recipes_fts`(`docid`, `title`, `description`, `source`, `ingredients`) VALUES (NEW.`rowid`, NEW.`title`, NEW.`description`, NEW.`source`, NEW.`ingredients`); END", + "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_recipes_fts_AFTER_INSERT AFTER INSERT ON `recipes` BEGIN INSERT INTO `recipes_fts`(`docid`, `title`, `description`, `source`, `ingredients`) VALUES (NEW.`rowid`, NEW.`title`, NEW.`description`, NEW.`source`, NEW.`ingredients`); END" + ], + "tableName": "recipes_fts", + "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`title` TEXT, `description` TEXT, `source` TEXT, `ingredients` TEXT, tokenize=unicode61 `tokenchars=#`, content=`recipes`)", + "fields": [ + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ingredients", + "columnName": "ingredients", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd7cb3f8620655a8a3eeb770e4e8fdf10')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/flauschcode/broccoli/recipe/CreateAndEditRecipeActivityTest.java b/app/src/androidTest/java/com/flauschcode/broccoli/recipe/CreateAndEditRecipeActivityTest.java index 61a32ef9..d91282db 100644 --- a/app/src/androidTest/java/com/flauschcode/broccoli/recipe/CreateAndEditRecipeActivityTest.java +++ b/app/src/androidTest/java/com/flauschcode/broccoli/recipe/CreateAndEditRecipeActivityTest.java @@ -144,6 +144,7 @@ public void save_new_recipe() throws IOException { onView(withId(android.R.id.content)).perform(swipeUp()); // scrollTo() does not work for NestedScrollViews onView(withId(R.id.new_ingredients)).perform(closeSoftKeyboard(), typeText(LAUCHKUCHEN.getIngredients())); // it seems not to be possible to make Espresso type the enter key in a deterministic way onView(withId(R.id.new_directions)).perform(closeSoftKeyboard(), typeText(LAUCHKUCHEN.getDirections())); + onView(withId(R.id.new_nutritional_values)).perform(closeSoftKeyboard(), typeText(LAUCHKUCHEN.getNutritionalValues())); onView(withId(R.id.new_notes)).perform(closeSoftKeyboard(), typeText(LAUCHKUCHEN.getNotes())); onView(withId(R.id.button_save_recipe)).perform(click()); // TODO find out why there sometimes is such a long wait @@ -158,6 +159,7 @@ public void save_new_recipe() throws IOException { assertThat(recipe.getPreparationTime(), is(LAUCHKUCHEN.getPreparationTime())); assertThat(recipe.getIngredients(), is(LAUCHKUCHEN.getIngredients())); assertThat(recipe.getDirections(), is(LAUCHKUCHEN.getDirections())); + assertThat(recipe.getNutritionalValues(), is(LAUCHKUCHEN.getNutritionalValues())); assertThat(recipe.getNotes(), is(LAUCHKUCHEN.getNotes())); assertThat(recipe.getImageName(), startsWith("12345.jpg")); assertThat(recipe.getCategories().size(), is(1)); @@ -181,6 +183,7 @@ public void edit_recipe(){ onView(withId(R.id.new_preparation_time)).check(matches(withText(LAUCHKUCHEN_SAVED.getPreparationTime()))); onView(withId(R.id.new_ingredients)).check(matches(withText(LAUCHKUCHEN_SAVED.getIngredients()))); onView(withId(R.id.new_directions)).check(matches(withText(LAUCHKUCHEN_SAVED.getDirections()))); + onView(withId(R.id.new_nutritional_values)).check(matches(withText(LAUCHKUCHEN_SAVED.getNutritionalValues()))); onView(withId(R.id.new_notes)).check(matches(withText(LAUCHKUCHEN_SAVED.getNotes()))); onView(withId(R.id.new_servings)).perform(replaceText("1 Portion")); diff --git a/app/src/androidTest/java/com/flauschcode/broccoli/util/RecipeTestUtil.java b/app/src/androidTest/java/com/flauschcode/broccoli/util/RecipeTestUtil.java index d3d8934c..155de186 100644 --- a/app/src/androidTest/java/com/flauschcode/broccoli/util/RecipeTestUtil.java +++ b/app/src/androidTest/java/com/flauschcode/broccoli/util/RecipeTestUtil.java @@ -15,6 +15,7 @@ public static Recipe createLauchkuchen() { recipe.setPreparationTime("50 Minuten"); recipe.setIngredients("500g Mehl\n2 Stangen Lauch"); recipe.setDirections("1. Lauch schnippeln und Teig machen.\n2. Kochen und backen."); + recipe.setNutritionalValues("400kcal pro Portion"); recipe.setNotes("Ein paar Anmerkungen zum Lauchkuchen."); recipe.getCategories().add(new Category("Hauptgerichte")); recipe.getCategories().add(new Category("Gebackenes")); @@ -40,6 +41,7 @@ public static Recipe createNusskuchen() { recipe.setPreparationTime("1 Stunde"); recipe.setIngredients("500g Mehl\nviel Schokolade"); recipe.setDirections("1. Teig machen.\n2. Backen.\n 3. Schokolade dazu."); + recipe.setNutritionalValues("500 kcal pro Portion"); recipe.setNotes("Ein paar Anmerkungen zum Nusskuchen."); return recipe; } diff --git a/app/src/main/java/com/flauschcode/broccoli/BroccoliDatabase.java b/app/src/main/java/com/flauschcode/broccoli/BroccoliDatabase.java index 069f9b4c..30b8a895 100644 --- a/app/src/main/java/com/flauschcode/broccoli/BroccoliDatabase.java +++ b/app/src/main/java/com/flauschcode/broccoli/BroccoliDatabase.java @@ -12,7 +12,7 @@ import com.flauschcode.broccoli.recipe.RecipeDAO; @Database( - version = 2, + version = 3, entities = { CoreRecipe.class, Category.class, @@ -20,7 +20,8 @@ CoreRecipeFts.class }, autoMigrations = { - @AutoMigration(from = 1, to = 2) + @AutoMigration(from = 1, to = 2), + @AutoMigration(from = 2, to = 3) } ) public abstract class BroccoliDatabase extends RoomDatabase { diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/CoreRecipe.java b/app/src/main/java/com/flauschcode/broccoli/recipe/CoreRecipe.java index 7c72e739..74089337 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/CoreRecipe.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/CoreRecipe.java @@ -21,6 +21,8 @@ public class CoreRecipe implements Serializable { private String ingredients = ""; private String directions = ""; + private String nutritionalValues = ""; + private String notes = ""; private boolean favorite = false; @@ -97,6 +99,14 @@ public void setDirections(String directions) { this.directions = directions; } + public String getNutritionalValues() { + return nutritionalValues; + } + + public void setNutritionalValues(String nutritionalValues) { + this.nutritionalValues = nutritionalValues; + } + public String getNotes() { return notes; } diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/Recipe.java b/app/src/main/java/com/flauschcode/broccoli/recipe/Recipe.java index 8fdde7ce..6ab051c8 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/Recipe.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/Recipe.java @@ -137,6 +137,14 @@ public void setDirections(String directions) { this.coreRecipe.setDirections(directions); } + public String getNutritionalValues() { + return coreRecipe.getNutritionalValues(); + } + + public void setNutritionalValues(String nutritionalValues) { + this.coreRecipe.setNutritionalValues(nutritionalValues); + } + public String getNotes() { return coreRecipe.getNotes(); } diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java b/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java index 8f4deb0f..39f98a71 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java @@ -69,6 +69,10 @@ private String toPlainText(Recipe recipe) { stringBuilder.append("\n"); } + if(!"".equals(recipe.getNutritionalValues())) { + stringBuilder.append(getNutritionalValuesString()).append(":\n").append(recipe.getNutritionalValues()).append("\n\n"); + } + if(!"".equals(recipe.getNotes())) { stringBuilder.append(getNotesString()).append(":\n").append(recipe.getNotes()).append("\n\n"); } @@ -98,11 +102,14 @@ private String getDirectionsString() { return application.getString(R.string.directions); } + private String getNutritionalValuesString() { + return application.getString(R.string.nutritional_values); + } + private String getNotesString() { return application.getString(R.string.notes); } - private String getSharedWithString() { return application.getString(R.string.shared_with, application.getString(R.string.store_url)); } diff --git a/app/src/main/res/drawable/ic_fire_24dp.xml b/app/src/main/res/drawable/ic_fire_24dp.xml new file mode 100644 index 00000000..2ea0f568 --- /dev/null +++ b/app/src/main/res/drawable/ic_fire_24dp.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/app/src/main/res/layout/activity_new_recipe.xml b/app/src/main/res/layout/activity_new_recipe.xml index c68267b6..a9d67ad9 100644 --- a/app/src/main/res/layout/activity_new_recipe.xml +++ b/app/src/main/res/layout/activity_new_recipe.xml @@ -228,6 +228,22 @@ + + + + + + + + + + + + + + Zeit Zutaten Anleitung + Nährwerte Notizen Rezept-Foto Foto ändern diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ea5f8f6c..868ddedc 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -23,6 +23,7 @@ Tiempo Ingredientes Instrucciones + Valores nutricionales Notas Foto de la receta Cambiar foto diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2fcaf7ff..9a36d01f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,6 +28,7 @@ Time Ingredients Directions + Nutritional values Notes Recipe photo diff --git a/app/src/test/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilderTest.java b/app/src/test/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilderTest.java index 03c8d492..ba3c731a 100644 --- a/app/src/test/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilderTest.java +++ b/app/src/test/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilderTest.java @@ -51,6 +51,9 @@ public class ShareableRecipeBuilderTest { 1. Erst dies. 2. Dann das. + Nutritional values: + 400kcal pro Portion + Notes: Ein paar Anmerkungen zum Lauchkuchen. @@ -76,6 +79,7 @@ public void setUp() { when(application.getString(R.string.source)).thenReturn("Source"); when(application.getString(R.string.ingredients)).thenReturn("Ingredients"); when(application.getString(R.string.directions)).thenReturn("Directions"); + when(application.getString(R.string.nutritional_values)).thenReturn("Nutritional values"); when(application.getString(R.string.notes)).thenReturn("Notes"); when(application.getString(R.string.store_url)).thenReturn("BROCCOLI_URL"); when(application.getString(R.string.shared_with, "BROCCOLI_URL")).thenReturn("Shared with BROCCOLI_URL"); @@ -91,6 +95,7 @@ public void to_plain_text_full() { recipe.setSource("www.flauschhaus.org"); recipe.setIngredients("- 500g Mehl\n - 100g Margarine "); recipe.setDirections(" 1. Erst dies. \n 2. Dann das. "); + recipe.setNutritionalValues("400kcal pro Portion"); recipe.setNotes("Ein paar Anmerkungen zum Lauchkuchen."); recipe.setImageName("image/bla.jpg"); From c8e8145869adb1fba8eda092620495cf0302cdbb Mon Sep 17 00:00:00 2001 From: flauschtrud Date: Sat, 14 Dec 2024 17:02:28 +0100 Subject: [PATCH 2/3] As a user I can import recipes with their nutritional information #188 Also added user agent to Jsoup request. --- .../ImportableRecipeBuilderTest.java | 1036 +++++++++-------- .../importing/ImportableRecipeBuilder.java | 52 +- .../recipe/importing/RecipeImportService.java | 17 +- .../sharing/ShareableRecipeBuilder.java | 4 +- app/src/main/res/values-de/strings.xml | 5 + app/src/main/res/values-es/strings.xml | 7 +- app/src/main/res/values/strings.xml | 8 +- 7 files changed, 600 insertions(+), 529 deletions(-) diff --git a/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java b/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java index cfb9c764..a8a5feed 100644 --- a/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java +++ b/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java @@ -5,6 +5,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.when; +import android.app.Application; + import androidx.test.espresso.accessibility.AccessibilityChecks; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -32,513 +34,524 @@ @RunWith(AndroidJUnit4.class) public class ImportableRecipeBuilderTest { + @Inject + Application application; + @Inject RecipeImageService recipeImageService; private static final String URL_CHEFKOCH = "https://www.chefkoch.de/rezepte/3212051478029180/Vegane-Chocolate-Chip-Cookies.html"; - private static final String RECIPE_JSONLD_CHEFKOCH = " {\n" + - " \"@context\": \"http://schema.org\",\n" + - " \"@type\": \"Recipe\",\n" + - " \"image\": \"https://img.chefkoch-cdn.de/rezepte/3212051478029180/bilder/1325560/crop-960x540/vegane-chocolate-chip-cookies.jpg\",\n" + - " \"recipeCategory\": \"Vegan\",\n" + - " \"suitableForDiet\": \"Vegan\",\n" + - " \"recipeIngredient\": [\n" + - " \"20 g Chiasamen\", \"50 ml Wasser\", \"190 g Butterersatz oder Margarine, vegan\", \"200 g Zucker , braun, alternativ Rohrzucker\", \"2 TL Zuckerr\\u00fcbensirup , alternativ Melasse, Ahornsirup oder Agavendicksaft\", \"2 Pck. Vanillezucker\", \"300 g Weizenmehl oder Dinkelmehl, oder gemischt\", \"4 g Natron\", \"n. B. Salz\", \"200 g Blockschokolade , zartbitter oder Schokotr\\u00f6pfchen\" ],\n" + - " \"name\": \"Vegane Chocolate Chip Cookies\",\n" + - " \"description\": \"Vegane Chocolate Chip Cookies - au\\u00dfen kross, innen weich, lecker und vegan, ergibt 35 St\\u00fcck. \\u00dcber 110 Bewertungen und f\\u00fcr sehr lecker befunden. Mit \\u25ba Portionsrechner \\u25ba Kochbuch \\u25ba Video-Tipps!\",\n" + - " \"recipeInstructions\": \"Den Backofen auf 180 \\u00b0C Umluft vorheizen. Die Chiasamen und das Wasser in einer kleinen Sch\\u00fcssel vermengen und ca. 10 Minuten quellen lassen.\\n\\nEin Backblech mit Backpapier auslegen. Vegane Butter bzw. Margarine und Zucker mit den Schneebesen des R\\u00fchrger\\u00e4ts cremig verr\\u00fchren. Dann die gequollenen Chiasamen, den Zuckerr\\u00fcbensirup und beide P\\u00e4ckchen Vanillezucker dazugeben und weiter r\\u00fchren. Unter weiterem R\\u00fchren jetzt zuerst das Mehl hinzugeben und anschlie\\u00dfend Natron sowie Salz. Alternativ - oder falls der Teig zu z\\u00e4h ist - kann alles auch mit den H\\u00e4nden verknetet werden. Abschlie\\u00dfend die Schokotr\\u00f6pfchen bzw. die gehackte Blockschokolade untermischen.\\n\\nDen nun fertigen Teig mit einem Essl\\u00f6ffel oder Eisportionierer klecksweise im Abstand von etwa 5 - 6 cm auf das Backpapier geben. Die Teigkleckse k\\u00f6nnen - m\\u00fcssen jedoch nicht - mit einem L\\u00f6ffel noch etwas rund geformt und flach gedr\\u00fcckt werden.\\n\\nDie Cookies bei 180 \\u00b0C Umluft maximal 15 Minuten backen, da sie sonst zu fest werden.\",\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"Esslust\"\n" + - " },\n" + - " \"publisher\": {\n" + - " \"@type\": \"Organization\",\n" + - " \"name\": \"Chefkoch.de\"\n" + - " },\n" + - " \"datePublished\": \"2016-11-03\",\n" + - " \"prepTime\": \"P0DT0H20M\",\n" + - " \"cookTime\": \"P0DT0H15M\",\n" + - " \"totalTime\": \"P0DT0H45M\"\n" + - " ,\n" + - " \"recipeYield\": \"1 Portion(en)\"\n" + - " ,\n" + - " \"aggregateRating\": {\n" + - " \"@type\": \"AggregateRating\",\n" + - " \"ratingCount\": 110,\n" + - " \"ratingValue\": 4.77,\n" + - " \"reviewCount\": 82,\n" + - " \"worstRating\": 0,\n" + - " \"bestRating\": 5\n" + - " }\n" + - " ,\n" + - " \"video\": [{\n" + - " \"@type\": \"VideoObject\",\n" + - " \"name\": \"Video zu Vegane Chocolate Chip Cookies\",\n" + - " \"description\": \"Video zu Vegane Chocolate Chip Cookies\",\n" + - " \"thumbnailUrl\": \"https://api.chefkoch.de/v2/images/crop-960x540/videos/2873/preview/org\",\n" + - " \"contentUrl\": \"https://www.chefkoch.de/rezepte/3212051478029180/Vegane-Chocolate-Chip-Cookies.html\",\n" + - " \"embedUrl\": \"https://video.chefkoch-cdn.de/ck.de/videos/2873-video.mp4\",\n" + - " \"uploadDate\": \"2016-11-03\"\n" + - " }]\n" + - " ,\n" + - " \"nutrition\": {\n" + - " \"@type\": \"NutritionInformation\",\n" + - " \"servingSize\": \"1\",\n" + - " \"calories\": \"4594 kcal\",\n" + - " \"proteinContent\": \"77,59g\",\n" + - " \"fatContent\": \"225,86g\",\n" + - " \"carbohydrateContent\": \"540,65g\"\n" + - " }\n" + - " ,\n" + - " \"keywords\": [\"Backen\",\"Vegetarisch\",\"einfach\",\"Kekse\",\"Gluten\",\"Lactose\"]\n" + - " ,\n" + - " \"reviews\": [\n" + - " {\n" + - " \"@type\": \"Review\",\n" + - " \"reviewBody\": \"Super \\ud83d\\udc4d\\ud83c\\udffb lecker\\ud83d\\ude0b \\nFoto ist schon unterwegs \\ud83e\\udd70\",\n" + - " \"datePublished\": \"2020-09-15\"\n" + - " ,\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"Neoncupcake\"\n" + - " }\n" + - " }, {\n" + - " \"@type\": \"Review\",\n" + - " \"reviewBody\": \"Mega gut! Ich hab 50g braunen Zucker und 100g Birkenzucker verwendet und sie bei 170\\u00b0C ca 12-13min gebacken. N\\u00e4chstes Mal mache ich eine Variante mit Kakao im Teig damit es so einen richtig schweren Schokogeschmack bekommt \\ud83d\\ude07\",\n" + - " \"datePublished\": \"2020-06-08\"\n" + - " ,\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"RoxPox\"\n" + - " }\n" + - " }, {\n" + - " \"@type\": \"Review\",\n" + - " \"reviewBody\": \"Super lecker, ich habe da die Chiasamen allerdings mit Flosamen ausgetauscht, da ich das da hatte. Habe daf\\u00fcr etwas mehr Wasser f\\u00fcr das Quellverhalten gegeben (80 ml). Hat aber auch klasse funktioniert und die Cookies sind sehr fein geworden. Bei ein paar habe ich auch ein paar Haferflocken untergemischt :)\",\n" + - " \"datePublished\": \"2020-06-04\"\n" + - " ,\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"alenalue\"\n" + - " }\n" + - " }, {\n" + - " \"@type\": \"Review\",\n" + - " \"reviewBody\": \"Genial!!! Die besten Cookies die ich je gegessen habe. Ich habe einen Test mit 3 verschiedenen Cookie Rezepten durchgef\\u00fchrt und dieses hat mit Abstand gewonnen. Wurde auch von all meinen nicht Veganer Freunden dankend angenommen. Schmeckt auch super mit Vollkorn- oder Dinkelmehl. LG\",\n" + - " \"datePublished\": \"2020-05-29\"\n" + - " ,\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"Mrs Sommer\"\n" + - " }\n" + - " }, {\n" + - " \"@type\": \"Review\",\n" + - " \"reviewBody\": \"Geschmacklich sind die 1a! Aber Chiasamen als Eiersatz st\\u00f6ren mich immer, weil sie beim Essen unangenehm an den Z\\u00e4hnen kleben bleiben. Ich denke n\\u00e4chstes mal probiere ich es mit gemahlenen Leinsamen.\",\n" + - " \"datePublished\": \"2020-05-18\"\n" + - " ,\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"electricblueberry\"\n" + - " }\n" + - " } ]\n" + - " }\n" + - "\n"; - - private static final String MINIMIZED_RECIPE_JSONLD = " {\n" + - " \"@context\": \"http://schema.org\",\n" + - " \"@type\": \"Recipe\",\n" + - " \"recipeCategory\": \"Vegan\",\n" + - " \"suitableForDiet\": \"Vegan\",\n" + - " \"name\": \"Vegane Chocolate Chip Cookies\"\n" + - " }\n" + - "\n"; + private static final String RECIPE_JSONLD_CHEFKOCH = """ + { + "@context": "http://schema.org", + "@type": "Recipe", + "image": "https://img.chefkoch-cdn.de/rezepte/3212051478029180/bilder/1325560/crop-960x540/vegane-chocolate-chip-cookies.jpg", + "recipeCategory": "Vegan", + "suitableForDiet": "Vegan", + "recipeIngredient": [ + "20 g Chiasamen", "50 ml Wasser", "190 g Butterersatz oder Margarine, vegan", "200 g Zucker , braun, alternativ Rohrzucker", "2 TL Zuckerr\\u00fcbensirup , alternativ Melasse, Ahornsirup oder Agavendicksaft", "2 Pck. Vanillezucker", "300 g Weizenmehl oder Dinkelmehl, oder gemischt", "4 g Natron", "n. B. Salz", "200 g Blockschokolade , zartbitter oder Schokotr\\u00f6pfchen" ], + "name": "Vegane Chocolate Chip Cookies", + "description": "Vegane Chocolate Chip Cookies - au\\u00dfen kross, innen weich, lecker und vegan, ergibt 35 St\\u00fcck. \\u00dcber 110 Bewertungen und f\\u00fcr sehr lecker befunden. Mit \\u25ba Portionsrechner \\u25ba Kochbuch \\u25ba Video-Tipps!", + "recipeInstructions": "Den Backofen auf 180 \\u00b0C Umluft vorheizen. Die Chiasamen und das Wasser in einer kleinen Sch\\u00fcssel vermengen und ca. 10 Minuten quellen lassen.\\n\\nEin Backblech mit Backpapier auslegen. Vegane Butter bzw. Margarine und Zucker mit den Schneebesen des R\\u00fchrger\\u00e4ts cremig verr\\u00fchren. Dann die gequollenen Chiasamen, den Zuckerr\\u00fcbensirup und beide P\\u00e4ckchen Vanillezucker dazugeben und weiter r\\u00fchren. Unter weiterem R\\u00fchren jetzt zuerst das Mehl hinzugeben und anschlie\\u00dfend Natron sowie Salz. Alternativ - oder falls der Teig zu z\\u00e4h ist - kann alles auch mit den H\\u00e4nden verknetet werden. Abschlie\\u00dfend die Schokotr\\u00f6pfchen bzw. die gehackte Blockschokolade untermischen.\\n\\nDen nun fertigen Teig mit einem Essl\\u00f6ffel oder Eisportionierer klecksweise im Abstand von etwa 5 - 6 cm auf das Backpapier geben. Die Teigkleckse k\\u00f6nnen - m\\u00fcssen jedoch nicht - mit einem L\\u00f6ffel noch etwas rund geformt und flach gedr\\u00fcckt werden.\\n\\nDie Cookies bei 180 \\u00b0C Umluft maximal 15 Minuten backen, da sie sonst zu fest werden.", + "author": { + "@type": "Person", + "name": "Esslust" + }, + "publisher": { + "@type": "Organization", + "name": "Chefkoch.de" + }, + "datePublished": "2016-11-03", + "prepTime": "P0DT0H20M", + "cookTime": "P0DT0H15M", + "totalTime": "P0DT0H45M" + , + "recipeYield": "1 Portion(en)" + , + "aggregateRating": { + "@type": "AggregateRating", + "ratingCount": 110, + "ratingValue": 4.77, + "reviewCount": 82, + "worstRating": 0, + "bestRating": 5 + } + , + "video": [{ + "@type": "VideoObject", + "name": "Video zu Vegane Chocolate Chip Cookies", + "description": "Video zu Vegane Chocolate Chip Cookies", + "thumbnailUrl": "https://api.chefkoch.de/v2/images/crop-960x540/videos/2873/preview/org", + "contentUrl": "https://www.chefkoch.de/rezepte/3212051478029180/Vegane-Chocolate-Chip-Cookies.html", + "embedUrl": "https://video.chefkoch-cdn.de/ck.de/videos/2873-video.mp4", + "uploadDate": "2016-11-03" + }] + , + "nutrition": { + "@type": "NutritionInformation", + "servingSize": "1", + "calories": "4594 kcal", + "proteinContent": "77,59g", + "fatContent": "225,86g", + "carbohydrateContent": "540,65g" + } + , + "keywords": ["Backen","Vegetarisch","einfach","Kekse","Gluten","Lactose"] + , + "reviews": [ + { + "@type": "Review", + "reviewBody": "Super \\ud83d\\udc4d\\ud83c\\udffb lecker\\ud83d\\ude0b \\nFoto ist schon unterwegs \\ud83e\\udd70", + "datePublished": "2020-09-15" + , + "author": { + "@type": "Person", + "name": "Neoncupcake" + } + }, { + "@type": "Review", + "reviewBody": "Mega gut! Ich hab 50g braunen Zucker und 100g Birkenzucker verwendet und sie bei 170\\u00b0C ca 12-13min gebacken. N\\u00e4chstes Mal mache ich eine Variante mit Kakao im Teig damit es so einen richtig schweren Schokogeschmack bekommt \\ud83d\\ude07", + "datePublished": "2020-06-08" + , + "author": { + "@type": "Person", + "name": "RoxPox" + } + }, { + "@type": "Review", + "reviewBody": "Super lecker, ich habe da die Chiasamen allerdings mit Flosamen ausgetauscht, da ich das da hatte. Habe daf\\u00fcr etwas mehr Wasser f\\u00fcr das Quellverhalten gegeben (80 ml). Hat aber auch klasse funktioniert und die Cookies sind sehr fein geworden. Bei ein paar habe ich auch ein paar Haferflocken untergemischt :)", + "datePublished": "2020-06-04" + , + "author": { + "@type": "Person", + "name": "alenalue" + } + }, { + "@type": "Review", + "reviewBody": "Genial!!! Die besten Cookies die ich je gegessen habe. Ich habe einen Test mit 3 verschiedenen Cookie Rezepten durchgef\\u00fchrt und dieses hat mit Abstand gewonnen. Wurde auch von all meinen nicht Veganer Freunden dankend angenommen. Schmeckt auch super mit Vollkorn- oder Dinkelmehl. LG", + "datePublished": "2020-05-29" + , + "author": { + "@type": "Person", + "name": "Mrs Sommer" + } + }, { + "@type": "Review", + "reviewBody": "Geschmacklich sind die 1a! Aber Chiasamen als Eiersatz st\\u00f6ren mich immer, weil sie beim Essen unangenehm an den Z\\u00e4hnen kleben bleiben. Ich denke n\\u00e4chstes mal probiere ich es mit gemahlenen Leinsamen.", + "datePublished": "2020-05-18" + , + "author": { + "@type": "Person", + "name": "electricblueberry" + } + } ] + } + """; + + private static final String MINIMIZED_RECIPE_JSONLD = """ + { + "@context": "http://schema.org", + "@type": "Recipe", + "recipeCategory": "Vegan", + "suitableForDiet": "Vegan", + "name": "Vegane Chocolate Chip Cookies" + } + """; private static final String URL_YOAST = "https://stilettosandsprouts.de/vegane-fenchel-pasta/"; - private static final String RECIPE_JSON_LD_YOAST = "{\n" + - " \"@context\": \"http://schema.org/\",\n" + - " \"@type\": \"Recipe\",\n" + - " \"name\": \"Vegane Fenchel-Pasta\",\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"Katja\"\n" + - " },\n" + - " \"description\": \"Pasta mit geröstetem Fenchel und Zitrone – ein herrlich leichtes Pastagericht, in nur 10 Minuten fertig zubereitet.\",\n" + - " \"datePublished\": \"2018-05-15T07:00:48+00:00\",\n" + - " \"image\": [\n" + - " \"https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B.jpg\",\n" + - " \"https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B-500x500.jpg\",\n" + - " \"https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B-500x375.jpg\",\n" + - " \"https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B-480x270.jpg\"\n" + - " ],\n" + - " \"recipeYield\": [\n" + - " \"2\",\n" + - " \"2 Personen\"\n" + - " ],\n" + - " \"cookTime\": \"PT10M\",\n" + - " \"recipeIngredient\": [\n" + - " \"1 Knolle Fenchel\",\n" + - " \"1 Bio-Zitrone\",\n" + - " \"1 Knoblauchzehe, geschält und fein gehackt\",\n" + - " \"1 Schalotte, geschält und fein gehackt\",\n" + - " \"1 Handvoll Petersilie, frisch, fein gehackt\",\n" + - " \"5 EL Semmelbrösel\",\n" + - " \"Olivenöl\",\n" + - " \"Salz & Pfeffer\",\n" + - " \"250 g Pasta (z.B. Linguini)\"\n" + - " ],\n" + - " \"recipeInstructions\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Vom Fenchel die oberen grünen Stängel entfernen. Dabei unbedingt das Fenchelgrün aufbewahren. Das kommt später an die Pasta ran. Die Knolle halbieren, den Strunk in der Mitte keilförmig entfernen und nun die zwei Hälften in dünne Streifen schneiden. Die Fenchel-Streifen waschen und gut abtrocknen.\",\n" + - " \"name\": \"Vom Fenchel die oberen grünen Stängel entfernen. Dabei unbedingt das Fenchelgrün aufbewahren. Das kommt später an die Pasta ran. Die Knolle halbieren, den Strunk in der Mitte keilförmig entfernen und nun die zwei Hälften in dünne Streifen schneiden. Die Fenchel-Streifen waschen und gut abtrocknen.\",\n" + - " \"url\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-0\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Die Pasta nach Packungsanweisung garen.\",\n" + - " \"name\": \"Die Pasta nach Packungsanweisung garen.\",\n" + - " \"url\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-1\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"In der Zwischenzeit in einer großen Pfanne Olivenöl erhitzen und den Fenchel darin mit etwas Salz ca. 8 Minuten lang anrösten.\",\n" + - " \"name\": \"In der Zwischenzeit in einer großen Pfanne Olivenöl erhitzen und den Fenchel darin mit etwas Salz ca. 8 Minuten lang anrösten.\",\n" + - " \"url\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-2\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"In einer kleinen Pfanne etwas Olivenöl erhitzen, Knoblauch- und Schalottenwürfel darin mit etwas Salz glasig andünsten. Zitronenabrieb, Petersilie und die Semmelbrösel hinzugeben und alles ca. 4 Minuten vorsichtig anrösten bis die Semmelbrösel leicht angebräunt sind. Die Mischung vom Herd nehmen.\",\n" + - " \"name\": \"In einer kleinen Pfanne etwas Olivenöl erhitzen, Knoblauch- und Schalottenwürfel darin mit etwas Salz glasig andünsten. Zitronenabrieb, Petersilie und die Semmelbrösel hinzugeben und alles ca. 4 Minuten vorsichtig anrösten bis die Semmelbrösel leicht angebräunt sind. Die Mischung vom Herd nehmen.\",\n" + - " \"url\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-3\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Die fertige Pasta abgießen. Dabei etwa ein halbes Wasserglas der Kochflüssigkeit auffangen. Abgetropfte Pasta zum Fenchel geben. Die Semmelbröselmischung dazugeben. Kochflüssigkeit nach Belieben dazugeben, damit die Pasta schön glänzend wird. Pasta ordentlich salzen und pfeffern und nach Geschmack Zitrone hinzugeben. Den Saft einer halben Zitrone verträgt das Gericht mindestens. Mit einem guten Schuss Olivenöl und mit Fenchelgrün bestreut servieren.\",\n" + - " \"name\": \"Die fertige Pasta abgießen. Dabei etwa ein halbes Wasserglas der Kochflüssigkeit auffangen. Abgetropfte Pasta zum Fenchel geben. Die Semmelbröselmischung dazugeben. Kochflüssigkeit nach Belieben dazugeben, damit die Pasta schön glänzend wird. Pasta ordentlich salzen und pfeffern und nach Geschmack Zitrone hinzugeben. Den Saft einer halben Zitrone verträgt das Gericht mindestens. Mit einem guten Schuss Olivenöl und mit Fenchelgrün bestreut servieren.\",\n" + - " \"url\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-4\"\n" + - " }\n" + - " ],\n" + - " \"aggregateRating\": {\n" + - " \"@type\": \"AggregateRating\",\n" + - " \"ratingValue\": \"5\",\n" + - " \"ratingCount\": \"4\"\n" + - " },\n" + - " \"@id\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#recipe\",\n" + - " \"isPartOf\": {\n" + - " \"@id\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#webpage\"\n" + - " },\n" + - " \"mainEntityOfPage\": \"https://stilettosandsprouts.de/vegane-fenchel-pasta/#webpage\"\n" + - "}"; + private static final String RECIPE_JSON_LD_YOAST = """ + { + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "Vegane Fenchel-Pasta", + "author": { + "@type": "Person", + "name": "Katja" + }, + "description": "Pasta mit geröstetem Fenchel und Zitrone – ein herrlich leichtes Pastagericht, in nur 10 Minuten fertig zubereitet.", + "datePublished": "2018-05-15T07:00:48+00:00", + "image": [ + "https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B.jpg", + "https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B-500x500.jpg", + "https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B-500x375.jpg", + "https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B-480x270.jpg" + ], + "recipeYield": [ + "2", + "2 Personen" + ], + "cookTime": "PT10M", + "recipeIngredient": [ + "1 Knolle Fenchel", + "1 Bio-Zitrone", + "1 Knoblauchzehe, geschält und fein gehackt", + "1 Schalotte, geschält und fein gehackt", + "1 Handvoll Petersilie, frisch, fein gehackt", + "5 EL Semmelbrösel", + "Olivenöl", + "Salz & Pfeffer", + "250 g Pasta (z.B. Linguini)" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Vom Fenchel die oberen grünen Stängel entfernen. Dabei unbedingt das Fenchelgrün aufbewahren. Das kommt später an die Pasta ran. Die Knolle halbieren, den Strunk in der Mitte keilförmig entfernen und nun die zwei Hälften in dünne Streifen schneiden. Die Fenchel-Streifen waschen und gut abtrocknen.", + "name": "Vom Fenchel die oberen grünen Stängel entfernen. Dabei unbedingt das Fenchelgrün aufbewahren. Das kommt später an die Pasta ran. Die Knolle halbieren, den Strunk in der Mitte keilförmig entfernen und nun die zwei Hälften in dünne Streifen schneiden. Die Fenchel-Streifen waschen und gut abtrocknen.", + "url": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-0" + }, + { + "@type": "HowToStep", + "text": "Die Pasta nach Packungsanweisung garen.", + "name": "Die Pasta nach Packungsanweisung garen.", + "url": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-1" + }, + { + "@type": "HowToStep", + "text": "In der Zwischenzeit in einer großen Pfanne Olivenöl erhitzen und den Fenchel darin mit etwas Salz ca. 8 Minuten lang anrösten.", + "name": "In der Zwischenzeit in einer großen Pfanne Olivenöl erhitzen und den Fenchel darin mit etwas Salz ca. 8 Minuten lang anrösten.", + "url": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-2" + }, + { + "@type": "HowToStep", + "text": "In einer kleinen Pfanne etwas Olivenöl erhitzen, Knoblauch- und Schalottenwürfel darin mit etwas Salz glasig andünsten. Zitronenabrieb, Petersilie und die Semmelbrösel hinzugeben und alles ca. 4 Minuten vorsichtig anrösten bis die Semmelbrösel leicht angebräunt sind. Die Mischung vom Herd nehmen.", + "name": "In einer kleinen Pfanne etwas Olivenöl erhitzen, Knoblauch- und Schalottenwürfel darin mit etwas Salz glasig andünsten. Zitronenabrieb, Petersilie und die Semmelbrösel hinzugeben und alles ca. 4 Minuten vorsichtig anrösten bis die Semmelbrösel leicht angebräunt sind. Die Mischung vom Herd nehmen.", + "url": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-3" + }, + { + "@type": "HowToStep", + "text": "Die fertige Pasta abgießen. Dabei etwa ein halbes Wasserglas der Kochflüssigkeit auffangen. Abgetropfte Pasta zum Fenchel geben. Die Semmelbröselmischung dazugeben. Kochflüssigkeit nach Belieben dazugeben, damit die Pasta schön glänzend wird. Pasta ordentlich salzen und pfeffern und nach Geschmack Zitrone hinzugeben. Den Saft einer halben Zitrone verträgt das Gericht mindestens. Mit einem guten Schuss Olivenöl und mit Fenchelgrün bestreut servieren.", + "name": "Die fertige Pasta abgießen. Dabei etwa ein halbes Wasserglas der Kochflüssigkeit auffangen. Abgetropfte Pasta zum Fenchel geben. Die Semmelbröselmischung dazugeben. Kochflüssigkeit nach Belieben dazugeben, damit die Pasta schön glänzend wird. Pasta ordentlich salzen und pfeffern und nach Geschmack Zitrone hinzugeben. Den Saft einer halben Zitrone verträgt das Gericht mindestens. Mit einem guten Schuss Olivenöl und mit Fenchelgrün bestreut servieren.", + "url": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#wprm-recipe-6504-step-0-4" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "5", + "ratingCount": "4" + }, + "@id": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#recipe", + "isPartOf": { + "@id": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#webpage" + }, + "mainEntityOfPage": "https://stilettosandsprouts.de/vegane-fenchel-pasta/#webpage" + } + """; private static final String URL_YOAST_WITH_SECTIONS = "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/"; - private static final String RECIPE_YOAST_WITH_SECTIONS = "{\n" + - " \"@context\": \"http://schema.org/\",\n" + - " \"@type\": \"Recipe\",\n" + - " \"name\": \"Saftiger Veganer Zitronenkuchen\",\n" + - " \"author\": {\n" + - " \"@type\": \"Person\",\n" + - " \"name\": \"Karen Wilkening\"\n" + - " },\n" + - " \"description\": \"▶ Super saftiger veganer Zitronenkuchen ▶ Einfaches, sehr leckeres Rezept ▶ Kommt immer gut an▶ Für 30 cm Kastenbackform.\",\n" + - " \"datePublished\": \"2019-07-01T20:18:20+00:00\",\n" + - " \"image\": [\n" + - " \"https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig.jpg\",\n" + - " \"https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig-500x500.jpg\",\n" + - " \"https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig-500x375.jpg\",\n" + - " \"https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig-480x270.jpg\"\n" + - " ],\n" + - " \"recipeYield\": [\n" + - " \"1\",\n" + - " \"1 Kuchen\"\n" + - " ],\n" + - " \"totalTime\": \"PT75M\",\n" + - " \"recipeIngredient\": [\n" + - " \"300 g Mehl\",\n" + - " \"200 g Zucker\",\n" + - " \"1 Tüte Backpulver\",\n" + - " \"1 Tüte Vanillezucker\",\n" + - " \"250 ml Vanille-Sojamilch\",\n" + - " \"100 g Öl (Rapsöl oder Sonnenblumenöl)\",\n" + - " \"50 ml Zitronensaft\",\n" + - " \"geriebene Zitronenschale\",\n" + - " \"evtl. Backaroma Zitrone\",\n" + - " \"150 g Puderzucker\",\n" + - " \"2 EL Zitronensaft\"\n" + - " ],\n" + - " \"recipeInstructions\": [\n" + - " {\n" + - " \"@type\": \"HowToSection\",\n" + - " \"name\": \"Vorbereiten:\",\n" + - " \"itemListElement\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Kastenbackform mit Backpapier auslegen oder einfetten.\",\n" + - " \"name\": \"Kastenbackform mit Backpapier auslegen oder einfetten.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-0-0\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"2 Zitronen abreiben und auspressen.\",\n" + - " \"name\": \"2 Zitronen abreiben und auspressen.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-0-1\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Backofen vorheizen auf 180 °C (Ober-Unter-Hitze).\",\n" + - " \"name\": \"Backofen vorheizen auf 180 °C (Ober-Unter-Hitze).\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-0-2\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToSection\",\n" + - " \"name\": \"Trockene Zutaten mischen:\",\n" + - " \"itemListElement\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"In eine Schüssel geben:300 g Mehl 200 g Zucker1 Tüte Backpulver1 Tüte VanillezuckerAbgeriebene ZitronenschaleGut mischen.\",\n" + - " \"name\": \"In eine Schüssel geben:300 g Mehl 200 g Zucker1 Tüte Backpulver1 Tüte VanillezuckerAbgeriebene ZitronenschaleGut mischen.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-1-0\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToSection\",\n" + - " \"name\": \"Feuchte Zutaten zufügen:\",\n" + - " \"itemListElement\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"250 ml Sojamilch Vanille100 g Öl50 ml Zitronensaft\",\n" + - " \"name\": \"250 ml Sojamilch Vanille100 g Öl50 ml Zitronensaft\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-2-0\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Evtl. weiteres Zitronenaroma zufügen(Backaroma oder Zitronenöl)\",\n" + - " \"name\": \"Evtl. weiteres Zitronenaroma zufügen(Backaroma oder Zitronenöl)\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-2-1\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToSection\",\n" + - " \"name\": \"Teig mixen und in Backform füllen:\",\n" + - " \"itemListElement\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Kurz gut durchmixen.\",\n" + - " \"name\": \"Kurz gut durchmixen.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-3-0\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"In die vorbereitete Kastenbackform füllen.\",\n" + - " \"name\": \"In die vorbereitete Kastenbackform füllen.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-3-1\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToSection\",\n" + - " \"name\": \"Backen:\",\n" + - " \"itemListElement\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Im vorgeheizten Ofen bei 180 °C (Ober-Unter-Hitze) 60 Minuten backen.(Stäbchenprobe)\",\n" + - " \"name\": \"Im vorgeheizten Ofen bei 180 °C (Ober-Unter-Hitze) 60 Minuten backen.(Stäbchenprobe)\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-4-0\"\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"Kuchen in der Form abkühlen lassen.\",\n" + - " \"name\": \"Kuchen in der Form abkühlen lassen.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-4-1\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"@type\": \"HowToSection\",\n" + - " \"name\": \"Zitronenglasur:\",\n" + - " \"itemListElement\": [\n" + - " {\n" + - " \"@type\": \"HowToStep\",\n" + - " \"text\": \"150 g Puderzucker sieben.Mit 2 EL Zitronensaft verrühren.Auf dem Kuchen verteilen.\",\n" + - " \"name\": \"150 g Puderzucker sieben.Mit 2 EL Zitronensaft verrühren.Auf dem Kuchen verteilen.\",\n" + - " \"url\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-5-0\"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"aggregateRating\": {\n" + - " \"@type\": \"AggregateRating\",\n" + - " \"ratingValue\": \"4.97\",\n" + - " \"ratingCount\": \"29\"\n" + - " },\n" + - " \"recipeCategory\": [\n" + - " \"Backen\"\n" + - " ],\n" + - " \"recipeCuisine\": [\n" + - " \"vegan\"\n" + - " ],\n" + - " \"suitableForDiet\": [\n" + - " \"https://schema.org/LowLactoseDiet\",\n" + - " \"https://schema.org/VeganDiet\"\n" + - " ],\n" + - " \"keywords\": \"einfach, mit öl, ohne butter, ohne ei, ohne milch, super saftig\",\n" + - " \"nutrition\": {\n" + - " \"@type\": \"NutritionInformation\",\n" + - " \"servingSize\": \"1 Stück (bei 12 Stücken)\",\n" + - " \"calories\": \"292 kcal\",\n" + - " \"carbohydrateContent\": \"49 g\",\n" + - " \"proteinContent\": \"3.8 g\",\n" + - " \"fatContent\": \"8.4 g\",\n" + - " \"saturatedFatContent\": \"0.75 g\",\n" + - " \"sugarContent\": \"31 g\"\n" + - " },\n" + - " \"@id\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#recipe\",\n" + - " \"isPartOf\": {\n" + - " \"@id\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#webpage\"\n" + - " },\n" + - " \"mainEntityOfPage\": \"https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#webpage\"\n" + - " }"; - - private static final String URL_ARRIFIED_TYPE ="https://www.allrecipes.com/recipe/147103/delicious-egg-salad-for-sandwiches/"; - - private static final String RECIPE_ARRIFIED_TYPE = "{\n" + - "\"@context\": \"http://schema.org\",\n" + - "\"@type\": [\"Recipe\",\"NewsArticle\"]\n" + - ",\"headline\": \"Delicious Egg Salad for Sandwiches\"\n" + - ",\"datePublished\": \"2007-08-12T01:48:39.000-04:00\"\n" + - ",\"dateModified\": \"2007-08-12T01:48:39.000-04:00\"\n" + - ",\"author\": [\n" + - "{\"@type\": \"Person\"\n" + - ",\"name\": \"wifeyluvs2cook\"\n" + - ",\"url\": \"https://www.allrecipes.com/cook/2309128\"\n" + - "}\n" + - "]\n" + - ",\"description\": \"This egg salad is quick to prep and easy to make with hard-cooked eggs, mayonnaise, mustard, paprika, and green onions. Perfect for sandwiches!\"\n" + - ",\"image\": {\n" + - "\"@type\": \"ImageObject\",\n" + - "\"url\": \"https://www.allrecipes.com/thmb/5SN7ROsF_d5tEiEUj5Vay0TD62g=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/4525663-570993c0dac74778a584ff6169bf8038.jpg\",\n" + - "\"height\": 1500,\n" + - "\"width\": 1500\n" + - "}\n" + - ",\"video\": {\n" + - "\"@type\": \"VideoObject\",\n" + - "\"embedUrl\": \"https://players.brightcove.net/1033249144001/default_default/index.html?videoId=1892556557001\",\n" + - "\"description\": \"AR0742\",\n" + - "\"duration\": \"PT2M31S\",\n" + - "\"name\": \"Delicious Egg Salad for Sandwiches\",\n" + - "\"thumbnailUrl\": \"https://cf-images.us-east-1.prod.boltdns.net/v1/static/1033249144001/952e480b-889d-4527-8f06-537723f5f600/dc9f0925-7fe3-4e43-aa34-14d4f1f18b1d/1280x720/match/image.jpg\",\n" + - "\"uploadDate\": \"2021-02-27T17:29:07.998-05:00\"}\n" + - ",\"publisher\": {\n" + - "\"@type\": \"Organization\",\n" + - "\"name\": \"Allrecipes\",\n" + - "\"url\": \"https://www.allrecipes.com\",\n" + - "\"logo\": {\n" + - "\"@type\": \"ImageObject\",\n" + - "\"url\": \"https://www.allrecipes.com/thmb/Z9lwz1y0B5aX-cemPiTgpn5YB0k=/112x112/filters:no_upscale():max_bytes(150000):strip_icc()/allrecipes_logo_schema-867c69d2999b439a9eba923a445ccfe3.png\",\n" + - "\"width\": 112,\n" + - "\"height\": 112\n" + - "},\n" + - "\"brand\": \"Allrecipes\"\n" + - ", \"publishingPrinciples\": \"https://www.allrecipes.com/about-us-6648102#toc-editorial-guidelines\"\n" + - ", \"sameAs\" : [\n" + - "\"https://www.facebook.com/allrecipes\",\n" + - "\"https://www.instagram.com/allrecipes/\",\n" + - "\"https://www.pinterest.com/allrecipes/\",\n" + - "\"https://www.tiktok.com/@allrecipes\",\n" + - "\"https://www.youtube.com/user/allrecipes/videos\",\n" + - "\"https://twitter.com/Allrecipes\",\n" + - "\"https://flipboard.com/@Allrecipes\",\n" + - "\"https://en.wikipedia.org/wiki/Allrecipes.com\",\n" + - "\"https://apps.apple.com/us/app/allrecipes-dinner-spinner/id299515267\",\n" + - "\"https://play.google.com/store/apps/details?id=com.allrecipes.spinner.free&hl=en_US&gl=US\",\n" + - "\"https://www.youtube.com/c/foodwishes\",\n" + - "\"https://www.linkedin.com/company/19312/admin/\"\n" + - "]\n" + - "}\n" + - ",\"name\": \"Delicious Egg Salad for Sandwiches\"\n" + - ",\"aggregateRating\": {\n" + - "\"@type\": \"AggregateRating\",\n" + - "\"ratingValue\": \"4.7\",\n" + - "\"ratingCount\": \"2210\"\n" + - "}\n" + - ",\"cookTime\": \"PT15M\"\n" + - ",\"nutrition\": {\n" + - "\"@type\": \"NutritionInformation\"\n" + - ",\"calories\": \"344 kcal\"\n" + - ",\"carbohydrateContent\": \"2 g\"\n" + - ",\"cholesterolContent\": \"383 mg\"\n" + - ",\"fiberContent\": \"0 g\"\n" + - ",\"proteinContent\": \"13 g\"\n" + - ",\"saturatedFatContent\": \"6 g\"\n" + - ",\"sodiumContent\": \"351 mg\"\n" + - ",\"sugarContent\": \"1 g\"\n" + - ",\"fatContent\": \"32 g\"\n" + - ",\"unsaturatedFatContent\": \"0 g\"\n" + - "}\n" + - ",\"prepTime\": \"PT10M\"\n" + - ", \"recipeCategory\": [\"Lunch\"]\n" + - ",\"recipeIngredient\": [\n" + - "\"8 eggs\",\n" + - "\"0.5 cup mayonnaise\",\n" + - "\"0.25 cup chopped green onion\",\n" + - "\"1 teaspoon prepared yellow mustard\",\n" + - "\"0.25 teaspoon paprika\",\n" + - "\"salt and pepper to taste\" ]\n" + - ",\"recipeInstructions\": [\n" + - "{\n" + - "\"@type\": \"HowToStep\"\n" + - ",\"text\": \"Place eggs in a saucepan and cover with cold water. Bring water to a boil and immediately remove from heat. Cover and let eggs stand in hot water for 10 to 12 minutes. Remove from hot water, cool, peel, and chop.\"\n" + - "} ,{\n" + - "\"@type\": \"HowToStep\"\n" + - ",\"image\": [\n" + - "{\n" + - "\"@type\": \"ImageObject\",\n" + - "\"url\": \"https://www.allrecipes.com/thmb/5SN7ROsF_d5tEiEUj5Vay0TD62g=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/4525663-570993c0dac74778a584ff6169bf8038.jpg\"\n" + - "}\n" + - "]\n" + - ",\"text\": \"Place chopped eggs in a bowl; stir in mayonnaise, green onion, and mustard. Season with paprika, salt, and pepper. Stir and serve on your favorite bread or crackers.\"\n" + - "} ]\n" + - ",\"recipeYield\": [\"4\"]\n" + - ",\"totalTime\": \"PT35M\"\n" + - ",\"mainEntityOfPage\": {\n" + - "\"@type\": [\"WebPage\"]\n" + - ",\"@id\": \"https://www.allrecipes.com/recipe/147103/delicious-egg-salad-for-sandwiches/\"\n" + - ",\"breadcrumb\": {\n" + - "\"@type\": \"BreadcrumbList\",\n" + - "\"itemListElement\": [\n" + - "{\n" + - "\"@type\": \"ListItem\",\n" + - "\"position\": 1,\n" + - "\"item\": {\n" + - "\"@id\": \"https://www.allrecipes.com/recipes/96/salad/\",\n" + - "\"name\": \"Salad\"\n" + - "}\n" + - "}\n" + - ",\n" + - "{\n" + - "\"@type\": \"ListItem\",\n" + - "\"position\": 2,\n" + - "\"item\": {\n" + - "\"@id\": \"https://www.allrecipes.com/recipes/1986/salad/egg-salad/\",\n" + - "\"name\": \"Egg Salad Recipes\"\n" + - "}\n" + - "}\n" + - ",\n" + - "{\n" + - "\"@type\": \"ListItem\",\n" + - "\"position\": 3,\n" + - "\"item\": {\n" + - "\"@id\": \"https://www.allrecipes.com/recipe/147103/delicious-egg-salad-for-sandwiches/\",\n" + - "\"name\": \"Delicious Egg Salad for Sandwiches\"\n" + - "}\n" + - "}\n" + - "]\n" + - "}\n" + - "}\n" + - ", \"about\": [\n" + - "]\n" + - "}"; + private static final String RECIPE_YOAST_WITH_SECTIONS = """ + { + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "Saftiger Veganer Zitronenkuchen", + "author": { + "@type": "Person", + "name": "Karen Wilkening" + }, + "description": "▶ Super saftiger veganer Zitronenkuchen ▶ Einfaches, sehr leckeres Rezept ▶ Kommt immer gut an▶ Für 30 cm Kastenbackform.", + "datePublished": "2019-07-01T20:18:20+00:00", + "image": [ + "https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig.jpg", + "https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig-500x500.jpg", + "https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig-500x375.jpg", + "https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig-480x270.jpg" + ], + "recipeYield": [ + "1", + "1 Kuchen" + ], + "totalTime": "PT75M", + "recipeIngredient": [ + "300 g Mehl", + "200 g Zucker", + "1 Tüte Backpulver", + "1 Tüte Vanillezucker", + "250 ml Vanille-Sojamilch", + "100 g Öl (Rapsöl oder Sonnenblumenöl)", + "50 ml Zitronensaft", + "geriebene Zitronenschale", + "evtl. Backaroma Zitrone", + "150 g Puderzucker", + "2 EL Zitronensaft" + ], + "recipeInstructions": [ + { + "@type": "HowToSection", + "name": "Vorbereiten:", + "itemListElement": [ + { + "@type": "HowToStep", + "text": "Kastenbackform mit Backpapier auslegen oder einfetten.", + "name": "Kastenbackform mit Backpapier auslegen oder einfetten.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-0-0" + }, + { + "@type": "HowToStep", + "text": "2 Zitronen abreiben und auspressen.", + "name": "2 Zitronen abreiben und auspressen.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-0-1" + }, + { + "@type": "HowToStep", + "text": "Backofen vorheizen auf 180 °C (Ober-Unter-Hitze).", + "name": "Backofen vorheizen auf 180 °C (Ober-Unter-Hitze).", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-0-2" + } + ] + }, + { + "@type": "HowToSection", + "name": "Trockene Zutaten mischen:", + "itemListElement": [ + { + "@type": "HowToStep", + "text": "In eine Schüssel geben:300 g Mehl 200 g Zucker1 Tüte Backpulver1 Tüte VanillezuckerAbgeriebene ZitronenschaleGut mischen.", + "name": "In eine Schüssel geben:300 g Mehl 200 g Zucker1 Tüte Backpulver1 Tüte VanillezuckerAbgeriebene ZitronenschaleGut mischen.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-1-0" + } + ] + }, + { + "@type": "HowToSection", + "name": "Feuchte Zutaten zufügen:", + "itemListElement": [ + { + "@type": "HowToStep", + "text": "250 ml Sojamilch Vanille100 g Öl50 ml Zitronensaft", + "name": "250 ml Sojamilch Vanille100 g Öl50 ml Zitronensaft", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-2-0" + }, + { + "@type": "HowToStep", + "text": "Evtl. weiteres Zitronenaroma zufügen(Backaroma oder Zitronenöl)", + "name": "Evtl. weiteres Zitronenaroma zufügen(Backaroma oder Zitronenöl)", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-2-1" + } + ] + }, + { + "@type": "HowToSection", + "name": "Teig mixen und in Backform füllen:", + "itemListElement": [ + { + "@type": "HowToStep", + "text": "Kurz gut durchmixen.", + "name": "Kurz gut durchmixen.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-3-0" + }, + { + "@type": "HowToStep", + "text": "In die vorbereitete Kastenbackform füllen.", + "name": "In die vorbereitete Kastenbackform füllen.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-3-1" + } + ] + }, + { + "@type": "HowToSection", + "name": "Backen:", + "itemListElement": [ + { + "@type": "HowToStep", + "text": "Im vorgeheizten Ofen bei 180 °C (Ober-Unter-Hitze) 60 Minuten backen.(Stäbchenprobe)", + "name": "Im vorgeheizten Ofen bei 180 °C (Ober-Unter-Hitze) 60 Minuten backen.(Stäbchenprobe)", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-4-0" + }, + { + "@type": "HowToStep", + "text": "Kuchen in der Form abkühlen lassen.", + "name": "Kuchen in der Form abkühlen lassen.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-4-1" + } + ] + }, + { + "@type": "HowToSection", + "name": "Zitronenglasur:", + "itemListElement": [ + { + "@type": "HowToStep", + "text": "150 g Puderzucker sieben.Mit 2 EL Zitronensaft verrühren.Auf dem Kuchen verteilen.", + "name": "150 g Puderzucker sieben.Mit 2 EL Zitronensaft verrühren.Auf dem Kuchen verteilen.", + "url": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#wprm-recipe-36837-step-5-0" + } + ] + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "4.97", + "ratingCount": "29" + }, + "recipeCategory": [ + "Backen" + ], + "recipeCuisine": [ + "vegan" + ], + "suitableForDiet": [ + "https://schema.org/LowLactoseDiet", + "https://schema.org/VeganDiet" + ], + "keywords": "einfach, mit öl, ohne butter, ohne ei, ohne milch, super saftig", + "nutrition": { + "@type": "NutritionInformation", + "servingSize": "1 Stück (bei 12 Stücken)", + "calories": "292 kcal", + "carbohydrateContent": "49 g", + "proteinContent": "3.8 g", + "fatContent": "8.4 g", + "saturatedFatContent": "0.75 g", + "sugarContent": "31 g" + }, + "@id": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#recipe", + "isPartOf": { + "@id": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#webpage" + }, + "mainEntityOfPage": "https://veggie-einhorn.de/saftiger-veganer-zitronenkuchen/#webpage" + } + """; + + private static final String URL_ARRIFIED_TYPE = "https://www.allrecipes.com/recipe/147103/delicious-egg-salad-for-sandwiches/"; + + private static final String RECIPE_ARRIFIED_TYPE = """ + { + "@context": "http://schema.org", + "@type": ["Recipe","NewsArticle"] + ,"headline": "Delicious Egg Salad for Sandwiches" + ,"datePublished": "2007-08-12T01:48:39.000-04:00" + ,"dateModified": "2007-08-12T01:48:39.000-04:00" + ,"author": [ + {"@type": "Person" + ,"name": "wifeyluvs2cook" + ,"url": "https://www.allrecipes.com/cook/2309128" + } + ] + ,"description": "This egg salad is quick to prep and easy to make with hard-cooked eggs, mayonnaise, mustard, paprika, and green onions. Perfect for sandwiches!" + ,"image": { + "@type": "ImageObject", + "url": "https://www.allrecipes.com/thmb/5SN7ROsF_d5tEiEUj5Vay0TD62g=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/4525663-570993c0dac74778a584ff6169bf8038.jpg", + "height": 1500, + "width": 1500 + } + ,"video": { + "@type": "VideoObject", + "embedUrl": "https://players.brightcove.net/1033249144001/default_default/index.html?videoId=1892556557001", + "description": "AR0742", + "duration": "PT2M31S", + "name": "Delicious Egg Salad for Sandwiches", + "thumbnailUrl": "https://cf-images.us-east-1.prod.boltdns.net/v1/static/1033249144001/952e480b-889d-4527-8f06-537723f5f600/dc9f0925-7fe3-4e43-aa34-14d4f1f18b1d/1280x720/match/image.jpg", + "uploadDate": "2021-02-27T17:29:07.998-05:00"} + ,"publisher": { + "@type": "Organization", + "name": "Allrecipes", + "url": "https://www.allrecipes.com", + "logo": { + "@type": "ImageObject", + "url": "https://www.allrecipes.com/thmb/Z9lwz1y0B5aX-cemPiTgpn5YB0k=/112x112/filters:no_upscale():max_bytes(150000):strip_icc()/allrecipes_logo_schema-867c69d2999b439a9eba923a445ccfe3.png", + "width": 112, + "height": 112 + }, + "brand": "Allrecipes" + , "publishingPrinciples": "https://www.allrecipes.com/about-us-6648102#toc-editorial-guidelines" + , "sameAs" : [ + "https://www.facebook.com/allrecipes", + "https://www.instagram.com/allrecipes/", + "https://www.pinterest.com/allrecipes/", + "https://www.tiktok.com/@allrecipes", + "https://www.youtube.com/user/allrecipes/videos", + "https://twitter.com/Allrecipes", + "https://flipboard.com/@Allrecipes", + "https://en.wikipedia.org/wiki/Allrecipes.com", + "https://apps.apple.com/us/app/allrecipes-dinner-spinner/id299515267", + "https://play.google.com/store/apps/details?id=com.allrecipes.spinner.free&hl=en_US&gl=US", + "https://www.youtube.com/c/foodwishes", + "https://www.linkedin.com/company/19312/admin/" + ] + } + ,"name": "Delicious Egg Salad for Sandwiches" + ,"aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "4.7", + "ratingCount": "2210" + } + ,"cookTime": "PT15M" + ,"nutrition": { + "@type": "NutritionInformation" + ,"calories": "344 kcal" + ,"carbohydrateContent": "2 g" + ,"cholesterolContent": "383 mg" + ,"fiberContent": "0 g" + ,"proteinContent": "13 g" + ,"saturatedFatContent": "6 g" + ,"sodiumContent": "351 mg" + ,"sugarContent": "1 g" + ,"fatContent": "32 g" + ,"unsaturatedFatContent": "0 g" + } + ,"prepTime": "PT10M" + , "recipeCategory": ["Lunch"] + ,"recipeIngredient": [ + "8 eggs", + "0.5 cup mayonnaise", + "0.25 cup chopped green onion", + "1 teaspoon prepared yellow mustard", + "0.25 teaspoon paprika", + "salt and pepper to taste" ] + ,"recipeInstructions": [ + { + "@type": "HowToStep" + ,"text": "Place eggs in a saucepan and cover with cold water. Bring water to a boil and immediately remove from heat. Cover and let eggs stand in hot water for 10 to 12 minutes. Remove from hot water, cool, peel, and chop." + } ,{ + "@type": "HowToStep" + ,"image": [ + { + "@type": "ImageObject", + "url": "https://www.allrecipes.com/thmb/5SN7ROsF_d5tEiEUj5Vay0TD62g=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/4525663-570993c0dac74778a584ff6169bf8038.jpg" + } + ] + ,"text": "Place chopped eggs in a bowl; stir in mayonnaise, green onion, and mustard. Season with paprika, salt, and pepper. Stir and serve on your favorite bread or crackers." + } ] + ,"recipeYield": ["4"] + ,"totalTime": "PT35M" + ,"mainEntityOfPage": { + "@type": ["WebPage"] + ,"@id": "https://www.allrecipes.com/recipe/147103/delicious-egg-salad-for-sandwiches/" + ,"breadcrumb": { + "@type": "BreadcrumbList", + "itemListElement": [ + { + "@type": "ListItem", + "position": 1, + "item": { + "@id": "https://www.allrecipes.com/recipes/96/salad/", + "name": "Salad" + } + } + , + { + "@type": "ListItem", + "position": 2, + "item": { + "@id": "https://www.allrecipes.com/recipes/1986/salad/egg-salad/", + "name": "Egg Salad Recipes" + } + } + , + { + "@type": "ListItem", + "position": 3, + "item": { + "@id": "https://www.allrecipes.com/recipe/147103/delicious-egg-salad-for-sandwiches/", + "name": "Delicious Egg Salad for Sandwiches" + } + } + ] + } + } + , "about": [ + ] + } + """; @Before public void setUp() { @@ -560,7 +573,7 @@ private BroccoliApplication getApplication() { public void example_chefkoch() throws JSONException, IOException { when(recipeImageService.downloadToCache(new URL("https://img.chefkoch-cdn.de/rezepte/3212051478029180/bilder/1325560/crop-960x540/vegane-chocolate-chip-cookies.jpg"))).thenReturn("blablupp.jpg"); - ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(recipeImageService); + ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(application, recipeImageService); Optional optionalRecipe = recipeBuilder .withRecipeJsonLd(new JSONObject(RECIPE_JSONLD_CHEFKOCH)) @@ -577,14 +590,15 @@ public void example_chefkoch() throws JSONException, IOException { assertThat(recipe.getDescription(), is("Vegane Chocolate Chip Cookies - außen kross, innen weich, lecker und vegan, ergibt 35 Stück. Über 110 Bewertungen und für sehr lecker befunden. Mit ► Portionsrechner ► Kochbuch ► Video-Tipps!")); assertThat(recipe.getIngredients(), is("20 g Chiasamen\n50 ml Wasser\n190 g Butterersatz oder Margarine, vegan\n200 g Zucker , braun, alternativ Rohrzucker\n2 TL Zuckerrübensirup , alternativ Melasse, Ahornsirup oder Agavendicksaft\n2 Pck. Vanillezucker\n300 g Weizenmehl oder Dinkelmehl, oder gemischt\n4 g Natron\nn. B. Salz\n200 g Blockschokolade , zartbitter oder Schokotröpfchen")); assertThat(recipe.getDirections(), is("Den Backofen auf 180 °C Umluft vorheizen. Die Chiasamen und das Wasser in einer kleinen Schüssel vermengen und ca. 10 Minuten quellen lassen.\n\nEin Backblech mit Backpapier auslegen. Vegane Butter bzw. Margarine und Zucker mit den Schneebesen des Rührgeräts cremig verrühren. Dann die gequollenen Chiasamen, den Zuckerrübensirup und beide Päckchen Vanillezucker dazugeben und weiter rühren. Unter weiterem Rühren jetzt zuerst das Mehl hinzugeben und anschließend Natron sowie Salz. Alternativ - oder falls der Teig zu zäh ist - kann alles auch mit den Händen verknetet werden. Abschließend die Schokotröpfchen bzw. die gehackte Blockschokolade untermischen.\n\nDen nun fertigen Teig mit einem Esslöffel oder Eisportionierer klecksweise im Abstand von etwa 5 - 6 cm auf das Backpapier geben. Die Teigkleckse können - müssen jedoch nicht - mit einem Löffel noch etwas rund geformt und flach gedrückt werden.\n\nDie Cookies bei 180 °C Umluft maximal 15 Minuten backen, da sie sonst zu fest werden.")); - assertThat(recipe.getImageName(), is ("blablupp.jpg")); + // TODO assert nutrition + assertThat(recipe.getImageName(), is("blablupp.jpg")); } @Test public void example_yoast() throws JSONException, IOException { when(recipeImageService.downloadToCache(new URL("https://stilettosandsprouts.de/wp-content/uploads/2018/05/Vegane_Fenchel_Pasta_02_B.jpg"))).thenReturn("blablupp.jpg"); - ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(recipeImageService); + ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(application, recipeImageService); Optional optionalRecipe = recipeBuilder .withRecipeJsonLd(new JSONObject(RECIPE_JSON_LD_YOAST)) @@ -601,14 +615,15 @@ public void example_yoast() throws JSONException, IOException { assertThat(recipe.getDescription(), is("Pasta mit geröstetem Fenchel und Zitrone – ein herrlich leichtes Pastagericht, in nur 10 Minuten fertig zubereitet.")); assertThat(recipe.getIngredients(), is("1 Knolle Fenchel\n1 Bio-Zitrone\n1 Knoblauchzehe, geschält und fein gehackt\n1 Schalotte, geschält und fein gehackt\n1 Handvoll Petersilie, frisch, fein gehackt\n5 EL Semmelbrösel\nOlivenöl\nSalz & Pfeffer\n250 g Pasta (z.B. Linguini)")); assertThat(recipe.getDirections(), is("Vom Fenchel die oberen grünen Stängel entfernen. Dabei unbedingt das Fenchelgrün aufbewahren. Das kommt später an die Pasta ran. Die Knolle halbieren, den Strunk in der Mitte keilförmig entfernen und nun die zwei Hälften in dünne Streifen schneiden. Die Fenchel-Streifen waschen und gut abtrocknen.\nDie Pasta nach Packungsanweisung garen.\nIn der Zwischenzeit in einer großen Pfanne Olivenöl erhitzen und den Fenchel darin mit etwas Salz ca. 8 Minuten lang anrösten.\nIn einer kleinen Pfanne etwas Olivenöl erhitzen, Knoblauch- und Schalottenwürfel darin mit etwas Salz glasig andünsten. Zitronenabrieb, Petersilie und die Semmelbrösel hinzugeben und alles ca. 4 Minuten vorsichtig anrösten bis die Semmelbrösel leicht angebräunt sind. Die Mischung vom Herd nehmen.\nDie fertige Pasta abgießen. Dabei etwa ein halbes Wasserglas der Kochflüssigkeit auffangen. Abgetropfte Pasta zum Fenchel geben. Die Semmelbröselmischung dazugeben. Kochflüssigkeit nach Belieben dazugeben, damit die Pasta schön glänzend wird. Pasta ordentlich salzen und pfeffern und nach Geschmack Zitrone hinzugeben. Den Saft einer halben Zitrone verträgt das Gericht mindestens. Mit einem guten Schuss Olivenöl und mit Fenchelgrün bestreut servieren.")); - assertThat(recipe.getImageName(), is ("blablupp.jpg")); + // TODO assert nutrition + assertThat(recipe.getImageName(), is("blablupp.jpg")); } @Test public void example_yoast_with_sections() throws JSONException, IOException { when(recipeImageService.downloadToCache(new URL("https://veggie-einhorn.de/wp-content/uploads/Einfacher-veganer-Zitronenkuchen-saftig.jpg"))).thenReturn("blablupp.jpg"); - ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(recipeImageService); + ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(application, recipeImageService); Optional optionalRecipe = recipeBuilder .withRecipeJsonLd(new JSONObject(RECIPE_YOAST_WITH_SECTIONS)) @@ -625,12 +640,13 @@ public void example_yoast_with_sections() throws JSONException, IOException { assertThat(recipe.getDescription(), is("▶ Super saftiger veganer Zitronenkuchen ▶ Einfaches, sehr leckeres Rezept ▶ Kommt immer gut an▶ Für 30 cm Kastenbackform.")); assertThat(recipe.getIngredients(), is("300 g Mehl\n200 g Zucker\n1 Tüte Backpulver\n1 Tüte Vanillezucker\n250 ml Vanille-Sojamilch\n100 g Öl (Rapsöl oder Sonnenblumenöl)\n50 ml Zitronensaft\ngeriebene Zitronenschale\nevtl. Backaroma Zitrone\n150 g Puderzucker\n2 EL Zitronensaft")); assertThat(recipe.getDirections(), is("VORBEREITEN: Kastenbackform mit Backpapier auslegen oder einfetten. 2 Zitronen abreiben und auspressen. Backofen vorheizen auf 180 °C (Ober-Unter-Hitze).\nTROCKENE ZUTATEN MISCHEN: In eine Schüssel geben:300 g Mehl 200 g Zucker1 Tüte Backpulver1 Tüte VanillezuckerAbgeriebene ZitronenschaleGut mischen.\nFEUCHTE ZUTATEN ZUFÜGEN: 250 ml Sojamilch Vanille100 g Öl50 ml Zitronensaft Evtl. weiteres Zitronenaroma zufügen(Backaroma oder Zitronenöl)\nTEIG MIXEN UND IN BACKFORM FÜLLEN: Kurz gut durchmixen. In die vorbereitete Kastenbackform füllen.\nBACKEN: Im vorgeheizten Ofen bei 180 °C (Ober-Unter-Hitze) 60 Minuten backen.(Stäbchenprobe) Kuchen in der Form abkühlen lassen.\nZITRONENGLASUR: 150 g Puderzucker sieben.Mit 2 EL Zitronensaft verrühren.Auf dem Kuchen verteilen.")); - assertThat(recipe.getImageName(), is ("blablupp.jpg")); + // TODO assert nutrition + assertThat(recipe.getImageName(), is("blablupp.jpg")); } @Test public void no_Recipe_JsonLd() { - ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(recipeImageService); + ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(application, recipeImageService); Optional optionalRecipe = recipeBuilder .build(); @@ -640,7 +656,7 @@ public void no_Recipe_JsonLd() { @Test public void missing_attributes() throws JSONException { - ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(recipeImageService); + ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(application, recipeImageService); Optional optionalRecipe = recipeBuilder .withRecipeJsonLd(new JSONObject(MINIMIZED_RECIPE_JSONLD)) @@ -661,7 +677,7 @@ public void missing_attributes() throws JSONException { public void arrified_type() throws JSONException, IOException { when(recipeImageService.downloadToCache(new URL("https://www.allrecipes.com/thmb/5SN7ROsF_d5tEiEUj5Vay0TD62g=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/4525663-570993c0dac74778a584ff6169bf8038.jpg"))).thenReturn("blablupp.jpg"); - ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(recipeImageService); + ImportableRecipeBuilder recipeBuilder = new ImportableRecipeBuilder(application, recipeImageService); Optional optionalRecipe = recipeBuilder .withRecipeJsonLd(new JSONObject(RECIPE_ARRIFIED_TYPE)) @@ -678,6 +694,8 @@ public void arrified_type() throws JSONException, IOException { assertThat(recipe.getDescription(), is("This egg salad is quick to prep and easy to make with hard-cooked eggs, mayonnaise, mustard, paprika, and green onions. Perfect for sandwiches!")); assertThat(recipe.getIngredients(), is("8 eggs\n0.5 cup mayonnaise\n0.25 cup chopped green onion\n1 teaspoon prepared yellow mustard\n0.25 teaspoon paprika\nsalt and pepper to taste")); assertThat(recipe.getDirections(), is("Place eggs in a saucepan and cover with cold water. Bring water to a boil and immediately remove from heat. Cover and let eggs stand in hot water for 10 to 12 minutes. Remove from hot water, cool, peel, and chop.\nPlace chopped eggs in a bowl; stir in mayonnaise, green onion, and mustard. Season with paprika, salt, and pepper. Stir and serve on your favorite bread or crackers.")); - assertThat(recipe.getImageName(), is ("blablupp.jpg")); + // TODO assert nutrition + assertThat(recipe.getImageName(), is("blablupp.jpg")); } + } \ No newline at end of file diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java b/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java index 5b75225f..00607864 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java @@ -1,7 +1,9 @@ package com.flauschcode.broccoli.recipe.importing; +import android.app.Application; import android.util.Log; +import com.flauschcode.broccoli.R; import com.flauschcode.broccoli.recipe.Recipe; import com.flauschcode.broccoli.recipe.images.RecipeImageService; @@ -16,7 +18,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; class ImportableRecipeBuilder { @@ -27,7 +28,7 @@ class ImportableRecipeBuilder { private static final String ITEM_LIST_ELEMENT = "itemListElement"; private static final String NAME = "name"; - private static final String RECIPE_DESCRIPTION = "description"; + private static final String DESCRIPTION = "description"; private static final String RECIPE_INGREDIENT = "recipeIngredient"; private static final String RECIPE_INSTRUCTIONS = "recipeInstructions"; private static final String HOW_TO_TEXT = "text"; @@ -36,13 +37,21 @@ class ImportableRecipeBuilder { private static final String COOK_TIME = "cookTime"; private static final String RECIPE_IMAGE = "image"; private static final String RECIPE_URL = "url"; - + private static final String NUTRITION = "nutrition"; + private static final String SERVING_SIZE = "servingSize"; + private static final String CALORIES = "calories"; + private static final String FAT_CONTENT = "fatContent"; + private static final String CARBOHYDRATE_CONTENT = "carbohydrateContent"; + private static final String PROTEIN_CONTENT = "proteinContent"; + private JSONObject recipeJson; - private Recipe recipe = new Recipe(); + private final Recipe recipe = new Recipe(); - private RecipeImageService recipeImageService; + private final Application application; + private final RecipeImageService recipeImageService; - public ImportableRecipeBuilder(RecipeImageService recipeImageService) { + public ImportableRecipeBuilder(Application application, RecipeImageService recipeImageService) { + this.application = application; this.recipeImageService = recipeImageService; } @@ -67,6 +76,7 @@ Optional build() { contributePreparationTime(); contributeIngredients(); contributeDirections(); + contributeNutritionalInformation(); contributeImage(); return Optional.of(recipe); @@ -77,7 +87,7 @@ private void contributeTitle() { } private void contributeDescription() { - recipe.setDescription(recipeJson.optString(RECIPE_DESCRIPTION)); + recipe.setDescription(recipeJson.optString(DESCRIPTION)); } private void contributeServings() { @@ -115,7 +125,7 @@ private void contributeIngredients() { for (int i = 0; i < ingredientArray.length(); i++) { list.add(ingredientArray.optString(i)); } - recipe.setIngredients(list.stream().collect(Collectors.joining("\n"))); + recipe.setIngredients(String.join("\n", list)); } } @@ -135,7 +145,29 @@ private void contributeDirections() { } } - recipe.setDirections(directions.stream().collect(Collectors.joining("\n"))); + recipe.setDirections(String.join("\n", directions)); + } + } + + private void contributeNutritionalInformation() { + contributeNutritionalInformationFor(SERVING_SIZE, R.string.serving); + contributeNutritionalInformationFor(CALORIES, R.string.calories); + contributeNutritionalInformationFor(FAT_CONTENT, R.string.fat); + contributeNutritionalInformationFor(CARBOHYDRATE_CONTENT, R.string.carbohydrates); + contributeNutritionalInformationFor(PROTEIN_CONTENT, R.string.protein); + } + + private void contributeNutritionalInformationFor(String jsonKey, int resourceKey) { + JSONObject nutritionalValuesObject = recipeJson.optJSONObject(NUTRITION); + if (nutritionalValuesObject == null) { + return; + } + + String nutritionalValue = nutritionalValuesObject.optString(jsonKey); + if (!nutritionalValue.isEmpty()) { + StringBuilder stringBuilder = new StringBuilder(recipe.getNutritionalValues()); + stringBuilder.append(application.getString(resourceKey).toUpperCase()).append(" ").append(nutritionalValue).append("\n"); recipe.setNutritionalValues(recipe.getNutritionalValues()); + recipe.setNutritionalValues(recipe.getNutritionalValues()); } } @@ -148,7 +180,7 @@ private String contributeSection(JSONObject instructionObject) { steps.add(contributeStep(sectionsArray, i)); } } - return steps.stream().collect(Collectors.joining(" ")); + return String.join(" ", steps); } private String contributeStep(JSONArray jsonArray, int position) { diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/importing/RecipeImportService.java b/app/src/main/java/com/flauschcode/broccoli/recipe/importing/RecipeImportService.java index 2ce27a18..c95f4637 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/importing/RecipeImportService.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/importing/RecipeImportService.java @@ -1,5 +1,6 @@ package com.flauschcode.broccoli.recipe.importing; +import android.app.Application; import android.util.Log; import com.flauschcode.broccoli.recipe.Recipe; @@ -22,14 +23,18 @@ public class RecipeImportService { + private static final String USER_AGENT = "Mozilla/5.0"; + private static final String FIELD_GRAPH = "@graph"; private static final String FIELD_TYPE = "@type"; private static final String TYPE_RECIPE = "Recipe"; - private RecipeImageService recipeImageService; + private final Application application; + private final RecipeImageService recipeImageService; @Inject - public RecipeImportService(RecipeImageService recipeImageService) { + public RecipeImportService(Application application, RecipeImageService recipeImageService) { + this.application = application; this.recipeImageService = recipeImageService; } @@ -37,7 +42,7 @@ public CompletableFuture> importFrom(String url) { return CompletableFuture.supplyAsync(() -> { Document document; try { - document = Jsoup.connect(url).get(); + document = Jsoup.connect(url).userAgent(USER_AGENT).get(); } catch (IOException e) { throw new CompletionException(e); } @@ -46,7 +51,7 @@ public CompletableFuture> importFrom(String url) { Optional recipeJsonLd = findRecipeIn(jsonLds); if (recipeJsonLd.isPresent()) { - return new ImportableRecipeBuilder(recipeImageService).withRecipeJsonLd(recipeJsonLd.get()).from(url).build(); + return new ImportableRecipeBuilder(application, recipeImageService).withRecipeJsonLd(recipeJsonLd.get()).from(url).build(); } return Optional.empty(); @@ -106,11 +111,11 @@ private boolean theTopLevelStructureIsAnArray(Object json) { } private boolean thereIsAGraphObject(Object json) { - return json instanceof JSONObject && ((JSONObject) json).has(FIELD_GRAPH); + return json instanceof JSONObject jsonObject && jsonObject.has(FIELD_GRAPH); } private boolean theRecipeIsTheTopLevelObject(Object json) { - return json instanceof JSONObject && isRecipe((JSONObject) json); + return json instanceof JSONObject jsonObject && isRecipe((jsonObject)); } } diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java b/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java index 39f98a71..539a4e31 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/sharing/ShareableRecipeBuilder.java @@ -16,8 +16,8 @@ @Singleton public class ShareableRecipeBuilder { - private Application application; - private RecipeImageService recipeImageService; + private final Application application; + private final RecipeImageService recipeImageService; @Inject ShareableRecipeBuilder(Application application, RecipeImageService recipeImageService) { diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c0e1bc0a..393fe956 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,6 +22,7 @@ Anleitung Nährwerte Notizen + Eiweiß Rezept-Foto Foto ändern Foto entfernen @@ -542,4 +543,8 @@ Chinesische Yamswurzeln, Yamswurzeln Yuzu Yuzu + Portion + Kalorien + Fett + Kohlenhydrate diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 868ddedc..8de13cb2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -23,8 +23,9 @@ Tiempo Ingredientes Instrucciones - Valores nutricionales + Nutrición Notas + Proteína Foto de la receta Cambiar foto Eliminar la foto @@ -246,4 +247,8 @@ champiñones Noruego bokmål Ruso + Porción + Calorías + Grasa + Carbohidratos \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9a36d01f..5491de7e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,9 +28,15 @@ Time Ingredients Directions - Nutritional values + Nutrition Notes + Serving + Calories + Fat + Carbohydrates + Protein + Recipe photo Change photo Remove photo From 1f0822d354465ea9cddc75eaebe70bf14f27d851 Mon Sep 17 00:00:00 2001 From: flauschtrud Date: Sun, 15 Dec 2024 17:02:45 +0100 Subject: [PATCH 3/3] As a user I can import recipes with their nutritional information #188 Changed formatting and added tests. --- .../ImportableRecipeBuilderTest.java | 8 +++---- .../importing/ImportableRecipeBuilder.java | 23 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java b/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java index a8a5feed..74240793 100644 --- a/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java +++ b/app/src/androidTest/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilderTest.java @@ -590,7 +590,7 @@ public void example_chefkoch() throws JSONException, IOException { assertThat(recipe.getDescription(), is("Vegane Chocolate Chip Cookies - außen kross, innen weich, lecker und vegan, ergibt 35 Stück. Über 110 Bewertungen und für sehr lecker befunden. Mit ► Portionsrechner ► Kochbuch ► Video-Tipps!")); assertThat(recipe.getIngredients(), is("20 g Chiasamen\n50 ml Wasser\n190 g Butterersatz oder Margarine, vegan\n200 g Zucker , braun, alternativ Rohrzucker\n2 TL Zuckerrübensirup , alternativ Melasse, Ahornsirup oder Agavendicksaft\n2 Pck. Vanillezucker\n300 g Weizenmehl oder Dinkelmehl, oder gemischt\n4 g Natron\nn. B. Salz\n200 g Blockschokolade , zartbitter oder Schokotröpfchen")); assertThat(recipe.getDirections(), is("Den Backofen auf 180 °C Umluft vorheizen. Die Chiasamen und das Wasser in einer kleinen Schüssel vermengen und ca. 10 Minuten quellen lassen.\n\nEin Backblech mit Backpapier auslegen. Vegane Butter bzw. Margarine und Zucker mit den Schneebesen des Rührgeräts cremig verrühren. Dann die gequollenen Chiasamen, den Zuckerrübensirup und beide Päckchen Vanillezucker dazugeben und weiter rühren. Unter weiterem Rühren jetzt zuerst das Mehl hinzugeben und anschließend Natron sowie Salz. Alternativ - oder falls der Teig zu zäh ist - kann alles auch mit den Händen verknetet werden. Abschließend die Schokotröpfchen bzw. die gehackte Blockschokolade untermischen.\n\nDen nun fertigen Teig mit einem Esslöffel oder Eisportionierer klecksweise im Abstand von etwa 5 - 6 cm auf das Backpapier geben. Die Teigkleckse können - müssen jedoch nicht - mit einem Löffel noch etwas rund geformt und flach gedrückt werden.\n\nDie Cookies bei 180 °C Umluft maximal 15 Minuten backen, da sie sonst zu fest werden.")); - // TODO assert nutrition + assertThat(recipe.getNutritionalValues(), is("Serving: 1\nCalories: 4594 kcal\nFat: 225,86g\nCarbohydrates: 540,65g\nProtein: 77,59g")); assertThat(recipe.getImageName(), is("blablupp.jpg")); } @@ -615,7 +615,7 @@ public void example_yoast() throws JSONException, IOException { assertThat(recipe.getDescription(), is("Pasta mit geröstetem Fenchel und Zitrone – ein herrlich leichtes Pastagericht, in nur 10 Minuten fertig zubereitet.")); assertThat(recipe.getIngredients(), is("1 Knolle Fenchel\n1 Bio-Zitrone\n1 Knoblauchzehe, geschält und fein gehackt\n1 Schalotte, geschält und fein gehackt\n1 Handvoll Petersilie, frisch, fein gehackt\n5 EL Semmelbrösel\nOlivenöl\nSalz & Pfeffer\n250 g Pasta (z.B. Linguini)")); assertThat(recipe.getDirections(), is("Vom Fenchel die oberen grünen Stängel entfernen. Dabei unbedingt das Fenchelgrün aufbewahren. Das kommt später an die Pasta ran. Die Knolle halbieren, den Strunk in der Mitte keilförmig entfernen und nun die zwei Hälften in dünne Streifen schneiden. Die Fenchel-Streifen waschen und gut abtrocknen.\nDie Pasta nach Packungsanweisung garen.\nIn der Zwischenzeit in einer großen Pfanne Olivenöl erhitzen und den Fenchel darin mit etwas Salz ca. 8 Minuten lang anrösten.\nIn einer kleinen Pfanne etwas Olivenöl erhitzen, Knoblauch- und Schalottenwürfel darin mit etwas Salz glasig andünsten. Zitronenabrieb, Petersilie und die Semmelbrösel hinzugeben und alles ca. 4 Minuten vorsichtig anrösten bis die Semmelbrösel leicht angebräunt sind. Die Mischung vom Herd nehmen.\nDie fertige Pasta abgießen. Dabei etwa ein halbes Wasserglas der Kochflüssigkeit auffangen. Abgetropfte Pasta zum Fenchel geben. Die Semmelbröselmischung dazugeben. Kochflüssigkeit nach Belieben dazugeben, damit die Pasta schön glänzend wird. Pasta ordentlich salzen und pfeffern und nach Geschmack Zitrone hinzugeben. Den Saft einer halben Zitrone verträgt das Gericht mindestens. Mit einem guten Schuss Olivenöl und mit Fenchelgrün bestreut servieren.")); - // TODO assert nutrition + assertThat(recipe.getNutritionalValues(), is("")); assertThat(recipe.getImageName(), is("blablupp.jpg")); } @@ -640,7 +640,7 @@ public void example_yoast_with_sections() throws JSONException, IOException { assertThat(recipe.getDescription(), is("▶ Super saftiger veganer Zitronenkuchen ▶ Einfaches, sehr leckeres Rezept ▶ Kommt immer gut an▶ Für 30 cm Kastenbackform.")); assertThat(recipe.getIngredients(), is("300 g Mehl\n200 g Zucker\n1 Tüte Backpulver\n1 Tüte Vanillezucker\n250 ml Vanille-Sojamilch\n100 g Öl (Rapsöl oder Sonnenblumenöl)\n50 ml Zitronensaft\ngeriebene Zitronenschale\nevtl. Backaroma Zitrone\n150 g Puderzucker\n2 EL Zitronensaft")); assertThat(recipe.getDirections(), is("VORBEREITEN: Kastenbackform mit Backpapier auslegen oder einfetten. 2 Zitronen abreiben und auspressen. Backofen vorheizen auf 180 °C (Ober-Unter-Hitze).\nTROCKENE ZUTATEN MISCHEN: In eine Schüssel geben:300 g Mehl 200 g Zucker1 Tüte Backpulver1 Tüte VanillezuckerAbgeriebene ZitronenschaleGut mischen.\nFEUCHTE ZUTATEN ZUFÜGEN: 250 ml Sojamilch Vanille100 g Öl50 ml Zitronensaft Evtl. weiteres Zitronenaroma zufügen(Backaroma oder Zitronenöl)\nTEIG MIXEN UND IN BACKFORM FÜLLEN: Kurz gut durchmixen. In die vorbereitete Kastenbackform füllen.\nBACKEN: Im vorgeheizten Ofen bei 180 °C (Ober-Unter-Hitze) 60 Minuten backen.(Stäbchenprobe) Kuchen in der Form abkühlen lassen.\nZITRONENGLASUR: 150 g Puderzucker sieben.Mit 2 EL Zitronensaft verrühren.Auf dem Kuchen verteilen.")); - // TODO assert nutrition + assertThat(recipe.getNutritionalValues(), is("Serving: 1 Stück (bei 12 Stücken)\nCalories: 292 kcal\nFat: 8.4 g\nCarbohydrates: 49 g\nProtein: 3.8 g")); assertThat(recipe.getImageName(), is("blablupp.jpg")); } @@ -694,7 +694,7 @@ public void arrified_type() throws JSONException, IOException { assertThat(recipe.getDescription(), is("This egg salad is quick to prep and easy to make with hard-cooked eggs, mayonnaise, mustard, paprika, and green onions. Perfect for sandwiches!")); assertThat(recipe.getIngredients(), is("8 eggs\n0.5 cup mayonnaise\n0.25 cup chopped green onion\n1 teaspoon prepared yellow mustard\n0.25 teaspoon paprika\nsalt and pepper to taste")); assertThat(recipe.getDirections(), is("Place eggs in a saucepan and cover with cold water. Bring water to a boil and immediately remove from heat. Cover and let eggs stand in hot water for 10 to 12 minutes. Remove from hot water, cool, peel, and chop.\nPlace chopped eggs in a bowl; stir in mayonnaise, green onion, and mustard. Season with paprika, salt, and pepper. Stir and serve on your favorite bread or crackers.")); - // TODO assert nutrition + assertThat(recipe.getNutritionalValues(), is("Calories: 344 kcal\nFat: 32 g\nCarbohydrates: 2 g\nProtein: 13 g")); assertThat(recipe.getImageName(), is("blablupp.jpg")); } diff --git a/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java b/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java index 00607864..7ae28a6d 100644 --- a/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java +++ b/app/src/main/java/com/flauschcode/broccoli/recipe/importing/ImportableRecipeBuilder.java @@ -150,25 +150,26 @@ private void contributeDirections() { } private void contributeNutritionalInformation() { - contributeNutritionalInformationFor(SERVING_SIZE, R.string.serving); - contributeNutritionalInformationFor(CALORIES, R.string.calories); - contributeNutritionalInformationFor(FAT_CONTENT, R.string.fat); - contributeNutritionalInformationFor(CARBOHYDRATE_CONTENT, R.string.carbohydrates); - contributeNutritionalInformationFor(PROTEIN_CONTENT, R.string.protein); + String nutritionalInformation = contributeNutritionalInformationFor(SERVING_SIZE, R.string.serving) + + contributeNutritionalInformationFor(CALORIES, R.string.calories) + + contributeNutritionalInformationFor(FAT_CONTENT, R.string.fat) + + contributeNutritionalInformationFor(CARBOHYDRATE_CONTENT, R.string.carbohydrates) + + contributeNutritionalInformationFor(PROTEIN_CONTENT, R.string.protein); + recipe.setNutritionalValues(nutritionalInformation.strip()); } - private void contributeNutritionalInformationFor(String jsonKey, int resourceKey) { + private String contributeNutritionalInformationFor(String jsonKey, int resourceKey) { JSONObject nutritionalValuesObject = recipeJson.optJSONObject(NUTRITION); if (nutritionalValuesObject == null) { - return; + return ""; } String nutritionalValue = nutritionalValuesObject.optString(jsonKey); - if (!nutritionalValue.isEmpty()) { - StringBuilder stringBuilder = new StringBuilder(recipe.getNutritionalValues()); - stringBuilder.append(application.getString(resourceKey).toUpperCase()).append(" ").append(nutritionalValue).append("\n"); recipe.setNutritionalValues(recipe.getNutritionalValues()); - recipe.setNutritionalValues(recipe.getNutritionalValues()); + if (nutritionalValue.isEmpty()) { + return ""; } + + return application.getString(resourceKey) + ": " + nutritionalValue + "\n"; } private String contributeSection(JSONObject instructionObject) {