Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2021-04-30 06:54:05 -07:00
commit 8761357905
74 changed files with 2195 additions and 661 deletions

View file

@ -7,6 +7,9 @@ DEBUG=false
DOMAIN=your.domain.here DOMAIN=your.domain.here
#EMAIL=your@email.here #EMAIL=your@email.here
# Used for deciding which editions to prefer
DEFAULT_LANGUAGE="English"
## Leave unset to allow all hosts ## Leave unset to allow all hosts
# ALLOWED_HOSTS="localhost,127.0.0.1,[::1]" # ALLOWED_HOSTS="localhost,127.0.0.1,[::1]"

View file

@ -7,6 +7,9 @@ DEBUG=false
DOMAIN=your.domain.here DOMAIN=your.domain.here
EMAIL=your@email.here EMAIL=your@email.here
# Used for deciding which editions to prefer
DEFAULT_LANGUAGE="English"
## Leave unset to allow all hosts ## Leave unset to allow all hosts
# ALLOWED_HOSTS="localhost,127.0.0.1,[::1]" # ALLOWED_HOSTS="localhost,127.0.0.1,[::1]"

View file

@ -9,10 +9,11 @@ Permission is hereby granted, free of charge, to any person or organization (the
1. The above copyright notice and this permission notice shall be included in all copies or modified versions of the Software. 1. The above copyright notice and this permission notice shall be included in all copies or modified versions of the Software.
2. The User is one of the following: 2. The User is one of the following:
a. An individual person, laboring for themselves
b. A non-profit organization 1. An individual person, laboring for themselves
c. An educational institution 2. A non-profit organization
d. An organization that seeks shared profit for all of its members, and allows non-members to set the cost of their labor 3. An educational institution
4. An organization that seeks shared profit for all of its members, and allows non-members to set the cost of their labor
3. If the User is an organization with owners, then all owners are workers and all workers are owners with equal equity and/or equal vote. 3. If the User is an organization with owners, then all owners are workers and all workers are owners with equal equity and/or equal vote.

View file

@ -7,11 +7,22 @@ from .image import Document
@dataclass(init=False) @dataclass(init=False)
class Book(ActivityObject): class BookData(ActivityObject):
"""shared fields for all book data and authors"""
openlibraryKey: str = None
inventaireId: str = None
librarythingKey: str = None
goodreadsKey: str = None
bnfId: str = None
lastEditedBy: str = None
@dataclass(init=False)
class Book(BookData):
"""serializes an edition or work, abstract""" """serializes an edition or work, abstract"""
title: str title: str
lastEditedBy: str = None
sortTitle: str = "" sortTitle: str = ""
subtitle: str = "" subtitle: str = ""
description: str = "" description: str = ""
@ -25,10 +36,6 @@ class Book(ActivityObject):
firstPublishedDate: str = "" firstPublishedDate: str = ""
publishedDate: str = "" publishedDate: str = ""
openlibraryKey: str = ""
librarythingKey: str = ""
goodreadsKey: str = ""
cover: Document = None cover: Document = None
type: str = "Book" type: str = "Book"
@ -55,23 +62,21 @@ class Work(Book):
"""work instance of a book object""" """work instance of a book object"""
lccn: str = "" lccn: str = ""
defaultEdition: str = ""
editions: List[str] = field(default_factory=lambda: []) editions: List[str] = field(default_factory=lambda: [])
type: str = "Work" type: str = "Work"
@dataclass(init=False) @dataclass(init=False)
class Author(ActivityObject): class Author(BookData):
"""author of a book""" """author of a book"""
name: str name: str
lastEditedBy: str = None isni: str = None
viafId: str = None
gutenbergId: str = None
born: str = None born: str = None
died: str = None died: str = None
aliases: List[str] = field(default_factory=lambda: []) aliases: List[str] = field(default_factory=lambda: [])
bio: str = "" bio: str = ""
openlibraryKey: str = ""
librarythingKey: str = ""
goodreadsKey: str = ""
wikipediaLink: str = "" wikipediaLink: str = ""
type: str = "Author" type: str = "Author"

View file

@ -83,4 +83,5 @@ class Rating(Comment):
rating: int rating: int
content: str = None content: str = None
name: str = None # not used, but the model inherits from Review
type: str = "Rating" type: str = "Rating"

View file

@ -44,7 +44,7 @@ class AbstractMinimalConnector(ABC):
if min_confidence: if min_confidence:
params["min_confidence"] = min_confidence params["min_confidence"] = min_confidence
data = get_data( data = self.get_search_data(
"%s%s" % (self.search_url, query), "%s%s" % (self.search_url, query),
params=params, params=params,
) )
@ -57,7 +57,7 @@ class AbstractMinimalConnector(ABC):
def isbn_search(self, query): def isbn_search(self, query):
"""isbn search""" """isbn search"""
params = {} params = {}
data = get_data( data = self.get_search_data(
"%s%s" % (self.isbn_search_url, query), "%s%s" % (self.isbn_search_url, query),
params=params, params=params,
) )
@ -68,6 +68,10 @@ class AbstractMinimalConnector(ABC):
results.append(self.format_isbn_search_result(doc)) results.append(self.format_isbn_search_result(doc))
return results return results
def get_search_data(self, remote_id, **kwargs): # pylint: disable=no-self-use
"""this allows connectors to override the default behavior"""
return get_data(remote_id, **kwargs)
@abstractmethod @abstractmethod
def get_or_create_book(self, remote_id): def get_or_create_book(self, remote_id):
"""pull up a book record by whatever means possible""" """pull up a book record by whatever means possible"""
@ -112,12 +116,12 @@ class AbstractConnector(AbstractMinimalConnector):
remote_id remote_id
) or models.Work.find_existing_by_remote_id(remote_id) ) or models.Work.find_existing_by_remote_id(remote_id)
if existing: if existing:
if hasattr(existing, "get_default_editon"): if hasattr(existing, "default_edition"):
return existing.get_default_editon() return existing.default_edition
return existing return existing
# load the json # load the json
data = get_data(remote_id) data = self.get_book_data(remote_id)
mapped_data = dict_from_mappings(data, self.book_mappings) mapped_data = dict_from_mappings(data, self.book_mappings)
if self.is_work_data(data): if self.is_work_data(data):
try: try:
@ -128,12 +132,12 @@ class AbstractConnector(AbstractMinimalConnector):
edition_data = data edition_data = data
work_data = mapped_data work_data = mapped_data
else: else:
edition_data = data
try: try:
work_data = self.get_work_from_edition_data(data) work_data = self.get_work_from_edition_data(data)
work_data = dict_from_mappings(work_data, self.book_mappings) work_data = dict_from_mappings(work_data, self.book_mappings)
except (KeyError, ConnectorException): except (KeyError, ConnectorException):
work_data = mapped_data work_data = mapped_data
edition_data = data
if not work_data or not edition_data: if not work_data or not edition_data:
raise ConnectorException("Unable to load book data: %s" % remote_id) raise ConnectorException("Unable to load book data: %s" % remote_id)
@ -150,6 +154,10 @@ class AbstractConnector(AbstractMinimalConnector):
load_more_data.delay(self.connector.id, work.id) load_more_data.delay(self.connector.id, work.id)
return edition return edition
def get_book_data(self, remote_id): # pylint: disable=no-self-use
"""this allows connectors to override the default behavior"""
return get_data(remote_id)
def create_edition_from_data(self, work, edition_data): def create_edition_from_data(self, work, edition_data):
"""if we already have the work, we're ready""" """if we already have the work, we're ready"""
mapped_data = dict_from_mappings(edition_data, self.book_mappings) mapped_data = dict_from_mappings(edition_data, self.book_mappings)
@ -159,10 +167,6 @@ class AbstractConnector(AbstractMinimalConnector):
edition.connector = self.connector edition.connector = self.connector
edition.save() edition.save()
if not work.default_edition:
work.default_edition = edition
work.save()
for author in self.get_authors_from_data(edition_data): for author in self.get_authors_from_data(edition_data):
edition.authors.add(author) edition.authors.add(author)
if not edition.authors.exists() and work.authors.exists(): if not edition.authors.exists() and work.authors.exists():
@ -176,7 +180,7 @@ class AbstractConnector(AbstractMinimalConnector):
if existing: if existing:
return existing return existing
data = get_data(remote_id) data = self.get_book_data(remote_id)
mapped_data = dict_from_mappings(data, self.author_mappings) mapped_data = dict_from_mappings(data, self.author_mappings)
try: try:
@ -273,6 +277,7 @@ class SearchResult:
title: str title: str
key: str key: str
connector: object connector: object
view_link: str = None
author: str = None author: str = None
year: str = None year: str = None
cover: str = None cover: str = None

View file

@ -7,11 +7,7 @@ class Connector(AbstractMinimalConnector):
"""this is basically just for search""" """this is basically just for search"""
def get_or_create_book(self, remote_id): def get_or_create_book(self, remote_id):
edition = activitypub.resolve_remote_id(remote_id, model=models.Edition) return activitypub.resolve_remote_id(remote_id, model=models.Edition)
work = edition.parent_work
work.default_edition = work.get_default_edition()
work.save()
return edition
def parse_search_data(self, data): def parse_search_data(self, data):
return data return data

View file

@ -0,0 +1,214 @@
""" inventaire data connector """
import re
from bookwyrm import models
from .abstract_connector import AbstractConnector, SearchResult, Mapping
from .abstract_connector import get_data
from .connector_manager import ConnectorException
class Connector(AbstractConnector):
"""instantiate a connector for OL"""
def __init__(self, identifier):
super().__init__(identifier)
get_first = lambda a: a[0]
shared_mappings = [
Mapping("id", remote_field="uri", formatter=self.get_remote_id),
Mapping("bnfId", remote_field="wdt:P268", formatter=get_first),
Mapping("openlibraryKey", remote_field="wdt:P648", formatter=get_first),
]
self.book_mappings = [
Mapping("title", remote_field="wdt:P1476", formatter=get_first),
Mapping("subtitle", remote_field="wdt:P1680", formatter=get_first),
Mapping("inventaireId", remote_field="uri"),
Mapping(
"description", remote_field="sitelinks", formatter=self.get_description
),
Mapping("cover", remote_field="image", formatter=self.get_cover_url),
Mapping("isbn13", remote_field="wdt:P212", formatter=get_first),
Mapping("isbn10", remote_field="wdt:P957", formatter=get_first),
Mapping("oclcNumber", remote_field="wdt:P5331", formatter=get_first),
Mapping("goodreadsKey", remote_field="wdt:P2969", formatter=get_first),
Mapping("librarythingKey", remote_field="wdt:P1085", formatter=get_first),
Mapping("languages", remote_field="wdt:P407", formatter=self.resolve_keys),
Mapping("publishers", remote_field="wdt:P123", formatter=self.resolve_keys),
Mapping("publishedDate", remote_field="wdt:P577", formatter=get_first),
Mapping("pages", remote_field="wdt:P1104", formatter=get_first),
Mapping(
"subjectPlaces", remote_field="wdt:P840", formatter=self.resolve_keys
),
Mapping("subjects", remote_field="wdt:P921", formatter=self.resolve_keys),
Mapping("asin", remote_field="wdt:P5749", formatter=get_first),
] + shared_mappings
# TODO: P136: genre, P674 characters, P950 bne
self.author_mappings = [
Mapping("id", remote_field="uri", formatter=self.get_remote_id),
Mapping("name", remote_field="labels", formatter=get_language_code),
Mapping("bio", remote_field="sitelinks", formatter=self.get_description),
Mapping("goodreadsKey", remote_field="wdt:P2963", formatter=get_first),
Mapping("isni", remote_field="wdt:P213", formatter=get_first),
Mapping("viafId", remote_field="wdt:P214", formatter=get_first),
Mapping("gutenberg_id", remote_field="wdt:P1938", formatter=get_first),
Mapping("born", remote_field="wdt:P569", formatter=get_first),
Mapping("died", remote_field="wdt:P570", formatter=get_first),
] + shared_mappings
def get_remote_id(self, value):
"""convert an id/uri into a url"""
return "{:s}?action=by-uris&uris={:s}".format(self.books_url, value)
def get_book_data(self, remote_id):
data = get_data(remote_id)
extracted = list(data.get("entities").values())
try:
data = extracted[0]
except KeyError:
raise ConnectorException("Invalid book data")
# flatten the data so that images, uri, and claims are on the same level
return {
**data.get("claims", {}),
**{k: data.get(k) for k in ["uri", "image", "labels", "sitelinks"]},
}
def parse_search_data(self, data):
return data.get("results")
def format_search_result(self, search_result):
images = search_result.get("image")
cover = (
"{:s}/img/entities/{:s}".format(self.covers_url, images[0])
if images
else None
)
return SearchResult(
title=search_result.get("label"),
key=self.get_remote_id(search_result.get("uri")),
author=search_result.get("description"),
view_link="{:s}/entity/{:s}".format(
self.base_url, search_result.get("uri")
),
cover=cover,
connector=self,
)
def parse_isbn_search_data(self, data):
"""got some daaaata"""
results = data.get("entities")
if not results:
return []
return list(results.values())
def format_isbn_search_result(self, search_result):
"""totally different format than a regular search result"""
title = search_result.get("claims", {}).get("wdt:P1476", [])
if not title:
return None
return SearchResult(
title=title[0],
key=self.get_remote_id(search_result.get("uri")),
author=search_result.get("description"),
view_link="{:s}/entity/{:s}".format(
self.base_url, search_result.get("uri")
),
cover=self.get_cover_url(search_result.get("image")),
connector=self,
)
def is_work_data(self, data):
return data.get("type") == "work"
def load_edition_data(self, work_uri):
"""get a list of editions for a work"""
url = "{:s}?action=reverse-claims&property=wdt:P629&value={:s}".format(
self.books_url, work_uri
)
return get_data(url)
def get_edition_from_work_data(self, data):
data = self.load_edition_data(data.get("uri"))
try:
uri = data["uris"][0]
except KeyError:
raise ConnectorException("Invalid book data")
return self.get_book_data(self.get_remote_id(uri))
def get_work_from_edition_data(self, data):
try:
uri = data["claims"]["wdt:P629"]
except KeyError:
raise ConnectorException("Invalid book data")
return self.get_book_data(self.get_remote_id(uri))
def get_authors_from_data(self, data):
authors = data.get("wdt:P50", [])
for author in authors:
yield self.get_or_create_author(self.get_remote_id(author))
def expand_book_data(self, book):
work = book
# go from the edition to the work, if necessary
if isinstance(book, models.Edition):
work = book.parent_work
try:
edition_options = self.load_edition_data(work.inventaire_id)
except ConnectorException:
# who knows, man
return
for edition_uri in edition_options.get("uris"):
remote_id = self.get_remote_id(edition_uri)
try:
data = self.get_book_data(remote_id)
except ConnectorException:
# who, indeed, knows
continue
self.create_edition_from_data(work, data)
def get_cover_url(self, cover_blob, *_):
"""format the relative cover url into an absolute one:
{"url": "/img/entities/e794783f01b9d4f897a1ea9820b96e00d346994f"}
"""
# covers may or may not be a list
if isinstance(cover_blob, list) and len(cover_blob) > 0:
cover_blob = cover_blob[0]
cover_id = cover_blob.get("url")
if not cover_id:
return None
# cover may or may not be an absolute url already
if re.match(r"^http", cover_id):
return cover_id
return "%s%s" % (self.covers_url, cover_id)
def resolve_keys(self, keys):
"""cool, it's "wd:Q3156592" now what the heck does that mean"""
results = []
for uri in keys:
try:
data = self.get_book_data(self.get_remote_id(uri))
except ConnectorException:
continue
results.append(get_language_code(data.get("labels")))
return results
def get_description(self, links):
"""grab an extracted excerpt from wikipedia"""
link = links.get("enwiki")
if not link:
return ""
url = "{:s}/api/data?action=wp-extract&lang=en&title={:s}".format(
self.base_url, link
)
try:
data = get_data(url)
except ConnectorException:
return ""
return data.get("extract")
def get_language_code(options, code="en"):
"""when there are a bunch of translation but we need a single field"""
return options.get(code)

View file

