diff --git a/modules/750g/__init__.py b/modules/750g/__init__.py new file mode 100644 index 00000000..da6bec56 --- /dev/null +++ b/modules/750g/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Julien Veyssier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from .backend import SevenFiftyGramsBackend + +__all__ = ['SevenFiftyGramsBackend'] diff --git a/modules/750g/backend.py b/modules/750g/backend.py new file mode 100644 index 00000000..7ead3b8f --- /dev/null +++ b/modules/750g/backend.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Julien Veyssier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from weboob.capabilities.recipe import ICapRecipe,Recipe +from weboob.tools.backend import BaseBackend + +from .browser import SevenFiftyGramsBrowser + +from urllib import quote_plus + +__all__ = ['SevenFiftyGramsBackend'] + + +class SevenFiftyGramsBackend(BaseBackend, ICapRecipe): + NAME = '750g' + MAINTAINER = u'Julien Veyssier' + EMAIL = 'julien.veyssier@aiur.fr' + VERSION = '0.f' + DESCRIPTION = '750g recipe website' + LICENSE = 'AGPLv3+' + BROWSER = SevenFiftyGramsBrowser + + def create_default_browser(self): + return self.create_browser() + + def get_recipe(self, id): + return self.browser.get_recipe(id) + + def iter_recipes(self, pattern): + return self.browser.iter_recipes(pattern.encode('utf-8')) + + def fill_recipe(self, recipe, fields): + if 'thumbnail_url' in fields or 'instructions' in fields: + rec = self.get_recipe(recipe.id) + recipe.picture_url = rec.picture_url + recipe.instructions = rec.instructions + recipe.ingredients = rec.ingredients + recipe.comments = rec.comments + recipe.nb_person = rec.nb_person + recipe.cooking_time = rec.cooking_time + recipe.preparation_time = rec.preparation_time + + return recipe + + OBJECTS = { + Recipe:fill_recipe, + } diff --git a/modules/750g/browser.py b/modules/750g/browser.py new file mode 100644 index 00000000..39e677e2 --- /dev/null +++ b/modules/750g/browser.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Julien Veyssier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from weboob.tools.browser import BaseBrowser + +from .pages import RecipePage, ResultsPage + + +__all__ = ['SevenFiftyGramsBrowser'] + +class SevenFiftyGramsBrowser(BaseBrowser): + DOMAIN = 'www.750g.com' + PROTOCOL = 'http' + ENCODING = 'utf-8' + USER_AGENT = BaseBrowser.USER_AGENTS['wget'] + PAGES = { + 'http://www.750g.com/recettes_.*.htm': ResultsPage, + 'http://www.750g.com/.*r[0-9]*.htm': RecipePage, + } + + def iter_recipes(self, pattern): + self.location('http://www.750g.com/recettes_%s.htm' % (pattern.replace(' ','_'))) + assert self.is_on_page(ResultsPage) + return self.page.iter_recipes() + + def get_recipe(self, id): + self.location('http://www.750g.com/%s.htm' % id) + assert self.is_on_page(RecipePage) + return self.page.get_recipe(id) diff --git a/modules/750g/favicon.png b/modules/750g/favicon.png new file mode 100644 index 00000000..9b8c4d27 Binary files /dev/null and b/modules/750g/favicon.png differ diff --git a/modules/750g/pages.py b/modules/750g/pages.py new file mode 100644 index 00000000..b5674741 --- /dev/null +++ b/modules/750g/pages.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Julien Veyssier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from weboob.capabilities.recipe import Recipe +from weboob.capabilities.base import NotAvailable, NotLoaded +from weboob.tools.browser import BasePage + + +__all__ = ['RecipePage', 'ResultsPage'] + + +class ResultsPage(BasePage): + """ Page which contains results as a list of recipies + """ + def iter_recipes(self): + for div in self.parser.select(self.document.getroot(),'div.recette_description > div.data'): + links = self.parser.select(div,'div.info > p.title > a.fn') + if len(links) > 0: + link = links[0] + title = unicode(link.text) + id = unicode(link.attrib.get('href','').strip('/').replace('.htm','htm')) + thumbnail_url = NotAvailable + short_description = NotAvailable + + imgs = self.parser.select(div,'img.recipe-image') + if len(imgs) > 0: + thumbnail_url = unicode(imgs[0].attrib.get('src','')) + short_description = unicode(' '.join(self.parser.select(div,'div.infos_column',1).text_content().split()).strip()) + imgs_cost = self.parser.select(div,'div.infos_column img') + cost_tot = len(imgs_cost) + cost_on = 0 + for img in imgs_cost: + if img.attrib.get('src','').endswith('euro_on.png'): + cost_on += 1 + short_description += u' %s/%s'%(cost_on,cost_tot) + + recipe = Recipe(id,title) + recipe.thumbnail_url = thumbnail_url + recipe.short_description= short_description + recipe.instructions = NotLoaded + recipe.ingredients = NotLoaded + recipe.nb_person = NotLoaded + recipe.cooking_time = NotLoaded + recipe.preparation_time = NotLoaded + yield recipe + + + +class RecipePage(BasePage): + """ Page which contains a recipe + """ + def get_recipe(self, id): + title = NotAvailable + preparation_time = NotAvailable + cooking_time = NotAvailable + nb_person = NotAvailable + ingredients = NotAvailable + picture_url = NotAvailable + instructions = NotAvailable + comments = [] + + title = unicode(self.parser.select(self.document.getroot(),'h1.m_title',1).text_content().strip()) + main = self.parser.select(self.document.getroot(),'div.m_content_recette_main',1) + preparation_time = int(self.parser.select(main,'p.m_content_recette_info span.preptime',1).text_content()) + cooking_time = int(self.parser.select(main,'p.m_content_recette_info span.cooktime',1).text_content()) + ing_header_line = self.parser.select(main,'p.m_content_recette_ingredients span',1).text_content() + if '(pour' in ing_header_line and ')' in ing_header_line: + nb_person = int(ing_header_line.split('pour ')[-1].split('personnes)')[0].split()[0]) + ingredients = self.parser.select(main,'p.m_content_recette_ingredients',1).text_content().strip().split('- ') + ingredients=ingredients[1:] + rinstructions = self.parser.select(main,'div.m_content_recette_todo',1).text_content().strip() + instructions = u'' + for line in rinstructions.split('\n'): + instructions += '%s\n'%line.strip() + instructions = instructions.strip('\n') + imgillu = self.parser.select(self.document.getroot(),'a.m_content_recette_illu img') + if len(imgillu) > 0: + picture_url = unicode(imgillu[0].attrib.get('src','')) + for divcom in self.parser.select(self.document.getroot(),'div.m_commentaire_row'): + note = self.parser.select(divcom,'div.m_commentaire_note span',1).text.strip() + user = self.parser.select(divcom,'div.m_commentaire_content span',1).text.strip() + content = self.parser.select(divcom,'div.m_commentaire_content p',1).text.strip() + comments.append(u'user: %s, note: %s, comment: %s'%(user,note,content)) + + recipe = Recipe(id,title) + recipe.preparation_time = preparation_time + recipe.cooking_time = cooking_time + recipe.nb_person = nb_person + recipe.ingredients = ingredients + recipe.instructions = instructions + recipe.picture_url = picture_url + recipe.comments = comments + recipe.thumbnail_url = NotLoaded + return recipe diff --git a/modules/750g/test.py b/modules/750g/test.py new file mode 100644 index 00000000..f4fdf1fa --- /dev/null +++ b/modules/750g/test.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Julien Veyssier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from weboob.tools.test import BackendTest + +class SevenFiftyGramsTest(BackendTest): + BACKEND = '750g' + + def test_recipe(self): + recipes = self.backend.iter_recipes('fondue') + for recipe in recipes: + full_recipe = self.backend.get_recipe(recipe.id) + assert full_recipe.instructions + assert full_recipe.ingredients + assert full_recipe.title + assert full_recipe.preparation_time + diff --git a/modules/marmiton/pages.py b/modules/marmiton/pages.py index 95c2b4eb..11c077a2 100644 --- a/modules/marmiton/pages.py +++ b/modules/marmiton/pages.py @@ -38,7 +38,7 @@ class ResultsPage(BasePage): short_description = NotAvailable imgs = self.parser.select(tds[0],'img') if len(imgs) > 0: - thumbnail_url = unicode(imgs[0].attrib('src','')) + thumbnail_url = unicode(imgs[0].attrib.get('src','')) link = self.parser.select(tds[1],'div.m_search_titre_recette a',1) title = unicode(link.text) id = link.attrib.get('href','').replace('.aspx','').replace('/recettes/recette_','')