Skip to content

Commit

Permalink
Merge pull request #459 from anshg1214/add_work_info_for_eg
Browse files Browse the repository at this point in the history
Display Relationships on Entity Pages
  • Loading branch information
alastair authored Sep 8, 2022
2 parents cba7523 + 0dd302e commit f56eb42
Show file tree
Hide file tree
Showing 15 changed files with 354 additions and 65 deletions.
4 changes: 2 additions & 2 deletions critiquebrainz/frontend/external/bookbrainz_db/author.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import critiquebrainz.frontend.external.bookbrainz_db as db
from critiquebrainz.frontend.external.bookbrainz_db import DEFAULT_CACHE_EXPIRATION
from critiquebrainz.frontend.external.bookbrainz_db.identifiers import fetch_bb_external_identifiers
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships, AUTHOR_WORK_AUTHOR_REL_ID

def get_author_by_bbid(bbid: uuid.UUID) -> dict:
"""
Expand Down Expand Up @@ -100,7 +100,7 @@ def fetch_multiple_authors(bbids: List[uuid.UUID]) -> dict:
author = dict(author)
author['bbid'] = str(author['bbid'])
author['identifiers'] = fetch_bb_external_identifiers(author['identifier_set_id'])
author['rels'] = fetch_relationships( author['relationship_set_id'], ['Author'])
author['rels'] = fetch_relationships( author['relationship_set_id'], [AUTHOR_WORK_AUTHOR_REL_ID])
results[author['bbid']] = author

cache.set(bb_author_key, results, DEFAULT_CACHE_EXPIRATION)
Expand Down
48 changes: 46 additions & 2 deletions critiquebrainz/frontend/external/bookbrainz_db/edition_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import critiquebrainz.frontend.external.bookbrainz_db as db
from critiquebrainz.frontend.external.bookbrainz_db import DEFAULT_CACHE_EXPIRATION
from critiquebrainz.frontend.external.bookbrainz_db.identifiers import fetch_bb_external_identifiers
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships, EDITION_EDITION_GROUP_EDITION_REL_ID, EDITION_WORK_CONTAINS_REL_ID


def get_edition_group_by_bbid(bbid: uuid.UUID) -> dict:
Expand Down Expand Up @@ -83,7 +83,7 @@ def fetch_multiple_edition_groups(bbids: List[uuid.UUID]) -> dict:
for edition_group in edition_groups:
edition_group = dict(edition_group)
edition_group['identifiers'] = fetch_bb_external_identifiers(edition_group['identifier_set_id'])
edition_group['rels'] = fetch_relationships( edition_group['relationship_set_id'], ['Edition'])
edition_group['rels'] = fetch_relationships( edition_group['relationship_set_id'], [EDITION_EDITION_GROUP_EDITION_REL_ID])
results[edition_group['bbid']] = edition_group

edition_groups = results
Expand All @@ -92,3 +92,47 @@ def fetch_multiple_edition_groups(bbids: List[uuid.UUID]) -> dict:
if not results:
return {}
return results


def fetch_works_for_edition_group(bbid: uuid.UUID):
"""
Get works linked to an edition group using its BookBrainz ID.
Args:
bbid : BBID of the edition group.
Returns:
A tuple containing the list of work bbids linked to edition group.
"""

bb_edition_group_work = cache.gen_key('bb-edition-groups-works', bbid)
results = cache.get(bb_edition_group_work)
if not results:
with db.bb_engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
SELECT DISTINCT(rel.target_bbid::text)
FROM edition_group
INNER JOIN edition ON edition.edition_group_bbid = edition_group.bbid
INNER JOIN relationship_set__relationship rels on rels.set_id = edition.relationship_set_id
LEFT JOIN relationship rel on rels.relationship_id = rel.id
WHERE edition_group.bbid = :bbid
AND edition_group.master = 't'
AND edition_group.data_id IS NOT NULL
AND edition.master = 't'
AND edition.data_id IS NOT NULL
AND rel.type_id = :relationship_type_id
"""), {'bbid': str(bbid), 'relationship_type_id': EDITION_WORK_CONTAINS_REL_ID})

works = result.fetchall()
work_bbids = []

for work in works:
work = dict(work)
work_bbids.append(work['target_bbid'])

results = work_bbids
cache.set(bb_edition_group_work, results, DEFAULT_CACHE_EXPIRATION)

if not results:
return []
return results
46 changes: 44 additions & 2 deletions critiquebrainz/frontend/external/bookbrainz_db/literary_work.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import critiquebrainz.frontend.external.bookbrainz_db as db
from critiquebrainz.frontend.external.bookbrainz_db import DEFAULT_CACHE_EXPIRATION
from critiquebrainz.frontend.external.bookbrainz_db.identifiers import fetch_bb_external_identifiers
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships, WORK_WORK_TRANSLATION_REL_ID, EDITION_WORK_CONTAINS_REL_ID

WORK_TYPE_FILTER_OPTIONS = ('Novel', 'Short Story', 'Poem')


def get_literary_work_by_bbid(bbid: uuid.UUID) -> dict:
"""
Get info related to a literary work using its BookBrainz ID.
Expand Down Expand Up @@ -107,11 +108,52 @@ def fetch_multiple_literary_works(bbids: List[uuid.UUID], work_type=None, limit=
for literary_work in literary_works:
literary_work = dict(literary_work)
literary_work['identifiers'] = fetch_bb_external_identifiers(literary_work['identifier_set_id'])
literary_work['rels'] = fetch_relationships(literary_work['relationship_set_id'], ['Edition'])
literary_work['rels'] = fetch_relationships(literary_work['relationship_set_id'], [WORK_WORK_TRANSLATION_REL_ID])
results[literary_work['bbid']] = literary_work

cache.set(bb_literary_work_key, results, DEFAULT_CACHE_EXPIRATION)

if not results:
return {}
return results


def fetch_edition_groups_for_works(bbid: uuid.UUID):
"""
Get edition groups for a literary work.
Args:
bbid : BBID of the literary work.
Returns:
A tuple containing the list of edition groups bbids linked to literary work.
"""
bb_work_edition_groups_key = cache.gen_key('bb_work_edition_groups', bbid)
results = cache.get(bb_work_edition_groups_key)
if not results:
with db.bb_engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
SELECT DISTINCT(edition.edition_group_bbid::text)
FROM work
INNER JOIN relationship_set__relationship rels on rels.set_id = work.relationship_set_id
LEFT JOIN relationship rel on rels.relationship_id = rel.id
INNER JOIN edition ON rel.source_bbid = edition.bbid
WHERE work.bbid = :bbid
AND work.master = 't'
AND work.data_id IS NOT NULL
AND edition.master = 't'
AND edition.data_id IS NOT NULL
AND rel.type_id = :relationship_type_id
"""), {'bbid': str(bbid), 'relationship_type_id': EDITION_WORK_CONTAINS_REL_ID})

edition_groups = result.fetchall()
edition_group_bbids = []

for edition_group in edition_groups:
edition_group = dict(edition_group)
edition_group_bbids.append(edition_group['edition_group_bbid'])

results = edition_group_bbids
cache.set(bb_work_edition_groups_key, results, DEFAULT_CACHE_EXPIRATION)

if not results:
return []
return results
52 changes: 18 additions & 34 deletions critiquebrainz/frontend/external/bookbrainz_db/relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,33 @@
import critiquebrainz.frontend.external.bookbrainz_db.exceptions as bb_exceptions
from critiquebrainz.frontend.external.bookbrainz_db import DEFAULT_CACHE_EXPIRATION