@ -14,8 +14,8 @@ class Connector(AbstractConnector):
def __init__(self, identifier): def __init__(self, identifier):
super().__init__(identifier) super().__init__(identifier)
get_first = lambda a: a[0] get_first = lambda a, *args: a[0]
get_remote_id = lambda a: self.base_url + a get_remote_id = lambda a, *args: self.base_url + a
self.book_mappings = [ self.book_mappings = [
Mapping("title"), Mapping("title"),
Mapping("id", remote_field="key", formatter=get_remote_id), Mapping("id", remote_field="key", formatter=get_remote_id),

View file

@ -3,7 +3,7 @@ from functools import reduce
import operator import operator
from django.contrib.postgres.search import SearchRank, SearchVector from django.contrib.postgres.search import SearchRank, SearchVector
from django.db.models import Count, F, Q from django.db.models import Count, OuterRef, Subquery, F, Q
from bookwyrm import models from bookwyrm import models
from .abstract_connector import AbstractConnector, SearchResult from .abstract_connector import AbstractConnector, SearchResult
@ -47,7 +47,16 @@ class Connector(AbstractConnector):
# when there are multiple editions of the same work, pick the default. # when there are multiple editions of the same work, pick the default.
# it would be odd for this to happen. # it would be odd for this to happen.
results = results.filter(parent_work__default_edition__id=F("id")) or results
default_editions = models.Edition.objects.filter(
parent_work=OuterRef("parent_work")
).order_by("-edition_rank")
results = (
results.annotate(
default_id=Subquery(default_editions.values("id")[:1])
).filter(default_id=F("id"))
or results
)
search_results = [] search_results = []
for result in results: for result in results:
@ -60,6 +69,10 @@ class Connector(AbstractConnector):
return search_results return search_results
def format_search_result(self, search_result): def format_search_result(self, search_result):
cover = None
if search_result.cover:
cover = "%s%s" % (self.covers_url, search_result.cover)
return SearchResult( return SearchResult(
title=search_result.title, title=search_result.title,
key=search_result.remote_id, key=search_result.remote_id,
@ -68,7 +81,7 @@ class Connector(AbstractConnector):
if search_result.published_date if search_result.published_date
else None, else None,
connector=self, connector=self,
cover="%s%s" % (self.covers_url, search_result.cover), cover=cover,
confidence=search_result.rank if hasattr(search_result, "rank") else 1, confidence=search_result.rank if hasattr(search_result, "rank") else 1,
) )
@ -112,7 +125,15 @@ def search_identifiers(query, *filters):
# when there are multiple editions of the same work, pick the default. # when there are multiple editions of the same work, pick the default.
# it would be odd for this to happen. # it would be odd for this to happen.
return results.filter(parent_work__default_edition__id=F("id")) or results default_editions = models.Edition.objects.filter(
parent_work=OuterRef("parent_work")
).order_by("-edition_rank")
return (
results.annotate(default_id=Subquery(default_editions.values("id")[:1])).filter(
default_id=F("id")
)
or results
)
def search_title_author(query, min_confidence, *filters): def search_title_author(query, min_confidence, *filters):
@ -140,10 +161,10 @@ def search_title_author(query, min_confidence, *filters):
for work_id in set(editions_of_work): for work_id in set(editions_of_work):
editions = results.filter(parent_work=work_id) editions = results.filter(parent_work=work_id)
default = editions.filter(parent_work__default_edition=F("id")) default = editions.order_by("-edition_rank").first()
default_rank = default.first().rank if default.exists() else 0 default_rank = default.rank if default else 0
# if mutliple books have the top rank, pick the default edition # if mutliple books have the top rank, pick the default edition
if default_rank == editions.first().rank: if default_rank == editions.first().rank:
yield default.first() yield default
else: else:
yield editions.first() yield editions.first()

View file

@ -1,3 +1,3 @@
""" settings book data connectors """ """ settings book data connectors """
CONNECTORS = ["openlibrary", "self_connector", "bookwyrm_connector"] CONNECTORS = ["openlibrary", "inventaire", "self_connector", "bookwyrm_connector"]

View file

@ -94,6 +94,18 @@ def init_connectors():
priority=2, priority=2,
) )
Connector.objects.create(
identifier="inventaire.io",
name="Inventaire",
connector_file="inventaire",
base_url="https://inventaire.io",
books_url="https://inventaire.io/api/entities",
covers_url="https://inventaire.io",
search_url="https://inventaire.io/api/search?types=works&types=works&search=",
isbn_search_url="https://inventaire.io/api/entities?action=by-uris&uris=isbn%3A",
priority=3,
)
Connector.objects.create( Connector.objects.create(
identifier="openlibrary.org", identifier="openlibrary.org",
name="OpenLibrary", name="OpenLibrary",

View file

@ -0,0 +1,30 @@
# Generated by Django 3.1.6 on 2021-04-06 17:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0061_auto_20210402_1435"),
]
operations = [
migrations.RemoveConstraint(
model_name="connector",
name="connector_file_valid",
),
migrations.AlterField(
model_name="connector",
name="connector_file",
field=models.CharField(
choices=[
("openlibrary", "Openlibrary"),
("inventaire", "Inventaire"),
("self_connector", "Self Connector"),
("bookwyrm_connector", "Bookwyrm Connector"),
],
max_length=255,
),
),
]

View file

@ -0,0 +1,63 @@
# Generated by Django 3.1.6 on 2021-04-07 00:45
import bookwyrm.models.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0062_auto_20210406_1731"),
]
operations = [
migrations.AddField(
model_name="author",
name="bnf_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="gutenberg_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="inventaire_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="isni",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="author",
name="viaf_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="book",
name="bnf_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
migrations.AddField(
model_name="book",
name="inventaire_id",
field=bookwyrm.models.fields.CharField(
blank=True, max_length=255, null=True
),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2 on 2021-04-26 21:32
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0063_auto_20210407_0045"),
("bookwyrm", "0070_auto_20210423_0121"),
]
operations = []

View file

@ -0,0 +1,17 @@
# Generated by Django 3.2 on 2021-04-28 22:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0071_merge_0063_auto_20210407_0045_0070_auto_20210423_0121"),
]
operations = [
migrations.RemoveField(
model_name="work",
name="default_edition",
),
]

View file

@ -14,6 +14,15 @@ class Author(BookDataModel):
wikipedia_link = fields.CharField( wikipedia_link = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
isni = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
viaf_id = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
gutenberg_id = 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)

View file

@ -1,11 +1,11 @@
""" database schema for books and shelves """ """ database schema for books and shelves """
import re import re
from django.db import models, transaction from django.db import models
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN, DEFAULT_LANGUAGE
from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -19,12 +19,18 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
openlibrary_key = fields.CharField( openlibrary_key = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
inventaire_id = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
librarything_key = fields.CharField( librarything_key = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
goodreads_key = fields.CharField( goodreads_key = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
bnf_id = fields.CharField( # Bibliothèque nationale de France
max_length=255, blank=True, null=True, deduplication_field=True
)
last_edited_by = fields.ForeignKey( last_edited_by = fields.ForeignKey(
"User", "User",
@ -137,10 +143,6 @@ class Work(OrderedCollectionPageMixin, Book):
lccn = fields.CharField( lccn = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
# this has to be nullable but should never be null
default_edition = fields.ForeignKey(
"Edition", on_delete=models.PROTECT, null=True, load_remote=False
)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""set some fields on the edition object""" """set some fields on the edition object"""
@ -149,18 +151,10 @@ class Work(OrderedCollectionPageMixin, Book):
edition.save() edition.save()
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
def get_default_edition(self): @property
def default_edition(self):
"""in case the default edition is not set""" """in case the default edition is not set"""
return self.default_edition or self.editions.order_by("-edition_rank").first() return self.editions.order_by("-edition_rank").first()
@transaction.atomic()
def reset_default_edition(self):
"""sets a new default edition based on computed rank"""
self.default_edition = None
# editions are re-ranked implicitly
self.save()
self.default_edition = self.get_default_edition()
self.save()
def to_edition_list(self, **kwargs): def to_edition_list(self, **kwargs):
"""an ordered collection of editions""" """an ordered collection of editions"""
@ -214,17 +208,20 @@ class Edition(Book):
activity_serializer = activitypub.Edition activity_serializer = activitypub.Edition
name_field = "title" name_field = "title"
def get_rank(self, ignore_default=False): def get_rank(self):
"""calculate how complete the data is on this edition""" """calculate how complete the data is on this edition"""
if (
not ignore_default
and self.parent_work
and self.parent_work.default_edition == self
):
# default edition has the highest rank
return 20
rank = 0 rank = 0
# big ups for havinga cover
rank += int(bool(self.cover)) * 3 rank += int(bool(self.cover)) * 3
# is it in the instance's preferred language?
rank += int(bool(DEFAULT_LANGUAGE in self.languages))
# prefer print editions
if self.physical_format:
rank += int(
bool(self.physical_format.lower() in ["paperback", "hardcover"])
)
# does it have metadata?
rank += int(bool(self.isbn_13)) rank += int(bool(self.isbn_13))
rank += int(bool(self.isbn_10)) rank += int(bool(self.isbn_10))
rank += int(bool(self.oclc_number)) rank += int(bool(self.oclc_number))
@ -242,6 +239,12 @@ class Edition(Book):
if self.isbn_10 and not self.isbn_13: if self.isbn_10 and not self.isbn_13:
self.isbn_13 = isbn_10_to_13(self.isbn_10) self.isbn_13 = isbn_10_to_13(self.isbn_10)
# normalize isbn format
if self.isbn_10:
self.isbn_10 = re.sub(r"[^0-9X]", "", self.isbn_10)
if self.isbn_13:
self.isbn_13 = re.sub(r"[^0-9X]", "", self.isbn_13)
# set rank # set rank
self.edition_rank = self.get_rank() self.edition_rank = self.get_rank()

View file

@ -31,16 +31,6 @@ class Connector(BookWyrmModel):
# when to reset the query count back to 0 (ie, after 1 day) # when to reset the query count back to 0 (ie, after 1 day)
query_count_expiry = models.DateTimeField(auto_now_add=True, blank=True) query_count_expiry = models.DateTimeField(auto_now_add=True, blank=True)
class Meta:
"""check that there's code to actually use this connector"""
constraints = [
models.CheckConstraint(
check=models.Q(connector_file__in=ConnectorFiles),
name="connector_file_valid",
)
]
def __str__(self): def __str__(self):
return "{} ({})".format( return "{} ({})".format(
self.identifier, self.identifier,

View file

@ -11,6 +11,7 @@ DOMAIN = env("DOMAIN")
VERSION = "0.0.1" VERSION = "0.0.1"
PAGE_LENGTH = env("PAGE_LENGTH", 15) PAGE_LENGTH = env("PAGE_LENGTH", 15)
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
# celery # celery
CELERY_BROKER = env("CELERY_BROKER") CELERY_BROKER = env("CELERY_BROKER")
@ -34,6 +35,8 @@ LOCALE_PATHS = [
os.path.join(BASE_DIR, "locale"), os.path.join(BASE_DIR, "locale"),
] ]
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

View file

@ -43,10 +43,6 @@ body {
white-space: nowrap !important; white-space: nowrap !important;
width: 0.01em !important; width: 0.01em !important;
} }
.m-0-mobile {
margin: 0 !important;
}
} }
.button.is-transparent { .button.is-transparent {
@ -132,7 +128,7 @@ body {
* *
* \e9d9: filled star * \e9d9: filled star
* \e9d7: empty star; * \e9d7: empty star;
******************************************************************************/ * -------------------------------------------------------------------------- */
.form-rate-stars { .form-rate-stars {
width: max-content; width: max-content;
@ -158,70 +154,67 @@ body {
} }
/** Book covers /** Book covers
*
* - .is-cover gives the behaviour of the cover and its surrounding. (optional)
* - .cover-container gives the dimensions and position (for borders, image and other elements).
* - .book-cover is positioned and sized based on its container.
*
* To have the cover within specific dimensions, specify a width or height for
* standard bulmas named breapoints:
*
* `is-(w|h)-(auto|xs|s|m|l|xl|xxl)[-(mobile|tablet|desktop)]`
*
* The cover will be centered horizontally and vertically within those dimensions.
*
* When using `.column.is-N`, add `.is-w-auto` to the container so that the flex
* calculations are not biased by the default `max-content`.
******************************************************************************/ ******************************************************************************/
.column.is-cover {
flex-grow: 0 !important;
}
.column.is-cover,
.column.is-cover + .column {
flex-basis: auto !important;
}
.cover-container { .cover-container {
height: 250px; display: flex;
justify-content: center;
align-items: center;
position: relative;
width: max-content; width: max-content;
max-width: 250px; max-width: 100%;
overflow: hidden;
} }
.cover-container.is-large { /* Book cover
height: max-content; * -------------------------------------------------------------------------- */
max-width: 330px;
}
.cover-container.is-large img {
max-height: 500px;
height: auto;
}
.cover-container.is-medium {
height: 150px;
}
.cover-container.is-small {
height: 100px;
}
@media only screen and (max-width: 768px) {
.cover-container {
height: 200px;
width: max-content;
}
.cover-container.is-medium {
height: 100px;
}
}
.book-cover { .book-cover {
height: 100%; display: block;
object-fit: scale-down; max-width: 100%;
max-height: 100%;
/* Useful when stretching under-sized images. */
image-rendering: optimizeQuality;
image-rendering: smooth;
} }
.no-cover { /* Cover caption
position: relative; * -------------------------------------------------------------------------- */
white-space: normal;
}
.no-cover div { .no-cover .cover_caption {
position: absolute; position: absolute;
padding: 1em;
color: white;
top: 0; top: 0;
right: 0;
bottom: 0;
left: 0; left: 0;
text-align: center; padding: 0.25em;
} font-size: 0.75em;
color: white;
.cover-container.is-medium .no-cover div { background-color: #002549;
font-size: 0.9em;
padding: 0.3em;
}
.cover-container.is-small .no-cover div {
font-size: 0.7em;
padding: 0.1em;
} }
/** Avatars /** Avatars
@ -232,16 +225,6 @@ body {
display: inline; display: inline;
} }
.is-32x32 {
min-width: 32px;
min-height: 32px;
}
.is-96x96 {
min-width: 96px;
min-height: 96px;
}
/** Statuses: Quotes /** Statuses: Quotes
* *
* \e906: icon-quote-open * \e906: icon-quote-open
@ -346,3 +329,386 @@ body {
display: none; display: none;
} }
} }
/* Dimensions
* @todo These could be in rem.
******************************************************************************/
.is-32x32 {
min-width: 32px !important;
min-height: 32px !important;
}
.is-96x96 {
min-width: 96px !important;
min-height: 96px !important;
}
.is-w-auto {
width: auto !important;
}
.is-w-xs {
width: 80px !important;
}
.is-w-s {
width: 100px !important;
}
.is-w-m {
width: 150px !important;
}
.is-w-l {
width: 200px !important;
}
.is-w-xl {
width: 250px !important;
}
.is-w-xxl {
width: 500px !important;
}
.is-h-xs {
height: 80px !important;
}
.is-h-s {
height: 100px !important;
}
.is-h-m {
height: 150px !important;
}
.is-h-l {
height: 200px !important;
}
.is-h-xl {
height: 250px !important;
}
.is-h-xxl {
height: 500px !important;
}
@media only screen and (max-width: 768px) {
.is-w-auto-mobile {
width: auto !important;
}
.is-w-xs-mobile {
width: 80px !important;
}
.is-w-s-mobile {
width: 100px !important;
}
.is-w-m-mobile {
width: 150px !important;
}
.is-w-l-mobile {
width: 200px !important;
}
.is-w-xl-mobile {
width: 250px !important;
}
.is-w-xxl-mobile {
width: 500px !important;
}
.is-h-xs-mobile {
height: 80px !important;
}
.is-h-s-mobile {
height: 100px !important;
}
.is-h-m-mobile {
height: 150px !important;
}
.is-h-l-mobile {
height: 200px !important;
}
.is-h-xl-mobile {
height: 250px !important;
}
.is-h-xxl-mobile {
height: 500px !important;
}
}
@media only screen and (min-width: 769px) {
.is-w-auto-tablet {
width: auto !important;
}
.is-w-xs-tablet {
width: 80px !important;
}
.is-w-s-tablet {
width: 100px !important;
}
.is-w-m-tablet {
width: 150px !important;
}
.is-w-l-tablet {
width: 200px !important;
}
.is-w-xl-tablet {
width: 250px !important;
}
.is-w-xxl-tablet {
width: 500px !important;
}
.is-h-xs-tablet {
height: 80px !important;
}
.is-h-s-tablet {
height: 100px !important;
}
.is-h-m-tablet {
height: 150px !important;
}
.is-h-l-tablet {
height: 200px !important;
}
.is-h-xl-tablet {
height: 250px !important;
}
.is-h-xxl-tablet {
height: 500px !important;
}
}
@media only screen and (min-width: 1024px) {
.is-w-auto-desktop {
width: auto !important;
}
.is-w-xs-desktop {
width: 80px !important;
}
.is-w-s-desktop {
width: 100px !important;
}
.is-w-m-desktop {
width: 150px !important;
}
.is-w-l-desktop {
width: 200px !important;
}
.is-w-xl-desktop {
width: 250px !important;
}
.is-w-xxl-desktop {
width: 500px !important;
}
.is-h-xs-desktop {
height: 80px !important;
}
.is-h-s-desktop {
height: 100px !important;
}
.is-h-m-desktop {
height: 150px !important;
}
.is-h-l-desktop {
height: 200px !important;
}
.is-h-xl-desktop {
height: 250px !important;
}
.is-h-xxl-desktop {
height: 500px !important;
}
}
/* Alignments
*
* Use them with `.align.to-(c|t|r|b|l)[-(mobile|tablet)]`
******************************************************************************/
/* Flex item position
* -------------------------------------------------------------------------- */
.align {
display: flex !important;
flex-direction: row !important;
}
.align.to-c {
justify-content: center !important;
}
.align.to-t {
align-items: flex-start !important;
}
.align.to-r {
justify-content: flex-end !important;
}
.align.to-b {
align-items: flex-end !important;
}
.align.to-l {
justify-content: flex-start !important;
}
@media screen and (max-width: 768px) {
.align.to-c-mobile {
justify-content: center !important;
}
.align.to-t-mobile {
align-items: flex-start !important;
}
.align.to-r-mobile {
justify-content: flex-end !important;
}
.align.to-b-mobile {
align-items: flex-end !important;
}
.align.to-l-mobile {
justify-content: flex-start !important;
}
}
@media screen and (min-width: 769px) {
.align.to-c-tablet {
justify-content: center !important;
}
.align.to-t-tablet {
align-items: flex-start !important;
}
.align.to-r-tablet {
justify-content: flex-end !important;
}
.align.to-b-tablet {
align-items: flex-end !important;
}
.align.to-l-tablet {
justify-content: flex-start !important;
}
}
/* Spacings
*
* Those are supplementary rules to Bulmas. They follow the same conventions.
* Add those youll need.
******************************************************************************/
.mr-auto {
margin-right: auto !important;
}
.ml-auto {
margin-left: auto !important;
}
@media screen and (max-width: 768px) {
.m-0-mobile {
margin: 0 !important;
}
.mr-auto-mobile {
margin-right: auto !important;
}
.ml-auto-mobile {
margin-left: auto !important;
}
.mt-3-mobile {
margin-top: 0.75rem !important;
}
.ml-3-mobile {
margin-left: 0.75rem !important;
}
.mx-3-mobile {
margin-right: 0.75rem !important;
margin-left: 0.75rem !important;
}
.my-3-mobile {
margin-top: 0.75rem !important;
margin-bottom: 0.75rem !important;
}
}
@media screen and (min-width: 769px) {
.m-0-tablet {
margin: 0 !important;
}
.mr-auto-tablet {
margin-right: auto !important;
}
.ml-auto-tablet {
margin-left: auto !important;
}
.mt-3-tablet {
margin-top: 0.75rem !important;
}
.ml-3-tablet {
margin-left: 0.75rem !important;
}
.mx-3-tablet {
margin-right: 0.75rem !important;
margin-left: 0.75rem !important;
}
.my-3-tablet {
margin-top: 0.75rem !important;
margin-bottom: 0.75rem !important;
}
}

File diff suppressed because one or more lines are too long

View file

@ -48,10 +48,9 @@
<div class="columns"> <div class="columns">
<div class="column is-one-fifth"> <div class="column is-one-fifth">
<div class="is-clipped"> {% include 'snippets/book_cover.html' with book=book cover_class='is-h-m-mobile' %}
{% include 'snippets/book_cover.html' with book=book size=large %} {% include 'snippets/rate_action.html' with user=request.user book=book %}
{% include 'snippets/rate_action.html' with user=request.user book=book %}
</div>
<div class="mb-3"> <div class="mb-3">
{% include 'snippets/shelve_button/shelve_button.html' %} {% include 'snippets/shelve_button/shelve_button.html' %}
</div> </div>
@ -81,6 +80,9 @@
{% if book.openlibrary_key %} {% if book.openlibrary_key %}
<p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">{% trans "View on OpenLibrary" %}</a></p> <p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">{% trans "View on OpenLibrary" %}</a></p>
{% endif %} {% endif %}
{% if book.inventaire_id %}
<p><a href="https://inventaire.io/entity/{{ book.inventaire_id }}" target="_blank" rel="noopener">{% trans "View on Inventaire" %}</a></p>
{% endif %}
</section> </section>
</div> </div>

View file

@ -169,10 +169,11 @@
<div class="column is-half"> <div class="column is-half">
<h2 class="title is-4">{% trans "Cover" %}</h2> <h2 class="title is-4">{% trans "Cover" %}</h2>
<div class="columns"> <div class="columns">
<div class="column is-narrow"> <div class="column is-3 is-cover">
{% include 'snippets/book_cover.html' with book=book size="small" %} {% include 'snippets/book_cover.html' with book=book cover_class='is-h-xl-mobile is-w-auto-tablet' %}
</div> </div>
<div class="column is-narrow">
<div class="column">
<div class="block"> <div class="block">
<p> <p>
<label class="label" for="id_cover">{% trans "Upload cover:" %}</label> <label class="label" for="id_cover">{% trans "Upload cover:" %}</label>

View file

@ -13,32 +13,34 @@
<div class="block"> <div class="block">
{% for book in editions %} {% for book in editions %}
<div class="columns"> <div class="columns is-gapless mb-6">
<div class="column is-2"> <div class="column is-cover">
<a href="/book/{{ book.id }}"> <a href="/book/{{ book.id }}">
{% include 'snippets/book_cover.html' with book=book size="medium" %} {% include 'snippets/book_cover.html' with book=book cover_class='is-w-m is-h-m align to-l-mobile' %}
</a> </a>
</div> </div>
<div class="column is-7">
<h2 class="title is-5"> <div class="column my-3-mobile ml-3-tablet mr-auto">
<h2 class="title is-5 mb-1">
<a href="/book/{{ book.id }}" class="has-text-black"> <a href="/book/{{ book.id }}" class="has-text-black">
{{ book.title }} {{ book.title }}
</a> </a>
</h2> </h2>
{% with book=book %} {% with book=book %}
<div class="columns is-multiline"> <div class="columns is-multiline is-gapless ml-3-tablet">
<div class="column is-half"> <div class="column is-half">
{% include 'book/publisher_info.html' %} {% include 'book/publisher_info.html' %}
</div> </div>
<div class="column is-half "> <div class="column ml-3-tablet">
{% include 'book/book_identifiers.html' %} {% include 'book/book_identifiers.html' %}
</div> </div>
</div> </div>
{% endwith %} {% endwith %}
</div> </div>
<div class="column is-3">
<div class="column is-narrow">
{% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %} {% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %}
</div> </div>
</div> </div>

View file

@ -11,14 +11,15 @@
{% with 0|uuid as uuid %} {% with 0|uuid as uuid %}
<div class="box columns"> <div class="box columns">
{% if book %} {% if book %}
<div class="column is-one-third"> <div class="column is-3 is-cover">
<div class="block"> <div class="block">
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book %}</a> <a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book cover_class='is-w-auto-tablet is-h-l-mobile' %}</a>
</div> </div>
<h3 class="title is-6">{% include 'snippets/book_titleby.html' with book=book %}</h3> <h3 class="title is-6">{% include 'snippets/book_titleby.html' with book=book %}</h3>
</div> </div>
{% endif %} {% endif %}
<div class="column is-two-thirds"> <div class="column">
{% if draft.reply_parent %} {% if draft.reply_parent %}
{% include 'snippets/status/status.html' with status=draft.reply_parent no_interact=True %} {% include 'snippets/status/status.html' with status=draft.reply_parent no_interact=True %}
{% endif %} {% endif %}

View file

@ -1,19 +1,38 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %} {% load i18n %}
{% if book %} {% if book %}
<div class="columns"> {% with book=book %}
<div class="column is-narrow"> <div class="columns is-gapless">
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="large" %}</a> <div class="column is-5-tablet is-cover">
{% include 'snippets/stars.html' with rating=book|rating:request.user %} <a
</div> class="align to-b to-l"
<div class="column"> href="{{ book.local_path }}"
<h3 class="title is-5"><a href="/book/{{ book.id }}">{{ book.title }}</a></h3> >{% include 'snippets/book_cover.html' with cover_class='is-w-l-mobile is-w-auto-tablet' %}</a>
{% if book.authors %}
<p class="subtitle is-5">{% trans "by" %} {% include 'snippets/authors.html' with book=book %}</p> {% include 'snippets/stars.html' with rating=book|rating:request.user %}
{% endif %} </div>
{% if book|book_description %}
<blockquote class="content">{{ book|book_description|to_markdown|safe|truncatewords_html:50 }}</blockquote>
{% endif %} <div class="column mt-3-mobile ml-3-tablet">
</div> <h3 class="title is-5">
</div> <a href="/book/{{ book.id }}">{{ book.title }}</a>
</h3>
{% if book.authors %}
<p class="subtitle is-5">
{% trans "by" %}
{% include 'snippets/authors.html' %}
</p>
{% endif %}
{% if book|book_description %}
<blockquote class="content">
{{ book|book_description|to_markdown|safe|truncatewords_html:50 }}
</blockquote>
{% endif %}
</div>
</div>
{% endwith %}
{% endif %} {% endif %}

View file

@ -1,12 +1,24 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %} {% load i18n %}
{% if book %} {% if book %}
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book %}</a> {% with book=book %}
{% include 'snippets/stars.html' with rating=book|rating:request.user %} <a href="{{ book.local_path }}">
{% include 'snippets/book_cover.html' with cover_class='is-w-l-mobile is-h-l-tablet is-w-auto align to-b to-l' %}
</a>
<h3 class="title is-6"><a href="/book/{{ book.id }}">{{ book.title }}</a></h3> {% include 'snippets/stars.html' with rating=book|rating:request.user %}
{% if book.authors %}
<p class="subtitle is-6">{% trans "by" %} {% include 'snippets/authors.html' with book=book %}</p>
{% endif %}
<h3 class="title is-6">
<a href="/book/{{ book.id }}">{{ book.title }}</a>
</h3>
{% if book.authors %}
<p class="subtitle is-6">
{% trans "by" %}
{% include 'snippets/authors.html' %}
</p>
{% endif %}
{% endwith %}
{% endif %} {% endif %}

View file

@ -37,7 +37,7 @@
aria-label="{{ book.title }}" aria-label="{{ book.title }}"
aria-selected="{% if active_book == book.id|stringformat:'d' %}true{% elif shelf_counter == 1 and forloop.first %}true{% else %}false{% endif %}" aria-selected="{% if active_book == book.id|stringformat:'d' %}true{% elif shelf_counter == 1 and forloop.first %}true{% else %}false{% endif %}"
aria-controls="book-{{ book.id }}"> aria-controls="book-{{ book.id }}">
{% include 'snippets/book_cover.html' with book=book size="medium" %} {% include 'snippets/book_cover.html' with book=book cover_class='is-h-m' %}
</a> </a>
</li> </li>
{% endfor %} {% endfor %}

View file

@ -1,8 +1,8 @@
{% load i18n %} {% load i18n %}
<div class="column is-narrow is-clipped has-text-centered"> <div class="column is-cover">
{% include 'snippets/book_cover.html' with book=book %} {% include 'snippets/book_cover.html' with book=book cover_class='is-h-l is-h-m-mobile' %}
<label class="label" for="id_shelve_{{ book.id }}">
<div class="select is-small"> <div class="select is-small mt-1 mb-3">
<select name="{{ book.id }}" aria-label="{% blocktrans with book_title=book.title %}Have you read {{ book_title }}?{% endblocktrans %}"> <select name="{{ book.id }}" aria-label="{% blocktrans with book_title=book.title %}Have you read {{ book_title }}?{% endblocktrans %}">
<option disabled selected value>Add to your books</option> <option disabled selected value>Add to your books</option>
{% for shelf in request.user.shelf_set.all %} {% for shelf in request.user.shelf_set.all %}

View file

@ -23,34 +23,43 @@
<form class="block" name="add-books" method="post" action="{% url 'get-started-books' %}"> <form class="block" name="add-books" method="post" action="{% url 'get-started-books' %}">
{% csrf_token %} {% csrf_token %}
<h3 class="title is-5">{% trans "Suggested Books" %}</h3> <h3 class="title is-5">{% trans "Suggested Books" %}</h3>
<fieldset name="books" class="columns scroll-x is-mobile">
{% if book_results %} <div class="block scroll-x">
<div class="column is-narrow content"> <fieldset name="books" class="columns is-mobile">
<p class="help mb-0">Search results</p> {% if book_results %}
<div class="columns is-mobile"> <div class="column is-narrow">
{% for book in book_results %} <p class="help mb-0">Search results</p>
{% include 'get_started/book_preview.html' %}
{% endfor %} <div class="columns is-mobile">
</div> {% for book in book_results %}
</div> {% include 'get_started/book_preview.html' %}
{% endif %} {% endfor %}
{% if popular_books %} </div>
<div class="column is-narrow content"> </div>
<p class="help mb-0"> {% endif %}
{% blocktrans %}Popular on {{ site_name }}{% endblocktrans %}
</p> {% if popular_books %}
<div class="columns is-mobile"> <div class="column is-narrow">
{% for book in popular_books %} <p class="help mb-0">
{% include 'get_started/book_preview.html' %} {% blocktrans %}Popular on {{ site_name }}{% endblocktrans %}
{% endfor %} </p>
</div>
</div> <div class="columns is-mobile">
{% endif %} {% for book in popular_books %}
{% if not book_results and not popular_books %} {% include 'get_started/book_preview.html' %}
<p><em>{% trans "No books found" %}</em></p> {% endfor %}
{% endif %} </div>
</fieldset> </div>
{% endif %}
{% if not book_results and not popular_books %}
<p><em>{% trans "No books found" %}</em></p>
{% endif %}
</fieldset>
</div>
<button type="submit" class="button is-primary">{% trans "Save &amp; continue" %}</button> <button type="submit" class="button is-primary">{% trans "Save &amp; continue" %}</button>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -45,21 +45,22 @@
</section> </section>
{% if goal.books %} {% if goal.books %}
<section class="content"> <section>
<h2> <h2 class="title is-4">
{% if goal.user == request.user %} {% if goal.user == request.user %}
{% blocktrans %}Your {{ year }} Books{% endblocktrans %} {% blocktrans %}Your {{ year }} Books{% endblocktrans %}
{% else %} {% else %}
{% blocktrans with username=goal.user.display_name %}{{ username }}'s {{ year }} Books{% endblocktrans %} {% blocktrans with username=goal.user.display_name %}{{ username }}'s {{ year }} Books{% endblocktrans %}
{% endif %} {% endif %}
</h2> </h2>
<div class="columns is-multiline">
<div class="columns is-mobile is-multiline">
{% for book in goal.books %} {% for book in goal.books %}
<div class="column is-one-fifth"> <div class="column is-cover">
<div class="is-clipped"> <a href="{{ book.book.local_path }}">
<a href="{{ book.book.local_path }}">{% include 'snippets/book_cover.html' with book=book.book %}</a> {% include 'snippets/book_cover.html' with book=book.book cover_class='is-h-xl is-h-l-mobile' %}
</a>
</div> </div>
</div>
{% endfor %} {% endfor %}
</div> </div>
</section> </section>

View file

@ -125,7 +125,7 @@
<td> <td>
{% if item.book %} {% if item.book %}
<a href="/book/{{ item.book.id }}"> <a href="/book/{{ item.book.id }}">
{% include 'snippets/book_cover.html' with book=item.book size='small' %} {% include 'snippets/book_cover.html' with book=item.book cover_class='is-h-s' %}
</a> </a>
{% endif %} {% endif %}
</td> </td>

View file

@ -2,49 +2,71 @@
{% load i18n %} {% load i18n %}
{% block panel %} {% block panel %}
<section class="content block"> <section class="block">
<h2>{% trans "Pending Books" %}</h2> <div class="columns is-mobile is-multiline is-align-items-baseline">
<p><a href="{% url 'list' list.id %}">{% trans "Go to list" %}</a></p> <div class="column is-narrow">
<h2 class="title is-4">{% trans "Pending Books" %}</h2>
</div>
<p class="column is-narrow"><a href="{% url 'list' list.id %}">{% trans "Go to list" %}</a></p>
</div>
{% if not pending.exists %} {% if not pending.exists %}
<p>{% trans "You're all set!" %}</p> <p>{% trans "You're all set!" %}</p>
{% else %} {% else %}
<table class="table is-striped">
<tr> <dl>
<th></th>
<th>{% trans "Book" %}</th>
<th>{% trans "Suggested by" %}</th>
<th></th>
</tr>
{% for item in pending %} {% for item in pending %}
<tr> {% with book=item.book %}
<td> <div
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=item.book size="small" %}</a> class="
</td> columns is-gapless
<td> is-vcentered is-justify-content-space-between
{% include 'snippets/book_titleby.html' with book=item.book %} mb-6
</td> "
<td> >
<a href="{{ item.user.local_path }}">{{ item.user.display_name }}</a> <dt class="column mr-auto">
</td> <div class="columns is-mobile is-gapless is-vcentered">
<td> <a
<div class="field has-addons"> class="column is-cover"
<form class="control" method="POST" action="{% url 'list-curate' list.id %}"> href="{{ book.local_path }}"
{% csrf_token %} aria-hidden="true"
<input type="hidden" name="item" value="{{ item.id }}"> >
<input type="hidden" name="approved" value="true"> {% include 'snippets/book_cover.html' with cover_class='is-w-xs-mobile is-w-s is-h-xs-mobile is-h-s' %}
<button class="button">{% trans "Approve" %}</button> </a>
</form>
<form class="control" method="POST" action="{% url 'list-curate' list.id %}"> <div class="column ml-3">
{% csrf_token %} {% include 'snippets/book_titleby.html' %}
<input type="hidden" name="item" value="{{ item.id }}"> </div>
<input type="hidden" name="approved" value="false"> </div>
<button class="button is-danger is-light">{% trans "Discard" %}</button> </dt>
</div>
</form> <dd class="column is-4-tablet mx-3-tablet my-3-mobile">
</td> {% trans "Suggested by" %}
</tr>
<a href="{{ item.user.local_path }}">
{{ item.user.display_name }}
</a>
</dd>
<dd class="column is-narrow field has-addons">
<form class="control" method="POST" action="{% url 'list-curate' list.id %}">
{% csrf_token %}
<input type="hidden" name="item" value="{{ item.id }}">
<input type="hidden" name="approved" value="true">
<button class="button">{% trans "Approve" %}</button>
</form>
<form class="control" method="POST" action="{% url 'list-curate' list.id %}">
{% csrf_token %}
<input type="hidden" name="item" value="{{ item.id }}">
<input type="hidden" name="approved" value="false">
<button class="button is-danger is-light">{% trans "Discard" %}</button>
</form>
</dd>
</div>
{% endwith %}
{% endfor %} {% endfor %}
</table> </dl>
{% endif %} {% endif %}
</section> </section>
{% endblock %} {% endblock %}

View file

@ -28,18 +28,29 @@
{% else %} {% else %}
<ol start="{{ items.start_index }}"> <ol start="{{ items.start_index }}">
{% for item in items %} {% for item in items %}
<li class="block pb-3"> <li class="block mb-5">
<div class="card"> <div class="card">
<div class="card-content columns p-0 pr-2 mb-0 is-mobile"> {% with book=item.book %}
<div class="column is-narrow pt-0 pb-0"> <div
<a href="{{ item.book.local_path }}">{% include 'snippets/book_cover.html' with book=item.book size="medium" %}</a> class="
card-content p-0 mb-0
columns is-mobile is-gapless
"
>
<div class="column is-2-mobile is-cover align to-t">
<a href="{{ item.book.local_path }}" aria-hidden="true">
{% include 'snippets/book_cover.html' with cover_class='is-w-auto is-h-m-tablet' %}
</a>
</div>
<div class="column ml-3">
<span>{% include 'snippets/book_titleby.html' %}</span>
{% include 'snippets/stars.html' with rating=item.book|rating:request.user %}
{% include 'snippets/shelve_button/shelve_button.html' %}
</div>
</div> </div>
<div class="column is-flex-direction-column is-align-items-self-start"> {% endwith %}
<span>{% include 'snippets/book_titleby.html' with book=item.book %}</span>
{% include 'snippets/stars.html' with rating=item.book|rating:request.user %}
{% include 'snippets/shelve_button/shelve_button.html' with book=item.book %}
</div>
</div>
<div class="card-footer has-background-white-bis is-align-items-baseline"> <div class="card-footer has-background-white-bis is-align-items-baseline">
<div class="card-footer-item"> <div class="card-footer-item">
<div> <div>
@ -76,7 +87,7 @@
{% include "snippets/pagination.html" with page=items %} {% include "snippets/pagination.html" with page=items %}
</section> </section>
<section class="column is-one-quarter content"> <section class="column is-one-quarter">
<h2>{% trans "Sort List" %}</h2> <h2>{% trans "Sort List" %}</h2>
<form name="sort" action="{% url 'list' list.id %}" method="GET" class="block"> <form name="sort" action="{% url 'list' list.id %}" method="GET" class="block">
<label class="label" for="id_sort_by">{% trans "Sort By" %}</label> <label class="label" for="id_sort_by">{% trans "Sort By" %}</label>
@ -118,24 +129,36 @@
<p>{% trans "No books found" %}</p> <p>{% trans "No books found" %}</p>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% for book in suggested_books %}
{% if book %} {% if suggested_books|length > 0 %}
<div class="block columns is-mobile"> {% for book in suggested_books %}
<div class="column is-narrow"> <div class="columns is-mobile is-gapless">
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a> <a
</div> class="column is-2-mobile is-3-tablet is-cover align to-c"
<div class="column"> href="{{ book.local_path }}"
<p>{% include 'snippets/book_titleby.html' with book=book %}</p> aria-hidden="true"
<form name="add-book" method="post" action="{% url 'list-add-book' %}{% if query %}?q={{ query }}{% endif %}"> >
{% csrf_token %} {% include 'snippets/book_cover.html' with book=book cover_class='is-w-auto is-h-s-mobile align to-t' %}
<input type="hidden" name="book" value="{{ book.id }}"> </a>
<input type="hidden" name="list" value="{{ list.id }}">
<button type="submit" class="button is-small is-link">{% if list.curation == 'open' or request.user == list.user %}{% trans "Add" %}{% else %}{% trans "Suggest" %}{% endif %}</button> <div class="column ml-3">
</form> <p>{% include 'snippets/book_titleby.html' with book=book %}</p>
</div>
</div> <form
class="mt-1"
name="add-book"
method="post"
action="{% url 'list-add-book' %}{% if query %}?q={{ query }}{% endif %}"
>
{% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<input type="hidden" name="list" value="{{ list.id }}">
<button type="submit" class="button is-small is-link">{% if list.curation == 'open' or request.user == list.user %}{% trans "Add" %}{% else %}{% trans "Suggest" %}{% endif %}</button>
</form>
</div>
</div>
{% endfor %}
{% endif %} {% endif %}
{% endfor %}
{% endif %} {% endif %}
</section> </section>
</div> </div>

View file

@ -8,11 +8,19 @@
<a href="{{ list.local_path }}">{{ list.name }}</a> <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span> <a href="{{ list.local_path }}">{{ list.name }}</a> <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span>
</h4> </h4>
</header> </header>
<div class="card-image is-flex is-clipped">
{% for book in list.listitem_set.all|slice:5 %} {% with list_books=list.listitem_set.all|slice:5 %}
<a href="{{ book.book.local_path }}">{% include 'snippets/book_cover.html' with book=book.book size="small" %}</a> {% if list_books %}
{% endfor %} <div class="card-image columns is-mobile is-gapless is-clipped">
</div> {% for book in list_books %}
<a class="column is-cover" href="{{ book.book.local_path }}">
{% include 'snippets/book_cover.html' with book=book.book cover_class='is-h-s' aria='show' %}
</a>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="card-content is-flex-grow-0"> <div class="card-content is-flex-grow-0">
<div {% if list.description %}title="{{ list.description }}"{% endif %}> <div {% if list.description %}title="{{ list.description }}"{% endif %}>
{% if list.description %} {% if list.description %}

View file

@ -28,7 +28,7 @@
</div> </div>
{% if lists %} {% if lists %}
<section class="block content"> <section class="block">
{% include 'lists/list_items.html' with lists=lists %} {% include 'lists/list_items.html' with lists=lists %}
</section> </section>

View file

@ -8,7 +8,7 @@
{% endblock %} {% endblock %}
{% block panel %} {% block panel %}
<form name="edit-profile" action="/change-password/" method="post" enctype="multipart/form-data"> <form name="edit-profile" action="{% url 'prefs-password' %}" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="block"> <div class="block">
<label class="label" for="id_password">{% trans "New password:" %}</label> <label class="label" for="id_password">{% trans "New password:" %}</label>

View file

@ -11,10 +11,15 @@
<div class="block columns"> <div class="block columns">
<div class="column"> <div class="column">
<h2 class="title">{% trans "Matching Books" %}</h2> <h2 class="title is-4">{% trans "Matching Books" %}</h2>
<section class="block"> <section class="block">
{% if not local_results.results %} {% if not local_results.results %}
<p>{% blocktrans %}No books found for "{{ query }}"{% endblocktrans %}</p> <p><em>{% blocktrans %}No books found for "{{ query }}"{% endblocktrans %}</em></p>
{% if not user.is_authenticated %}
<p>
<a href="{% url 'login' %}">{% trans "Log in to import or add books." %}</a>
</p>
{% endif %}
{% else %} {% else %}
<ul> <ul>
{% for result in local_results.results %} {% for result in local_results.results %}
@ -29,39 +34,57 @@
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
{% if book_results|slice:":1" and local_results.results %} {% if book_results|slice:":1" and local_results.results %}
<div class="block"> <div class="block">
<p> <h3 class="title is-6">
{% trans "Didn't find what you were looking for?" %} {% trans "Didn't find what you were looking for?" %}
</p> </h3>
{% trans "Show results from other catalogues" as button_text %} {% trans "Show results from other catalogues" as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text small=True controls_text="more-results" %} {% include 'snippets/toggle/open_button.html' with text=button_text small=True controls_text="more-results" %}
{% if local_results.results %}
{% trans "Hide results from other catalogues" as button_text %}
{% include 'snippets/toggle/close_button.html' with text=button_text small=True controls_text="more-results" %}
{% endif %}
</div> </div>
{% endif %} {% endif %}
<div class="{% if local_results.results %}is-hidden{% endif %}" id="more-results"> <div class="{% if local_results.results %}is-hidden{% endif %}" id="more-results">
{% for result_set in book_results|slice:"1:" %} {% for result_set in book_results|slice:"1:" %}
{% if result_set.results %} {% if result_set.results %}
<section class="block"> <section class="box has-background-white-bis">
{% if not result_set.connector.local %} {% if not result_set.connector.local %}
<h3 class="title is-5"> <header class="columns is-mobile">
Results from <a href="{{ result_set.connector.base_url }}" target="_blank">{% if result_set.connector.name %}{{ result_set.connector.name }}{% else %}{{ result_set.connector.identifier }}{% endif %}</a> <div class="column">
</h3> <h3 class="title is-5">
Results from
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
</h3>
</div>
<div class="column is-narrow">
{% trans "Show" as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text small=True controls_text="more-results-panel" controls_uid=result_set.connector.identifier class="is-small" icon="arrow-down" pressed=forloop.first %}
</div>
</header>
{% endif %} {% endif %}
<ul> <div class="box has-background-white is-shadowless{% if not forloop.first %} is-hidden{% endif %}" id="more-results-panel-{{ result_set.connector.identifier }}">
{% for result in result_set.results %} <div class="is-flex is-flex-direction-row-reverse">
<li class="pb-4"> <div>
{% include 'snippets/search_result_text.html' with result=result remote_result=True %} {% trans "Close" as button_text %}
</li> {% include 'snippets/toggle/toggle_button.html' with label=button_text class="delete" nonbutton=True controls_text="more-results-panel" controls_uid=result_set.connector.identifier pressed=forloop.first %}
{% endfor %} </div>
</ul>
<ul class="is-flex-grow-1">
{% for result in result_set.results %}
<li class="mb-5">
{% include 'snippets/search_result_text.html' with result=result remote_result=True %}
</li>
{% endfor %}
</ul>
</div>
</div>
</section> </section>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if local_results.results %}
{% trans "Hide results from other catalogues" as button_text %}
{% include 'snippets/toggle/close_button.html' with text=button_text small=True controls_text="more-results" %}
{% endif %}
</div> </div>
<div class="block"> <div class="block">
@ -70,10 +93,11 @@
{% endif %} {% endif %}
</div> </div>
<div class="column"> <div class="column">
<section class="block"> {% if request.user.is_authenticated %}
<h2 class="title">{% trans "Matching Users" %}</h2> <section class="box">
<h2 class="title is-4">{% trans "Matching Users" %}</h2>
{% if not user_results %} {% if not user_results %}
<p>{% blocktrans %}No users found for "{{ query }}"{% endblocktrans %}</p> <p><em>{% blocktrans %}No users found for "{{ query }}"{% endblocktrans %}</em></p>
{% endif %} {% endif %}
<ul> <ul>
{% for result in user_results %} {% for result in user_results %}
@ -87,10 +111,11 @@
{% endfor %} {% endfor %}
</ul> </ul>
</section> </section>
<section class="block"> {% endif %}
<h2 class="title">{% trans "Lists" %}</h2> <section class="box">
<h2 class="title is-4">{% trans "Lists" %}</h2>
{% if not list_results %} {% if not list_results %}
<p>{% blocktrans %}No lists found for "{{ query }}"{% endblocktrans %}</p> <p><em>{% blocktrans %}No lists found for "{{ query }}"{% endblocktrans %}</em></p>
{% endif %} {% endif %}
{% for result in list_results %} {% for result in list_results %}
<div class="block"> <div class="block">

View file

@ -3,27 +3,44 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %} {% load i18n %}
<div class="cover-container is-{{ size }}"> <figure
{% if book.cover %} class="
<img cover-container
class="book-cover" {{ cover_class }}
src="/images/{{ book.cover }}"
alt="{{ book.alt_text }}"
title="{{ book.alt_text }}"
itemprop="thumbnailUrl"
>
{% else %}
<div class="no-cover book-cover">
<img
class="book-cover"
src="/static/images/no_cover.jpg"
alt="{% trans "No cover" %}"
>
<div> {% if not book.cover %}
<p>{{ book.alt_text }}</p> no-cover
</div> {% endif %}
</div> "
{% if aria != "show" %}
aria-hidden="true"
{% endif %} {% endif %}
</div>
{% if book.alt_text %}
title="{{ book.alt_text }}"
{% endif %}
>
<img
class="book-cover"
{% if book.cover %}
src="{% if img_path is None %}/images/{% else %}{{ img_path }}{% endif %}{{ book.cover }}"
itemprop="thumbnailUrl"
{% if book.alt_text %}
alt="{{ book.alt_text }}"
{% endif %}
{% else %}
src="/static/images/no_cover.jpg"
alt="{% trans "No cover" %}"
{% endif %}
>
{% if not book.cover and book.alt_text %}
<figcaption class="cover_caption">
<p>{{ book.alt_text }}</p>
</figcaption>
{% endif %}
</figure>
{% endspaceless %} {% endspaceless %}

