mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-27 12:01:14 +00:00
Merge pull request #1113 from bookwyrm-social/book-format-choices
Adds book format field with choices
This commit is contained in:
commit
009a1fe4fd
15 changed files with 4761 additions and 4357 deletions
|
@ -54,6 +54,7 @@ class Edition(Book):
|
||||||
asin: str = ""
|
asin: str = ""
|
||||||
pages: int = None
|
pages: int = None
|
||||||
physicalFormat: str = ""
|
physicalFormat: str = ""
|
||||||
|
physicalFormatDetail: str = ""
|
||||||
publishers: List[str] = field(default_factory=lambda: [])
|
publishers: List[str] = field(default_factory=lambda: [])
|
||||||
editionRank: int = 0
|
editionRank: int = 0
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from requests.exceptions import RequestException
|
||||||
|
|
||||||
from bookwyrm import activitypub, models, settings
|
from bookwyrm import activitypub, models, settings
|
||||||
from .connector_manager import load_more_data, ConnectorException
|
from .connector_manager import load_more_data, ConnectorException
|
||||||
|
from .format_mappings import format_mappings
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -312,3 +313,25 @@ class Mapping:
|
||||||
return self.formatter(value)
|
return self.formatter(value)
|
||||||
except: # pylint: disable=bare-except
|
except: # pylint: disable=bare-except
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def infer_physical_format(format_text):
|
||||||
|
"""try to figure out what the standardized format is from the free value"""
|
||||||
|
format_text = format_text.lower()
|
||||||
|
if format_text in format_mappings:
|
||||||
|
# try a direct match
|
||||||
|
return format_mappings[format_text]
|
||||||
|
# failing that, try substring
|
||||||
|
matches = [v for k, v in format_mappings.items() if k in format_text]
|
||||||
|
if not matches:
|
||||||
|
return None
|
||||||
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
|
def unique_physical_format(format_text):
|
||||||
|
"""only store the format if it isn't diretly in the format mappings"""
|
||||||
|
format_text = format_text.lower()
|
||||||
|
if format_text in format_mappings:
|
||||||
|
# try a direct match, so saving this would be redundant
|
||||||
|
return None
|
||||||
|
return format_text
|
||||||
|
|
43
bookwyrm/connectors/format_mappings.py
Normal file
43
bookwyrm/connectors/format_mappings.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
""" comparing a free text format to the standardized one """
|
||||||
|
format_mappings = {
|
||||||
|
"paperback": "Paperback",
|
||||||
|
"soft": "Paperback",
|
||||||
|
"pamphlet": "Paperback",
|
||||||
|
"peperback": "Paperback",
|
||||||
|
"tapa blanda": "Paperback",
|
||||||
|
"turtleback": "Paperback",
|
||||||
|
"pocket": "Paperback",
|
||||||
|
"spiral": "Paperback",
|
||||||
|
"ring": "Paperback",
|
||||||
|
"平装": "Paperback",
|
||||||
|
"简装": "Paperback",
|
||||||
|
"hardcover": "Hardcover",
|
||||||
|
"hardcocer": "Hardcover",
|
||||||
|
"hardover": "Hardcover",
|
||||||
|
"hardback": "Hardcover",
|
||||||
|
"library": "Hardcover",
|
||||||
|
"tapa dura": "Hardcover",
|
||||||
|
"leather": "Hardcover",
|
||||||
|
"clothbound": "Hardcover",
|
||||||
|
"精装": "Hardcover",
|
||||||
|
"ebook": "EBook",
|
||||||
|
"e-book": "EBook",
|
||||||
|
"digital": "EBook",
|
||||||
|
"computer file": "EBook",
|
||||||
|
"epub": "EBook",
|
||||||
|
"online": "EBook",
|
||||||
|
"pdf": "EBook",
|
||||||
|
"elektronische": "EBook",
|
||||||
|
"electronic": "EBook",
|
||||||
|
"audiobook": "AudiobookFormat",
|
||||||
|
"audio": "AudiobookFormat",
|
||||||
|
"cd": "AudiobookFormat",
|
||||||
|
"dvd": "AudiobookFormat",
|
||||||
|
"mp3": "AudiobookFormat",
|
||||||
|
"cassette": "AudiobookFormat",
|
||||||
|
"kindle": "AudiobookFormat",
|
||||||
|
"talking": "AudiobookFormat",
|
||||||
|
"sound": "AudiobookFormat",
|
||||||
|
"comic": "GraphicNovel",
|
||||||
|
"graphic": "GraphicNovel",
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ from .connector_manager import ConnectorException
|
||||||
|
|
||||||
|
|
||||||
class Connector(AbstractConnector):
|
class Connector(AbstractConnector):
|
||||||
"""instantiate a connector for OL"""
|
"""instantiate a connector for inventaire"""
|
||||||
|
|
||||||
def __init__(self, identifier):
|
def __init__(self, identifier):
|
||||||
super().__init__(identifier)
|
super().__init__(identifier)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import re
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from .abstract_connector import AbstractConnector, SearchResult, Mapping
|
from .abstract_connector import AbstractConnector, SearchResult, Mapping
|
||||||
from .abstract_connector import get_data
|
from .abstract_connector import get_data, infer_physical_format, unique_physical_format
|
||||||
from .connector_manager import ConnectorException
|
from .connector_manager import ConnectorException
|
||||||
from .openlibrary_languages import languages
|
from .openlibrary_languages import languages
|
||||||
|
|
||||||
|
@ -43,7 +43,16 @@ class Connector(AbstractConnector):
|
||||||
),
|
),
|
||||||
Mapping("publishedDate", remote_field="publish_date"),
|
Mapping("publishedDate", remote_field="publish_date"),
|
||||||
Mapping("pages", remote_field="number_of_pages"),
|
Mapping("pages", remote_field="number_of_pages"),
|
||||||
Mapping("physicalFormat", remote_field="physical_format"),
|
Mapping(
|
||||||
|
"physicalFormat",
|
||||||
|
remote_field="physical_format",
|
||||||
|
formatter=infer_physical_format,
|
||||||
|
),
|
||||||
|
Mapping(
|
||||||
|
"physicalFormatDetail",
|
||||||
|
remote_field="physical_format",
|
||||||
|
formatter=unique_physical_format,
|
||||||
|
),
|
||||||
Mapping("publishers"),
|
Mapping("publishers"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
56
bookwyrm/migrations/0101_auto_20210929_1847.py
Normal file
56
bookwyrm/migrations/0101_auto_20210929_1847.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Generated by Django 3.2 on 2021-05-21 00:17
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import bookwyrm
|
||||||
|
from bookwyrm.connectors.abstract_connector import infer_physical_format
|
||||||
|
|
||||||
|
|
||||||
|
def infer_format(app_registry, schema_editor):
|
||||||
|
"""set the new phsyical format field based on existing format data"""
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
editions = (
|
||||||
|
app_registry.get_model("bookwyrm", "Edition")
|
||||||
|
.objects.using(db_alias)
|
||||||
|
.filter(physical_format_detail__isnull=False)
|
||||||
|
)
|
||||||
|
for edition in editions:
|
||||||
|
free_format = edition.physical_format_detail.lower()
|
||||||
|
edition.physical_format = infer_physical_format(free_format)
|
||||||
|
edition.save()
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(app_registry, schema_editor):
|
||||||
|
"""doesn't need to do anything"""
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0100_shelf_description"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="edition",
|
||||||
|
old_name="physical_format",
|
||||||
|
new_name="physical_format_detail",
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="edition",
|
||||||
|
name="physical_format",
|
||||||
|
field=bookwyrm.models.fields.CharField(
|
||||||
|
blank=True,
|
||||||
|
choices=[
|
||||||
|
("AudiobookFormat", "Audiobook"),
|
||||||
|
("EBook", "eBook"),
|
||||||
|
("GraphicNovel", "Graphic novel"),
|
||||||
|
("Hardcover", "Hardcover"),
|
||||||
|
("Paperback", "Paperback"),
|
||||||
|
],
|
||||||
|
max_length=255,
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RunPython(infer_format, reverse),
|
||||||
|
]
|
|
@ -6,6 +6,7 @@ from django.contrib.postgres.indexes import GinIndex
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from model_utils import FieldTracker
|
from model_utils import FieldTracker
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
from imagekit.models import ImageSpecField
|
from imagekit.models import ImageSpecField
|
||||||
|
@ -226,6 +227,16 @@ class Work(OrderedCollectionPageMixin, Book):
|
||||||
deserialize_reverse_fields = [("editions", "editions")]
|
deserialize_reverse_fields = [("editions", "editions")]
|
||||||
|
|
||||||
|
|
||||||
|
# https://schema.org/BookFormatType
|
||||||
|
FormatChoices = [
|
||||||
|
("AudiobookFormat", _("Audiobook")),
|
||||||
|
("EBook", _("eBook")),
|
||||||
|
("GraphicNovel", _("Graphic novel")),
|
||||||
|
("Hardcover", _("Hardcover")),
|
||||||
|
("Paperback", _("Paperback")),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Edition(Book):
|
class Edition(Book):
|
||||||
"""an edition of a book"""
|
"""an edition of a book"""
|
||||||
|
|
||||||
|
@ -243,7 +254,10 @@ class Edition(Book):
|
||||||
max_length=255, blank=True, null=True, deduplication_field=True
|
max_length=255, blank=True, null=True, deduplication_field=True
|
||||||
)
|
)
|
||||||
pages = fields.IntegerField(blank=True, null=True)
|
pages = fields.IntegerField(blank=True, null=True)
|
||||||
physical_format = fields.CharField(max_length=255, blank=True, null=True)
|
physical_format = fields.CharField(
|
||||||
|
max_length=255, choices=FormatChoices, null=True, blank=True
|
||||||
|
)
|
||||||
|
physical_format_detail = fields.CharField(max_length=255, blank=True, null=True)
|
||||||
publishers = fields.ArrayField(
|
publishers = fields.ArrayField(
|
||||||
models.CharField(max_length=255), blank=True, default=list
|
models.CharField(max_length=255), blank=True, default=list
|
||||||
)
|
)
|
||||||
|
|
|
@ -251,13 +251,28 @@
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<h2 class="title is-4">{% trans "Physical Properties" %}</h2>
|
<h2 class="title is-4">{% trans "Physical Properties" %}</h2>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-one-third">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="id_physical_format">{% trans "Format:" %}</label>
|
<label class="label" for="id_physical_format">{% trans "Format:" %}</label>
|
||||||
|
<div class="select">
|
||||||
{{ form.physical_format }}
|
{{ form.physical_format }}
|
||||||
|
</div>
|
||||||
{% for error in form.physical_format.errors %}
|
{% for error in form.physical_format.errors %}
|
||||||
<p class="help is-danger">{{ error | escape }}</p>
|
<p class="help is-danger">{{ error | escape }}</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_physical_format_detail">{% trans "Format details:" %}</label>
|
||||||
|
{{ form.physical_format_detail }}
|
||||||
|
{% for error in form.physical_format_detail.errors %}
|
||||||
|
<p class="help is-danger">{{ error | escape }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="id_pages">{% trans "Pages:" %}</label>
|
<label class="label" for="id_pages">{% trans "Pages:" %}</label>
|
||||||
|
|
|
@ -4,13 +4,15 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% with format=book.physical_format pages=book.pages %}
|
{% firstof book.physical_format_detail book.physical_format as format %}
|
||||||
|
{% firstof book.physical_format book.physical_format_detail as format_property %}
|
||||||
|
{% with pages=book.pages %}
|
||||||
{% if format %}
|
{% if format %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
@todo The bookFormat property is limited to a list of values whereas the book edition is free text.
|
@todo The bookFormat property is limited to a list of values whereas the book edition is free text.
|
||||||
@see https://schema.org/bookFormat
|
@see https://schema.org/bookFormat
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
<meta itemprop="bookFormat" content="{{ format }}">
|
<meta itemprop="bookFormat" content="{{ format_property }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if pages %}
|
{% if pages %}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue