Skip to content

Commit

Permalink
CB-231: Use direct database access for the Release Group entity
Browse files Browse the repository at this point in the history
  • Loading branch information
ferbncode authored and gentlecat committed Aug 7, 2017
1 parent 42d7610 commit 154782d
Show file tree
Hide file tree
Showing 13 changed files with 476 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

engine = None
Session: Optional[Session] = None
DEFAULT_CACHE_EXPIRATION = 12 * 60 * 60 # seconds (12 hours)

def init_db_engine(connect_str):
global engine, Session
Expand Down
21 changes: 21 additions & 0 deletions critiquebrainz/frontend/external/musicbrainz_db/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from mbdata.utils.models import get_entity_type_model, get_link_model
from mbdata.models import Tag
from sqlalchemy.orm import joinedload
from sqlalchemy import func


def get_relationship_info(*, db, target_type, source_type, source_entity_ids, includes_data):
Expand Down Expand Up @@ -54,3 +56,22 @@ def _relationship_link_helper(relation, query, source_attr, target_attr, target_
for link in query:
includes_data[getattr(link, source_id_attr)].setdefault('relationship_objs', {}).\
setdefault(relation_type, []).append(link)


def get_tags(*, db, entity_model, tag_model, entity_ids):
"""Get tags associated with entities.
Args:
db (Session object): Session object.
entity_model (mbdata.models): Model of the entity.
tag_model (mbdata.models): Tag of the model.
entity_ids (list): IDs of the entity whose tags are to be fetched
Returns:
List of tuples containing the entity_ids and the list of associated tags.
"""
tags = db.query(entity_model.id, func.array_agg(Tag.name)).\
join(tag_model).\
join(Tag).filter(entity_model.id.in_(entity_ids)).\
group_by(entity_model.id).all()
return tags
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
RATING_INCLUDES = ["ratings", "user-ratings"]
VALID_INCLUDES = {
'place': ["aliases", "annotation"] + RELATION_INCLUDES + TAG_INCLUDES,
'release_group': ["artists", "media", "releases"] + TAG_INCLUDES + RELATION_INCLUDES,
}


Expand Down
10 changes: 3 additions & 7 deletions critiquebrainz/frontend/external/musicbrainz_db/place.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@
from mbdata import models
from sqlalchemy.orm import joinedload
from brainzutils import cache
from critiquebrainz.frontend.external.musicbrainz_db import mb_session
from critiquebrainz.frontend.external.musicbrainz_db import mb_session, DEFAULT_CACHE_EXPIRATION
from critiquebrainz.frontend.external.musicbrainz_db.includes import check_includes
from critiquebrainz.frontend.external.musicbrainz_db.serialize import to_dict_places
from critiquebrainz.frontend.external.musicbrainz_db.helpers import get_relationship_info
from critiquebrainz.frontend.external.relationships import place as place_rel
from critiquebrainz.frontend.external.musicbrainz_db.utils import get_entities_by_gids


DEFAULT_CACHE_EXPIRATION = 12 * 60 * 60 # seconds (12 hours)
THREAD_POOL_PROCESSES = 10


def get_place_by_id(mbid):
"""Get place with the MusicBrainz ID.
Expand All @@ -28,8 +24,8 @@ def get_place_by_id(mbid):
place = fetch_multiple_places(
[mbid],
includes=['artist-rels', 'place-rels', 'release-group-rels', 'url-rels'],
).get(mbid)
cache.set(key=key, val=place, time=DEFAULT_CACHE_EXPIRATION)
)[mbid]
cache.set(key=key, val=place, time=DEFAULT_CACHE_EXPIRATION)
return place_rel.process(place)


Expand Down
102 changes: 102 additions & 0 deletions critiquebrainz/frontend/external/musicbrainz_db/release_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from collections import defaultdict
from brainzutils import cache
from sqlalchemy.orm import joinedload
from mbdata import models
from critiquebrainz.frontend.external.musicbrainz_db import mb_session, DEFAULT_CACHE_EXPIRATION
from critiquebrainz.frontend.external.musicbrainz_db.helpers import get_relationship_info, get_tags
from critiquebrainz.frontend.external.musicbrainz_db.serialize import to_dict_release_groups
from critiquebrainz.frontend.external.musicbrainz_db.includes import check_includes
from critiquebrainz.frontend.external.musicbrainz_db.utils import get_entities_by_gids
import critiquebrainz.frontend.external.relationships.release_group as release_group_rel


def get_release_group_by_id(mbid):
"""Get release group using the MusicBrainz ID."""
key = cache.gen_key(mbid)
release_group = cache.get(key)
if not release_group:
release_group = _get_release_group_by_id(mbid)
cache.set(key=key, val=release_group, time=DEFAULT_CACHE_EXPIRATION)
return release_group_rel.process(release_group)


def _get_release_group_by_id(mbid):
return fetch_multiple_release_groups(
[mbid],
includes=['artists', 'releases', 'release-group-rels', 'url-rels', 'tags'],
)[mbid]


def fetch_multiple_release_groups(mbids, *, includes=None):
includes = [] if includes is None else includes
includes_data = defaultdict(dict)
check_includes('release_group', includes)
with mb_session() as db:
# Join table meta which contains release date for a release group
query = db.query(models.ReleaseGroup).options(joinedload("meta"))

if 'artists' in includes:
query = query.options(joinedload("artist_credit")).\
options(joinedload("artist_credit.artists")).\
options(joinedload("artist_credit.artists.artist"))

release_groups = get_entities_by_gids(
query=query,
entity_type='release_group',
mbids=mbids,
)
release_group_ids = [release_group.id for release_group in release_groups]

if 'artists' in includes:
for release_group in release_groups:
artist_credit_names = release_group.artist_credit.artists
includes_data[release_group.id]['artist-credit-names'] = artist_credit_names
includes_data[release_group.id]['artist-credit-phrase'] = release_group.artist_credit.name

if 'releases' in includes:
query = db.query(models.Release).filter(getattr(models.Release, "release_group_id").in_(release_group_ids))
for release in query:
includes_data[release.release_group_id].setdefault('releases', []).append(release)

if 'release-group-rels' in includes:
get_relationship_info(
db=db,
target_type='release_group',
source_type='release_group',
source_entity_ids=release_group_ids,
includes_data=includes_data,
)

if 'url-rels' in includes:
get_relationship_info(
db=db,
target_type='url',
source_type='release_group',
source_entity_ids=release_group_ids,
includes_data=includes_data,
)

if 'work-rels' in includes:
get_relationship_info(
db=db,
target_type='work',
source_type='release_group',
source_entity_ids=release_group_ids,
includes_data=includes_data,
)

if 'tags' in includes:
release_group_tags = get_tags(
db=db,
entity_model=models.ReleaseGroup,
tag_model=models.ReleaseGroupTag,
entity_ids=release_group_ids,
)
for release_group_id, tags in release_group_tags:
includes_data[release_group_id]['tags'] = tags

for release_group in release_groups:
includes_data[release_group.id]['meta'] = release_group.meta
release_groups = {str(release_group.gid): to_dict_release_groups(release_group, includes_data[release_group.id])
for release_group in release_groups}
return release_groups
47 changes: 45 additions & 2 deletions critiquebrainz/frontend/external/musicbrainz_db/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,64 @@ def to_dict_places(place, includes=None):
return data


def to_dict_artist_credit_names(artist_credit_name):
data = {
'name': artist_credit_name.name,
'artist': to_dict_artists(artist_credit_name.artist),
}
if artist_credit_name.join_phrase:
data['join_phrase'] = artist_credit_name.join_phrase
return data


def to_dict_release_groups(release_group, includes=None):
if includes is None:
includes = {}

data = {
'id': release_group.id,
'name': release_group.name,
'id': release_group.gid,
'title': release_group.name,
}

if 'artist-credit-phrase' in includes:
data['artist-credit-phrase'] = includes['artist-credit-phrase']

if 'meta' in includes and includes['meta'].first_release_date_year:
data['first-release-year'] = includes['meta'].first_release_date_year

if 'artist-credit-names' in includes:
data['artist-credit'] = [to_dict_artist_credit_names(artist_credit_name)
for artist_credit_name in includes['artist-credit-names']]

if 'releases' in includes:
data['release-list'] = [to_dict_releases(release) for release in includes['releases']]

if 'relationship_objs' in includes:
to_dict_relationships(data, release_group, includes['relationship_objs'])

if 'tags' in includes:
data['tag-list'] = includes['tags']

return data


def to_dict_releases(release, includes=None):
if includes is None:
includes = {}
data = {
'id': release.gid,
'name': release.name,
}
if 'relationship_objs' in includes:
to_dict_relationships(data, release, includes['relationship_objs'])
return data


TO_DICT_ENTITIES = {
'artist': to_dict_artists,
'url': to_dict_urls,
'place': to_dict_places,
'release_group': to_dict_release_groups,
'area': to_dict_areas,
'release': to_dict_releases,
}

0 comments on commit 154782d

Please sign in to comment.