def get_mapped_relationships(relation_types):
"""Get relation types mapped to their case sensitive name in bookbrainz.
relationship_type table.
Args:
relation_types (list): List of relation types.
Returns:
List of mapped relation types.
"""
mapped_relation_types = []
relation_types = [relation_type.lower() for relation_type in relation_types]
with db.bb_engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
SELECT label
FROM relationship_type
"""))
relationships = [relationship[0] for relationship in result.fetchall()]
relationships_mapping = {relationship.lower(): relationship for relationship in relationships}

for relation_type in relation_types:
if relation_type not in relationships_mapping:
raise bb_exceptions.InvalidTypeError("Bad relation: {rtype} is not supported".format(rtype=relation_type))
else:
mapped_relation_types.append(relationships_mapping[relation_type])

return mapped_relation_types


def fetch_relationships(relationship_set_id: int, relation_types: List) -> List:
AUTHOR_WORK_AUTHOR_REL_ID = 8
EDITION_EDITION_GROUP_EDITION_REL_ID = 3
EDITION_WORK_CONTAINS_REL_ID = 10
WORK_WORK_TRANSLATION_REL_ID = 56

SERIES_REL_MAP = {
'Author': 70,
'Edition': 72,
'EditionGroup': 73,
'Publisher': 74,
'Work': 71,
}

def fetch_relationships(relationship_set_id: int, relation_types_id: List) -> List:
"""
Fetch relationships from the database.
"""
if not relationship_set_id:
return None

relation_types = get_mapped_relationships(relation_types)
key = cache.gen_key('bb_relationship', relationship_set_id, relation_types)
key = cache.gen_key('bb_relationship', relationship_set_id, relation_types_id)
relationships = cache.get(key)
if not relationships:
with db.bb_engine.connect() as connection:
result = connection.execute(sqlalchemy.text("""
SELECT rel.id as id,
reltype.id as relation_type_id,
reltype.label as label,
rel.source_bbid::text as source_bbid,
rel.target_bbid::text as target_bbid,
Expand All @@ -58,8 +42,8 @@ def fetch_relationships(relationship_set_id: int, relation_types: List) -> List:
LEFT JOIN relationship rel on rels.relationship_id = rel.id
LEFT JOIN relationship_type reltype on rel.type_id = reltype.id
WHERE rels.set_id = :relationship_set_id
AND reltype.label in :relation_types
"""), {'relationship_set_id': relationship_set_id, 'relation_types': tuple(relation_types)})
AND reltype.id in :relation_types_id
"""), {'relationship_set_id': relationship_set_id, 'relation_types_id': tuple(relation_types_id)})
relationships = result.fetchall()
relationships = [dict(relationship) for relationship in relationships]
cache.set(key, relationships, DEFAULT_CACHE_EXPIRATION)
Expand Down
9 changes: 1 addition & 8 deletions critiquebrainz/frontend/external/bookbrainz_db/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,13 @@
import critiquebrainz.frontend.external.bookbrainz_db as db
from critiquebrainz.frontend.external.bookbrainz_db import DEFAULT_CACHE_EXPIRATION
from critiquebrainz.frontend.external.bookbrainz_db.identifiers import fetch_bb_external_identifiers
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships
from critiquebrainz.frontend.external.bookbrainz_db.relationships import fetch_relationships, SERIES_REL_MAP
from critiquebrainz.frontend.external.bookbrainz_db.author import fetch_multiple_authors
from critiquebrainz.frontend.external.bookbrainz_db.edition import fetch_multiple_editions
from critiquebrainz.frontend.external.bookbrainz_db.edition_group import fetch_multiple_edition_groups
from critiquebrainz.frontend.external.bookbrainz_db.literary_work import fetch_multiple_literary_works
from critiquebrainz.frontend.external.bookbrainz_db.publisher import fetch_multiple_publishers

SERIES_REL_MAP = {
'Author': 'Author Series',
'Edition': 'Edition Series',
'EditionGroup': 'Edition Group Series',
'Publisher': 'Publisher Series',
'Work': 'Work Series',
}


def get_series_by_bbid(bbid: uuid.UUID) -> dict:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ def setUP(self):
super(BBRelationshipTestCase, self).setUp()

def test_bb_relationship(self):
relationship = relationships.fetch_relationships(99999999, ["Author"])
relationship = relationships.fetch_relationships(99999999, [relationships.AUTHOR_WORK_AUTHOR_REL_ID])
self.assertEqual(relationship[0]["label"], "Author")
self.assertEqual(relationship[0]["source_bbid"], "e5c4e68b-bfce-4c77-9ca2-0f0a2d4d09f0")
self.assertEqual(relationship[0]["target_bbid"], "9f49df73-8ee5-4c5f-8803-427c9b216d8f")

relationship = relationships.fetch_relationships(99999999, ["Edition"])
relationship = relationships.fetch_relationships(99999999, [relationships.EDITION_EDITION_GROUP_EDITION_REL_ID])
self.assertEqual(relationship, None)
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ def test_author_credits_for_edition_groups(self):
self.assertEqual(len(edition_groups[self.bbid3]["author_credits"]), 1)
self.assertEqual(edition_groups[self.bbid3]["author_credits"][0]["name"], "Test Author")
self.assertEqual(edition_groups[self.bbid3]["author_credits"][0]["author_bbid"], "e5c4e68b-bfce-4c77-9ca2-0f0a2d4d09f0")

def test_fetch_works_for_edition_group(self):
works_bbids = edition_group.fetch_works_for_edition_group(self.bbid2)
self.assertEqual(len(works_bbids), 5)
self.assertEqual(works_bbids[0], "5dd33da7-740b-451b-98fc-2556783de658")

