mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-29 21:11:16 +00:00
Isfdb ID for books and authors (#2482)
* New ID: Audible ASIN Audible belongs to Amazon BUT they do not share the same IDs. The Audible ASIN of an audiobook is never the same as the Amazon ASIN. Yeah, I know, Amazon is great. The fact that the ASIN is a good distinction for different works and editions bothers me more than I will ever be willing to admint. * New ID "ISFDB" Internet Speculative Ficiton Database ID for books and authors. Links to the entry if set. * Added aasin to test Added aasin to test * the answer expects more emptxy fields...
This commit is contained in:
parent
0e2b88ad0c
commit
ac8b060d58
15 changed files with 150 additions and 4 deletions
|
@ -19,6 +19,8 @@ class BookData(ActivityObject):
|
||||||
viaf: str = None
|
viaf: str = None
|
||||||
wikidata: str = None
|
wikidata: str = None
|
||||||
asin: str = None
|
asin: str = None
|
||||||
|
aasin: str = None
|
||||||
|
isfdb: str = None
|
||||||
lastEditedBy: str = None
|
lastEditedBy: str = None
|
||||||
links: List[str] = field(default_factory=lambda: [])
|
links: List[str] = field(default_factory=lambda: [])
|
||||||
fileLinks: List[str] = field(default_factory=lambda: [])
|
fileLinks: List[str] = field(default_factory=lambda: [])
|
||||||
|
|
|
@ -21,6 +21,7 @@ class AuthorForm(CustomForm):
|
||||||
"inventaire_id",
|
"inventaire_id",
|
||||||
"librarything_key",
|
"librarything_key",
|
||||||
"goodreads_key",
|
"goodreads_key",
|
||||||
|
"isfdb",
|
||||||
"isni",
|
"isni",
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
|
|
|
@ -89,6 +89,8 @@ class EditionForm(CustomForm):
|
||||||
attrs={"aria-describedby": "desc_oclc_number"}
|
attrs={"aria-describedby": "desc_oclc_number"}
|
||||||
),
|
),
|
||||||
"ASIN": forms.TextInput(attrs={"aria-describedby": "desc_ASIN"}),
|
"ASIN": forms.TextInput(attrs={"aria-describedby": "desc_ASIN"}),
|
||||||
|
"AASIN": forms.TextInput(attrs={"aria-describedby": "desc_AASIN"}),
|
||||||
|
"isfdb": forms.TextInput(attrs={"aria-describedby": "desc_isfdb"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
28
bookwyrm/migrations/0168_auto_20221205_1701.py
Normal file
28
bookwyrm/migrations/0168_auto_20221205_1701.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 3.2.16 on 2022-12-05 17:01
|
||||||
|
|
||||||
|
import bookwyrm.models.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0167_auto_20221125_1900"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="author",
|
||||||
|
name="aasin",
|
||||||
|
field=bookwyrm.models.fields.CharField(
|
||||||
|
blank=True, max_length=255, null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="book",
|
||||||
|
name="aasin",
|
||||||
|
field=bookwyrm.models.fields.CharField(
|
||||||
|
blank=True, max_length=255, null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
28
bookwyrm/migrations/0169_auto_20221206_0902.py
Normal file
28
bookwyrm/migrations/0169_auto_20221206_0902.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 3.2.16 on 2022-12-06 09:02
|
||||||
|
|
||||||
|
import bookwyrm.models.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0168_auto_20221205_1701"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="author",
|
||||||
|
name="isfdb",
|
||||||
|
field=bookwyrm.models.fields.CharField(
|
||||||
|
blank=True, max_length=255, null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="book",
|
||||||
|
name="isfdb",
|
||||||
|
field=bookwyrm.models.fields.CharField(
|
||||||
|
blank=True, max_length=255, null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -24,6 +24,9 @@ class Author(BookDataModel):
|
||||||
gutenberg_id = fields.CharField(
|
gutenberg_id = fields.CharField(
|
||||||
max_length=255, blank=True, null=True, deduplication_field=True
|
max_length=255, blank=True, null=True, deduplication_field=True
|
||||||
)
|
)
|
||||||
|
isfdb = fields.CharField(
|
||||||
|
max_length=255, blank=True, null=True, deduplication_field=True
|
||||||
|
)
|
||||||
# idk probably other keys would be useful here?
|
# idk probably other keys would be useful here?
|
||||||
born = fields.DateTimeField(blank=True, null=True)
|
born = fields.DateTimeField(blank=True, null=True)
|
||||||
died = fields.DateTimeField(blank=True, null=True)
|
died = fields.DateTimeField(blank=True, null=True)
|
||||||
|
@ -60,6 +63,11 @@ class Author(BookDataModel):
|
||||||
"""generate the url from the openlibrary id"""
|
"""generate the url from the openlibrary id"""
|
||||||
return f"https://openlibrary.org/authors/{self.openlibrary_key}"
|
return f"https://openlibrary.org/authors/{self.openlibrary_key}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isfdb_link(self):
|
||||||
|
"""generate the url from the isni id"""
|
||||||
|
return f"https://www.isfdb.org/cgi-bin/ea.cgi?{self.isfdb}"
|
||||||
|
|
||||||
def get_remote_id(self):
|
def get_remote_id(self):
|
||||||
"""editions and works both use "book" instead of model_name"""
|
"""editions and works both use "book" instead of model_name"""
|
||||||
return f"https://{DOMAIN}/author/{self.id}"
|
return f"https://{DOMAIN}/author/{self.id}"
|
||||||
|
|
|
@ -55,6 +55,12 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
|
||||||
asin = fields.CharField(
|
asin = fields.CharField(
|
||||||
max_length=255, blank=True, null=True, deduplication_field=True
|
max_length=255, blank=True, null=True, deduplication_field=True
|
||||||
)
|
)
|
||||||
|
aasin = fields.CharField(
|
||||||
|
max_length=255, blank=True, null=True, deduplication_field=True
|
||||||
|
)
|
||||||
|
isfdb = fields.CharField(
|
||||||
|
max_length=255, blank=True, null=True, deduplication_field=True
|
||||||
|
)
|
||||||
search_vector = SearchVectorField(null=True)
|
search_vector = SearchVectorField(null=True)
|
||||||
|
|
||||||
last_edited_by = fields.ForeignKey(
|
last_edited_by = fields.ForeignKey(
|
||||||
|
@ -73,6 +79,11 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
|
||||||
"""generate the url from the inventaire id"""
|
"""generate the url from the inventaire id"""
|
||||||
return f"https://inventaire.io/entity/{self.inventaire_id}"
|
return f"https://inventaire.io/entity/{self.inventaire_id}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isfdb_link(self):
|
||||||
|
"""generate the url from the isfdb id"""
|
||||||
|
return f"https://www.isfdb.org/cgi-bin/title.cgi?{self.isfdb}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""can't initialize this model, that wouldn't make sense"""
|
"""can't initialize this model, that wouldn't make sense"""
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<meta itemprop="name" content="{{ author.name }}">
|
<meta itemprop="name" content="{{ author.name }}">
|
||||||
|
|
||||||
{% firstof author.aliases author.born author.died as details %}
|
{% firstof author.aliases author.born author.died as details %}
|
||||||
{% firstof author.wikipedia_link author.openlibrary_key author.inventaire_id author.isni as links %}
|
{% firstof author.wikipedia_link author.openlibrary_key author.inventaire_id author.isni author.isfdb as links %}
|
||||||
{% if details or links %}
|
{% if details or links %}
|
||||||
<div class="column is-3">
|
<div class="column is-3">
|
||||||
{% if details %}
|
{% if details %}
|
||||||
|
@ -81,6 +81,14 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if author.isfdb %}
|
||||||
|
<div class="mt-1">
|
||||||
|
<a itemprop="sameAs" href="{{ author.isfdb_link }}" rel="nofollow noopener noreferrer" target="_blank">
|
||||||
|
{% trans "View on ISFDB" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% trans "Load data" as button_text %}
|
{% trans "Load data" as button_text %}
|
||||||
{% if author.openlibrary_key %}
|
{% if author.openlibrary_key %}
|
||||||
<div class="mt-1 is-flex">
|
<div class="mt-1 is-flex">
|
||||||
|
@ -128,6 +136,14 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if author.isfdb %}
|
||||||
|
<div>
|
||||||
|
<a itemprop="sameAs" href="https://www.isfdb.org/cgi-bin/ea.cgi?{{ author.isfdb }}" target="_blank" rel="nofollow noopener noreferrer">
|
||||||
|
{% trans "View ISFDB entry" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -101,6 +101,13 @@
|
||||||
{% include 'snippets/form_errors.html' with errors_list=form.goodreads_key.errors id="desc_goodreads_key" %}
|
{% include 'snippets/form_errors.html' with errors_list=form.goodreads_key.errors id="desc_goodreads_key" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_isfdb">{% trans "ISFDB:" %}</label>
|
||||||
|
{{ form.isfdb }}
|
||||||
|
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.isfdb.errors id="desc_isfdb" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="id_isni">{% trans "ISNI:" %}</label>
|
<label class="label" for="id_isni">{% trans "ISNI:" %}</label>
|
||||||
{{ form.isni }}
|
{{ form.isni }}
|
||||||
|
|
|
@ -158,6 +158,13 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if book.isfdb %}
|
||||||
|
<p>
|
||||||
|
<a href="{{ book.isfdb_link }}" target="_blank" rel="nofollow noopener noreferrer">
|
||||||
|
{% trans "View on ISFDB" %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% spaceless %}
|
{% spaceless %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% if book.isbn_13 or book.oclc_number or book.asin %}
|
{% if book.isbn_13 or book.oclc_number or book.asin or book.aasin or book.isfdb %}
|
||||||
<dl>
|
<dl>
|
||||||
{% if book.isbn_13 %}
|
{% if book.isbn_13 %}
|
||||||
<div class="is-flex">
|
<div class="is-flex">
|
||||||
|
@ -23,6 +23,21 @@
|
||||||
<dd>{{ book.asin }}</dd>
|
<dd>{{ book.asin }}</dd>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.aasin %}
|
||||||
|
<div class="is-flex">
|
||||||
|
<dt class="mr-1">{% trans "Audible ASIN:" %}</dt>
|
||||||
|
<dd>{{ book.aasin }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.isfdb %}
|
||||||
|
<div class="is-flex">
|
||||||
|
<dt class="mr-1">{% trans "ISFDB ID:" %}</dt>
|
||||||
|
<dd>{{ book.isfdb }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if book.goodreads_key %}
|
{% if book.goodreads_key %}
|
||||||
<div class="is-flex">
|
<div class="is-flex">
|
||||||
<dt class="mr-1">{% trans "Goodreads:" %}</dt>
|
<dt class="mr-1">{% trans "Goodreads:" %}</dt>
|
||||||
|
|
|
@ -353,6 +353,24 @@
|
||||||
|
|
||||||
{% include 'snippets/form_errors.html' with errors_list=form.ASIN.errors id="desc_ASIN" %}
|
{% include 'snippets/form_errors.html' with errors_list=form.ASIN.errors id="desc_ASIN" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_aasin">
|
||||||
|
{% trans "Audible ASIN:" %}
|
||||||
|
</label>
|
||||||
|
{{ form.aasin }}
|
||||||
|
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.AASIN.errors id="desc_AASIN" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_isfdb">
|
||||||
|
{% trans "ISFDB ID:" %}
|
||||||
|
</label>
|
||||||
|
{{ form.isfdb }}
|
||||||
|
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.isfdb.errors id="desc_isfdb" %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"openlibrary_key": "OL29486417M",
|
"openlibrary_key": "OL29486417M",
|
||||||
"librarything_key": null,
|
"librarything_key": null,
|
||||||
"goodreads_key": null,
|
"goodreads_key": null,
|
||||||
|
"isfdb": null,
|
||||||
"attachment": [
|
"attachment": [
|
||||||
{
|
{
|
||||||
"url": "https://bookwyrm.social/images/covers/50202953._SX318_.jpg",
|
"url": "https://bookwyrm.social/images/covers/50202953._SX318_.jpg",
|
||||||
|
|
|
@ -63,7 +63,7 @@ class ExportViews(TestCase):
|
||||||
# pylint: disable=line-too-long
|
# pylint: disable=line-too-long
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result[0],
|
result[0],
|
||||||
b"title,author_text,remote_id,openlibrary_key,inventaire_id,librarything_key,goodreads_key,bnf_id,viaf,wikidata,asin,isbn_10,isbn_13,oclc_number,rating,review_name,review_cw,review_content\r\n",
|
b"title,author_text,remote_id,openlibrary_key,inventaire_id,librarything_key,goodreads_key,bnf_id,viaf,wikidata,asin,aasin,isfdb,isbn_10,isbn_13,oclc_number,rating,review_name,review_cw,review_content\r\n",
|
||||||
)
|
)
|
||||||
expected = f"Test Book,,{self.book.remote_id},,,,,beep,,,,123456789X,9781234567890,,,,,\r\n"
|
expected = f"Test Book,,{self.book.remote_id},,,,,beep,,,,,,123456789X,9781234567890,,,,,\r\n"
|
||||||
self.assertEqual(result[1].decode("utf-8"), expected)
|
self.assertEqual(result[1].decode("utf-8"), expected)
|
||||||
|
|
|
@ -49,6 +49,8 @@ class Editions(View):
|
||||||
"isbn_13",
|
"isbn_13",
|
||||||
"oclc_number",
|
"oclc_number",
|
||||||
"asin",
|
"asin",
|
||||||
|
"aasin",
|
||||||
|
"isfdb",
|
||||||
]
|
]
|
||||||
search_filter_entries = [
|
search_filter_entries = [
|
||||||
{f"{f}__icontains": query} for f in searchable_fields
|
{f"{f}__icontains": query} for f in searchable_fields
|
||||||
|
|
Loading…
Reference in a new issue