View file

@ -1,10 +1,13 @@
<div class="columns is-multiline"> <div class="columns is-mobile is-multiline">
{% for book in books %} {% for book in books %}
<div class="column is-narrow"> <div class="column is-narrow">
<div class="box"> <div class="box is-flex is-flex-direction-column is-align-items-center">
<a href="/book/{{ book.id }}"> <div class="mb-3">
{% include 'snippets/book_cover.html' with book=book %} <a href="/book/{{ book.id }}">
</a> {% include 'snippets/book_cover.html' with book=book cover_class='is-w-l-mobile is-h-l-mobile is-w-l-tablet is-h-xl-tablet' %}
</a>
</div>
{% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %} {% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %}
</div> </div>
</div> </div>

View file

@ -1,34 +1,41 @@
{% load i18n %} {% load i18n %}
<div class="columns is-mobile"> <div class="columns is-mobile is-gapless">
<div class="cover-container is-small column is-2"> <div class="column is-cover">
{% if result.cover %} {% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' img_path=false %}
<img src="{{ result.cover }}" class="book-cover" aria-hidden="true">
{% else %}
<div class="no-cover book-cover">
<img class="book-cover" src="/static/images/no_cover.jpg" aria-hidden="true">
<div>
<p>{% trans "No cover" %}</p>
</div>
</div>
{% endif %}
</div> </div>
<div class="column"> <div class="column is-10 ml-3">
<p> <p>
<strong> <strong>
<a href="{{ result.key }}"{% if remote_result %} rel=”noopener” target="_blank"{% endif %}>{{ result.title }}</a> <a
href="{{ result.view_link|default:result.key }}"
{% if remote_result %}
rel=”noopener”
target="_blank"
{% endif %}
>{{ result.title }}</a>
</strong> </strong>
</p>
<p>
{% if result.author %} {% if result.author %}
{% blocktrans with author=result.author %}by {{ author }}{% endblocktrans %}{% endif %}{% if result.year %} ({{ result.year }}) {{ result.author }}
{% endif %}
{% if result.year %}
({{ result.year }})
{% endif %} {% endif %}
</p> </p>
{% if remote_result %} {% if remote_result %}
<form action="/resolve-book" method="POST"> <form class="mt-1" action="/resolve-book" method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="remote_id" value="{{ result.key }}">
<button type="submit" class="button is-small is-link">{% trans "Import book" %}</button> <input type="hidden" name="remote_id" value="{{ result.key }}">
</form>
<button type="submit" class="button is-small is-link">
{% trans "Import book" %}
</button>
</form>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -10,27 +10,30 @@
{% endif %} {% endif %}
> >
<div class="columns"> <div class="columns is-gapless">
{% if not hide_book %} {% if not hide_book %}
{% with book=status.book|default:status.mention_books.first %} {% with book=status.book|default:status.mention_books.first %}
{% if book %} {% if book %}
<div class="column is-narrow"> <div class="column is-cover">
<div class="columns is-mobile"> <div class="columns is-mobile is-gapless">
<div class="column is-narrow"> <div class="column is-cover">
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book %}</a> <a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book cover_class='is-w-s-mobile is-h-l-tablet' %}</a>
{% include 'snippets/stars.html' with rating=book|rating:request.user %}
{% include 'snippets/shelve_button/shelve_button.html' with book=book %} {% include 'snippets/stars.html' with rating=book|rating:request.user %}
</div>
<div class="column is-hidden-tablet"> {% include 'snippets/shelve_button/shelve_button.html' with book=book %}
<p>{{ book|book_description|to_markdown|default:""|safe|truncatewords_html:15 }}</p> </div>
</div>
</div> <div class="column ml-3-mobile is-hidden-tablet">
</div> <p>{{ book|book_description|to_markdown|default:""|safe|truncatewords_html:15 }}</p>
{% endif %} </div>
{% endwith %} </div>
</div>
{% endif %}
{% endwith %}
{% endif %} {% endif %}
<article class="column"> <article class="column ml-3-tablet my-3-mobile">
{% if status_type == 'Review' %} {% if status_type == 'Review' %}
<header class="mb-2"> <header class="mb-2">
<h3 <h3

