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:
Jascha Urbach 2022-12-11 20:33:33 +01:00 committed by GitHub
parent 0e2b88ad0c
commit ac8b060d58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 150 additions and 4 deletions

View file

@ -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: [])

View file

@ -21,6 +21,7 @@ class AuthorForm(CustomForm):
"inventaire_id", "inventaire_id",
"librarything_key", "librarything_key",
"goodreads_key", "goodreads_key",
"isfdb",
"isni", "isni",
] ]
widgets = { widgets = {

View file

@ -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"}),
} }

View 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
),
),
]

View 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
),
),
]

View file

@ -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}"

View file

@ -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"""

View file

@ -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 %}

View file

@ -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 }}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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",

View file

@ -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)

View file

@ -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