-
Notifications
You must be signed in to change notification settings - Fork 192
New image source europeana #808
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
base: master
Are you sure you want to change the base?
Changes from 9 commits
71e7a6b
85fbece
55bf6b8
f028a9e
a8978df
6cd5b0a
fe16f8b
ac9e857
9acadc4
aad124f
2e790bf
3d2d435
48e2a8a
ac5bf07
63ca14e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,3 +68,4 @@ venv/ | |
| ENV/ | ||
| env.bak/ | ||
| venv.bak/ | ||
| *venv/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| #!/usr/bin/python3 | ||
| # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
| ### BEGIN LICENSE | ||
| # Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com> | ||
| # This program is free software: you can redistribute it and/or modify it | ||
| # under the terms of the GNU General Public License version 3, as published | ||
| # by the Free Software Foundation. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, but | ||
| # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
| # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
| # PURPOSE. See the GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License along | ||
| # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| ### END LICENSE | ||
|
|
||
| import unittest | ||
|
|
||
| from tests.TestDownloader import test_download_one_for | ||
| from variety.AttrDict import AttrDict | ||
| from variety.plugins.builtin.downloaders.EuropeanaConfigurableSource import ( | ||
| EuropeanaConfigurableSource, | ||
| ) | ||
|
|
||
|
|
||
| class TestEuropeanaConfigurableDownloader(unittest.TestCase): | ||
| def _source(self): | ||
| parent = AttrDict() | ||
| parent.size_ok = lambda x, y: True | ||
| source = EuropeanaConfigurableSource() | ||
| source.set_variety(parent) | ||
| return source | ||
|
|
||
| def test_download_one(self): | ||
| test_download_one_for(self, self._source().create_downloader("sea")) | ||
|
|
||
| def test_validate(self): | ||
| source = self._source() | ||
| self.assertIsNone(source.validate("")[1]) | ||
| self.assertIsNone(source.validate("forest")[1]) | ||
|
|
||
| def test_fill_queue(self): | ||
| dl = self._source().create_downloader("Rome") | ||
| queue = dl.fill_queue() | ||
| self.assertTrue(len(queue) > 0) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| #!/usr/bin/python3 | ||
| # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
| ### BEGIN LICENSE | ||
| # Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com> | ||
| # This program is free software: you can redistribute it and/or modify it | ||
| # under the terms of the GNU General Public License version 3, as published | ||
| # by the Free Software Foundation. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, but | ||
| # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
| # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
| # PURPOSE. See the GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License along | ||
| # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| ### END LICENSE | ||
|
|
||
| import unittest | ||
|
|
||
| from tests.TestDownloader import get_plugin_downloader, test_download_one_for | ||
|
|
||
|
|
||
| class TestEuropeanaDownloader(unittest.TestCase): | ||
| def test_download_one(self): | ||
| dl = get_plugin_downloader("EuropeanaDownloader") | ||
| test_download_one_for(self, dl) | ||
|
|
||
| def test_fill_queue(self): | ||
| dl = get_plugin_downloader("EuropeanaDownloader") | ||
| dl.target_folder = "/tmp/variety/" | ||
| queue = dl.fill_queue() | ||
| self.assertTrue(len(queue) > 0) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,102 @@ | ||||||
| # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||||||
| ### BEGIN LICENSE | ||||||
| # Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com> | ||||||
|
jlu5 marked this conversation as resolved.
Outdated
|
||||||
| # This program is free software: you can redistribute it and/or modify it | ||||||
| # under the terms of the GNU General Public License version 3, as published | ||||||
| # by the Free Software Foundation. | ||||||
| # | ||||||
| # This program is distributed in the hope that it will be useful, but | ||||||
| # WITHOUT ANY WARRANTY; without even the implied warranties of | ||||||
| # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||||||
| # PURPOSE. See the GNU General Public License for more details. | ||||||
| # | ||||||
| # You should have received a copy of the GNU General Public License along | ||||||
| # with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
| ### END LICENSE | ||||||
| import logging | ||||||
| import random | ||||||
|
|
||||||
| from requests import HTTPError | ||||||
|
|
||||||
| from variety.plugins.builtin.downloaders.EuropeanaDownloader import EuropeanaDownloader | ||||||
| from variety.plugins.downloaders.ConfigurableImageSource import ConfigurableImageSource | ||||||
| from variety.plugins.downloaders.DefaultDownloader import DefaultDownloader | ||||||
| from variety.plugins.downloaders.ImageSource import Throttling | ||||||
| from variety.Util import Util, _ | ||||||
|
|
||||||
| logger = logging.getLogger("variety") | ||||||
|
|
||||||
| random.seed() | ||||||
|
|
||||||
|
|
||||||
| class UnsupportedConfig(Exception): | ||||||
| pass | ||||||
|
|
||||||
|
|
||||||
| class EuropeanaConfigurableSource(ConfigurableImageSource): | ||||||
| class EuropeanaConfigurableDownloader(EuropeanaDownloader): | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be a top level class (I'm not sure why it's laid out this way in the Unsplash downloader) |
||||||
| def __init__(self, source, config): | ||||||
| DefaultDownloader.__init__(self, source, config) | ||||||
| self.set_variety(source.get_variety()) | ||||||
|
|
||||||
| def get_source_type(self): | ||||||
| return self.source.get_source_type() | ||||||
|
|
||||||
| def get_description(self): | ||||||
| return self.config | ||||||
|
|
||||||
| def get_folder_name(self): | ||||||
| return super(DefaultDownloader, self).get_folder_name() | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this function override isn't needed |
||||||
|
|
||||||
| def get_europeana_api_url(self): | ||||||
| return super().get_europeana_api_url() | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function shouldn't be needed as all it does is call its parent |
||||||
|
|
||||||
| @classmethod | ||||||
| def get_info(cls): | ||||||
| return { | ||||||
| "name": "EuropeanaConfigurableSource", | ||||||
| "description": _("Configurable source for fetching photos from Europeana.com"), | ||||||
| "author": "Andrea Pasquali", | ||||||
| "version": "0.1", | ||||||
| } | ||||||
|
|
||||||
| def get_source_type(self): | ||||||
| return "europeana-search" | ||||||
|
|
||||||
| def validate(self, config): | ||||||
| try: | ||||||
| url = self.EuropeanaConfigurableDownloader(self, config).get_europeana_api_url() | ||||||
| data = Util.fetch_json(url) | ||||||
| valid = data.get("success") == True | ||||||
| return config, None if valid else _("No images found") | ||||||
| except UnsupportedConfig: | ||||||
| return config, _("Something's wrong with your search parameter") | ||||||
| except Exception as e: | ||||||
| if isinstance(e, HTTPError) and e.response.status_code == 404: | ||||||
| return config, _("No images found") | ||||||
| else: | ||||||
| return config, _("Oops, this didn't work. Is the remote service up?") | ||||||
|
|
||||||
| def create_downloader(self, config): | ||||||
| return self.EuropeanaConfigurableDownloader(self, config) | ||||||
|
|
||||||
| def get_ui_instruction(self): | ||||||
| return _( | ||||||
| "We use the <a href='https://europeana.eu'>Europeana</a> API to fetch random artwork images that match the given search keyword.\n" | ||||||
| "The Europeana API is rate-limited, so Variety fetches images from Europeana sources as a reduced rate.\n" | ||||||
| "\n" | ||||||
| "Please specify a search keyword\n" | ||||||
| "Keyword example: sea, forest, Rome" | ||||||
| ) | ||||||
|
|
||||||
| def get_ui_short_instruction(self): | ||||||
| return _("Search keyword: ") | ||||||
|
|
||||||
| def get_ui_short_description(self): | ||||||
| return _("Fetch artwork images from Europeana.eu for a given keyword") | ||||||
|
|
||||||
| def get_source_name(self): | ||||||
| return "Europeana" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
seems we always use lowercase for sources by convention |
||||||
|
|
||||||
| def get_default_throttling(self): | ||||||
| return Throttling(max_downloads_per_hour=120, max_queue_fills_per_hour=4) | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,126 @@ | ||||||
| # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||||||
| ### BEGIN LICENSE | ||||||
| # Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com> | ||||||
| # This program is free software: you can redistribute it and/or modify it | ||||||
| # under the terms of the GNU General Public License version 3, as published | ||||||
| # by the Free Software Foundation. | ||||||
| # | ||||||
| # This program is distributed in the hope that it will be useful, but | ||||||
| # WITHOUT ANY WARRANTY; without even the implied warranties of | ||||||
| # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||||||
| # PURPOSE. See the GNU General Public License for more details. | ||||||
| # | ||||||
| # You should have received a copy of the GNU General Public License along | ||||||
| # with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
| ### END LICENSE | ||||||
| import logging | ||||||
| import random | ||||||
|
|
||||||
| from variety.plugins.downloaders.ImageSource import Throttling | ||||||
| from variety.plugins.downloaders.SimpleDownloader import SimpleDownloader | ||||||
| from variety.Util import Util, _ | ||||||
|
|
||||||
| logger = logging.getLogger("variety") | ||||||
|
|
||||||
| random.seed() | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be needed, random is automatically seeded by default |
||||||
|
|
||||||
|
|
||||||
| class EuropeanaDownloader(SimpleDownloader): | ||||||
|
|
||||||
| DESCRIPTION = _("Europe's digital cultural heritage from Europeana.eu") | ||||||
|
|
||||||
| # This API key has been requested from Andrea Pasquali to Europeana.ue in order to use it for Variety project | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| API_KEY = "damolystist" | ||||||
|
|
||||||
| @classmethod | ||||||
| def get_info(cls): | ||||||
| return { | ||||||
| "name": "EuropeanaDownloader", | ||||||
| "description": EuropeanaDownloader.DESCRIPTION, | ||||||
| "author": "Andrea Pasquali", | ||||||
| "version": "0.1", | ||||||
| } | ||||||
|
|
||||||
| def get_source_type(self): | ||||||
| return "Europeana" | ||||||
|
|
||||||
| def get_description(self): | ||||||
| return EuropeanaDownloader.DESCRIPTION | ||||||
|
|
||||||
| def get_source_name(self): | ||||||
| return "Europeana.eu" | ||||||
|
|
||||||
| def get_source_location(self): | ||||||
| return "https://www.europeana.eu" | ||||||
|
|
||||||
| def get_folder_name(self): | ||||||
| return "Europeana" | ||||||
|
|
||||||
| def get_default_throttling(self): | ||||||
| return Throttling(max_downloads_per_hour=120, max_queue_fills_per_hour=4) | ||||||
|
|
||||||
| def get_europeana_api_url(self): | ||||||
| url = "https://api.europeana.eu/record/v2/search.json?wskey={api_key}&query={query}&sort={sort}&rows={rows}&profile={profile}&reusability={reusability}&media={media}&europeana_completeness:{completeness}&qf=collection:{collection}&qf=TYPE:{type}&qf=IMAGE_COLOUR:{img_color}&qf=IMAGE_SIZE:{img_size}&qf=IMAGE_ASPECTRATIO:{img_ratio}&qf=MIME_TYPE:{mime}" | ||||||
| return url.format( | ||||||
|
jlu5 marked this conversation as resolved.
Outdated
|
||||||
| api_key=EuropeanaDownloader.API_KEY, | ||||||
| query=f"{self.config if self.config else ''}(painting OR watercolor OR canvas OR artwork) NOT photograph NOT manuscript NOT print NOT book", | ||||||
| sort="random", | ||||||
| rows=30, | ||||||
| profile="minimal", | ||||||
| reusability="open", | ||||||
| media="true", | ||||||
| completeness="[1 TO 10]", | ||||||
| collection="art", | ||||||
| type="IMAGE", | ||||||
| img_color="true", | ||||||
| img_size="extra_large", | ||||||
| img_ratio="landscape", | ||||||
| mime="image/jpeg", | ||||||
|
jlu5 marked this conversation as resolved.
Outdated
|
||||||
| ) | ||||||
|
|
||||||
| def fill_queue(self): | ||||||
|
|
||||||
| url = self.get_europeana_api_url() | ||||||
| logger.info(lambda: "Filling Europeana queue from " + url) | ||||||
|
|
||||||
| r = Util.request(url) | ||||||
| queue = [] | ||||||
| for item in r.json()["items"]: | ||||||
| try: | ||||||
|
|
||||||
| image_url = item["edmIsShownBy"][0] | ||||||
| origin_url = item["guid"] | ||||||
|
|
||||||
| author = [ | ||||||
| creator | ||||||
| for creator in ( | ||||||
| item["dcCreator"] if item.get("dcCreator", None) else [] | ||||||
| ) | ||||||
| if "http" not in creator | ||||||
| ] | ||||||
| author = author[0] if len(author) > 0 else item["dataProvider"][0] | ||||||
| author_url = [ | ||||||
| creator | ||||||
| for creator in (item["dcCreator"] if item.get("dcCreator", None) else []) | ||||||
| if "http" in creator | ||||||
| ] | ||||||
| artwork_url = item['edmIsShownAt'][0] if item.get('edmIsShownAt') else origin_url | ||||||
| extra_metadata = { | ||||||
| "sourceType": "europeana", | ||||||
| "headline": item["title"][-1], | ||||||
| "author": author, | ||||||
| "authorURL": author_url[0] if len(author_url) > 0 else artwork_url, | ||||||
| "description": item["dcDescription"][0][:10000] if item.get("dcDescription") else None, | ||||||
| "keywords": [], | ||||||
| "extraData": { | ||||||
| "provider": item["dataProvider"][0], | ||||||
| }, | ||||||
| } | ||||||
|
|
||||||
| queue.append((origin_url, image_url, extra_metadata)) | ||||||
| except: | ||||||
| logger.exception(lambda: "Could not process an item from Europeana") | ||||||
| raise | ||||||
|
|
||||||
| random.shuffle(queue) | ||||||
|
jlu5 marked this conversation as resolved.
Outdated
|
||||||
| return queue | ||||||
Uh oh!
There was an error while loading. Please reload this page.