View file

@ -4,20 +4,25 @@
{% load i18n %} {% load i18n %}
{% if not hide_book %} {% if not hide_book %}
{% with book=status.book|default:status.mention_books.first %} {% with book=status.book|default:status.mention_books.first %}
<div class="columns is-mobile"> <div class="columns is-mobile is-gapless">
<div class="column is-narrow"> <a class="column is-cover is-narrow" href="{{ book.local_path }}">
<div> {% include 'snippets/book_cover.html' with book=book cover_class='is-h-xs is-h-s-tablet' %}
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a> </a>
<div class="column ml-3">
<h3 class="title is-6 mb-1">
{% include 'snippets/book_titleby.html' with book=book %}
</h3>
<p>
{{ book|book_description|to_markdown|default:""|safe|truncatewords_html:20 }}
</p>
{% include 'snippets/shelve_button/shelve_button.html' with book=book %}
</div>
</div> </div>
</div> {% endwith %}
<div class="column">
<h3 class="title is-6 mb-1">{% include 'snippets/book_titleby.html' with book=book %}</h3>
<p>{{ book|book_description|to_markdown|default:""|safe|truncatewords_html:20 }}</p>
{% include 'snippets/shelve_button/shelve_button.html' with book=book %}
</div>
</div>
{% endwith %}
{% endif %} {% endif %}
{% endspaceless %} {% endspaceless %}

