diff --git a/brainzutils/musicbrainz_db/artist.py b/brainzutils/musicbrainz_db/artist.py index eca87de..256796c 100644 --- a/brainzutils/musicbrainz_db/artist.py +++ b/brainzutils/musicbrainz_db/artist.py @@ -8,7 +8,7 @@ from brainzutils.musicbrainz_db.includes import check_includes -def get_artist_by_id(mbid, includes=None): +def get_artist_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get artist with MusicBrainz ID. Args: mbid (uuid): MBID(gid) of the artist. @@ -21,10 +21,11 @@ def get_artist_by_id(mbid, includes=None): return fetch_multiple_artists( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_artists(mbids, includes=None): +def fetch_multiple_artists(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple artists using their MusicBrainz IDs. Args: mbids (list): List of MBIDs of artists. @@ -43,6 +44,7 @@ def fetch_multiple_artists(mbids, includes=None): query=query, entity_type='artist', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) artist_ids = [artist.id for artist in artists.values()] diff --git a/brainzutils/musicbrainz_db/editor.py b/brainzutils/musicbrainz_db/editor.py index 16466d6..46ce735 100644 --- a/brainzutils/musicbrainz_db/editor.py +++ b/brainzutils/musicbrainz_db/editor.py @@ -7,7 +7,7 @@ from brainzutils.musicbrainz_db.includes import check_includes -def get_editor_by_id(editor_id, includes=None): +def get_editor_by_id(editor_id, includes=None, unknown_entities_for_missing=False): """Get editor with editor ID. Args: editor_id (int): ID of the editor. @@ -20,10 +20,11 @@ def get_editor_by_id(editor_id, includes=None): return fetch_multiple_editors( [editor_id], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(editor_id) -def fetch_multiple_editors(editor_ids, includes=None): +def fetch_multiple_editors(editor_ids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple editors using their editor IDs. Args: editor_ids (list): List of IDs of editors. @@ -42,6 +43,7 @@ def fetch_multiple_editors(editor_ids, includes=None): query=query, entity_type='editor', ids=editor_ids, + unknown_entities_for_missing=unknown_entities_for_missing, ) editor_ids = [editor.id for editor in editors.values()] editors = {editor_id: serialize_editor(editors[editor_id], includes_data) for editor_id in editor_ids} diff --git a/brainzutils/musicbrainz_db/event.py b/brainzutils/musicbrainz_db/event.py index a80ca20..7f4dbd4 100644 --- a/brainzutils/musicbrainz_db/event.py +++ b/brainzutils/musicbrainz_db/event.py @@ -7,7 +7,7 @@ from brainzutils.musicbrainz_db.helpers import get_relationship_info -def get_event_by_id(mbid, includes=None): +def get_event_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get event with the MusicBrainz ID. Args: @@ -21,10 +21,11 @@ def get_event_by_id(mbid, includes=None): return fetch_multiple_events( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_events(mbids, includes=None): +def fetch_multiple_events(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple events using their MusicBrainz IDs. Args: @@ -44,6 +45,7 @@ def fetch_multiple_events(mbids, includes=None): query=query, entity_type='event', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) event_ids = [event.id for event in events.values()] diff --git a/brainzutils/musicbrainz_db/exceptions.py b/brainzutils/musicbrainz_db/exceptions.py index e407463..292f83e 100644 --- a/brainzutils/musicbrainz_db/exceptions.py +++ b/brainzutils/musicbrainz_db/exceptions.py @@ -3,6 +3,11 @@ class MBDatabaseException(Exception): pass +class InvalidTypeError(MBDatabaseException): + """Exception related to wrong type in present functions""" + pass + + class InvalidIncludeError(MBDatabaseException): """Exception related to wrong includes in present functions""" pass diff --git a/brainzutils/musicbrainz_db/helpers.py b/brainzutils/musicbrainz_db/helpers.py index 5690124..debc72f 100644 --- a/brainzutils/musicbrainz_db/helpers.py +++ b/brainzutils/musicbrainz_db/helpers.py @@ -2,7 +2,7 @@ from mbdata.models import Tag from sqlalchemy.orm import joinedload from sqlalchemy import func -from brainzutils.musicbrainz_db.utils import ENTITY_MODELS +from brainzutils.musicbrainz_db.models import ENTITY_MODELS def get_relationship_info(db, target_type, source_type, source_entity_ids, includes_data): diff --git a/brainzutils/musicbrainz_db/label.py b/brainzutils/musicbrainz_db/label.py index dd5ceb7..06196e2 100644 --- a/brainzutils/musicbrainz_db/label.py +++ b/brainzutils/musicbrainz_db/label.py @@ -8,7 +8,7 @@ from brainzutils.musicbrainz_db.helpers import get_relationship_info -def get_label_by_id(mbid, includes=None): +def get_label_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get label with the MusicBrainz ID. Args: @@ -22,10 +22,11 @@ def get_label_by_id(mbid, includes=None): return fetch_multiple_labels( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_labels(mbids, includes=None): +def fetch_multiple_labels(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple labels using their MusicBrainz IDs. Args: @@ -46,6 +47,7 @@ def fetch_multiple_labels(mbids, includes=None): query=query, entity_type='label', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) label_ids = [label.id for label in labels.values()] diff --git a/brainzutils/musicbrainz_db/models.py b/brainzutils/musicbrainz_db/models.py new file mode 100644 index 0000000..a3ba389 --- /dev/null +++ b/brainzutils/musicbrainz_db/models.py @@ -0,0 +1,30 @@ +from mbdata import models + + +# Entity models +ENTITY_MODELS = { + 'artist': models.Artist, + 'place': models.Place, + 'release_group': models.ReleaseGroup, + 'release': models.Release, + 'event': models.Event, + 'label': models.Label, + 'series': models.Series, + 'url': models.URL, + 'recording': models.Recording, + 'work': models.Work, + 'editor': models.Editor, +} + + +# Redirect models +REDIRECT_MODELS = { + 'place': models.PlaceGIDRedirect, + 'artist': models.ArtistGIDRedirect, + 'release': models.ReleaseGIDRedirect, + 'release_group': models.ReleaseGroupGIDRedirect, + 'event': models.EventGIDRedirect, + 'label': models.LabelGIDRedirect, + 'recording': models.RecordingGIDRedirect, + 'work': models.WorkGIDRedirect, +} diff --git a/brainzutils/musicbrainz_db/place.py b/brainzutils/musicbrainz_db/place.py index c3298e8..a660105 100644 --- a/brainzutils/musicbrainz_db/place.py +++ b/brainzutils/musicbrainz_db/place.py @@ -8,7 +8,7 @@ from brainzutils.musicbrainz_db.utils import get_entities_by_gids -def get_place_by_id(mbid, includes=None): +def get_place_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get place with the MusicBrainz ID. Args: @@ -22,10 +22,11 @@ def get_place_by_id(mbid, includes=None): return fetch_multiple_places( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_places(mbids, includes=None): +def fetch_multiple_places(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple places using their MusicBrainz IDs. Args: @@ -47,6 +48,7 @@ def fetch_multiple_places(mbids, includes=None): query=query, entity_type='place', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) place_ids = [place.id for place in places.values()] diff --git a/brainzutils/musicbrainz_db/recording.py b/brainzutils/musicbrainz_db/recording.py index 6d03a04..eb46c54 100644 --- a/brainzutils/musicbrainz_db/recording.py +++ b/brainzutils/musicbrainz_db/recording.py @@ -8,7 +8,7 @@ from sqlalchemy.orm import joinedload, subqueryload -def get_recording_by_mbid(mbid, includes=None): +def get_recording_by_mbid(mbid, includes=None, unknown_entities_for_missing=False): """ Get recording with MusicBrainz ID. Args: @@ -23,10 +23,11 @@ def get_recording_by_mbid(mbid, includes=None): return fetch_multiple_recordings( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def get_many_recordings_by_mbid(mbids, includes=None): +def get_many_recordings_by_mbid(mbids, includes=None, unknown_entities_for_missing=False): """ Get multiple recordings with MusicBrainz IDs. It fetches recordings using fetch_multiple_recordings. @@ -40,10 +41,14 @@ def get_many_recordings_by_mbid(mbids, includes=None): if includes is None: includes = [] - return fetch_multiple_recordings(mbids, includes) + return fetch_multiple_recordings( + mbids, + includes, + unknown_entities_for_missing=unknown_entities_for_missing, + ) -def fetch_multiple_recordings(mbids, includes=None): +def fetch_multiple_recordings(mbids, includes=None, unknown_entities_for_missing=False): """ Fetch multiple recordings with MusicBrainz IDs. Args: @@ -78,6 +83,7 @@ def fetch_multiple_recordings(mbids, includes=None): query=query, entity_type='recording', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) recording_ids = [recording.id for recording in recordings.values()] diff --git a/brainzutils/musicbrainz_db/release.py b/brainzutils/musicbrainz_db/release.py index 1c29196..b1c0bcf 100644 --- a/brainzutils/musicbrainz_db/release.py +++ b/brainzutils/musicbrainz_db/release.py @@ -10,7 +10,7 @@ from brainzutils.musicbrainz_db import recording -def get_release_by_id(mbid, includes=None): +def get_release_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get release with the MusicBrainz ID. Args: mbid (uuid): MBID(gid) of the release. @@ -23,10 +23,11 @@ def get_release_by_id(mbid, includes=None): return fetch_multiple_releases( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_releases(mbids, includes=None): +def fetch_multiple_releases(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple releases using their MusicBrainz IDs. Args: mbids (list): List of MBIDs of releases. @@ -52,6 +53,7 @@ def fetch_multiple_releases(mbids, includes=None): query=query, entity_type='release', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) release_ids = [release.id for release in releases.values()] diff --git a/brainzutils/musicbrainz_db/release_group.py b/brainzutils/musicbrainz_db/release_group.py index 78acea0..f26edf0 100644 --- a/brainzutils/musicbrainz_db/release_group.py +++ b/brainzutils/musicbrainz_db/release_group.py @@ -9,7 +9,7 @@ from brainzutils.musicbrainz_db.helpers import get_relationship_info, get_tags -def get_release_group_by_id(mbid, includes=None): +def get_release_group_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get release group with the MusicBrainz ID. Args: mbid (uuid): MBID(gid) of the release group. @@ -22,10 +22,11 @@ def get_release_group_by_id(mbid, includes=None): return fetch_multiple_release_groups( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_release_groups(mbids, includes=None): +def fetch_multiple_release_groups(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple release groups using their MusicBrainz IDs. Args: mbids (list): List of MBIDs of releases groups. @@ -52,6 +53,7 @@ def fetch_multiple_release_groups(mbids, includes=None): query=query, entity_type='release_group', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) release_group_ids = [release_group.id for release_group in release_groups.values()] diff --git a/brainzutils/musicbrainz_db/serialize.py b/brainzutils/musicbrainz_db/serialize.py index 05ecedf..510be2b 100644 --- a/brainzutils/musicbrainz_db/serialize.py +++ b/brainzutils/musicbrainz_db/serialize.py @@ -1,4 +1,4 @@ -from brainzutils.musicbrainz_db.utils import ENTITY_MODELS +from brainzutils.musicbrainz_db.models import ENTITY_MODELS from mbdata.utils.models import get_link_target from sqlalchemy_dst import row2dict diff --git a/brainzutils/musicbrainz_db/tests/test_artist.py b/brainzutils/musicbrainz_db/tests/test_artist.py index 6f80bfd..ae5a5e9 100644 --- a/brainzutils/musicbrainz_db/tests/test_artist.py +++ b/brainzutils/musicbrainz_db/tests/test_artist.py @@ -1,6 +1,7 @@ from unittest import TestCase from mock import MagicMock from brainzutils.musicbrainz_db.test_data import artist_linkin_park, artist_jay_z +from brainzutils.musicbrainz_db.unknown_entities import unknown_artist from brainzutils.musicbrainz_db import artist as mb_artist @@ -39,3 +40,12 @@ def test_fetch_multiple_artists(self): "sort_name": "Linkin Park", "type": "Group", }) + + def test_fetch_multiple_artists_empty(self): + self.artist_query.return_value = [] + artists = mb_artist.fetch_multiple_artists([ + "f59c5520-5f46-4d2c-b2c4-822eabf53419", + "f82bcf78-5b69-4622-a5ef-73800768d9ac", + ], unknown_entities_for_missing=True) + self.assertEqual(artists["f82bcf78-5b69-4622-a5ef-73800768d9ac"]["name"], unknown_artist.name) + self.assertEqual(artists["f59c5520-5f46-4d2c-b2c4-822eabf53419"]["name"], unknown_artist.name) diff --git a/brainzutils/musicbrainz_db/tests/test_editor.py b/brainzutils/musicbrainz_db/tests/test_editor.py index f1ca15f..dffe732 100644 --- a/brainzutils/musicbrainz_db/tests/test_editor.py +++ b/brainzutils/musicbrainz_db/tests/test_editor.py @@ -1,6 +1,7 @@ from unittest import TestCase from mock import MagicMock from brainzutils.musicbrainz_db.test_data import editor_dt, editor_date, editor_1, editor_2 +from brainzutils.musicbrainz_db.unknown_entities import unknown_editor from brainzutils.musicbrainz_db import editor as mb_editor @@ -54,3 +55,12 @@ def test_fetch_multiple_editors(self): editors = mb_editor.fetch_multiple_editors([2323, 2324]) self.assertDictEqual(editors[2323], self.editor_1_dict) self.assertDictEqual(editors[2324], self.editor_2_dict) + + def test_fetch_multiple_editors_empty(self): + self.editor_query.return_value = [] + editors = mb_editor.fetch_multiple_editors( + [2323, 2324], + unknown_entities_for_missing=True, + ) + self.assertEqual(editors[2323]["name"], unknown_editor.name) + self.assertEqual(editors[2324]["name"], unknown_editor.name) diff --git a/brainzutils/musicbrainz_db/tests/test_event.py b/brainzutils/musicbrainz_db/tests/test_event.py index 540e354..7d131bf 100644 --- a/brainzutils/musicbrainz_db/tests/test_event.py +++ b/brainzutils/musicbrainz_db/tests/test_event.py @@ -2,6 +2,7 @@ from mock import MagicMock from brainzutils.musicbrainz_db import event as mb_event from brainzutils.musicbrainz_db.test_data import taubertal_festival_2004, event_ra_hall_uk +from brainzutils.musicbrainz_db.unknown_entities import unknown_event class EventTestCase(TestCase): @@ -28,3 +29,14 @@ def test_fetch_multiple_events(self): 'Taubertal-Festival 2004, Day 1') self.assertEqual(events['40e6153d-a042-4c95-a0a9-b0a47e3825ce']['name'], '1996-04-17: Royal Albert Hall, London, England, UK') + + def test_fetch_multiple_events_empty(self): + self.event_query.return_value = [] + events = mb_event.fetch_multiple_events([ + 'ebe6ce0f-22c0-4fe7-bfd4-7a0397c9fe94', + '40e6153d-a042-4c95-a0a9-b0a47e3825ce' + ], unknown_entities_for_missing=True) + self.assertEqual(events['ebe6ce0f-22c0-4fe7-bfd4-7a0397c9fe94']['name'], + unknown_event.name) + self.assertEqual(events['40e6153d-a042-4c95-a0a9-b0a47e3825ce']['name'], + unknown_event.name) diff --git a/brainzutils/musicbrainz_db/tests/test_label.py b/brainzutils/musicbrainz_db/tests/test_label.py index 8b6f7d1..2fc6735 100644 --- a/brainzutils/musicbrainz_db/tests/test_label.py +++ b/brainzutils/musicbrainz_db/tests/test_label.py @@ -1,6 +1,7 @@ from unittest import TestCase from mock import MagicMock from brainzutils.musicbrainz_db.test_data import label_dreamville, label_roc_a_fella +from brainzutils.musicbrainz_db.unknown_entities import unknown_label from brainzutils.musicbrainz_db import label as mb_label @@ -39,3 +40,12 @@ def test_fetch_multiple_labels(self): "type": "Original Production", "area": "United States", }) + + def test_fetch_multiple_labels_empty(self): + self.label_query.return_value = [] + labels = mb_label.fetch_multiple_labels([ + '1aed8c3b-8e1e-46f8-b558-06357ff5f298', + '4cccc72a-0bd0-433a-905e-dad87871397d' + ], unknown_entities_for_missing=True) + self.assertEqual(labels["1aed8c3b-8e1e-46f8-b558-06357ff5f298"]["name"], unknown_label.name) + self.assertEqual(labels["4cccc72a-0bd0-433a-905e-dad87871397d"]["name"], unknown_label.name) diff --git a/brainzutils/musicbrainz_db/tests/test_place.py b/brainzutils/musicbrainz_db/tests/test_place.py index 40a83f1..d90dd36 100644 --- a/brainzutils/musicbrainz_db/tests/test_place.py +++ b/brainzutils/musicbrainz_db/tests/test_place.py @@ -2,6 +2,7 @@ from unittest import TestCase from mock import MagicMock from brainzutils.musicbrainz_db import place as mb_place +from brainzutils.musicbrainz_db.unknown_entities import unknown_place from brainzutils.musicbrainz_db.test_data import place_suisto, place_verkatehdas @@ -31,3 +32,12 @@ def test_fetch_multiple_places(self): places = mb_place.fetch_multiple_places(['f9587914-8505-4bd1-833b-16a3100a4948', 'd71ffe38-5eaf-426b-9a2e-e1f21bc84609']) self.assertEqual(places['d71ffe38-5eaf-426b-9a2e-e1f21bc84609']['name'], 'Suisto') self.assertEqual(places['f9587914-8505-4bd1-833b-16a3100a4948']['name'], 'Verkatehdas') + + def test_fetch_multiple_places_empty(self): + self.place_query.return_value = [] + places = mb_place.fetch_multiple_places( + ['f9587914-8505-4bd1-833b-16a3100a4948', 'd71ffe38-5eaf-426b-9a2e-e1f21bc84609'], + unknown_entities_for_missing=True + ) + self.assertEqual(places['d71ffe38-5eaf-426b-9a2e-e1f21bc84609']['name'], unknown_place.name) + self.assertEqual(places['f9587914-8505-4bd1-833b-16a3100a4948']['name'], unknown_place.name) diff --git a/brainzutils/musicbrainz_db/tests/test_recording.py b/brainzutils/musicbrainz_db/tests/test_recording.py index 092c15d..a7df9f4 100644 --- a/brainzutils/musicbrainz_db/tests/test_recording.py +++ b/brainzutils/musicbrainz_db/tests/test_recording.py @@ -1,5 +1,6 @@ from brainzutils.musicbrainz_db import recording as mb_recording from brainzutils.musicbrainz_db.serialize import serialize_recording +from brainzutils.musicbrainz_db.unknown_entities import unknown_recording from brainzutils.musicbrainz_db.test_data import recording_numb_encore_explicit, recording_numb_encore_instrumental from unittest import TestCase from mock import MagicMock @@ -88,3 +89,14 @@ def test_fetch_multiple_recordings(self): } } ) + + def test_fetch_multiple_recordings_empty(self): + """ Tests if appropriate recordings are returned for a given list of MBIDs. """ + + self.recording_query.return_value = [] + + mbids = ['daccb724-8023-432a-854c-e0accb6c8678', '965b75df-397d-4395-aac8-de11854c4630'] + recordings = mb_recording.fetch_multiple_recordings(mbids, includes=['artists'], unknown_entities_for_missing=True) + + self.assertEqual(recordings['daccb724-8023-432a-854c-e0accb6c8678']['name'], unknown_recording.name) + self.assertEqual(recordings['965b75df-397d-4395-aac8-de11854c4630']['name'], unknown_recording.name) diff --git a/brainzutils/musicbrainz_db/tests/test_release.py b/brainzutils/musicbrainz_db/tests/test_release.py index c3df89c..f46e35c 100644 --- a/brainzutils/musicbrainz_db/tests/test_release.py +++ b/brainzutils/musicbrainz_db/tests/test_release.py @@ -1,5 +1,6 @@ from unittest import TestCase from mock import MagicMock +from brainzutils.musicbrainz_db.unknown_entities import unknown_release from brainzutils.musicbrainz_db.test_data import ( recording_numb_encore_explicit, release_numb_encore, @@ -47,6 +48,15 @@ def test_fetch_multiple_releases(self): self.assertEqual(releases['a64a0467-9d7a-4ffa-90b8-d87d9b41e311']['name'], 'Numb/Encore') self.assertEqual(releases['f51598f5-4ef9-4b8a-865d-06a077bf78cf']['name'], 'Collision Course') + def test_fetch_multiple_releases_empty(self): + self.mock_db.query.return_value.filter.return_value.all.return_value = [] + releases = mb_release.fetch_multiple_releases( + mbids=['f51598f5-4ef9-4b8a-865d-06a077bf78cf', 'a64a0467-9d7a-4ffa-90b8-d87d9b41e311'], + unknown_entities_for_missing=True + ) + self.assertEqual(releases['a64a0467-9d7a-4ffa-90b8-d87d9b41e311']['name'], unknown_release.name) + self.assertEqual(releases['f51598f5-4ef9-4b8a-865d-06a077bf78cf']['name'], unknown_release.name) + def test_get_releases_using_recording_mbid(self): """Tests if releases are fetched correctly for a given recording MBID""" diff --git a/brainzutils/musicbrainz_db/tests/test_release_group.py b/brainzutils/musicbrainz_db/tests/test_release_group.py index a7b16ec..e71e60d 100644 --- a/brainzutils/musicbrainz_db/tests/test_release_group.py +++ b/brainzutils/musicbrainz_db/tests/test_release_group.py @@ -1,6 +1,7 @@ from unittest import TestCase from mock import MagicMock from brainzutils.musicbrainz_db import release_group as mb_release_group +from brainzutils.musicbrainz_db.unknown_entities import unknown_release_group from brainzutils.musicbrainz_db.test_data import releasegroup_numb_encore, releasegroup_collision_course @@ -54,6 +55,15 @@ def test_fetch_release_groups(self): self.assertEqual(release_groups['7c1014eb-454c-3867-8854-3c95d265f8de']['title'], 'Numb/Encore') self.assertEqual(release_groups['8ef859e3-feb2-4dd1-93da-22b91280d768']['title'], 'Collision Course') + def test_fetch_release_groups_empty(self): + self.release_group_query.return_value = [] + release_groups = mb_release_group.fetch_multiple_release_groups( + mbids=['8ef859e3-feb2-4dd1-93da-22b91280d768', '7c1014eb-454c-3867-8854-3c95d265f8de'], + unknown_entities_for_missing=True + ) + self.assertEqual(release_groups['7c1014eb-454c-3867-8854-3c95d265f8de']['title'], unknown_release_group.name) + self.assertEqual(release_groups['8ef859e3-feb2-4dd1-93da-22b91280d768']['title'], unknown_release_group.name) + def test_fetch_get_release_groups_for_artist(self): mb_release_group._get_release_groups_for_artist_query = MagicMock() mock_query = mb_release_group._get_release_groups_for_artist_query.return_value diff --git a/brainzutils/musicbrainz_db/tests/test_work.py b/brainzutils/musicbrainz_db/tests/test_work.py index 5c03efb..ecfd7fb 100644 --- a/brainzutils/musicbrainz_db/tests/test_work.py +++ b/brainzutils/musicbrainz_db/tests/test_work.py @@ -1,6 +1,7 @@ from unittest import TestCase from mock import MagicMock from brainzutils.musicbrainz_db.test_data import work_a_lot, work_aquemini +from brainzutils.musicbrainz_db.unknown_entities import unknown_work from brainzutils.musicbrainz_db import work as mb_work @@ -36,3 +37,12 @@ def test_fetch_multiple_works(self): "name": "Aquemini", "type": "Song", }) + + def test_fetch_multiple_works_empty(self): + self.work_query.return_value = [] + works = mb_work.fetch_multiple_works([ + '54ce5e07-2aca-4578-83d8-5a41a7b2f434', + '757504fb-a130-4b84-9eb5-1b37164f12dd' + ], unknown_entities_for_missing=True) + self.assertEqual(works["54ce5e07-2aca-4578-83d8-5a41a7b2f434"]["name"], unknown_work.name) + self.assertEqual(works["757504fb-a130-4b84-9eb5-1b37164f12dd"]["name"], unknown_work.name) diff --git a/brainzutils/musicbrainz_db/unknown_entities.py b/brainzutils/musicbrainz_db/unknown_entities.py new file mode 100644 index 0000000..3342699 --- /dev/null +++ b/brainzutils/musicbrainz_db/unknown_entities.py @@ -0,0 +1,51 @@ +from mbdata import models + +# Unknown entities +unknown_artist = models.Artist() +unknown_artist.id = 0 +unknown_artist.name = '[Unknown Artist]' +unknown_artist.sort_name = '[Unknown Artist]' + +unknown_artist_credit = models.ArtistCredit() +unknown_artist_credit.id = 0 +unknown_artist_credit.name = '[Unknown Artist]' + +unknown_release_group = models.ReleaseGroup() +unknown_release_group.id = 0 +unknown_release_group.name = '[Unknown Release Group]' + +unknown_release = models.Release() +unknown_release.id = 0 +unknown_release.name = '[Unknown Release]' +unknown_release.artist_credit = unknown_artist_credit + +unknown_recording = models.Recording() +unknown_recording.id = 0 +unknown_recording.name = '[Unknown Recording]' +unknown_recording.artist_credit = unknown_artist_credit + +unknown_area = models.Area() +unknown_area.id = 0 +unknown_area.name = '[Unknown Area]' + +unknown_place = models.Place() +unknown_place.id = 0 +unknown_place.name = '[Unknown Place]' +unknown_place.address = '[Unknown Address]' +unknown_place.area = unknown_area + +unknown_event = models.Event() +unknown_event.id = 0 +unknown_event.name = '[Unknown Event]' + +unknown_label = models.Label() +unknown_label.id = 0 +unknown_label.name = '[Unknown Label]' +unknown_label.area = unknown_area + +unknown_work = models.Work() +unknown_work.id = 0 +unknown_work.name = '[Unknown Work]' + +unknown_editor = models.Editor +unknown_editor.name = '[Unknown Editor]' diff --git a/brainzutils/musicbrainz_db/utils.py b/brainzutils/musicbrainz_db/utils.py index 54ff5eb..48689ab 100644 --- a/brainzutils/musicbrainz_db/utils.py +++ b/brainzutils/musicbrainz_db/utils.py @@ -1,37 +1,61 @@ +from brainzutils.musicbrainz_db.models import ENTITY_MODELS, REDIRECT_MODELS import brainzutils.musicbrainz_db.exceptions as mb_exceptions -from mbdata import models - - -# Entity models -ENTITY_MODELS = { - 'artist': models.Artist, - 'place': models.Place, - 'release_group': models.ReleaseGroup, - 'release': models.Release, - 'event': models.Event, - 'label': models.Label, - 'series': models.Series, - 'url': models.URL, - 'recording': models.Recording, - 'work': models.Work, - 'editor': models.Editor, -} - - -# Redirect models -REDIRECT_MODELS = { - 'place': models.PlaceGIDRedirect, - 'artist': models.ArtistGIDRedirect, - 'release': models.ReleaseGIDRedirect, - 'release_group': models.ReleaseGroupGIDRedirect, - 'event': models.EventGIDRedirect, - 'label': models.LabelGIDRedirect, - 'recording': models.RecordingGIDRedirect, - 'work': models.WorkGIDRedirect, -} - - -def get_entities_by_gids(query, entity_type, mbids): +from brainzutils.musicbrainz_db import unknown_entities + + +def unknown_entity_by_gid(entity_gid, entity_type): + """Returns special unknown entities (in case, they are not present in MB). + + Args: + entity_gid: MBID of the unknown entity. + entity_type: Type of the unknown entity. + Returns: + Special entity object (unknown) of specified entity_type. + """ + if entity_type == 'artist': + entity = unknown_entities.unknown_artist + if entity_type == 'release_group': + entity = unknown_entities.unknown_release_group + elif entity_type == 'recording': + entity = unknown_entities.unknown_recording + elif entity_type == 'release': + entity = unknown_entities.unknown_release + elif entity_type == 'artist': + entity = unknown_entities.unknown_artist + elif entity_type == 'place': + entity = unknown_entities.unknown_place + elif entity_type == 'event': + entity = unknown_entities.unknown_event + elif entity_type == 'label': + entity = unknown_entities.unknown_label + elif entity_type == 'work': + entity = unknown_entities.unknown_work + else: + raise mb_exceptions.InvalidTypeError("Couldn't create unknown for invalid type: {type}".format(type=entity_type)) + + entity.gid = entity_gid + return entity + + +def unknown_entity_by_id(entity_id, entity_type): + """Returns special unknown entities (in case, they are not present in MB). + + Args: + entity_id: ID of the unknown entity. + entity_type: Type of the unknown entity. + Returns: + Special entity object (unknown) of specified entity_type. + """ + if entity_type == 'editor': + entity = unknown_entities.unknown_editor(id=0) + else: + raise mb_exceptions.InvalidTypeError("Couldn't create unknown for invalid type: {type}".format(type=entity_type)) + + entity.id = entity_id + return entity + + +def get_entities_by_gids(query, entity_type, mbids, unknown_entities_for_missing=False): """Get entities using their MBIDs. An entity can have multiple MBIDs. This function may be passed another @@ -44,6 +68,7 @@ def get_entities_by_gids(query, entity_type, mbids): query (Query): SQLAlchemy Query object. entity_type (str): Type of entity being queried. mbids (list): IDs of the target entities. + unknown_entities_for_missing (bool): If set, NoDataFoundException is suppressed and unknown entities are returned instead. Returns: Dictionary of objects of target entities keyed by their MBID. @@ -61,12 +86,16 @@ def get_entities_by_gids(query, entity_type, mbids): remaining_gids = list(set(remaining_gids) - {redirect_obj.gid for entity, redirect_obj in results}) if remaining_gids: - raise mb_exceptions.NoDataFoundException("Couldn't find entities with IDs: {mbids}".format(mbids=remaining_gids)) - + if unknown_entities_for_missing: + for gid in remaining_gids: + entities[gid] = unknown_entity_by_gid(gid, entity_type) + else: + raise mb_exceptions.NoDataFoundException("Couldn't find entities with IDs: {mbids}".format(mbids=remaining_gids)) + return entities -def get_entities_by_ids(query, entity_type, ids): +def get_entities_by_ids(query, entity_type, ids, unknown_entities_for_missing=False): """Get entities using their IDs. Note that the query may be modified before passing it to this @@ -76,6 +105,7 @@ def get_entities_by_ids(query, entity_type, ids): query (Query): SQLAlchemy Query object. entity_type (str): Type of entity being queried. ids (list): IDs of the target entities. + unknown_entities_for_missing (bool): If set, NoDataFoundException is suppressed and unknown entities are returned instead. Returns: Dictionary of objects of target entities keyed by their ID. @@ -86,7 +116,11 @@ def get_entities_by_ids(query, entity_type, ids): entities = {entity.id: entity for entity in results} if remaining_ids: - raise mb_exceptions.NoDataFoundException( - "Couldn't find entities with IDs: {ids}".format(ids=remaining_ids)) + if unknown_entities_for_missing: + for entity_id in remaining_ids: + entities[entity_id] = unknown_entity_by_id(entity_id, entity_type) + else: + raise mb_exceptions.NoDataFoundException( + "Couldn't find entities with IDs: {ids}".format(ids=remaining_ids)) return entities diff --git a/brainzutils/musicbrainz_db/work.py b/brainzutils/musicbrainz_db/work.py index a347a54..296fcc3 100644 --- a/brainzutils/musicbrainz_db/work.py +++ b/brainzutils/musicbrainz_db/work.py @@ -8,7 +8,7 @@ from brainzutils.musicbrainz_db.helpers import get_relationship_info -def get_work_by_id(mbid, includes=None): +def get_work_by_id(mbid, includes=None, unknown_entities_for_missing=False): """Get work with the MusicBrainz ID. Args: @@ -22,10 +22,11 @@ def get_work_by_id(mbid, includes=None): return fetch_multiple_works( [mbid], includes=includes, + unknown_entities_for_missing=unknown_entities_for_missing, ).get(mbid) -def fetch_multiple_works(mbids, includes=None): +def fetch_multiple_works(mbids, includes=None, unknown_entities_for_missing=False): """Get info related to multiple works using their MusicBrainz IDs. Args: @@ -46,6 +47,7 @@ def fetch_multiple_works(mbids, includes=None): query=query, entity_type='work', mbids=mbids, + unknown_entities_for_missing=unknown_entities_for_missing, ) work_ids = [work.id for work in works.values()]