Repository: hhursev/recipe-scrapers
Pull request #1107 (2024-04-28 16:22:06)

Reopening #1056: Adds output for dietary restrictions outlined by the recipe
Adds coverage for recipes to provide the schema specified dietary restrictions via [suitableForDiet](https://schema.org/RestrictedDiet) which is called as dietary_restrictions.

Also introduced a helper function to parse schema listed enumerations

[DiabeticDiet](https://schema.org/DiabeticDiet)
[GlutenFreeDiet](https://schema.org/GlutenFreeDiet)
[HalalDiet](https://schema.org/HalalDiet)
[HinduDiet](https://schema.org/HinduDiet)
[KosherDiet](https://schema.org/KosherDiet)
[LowCalorieDiet](https://schema.org/LowCalorieDiet)
[LowFatDiet](https://schema.org/LowFatDiet)
[LowLactoseDiet](https://schema.org/LowLactoseDiet)
[LowSaltDiet](https://schema.org/LowSaltDiet)
[VeganDiet](https://schema.org/VeganDiet)
[VegetarianDiet](https://schema.org/VegetarianDiet)

cc. @mlduff 


Patch:
diff --git a/README.rst b/README.rst
index d9aacafe..d9838807 100644
--- a/README.rst
+++ b/README.rst
@@ -61,6 +61,7 @@ then:
     scraper.equipment()  # not always available
     scraper.cooking_method()  # not always available
     scraper.keywords()  # not always available
+    scraper.dietary_restrictions() # not always available
 
 You also have an option to scrape html-like content
 
diff --git a/docs/in-depth-guide-scraper-functions.md b/docs/in-depth-guide-scraper-functions.md
index b9a924a7..9f91150b 100644
--- a/docs/in-depth-guide-scraper-functions.md
+++ b/docs/in-depth-guide-scraper-functions.md
@@ -340,3 +340,12 @@ Returns a `list` of keywords associated with a recipe.
 >>> scraper.keywords()
 ['easy', 'quick', 'dinner']
 ```
+
+### `dietary_restrictions"() -> List[str] | None`
+
+Returns the dietary restrictions specified by the recipe.
+
+```py
+>>> scraper.dietary_restrictions()
+['Vegan Diet', 'Vegetarian Diet']
+```
diff --git a/recipe_scrapers/_abstract.py b/recipe_scrapers/_abstract.py
index 15f6c690..78588c89 100644
--- a/recipe_scrapers/_abstract.py
+++ b/recipe_scrapers/_abstract.py
@@ -188,6 +188,10 @@ class AbstractScraper:
         """Equipment needed for the recipe."""
         raise NotImplementedError("This should be implemented.")
 
+    def dietary_restrictions(self):
+        """The specified dietary restrictions or guidelines for which this recipe is suitable"""
+        raise NotImplementedError("This should be implemented.")
+
     def links(self):
         """Links found in the recipe."""
         invalid_href = {"#", ""}
diff --git a/recipe_scrapers/_schemaorg.py b/recipe_scrapers/_schemaorg.py
index 5a35bf1e..2b036550 100644
--- a/recipe_scrapers/_schemaorg.py
+++ b/recipe_scrapers/_schemaorg.py
@@ -10,7 +10,13 @@ import extruct
 from recipe_scrapers.settings import settings
 
 from ._exceptions import SchemaOrgException
-from ._utils import csv_to_tags, get_minutes, get_yields, normalize_string
+from ._utils import (
+    csv_to_tags,
+    format_diet_name,
+    get_minutes,
+    get_yields,
+    normalize_string,
+)
 
 SCHEMA_ORG_HOST = "schema.org"
 
@@ -333,3 +339,16 @@ class SchemaOrg:
             keywords = normalize_string(keywords)
             keywords = csv_to_tags(keywords)
         return keywords
+
+    def dietary_restrictions(self):
+        dietary_restrictions = self.data.get("suitableForDiet")
+        if dietary_restrictions is None:
+            raise SchemaOrgException("No dietary restrictions data in SchemaOrg.")
+        if not isinstance(dietary_restrictions, list):
+            dietary_restrictions = [dietary_restrictions]
+
+        formatted_diets = [format_diet_name(diet) for diet in dietary_restrictions]
+        formatted_diets = ", ".join(formatted_diets)
+        final_diets = csv_to_tags(formatted_diets)
+
+        return final_diets
diff --git a/recipe_scrapers/_utils.py b/recipe_scrapers/_utils.py
index 6144193a..c430f538 100644
--- a/recipe_scrapers/_utils.py
+++ b/recipe_scrapers/_utils.py
@@ -72,6 +72,31 @@ RECIPE_YIELD_TYPES = (
 )
 
 
+def format_diet_name(diet_input):
+    replacements = {
+        # https://schema.org/RestrictedDiet
+        "DiabeticDiet": "Diabetic Diet",
+        "GlutenFreeDiet": "Gluten Free Diet",
+        "HalalDiet": "Halal Diet",
+        "HinduDiet": "Hindu Diet",
+        "KosherDiet": "Kosher Diet",
+        "LowCalorieDiet": "Low Calorie Diet",
+        "LowFatDiet": "Low Fat Diet",
+        "LowLactoseDiet": "Low Lactose Diet",
+        "LowSaltDiet": "Low Salt Diet",
+        "VeganDiet": "Vegan Diet",
+        "VegetarianDiet": "Vegetarian Diet",
+    }
+    if "https://schema.org/" in diet_input:
+        diet_input = diet_input.replace("https://schema.org/", "")
+
+    for key, value in replacements.items():
+        if key in diet_input:
+            return value
+
+    return diet_input
+
+
 def _extract_fractional(input_string: str) -> float:
     input_string = input_string.strip()
 
diff --git a/recipe_scrapers/creativecanning.py b/recipe_scrapers/creativecanning.py
index 43b92684..a8112653 100644
--- a/recipe_scrapers/creativecanning.py
+++ b/recipe_scrapers/creativecanning.py
@@ -6,4 +6,4 @@ from ._abstract import AbstractScraper
 class CreativeCanning(AbstractScraper):
     @classmethod
     def host(cls):
-        return "practicalselfreliance.com"
+        return "creativecanning.com"
diff --git a/recipe_scrapers/plugins/schemaorg_fill.py b/recipe_scrapers/plugins/schemaorg_fill.py
index bf3c2d0b..6c297f2e 100644
--- a/recipe_scrapers/plugins/schemaorg_fill.py
+++ b/recipe_scrapers/plugins/schemaorg_fill.py
@@ -39,6 +39,7 @@ class SchemaOrgFillPlugin(PluginInterface):
         "prep_time",
         "keywords",
         "ratings_count",
+        "dietary_restrictions",
     )
 
     @classmethod


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
index 6c69cde2..068c4a81 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -35,6 +35,7 @@ OPTIONAL_TESTS = [
     "reviews",
     "equipment",
     "ratings_count",
+    "dietary_restrictions",
 ]
 
 
diff --git a/tests/library/test_utils.py b/tests/library/test_utils.py
index 55b55dc9..5a5c7a9e 100644
--- a/tests/library/test_utils.py
+++ b/tests/library/test_utils.py
@@ -83,6 +83,7 @@ class TestUtils(unittest.TestCase):
             "cooking_method",
             "cuisine",
             "description",
+            "dietary_restrictions",
             "equipment",
             "host",
             "image",
diff --git a/tests/test_data/acouplecooks.com/acouplecooks.json b/tests/test_data/acouplecooks.com/acouplecooks.json
index be5be407..3cb8c90d 100644
--- a/tests/test_data/acouplecooks.com/acouplecooks.json
+++ b/tests/test_data/acouplecooks.com/acouplecooks.json
@@ -51,6 +51,9 @@
   "total_time": 8,
   "yields": "4 servings",
   "cooking_method": "Stovetop",
+  "dietary_restrictions": [
+    "Gluten Free Diet"
+  ],
   "keywords": [
     "Garlic Butter Shrimp"
   ],
diff --git a/tests/test_data/ah.nl/albertheijn.json b/tests/test_data/ah.nl/albertheijn.json
index 64743cc0..b6be8137 100644
--- a/tests/test_data/ah.nl/albertheijn.json
+++ b/tests/test_data/ah.nl/albertheijn.json
@@ -57,6 +57,9 @@
   "title": "Rijkgevulde vegan pastasalade",
   "total_time": 20,
   "yields": "4 servings",
+  "dietary_restrictions": [
+    "Vegan Diet"
+  ],
   "keywords": [
     "veganistisch",
     "vooraf te maken",
diff --git a/tests/test_data/bettybossi.ch/bettybossi.json b/tests/test_data/bettybossi.ch/bettybossi.json
index b6a5f2f5..27ad379b 100644
--- a/tests/test_data/bettybossi.ch/bettybossi.json
+++ b/tests/test_data/bettybossi.ch/bettybossi.json
@@ -70,6 +70,9 @@
   "yields": "12 servings",
   "cook_time": 50,
   "prep_time": 40,
+  "dietary_restrictions": [
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "biscuits au beurre",
     "beurre",
diff --git a/tests/test_data/comidinhasdochef.com/comidinhasdochef.json b/tests/test_data/comidinhasdochef.com/comidinhasdochef.json
index 058e8947..bc69a7e9 100644
--- a/tests/test_data/comidinhasdochef.com/comidinhasdochef.json
+++ b/tests/test_data/comidinhasdochef.com/comidinhasdochef.json
@@ -70,6 +70,11 @@
   "title": "Coxa de Frango na Fritadeira Elétrica",
   "total_time": 30,
   "yields": "5 servings",
+  "dietary_restrictions": [
+    "Diabetic Diet",
+    "Low Calorie Diet",
+    "Low Lactose Diet"
+  ],
   "keywords": [
     "Coxa de Frango na Fritadeira Elétrica"
   ],
diff --git a/tests/test_data/eatsmarter.com/eatsmarter.json b/tests/test_data/eatsmarter.com/eatsmarter.json
index 584641fc..9446d710 100644
--- a/tests/test_data/eatsmarter.com/eatsmarter.json
+++ b/tests/test_data/eatsmarter.com/eatsmarter.json
@@ -64,5 +64,9 @@
   "title": "Pumpkin Black Bean Salad",
   "total_time": 35,
   "yields": "4 servings",
-  "description": "EAT SMARTER's pumpkin salad with beans is the perfect fall dish."
+  "description": "EAT SMARTER's pumpkin salad with beans is the perfect fall dish.",
+  "dietary_restrictions": [
+    "Low-calorie",
+    "Vegetarian"
+  ]
 }
diff --git a/tests/test_data/feastingathome.com/feastingathome.json b/tests/test_data/feastingathome.com/feastingathome.json
index 87da6ae6..dc18af84 100644
--- a/tests/test_data/feastingathome.com/feastingathome.json
+++ b/tests/test_data/feastingathome.com/feastingathome.json
@@ -77,6 +77,9 @@
   "language": "en-US",
   "site_name": "Feasting At Home",
   "cooking_method": "stovetop",
+  "dietary_restrictions": [
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "tomato risotto",
     "vegetarian risotto",
diff --git a/tests/test_data/fifteenspatulas.com/fifteenspatulas.json b/tests/test_data/fifteenspatulas.com/fifteenspatulas.json
index 97fe1cd2..6604b09a 100644
--- a/tests/test_data/fifteenspatulas.com/fifteenspatulas.json
+++ b/tests/test_data/fifteenspatulas.com/fifteenspatulas.json
@@ -56,6 +56,10 @@
   "title": "Creme Brulee",
   "total_time": 420,
   "yields": "6 servings",
+  "dietary_restrictions": [
+    "Gluten Free Diet",
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "creme brulee",
     "orange creme brulee"
diff --git a/tests/test_data/halfbakedharvest.com/halfbakedharvest_groups.json b/tests/test_data/halfbakedharvest.com/halfbakedharvest_groups.json
index af6681db..5c5631e2 100644
--- a/tests/test_data/halfbakedharvest.com/halfbakedharvest_groups.json
+++ b/tests/test_data/halfbakedharvest.com/halfbakedharvest_groups.json
@@ -77,6 +77,9 @@
   "total_time": 30,
   "yields": "8 servings",
   "cuisine": "American,Mexican",
+  "dietary_restrictions": [
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "30 minute meal",
     "quick and easy",
diff --git a/tests/test_data/lovingitvegan.com/lovingitvegan.json b/tests/test_data/lovingitvegan.com/lovingitvegan.json
index 29d54560..7b16ea70 100644
--- a/tests/test_data/lovingitvegan.com/lovingitvegan.json
+++ b/tests/test_data/lovingitvegan.com/lovingitvegan.json
@@ -58,6 +58,9 @@
   "yields": "2 servings",
   "cuisine": "American,Vegan",
   "prep_time": 10,
+  "dietary_restrictions": [
+    "Vegan Diet"
+  ],
   "keywords": [
     "kale smoothie",
     "smoothies",
diff --git a/tests/test_data/ohsheglows.com/ohsheglows.json b/tests/test_data/ohsheglows.com/ohsheglows.json
index 124ae7cf..dab65109 100644
--- a/tests/test_data/ohsheglows.com/ohsheglows.json
+++ b/tests/test_data/ohsheglows.com/ohsheglows.json
@@ -54,6 +54,11 @@
   "title": "Obsession-Worthy Peanut Butter Cookie Ice Cream",
   "total_time": 22,
   "yields": "8 cups",
+  "dietary_restrictions": [
+    "Gluten Free Diet",
+    "Vegan Diet",
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "Vegan",
     "Gluten-Free",
diff --git a/tests/test_data/pressureluckcooking.com/pressureluckcooking.json b/tests/test_data/pressureluckcooking.com/pressureluckcooking.json
index dce6e785..4c9cf185 100644
--- a/tests/test_data/pressureluckcooking.com/pressureluckcooking.json
+++ b/tests/test_data/pressureluckcooking.com/pressureluckcooking.json
@@ -68,6 +68,9 @@
   "title": "Instant Pot Jeffrey's Favorite Chicken",
   "total_time": 30,
   "yields": "6 servings",
+  "dietary_restrictions": [
+    "Gluten Free Diet"
+  ],
   "cook_time": 5,
   "prep_time": 10,
   "keywords": [
diff --git a/tests/test_data/rainbowplantlife.com/rainbowplantlife.json b/tests/test_data/rainbowplantlife.com/rainbowplantlife.json
index bb2bafbe..bf29f2af 100644
--- a/tests/test_data/rainbowplantlife.com/rainbowplantlife.json
+++ b/tests/test_data/rainbowplantlife.com/rainbowplantlife.json
@@ -76,6 +76,9 @@
   "title": "Vegan Brown Butter Peach Cobbler",
   "total_time": 60,
   "yields": "10 servings",
+  "dietary_restrictions": [
+    "Vegan"
+  ],
   "keywords": [
     "cobbler",
     "nut-free",
diff --git a/tests/test_data/rainbowplantlife.com/rainbowplantlife_groups.json b/tests/test_data/rainbowplantlife.com/rainbowplantlife_groups.json
index 6f6d8848..d24381af 100644
--- a/tests/test_data/rainbowplantlife.com/rainbowplantlife_groups.json
+++ b/tests/test_data/rainbowplantlife.com/rainbowplantlife_groups.json
@@ -89,6 +89,10 @@
   "title": "Vegan Pasta Salad",
   "total_time": 40,
   "yields": "10 servings",
+  "dietary_restrictions": [
+    "Vegan",
+    "Vegetarian"
+  ],
   "keywords": [
     "pasta salad",
     "roasted red pepper pasta",
diff --git a/tests/test_data/recept.se/recept.json b/tests/test_data/recept.se/recept.json
index c2b0b340..d35c4b61 100644
--- a/tests/test_data/recept.se/recept.json
+++ b/tests/test_data/recept.se/recept.json
@@ -41,6 +41,9 @@
   "title": "Chokladbollar – grundrecept",
   "total_time": 30,
   "yields": "25 servings",
+  "dietary_restrictions": [
+    "Low Lactose Diet"
+  ],
   "keywords": [
     "laktosfri",
     "vegetarian",
diff --git a/tests/test_data/reciperunner.com/reciperunner.json b/tests/test_data/reciperunner.com/reciperunner.json
index d65b2e98..a6fa0b8a 100644
--- a/tests/test_data/reciperunner.com/reciperunner.json
+++ b/tests/test_data/reciperunner.com/reciperunner.json
@@ -72,6 +72,9 @@
   "title": "Vanilla and Chocolate Pizzelles",
   "total_time": 30,
   "yields": "70 servings",
+  "dietary_restrictions": [
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "cookies",
     "pizzelles",
diff --git a/tests/test_data/redhousespice.com/redhousespice_2.json b/tests/test_data/redhousespice.com/redhousespice_2.json
index d0db5f47..989b40d0 100644
--- a/tests/test_data/redhousespice.com/redhousespice_2.json
+++ b/tests/test_data/redhousespice.com/redhousespice_2.json
@@ -57,6 +57,10 @@
   "title": "Smashed Cucumber Salad (拍黄瓜)",
   "total_time": 15,
   "yields": "2 servings",
+  "dietary_restrictions": [
+    "Vegan Diet",
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "cucumber salad",
     "smashed cucumber salad"
diff --git a/tests/test_data/sweetcsdesigns.com/sweetcsdesigns.json b/tests/test_data/sweetcsdesigns.com/sweetcsdesigns.json
index 34240982..8c86a41d 100644
--- a/tests/test_data/sweetcsdesigns.com/sweetcsdesigns.json
+++ b/tests/test_data/sweetcsdesigns.com/sweetcsdesigns.json
@@ -54,6 +54,9 @@
   "title": "The Best Easy Air Fryer French Fries",
   "total_time": 85,
   "yields": "6 servings",
+  "dietary_restrictions": [
+    "Gluten Free Diet"
+  ],
   "keywords": [
     "side dish",
     "potatoes",
diff --git a/tests/test_data/theclevercarrot.com/theclevercarrot_1.json b/tests/test_data/theclevercarrot.com/theclevercarrot_1.json
index 41fe998f..894f7017 100644
--- a/tests/test_data/theclevercarrot.com/theclevercarrot_1.json
+++ b/tests/test_data/theclevercarrot.com/theclevercarrot_1.json
@@ -107,6 +107,9 @@
   "total_time": 930,
   "yields": "8 servings",
   "cooking_method": "Oven-Baked",
+  "dietary_restrictions": [
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "sourdough",
     "sourdough recipes",
diff --git a/tests/test_data/vegrecipesofindia.com/vegrecipesofindia.json b/tests/test_data/vegrecipesofindia.com/vegrecipesofindia.json
index b6ddb80f..9b7d16b6 100644
--- a/tests/test_data/vegrecipesofindia.com/vegrecipesofindia.json
+++ b/tests/test_data/vegrecipesofindia.com/vegrecipesofindia.json
@@ -91,6 +91,10 @@
   "title": "Sarson ka Saag Recipe (Authentic Punjabi Style)",
   "total_time": 240,
   "yields": "8 servings",
+  "dietary_restrictions": [
+    "Vegan Diet",
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "Sarson da Saag",
     "Sarson ka Saag"
diff --git a/tests/test_data/watchwhatueat.com/watchwhatueat.json b/tests/test_data/watchwhatueat.com/watchwhatueat.json
index ae044806..dc80c7c6 100644
--- a/tests/test_data/watchwhatueat.com/watchwhatueat.json
+++ b/tests/test_data/watchwhatueat.com/watchwhatueat.json
@@ -71,6 +71,11 @@
   "title": "Healthy Instant Pot Cauliflower With Gravy",
   "total_time": 25,
   "yields": "4 servings",
+  "dietary_restrictions": [
+    "Low Fat Diet",
+    "Vegan Diet",
+    "Vegetarian Diet"
+  ],
   "keywords": [
     "instant pot cauliflower",
     "instant pot whole cauliflower"