Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add bergamot #1064

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ Scrapers available for:
- `https://bbc.com/ <https://bbc.com/food/recipes>`_
- `https://bbc.co.uk/ <http://bbc.co.uk/food/recipes>`_
- `https://bbcgoodfood.com/ <https://bbcgoodfood.com>`_
- `https://dashboard.bergamot.app/ <https://dashboard.bergamot.app/>`_
- `https://bestrecipes.com.au/ <https://bestrecipes.com.au>`_
- `https://bettybossi.ch/ <https://bettybossi.ch>`_
- `https://bettycrocker.com/ <https://bettycrocker.com>`_
Expand Down
2 changes: 2 additions & 0 deletions recipe_scrapers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .barefootcontessa import BareFootContessa
from .bbcfood import BBCFood
from .bbcgoodfood import BBCGoodFood
from .bergamot import Bergamot
from .bestrecipes import BestRecipes
from .bettybossi import BettyBossi
from .bettycrocker import BettyCrocker
Expand Down Expand Up @@ -338,6 +339,7 @@
BakingSense.host(): BakingSense,
BakingMischief.host(): BakingMischief,
BareFootContessa.host(): BareFootContessa,
Bergamot.host(): Bergamot,
BestRecipes.host(): BestRecipes,
BettyBossi.host(): BettyBossi,
BettyCrocker.host(): BettyCrocker,
Expand Down
86 changes: 86 additions & 0 deletions recipe_scrapers/bergamot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# mypy: allow-untyped-defs
import requests

from ._abstract import HEADERS, AbstractScraper
from ._utils import url_path_to_dict


class Bergamot(AbstractScraper):
def __init__(self, url, proxies=None, timeout=None, *args, **kwargs):
super().__init__(url=url, proxies=proxies, timeout=timeout, *args, **kwargs)

url_dict = url_path_to_dict(url)
path = url_dict.get("path")
recipe_id = path.split("/")[-1]

data_url = f"https://api.bergamot.app/recipes/shared?r={recipe_id}"
response = requests.get(
data_url, headers=HEADERS, proxies=proxies, timeout=timeout
)
self.data = response.json()

@classmethod
def host(cls):
return "dashboard.bergamot.app"

def canonical_url(self):
return self.data.get("sourceUrl") or self.url

def author(self):
return self.data.get("sourceDomain")

def title(self):
return self.data.get("title")

def category(self):
return None

def total_time(self):
return self._get_time_value("totalTime")

def yields(self):
servings = self.data.get("servings")
return f"{servings} servings"

def image(self):
photos = self.data.get("photos")
if not photos:
return

photo = photos[0]
return photo.get("sourceUrl")

def ingredients(self):
return self._map_list("ingredients")

def instructions(self):
instructions_list = self._map_list("instructions")
return "\n".join(instructions_list)

def ratings(self):
return None

def cuisine(self):
return None
mlduff marked this conversation as resolved.
Show resolved Hide resolved

def description(self):
return self.data.get("description")

def prep_time(self):
return self._get_time_value("prepTime")

def cook_time(self):
return self._get_time_value("cookTime")

def _map_list(self, data_key):
output = []
for entry in self.data.get(data_key):
output.extend(entry.get("data"))
return output

def _get_time_value(self, time_key):
time_values = self.data.get("time")
if not time_values:
return None

return time_values.get(time_key)
67 changes: 67 additions & 0 deletions tests/legacy/test_bergamot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from responses import GET

from recipe_scrapers.bergamot import Bergamot
from tests.legacy import ScraperTest


class TestBergamotScraper(ScraperTest):
scraper_class = Bergamot

@classmethod
def expected_requests(cls):
yield GET, "https://dashboard.bergamot.app/shared/mIB4jYQtZU1A97", "tests/legacy/test_data/bergamot.testhtml"
yield GET, "https://api.bergamot.app/recipes/shared?r=mIB4jYQtZU1A97", "tests/legacy/test_data/bergamot.testjson"

def test_canonical_url(self):
self.assertEqual(
self.harvester_class.canonical_url(),
"https://www.elle.fr/Elle-a-Table/Recettes-de-cuisine/Soupe-miso-aux-oignons-nouveaux-tofu-et-saumon-emiette-4188650",
)

def test_host(self):
self.assertEqual("dashboard.bergamot.app", self.harvester_class.host())

def test_title(self):
self.assertEqual(
"Soupe miso aux oignons nouveaux, tofu et saumon émietté",
self.harvester_class.title(),
)

def test_author(self):
self.assertEqual("elle.fr", self.harvester_class.author())

def test_total_time(self):
self.assertEqual(50, self.harvester_class.total_time())

def test_yields(self):
self.assertEqual("4 servings", self.harvester_class.yields())

def test_ingredients(self):
self.assertEqual(
[
"100 g de saumon frais",
"6 oignons nouveaux",
"30 g d'algues wakame séchées",
"70 g de pâte de miso blanc",
"quelques cives",
"300 g de tofu soyeux",
"1 cuillère(s) à soupe d'huile de sésame",
"1 cuillère(s) à soupe de graines de sésame",
],
self.harvester_class.ingredients(),
)