View file

@ -88,7 +88,7 @@
{% spaceless %} {% spaceless %}
<tr class="book-preview"> <tr class="book-preview">
<td class="book-preview-top-row"> <td class="book-preview-top-row">
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a> <a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book cover_class='is-w-s-tablet is-h-s' %}</a>
</td> </td>
<td data-title="{% trans "Title" %}"> <td data-title="{% trans "Title" %}">
<a href="{{ book.local_path }}">{{ book.title }}</a> <a href="{{ book.local_path }}">{{ book.title }}</a>

View file

@ -36,7 +36,7 @@
{% for book in shelf.books %} {% for book in shelf.books %}
<div class="control"> <div class="control">
<a href="{{ book.local_path }}"> <a href="{{ book.local_path }}">
{% include 'snippets/book_cover.html' with book=book size="medium" %} {% include 'snippets/book_cover.html' with book=book cover_class='is-h-m is-h-s-mobile' %}
</a> </a>
</div> </div>
{% endfor %} {% endfor %}

View file

@ -17,8 +17,6 @@ class ConnectorManager(TestCase):
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work, isbn_10="0000000000" title="Example Edition", parent_work=self.work, isbn_10="0000000000"
) )
self.work.default_edition = self.edition
self.work.save()
self.connector = models.Connector.objects.create( self.connector = models.Connector.objects.create(
identifier="test_connector", identifier="test_connector",

View file

@ -0,0 +1,158 @@
""" testing book data connectors """
import json
import pathlib
from django.test import TestCase
import responses
from bookwyrm import models
from bookwyrm.connectors.inventaire import Connector
class Inventaire(TestCase):
"""test loading data from inventaire.io"""
def setUp(self):
"""creates the connector we'll use"""
models.Connector.objects.create(
identifier="inventaire.io",
name="Inventaire",
connector_file="inventaire",
base_url="https://inventaire.io",
books_url="https://inventaire.io",
covers_url="https://covers.inventaire.io",
search_url="https://inventaire.io/search?q=",
isbn_search_url="https://inventaire.io/isbn",
)
self.connector = Connector("inventaire.io")
@responses.activate
def test_get_book_data(self):
"""flattens the default structure to make it easier to parse"""
responses.add(
responses.GET,
"https://test.url/ok",
json={
"entities": {
"isbn:9780375757853": {
"claims": {
"wdt:P31": ["wd:Q3331189"],
},
"uri": "isbn:9780375757853",
}
},
"redirects": {},
},
)
result = self.connector.get_book_data("https://test.url/ok")
self.assertEqual(result["wdt:P31"], ["wd:Q3331189"])
self.assertEqual(result["uri"], "isbn:9780375757853")
def test_format_search_result(self):
"""json to search result objs"""
search_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_search.json"
)
search_results = json.loads(search_file.read_bytes())
results = self.connector.parse_search_data(search_results)
formatted = self.connector.format_search_result(results[0])
self.assertEqual(formatted.title, "The Stories of Vladimir Nabokov")
self.assertEqual(
formatted.key, "https://inventaire.io?action=by-uris&uris=wd:Q7766679"
)
self.assertEqual(
formatted.cover,
"https://covers.inventaire.io/img/entities/ddb32e115a28dcc0465023869ba19f6868ec4042",
)
def test_get_cover_url(self):
"""figure out where the cover image is"""
cover_blob = {"url": "/img/entities/d46a8"}
result = self.connector.get_cover_url(cover_blob)
self.assertEqual(result, "https://covers.inventaire.io/img/entities/d46a8")
cover_blob = {
"url": "https://commons.wikimedia.org/wiki/Special:FilePath/The%20Moonstone%201st%20ed.jpg?width=1000",
"file": "The Moonstone 1st ed.jpg",
"credits": {
"text": "Wikimedia Commons",
"url": "https://commons.wikimedia.org/wiki/File:The Moonstone 1st ed.jpg",
},
}
result = self.connector.get_cover_url(cover_blob)
self.assertEqual(
result,
"https://commons.wikimedia.org/wiki/Special:FilePath/The%20Moonstone%201st%20ed.jpg?width=1000",
)
@responses.activate
def test_resolve_keys(self):
"""makes an http request"""
responses.add(
responses.GET,
"https://inventaire.io?action=by-uris&uris=wd:Q465821",
json={
"entities": {
"wd:Q465821": {
"type": "genre",
"labels": {
"nl": "briefroman",
"en": "epistolary novel",
"de-ch": "Briefroman",
"en-ca": "Epistolary novel",
"nb": "brev- og dagbokroman",
},
"descriptions": {
"en": "novel written as a series of documents",
"es": "novela escrita como una serie de documentos",
"eo": "romano en la formo de serio de leteroj",
},
},
"redirects": {},
}
},
)
responses.add(
responses.GET,
"https://inventaire.io?action=by-uris&uris=wd:Q208505",
json={
"entities": {
"wd:Q208505": {
"type": "genre",
"labels": {
"en": "crime novel",
},
},
}
},
)
keys = [
"wd:Q465821",
"wd:Q208505",
]
result = self.connector.resolve_keys(keys)
self.assertEqual(result, ["epistolary novel", "crime novel"])
def test_isbn_search(self):
"""another search type"""
search_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_isbn_search.json"
)
search_results = json.loads(search_file.read_bytes())
results = self.connector.parse_isbn_search_data(search_results)
formatted = self.connector.format_isbn_search_result(results[0])
self.assertEqual(formatted.title, "L'homme aux cercles bleus")
self.assertEqual(
formatted.key,
"https://inventaire.io?action=by-uris&uris=isbn:9782290349229",
)
self.assertEqual(
formatted.cover,
"https://covers.inventaire.io/img/entities/12345",
)

View file

@ -84,11 +84,11 @@ class SelfConnector(TestCase):
title="Edition 1 Title", parent_work=work title="Edition 1 Title", parent_work=work
) )
edition_2 = models.Edition.objects.create( edition_2 = models.Edition.objects.create(
title="Edition 2 Title", parent_work=work title="Edition 2 Title",
parent_work=work,
edition_rank=20, # that's default babey
) )
edition_3 = models.Edition.objects.create(title="Fish", parent_work=work) edition_3 = models.Edition.objects.create(title="Fish", parent_work=work)
work.default_edition = edition_2
work.save()
# pick the best edition # pick the best edition
results = self.connector.search("Edition 1 Title") results = self.connector.search("Edition 1 Title")

View file

@ -0,0 +1,45 @@
{
"entities": {
"isbn:9780375757853": {
"_id": "7beee121a8d9ac345cdf4e9128577723",
"_rev": "2-ac318b04b953ca3894deb77fee28211c",
"type": "edition",
"labels": {},
"claims": {
"wdt:P31": [
"wd:Q3331189"
],
"wdt:P212": [
"978-0-375-75785-3"
],
"wdt:P957": [
"0-375-75785-6"
],
"wdt:P407": [
"wd:Q1860"
],
"wdt:P1476": [
"The Moonstone"
],
"wdt:P577": [
"2001"
],
"wdt:P629": [
"wd:Q2362563"
],
"invp:P2": [
"d46a8eac7555afa479b8bbb5149f35858e8e19c4"
]
},
"created": 1495452670475,
"updated": 1541032981834,
"version": 3,
"uri": "isbn:9780375757853",
"originalLang": "en",
"image": {
"url": "/img/entities/d46a8eac7555afa479b8bbb5149f35858e8e19c4"
}
}
},
"redirects": {}
}

View file

@ -0,0 +1,48 @@
{
"entities": {
"isbn:9782290349229": {
"_id": "d59e3e64f92c6340fbb10c5dcf7c0abf",
"_rev": "3-079ed51158a001dc74caafb21cff1c22",
"type": "edition",
"labels": {},
"claims": {
"wdt:P31": [
"wd:Q3331189"
],
"wdt:P212": [
"978-2-290-34922-9"
],
"wdt:P957": [
"2-290-34922-4"
],
"wdt:P407": [
"wd:Q150"
],
"wdt:P1476": [
"L'homme aux cercles bleus"
],
"wdt:P629": [
"wd:Q3203603"
],
"wdt:P123": [
"wd:Q3156592"
],
"invp:P2": [
"57883743aa7c6ad25885a63e6e94349ec4f71562"
],
"wdt:P577": [
"2005-05-01"
]
},
"created": 1485023383338,
"updated": 1609171008418,
"version": 5,
"uri": "isbn:9782290349229",
"originalLang": "fr",
"image": {
"url": "/img/entities/12345"
}
}
},
"redirects": {}
}

View file

@ -0,0 +1,111 @@
{
"results": [
{
"id": "Q7766679",
"type": "works",
"uri": "wd:Q7766679",
"label": "The Stories of Vladimir Nabokov",
"description": "book by Vladimir Nabokov",
"image": [
"ddb32e115a28dcc0465023869ba19f6868ec4042"
],
"_score": 25.180836,
"_popularity": 4
},
{
"id": "Q47407212",
"type": "works",
"uri": "wd:Q47407212",
"label": "Conversations with Vladimir Nabokov",
"description": "book edited by Robert Golla",
"image": [],
"_score": 24.41498,
"_popularity": 2
},
{
"id": "Q6956987",
"type": "works",
"uri": "wd:Q6956987",
"label": "Nabokov's Congeries",
"description": "book by Vladimir Nabokov",
"image": [],
"_score": 22.343866,
"_popularity": 2
},
{
"id": "Q6956986",
"type": "works",
"uri": "wd:Q6956986",
"label": "Nabokov's Butterflies",
"description": "book by Brian Boyd",
"image": [],
"_score": 22.343866,
"_popularity": 2
},
{
"id": "Q47472170",
"type": "works",
"uri": "wd:Q47472170",
"label": "A Reader's Guide to Nabokov's \"Lolita\"",
"description": "book by Julian W. Connolly",
"image": [],
"_score": 19.482553,
"_popularity": 2
},
{
"id": "Q7936323",
"type": "works",
"uri": "wd:Q7936323",
"label": "Visiting Mrs Nabokov: And Other Excursions",
"description": "book by Martin Amis",
"image": [],
"_score": 18.684965,
"_popularity": 2
},
{
"id": "1732d81bf7376e04da27568a778561a4",
"type": "works",
"uri": "inv:1732d81bf7376e04da27568a778561a4",
"label": "Nabokov's Dark Cinema",
"image": [
"7512805a53da569b11bf29cc3fb272c969619749"
],
"_score": 16.56681,
"_popularity": 1
},
{
"id": "00f118336b02219e1bddc8fa93c56050",
"type": "works",
"uri": "inv:00f118336b02219e1bddc8fa93c56050",
"label": "The Cambridge Companion to Nabokov",
"image": [
"0683a059fb95430cfa73334f9eff2ef377f3ae3d"
],
"_score": 15.502292,
"_popularity": 1
},
{
"id": "6e59f968a1cd00dbedeb1964dec47507",
"type": "works",
"uri": "inv:6e59f968a1cd00dbedeb1964dec47507",
"label": "Vladimir Nabokov : selected letters, 1940-1977",
"image": [
"e3ce8c0ee89d576adf2651a6e5ce55fc6d9f8cb3"
],
"_score": 15.019735,
"_popularity": 1
},
{
"id": "Q127149",
"type": "works",
"uri": "wd:Q127149",
"label": "Lolita",
"description": "novel by Vladimir Nabokov",
"image": [
"51cbfdbf7257b1a6bb3ea3fbb167dbce1fb44a0e"
],
"_score": 13.458428,
"_popularity": 32
}
]
}

View file