works_bbids_2 = edition_group.fetch_works_for_edition_group(self.bbid1)
self.assertEqual(len(works_bbids_2), 0)
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

class LiteraryWorkTestCase(DataTestCase):
def setUp(self):

super(LiteraryWorkTestCase, self).setUp()
self.bbid1 = "56efa555-abd5-4ccb-89a6-ff9d9021971f"
self.bbid2 = "65e71f2e-7245-42df-b93e-89463a28f75c"
self.bbid3 = "0e03bc2a-2867-4687-afee-e211ece30772"
self.bbid3 = "0e03bc2a-2867-4687-afee-e211ece30772"

def test_get_literary_work_by_bbid(self):
literary_work_info = literary_work.get_literary_work_by_bbid(self.bbid1)
self.assertEqual(literary_work_info["bbid"], self.bbid1)
self.assertEqual(literary_work_info["name"], "Assassin's Creed: Brotherhood")
self.assertEqual(literary_work_info["sort_name"], "Brotherhood, Assassin's Creed:")
self.assertEqual(literary_work_info["work_type"], "Novel")

def test_fetch_multiple_literary_works(self):
literary_works = literary_work.fetch_multiple_literary_works([self.bbid2, self.bbid3])
self.assertEqual(len(literary_works), 2)
Expand All @@ -25,4 +25,12 @@ def test_fetch_multiple_literary_works(self):
self.assertEqual(literary_works[self.bbid2]["work_type"], "Novel")
self.assertEqual(literary_works[self.bbid3]["bbid"], self.bbid3)
self.assertEqual(literary_works[self.bbid3]["name"], "Oliver Twist")
self.assertEqual(literary_works[self.bbid3]["work_type"], "Novel")
self.assertEqual(literary_works[self.bbid3]["work_type"], "Novel")

def test_fetch_edition_groups_for_works(self):
edition_group_bbids_1 = literary_work.fetch_edition_groups_for_works(self.bbid2)
self.assertEqual(len(edition_group_bbids_1), 1)
self.assertEqual(edition_group_bbids_1[0], "02ae4cfc-6412-4693-93b1-e24dce5e31f9")

edition_group_bbids_2 = literary_work.fetch_edition_groups_for_works(self.bbid3)
self.assertEqual(len(edition_group_bbids_2), 0)
53 changes: 51 additions & 2 deletions critiquebrainz/frontend/templates/bb_edition_group/entity.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends 'base.html' %}
{% from 'macros.html' import show_avg_rating, entity_rate_form, show_review_buttons with context %}
{% from 'macros.html' import show_avg_rating, entity_rate_form, show_review_buttons, display_pagination with context %}
{% from 'common.html' import rating_script with context %}

{% block title %}{{ edition_group.name }} - CritiqueBrainz{% endblock %}
Expand Down Expand Up @@ -60,6 +60,51 @@ <h4 style="margin-bottom:0;">{{ _('Reviews') }}</h4>
{% endif %}
</ul>
{% endif %}

{% if works %}
<h4>{{ _('Works for this Edition Group') }}</h4>
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>{{ _('Name') }}</th>
<th>{{ _('Languages') }}</th>
<th>{{ _('Type') }}</th>
</tr>
</thead>
<tbody>
{% for work in works %}
<tr>
<td>
<a href="{{ url_for('bb_literary_work.entity', id=work.bbid) }}">
{{ work.name }}
</a>
</td>
<td>
{% if work['languages'] is defined and work['languages'] %}
{% if work["languages"]|length > 1 %}
{{ work['languages'][0] }} + {{ work["languages"]|length - 1 }} more
{% else %}
{{ work['languages'][0] }}
{% endif %}
{% else %}
-
{% endif %}
</td>
<td>
{% if work['work_type'] is defined and work['work_type'] %}
{{ work.work_type }}
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>

{{ display_pagination(page, works_count, works_limit, 'bb_edition_group.entity', {'id': id}) }}

{% endif %}
</div>

<div class="col-md-3">
Expand All @@ -77,7 +122,11 @@ <h4>{{ _('Edition Group information') }}</h4>
<b>{{ _('Author') }}</b>
<ul class="list-unstyled">
{% for author in edition_group.author_credits %}
<li>{{ author.name }}</li>
<li>
<a href="{{ url_for('bb_author.entity', id=author.author_bbid) }}">
{{ author.name }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
Expand Down
Loading

0 comments on commit f56eb42

Please sign in to comment.