def test_instructions(self):
return self.assertEqual(
"Dans une poêle bien chaude, faites cuire le saumon côté peau pendant 5 mn, puis laissez-le refroidir avant de l’émietter.\nDans une casserole, versez 1,5l d’eau, la moitié des oignons lavés et coupés en deux dans la hauteur, et les algues, puis portez à ébullition, réduisez ensuite le feu et laissez mijoter pendant 20 mn. Filtrez et ajoutez le miso, mélangez soigneusement.\nAjoutez le reste des oignons coupés en quatre, les cives lavées et émincées, le tofu coupé en dés et les miettes de saumon. Arrosez d’huile de sésame et parsemez de graines de sésame. Dégustez bien chaud.",
self.harvester_class.instructions(),
)

def test_ratings(self):
self.assertEqual(None, self.harvester_class.ratings())

def test_cook_time(self):
self.assertEqual(None, self.harvester_class.cook_time())

def test_prep_time(self):
self.assertEqual(20, self.harvester_class.prep_time())
1 change: 1 addition & 0 deletions tests/legacy/test_data/bergamot.testhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/><meta name="description" content="Bergamot"/><meta name="robots" content="noindex"><script defer="defer" src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script><link rel="icon" href="/favicon/favicon32.png" sizes="32x32"><link rel="icon" href="/favicon/favicon57.png" sizes="57x57"><link rel="icon" href="/favicon/favicon76.png" sizes="76x76"><link rel="icon" href="/favicon/favicon96.png" sizes="96x96"><link rel="icon" href="/favicon/favicon128.png" sizes="128x128"><link rel="icon" href="/favicon/favicon192.png" sizes="192x192"><link rel="icon" href="/favicon/favicon228.png" sizes="228x228"><link rel="shortcut icon" sizes="196x196" href="/favicon/favicon196.png"><link rel="apple-touch-icon" href="/favicon/favicon120.png" sizes="120x120"><link rel="apple-touch-icon" href="/favicon/favicon152.png" sizes="152x152"><link rel="apple-touch-icon" href="/favicon/favicon180.png" sizes="180x180"><link rel="manifest" href="/manifest.json"/><title>Bergamot</title><link href="/static/css/2.fd6bfe4a.chunk.css" rel="stylesheet"><link href="/static/css/main.6c887899.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,a=r[0],f=r[1],i=r[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var f=t[a];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this.webpackJsonpbergamot=this.webpackJsonpbergamot||[],f=a.push.bind(a);a.push=r,a=a.slice();for(var i=0;i<a.length;i++)r(a[i]);var p=f;t()}([])</script><script src="/static/js/2.f6f0379b.chunk.js"></script><script src="/static/js/main.6b7d6305.chunk.js"></script></body></html>
1 change: 1 addition & 0 deletions tests/legacy/test_data/bergamot.testjson
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":210338,"shortId":"mIB4jYQtZU1A97","userId":585,"userFavorite":0,"sourceId":10,"sourceUrl":"https://www.elle.fr/Elle-a-Table/Recettes-de-cuisine/Soupe-miso-aux-oignons-nouveaux-tofu-et-saumon-emiette-4188650","lang":"","title":"Soupe miso aux oignons nouveaux, tofu et saumon émietté","description":"La soupe miso enrichie de saumon.","userNote":null,"ingredients":[{"data":["100 g de saumon frais","6 oignons nouveaux","30 g d'algues wakame séchées","70 g de pâte de miso blanc","quelques cives","300 g de tofu soyeux","1 cuillère(s) à soupe d'huile de sésame","1 cuillère(s) à soupe de graines de sésame"]}],"instructions":[{"data":["Dans une poêle bien chaude, faites cuire le saumon côté peau pendant 5 mn, puis laissez-le refroidir avant de l’émietter.","Dans une casserole, versez 1,5l d’eau, la moitié des oignons lavés et coupés en deux dans la hauteur, et les algues, puis portez à ébullition, réduisez ensuite le feu et laissez mijoter pendant 20 mn. Filtrez et ajoutez le miso, mélangez soigneusement.","Ajoutez le reste des oignons coupés en quatre, les cives lavées et émincées, le tofu coupé en dés et les miettes de saumon. Arrosez d’huile de sésame et parsemez de graines de sésame. Dégustez bien chaud."]}],"time":{"prepTime":20,"cookTime":null,"totalTime":50},"nutrition":{},"servings":4,"createdAt":"2024-01-16T16:16:24.000Z","updatedAt":"2024-01-16T16:16:24.000Z","deletedAt":null,"photos":[{"id":198989,"recipeId":210338,"reference":"210338PZAE79ER","order":0,"status":"uploaded","isUserUploaded":0,"sourceUrl":"https://resize.elle.fr/portrait_1280/var/plain_site/storage/images/elle-a-table/recettes-de-cuisine/soupe-miso-aux-oignons-nouveaux-tofu-et-saumon-emiette-4188650/101348896-2-fre-FR/Soupe-miso-aux-oignons-nouveaux-tofu-et-saumon-emiette.jpg","filenameExtension":"jpg","createdAt":"2024-01-16T16:16:24.000Z","updatedAt":"2024-01-16T16:16:24.000Z","deletedAt":null,"photoUrl":"https://aihkimhfpo.cloudimg.io/v7/foodbox/210338PZAE79ER.jpg?w=1280","photoThumbUrl":"https://aihkimhfpo.cloudimg.io/v7/foodbox/210338PZAE79ER.jpg?w=600&h=338"}],"sourceDomain":"elle.fr"}
Loading