@ -0,0 +1,155 @@
{
"entities": {
"wd:Q2362563": {
"type": "work",
"labels": {
"zh-hans": "月亮宝石",
"zh-hant": "月亮寶石",
"zh-hk": "月光石",
"zh-tw": "月光石",
"cy": "The Moonstone",
"ml": "ദ മൂൺസ്റ്റോൺ",
"ja": "月長石",
"te": "ది మూన్ స్టోన్",
"ru": "Лунный камень",
"fr": "La Pierre de lune",
"en": "The Moonstone",
"es": "La piedra lunar",
"it": "La Pietra di Luna",
"zh": "月亮宝石",
"pl": "Kamień Księżycowy",
"sr": "2 Јн",
"ta": "moon stone",
"ar": "حجر القمر",
"fa": "ماه‌الماس",
"uk": "Місячний камінь",
"nl": "The Moonstone",
"de": "Der Monddiamant",
"sl": "Diamant",
"sv": "Månstenen",
"he": "אבן הירח",
"eu": "Ilargi-harriak",
"bg": "Лунният камък",
"ka": "მთვარის ქვა",
"eo": "La Lunŝtono",
"hy": "Լուսնաքար",
"ro": "Piatra Lunii",
"ca": "The Moonstone",
"is": "The Moonstone"
},
"descriptions": {
"it": "romanzo scritto da Wilkie Collins",
"en": "novel by Wilkie Collins",
"de": "Buch von Wilkie Collins",
"nl": "boek van Wilkie Collins",
"ru": "роман Уилки Коллинза",
"he": "רומן מאת וילקי קולינס",
"ar": "رواية من تأليف ويلكي كولينز",
"fr": "livre de Wilkie Collins",
"es": "libro de Wilkie Collins",
"bg": "роман на Уилки Колинс",
"ka": "უილკი კოლინსის რომანი",
"eo": "angalingva romano far Wilkie Collins",
"ro": "roman de Wilkie Collins"
},
"aliases": {
"zh": [
"月光石"
],
"ml": [
"The Moonstone"
],
"fr": [
"The Moonstone"
],
"it": [
"Il diamante indiano",
"La pietra della luna",
"La maledizione del diamante indiano"
],
"ro": [
"The Moonstone"
]
},
"claims": {
"wdt:P18": [
"The Moonstone 1st ed.jpg"
],
"wdt:P31": [
"wd:Q7725634"
],
"wdt:P50": [
"wd:Q210740"
],
"wdt:P123": [
"wd:Q4457856"
],
"wdt:P136": [
"wd:Q465821",
"wd:Q208505",
"wd:Q10992055"
],
"wdt:P156": [
"wd:Q7228798"
],
"wdt:P268": [
"12496407z"
],
"wdt:P407": [
"wd:Q7979"
],
"wdt:P577": [
"1868"
],
"wdt:P1433": [
"wd:Q21"
],
"wdt:P1476": [
"The Moonstone"
],
"wdt:P1680": [
"A Romance"
],
"wdt:P2034": [
"155"
]
},
"sitelinks": {
"arwiki": "حجر القمر (رواية)",
"bgwiki": "Лунният камък (роман)",
"cywiki": "The Moonstone",
"dewiki": "Der Monddiamant",
"enwiki": "The Moonstone",
"enwikisource": "The Moonstone",
"eswiki": "La piedra lunar",
"euwiki": "Ilargi-harria",
"fawiki": "ماه‌الماس",
"frwiki": "La Pierre de lune (roman de Wilkie Collins)",
"hewiki": "אבן הירח",
"hywiki": "Լուսնաքար",
"iswiki": "The Moonstone",
"itwiki": "La pietra di Luna",
"jawiki": "月長石 (小説)",
"mlwiki": "ദ മൂൺസ്റ്റോൺ",
"plwiki": "Kamień Księżycowy (powieść)",
"ruwiki": "Лунный камень (роман)",
"slwiki": "Diamant (roman)",
"srwikisource": "Нови завјет (Караџић) / 2. Јованова",
"svwiki": "Månstenen",
"tewiki": "ది మూన్‌స్టోన్",
"ukwiki": "Місячний камінь (роман)",
"zhwiki": "月亮宝石"
},
"uri": "wd:Q2362563",
"image": {
"url": "https://commons.wikimedia.org/wiki/Special:FilePath/The%20Moonstone%201st%20ed.jpg?width=1000",
"file": "The Moonstone 1st ed.jpg",
"credits": {
"text": "Wikimedia Commons",
"url": "https://commons.wikimedia.org/wiki/File:The Moonstone 1st ed.jpg"
}
}
}
},
"redirects": {}
}

View file

@ -26,20 +26,23 @@ class BaseModel(TestCase):
outbox="https://example.com/users/rat/outbox", outbox="https://example.com/users/rat/outbox",
) )
class BookWyrmTestModel(base_model.BookWyrmModel):
"""just making it not abstract"""
self.test_model = BookWyrmTestModel()
def test_remote_id(self): def test_remote_id(self):
"""these should be generated""" """these should be generated"""
instance = base_model.BookWyrmModel() self.test_model.id = 1
instance.id = 1 expected = self.test_model.get_remote_id()
expected = instance.get_remote_id() self.assertEqual(expected, "https://%s/bookwyrmtestmodel/1" % DOMAIN)
self.assertEqual(expected, "https://%s/bookwyrmmodel/1" % DOMAIN)
def test_remote_id_with_user(self): def test_remote_id_with_user(self):
"""format of remote id when there's a user object""" """format of remote id when there's a user object"""
instance = base_model.BookWyrmModel() self.test_model.user = self.local_user
instance.user = self.local_user self.test_model.id = 1
instance.id = 1 expected = self.test_model.get_remote_id()
expected = instance.get_remote_id() self.assertEqual(expected, "https://%s/user/mouse/bookwyrmtestmodel/1" % DOMAIN)
self.assertEqual(expected, "https://%s/user/mouse/bookwyrmmodel/1" % DOMAIN)
def test_set_remote_id(self): def test_set_remote_id(self):
"""this function sets remote ids after creation""" """this function sets remote ids after creation"""

View file

@ -84,9 +84,3 @@ class Book(TestCase):
self.first_edition.description = "hi" self.first_edition.description = "hi"
self.first_edition.save() self.first_edition.save()
self.assertEqual(self.first_edition.edition_rank, 1) self.assertEqual(self.first_edition.edition_rank, 1)
# default edition
self.work.default_edition = self.first_edition
self.work.save()
self.first_edition.refresh_from_db()
self.assertEqual(self.first_edition.edition_rank, 20)

View file

@ -2,7 +2,7 @@
from django.test import TestCase from django.test import TestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from bookwyrm import models, settings from bookwyrm import models
class ReadThrough(TestCase): class ReadThrough(TestCase):
@ -19,8 +19,6 @@ class ReadThrough(TestCase):
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work title="Example Edition", parent_work=self.work
) )
self.work.default_edition = self.edition
self.work.save()
self.readthrough = models.ReadThrough.objects.create( self.readthrough = models.ReadThrough.objects.create(
user=self.user, book=self.edition user=self.user, book=self.edition

View file

@ -127,6 +127,43 @@ class InboxCreate(TestCase):
self.assertTrue(models.Notification.objects.filter(user=self.local_user)) self.assertTrue(models.Notification.objects.filter(user=self.local_user))
self.assertEqual(models.Notification.objects.get().notification_type, "REPLY") self.assertEqual(models.Notification.objects.get().notification_type, "REPLY")
def test_create_rating(self):
"""a remote rating activity"""
book = models.Edition.objects.create(
title="Test Book", remote_id="https://example.com/book/1"
)
activity = self.create_json
activity["object"] = {
"id": "https://example.com/user/mouse/reviewrating/12",
"type": "Rating",
"published": "2021-04-29T21:27:30.014235+00:00",
"attributedTo": "https://example.com/user/mouse",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"cc": ["https://example.com/user/mouse/followers"],
"replies": {
"id": "https://example.com/user/mouse/reviewrating/12/replies",
"type": "OrderedCollection",
"totalItems": 0,
"first": "https://example.com/user/mouse/reviewrating/12/replies?page=1",
"last": "https://example.com/user/mouse/reviewrating/12/replies?page=1",
"@context": "https://www.w3.org/ns/activitystreams",
},
"inReplyTo": "",
"summary": "",
"tag": [],
"attachment": [],
"sensitive": False,
"inReplyToBook": "https://example.com/book/1",
"rating": 3,
"@context": "https://www.w3.org/ns/activitystreams",
}
with patch("bookwyrm.activitystreams.ActivityStream.add_status") as redis_mock:
views.inbox.activity_task(activity)
self.assertTrue(redis_mock.called)
rating = models.ReviewRating.objects.first()
self.assertEqual(rating.book, book)
self.assertEqual(rating.rating, 3.0)
def test_create_list(self): def test_create_list(self):
"""a new list""" """a new list"""
activity = self.create_json activity = self.create_json

View file

@ -219,7 +219,7 @@ class ViewsHelpers(TestCase):
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
# 1 shared follow # 1 shared follow
self.local_user.following.add(user_2) self.local_user.following.add(user_2)
user_1.following.add(user_2) user_1.followers.add(user_2)
# 1 shared book # 1 shared book
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
@ -264,7 +264,7 @@ class ViewsHelpers(TestCase):
local=True, local=True,
localname=i, localname=i,
) )
user.followers.add(user_1) user.following.add(user_1)
user.followers.add(self.local_user) user.followers.add(self.local_user)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):

View file

@ -20,8 +20,6 @@ class ReadThrough(TestCase):
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work title="Example Edition", parent_work=self.work
) )
self.work.default_edition = self.edition
self.work.save()
self.user = models.User.objects.create_user( self.user = models.User.objects.create_user(
"cinco", "cinco@example.com", "seissiete", local=True, localname="cinco" "cinco", "cinco@example.com", "seissiete", local=True, localname="cinco"

View file

@ -43,7 +43,7 @@ urlpatterns = [
re_path("^api/updates/notifications/?$", views.get_notification_count), re_path("^api/updates/notifications/?$", views.get_notification_count),
re_path("^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_count), re_path("^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_count),
# authentication # authentication
re_path(r"^login/?$", views.Login.as_view()), re_path(r"^login/?$", views.Login.as_view(), name="login"),
re_path(r"^register/?$", views.Register.as_view()), re_path(r"^register/?$", views.Register.as_view()),
re_path(r"^logout/?$", views.Logout.as_view()), re_path(r"^logout/?$", views.Logout.as_view()),
re_path(r"^password-reset/?$", views.PasswordResetRequest.as_view()), re_path(r"^password-reset/?$", views.PasswordResetRequest.as_view()),
@ -224,7 +224,11 @@ urlpatterns = [
re_path(r"^hide-goal/?$", views.hide_goal, name="hide-goal"), re_path(r"^hide-goal/?$", views.hide_goal, name="hide-goal"),
# preferences # preferences
re_path(r"^preferences/profile/?$", views.EditUser.as_view(), name="prefs-profile"), re_path(r"^preferences/profile/?$", views.EditUser.as_view(), name="prefs-profile"),
re_path(r"^preferences/password/?$", views.ChangePassword.as_view()), re_path(
r"^preferences/password/?$",
views.ChangePassword.as_view(),
name="prefs-password",
),
re_path(r"^preferences/block/?$", views.Block.as_view()), re_path(r"^preferences/block/?$", views.Block.as_view()),
re_path(r"^block/(?P<user_id>\d+)/?$", views.Block.as_view()), re_path(r"^block/(?P<user_id>\d+)/?$", views.Block.as_view()),
re_path(r"^unblock/(?P<user_id>\d+)/?$", views.unblock), re_path(r"^unblock/(?P<user_id>\d+)/?$", views.unblock),

View file

@ -27,7 +27,7 @@ class Author(View):
).distinct() ).distinct()
data = { data = {
"author": author, "author": author,
"books": [b.get_default_edition() for b in books], "books": [b.default_edition for b in books],
} }
return TemplateResponse(request, "author.html", data) return TemplateResponse(request, "author.html", data)

View file

@ -39,7 +39,7 @@ class Book(View):
return ActivitypubResponse(book.to_activity()) return ActivitypubResponse(book.to_activity())
if isinstance(book, models.Work): if isinstance(book, models.Work):
book = book.get_default_edition() book = book.default_edition
if not book or not book.parent_work: if not book or not book.parent_work:
return HttpResponseNotFound() return HttpResponseNotFound()
@ -156,7 +156,6 @@ class EditBook(View):
), ),
} }
) )
print(data["author_matches"])
# we're creating a new book # we're creating a new book
if not book: if not book:

View file

