Skip to content
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

Display Relationships on Entity Pages #459

Merged
merged 8 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'])
alastair marked this conversation as resolved.
Show resolved Hide resolved
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 %}
anshg1214 marked this conversation as resolved.
Show resolved Hide resolved
<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