@ -123,7 +123,7 @@ def get_edition(book_id):
"""look up a book in the db and return an edition""" """look up a book in the db and return an edition"""
book = models.Book.objects.select_subclasses().get(id=book_id) book = models.Book.objects.select_subclasses().get(id=book_id)
if isinstance(book, models.Work): if isinstance(book, models.Work):
book = book.get_default_edition() book = book.default_edition
return book return book
@ -190,11 +190,11 @@ def get_annotated_users(user, *args, **kwargs):
.exclude(Q(id__in=user.blocks.all()) | Q(blocks=user)) .exclude(Q(id__in=user.blocks.all()) | Q(blocks=user))
.annotate( .annotate(
mutuals=Count( mutuals=Count(
"following", "followers",
filter=Q( filter=Q(
~Q(id=user.id), ~Q(id=user.id),
~Q(id__in=user.following.all()), ~Q(id__in=user.following.all()),
following__in=user.following.all(), followers__in=user.following.all(),
), ),
distinct=True, distinct=True,
), ),

View file

@ -30,27 +30,30 @@ class Search(View):
) )
return JsonResponse([r.json() for r in book_results], safe=False) return JsonResponse([r.json() for r in book_results], safe=False)
data = {"query": query or ""}
# use webfinger for mastodon style account@domain.com username # use webfinger for mastodon style account@domain.com username
if query and re.match(regex.full_username, query): if query and re.match(regex.full_username, query):
handle_remote_webfinger(query) handle_remote_webfinger(query)
# do a user search # do a user search
user_results = ( if request.user.is_authenticated:
models.User.viewer_aware_objects(request.user) data["user_results"] = (
.annotate( models.User.viewer_aware_objects(request.user)
similarity=Greatest( .annotate(
TrigramSimilarity("username", query), similarity=Greatest(
TrigramSimilarity("localname", query), TrigramSimilarity("username", query),
TrigramSimilarity("localname", query),
)
) )
.filter(
similarity__gt=0.5,
)
.order_by("-similarity")[:10]
) )
.filter(
similarity__gt=0.5,
)
.order_by("-similarity")[:10]
)
# any relevent lists? # any relevent lists?
list_results = ( data["list_results"] = (
privacy_filter( privacy_filter(
request.user, request.user,
models.List.objects, models.List.objects,
@ -68,11 +71,7 @@ class Search(View):
.order_by("-similarity")[:10] .order_by("-similarity")[:10]
) )
book_results = connector_manager.search(query, min_confidence=min_confidence) data["book_results"] = connector_manager.search(
data = { query, min_confidence=min_confidence
"book_results": book_results, )
"user_results": user_results,
"list_results": list_results,
"query": query or "",
}
return TemplateResponse(request, "search_results.html", data) return TemplateResponse(request, "search_results.html", data)

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 13:24-0700\n"
"PO-Revision-Date: 2021-03-02 17:19-0800\n" "PO-Revision-Date: 2021-03-02 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n" "Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n" "Language-Team: English <LL@li.org>\n"
@ -100,23 +100,23 @@ msgstr "Username"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "Dieser Benutzename ist bereits vergeben." msgstr "Dieser Benutzename ist bereits vergeben."
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Spanisch" msgstr "Spanisch"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "Vereinfachtes Chinesisch" msgstr "Vereinfachtes Chinesisch"
@ -178,24 +178,30 @@ msgstr "Laden fehlgeschlagen"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "In OpenLibrary ansehen" msgstr "In OpenLibrary ansehen"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "In OpenLibrary ansehen"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s Bewertung)" msgstr[0] "(%(review_count)s Bewertung)"
msgstr[1] "(%(review_count)s Bewertungen)" msgstr[1] "(%(review_count)s Bewertungen)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "Beschreibung hinzufügen" msgstr "Beschreibung hinzufügen"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "Beschreibung:" msgstr "Beschreibung:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -210,7 +216,7 @@ msgstr "Beschreibung:"
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -226,19 +232,19 @@ msgstr "Speichern"
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, fuzzy, python-format #, fuzzy, python-format
#| msgid "<a href=\"%(path)s\">%(title)s</a> by " #| msgid "<a href=\"%(path)s\">%(title)s</a> by "
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s\">%(title)s</a> von" msgstr "<a href=\"%(path)s\">%(title)s</a> von"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, fuzzy, python-format #, fuzzy, python-format
#| msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>" #| msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>"
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "Direktnachrichten mit <a href=\"%(path)s\">%(username)s</a>" msgstr "Direktnachrichten mit <a href=\"%(path)s\">%(username)s</a>"
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, fuzzy, python-format #, fuzzy, python-format
#| msgid "" #| msgid ""
#| " added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list " #| " added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list "
@ -250,74 +256,74 @@ msgstr ""
"hat <em><a href=\"%(book_path)s\">%(book_title)s</a></em> zu deiner Liste " "hat <em><a href=\"%(book_path)s\">%(book_title)s</a></em> zu deiner Liste "
"\"<a href=\"%(list_path)s\">%(list_name)s</a>\" Hinzugefügt" "\"<a href=\"%(list_path)s\">%(list_name)s</a>\" Hinzugefügt"
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "Deine Leseaktivität" msgstr "Deine Leseaktivität"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "Lesedaten hinzufügen" msgstr "Lesedaten hinzufügen"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "Du hast keine Leseaktivität für dieses Buch." msgstr "Du hast keine Leseaktivität für dieses Buch."
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "Erstellen" msgstr "Erstellen"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "Themen" msgstr "Themen"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "Orte" msgstr "Orte"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:115
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "Listen" msgstr "Listen"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
#, fuzzy #, fuzzy
#| msgid "Go to list" #| msgid "Go to list"
msgid "Add to list" msgid "Add to list"
msgstr "Zur Liste" msgstr "Zur Liste"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "Hinzufügen" msgstr "Hinzufügen"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "Bewerten" msgstr "Bewerten"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "Deine Regale" msgstr "Deine Regale"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "Dein Account" msgstr "Dein Account"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "Deine Bücher" msgstr "Deine Bücher"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "bewertet" msgstr "bewertet"
@ -575,6 +581,7 @@ msgstr "Veröffentlicht von %(publisher)s."
#: bookwyrm/templates/feed/feed_layout.html:70 #: bookwyrm/templates/feed/feed_layout.html:70
#: bookwyrm/templates/get_started/layout.html:19 #: bookwyrm/templates/get_started/layout.html:19
#: bookwyrm/templates/get_started/layout.html:52 #: bookwyrm/templates/get_started/layout.html:52
#: bookwyrm/templates/search_results.html:72
msgid "Close" msgid "Close"
msgstr "Schließen" msgstr "Schließen"
@ -1123,7 +1130,7 @@ msgid "Search for a user"
msgstr "Suche nach Buch oder Benutzer*in" msgstr "Suche nach Buch oder Benutzer*in"
#: bookwyrm/templates/get_started/users.html:13 #: bookwyrm/templates/get_started/users.html:13
#: bookwyrm/templates/search_results.html:76 #: bookwyrm/templates/search_results.html:99
#, python-format #, python-format
msgid "No users found for \"%(query)s\"" msgid "No users found for \"%(query)s\""
msgstr "Keine Nutzer*innen für \"%(query)s\" gefunden" msgstr "Keine Nutzer*innen für \"%(query)s\" gefunden"
@ -1902,23 +1909,33 @@ msgstr "Profil"
msgid "Relationships" msgid "Relationships"
msgstr "Beziehungen" msgstr "Beziehungen"
#: bookwyrm/templates/search_results.html:33 #: bookwyrm/templates/search_results.html:20
msgid "Log in to import or add books."
msgstr ""
#: bookwyrm/templates/search_results.html:38
msgid "Didn't find what you were looking for?" msgid "Didn't find what you were looking for?"
msgstr "Nicht gefunden, wonach du gesucht hast?" msgstr "Nicht gefunden, wonach du gesucht hast?"
#: bookwyrm/templates/search_results.html:35 #: bookwyrm/templates/search_results.html:40
msgid "Show results from other catalogues" msgid "Show results from other catalogues"
msgstr "Ergebnisse aus anderen Katalogen zeigen" msgstr "Ergebnisse aus anderen Katalogen zeigen"
#: bookwyrm/templates/search_results.html:62 #: bookwyrm/templates/search_results.html:44
msgid "Hide results from other catalogues" msgid "Hide results from other catalogues"
msgstr "Ergebnisse aus anderen Katalogen ausblenden" msgstr "Ergebnisse aus anderen Katalogen ausblenden"
#: bookwyrm/templates/search_results.html:74 #: bookwyrm/templates/search_results.html:63
#, fuzzy
#| msgid "Show more"
msgid "Show"
msgstr "Mehr anzeigen"
#: bookwyrm/templates/search_results.html:97
msgid "Matching Users" msgid "Matching Users"
msgstr "Passende Nutzer*innen" msgstr "Passende Nutzer*innen"
#: bookwyrm/templates/search_results.html:93 #: bookwyrm/templates/search_results.html:117
#, python-format #, python-format
msgid "No lists found for \"%(query)s\"" msgid "No lists found for \"%(query)s\""
msgstr "Keine Liste für \"%(query)s\" gefunden" msgstr "Keine Liste für \"%(query)s\" gefunden"
@ -2728,12 +2745,7 @@ msgstr "kommentierte"
msgid "quoted" msgid "quoted"
msgstr "zitierte" msgstr "zitierte"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "von %(author)s"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "Buch importieren" msgstr "Buch importieren"
@ -4495,6 +4507,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "" msgstr ""
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "von %(author)s"
#~ msgid "Deactivate user" #~ msgid "Deactivate user"
#~ msgstr "Nutzer:in deaktivieren" #~ msgstr "Nutzer:in deaktivieren"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 13:24-0700\n"
"PO-Revision-Date: 2021-02-28 17:19-0800\n" "PO-Revision-Date: 2021-02-28 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n" "Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n" "Language-Team: English <LL@li.org>\n"
@ -90,23 +90,23 @@ msgstr ""
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "" msgstr ""
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "" msgstr ""
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "" msgstr ""
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "" msgstr ""
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "" msgstr ""
@ -166,24 +166,28 @@ msgstr ""
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
msgid "View on Inventaire"
msgstr ""
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -198,7 +202,7 @@ msgstr ""
msgid "Save" msgid "Save"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -214,81 +218,81 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
"href=\"%(shelf_path)s\">%(shelf_name)s</a> shelf." "href=\"%(shelf_path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:115
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
msgid "Reviews" msgid "Reviews"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
msgid "Your reviews" msgid "Your reviews"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
msgid "Your comments" msgid "Your comments"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
msgid "Your quotes" msgid "Your quotes"
msgstr "" msgstr ""
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "" msgstr ""
@ -531,6 +535,7 @@ msgstr ""
#: bookwyrm/templates/feed/feed_layout.html:70 #: bookwyrm/templates/feed/feed_layout.html:70
#: bookwyrm/templates/get_started/layout.html:19 #: bookwyrm/templates/get_started/layout.html:19
#: bookwyrm/templates/get_started/layout.html:52 #: bookwyrm/templates/get_started/layout.html:52
#: bookwyrm/templates/search_results.html:72
msgid "Close" msgid "Close"
msgstr "" msgstr ""
@ -1028,7 +1033,7 @@ msgid "Search for a user"
msgstr "" msgstr ""
#: bookwyrm/templates/get_started/users.html:13 #: bookwyrm/templates/get_started/users.html:13
#: bookwyrm/templates/search_results.html:76 #: bookwyrm/templates/search_results.html:99
#, python-format #, python-format
msgid "No users found for \"%(query)s\"" msgid "No users found for \"%(query)s\""
msgstr "" msgstr ""
@ -1735,23 +1740,31 @@ msgstr ""
msgid "Relationships" msgid "Relationships"
msgstr "" msgstr ""
#: bookwyrm/templates/search_results.html:33 #: bookwyrm/templates/search_results.html:20
msgid "Log in to import or add books."
msgstr ""
#: bookwyrm/templates/search_results.html:38
msgid "Didn't find what you were looking for?" msgid "Didn't find what you were looking for?"
msgstr "" msgstr ""
#: bookwyrm/templates/search_results.html:35 #: bookwyrm/templates/search_results.html:40
msgid "Show results from other catalogues" msgid "Show results from other catalogues"
msgstr "" msgstr ""
#: bookwyrm/templates/search_results.html:62 #: bookwyrm/templates/search_results.html:44
msgid "Hide results from other catalogues" msgid "Hide results from other catalogues"
msgstr "" msgstr ""
#: bookwyrm/templates/search_results.html:74 #: bookwyrm/templates/search_results.html:63
msgid "Show"
msgstr ""
#: bookwyrm/templates/search_results.html:97
msgid "Matching Users" msgid "Matching Users"
msgstr "" msgstr ""
#: bookwyrm/templates/search_results.html:93 #: bookwyrm/templates/search_results.html:117
#, python-format #, python-format
msgid "No lists found for \"%(query)s\"" msgid "No lists found for \"%(query)s\""
msgstr "" msgstr ""
@ -2481,12 +2494,7 @@ msgstr ""
msgid "quoted" msgid "quoted"
msgstr "" msgstr ""
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr ""
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "" msgstr ""

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 13:24-0700\n"
"PO-Revision-Date: 2021-03-19 11:49+0800\n" "PO-Revision-Date: 2021-03-19 11:49+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -90,23 +90,23 @@ msgstr "nombre de usuario"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "Ya existe un usuario con ese nombre." msgstr "Ya existe un usuario con ese nombre."
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "Inglés" msgstr "Inglés"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Aléman" msgstr "Aléman"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Español" msgstr "Español"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Francés" msgstr "Francés"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "Chino simplificado" msgstr "Chino simplificado"
@ -166,24 +166,30 @@ msgstr "No se pudo cargar la portada"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "Ver en OpenLibrary" msgstr "Ver en OpenLibrary"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "Ver en OpenLibrary"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s reseña)" msgstr[0] "(%(review_count)s reseña)"
msgstr[1] "(%(review_count)s reseñas)" msgstr[1] "(%(review_count)s reseñas)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "Agregar descripción" msgstr "Agregar descripción"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "Descripción:" msgstr "Descripción:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -198,7 +204,7 @@ msgstr "Descripción:"
msgid "Save" msgid "Save"
msgstr "Guardar" msgstr "Guardar"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -214,18 +220,18 @@ msgstr "Guardar"
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s/editions\">%(count)s ediciones</a>" msgstr "<a href=\"%(path)s/editions\">%(count)s ediciones</a>"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
"Esta edición está en tu <a href=\"%(path)s\">%(shelf_name)s</a> estante." "Esta edición está en tu <a href=\"%(path)s\">%(shelf_name)s</a> estante."
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
@ -234,72 +240,72 @@ msgstr ""
"Una <a href=\"%(book_path)s\">edición diferente</a> de este libro está en tu " "Una <a href=\"%(book_path)s\">edición diferente</a> de este libro está en tu "
"<a href=\"%(shelf_path)s\">%(shelf_name)s</a> estante." "<a href=\"%(shelf_path)s\">%(shelf_name)s</a> estante."
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "Tu actividad de lectura" msgstr "Tu actividad de lectura"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "Agregar fechas de lectura" msgstr "Agregar fechas de lectura"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "No tienes ninguna actividad de lectura para este libro." msgstr "No tienes ninguna actividad de lectura para este libro."
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "Crear" msgstr "Crear"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "Sujetos" msgstr "Sujetos"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "Lugares" msgstr "Lugares"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:115
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "Listas" msgstr "Listas"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "Agregar a lista" msgstr "Agregar a lista"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "Agregar" msgstr "Agregar"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "Reseña" msgstr "Reseña"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "Tus estantes" msgstr "Tus estantes"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "Tu cuenta" msgstr "Tu cuenta"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "Tus libros" msgstr "Tus libros"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "lo calificó con" msgstr "lo calificó con"
@ -542,6 +548,7 @@ msgstr "Publicado por %(publisher)s."
#: bookwyrm/templates/feed/feed_layout.html:70 #: bookwyrm/templates/feed/feed_layout.html:70
#: bookwyrm/templates/get_started/layout.html:19 #: bookwyrm/templates/get_started/layout.html:19
#: bookwyrm/templates/get_started/layout.html:52 #: bookwyrm/templates/get_started/layout.html:52
#: bookwyrm/templates/search_results.html:72
msgid "Close" msgid "Close"
msgstr "Cerrar" msgstr "Cerrar"
@ -1054,7 +1061,7 @@ msgid "Search for a user"
msgstr "Buscar un usuario" msgstr "Buscar un usuario"
#: bookwyrm/templates/get_started/users.html:13 #: bookwyrm/templates/get_started/users.html:13
#: bookwyrm/templates/search_results.html:76 #: bookwyrm/templates/search_results.html:99
#, python-format #, python-format
msgid "No users found for \"%(query)s\"" msgid "No users found for \"%(query)s\""
msgstr "No se encontró ningún usuario correspondiente a \"%(query)s\"" msgstr "No se encontró ningún usuario correspondiente a \"%(query)s\""
@ -1805,23 +1812,33 @@ msgstr "Perfil"
msgid "Relationships" msgid "Relationships"
msgstr "Relaciones" msgstr "Relaciones"
#: bookwyrm/templates/search_results.html:33 #: bookwyrm/templates/search_results.html:20
msgid "Log in to import or add books."
msgstr ""
#: bookwyrm/templates/search_results.html:38
msgid "Didn't find what you were looking for?" msgid "Didn't find what you were looking for?"
msgstr "¿No encontraste lo que buscabas?" msgstr "¿No encontraste lo que buscabas?"
#: bookwyrm/templates/search_results.html:35 #: bookwyrm/templates/search_results.html:40
msgid "Show results from other catalogues" msgid "Show results from other catalogues"
msgstr "Mostrar resultados de otros catálogos" msgstr "Mostrar resultados de otros catálogos"
#: bookwyrm/templates/search_results.html:62 #: bookwyrm/templates/search_results.html:44
msgid "Hide results from other catalogues" msgid "Hide results from other catalogues"
msgstr "Ocultar resultados de otros catálogos" msgstr "Ocultar resultados de otros catálogos"
#: bookwyrm/templates/search_results.html:74 #: bookwyrm/templates/search_results.html:63
#, fuzzy
#| msgid "Show more"
msgid "Show"
msgstr "Mostrar más"
#: bookwyrm/templates/search_results.html:97
msgid "Matching Users" msgid "Matching Users"
msgstr "Usuarios correspondientes" msgstr "Usuarios correspondientes"
#: bookwyrm/templates/search_results.html:93 #: bookwyrm/templates/search_results.html:117
#, python-format #, python-format
msgid "No lists found for \"%(query)s\"" msgid "No lists found for \"%(query)s\""
msgstr "No se encontró ningúna lista correspondiente a \"%(query)s\"" msgstr "No se encontró ningúna lista correspondiente a \"%(query)s\""
@ -2571,12 +2588,7 @@ msgstr "comentó en"
msgid "quoted" msgid "quoted"
msgstr "citó" msgstr "citó"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "por %(author)s"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "Importar libro" msgstr "Importar libro"
@ -4299,6 +4311,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "garrote" msgstr "garrote"
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "por %(author)s"
#, python-format #, python-format
#~ msgid "%(rating)s star" #~ msgid "%(rating)s star"
#~ msgid_plural "%(rating)s stars" #~ msgid_plural "%(rating)s stars"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.1.1\n" "Project-Id-Version: 0.1.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 13:24-0700\n"
"PO-Revision-Date: 2021-04-05 12:44+0100\n" "PO-Revision-Date: 2021-04-05 12:44+0100\n"
"Last-Translator: Fabien Basmaison <contact@arkhi.org>\n" "Last-Translator: Fabien Basmaison <contact@arkhi.org>\n"
"Language-Team: Mouse Reeve <LL@li.org>\n" "Language-Team: Mouse Reeve <LL@li.org>\n"
@ -96,23 +96,23 @@ msgstr "nom du compte:"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "Ce nom est déjà associé à un compte." msgstr "Ce nom est déjà associé à un compte."
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "English" msgstr "English"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Español" msgstr "Español"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Français" msgstr "Français"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "简化字" msgstr "简化字"
@ -172,24 +172,30 @@ msgstr "La couverture na pu être chargée"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "Voir sur OpenLibrary" msgstr "Voir sur OpenLibrary"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "Voir sur OpenLibrary"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s critique)" msgstr[0] "(%(review_count)s critique)"
msgstr[1] "(%(review_count)s critiques)" msgstr[1] "(%(review_count)s critiques)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "Ajouter une description" msgstr "Ajouter une description"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "Description:" msgstr "Description:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -204,7 +210,7 @@ msgstr "Description:"
msgid "Save" msgid "Save"
msgstr "Enregistrer" msgstr "Enregistrer"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -220,18 +226,18 @@ msgstr "Enregistrer"
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s/editions\">%(count)s éditions</a>" msgstr "<a href=\"%(path)s/editions\">%(count)s éditions</a>"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "" msgstr ""
"Cette édition est sur votre étagère <a href=\"%(path)s\">%(shelf_name)s</a>." "Cette édition est sur votre étagère <a href=\"%(path)s\">%(shelf_name)s</a>."
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
@ -240,72 +246,72 @@ msgstr ""
"Une <a href=\"%(book_path)s\">édition différente</a> de ce livre existe sur " "Une <a href=\"%(book_path)s\">édition différente</a> de ce livre existe sur "
"votre étagère <a href=\"%(shelf_path)s\">%(shelf_name)s</a>." "votre étagère <a href=\"%(shelf_path)s\">%(shelf_name)s</a>."
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "Votre activité de lecture" msgstr "Votre activité de lecture"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "Ajouter des dates de lecture" msgstr "Ajouter des dates de lecture"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "Vous navez aucune activité de lecture pour ce livre" msgstr "Vous navez aucune activité de lecture pour ce livre"
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "Créer" msgstr "Créer"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "Sujets" msgstr "Sujets"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "Lieux" msgstr "Lieux"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:115
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "Listes" msgstr "Listes"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "Ajouter à la liste" msgstr "Ajouter à la liste"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "Ajouter" msgstr "Ajouter"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "Critique" msgstr "Critique"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "Vos étagères" msgstr "Vos étagères"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "Votre compte" msgstr "Votre compte"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "Vos livres" msgstr "Vos livres"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "la noté" msgstr "la noté"
@ -548,6 +554,7 @@ msgstr "Publié par %(publisher)s."
#: bookwyrm/templates/feed/feed_layout.html:70 #: bookwyrm/templates/feed/feed_layout.html:70
#: bookwyrm/templates/get_started/layout.html:19 #: bookwyrm/templates/get_started/layout.html:19
#: bookwyrm/templates/get_started/layout.html:52 #: bookwyrm/templates/get_started/layout.html:52
#: bookwyrm/templates/search_results.html:72
msgid "Close" msgid "Close"
msgstr "Fermer" msgstr "Fermer"
@ -1066,7 +1073,7 @@ msgid "Search for a user"
msgstr "Chercher un compte" msgstr "Chercher un compte"
#: bookwyrm/templates/get_started/users.html:13 #: bookwyrm/templates/get_started/users.html:13
#: bookwyrm/templates/search_results.html:76 #: bookwyrm/templates/search_results.html:99
#, python-format #, python-format
msgid "No users found for \"%(query)s\"" msgid "No users found for \"%(query)s\""
msgstr "Aucun compte trouvé pour « %(query)s»" msgstr "Aucun compte trouvé pour « %(query)s»"
@ -1830,23 +1837,33 @@ msgstr "Profil"
msgid "Relationships" msgid "Relationships"
msgstr "Relations" msgstr "Relations"
#: bookwyrm/templates/search_results.html:33 #: bookwyrm/templates/search_results.html:20
msgid "Log in to import or add books."
msgstr ""
#: bookwyrm/templates/search_results.html:38
msgid "Didn't find what you were looking for?" msgid "Didn't find what you were looking for?"
msgstr "Vous navez pas trouvé ce que vous cherchiez?" msgstr "Vous navez pas trouvé ce que vous cherchiez?"
#: bookwyrm/templates/search_results.html:35 #: bookwyrm/templates/search_results.html:40
msgid "Show results from other catalogues" msgid "Show results from other catalogues"
msgstr "Montrer les résultats dautres catalogues" msgstr "Montrer les résultats dautres catalogues"
#: bookwyrm/templates/search_results.html:62 #: bookwyrm/templates/search_results.html:44
msgid "Hide results from other catalogues" msgid "Hide results from other catalogues"
msgstr "Masquer les résultats dautres catalogues" msgstr "Masquer les résultats dautres catalogues"
#: bookwyrm/templates/search_results.html:74 #: bookwyrm/templates/search_results.html:63
#, fuzzy
#| msgid "Show more"
msgid "Show"
msgstr "Déplier"
#: bookwyrm/templates/search_results.html:97
msgid "Matching Users" msgid "Matching Users"
msgstr "Comptes correspondants" msgstr "Comptes correspondants"
#: bookwyrm/templates/search_results.html:93 #: bookwyrm/templates/search_results.html:117
#, python-format #, python-format
msgid "No lists found for \"%(query)s\"" msgid "No lists found for \"%(query)s\""
msgstr "Aucune liste trouvée pour « %(query)s»" msgstr "Aucune liste trouvée pour « %(query)s»"
@ -2605,12 +2622,7 @@ msgstr "a commenté"
msgid "quoted" msgid "quoted"
msgstr "a cité" msgstr "a cité"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "par %(author)s"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "Importer le livre" msgstr "Importer le livre"
@ -4341,6 +4353,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "" msgstr ""
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "par %(author)s"
#~ msgid "Deactivate user" #~ msgid "Deactivate user"
#~ msgstr "Désactiver le compte" #~ msgstr "Désactiver le compte"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.1.1\n" "Project-Id-Version: 0.1.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-26 09:56-0700\n" "POT-Creation-Date: 2021-04-29 13:24-0700\n"
"PO-Revision-Date: 2021-03-20 00:56+0000\n" "PO-Revision-Date: 2021-03-20 00:56+0000\n"
"Last-Translator: Kana <gudzpoz@live.com>\n" "Last-Translator: Kana <gudzpoz@live.com>\n"
"Language-Team: Mouse Reeve <LL@li.org>\n" "Language-Team: Mouse Reeve <LL@li.org>\n"
@ -96,23 +96,23 @@ msgstr "用户名"
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "已经存在使用该用户名的用户。" msgstr "已经存在使用该用户名的用户。"
#: bookwyrm/settings.py:152 #: bookwyrm/settings.py:155
msgid "English" msgid "English"
msgstr "English英语" msgstr "English英语"
#: bookwyrm/settings.py:153 #: bookwyrm/settings.py:156
msgid "German" msgid "German"
msgstr "Deutsch德语" msgstr "Deutsch德语"
#: bookwyrm/settings.py:154 #: bookwyrm/settings.py:157
msgid "Spanish" msgid "Spanish"
msgstr "Español西班牙语" msgstr "Español西班牙语"
#: bookwyrm/settings.py:155 #: bookwyrm/settings.py:158
msgid "French" msgid "French"
msgstr "Français法语" msgstr "Français法语"
#: bookwyrm/settings.py:156 #: bookwyrm/settings.py:159
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "简体中文" msgstr "简体中文"
@ -172,23 +172,29 @@ msgstr "加载封面失败"
msgid "View on OpenLibrary" msgid "View on OpenLibrary"
msgstr "在 OpenLibrary 查看" msgstr "在 OpenLibrary 查看"
#: bookwyrm/templates/book/book.html:102 #: bookwyrm/templates/book/book.html:85
#, fuzzy
#| msgid "View on OpenLibrary"
msgid "View on Inventaire"
msgstr "在 OpenLibrary 查看"
#: bookwyrm/templates/book/book.html:105
#, python-format #, python-format
msgid "(%(review_count)s review)" msgid "(%(review_count)s review)"
msgid_plural "(%(review_count)s reviews)" msgid_plural "(%(review_count)s reviews)"
msgstr[0] "(%(review_count)s 则书评)" msgstr[0] "(%(review_count)s 则书评)"
#: bookwyrm/templates/book/book.html:114 #: bookwyrm/templates/book/book.html:117
msgid "Add Description" msgid "Add Description"
msgstr "添加描述" msgstr "添加描述"
#: bookwyrm/templates/book/book.html:121 #: bookwyrm/templates/book/book.html:124
#: bookwyrm/templates/book/edit_book.html:107 #: bookwyrm/templates/book/edit_book.html:107
#: bookwyrm/templates/lists/form.html:12 #: bookwyrm/templates/lists/form.html:12
msgid "Description:" msgid "Description:"
msgstr "描述:" msgstr "描述:"
#: bookwyrm/templates/book/book.html:125 #: bookwyrm/templates/book/book.html:128
#: bookwyrm/templates/book/edit_book.html:240 #: bookwyrm/templates/book/edit_book.html:240
#: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42 #: bookwyrm/templates/edit_author.html:78 bookwyrm/templates/lists/form.html:42
#: bookwyrm/templates/preferences/edit_user.html:70 #: bookwyrm/templates/preferences/edit_user.html:70
@ -203,7 +209,7 @@ msgstr "描述:"
msgid "Save" msgid "Save"
msgstr "保存" msgstr "保存"
#: bookwyrm/templates/book/book.html:126 bookwyrm/templates/book/book.html:175 #: bookwyrm/templates/book/book.html:129 bookwyrm/templates/book/book.html:178
#: bookwyrm/templates/book/cover_modal.html:32 #: bookwyrm/templates/book/cover_modal.html:32
#: bookwyrm/templates/book/edit_book.html:241 #: bookwyrm/templates/book/edit_book.html:241
#: bookwyrm/templates/edit_author.html:79 #: bookwyrm/templates/edit_author.html:79
@ -219,17 +225,17 @@ msgstr "保存"
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: bookwyrm/templates/book/book.html:135 #: bookwyrm/templates/book/book.html:138
#, python-format #, python-format
msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>" msgid "<a href=\"%(path)s/editions\">%(count)s editions</a>"
msgstr "<a href=\"%(path)s/editions\">%(count)s 个版本</a>" msgstr "<a href=\"%(path)s/editions\">%(count)s 个版本</a>"
#: bookwyrm/templates/book/book.html:143 #: bookwyrm/templates/book/book.html:146
#, python-format #, python-format
msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf." msgid "This edition is on your <a href=\"%(path)s\">%(shelf_name)s</a> shelf."
msgstr "此版本在你的 <a href=\"%(path)s\">%(shelf_name)s</a> 书架上。" msgstr "此版本在你的 <a href=\"%(path)s\">%(shelf_name)s</a> 书架上。"
#: bookwyrm/templates/book/book.html:149 #: bookwyrm/templates/book/book.html:152
#, python-format #, python-format
msgid "" msgid ""
"A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a " "A <a href=\"%(book_path)s\">different edition</a> of this book is on your <a "
@ -238,72 +244,72 @@ msgstr ""
"本书的 <a href=\"%(book_path)s\">另一个版本</a> 在你的 <a href=" "本书的 <a href=\"%(book_path)s\">另一个版本</a> 在你的 <a href="
"\"%(shelf_path)s\">%(shelf_name)s</a> 书架上。" "\"%(shelf_path)s\">%(shelf_name)s</a> 书架上。"
#: bookwyrm/templates/book/book.html:158 #: bookwyrm/templates/book/book.html:161
msgid "Your reading activity" msgid "Your reading activity"
msgstr "你的阅读活动" msgstr "你的阅读活动"
#: bookwyrm/templates/book/book.html:160 #: bookwyrm/templates/book/book.html:163
msgid "Add read dates" msgid "Add read dates"
msgstr "添加阅读日期" msgstr "添加阅读日期"
#: bookwyrm/templates/book/book.html:165 #: bookwyrm/templates/book/book.html:168
msgid "You don't have any reading activity for this book." msgid "You don't have any reading activity for this book."
msgstr "你还没有任何这本书的阅读活动。" msgstr "你还没有任何这本书的阅读活动。"
#: bookwyrm/templates/book/book.html:172 #: bookwyrm/templates/book/book.html:175
msgid "Create" msgid "Create"
msgstr "创建" msgstr "创建"
#: bookwyrm/templates/book/book.html:194 #: bookwyrm/templates/book/book.html:197
msgid "Subjects" msgid "Subjects"
msgstr "主题" msgstr "主题"
#: bookwyrm/templates/book/book.html:206 #: bookwyrm/templates/book/book.html:209
msgid "Places" msgid "Places"
msgstr "地点" msgstr "地点"
#: bookwyrm/templates/book/book.html:217 bookwyrm/templates/layout.html:65 #: bookwyrm/templates/book/book.html:220 bookwyrm/templates/layout.html:65
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search_results.html:91 #: bookwyrm/templates/search_results.html:115
#: bookwyrm/templates/user/user_layout.html:62 #: bookwyrm/templates/user/user_layout.html:62
msgid "Lists" msgid "Lists"
msgstr "列表" msgstr "列表"
#: bookwyrm/templates/book/book.html:228 #: bookwyrm/templates/book/book.html:231
msgid "Add to list" msgid "Add to list"
msgstr "添加到列表" msgstr "添加到列表"
#: bookwyrm/templates/book/book.html:238 #: bookwyrm/templates/book/book.html:241
#: bookwyrm/templates/book/cover_modal.html:31 #: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:133 #: bookwyrm/templates/lists/list.html:133
msgid "Add" msgid "Add"
msgstr "添加" msgstr "添加"
#: bookwyrm/templates/book/book.html:254 #: bookwyrm/templates/book/book.html:257
#, fuzzy #, fuzzy
#| msgid "Review" #| msgid "Review"
msgid "Reviews" msgid "Reviews"
msgstr "书评" msgstr "书评"
#: bookwyrm/templates/book/book.html:259 #: bookwyrm/templates/book/book.html:262
#, fuzzy #, fuzzy
#| msgid "Your shelves" #| msgid "Your shelves"
msgid "Your reviews" msgid "Your reviews"
msgstr "你的书架" msgstr "你的书架"
#: bookwyrm/templates/book/book.html:265 #: bookwyrm/templates/book/book.html:268
#, fuzzy #, fuzzy
#| msgid "Your Account" #| msgid "Your Account"
msgid "Your comments" msgid "Your comments"
msgstr "你的帐号" msgstr "你的帐号"
#: bookwyrm/templates/book/book.html:271 #: bookwyrm/templates/book/book.html:274
#, fuzzy #, fuzzy
#| msgid "Your books" #| msgid "Your books"
msgid "Your quotes" msgid "Your quotes"
msgstr "你的书目" msgstr "你的书目"
#: bookwyrm/templates/book/book.html:305 #: bookwyrm/templates/book/book.html:308
msgid "rated it" msgid "rated it"
msgstr "评价了" msgstr "评价了"
@ -546,6 +552,7 @@ msgstr "由 %(publisher)s 出版。"
#: bookwyrm/templates/feed/feed_layout.html:70 #: bookwyrm/templates/feed/feed_layout.html:70
#: bookwyrm/templates/get_started/layout.html:19 #: bookwyrm/templates/get_started/layout.html:19
#: bookwyrm/templates/get_started/layout.html:52 #: bookwyrm/templates/get_started/layout.html:52
#: bookwyrm/templates/search_results.html:72
msgid "Close" msgid "Close"
msgstr "关闭" msgstr "关闭"
@ -1046,7 +1053,7 @@ msgid "Search for a user"
msgstr "搜索用户" msgstr "搜索用户"
#: bookwyrm/templates/get_started/users.html:13 #: bookwyrm/templates/get_started/users.html:13
#: bookwyrm/templates/search_results.html:76 #: bookwyrm/templates/search_results.html:99
#, python-format #, python-format
msgid "No users found for \"%(query)s\"" msgid "No users found for \"%(query)s\""
msgstr "没有找到 \"%(query)s\" 的用户" msgstr "没有找到 \"%(query)s\" 的用户"
@ -1795,23 +1802,33 @@ msgstr "个人资料"
msgid "Relationships" msgid "Relationships"
msgstr "关系" msgstr "关系"
#: bookwyrm/templates/search_results.html:33 #: bookwyrm/templates/search_results.html:20
msgid "Log in to import or add books."
msgstr ""
#: bookwyrm/templates/search_results.html:38
msgid "Didn't find what you were looking for?" msgid "Didn't find what you were looking for?"
msgstr "没有找到你想找的?" msgstr "没有找到你想找的?"
#: bookwyrm/templates/search_results.html:35 #: bookwyrm/templates/search_results.html:40
msgid "Show results from other catalogues" msgid "Show results from other catalogues"
msgstr "显示其它类别的结果" msgstr "显示其它类别的结果"
#: bookwyrm/templates/search_results.html:62 #: bookwyrm/templates/search_results.html:44
msgid "Hide results from other catalogues" msgid "Hide results from other catalogues"
msgstr "隐藏其它类别的结果" msgstr "隐藏其它类别的结果"
#: bookwyrm/templates/search_results.html:74 #: bookwyrm/templates/search_results.html:63
#, fuzzy
#| msgid "Show more"
msgid "Show"
msgstr "显示更多"
#: bookwyrm/templates/search_results.html:97
msgid "Matching Users" msgid "Matching Users"
msgstr "匹配的用户" msgstr "匹配的用户"
#: bookwyrm/templates/search_results.html:93 #: bookwyrm/templates/search_results.html:117
#, python-format #, python-format
msgid "No lists found for \"%(query)s\"" msgid "No lists found for \"%(query)s\""
msgstr "没有找到 \"%(query)s\" 的列表" msgstr "没有找到 \"%(query)s\" 的列表"
@ -2570,12 +2587,7 @@ msgstr "评论了"
msgid "quoted" msgid "quoted"
msgstr "引用了" msgstr "引用了"
#: bookwyrm/templates/snippets/search_result_text.html:22 #: bookwyrm/templates/snippets/search_result_text.html:35
#, python-format
msgid "by %(author)s"
msgstr "由 %(author)s 所著"
#: bookwyrm/templates/snippets/search_result_text.html:30
msgid "Import book" msgid "Import book"
msgstr "导入书目" msgstr "导入书目"
@ -4292,6 +4304,10 @@ msgctxt "stick"
msgid "club" msgid "club"
msgstr "" msgstr ""
#, python-format
#~ msgid "by %(author)s"
#~ msgstr "由 %(author)s 所著"
#~ msgid "Deactivate user" #~ msgid "Deactivate user"
#~ msgstr "停用用户" #~ msgstr "停用用户"

View file

@ -1,5 +1,5 @@
celery==4.4.2 celery==4.4.2
Django==3.1.8 Django==3.2.0
django-model-utils==4.0.0 django-model-utils==4.0.0
environs==7.2.0 environs==7.2.0
flower==0.9.4 flower==0.9.4