From b8b491bbf2d488655f83263b7b3bb4096a72180a Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 16:55:12 +0200 Subject: [PATCH 001/121] Add get_absolute_url to ImageField --- bookwyrm/models/fields.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 3b369e84..e57374d5 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -3,6 +3,7 @@ from dataclasses import MISSING import imghdr import re from uuid import uuid4 +from urllib.parse import urljoin import dateutil.parser from dateutil.parser import ParserError @@ -13,11 +14,12 @@ from django.db import models from django.forms import ClearableFileInput, ImageField as DjangoImageField from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from django.utils.encoding import filepath_to_uri from bookwyrm import activitypub from bookwyrm.connectors import get_image from bookwyrm.sanitize_html import InputHtmlParser -from bookwyrm.settings import DOMAIN +from bookwyrm.settings import MEDIA_FULL_URL def validate_remote_id(value): @@ -355,8 +357,6 @@ def image_serializer(value, alt): url = value.url else: return None - if not url[:4] == "http": - url = "https://{:s}{:s}".format(DOMAIN, url) return activitypub.Document(url=url, name=alt) @@ -423,6 +423,19 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): } ) + def get_absolute_url(self, instance): + """returns an absolute URL for the image""" + value = getattr(instance, self.name) + if value is None: + return + + url = filepath_to_uri(value) + if url is not None: + url = url.lstrip('/') + url = urljoin(MEDIA_FULL_URL, url) + + return url + class DateTimeField(ActivitypubFieldMixin, models.DateTimeField): """activitypub-aware datetime field""" From 7a716db48aad0093b1429bd66f360fb22ff258c4 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 16:56:07 +0200 Subject: [PATCH 002/121] lint --- bookwyrm/models/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index e57374d5..3baf3734 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -431,7 +431,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): url = filepath_to_uri(value) if url is not None: - url = url.lstrip('/') + url = url.lstrip("/") url = urljoin(MEDIA_FULL_URL, url) return url From 60e805ac2b1faaeb977d03397880d3939dc17674 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 17:39:46 +0200 Subject: [PATCH 003/121] Fix tests --- bookwyrm/models/fields.py | 8 ++++---- bookwyrm/tests/models/test_fields.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 3baf3734..1a8c082a 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -423,13 +423,13 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): } ) - def get_absolute_url(self, instance): + def get_absolute_url(self, value): """returns an absolute URL for the image""" - value = getattr(instance, self.name) - if value is None: + name = getattr(value, self.name) + if name is None: return - url = filepath_to_uri(value) + url = filepath_to_uri(name) if url is not None: url = url.lstrip("/") url = urljoin(MEDIA_FULL_URL, url) diff --git a/bookwyrm/tests/models/test_fields.py b/bookwyrm/tests/models/test_fields.py index c234ffd0..083e8a1b 100644 --- a/bookwyrm/tests/models/test_fields.py +++ b/bookwyrm/tests/models/test_fields.py @@ -429,7 +429,7 @@ class ActivitypubFields(TestCase): def test_image_serialize(self): """make sure we're creating sensible image paths""" ValueMock = namedtuple("ValueMock", ("url")) - value_mock = ValueMock("/images/fish.jpg") + value_mock = ValueMock("https://your.domain.here/images/fish.jpg") result = fields.image_serializer(value_mock, "hello") self.assertEqual(result.type, "Document") self.assertEqual(result.url, "https://your.domain.here/images/fish.jpg") From bc7710a4a71e115d9eb6b7f88793acdf66f09d9c Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 18:18:18 +0200 Subject: [PATCH 004/121] Update Status Model Test --- bookwyrm/models/fields.py | 2 +- bookwyrm/tests/models/test_status_model.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 1a8c082a..0e2a42e5 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -427,7 +427,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): """returns an absolute URL for the image""" name = getattr(value, self.name) if name is None: - return + return None url = filepath_to_uri(name) if url is not None: diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index 355caab9..a01d8679 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -177,7 +177,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://%s%s" % (settings.DOMAIN, self.book.cover.url), + "https://%s%s" % (settings.MEDIA_FULL_URL, self.book.cover.url), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -208,7 +208,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://%s%s" % (settings.DOMAIN, self.book.cover.url), + "https://%s%s" % (settings.MEDIA_FULL_URL, self.book.cover.url), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -246,7 +246,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://%s%s" % (settings.DOMAIN, self.book.cover.url), + "https://%s%s" % (settings.MEDIA_FULL_URL, self.book.cover.url), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -287,7 +287,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://%s%s" % (settings.DOMAIN, self.book.cover.url), + "https://%s%s" % (settings.MEDIA_FULL_URL, self.book.cover.url), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -309,7 +309,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://%s%s" % (settings.DOMAIN, self.book.cover.url), + "https://%s%s" % (settings.MEDIA_FULL_URL, self.book.cover.url), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -331,7 +331,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://%s%s" % (settings.DOMAIN, self.book.cover.url), + "https://%s%s" % (settings.MEDIA_FULL_URL, self.book.cover.url), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") From c6f8236b07806edc596713cd82dfb172f6aea6a6 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 19:11:57 +0200 Subject: [PATCH 005/121] Fix tests --- bookwyrm/models/fields.py | 5 +++-- bookwyrm/tests/models/test_fields.py | 14 ++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 0e2a42e5..2069ed1b 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -387,7 +387,8 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): activity[key] = formatted def field_to_activity(self, value, alt=None): - return image_serializer(value, alt) + url = self.get_absolute_url(value) + return activitypub.Document(url=url, name=alt) def field_from_activity(self, value): image_slug = value @@ -425,7 +426,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): def get_absolute_url(self, value): """returns an absolute URL for the image""" - name = getattr(value, self.name) + name = getattr(value, "name") if name is None: return None diff --git a/bookwyrm/tests/models/test_fields.py b/bookwyrm/tests/models/test_fields.py index 083e8a1b..d9c470e0 100644 --- a/bookwyrm/tests/models/test_fields.py +++ b/bookwyrm/tests/models/test_fields.py @@ -22,6 +22,7 @@ from bookwyrm.activitypub.base_activity import ActivityObject from bookwyrm.models import fields, User, Status from bookwyrm.models.base_model import BookWyrmModel from bookwyrm.models.activitypub_mixin import ActivitypubMixin +from bookwyrm.settings import DOMAIN # pylint: disable=too-many-public-methods class ActivitypubFields(TestCase): @@ -401,21 +402,18 @@ class ActivitypubFields(TestCase): image.save(output, format=image.format) user.avatar.save("test.jpg", ContentFile(output.getvalue())) - output = fields.image_serializer(user.avatar, alt="alt text") + instance = fields.ImageField() + + output = instance.field_to_activity(user.avatar) self.assertIsNotNone( re.match( - r".*\.jpg", + fr"https:\/\/{DOMAIN}\/.*\.jpg", output.url, ) ) - self.assertEqual(output.name, "alt text") + self.assertEqual(output.name, "") self.assertEqual(output.type, "Document") - instance = fields.ImageField() - - output = fields.image_serializer(user.avatar, alt=None) - self.assertEqual(instance.field_to_activity(user.avatar), output) - responses.add( responses.GET, "http://www.example.com/image.jpg", From ee39e8c036299cc3670f154324192b3131586880 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 19:16:24 +0200 Subject: [PATCH 006/121] Fix R0201: Method could be a function (no-self-use) --- bookwyrm/models/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 2069ed1b..cc9e0ec4 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -424,7 +424,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): } ) - def get_absolute_url(self, value): + def get_absolute_url(value): """returns an absolute URL for the image""" name = getattr(value, "name") if name is None: From 0db3512eb3deca2f4be422d4274412d1794b35b3 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 19:21:56 +0200 Subject: [PATCH 007/121] Revert previous commit --- bookwyrm/models/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index cc9e0ec4..2069ed1b 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -424,7 +424,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): } ) - def get_absolute_url(value): + def get_absolute_url(self, value): """returns an absolute URL for the image""" name = getattr(value, "name") if name is None: From c1673ef7174381ddfdccb1e8d33bd07db9ca5c4e Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 4 Aug 2021 19:25:19 +0200 Subject: [PATCH 008/121] Update fields.py --- bookwyrm/models/fields.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 2069ed1b..57b364c9 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -424,6 +424,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): } ) + # pylint: disable=no-self-use def get_absolute_url(self, value): """returns an absolute URL for the image""" name = getattr(value, "name") From 35bd4a4071a13047c2c52ef51ef42616b48447d1 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 27 Oct 2021 18:13:47 +0200 Subject: [PATCH 009/121] Apply review suggestion --- bookwyrm/models/fields.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 7d4981e3..489ed061 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -425,6 +425,10 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): def field_to_activity(self, value, alt=None): url = self.get_absolute_url(value) + + if not url: + return None + return activitypub.Document(url=url, name=alt) def field_from_activity(self, value): @@ -465,7 +469,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): def get_absolute_url(self, value): """returns an absolute URL for the image""" name = getattr(value, "name") - if name is None: + if not name: return None url = filepath_to_uri(name) From b956b79bd03dc844ac7d94755edd7e0e8ee9ae90 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 27 Oct 2021 18:56:37 +0200 Subject: [PATCH 010/121] Add full URL generation to image_serializer --- bookwyrm/models/fields.py | 4 ++++ bookwyrm/tests/models/test_status_model.py | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 489ed061..a490c133 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -389,6 +389,10 @@ def image_serializer(value, alt): url = value.url else: return None + if url is not None: + url = url.lstrip("/") + url = urljoin(MEDIA_FULL_URL, url) + return activitypub.Document(url=url, name=alt) diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index cbec151b..a46abe3b 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -2,6 +2,7 @@ from unittest.mock import patch from io import BytesIO import pathlib +from urllib.parse import urljoin from django.http import Http404 from django.core.files.base import ContentFile @@ -192,7 +193,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - f"https://{settings.MEDIA_FULL_URL}{self.book.cover.url}", + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -222,7 +223,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - f"https://{settings.MEDIA_FULL_URL}{self.book.cover.url}", + urljoin(settings.MEDIA_FULL_URL,self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -259,7 +260,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - "https://{settings.MEDIA_FULL_URL}{self.book.cover.url}", + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -300,7 +301,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - f"https://{settings.MEDIA_FULL_URL}{self.book.cover.url}", + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -322,7 +323,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - f"https://{settings.MEDIA_FULL_URL}{self.book.cover.url}", + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -343,7 +344,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - f"https://{settings.MEDIA_FULL_URL}{self.book.cover.url}", + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") From 56fd147c883a6f2d5823edd5dc8a18ecc8e87145 Mon Sep 17 00:00:00 2001 From: Joachim Date: Wed, 27 Oct 2021 19:00:09 +0200 Subject: [PATCH 011/121] Update test_status_model.py --- bookwyrm/tests/models/test_status_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index a46abe3b..f5013422 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -223,7 +223,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL,self.book.cover.url.lstrip("/")), + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -301,7 +301,7 @@ class Status(TestCase): self.assertEqual(activity["attachment"][0].type, "Document") self.assertEqual( activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")) + urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), ) self.assertEqual(activity["attachment"][0].name, "Test Edition") From d8098357e605eef0468326937cfeeb5487e5dc1d Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Sat, 6 Nov 2021 10:53:41 +1100 Subject: [PATCH 012/121] match page title to active shelf resolves #1586 --- bookwyrm/templates/shelf/shelf.html | 2 +- bookwyrm/templates/user/books_header.html | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templates/shelf/shelf.html b/bookwyrm/templates/shelf/shelf.html index 662d7507..02e58ea3 100644 --- a/bookwyrm/templates/shelf/shelf.html +++ b/bookwyrm/templates/shelf/shelf.html @@ -5,7 +5,7 @@ {% load i18n %} {% block title %} -{% include 'user/books_header.html' %} +{% include 'user/books_header.html' with shelfname=shelf.identifier %} {% endblock %} {% block opengraph_images %} diff --git a/bookwyrm/templates/user/books_header.html b/bookwyrm/templates/user/books_header.html index 8fea84c7..84493b9c 100644 --- a/bookwyrm/templates/user/books_header.html +++ b/bookwyrm/templates/user/books_header.html @@ -1,6 +1,16 @@ {% load i18n %} {% if is_self %} +{% if shelfname == 'to-read' %} +{% trans "To Read" %} +{% elif shelfname == 'reading' %} +{% trans "Currently Reading" %} +{% elif shelfname == 'read' %} +{% trans "Read" %} +{% elif shelfname == 'all' %} {% trans "Your books" %} {% else %} +{{ shelfname }} +{% endif %} +{% else %} {% blocktrans with username=user.display_name %}{{ username }}'s books{% endblocktrans %} {% endif %} From dfe92a27c074239f41784966d3213b26f3e3b673 Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Sat, 6 Nov 2021 11:10:58 +1100 Subject: [PATCH 013/121] use shelf name if not default shelf (instead of shelf.identifier) --- bookwyrm/templates/shelf/shelf.html | 2 +- bookwyrm/templates/user/books_header.html | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bookwyrm/templates/shelf/shelf.html b/bookwyrm/templates/shelf/shelf.html index 02e58ea3..01d41aa0 100644 --- a/bookwyrm/templates/shelf/shelf.html +++ b/bookwyrm/templates/shelf/shelf.html @@ -5,7 +5,7 @@ {% load i18n %} {% block title %} -{% include 'user/books_header.html' with shelfname=shelf.identifier %} +{% include 'user/books_header.html' with shelf=shelf %} {% endblock %} {% block opengraph_images %} diff --git a/bookwyrm/templates/user/books_header.html b/bookwyrm/templates/user/books_header.html index 84493b9c..7311e242 100644 --- a/bookwyrm/templates/user/books_header.html +++ b/bookwyrm/templates/user/books_header.html @@ -1,15 +1,15 @@ {% load i18n %} {% if is_self %} -{% if shelfname == 'to-read' %} +{% if shelf.identifier == 'to-read' %} {% trans "To Read" %} -{% elif shelfname == 'reading' %} +{% elif shelf.identifier == 'reading' %} {% trans "Currently Reading" %} -{% elif shelfname == 'read' %} +{% elif shelf.identifier == 'read' %} {% trans "Read" %} -{% elif shelfname == 'all' %} +{% elif shelf.identifier == 'all' %} {% trans "Your books" %} {% else %} -{{ shelfname }} +{{ shelf.name }} {% endif %} {% else %} {% blocktrans with username=user.display_name %}{{ username }}'s books{% endblocktrans %} From 1dec882dba5770a4266f9501bea274c88a9a9eeb Mon Sep 17 00:00:00 2001 From: nycterent Date: Sun, 7 Nov 2021 16:06:20 +0200 Subject: [PATCH 014/121] removed quotes from the host - fixes connecting to smtp server --- .env.dev.example | 2 +- .env.prod.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.dev.example b/.env.dev.example index 1e4fb981..9a4366e0 100644 --- a/.env.dev.example +++ b/.env.dev.example @@ -36,7 +36,7 @@ FLOWER_PORT=8888 #FLOWER_USER=mouse #FLOWER_PASSWORD=changeme -EMAIL_HOST="smtp.mailgun.org" +EMAIL_HOST=smtp.mailgun.org EMAIL_PORT=587 EMAIL_HOST_USER=mail@your.domain.here EMAIL_HOST_PASSWORD=emailpassword123 diff --git a/.env.prod.example b/.env.prod.example index 49729d53..56f52a28 100644 --- a/.env.prod.example +++ b/.env.prod.example @@ -36,7 +36,7 @@ FLOWER_PORT=8888 FLOWER_USER=mouse FLOWER_PASSWORD=changeme -EMAIL_HOST="smtp.mailgun.org" +EMAIL_HOST=smtp.mailgun.org EMAIL_PORT=587 EMAIL_HOST_USER=mail@your.domain.here EMAIL_HOST_PASSWORD=emailpassword123 From 67d830e327a7a16709b7be8f3a5760ac06fdc1e0 Mon Sep 17 00:00:00 2001 From: nycterent Date: Sun, 7 Nov 2021 16:07:21 +0200 Subject: [PATCH 015/121] fixed typo for the domain placeholder for easier substitution --- nginx/production | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/production b/nginx/production index 3c8b2ea7..8a13413a 100644 --- a/nginx/production +++ b/nginx/production @@ -27,7 +27,7 @@ server { # # client_max_body_size 3M; # -# if ($host != "you-domain.com") { +# if ($host != "your-domain.com") { # return 301 $scheme://your-domain.com$request_uri; # } # From 23549c7fdb89de3343ccfc2ad62e529589420313 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Nov 2021 06:13:44 -0800 Subject: [PATCH 016/121] Removes unused `rundb` command --- bw-dev | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bw-dev b/bw-dev index 6c9def8f..256418e2 100755 --- a/bw-dev +++ b/bw-dev @@ -148,9 +148,6 @@ case "$CMD" in runweb) runweb "$@" ;; - rundb) - rundb "$@" - ;; *) set +x # No need to echo echo echo "Unrecognised command. Try:" @@ -180,6 +177,5 @@ case "$CMD" in echo " copy_media_to_s3" echo " set_cors_to_s3 [cors file]" echo " runweb [command]" - echo " rundb [command]" ;; esac From b2dea343af8dca6b10fdf315c8c8a390612bf911 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Nov 2021 06:17:04 -0800 Subject: [PATCH 017/121] Removes outdated test command --- bw-dev | 4 ---- requirements.txt | 1 - 2 files changed, 5 deletions(-) diff --git a/bw-dev b/bw-dev index 256418e2..195aa061 100755 --- a/bw-dev +++ b/bw-dev @@ -96,9 +96,6 @@ case "$CMD" in restart_celery) docker-compose restart celery_worker ;; - test) - runweb coverage run --source='.' --omit="*/test*,celerywyrm*,bookwyrm/migrations/*" manage.py test "$@" - ;; pytest) execweb pytest --no-cov-on-fail "$@" ;; @@ -161,7 +158,6 @@ case "$CMD" in echo " shell" echo " dbshell" echo " restart_celery" - echo " test [path]" echo " pytest [path]" echo " collectstatic" echo " add_locale [locale]" diff --git a/requirements.txt b/requirements.txt index 7cf1c68a..2cb1eec9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,6 @@ django-storages==1.11.1 # Dev black==21.4b0 -coverage==5.1 pytest-django==4.1.0 pytest==6.1.2 pytest-cov==2.10.1 From c2f44a9f1e0e27698707157592722b712268b4a7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Nov 2021 06:19:38 -0800 Subject: [PATCH 018/121] Removed outdated `add_locales` command from doc --- bw-dev | 1 - 1 file changed, 1 deletion(-) diff --git a/bw-dev b/bw-dev index 195aa061..e442d3bc 100755 --- a/bw-dev +++ b/bw-dev @@ -160,7 +160,6 @@ case "$CMD" in echo " restart_celery" echo " pytest [path]" echo " collectstatic" - echo " add_locale [locale]" echo " makemessages" echo " compilemessages [locale]" echo " build" From 3f5fe839502042881f4fa0d413a838a4ec1efb56 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Nov 2021 06:20:38 -0800 Subject: [PATCH 019/121] Renames run web with service ports command --- bw-dev | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bw-dev b/bw-dev index e442d3bc..35f3694e 100755 --- a/bw-dev +++ b/bw-dev @@ -61,7 +61,7 @@ case "$CMD" in up) docker-compose up --build "$@" ;; - run) + service_ports_web) docker-compose run --rm --service-ports web ;; initdb) @@ -149,7 +149,7 @@ case "$CMD" in set +x # No need to echo echo echo "Unrecognised command. Try:" echo " up [container]" - echo " run" + echo " service_ports_web" echo " initdb" echo " resetdb" echo " makemigrations [migration]" From e19c4620ceab8197436fefca6f7841c362f65155 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Nov 2021 12:00:08 -0800 Subject: [PATCH 020/121] Don't broadcast imported reviews outside bookwyrm --- bookwyrm/importers/importer.py | 6 ++++-- bookwyrm/models/activitypub_mixin.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 6d898a2a..a5243cd3 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -136,7 +136,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): if item.review else "" ) - models.Review.objects.create( + review = models.Review( user=user, book=item.book, name=review_title, @@ -147,10 +147,12 @@ def handle_imported_book(source, user, item, include_reviews, privacy): ) else: # just a rating - models.ReviewRating.objects.create( + review = models.ReviewRating( user=user, book=item.book, rating=item.rating, published_date=published_date_guess, privacy=privacy, ) + # only broadcast this review to other bookwyrm instances + review.save(software="bookwyrm") diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index 3a88c524..031963aa 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -195,7 +195,7 @@ class ActivitypubMixin: class ObjectMixin(ActivitypubMixin): """add this mixin for object models that are AP serializable""" - def save(self, *args, created=None, **kwargs): + def save(self, *args, created=None, software=None, **kwargs): """broadcast created/updated/deleted objects as appropriate""" broadcast = kwargs.get("broadcast", True) # this bonus kwarg would cause an error in the base save method @@ -219,15 +219,16 @@ class ObjectMixin(ActivitypubMixin): return try: - software = None # do we have a "pure" activitypub version of this for mastodon? - if hasattr(self, "pure_content"): + if not software and hasattr(self, "pure_content"): pure_activity = self.to_create_activity(user, pure=True) self.broadcast(pure_activity, user, software="other") + # set bookwyrm so that that type is also sent software = "bookwyrm" - # sends to BW only if we just did a pure version for masto - activity = self.to_create_activity(user) - self.broadcast(activity, user, software=software) + if software == "bookwyrm": + # sends to BW only if we just did a pure version for masto + activity = self.to_create_activity(user) + self.broadcast(activity, user, software=software) except AttributeError: # janky as heck, this catches the mutliple inheritence chain # for boosts and ignores this auxilliary broadcast @@ -241,8 +242,7 @@ class ObjectMixin(ActivitypubMixin): if isinstance(self, user_model): user = self # book data tracks last editor - elif hasattr(self, "last_edited_by"): - user = self.last_edited_by + user = user or getattr(self, "last_edited_by", None) # again, if we don't know the user or they're remote, don't bother if not user or not user.local: return From 3f6b0608b2157f17ebccf761384d3dd1c4631b6f Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Wed, 10 Nov 2021 21:37:16 +1100 Subject: [PATCH 021/121] Show user and book names for reading actions in Discover fixes #1596 This uses the same technique as #1572 to ensure read statuses from GeneratedNotes are translated. --- bookwyrm/templates/discover/card-header.html | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templates/discover/card-header.html b/bookwyrm/templates/discover/card-header.html index 0eb9a678..56d412ea 100644 --- a/bookwyrm/templates/discover/card-header.html +++ b/bookwyrm/templates/discover/card-header.html @@ -4,7 +4,21 @@ {% with user_path=status.user.local_path username=status.user.display_name book_path=status.book.local_poth book_title=book|book_title %} {% if status.status_type == 'GeneratedNote' %} - {{ status.content|safe }} + {% if status.content == 'wants to read' %} + {% blocktrans trimmed %} + {{ username }} wants to read {{ book_title }} + {% endblocktrans %} + {% endif %} + {% if status.content == 'finished reading' %} + {% blocktrans trimmed %} + {{ username }} finished reading {{ book_title }} + {% endblocktrans %} + {% endif %} + {% if status.content == 'started reading' %} + {% blocktrans trimmed %} + {{ username }} started reading {{ book_title }} + {% endblocktrans %} + {% endif %} {% elif status.status_type == 'Rating' %} {% blocktrans trimmed %} {{ username }} rated {{ book_title }} From eb62474b97535cb097631868eb67e4aeaaf175bf Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Wed, 10 Nov 2021 22:04:19 +1100 Subject: [PATCH 022/121] fix broken book links in Discover --- bookwyrm/templates/discover/card-header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/discover/card-header.html b/bookwyrm/templates/discover/card-header.html index 56d412ea..8b9f6fc1 100644 --- a/bookwyrm/templates/discover/card-header.html +++ b/bookwyrm/templates/discover/card-header.html @@ -1,7 +1,7 @@ {% load i18n %} {% load utilities %} -{% with user_path=status.user.local_path username=status.user.display_name book_path=status.book.local_poth book_title=book|book_title %} +{% with user_path=status.user.local_path username=status.user.display_name book_path=book.local_path book_title=book|book_title %} {% if status.status_type == 'GeneratedNote' %} {% if status.content == 'wants to read' %} From 20c6a3ea1c7e32055e1bbfea9b69a3d18c79495b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 08:56:28 -0800 Subject: [PATCH 023/121] Creates generic importer test file And removes some tests that duplicate the generic tests --- .../tests/importers/test_goodreads_import.py | 123 +------- bookwyrm/tests/importers/test_importer.py | 289 ++++++++++++++++++ .../importers/test_librarything_import.py | 50 --- 3 files changed, 290 insertions(+), 172 deletions(-) create mode 100644 bookwyrm/tests/importers/test_importer.py diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index d2b0ea7d..a2451292 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -1,5 +1,4 @@ """ testing import """ -from collections import namedtuple import csv import pathlib from unittest.mock import patch @@ -7,11 +6,10 @@ import datetime import pytz from django.test import TestCase -import responses from bookwyrm import models from bookwyrm.importers import GoodreadsImporter -from bookwyrm.importers.importer import import_data, handle_imported_book +from bookwyrm.importers.importer import handle_imported_book def make_date(*args): @@ -48,9 +46,6 @@ class GoodreadsImport(TestCase): def test_create_job(self, *_): """creates the import job entry and checks csv""" import_job = self.importer.create_job(self.user, self.csv, False, "public") - self.assertEqual(import_job.user, self.user) - self.assertEqual(import_job.include_reviews, False) - self.assertEqual(import_job.privacy, "public") import_items = models.ImportItem.objects.filter(job=import_job).all() self.assertEqual(len(import_items), 3) @@ -79,33 +74,6 @@ class GoodreadsImport(TestCase): self.assertEqual(retry_items[1].index, 1) self.assertEqual(retry_items[1].data["Book Id"], "52691223") - def test_start_import(self, *_): - """begin loading books""" - import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") - MockTask = namedtuple("Task", ("id")) - mock_task = MockTask(7) - with patch("bookwyrm.importers.importer.import_data.delay") as start: - start.return_value = mock_task - self.importer.start_import(import_job) - import_job.refresh_from_db() - self.assertEqual(import_job.task_id, "7") - - @responses.activate - def test_import_data(self, *_): - """resolve entry""" - import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") - book = models.Edition.objects.create(title="Test Book") - - with patch( - "bookwyrm.models.import_job.ImportItem.get_book_from_isbn" - ) as resolve: - resolve.return_value = book - with patch("bookwyrm.importers.importer.handle_imported_book"): - import_data(self.importer.service, import_job.id) - - import_item = models.ImportItem.objects.get(job=import_job, index=0) - self.assertEqual(import_item.book.id, book.id) - def test_handle_imported_book(self, *_): """goodreads import added a book, this adds related connections""" shelf = self.user.shelf_set.filter(identifier="read").first() @@ -137,76 +105,6 @@ class GoodreadsImport(TestCase): self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) - def test_handle_imported_book_already_shelved(self, *_): - """goodreads import added a book, this adds related connections""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - shelf = self.user.shelf_set.filter(identifier="to-read").first() - models.ShelfBook.objects.create( - shelf=shelf, - user=self.user, - book=self.book, - shelved_date=make_date(2020, 2, 2), - ) - - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - handle_imported_book( - self.importer.service, self.user, import_item, False, "public" - ) - - shelf.refresh_from_db() - self.assertEqual(shelf.books.first(), self.book) - self.assertEqual( - shelf.shelfbook_set.first().shelved_date, make_date(2020, 2, 2) - ) - self.assertIsNone(self.user.shelf_set.get(identifier="read").books.first()) - - readthrough = models.ReadThrough.objects.get(user=self.user) - self.assertEqual(readthrough.book, self.book) - self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) - self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) - - def test_handle_import_twice(self, *_): - """re-importing books""" - shelf = self.user.shelf_set.filter(identifier="read").first() - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - handle_imported_book( - self.importer.service, self.user, import_item, False, "public" - ) - handle_imported_book( - self.importer.service, self.user, import_item, False, "public" - ) - - shelf.refresh_from_db() - self.assertEqual(shelf.books.first(), self.book) - self.assertEqual( - shelf.shelfbook_set.first().shelved_date, make_date(2020, 10, 21) - ) - - readthrough = models.ReadThrough.objects.get(user=self.user) - self.assertEqual(readthrough.book, self.book) - self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) - self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) - @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """goodreads review import""" @@ -252,22 +150,3 @@ class GoodreadsImport(TestCase): self.assertEqual(review.rating, 2) self.assertEqual(review.published_date, make_date(2019, 7, 8)) self.assertEqual(review.privacy, "unlisted") - - def test_handle_imported_book_reviews_disabled(self, *_): - """goodreads review import""" - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[2] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book - ) - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - handle_imported_book( - self.importer.service, self.user, import_item, False, "unlisted" - ) - self.assertFalse( - models.Review.objects.filter(book=self.book, user=self.user).exists() - ) diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py new file mode 100644 index 00000000..f5d9af30 --- /dev/null +++ b/bookwyrm/tests/importers/test_importer.py @@ -0,0 +1,289 @@ +""" testing import """ +from collections import namedtuple +import csv +import pathlib +from unittest.mock import patch +import datetime +import pytz + +from django.test import TestCase +import responses + +from bookwyrm import models +from bookwyrm.importers import Importer +from bookwyrm.importers.importer import import_data, handle_imported_book + + +def make_date(*args): + """helper function to easily generate a date obj""" + return datetime.datetime(*args, tzinfo=pytz.UTC) + + +# pylint: disable=consider-using-with +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +class GenericImporter(TestCase): + """importing from csv""" + + def setUp(self): + """use a test csv""" + + class TestImporter(Importer): + """basic importer""" + + mandatory_fields = ["title", "author"] + + def parse_fields(self, entry): + return { + "id": entry["id"], + "Title": entry["title"], + "Author": entry["author"], + "ISBN13": entry["ISBN"], + "Star Rating": entry["rating"], + "My Rating": entry["rating"], + "My Review": entry["review"], + "Exclusive Shelf": entry["shelf"], + "Date Added": entry["added"], + "Date Read": None, + } + + self.importer = TestImporter() + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + self.csv = open(datafile, "r", encoding=self.importer.encoding) + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True + ) + + work = models.Work.objects.create(title="Test Work") + self.book = models.Edition.objects.create( + title="Example Edition", + remote_id="https://example.com/book/1", + parent_work=work, + ) + + def test_create_job(self, *_): + """creates the import job entry and checks csv""" + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + self.assertEqual(import_job.user, self.local_user) + self.assertEqual(import_job.include_reviews, False) + self.assertEqual(import_job.privacy, "public") + + import_items = models.ImportItem.objects.filter(job=import_job).all() + self.assertEqual(len(import_items), 4) + self.assertEqual(import_items[0].index, 0) + self.assertEqual(import_items[0].data["id"], "38") + self.assertEqual(import_items[1].index, 1) + self.assertEqual(import_items[1].data["id"], "48") + self.assertEqual(import_items[2].index, 2) + self.assertEqual(import_items[2].data["id"], "23") + self.assertEqual(import_items[3].index, 3) + self.assertEqual(import_items[3].data["id"], "10") + + def test_create_retry_job(self, *_): + """trying again with items that didn't import""" + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) + import_items = models.ImportItem.objects.filter(job=import_job).all()[:2] + + retry = self.importer.create_retry_job( + self.local_user, import_job, import_items + ) + self.assertNotEqual(import_job, retry) + self.assertEqual(retry.user, self.local_user) + self.assertEqual(retry.include_reviews, False) + self.assertEqual(retry.privacy, "unlisted") + + retry_items = models.ImportItem.objects.filter(job=retry).all() + self.assertEqual(len(retry_items), 2) + self.assertEqual(retry_items[0].index, 0) + self.assertEqual(retry_items[0].data["id"], "38") + self.assertEqual(retry_items[1].index, 1) + self.assertEqual(retry_items[1].data["id"], "48") + + def test_start_import(self, *_): + """check that a task was created""" + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) + MockTask = namedtuple("Task", ("id")) + mock_task = MockTask(7) + with patch("bookwyrm.importers.importer.import_data.delay") as start: + start.return_value = mock_task + self.importer.start_import(import_job) + import_job.refresh_from_db() + self.assertEqual(import_job.task_id, "7") + + @responses.activate + def test_import_data(self, *_): + """resolve entry""" + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) + book = models.Edition.objects.create(title="Test Book") + + with patch( + "bookwyrm.models.import_job.ImportItem.get_book_from_isbn" + ) as resolve: + resolve.return_value = book + with patch("bookwyrm.importers.importer.handle_imported_book"): + import_data(self.importer.service, import_job.id) + + import_item = models.ImportItem.objects.get(job=import_job, index=0) + self.assertEqual(import_item.book.id, book.id) + + def test_handle_imported_book(self, *_): + """import added a book, this adds related connections""" + shelf = self.local_user.shelf_set.filter(identifier="read").first() + self.assertIsNone(shelf.books.first()) + + import_job = models.ImportJob.objects.create(user=self.local_user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + for index, entry in enumerate(list(csv.DictReader(csv_file))): + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=index, data=entry, book=self.book + ) + break + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.local_user, import_item, False, "public" + ) + + shelf.refresh_from_db() + self.assertEqual(shelf.books.first(), self.book) + + def test_handle_imported_book_already_shelved(self, *_): + """import added a book, this adds related connections""" + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + shelf = self.local_user.shelf_set.filter(identifier="to-read").first() + models.ShelfBook.objects.create( + shelf=shelf, + user=self.local_user, + book=self.book, + shelved_date=make_date(2020, 2, 2), + ) + + import_job = models.ImportJob.objects.create(user=self.local_user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + for index, entry in enumerate(list(csv.DictReader(csv_file))): + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=index, data=entry, book=self.book + ) + break + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.local_user, import_item, False, "public" + ) + + shelf.refresh_from_db() + self.assertEqual(shelf.books.first(), self.book) + self.assertEqual( + shelf.shelfbook_set.first().shelved_date, make_date(2020, 2, 2) + ) + self.assertIsNone( + self.local_user.shelf_set.get(identifier="read").books.first() + ) + + def test_handle_import_twice(self, *_): + """re-importing books""" + shelf = self.local_user.shelf_set.filter(identifier="read").first() + import_job = models.ImportJob.objects.create(user=self.local_user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + for index, entry in enumerate(list(csv.DictReader(csv_file))): + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=index, data=entry, book=self.book + ) + break + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.local_user, import_item, False, "public" + ) + handle_imported_book( + self.importer.service, self.local_user, import_item, False, "public" + ) + + shelf.refresh_from_db() + self.assertEqual(shelf.books.first(), self.book) + + @patch("bookwyrm.activitystreams.add_status_task.delay") + def test_handle_imported_book_review(self, *_): + """review import""" + import_job = models.ImportJob.objects.create(user=self.local_user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + entry = list(csv.DictReader(csv_file))[3] + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=0, data=entry, book=self.book + ) + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.Status.broadcast") as broadcast_mock: + handle_imported_book( + self.importer.service, + self.local_user, + import_item, + True, + "unlisted", + ) + kwargs = broadcast_mock.call_args.kwargs + self.assertEqual(kwargs["software"], "bookwyrm") + review = models.Review.objects.get(book=self.book, user=self.local_user) + self.assertEqual(review.content, "mixed feelings") + self.assertEqual(review.rating, 2.0) + self.assertEqual(review.privacy, "unlisted") + + @patch("bookwyrm.activitystreams.add_status_task.delay") + def test_handle_imported_book_rating(self, *_): + """rating import""" + import_job = models.ImportJob.objects.create(user=self.local_user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + entry = list(csv.DictReader(csv_file))[1] + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=0, data=entry, book=self.book + ) + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.local_user, import_item, True, "unlisted" + ) + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) + self.assertIsInstance(review, models.ReviewRating) + self.assertEqual(review.rating, 3.0) + self.assertEqual(review.privacy, "unlisted") + + def test_handle_imported_book_reviews_disabled(self, *_): + """review import""" + import_job = models.ImportJob.objects.create(user=self.local_user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + entry = list(csv.DictReader(csv_file))[2] + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=0, data=entry, book=self.book + ) + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.local_user, import_item, False, "unlisted" + ) + self.assertFalse( + models.Review.objects.filter(book=self.book, user=self.local_user).exists() + ) diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index ab92c11b..f76666a7 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -157,37 +157,6 @@ class LibrarythingImport(TestCase): self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) - def test_handle_import_twice(self, *_): - """re-importing books""" - shelf = self.user.shelf_set.filter(identifier="read").first() - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv") - csv_file = open(datafile, "r", encoding=self.importer.encoding) - for index, entry in enumerate( - list(csv.DictReader(csv_file, delimiter=self.importer.delimiter)) - ): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - handle_imported_book( - self.importer.service, self.user, import_item, False, "public" - ) - handle_imported_book( - self.importer.service, self.user, import_item, False, "public" - ) - - shelf.refresh_from_db() - self.assertEqual(shelf.books.first(), self.book) - - readthrough = models.ReadThrough.objects.get(user=self.user) - self.assertEqual(readthrough.book, self.book) - self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) - self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) - @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """librarything review import""" @@ -209,22 +178,3 @@ class LibrarythingImport(TestCase): self.assertEqual(review.rating, 5) self.assertEqual(review.published_date, make_date(2007, 5, 8)) self.assertEqual(review.privacy, "unlisted") - - def test_handle_imported_book_reviews_disabled(self, *_): - """librarything review import""" - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv") - csv_file = open(datafile, "r", encoding=self.importer.encoding) - entry = list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))[2] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book - ) - - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - handle_imported_book( - self.importer.service, self.user, import_item, False, "unlisted" - ) - self.assertFalse( - models.Review.objects.filter(book=self.book, user=self.user).exists() - ) From aeef472ee1542e01e6d7e0d204faab725c39a43e Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 09:33:00 -0800 Subject: [PATCH 024/121] Fixes flow in checking software for broadcast --- bookwyrm/models/activitypub_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index 031963aa..fb2771ae 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -220,7 +220,7 @@ class ObjectMixin(ActivitypubMixin): try: # do we have a "pure" activitypub version of this for mastodon? - if not software and hasattr(self, "pure_content"): + if not software == "bookwyrm" and hasattr(self, "pure_content"): pure_activity = self.to_create_activity(user, pure=True) self.broadcast(pure_activity, user, software="other") # set bookwyrm so that that type is also sent From 1b9d08414f729857a2ba9080ea86de74cb53033f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 09:53:52 -0800 Subject: [PATCH 025/121] Adds storygraph tests --- bookwyrm/importers/storygraph_import.py | 2 +- bookwyrm/tests/data/generic.csv | 5 + bookwyrm/tests/data/storygraph.csv | 3 + .../tests/importers/test_storygraph_import.py | 122 ++++++++++++++++++ 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 bookwyrm/tests/data/generic.csv create mode 100644 bookwyrm/tests/data/storygraph.csv create mode 100644 bookwyrm/tests/importers/test_storygraph_import.py diff --git a/bookwyrm/importers/storygraph_import.py b/bookwyrm/importers/storygraph_import.py index 25498432..bf532e58 100644 --- a/bookwyrm/importers/storygraph_import.py +++ b/bookwyrm/importers/storygraph_import.py @@ -21,7 +21,7 @@ class StorygraphImporter(Importer): data["ISBN13"] = entry["ISBN"] data["My Review"] = entry["Review"] if entry["Star Rating"]: - data["My Rating"] = math.ceil(float(entry["Star Rating"])) + data["My Rating"] = float(entry["Star Rating"]) else: data["My Rating"] = "" diff --git a/bookwyrm/tests/data/generic.csv b/bookwyrm/tests/data/generic.csv new file mode 100644 index 00000000..a081a642 --- /dev/null +++ b/bookwyrm/tests/data/generic.csv @@ -0,0 +1,5 @@ +id,title,author,ISBN,rating,shelf,review,added +38,Gideon the Ninth (The Locked Tomb #1),Tamsyn Muir,"9781250313195",,read,,2021-11-10 +48,Harrow the Ninth (The Locked Tomb #2),Tamsyn Muir,,3,read,,2021-11-10 +23,Subcutanean,Aaron A. Reed,,,read,,2021-11-10 +10,Patisserie at Home,Mélanie Dupuis,"9780062445315",2,read,"mixed feelings",2021-11-10 diff --git a/bookwyrm/tests/data/storygraph.csv b/bookwyrm/tests/data/storygraph.csv new file mode 100644 index 00000000..4dd0b16e --- /dev/null +++ b/bookwyrm/tests/data/storygraph.csv @@ -0,0 +1,3 @@ +Title,Authors,Contributors,ISBN,Format,Read Status,Date Added,Last Date Read,Dates Read,Read Count,Moods,Pace,Character- or Plot-Driven?,Strong Character Development?,Loveable Characters?,Diverse Characters?,Flawed Characters?,Star Rating,Review,Content Warnings,Content Warning Description,Tags,Owned? +Always Coming Home,"Ursula K. Le Guin, Todd Barton, Margaret Chodos-Irvine","",,,to-read,2021/05/10,"","",0,"",,,,,,,,,"",,"",No +Subprime Attention Crisis,Tim Hwang,"",,,read,2021/05/10,"","",1,informative,fast,,,,,,5.0,"","","","",No diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py new file mode 100644 index 00000000..addf362c --- /dev/null +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -0,0 +1,122 @@ +""" testing import """ +import csv +import pathlib +from unittest.mock import patch +import datetime +import pytz + +from django.test import TestCase + +from bookwyrm import models +from bookwyrm.importers import StorygraphImporter +from bookwyrm.importers.importer import handle_imported_book + + +def make_date(*args): + """helper function to easily generate a date obj""" + return datetime.datetime(*args, tzinfo=pytz.UTC) + + +# pylint: disable=consider-using-with +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +class StorygraphImport(TestCase): + """importing from storygraph csv""" + + def setUp(self): + """use a test csv""" + self.importer = StorygraphImporter() + datafile = pathlib.Path(__file__).parent.joinpath("../data/storygraph.csv") + self.csv = open(datafile, "r", encoding=self.importer.encoding) + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True + ) + + work = models.Work.objects.create(title="Test Work") + self.book = models.Edition.objects.create( + title="Example Edition", + remote_id="https://example.com/book/1", + parent_work=work, + ) + + def test_create_job(self, *_): + """creates the import job entry and checks csv""" + import_job = self.importer.create_job(self.user, self.csv, False, "public") + + import_items = models.ImportItem.objects.filter(job=import_job).all() + self.assertEqual(len(import_items), 2) + self.assertEqual(import_items[0].index, 0) + self.assertEqual(import_items[0].data["Title"], "Always Coming Home") + self.assertEqual(import_items[1].index, 1) + self.assertEqual(import_items[1].data["Title"], "Subprime Attention Crisis") + self.assertEqual(import_items[1].data["My Rating"], 5.0) + + def test_create_retry_job(self, *_): + """trying again with items that didn't import""" + import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") + import_items = models.ImportItem.objects.filter(job=import_job).all()[:2] + + retry = self.importer.create_retry_job(self.user, import_job, import_items) + self.assertNotEqual(import_job, retry) + self.assertEqual(retry.user, self.user) + self.assertEqual(retry.include_reviews, False) + self.assertEqual(retry.privacy, "unlisted") + + retry_items = models.ImportItem.objects.filter(job=retry).all() + self.assertEqual(len(retry_items), 2) + self.assertEqual(retry_items[0].index, 0) + self.assertEqual(retry_items[0].data["Title"], "Always Coming Home") + self.assertEqual(retry_items[1].index, 1) + self.assertEqual(retry_items[1].data["Title"], "Subprime Attention Crisis") + + def test_handle_imported_book(self, *_): + """storygraph import added a book, this adds related connections""" + shelf = self.user.shelf_set.filter(identifier="to-read").first() + self.assertIsNone(shelf.books.first()) + + import_job = models.ImportJob.objects.create(user=self.user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/storygraph.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + for index, entry in enumerate(list(csv.DictReader(csv_file))): + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=index, data=entry, book=self.book + ) + break + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.user, import_item, False, "public" + ) + + shelf.refresh_from_db() + self.assertEqual(shelf.books.first(), self.book) + self.assertEqual( + shelf.shelfbook_set.first().shelved_date, make_date(2021, 5, 10) + ) + + @patch("bookwyrm.activitystreams.add_status_task.delay") + def test_handle_imported_book_rating(self, *_): + """storygraph rating import""" + import_job = models.ImportJob.objects.create(user=self.user) + datafile = pathlib.Path(__file__).parent.joinpath("../data/storygraph.csv") + csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding + entry = list(csv.DictReader(csv_file))[1] + entry = self.importer.parse_fields(entry) + import_item = models.ImportItem.objects.create( + job_id=import_job.id, index=0, data=entry, book=self.book + ) + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + handle_imported_book( + self.importer.service, self.user, import_item, True, "unlisted" + ) + review = models.ReviewRating.objects.get(book=self.book, user=self.user) + self.assertIsInstance(review, models.ReviewRating) + self.assertEqual(review.rating, 5.0) + self.assertEqual(review.published_date, make_date(2021, 5, 10)) + self.assertEqual(review.privacy, "unlisted") From 97a71f5e39ea4cee4d96af9f7f6b3b7971aee27f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 09:55:56 -0800 Subject: [PATCH 026/121] Cleans up software check syntax --- bookwyrm/models/activitypub_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index fb2771ae..0cb8a2cc 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -220,7 +220,7 @@ class ObjectMixin(ActivitypubMixin): try: # do we have a "pure" activitypub version of this for mastodon? - if not software == "bookwyrm" and hasattr(self, "pure_content"): + if software != "bookwyrm" and hasattr(self, "pure_content"): pure_activity = self.to_create_activity(user, pure=True) self.broadcast(pure_activity, user, software="other") # set bookwyrm so that that type is also sent From 4f5d23e785dc8f070bfcf36fe075115a275c03df Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 10:28:43 -0800 Subject: [PATCH 027/121] Replace image_serialzier helper with built-in serializers --- bookwyrm/models/fields.py | 13 ------------- bookwyrm/models/status.py | 12 ++---------- bookwyrm/tests/models/test_fields.py | 9 --------- 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index a490c133..03305f6b 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -383,19 +383,6 @@ class CustomImageField(DjangoImageField): widget = ClearableFileInputWithWarning -def image_serializer(value, alt): - """helper for serializing images""" - if value and hasattr(value, "url"): - url = value.url - else: - return None - if url is not None: - url = url.lstrip("/") - url = urljoin(MEDIA_FULL_URL, url) - - return activitypub.Document(url=url, name=alt) - - class ImageField(ActivitypubFieldMixin, models.ImageField): """activitypub-aware image field""" diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 2b395ec8..a298b132 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -19,7 +19,6 @@ from bookwyrm.settings import ENABLE_PREVIEW_IMAGES from .activitypub_mixin import ActivitypubMixin, ActivityMixin from .activitypub_mixin import OrderedCollectionPageMixin from .base_model import BookWyrmModel -from .fields import image_serializer from .readthrough import ProgressMode from . import fields @@ -190,15 +189,8 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): if hasattr(activity, "name"): activity.name = self.pure_name activity.type = self.pure_type - activity.attachment = [ - image_serializer(b.cover, b.alt_text) - for b in self.mention_books.all()[:4] - if b.cover - ] - if hasattr(self, "book") and self.book.cover: - activity.attachment.append( - image_serializer(self.book.cover, self.book.alt_text) - ) + covers = [b.to_activity().get("cover") for b in [getattr(self, "book", None)] + list(self.mention_books.all()) if b] + activity.attachment = covers return activity def to_activity(self, pure=False): # pylint: disable=arguments-differ diff --git a/bookwyrm/tests/models/test_fields.py b/bookwyrm/tests/models/test_fields.py index 6796f8d3..74f4c48b 100644 --- a/bookwyrm/tests/models/test_fields.py +++ b/bookwyrm/tests/models/test_fields.py @@ -447,15 +447,6 @@ class ModelFields(TestCase): self.assertIsInstance(loaded_image, list) self.assertIsInstance(loaded_image[1], ContentFile) - def test_image_serialize(self, *_): - """make sure we're creating sensible image paths""" - ValueMock = namedtuple("ValueMock", ("url")) - value_mock = ValueMock("https://your.domain.here/images/fish.jpg") - result = fields.image_serializer(value_mock, "hello") - self.assertEqual(result.type, "Document") - self.assertEqual(result.url, "https://your.domain.here/images/fish.jpg") - self.assertEqual(result.name, "hello") - def test_datetime_field(self, *_): """this one is pretty simple, it just has to use isoformat""" instance = fields.DateTimeField() From 9815e9e1009db30b46c814238da949e99708bad2 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 10:30:18 -0800 Subject: [PATCH 028/121] Python formatting --- bookwyrm/models/status.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index a298b132..767841c8 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -189,7 +189,11 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): if hasattr(activity, "name"): activity.name = self.pure_name activity.type = self.pure_type - covers = [b.to_activity().get("cover") for b in [getattr(self, "book", None)] + list(self.mention_books.all()) if b] + covers = [ + b.to_activity().get("cover") + for b in [getattr(self, "book", None)] + list(self.mention_books.all()) + if b + ] activity.attachment = covers return activity From 7e784fa705521be9fa0688276918ca105e5ab20f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 10:35:37 -0800 Subject: [PATCH 029/121] Removes used import --- bookwyrm/importers/storygraph_import.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bookwyrm/importers/storygraph_import.py b/bookwyrm/importers/storygraph_import.py index bf532e58..1333b8b9 100644 --- a/bookwyrm/importers/storygraph_import.py +++ b/bookwyrm/importers/storygraph_import.py @@ -1,6 +1,5 @@ """ handle reading a csv from librarything """ import re -import math from . import Importer From cf477a03aee26373fa4105862100b904bfa17907 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 10:39:51 -0800 Subject: [PATCH 030/121] Corrects broadcast flow for objects --- bookwyrm/models/activitypub_mixin.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index 0cb8a2cc..e1276d46 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -225,10 +225,9 @@ class ObjectMixin(ActivitypubMixin): self.broadcast(pure_activity, user, software="other") # set bookwyrm so that that type is also sent software = "bookwyrm" - if software == "bookwyrm": - # sends to BW only if we just did a pure version for masto - activity = self.to_create_activity(user) - self.broadcast(activity, user, software=software) + # sends to BW only if we just did a pure version for masto + activity = self.to_create_activity(user) + self.broadcast(activity, user, software=software) except AttributeError: # janky as heck, this catches the mutliple inheritence chain # for boosts and ignores this auxilliary broadcast From d61595abb9df3c816a60f60332aa07a3fbdadde8 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 10:50:05 -0800 Subject: [PATCH 031/121] Clearer syntax --- bookwyrm/models/status.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 767841c8..d3fcc254 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -189,10 +189,9 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): if hasattr(activity, "name"): activity.name = self.pure_name activity.type = self.pure_type + books = [getattr(self, "book", None)] + list(self.mention_books.all()) covers = [ - b.to_activity().get("cover") - for b in [getattr(self, "book", None)] + list(self.mention_books.all()) - if b + b.to_activity().get("cover") for b in books if b ] activity.attachment = covers return activity From 717da918cfefdb1bb6586956028490e9ebce7d60 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 10:58:02 -0800 Subject: [PATCH 032/121] Use social media preview images --- bookwyrm/models/fields.py | 24 ++++++++++++------------ bookwyrm/models/status.py | 19 ++++++++++++++++--- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 03305f6b..36107990 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -415,7 +415,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): activity[key] = formatted def field_to_activity(self, value, alt=None): - url = self.get_absolute_url(value) + url = get_absolute_url(value) if not url: return None @@ -456,19 +456,19 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): } ) - # pylint: disable=no-self-use - def get_absolute_url(self, value): - """returns an absolute URL for the image""" - name = getattr(value, "name") - if not name: - return None - url = filepath_to_uri(name) - if url is not None: - url = url.lstrip("/") - url = urljoin(MEDIA_FULL_URL, url) +def get_absolute_url(value): + """returns an absolute URL for the image""" + name = getattr(value, "name") + if not name: + return None - return url + url = filepath_to_uri(name) + if url is not None: + url = url.lstrip("/") + url = urljoin(MEDIA_FULL_URL, url) + + return url class DateTimeField(ActivitypubFieldMixin, models.DateTimeField): diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index d3fcc254..a52af123 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -190,9 +190,22 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): activity.name = self.pure_name activity.type = self.pure_type books = [getattr(self, "book", None)] + list(self.mention_books.all()) - covers = [ - b.to_activity().get("cover") for b in books if b - ] + if len(books) == 1 and books[0].preview_image: + covers = [ + activitypub.Document( + url=fields.get_absolute_url(books[0].preview_image), + name=books[0].alt_text, + ) + ] + else: + covers = [ + activitypub.Document( + url=fields.get_absolute_url(b.cover), + name=b.alt_text, + ) + for b in books + if b and b.cover + ] activity.attachment = covers return activity From 0736c7e160fe544db8f4be07840de2f197af4b68 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 11:10:09 -0800 Subject: [PATCH 033/121] Uses general names for fields in parsed csvs --- bookwyrm/models/import_job.py | 44 ++++++++++++++--------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 22253fef..949e3edb 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -10,14 +10,6 @@ from bookwyrm.models import ReadThrough, User, Book from .fields import PrivacyLevels -# Mapping goodreads -> bookwyrm shelf titles. -GOODREADS_SHELVES = { - "read": "read", - "currently-reading": "reading", - "to-read": "to-read", -} - - def unquote_string(text): """resolve csv quote weirdness""" match = re.match(r'="([^"]*)"', text) @@ -106,56 +98,56 @@ class ImportItem(models.Model): @property def title(self): """get the book title""" - return self.data["Title"] + return self.data["title"] @property def author(self): - """get the book title""" - return self.data["Author"] + """get the book's authors""" + return self.data["authors"] @property def isbn(self): """pulls out the isbn13 field from the csv line data""" - return unquote_string(self.data["ISBN13"]) + return unquote_string(self.data["isbn_13"]) @property def shelf(self): """the goodreads shelf field""" - if self.data["Exclusive Shelf"]: - return GOODREADS_SHELVES.get(self.data["Exclusive Shelf"]) - return None + return self.data.get("shelf") @property def review(self): """a user-written review, to be imported with the book data""" - return self.data["My Review"] + return self.data["review_body"] @property def rating(self): """x/5 star rating for a book""" - if self.data.get("My Rating", None): - return int(self.data["My Rating"]) + if self.data.get("rating"): + return float(self.data["rating"]) return None @property def date_added(self): """when the book was added to this dataset""" - if self.data["Date Added"]: - return timezone.make_aware(dateutil.parser.parse(self.data["Date Added"])) + if self.data.get("date_added"): + return timezone.make_aware(dateutil.parser.parse(self.data["date_added"])) return None @property def date_started(self): """when the book was started""" - if "Date Started" in self.data and self.data["Date Started"]: - return timezone.make_aware(dateutil.parser.parse(self.data["Date Started"])) + if self.data.get("date_started"): + return timezone.make_aware(dateutil.parser.parse(self.data["date_started"])) return None @property def date_read(self): """the date a book was completed""" - if self.data["Date Read"]: - return timezone.make_aware(dateutil.parser.parse(self.data["Date Read"])) + if self.data.get("date_finished"): + return timezone.make_aware( + dateutil.parser.parse(self.data["date_finished"]) + ) return None @property @@ -185,8 +177,8 @@ class ImportItem(models.Model): def __repr__(self): # pylint: disable=consider-using-f-string - return "<{!r}Item {!r}>".format(self.data["import_source"], self.data["Title"]) + return "<{!r}Item {!r}>".format(self.data["import_source"], self.data["title"]) def __str__(self): # pylint: disable=consider-using-f-string - return "{} by {}".format(self.data["Title"], self.data["Author"]) + return "{} by {}".format(self.data["title"], self.data["authors"]) From 4ccd9fc633321970d2ecaf64631bf19282852e69 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 10 Nov 2021 16:49:54 -0800 Subject: [PATCH 034/121] Use generalized mappings to handle import --- bookwyrm/importers/goodreads_import.py | 7 -- bookwyrm/importers/importer.py | 87 ++++++++++++--- bookwyrm/importers/librarything_import.py | 34 +----- bookwyrm/importers/storygraph_import.py | 27 +---- bookwyrm/models/import_job.py | 40 ++++--- bookwyrm/tests/data/generic.csv | 8 +- bookwyrm/tests/importers/test_importer.py | 127 +++++++++------------- 7 files changed, 152 insertions(+), 178 deletions(-) diff --git a/bookwyrm/importers/goodreads_import.py b/bookwyrm/importers/goodreads_import.py index c62e6582..c0dc0ea2 100644 --- a/bookwyrm/importers/goodreads_import.py +++ b/bookwyrm/importers/goodreads_import.py @@ -7,10 +7,3 @@ class GoodreadsImporter(Importer): For a more complete example of overriding see librarything_import.py""" service = "Goodreads" - - def parse_fields(self, entry): - """handle the specific fields in goodreads csvs""" - entry.update({"import_source": self.service}) - # add missing 'Date Started' field - entry.update({"Date Started": None}) - return entry diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index a5243cd3..b0458bab 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -1,5 +1,6 @@ """ handle reading a csv from an external service, defaults are from Goodreads """ import csv +from dataclasses import dataclass import logging from django.utils import timezone @@ -18,30 +19,59 @@ class Importer: service = "Unknown" delimiter = "," encoding = "UTF-8" - mandatory_fields = ["Title", "Author"] + + # these are from Goodreads + row_mappings_guesses = { + "id": ["id", "book id"], + "title": ["title"], + "authors": ["author", "authors", "primary author"], + "isbn_13": ["isbn13", "isbn"], + "isbn_10": ["isbn10", "isbn"], + "shelf": ["shelf", "exclusive shelf", "read status"], + "review_name": [], + "review_body": ["my review"], + "rating": ["my rating", "rating", "star rating"], + "date_added": ["date added", "entry date", "added"], + "date_started": ["date started", "started"], + "date_finished": ["date finished", "last date read", "date read", "finished"], + } def create_job(self, user, csv_file, include_reviews, privacy): """check over a csv and creates a database entry for the job""" + csv_reader = csv.DictReader(csv_file, delimiter=self.delimiter) + rows = enumerate(list(csv_reader)) job = ImportJob.objects.create( - user=user, include_reviews=include_reviews, privacy=privacy + user=user, + include_reviews=include_reviews, + privacy=privacy, + mappings=self.create_row_mappings(csv_reader.fieldnames), ) - for index, entry in enumerate( - list(csv.DictReader(csv_file, delimiter=self.delimiter)) - ): - if not all(x in entry for x in self.mandatory_fields): - raise ValueError("Author and title must be in data.") - entry = self.parse_fields(entry) - self.save_item(job, index, entry) + + for index, entry in rows: + print(index, entry) + self.create_item(job, index, entry) return job - def save_item(self, job, index, data): # pylint: disable=no-self-use - """creates and saves an import item""" - ImportItem(job=job, index=index, data=data).save() + def create_row_mappings(self, headers): + """guess what the headers mean""" + mappings = {} + for (key, guesses) in self.row_mappings_guesses.items(): + value = [h for h in headers if h.lower() in guesses] + value = value[0] if len(value) else None + if value: + headers.remove(value) + mappings[key] = value + return mappings - def parse_fields(self, entry): - """updates csv data with additional info""" - entry.update({"import_source": self.service}) - return entry + def create_item(self, job, index, data): + """creates and saves an import item""" + print(data) + normalized = self.normalize_row(data, job.mappings) + ImportItem(job=job, index=index, data=data, normalized_data=normalized).save() + + def normalize_row(self, entry, mappings): # pylint: disable=no-self-use + """use the dataclass to create the formatted row of data""" + return {k: entry.get(v) for k, v in mappings.items()} def create_retry_job(self, user, original_job, items): """retry items that didn't import""" @@ -49,10 +79,13 @@ class Importer: user=user, include_reviews=original_job.include_reviews, privacy=original_job.privacy, + # TODO: allow users to adjust mappings + mappings=original_job.mappings, retry=True, ) for item in items: - self.save_item(job, item.index, item.data) + # this will re-normalize the raw data + self.create_item(job, item.index, item.data) return job def start_import(self, job): @@ -156,3 +189,23 @@ def handle_imported_book(source, user, item, include_reviews, privacy): ) # only broadcast this review to other bookwyrm instances review.save(software="bookwyrm") + + +@dataclass +class ImportEntry: + """data extracted from a line in a csv""" + + title: str + authors: str = None + isbn_13: str = None + isbn_10: str = None + shelf: str = None + review_name: str = None + review_rating: float = None + review_body: str = None + review_cw: str = None + rating: float = None + date_added: str = None + date_started: str = None + date_finished: str = None + import_source: str = "Unknown" diff --git a/bookwyrm/importers/librarything_import.py b/bookwyrm/importers/librarything_import.py index b3175a82..3d42e539 100644 --- a/bookwyrm/importers/librarything_import.py +++ b/bookwyrm/importers/librarything_import.py @@ -1,7 +1,4 @@ -""" handle reading a csv from librarything """ -import re -import math - +""" handle reading a tsv from librarything """ from . import Importer @@ -11,32 +8,3 @@ class LibrarythingImporter(Importer): service = "LibraryThing" delimiter = "\t" encoding = "ISO-8859-1" - # mandatory_fields : fields matching the book title and author - mandatory_fields = ["Title", "Primary Author"] - - def parse_fields(self, entry): - """custom parsing for librarything""" - data = {} - data["import_source"] = self.service - data["Book Id"] = entry["Book Id"] - data["Title"] = entry["Title"] - data["Author"] = entry["Primary Author"] - data["ISBN13"] = entry["ISBN"] - data["My Review"] = entry["Review"] - if entry["Rating"]: - data["My Rating"] = math.ceil(float(entry["Rating"])) - else: - data["My Rating"] = "" - data["Date Added"] = re.sub(r"\[|\]", "", entry["Entry Date"]) - data["Date Started"] = re.sub(r"\[|\]", "", entry["Date Started"]) - data["Date Read"] = re.sub(r"\[|\]", "", entry["Date Read"]) - - data["Exclusive Shelf"] = None - if data["Date Read"]: - data["Exclusive Shelf"] = "read" - elif data["Date Started"]: - data["Exclusive Shelf"] = "reading" - else: - data["Exclusive Shelf"] = "to-read" - - return data diff --git a/bookwyrm/importers/storygraph_import.py b/bookwyrm/importers/storygraph_import.py index 1333b8b9..9368115d 100644 --- a/bookwyrm/importers/storygraph_import.py +++ b/bookwyrm/importers/storygraph_import.py @@ -1,6 +1,4 @@ -""" handle reading a csv from librarything """ -import re - +""" handle reading a csv from storygraph""" from . import Importer @@ -8,26 +6,3 @@ class StorygraphImporter(Importer): """csv downloads from librarything""" service = "Storygraph" - # mandatory_fields : fields matching the book title and author - mandatory_fields = ["Title"] - - def parse_fields(self, entry): - """custom parsing for storygraph""" - data = {} - data["import_source"] = self.service - data["Title"] = entry["Title"] - data["Author"] = entry["Authors"] if "Authors" in entry else entry["Author"] - data["ISBN13"] = entry["ISBN"] - data["My Review"] = entry["Review"] - if entry["Star Rating"]: - data["My Rating"] = float(entry["Star Rating"]) - else: - data["My Rating"] = "" - - data["Date Added"] = re.sub(r"[/]", "-", entry["Date Added"]) - data["Date Read"] = re.sub(r"[/]", "-", entry["Last Date Read"]) - - data["Exclusive Shelf"] = ( - {"read": "read", "currently-reading": "reading", "to-read": "to-read"} - ).get(entry["Read Status"], None) - return data diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 949e3edb..6bca57f8 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -35,6 +35,7 @@ class ImportJob(models.Model): created_date = models.DateTimeField(default=timezone.now) task_id = models.CharField(max_length=100, null=True) include_reviews = models.BooleanField(default=True) + mappings = models.JSONField() complete = models.BooleanField(default=False) privacy = models.CharField( max_length=255, default="public", choices=PrivacyLevels.choices @@ -48,6 +49,7 @@ class ImportItem(models.Model): job = models.ForeignKey(ImportJob, on_delete=models.CASCADE, related_name="items") index = models.IntegerField() data = models.JSONField() + normalized_data = models.JSONField() book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True, blank=True) book_guess = models.ForeignKey( Book, @@ -98,55 +100,59 @@ class ImportItem(models.Model): @property def title(self): """get the book title""" - return self.data["title"] + return self.normalized_data["title"] @property def author(self): """get the book's authors""" - return self.data["authors"] + return self.normalized_data["authors"] @property def isbn(self): """pulls out the isbn13 field from the csv line data""" - return unquote_string(self.data["isbn_13"]) + return unquote_string(self.normalized_data["isbn_13"]) @property def shelf(self): """the goodreads shelf field""" - return self.data.get("shelf") + return self.normalized_data.get("shelf") @property def review(self): """a user-written review, to be imported with the book data""" - return self.data["review_body"] + return self.normalized_data["review_body"] @property def rating(self): """x/5 star rating for a book""" - if self.data.get("rating"): - return float(self.data["rating"]) + if self.normalized_data.get("rating"): + return float(self.normalized_data["rating"]) return None @property def date_added(self): """when the book was added to this dataset""" - if self.data.get("date_added"): - return timezone.make_aware(dateutil.parser.parse(self.data["date_added"])) + if self.normalized_data.get("date_added"): + return timezone.make_aware( + dateutil.parser.parse(self.normalized_data["date_added"]) + ) return None @property def date_started(self): """when the book was started""" - if self.data.get("date_started"): - return timezone.make_aware(dateutil.parser.parse(self.data["date_started"])) + if self.normalized_data.get("date_started"): + return timezone.make_aware( + dateutil.parser.parse(self.normalized_data["date_started"]) + ) return None @property def date_read(self): """the date a book was completed""" - if self.data.get("date_finished"): + if self.normalized_data.get("date_finished"): return timezone.make_aware( - dateutil.parser.parse(self.data["date_finished"]) + dateutil.parser.parse(self.normalized_data["date_finished"]) ) return None @@ -177,8 +183,12 @@ class ImportItem(models.Model): def __repr__(self): # pylint: disable=consider-using-f-string - return "<{!r}Item {!r}>".format(self.data["import_source"], self.data["title"]) + return "<{!r}Item {!r}>".format( + self.normalized_data["import_source"], self.normalized_data["title"] + ) def __str__(self): # pylint: disable=consider-using-f-string - return "{} by {}".format(self.data["title"], self.data["authors"]) + return "{} by {}".format( + self.normalized_data["title"], self.normalized_data["authors"] + ) diff --git a/bookwyrm/tests/data/generic.csv b/bookwyrm/tests/data/generic.csv index a081a642..9c5b6f02 100644 --- a/bookwyrm/tests/data/generic.csv +++ b/bookwyrm/tests/data/generic.csv @@ -1,5 +1,5 @@ -id,title,author,ISBN,rating,shelf,review,added -38,Gideon the Ninth (The Locked Tomb #1),Tamsyn Muir,"9781250313195",,read,,2021-11-10 -48,Harrow the Ninth (The Locked Tomb #2),Tamsyn Muir,,3,read,,2021-11-10 +id,title,author,ISBN,rating,shelf,review,added,finished +38,Gideon the Ninth,Tamsyn Muir,"9781250313195",,read,,2021-11-10,2021-11-11 +48,Harrow the Ninth,Tamsyn Muir,,3,read,,2021-11-10 23,Subcutanean,Aaron A. Reed,,,read,,2021-11-10 -10,Patisserie at Home,Mélanie Dupuis,"9780062445315",2,read,"mixed feelings",2021-11-10 +10,Patisserie at Home,Mélanie Dupuis,"9780062445315",2,read,"mixed feelings",2021-11-10,2021-11-11 diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index f5d9af30..b2f0284d 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -1,6 +1,5 @@ """ testing import """ from collections import namedtuple -import csv import pathlib from unittest.mock import patch import datetime @@ -29,26 +28,7 @@ class GenericImporter(TestCase): def setUp(self): """use a test csv""" - class TestImporter(Importer): - """basic importer""" - - mandatory_fields = ["title", "author"] - - def parse_fields(self, entry): - return { - "id": entry["id"], - "Title": entry["title"], - "Author": entry["author"], - "ISBN13": entry["ISBN"], - "Star Rating": entry["rating"], - "My Rating": entry["rating"], - "My Review": entry["review"], - "Exclusive Shelf": entry["shelf"], - "Date Added": entry["added"], - "Date Read": None, - } - - self.importer = TestImporter() + self.importer = Importer() datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") self.csv = open(datafile, "r", encoding=self.importer.encoding) with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( @@ -77,13 +57,24 @@ class GenericImporter(TestCase): import_items = models.ImportItem.objects.filter(job=import_job).all() self.assertEqual(len(import_items), 4) self.assertEqual(import_items[0].index, 0) - self.assertEqual(import_items[0].data["id"], "38") + self.assertEqual(import_items[0].normalized_data["id"], "38") + self.assertEqual(import_items[0].normalized_data["title"], "Gideon the Ninth") + self.assertEqual(import_items[0].normalized_data["authors"], "Tamsyn Muir") + self.assertEqual(import_items[0].normalized_data["isbn_13"], "9781250313195") + self.assertIsNone(import_items[0].normalized_data["isbn_10"]) + self.assertEqual(import_items[0].normalized_data["shelf"], "read") + self.assertEqual(import_items[1].index, 1) - self.assertEqual(import_items[1].data["id"], "48") + self.assertEqual(import_items[1].normalized_data["id"], "48") + self.assertEqual(import_items[1].normalized_data["title"], "Harrow the Ninth") + self.assertEqual(import_items[2].index, 2) - self.assertEqual(import_items[2].data["id"], "23") + self.assertEqual(import_items[2].normalized_data["id"], "23") + self.assertEqual(import_items[2].normalized_data["title"], "Subcutanean") + self.assertEqual(import_items[3].index, 3) - self.assertEqual(import_items[3].data["id"], "10") + self.assertEqual(import_items[3].normalized_data["id"], "10") + self.assertEqual(import_items[3].normalized_data["title"], "Patisserie at Home") def test_create_retry_job(self, *_): """trying again with items that didn't import""" @@ -103,9 +94,9 @@ class GenericImporter(TestCase): retry_items = models.ImportItem.objects.filter(job=retry).all() self.assertEqual(len(retry_items), 2) self.assertEqual(retry_items[0].index, 0) - self.assertEqual(retry_items[0].data["id"], "38") + self.assertEqual(retry_items[0].normalized_data["id"], "38") self.assertEqual(retry_items[1].index, 1) - self.assertEqual(retry_items[1].data["id"], "48") + self.assertEqual(retry_items[1].normalized_data["id"], "48") def test_start_import(self, *_): """check that a task was created""" @@ -143,15 +134,12 @@ class GenericImporter(TestCase): shelf = self.local_user.shelf_set.filter(identifier="read").first() self.assertIsNone(shelf.books.first()) - import_job = models.ImportJob.objects.create(user=self.local_user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( @@ -172,15 +160,12 @@ class GenericImporter(TestCase): shelved_date=make_date(2020, 2, 2), ) - import_job = models.ImportJob.objects.create(user=self.local_user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( @@ -199,15 +184,12 @@ class GenericImporter(TestCase): def test_handle_import_twice(self, *_): """re-importing books""" shelf = self.local_user.shelf_set.filter(identifier="read").first() - import_job = models.ImportJob.objects.create(user=self.local_user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( @@ -219,18 +201,15 @@ class GenericImporter(TestCase): shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) + self.assertEqual(models.ReadThrough.objects.count(), 1) @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """review import""" - import_job = models.ImportJob.objects.create(user=self.local_user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[3] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book - ) + import_job = self.importer.create_job(self.local_user, self.csv, True, "public") + import_item = import_job.items.filter(index=3).first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.Status.broadcast") as broadcast_mock: @@ -251,14 +230,12 @@ class GenericImporter(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_rating(self, *_): """rating import""" - import_job = models.ImportJob.objects.create(user=self.local_user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[1] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" ) + import_item = import_job.items.filter(index=1).first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( @@ -271,14 +248,12 @@ class GenericImporter(TestCase): def test_handle_imported_book_reviews_disabled(self, *_): """review import""" - import_job = models.ImportJob.objects.create(user=self.local_user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[2] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" ) + import_item = import_job.items.filter(index=3).first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( From 20baf9385d2253a328a69cf599a4b49e0dbf8b72 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 09:21:28 -0800 Subject: [PATCH 035/121] Updates goodreads tests --- bookwyrm/importers/importer.py | 6 +- bookwyrm/tests/data/goodreads-rating.csv | 5 - bookwyrm/tests/data/goodreads.csv | 2 +- .../tests/importers/test_goodreads_import.py | 76 +++++++-------- .../importers/test_librarything_import.py | 92 +++++++++---------- .../tests/importers/test_storygraph_import.py | 51 +++++----- 6 files changed, 109 insertions(+), 123 deletions(-) delete mode 100644 bookwyrm/tests/data/goodreads-rating.csv diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index b0458bab..f6abef0e 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -28,8 +28,8 @@ class Importer: "isbn_13": ["isbn13", "isbn"], "isbn_10": ["isbn10", "isbn"], "shelf": ["shelf", "exclusive shelf", "read status"], - "review_name": [], - "review_body": ["my review"], + "review_name": ["review name"], + "review_body": ["my review", "review"], "rating": ["my rating", "rating", "star rating"], "date_added": ["date added", "entry date", "added"], "date_started": ["date started", "started"], @@ -48,7 +48,6 @@ class Importer: ) for index, entry in rows: - print(index, entry) self.create_item(job, index, entry) return job @@ -65,7 +64,6 @@ class Importer: def create_item(self, job, index, data): """creates and saves an import item""" - print(data) normalized = self.normalize_row(data, job.mappings) ImportItem(job=job, index=index, data=data, normalized_data=normalized).save() diff --git a/bookwyrm/tests/data/goodreads-rating.csv b/bookwyrm/tests/data/goodreads-rating.csv deleted file mode 100644 index fec0c77d..00000000 --- a/bookwyrm/tests/data/goodreads-rating.csv +++ /dev/null @@ -1,5 +0,0 @@ -Book Id,Title,Author,Author l-f,Additional Authors,ISBN,ISBN13,My Rating,Average Rating,Publisher,Binding,Number of Pages,Year Published,Original Publication Year,Date Read,Date Added,Bookshelves,Bookshelves with positions,Exclusive Shelf,My Review,Spoiler,Private Notes,Read Count,Recommended For,Recommended By,Owned Copies,Original Purchase Date,Original Purchase Location,Condition,Condition Description,BCID -42036538,Gideon the Ninth (The Locked Tomb #1),Tamsyn Muir,"Muir, Tamsyn",,"=""1250313198""","=""9781250313195""",0,4.20,Tor,Hardcover,448,2019,2019,2020/10/25,2020/10/21,,,read,,,,1,,,0,,,,, -52691223,Subcutanean,Aaron A. Reed,"Reed, Aaron A.",,"=""""","=""""",0,4.45,,Paperback,232,2020,,2020/03/06,2020/03/05,,,read,,,,1,,,0,,,,, -28694510,Patisserie at Home,Mélanie Dupuis,"Dupuis, Mélanie",Anne Cazor,"=""0062445316""","=""9780062445315""",2,4.60,Harper Design,Hardcover,288,2016,,,2019/07/08,,,read,,,,2,,,0,,,,, - diff --git a/bookwyrm/tests/data/goodreads.csv b/bookwyrm/tests/data/goodreads.csv index 5f124edc..a0a8232e 100644 --- a/bookwyrm/tests/data/goodreads.csv +++ b/bookwyrm/tests/data/goodreads.csv @@ -1,4 +1,4 @@ Book Id,Title,Author,Author l-f,Additional Authors,ISBN,ISBN13,My Rating,Average Rating,Publisher,Binding,Number of Pages,Year Published,Original Publication Year,Date Read,Date Added,Bookshelves,Bookshelves with positions,Exclusive Shelf,My Review,Spoiler,Private Notes,Read Count,Recommended For,Recommended By,Owned Copies,Original Purchase Date,Original Purchase Location,Condition,Condition Description,BCID -42036538,Gideon the Ninth (The Locked Tomb #1),Tamsyn Muir,"Muir, Tamsyn",,"=""1250313198""","=""9781250313195""",0,4.20,Tor,Hardcover,448,2019,2019,2020/10/25,2020/10/21,,,read,,,,1,,,0,,,,, +42036538,Gideon the Ninth (The Locked Tomb #1),Tamsyn Muir,"Muir, Tamsyn",,"=""1250313198""","=""9781250313195""",3,4.20,Tor,Hardcover,448,2019,2019,2020/10/25,2020/10/21,,,read,,,,1,,,0,,,,, 52691223,Subcutanean,Aaron A. Reed,"Reed, Aaron A.",,"=""""","=""""",0,4.45,,Paperback,232,2020,,2020/03/06,2020/03/05,,,read,,,,1,,,0,,,,, 28694510,Patisserie at Home,Mélanie Dupuis,"Dupuis, Mélanie",Anne Cazor,"=""0062445316""","=""9780062445315""",2,4.60,Harper Design,Hardcover,288,2016,,,2019/07/08,,,read,"mixed feelings",,,2,,,0,,,,, diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index a2451292..87624a0e 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -1,5 +1,4 @@ """ testing import """ -import csv import pathlib from unittest.mock import patch import datetime @@ -32,7 +31,7 @@ class GoodreadsImport(TestCase): with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( "bookwyrm.activitystreams.populate_stream_task.delay" ): - self.user = models.User.objects.create_user( + self.local_user = models.User.objects.create_user( "mouse", "mouse@mouse.mouse", "password", local=True ) @@ -45,7 +44,9 @@ class GoodreadsImport(TestCase): def test_create_job(self, *_): """creates the import job entry and checks csv""" - import_job = self.importer.create_job(self.user, self.csv, False, "public") + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) import_items = models.ImportItem.objects.filter(job=import_job).all() self.assertEqual(len(import_items), 3) @@ -58,12 +59,16 @@ class GoodreadsImport(TestCase): def test_create_retry_job(self, *_): """trying again with items that didn't import""" - import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) import_items = models.ImportItem.objects.filter(job=import_job).all()[:2] - retry = self.importer.create_retry_job(self.user, import_job, import_items) + retry = self.importer.create_retry_job( + self.local_user, import_job, import_items + ) self.assertNotEqual(import_job, retry) - self.assertEqual(retry.user, self.user) + self.assertEqual(retry.user, self.local_user) self.assertEqual(retry.include_reviews, False) self.assertEqual(retry.privacy, "unlisted") @@ -76,22 +81,19 @@ class GoodreadsImport(TestCase): def test_handle_imported_book(self, *_): """goodreads import added a book, this adds related connections""" - shelf = self.user.shelf_set.filter(identifier="read").first() + shelf = self.local_user.shelf_set.filter(identifier="read").first() self.assertIsNone(shelf.books.first()) - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, False, "public" + self.importer.service, self.local_user, import_item, False, "public" ) shelf.refresh_from_db() @@ -100,7 +102,7 @@ class GoodreadsImport(TestCase): shelf.shelfbook_set.first().shelved_date, make_date(2020, 10, 21) ) - readthrough = models.ReadThrough.objects.get(user=self.user) + readthrough = models.ReadThrough.objects.get(user=self.local_user) self.assertEqual(readthrough.book, self.book) self.assertEqual(readthrough.start_date, make_date(2020, 10, 21)) self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25)) @@ -108,20 +110,16 @@ class GoodreadsImport(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """goodreads review import""" - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[2] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book - ) + import_job = self.importer.create_job(self.local_user, self.csv, True, "public") + import_item = import_job.items.get(index=2) + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, True, "unlisted" + self.importer.service, self.local_user, import_item, True, "unlisted" ) - review = models.Review.objects.get(book=self.book, user=self.user) + review = models.Review.objects.get(book=self.book, user=self.local_user) self.assertEqual(review.content, "mixed feelings") self.assertEqual(review.rating, 2) self.assertEqual(review.published_date, make_date(2019, 7, 8)) @@ -130,23 +128,19 @@ class GoodreadsImport(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_rating(self, *_): """goodreads rating import""" - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath( - "../data/goodreads-rating.csv" - ) - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[2] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" ) + import_item = import_job.items.filter(index=0).first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, True, "unlisted" + self.importer.service, self.local_user, import_item, True, "unlisted" ) - review = models.ReviewRating.objects.get(book=self.book, user=self.user) + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) self.assertIsInstance(review, models.ReviewRating) - self.assertEqual(review.rating, 2) - self.assertEqual(review.published_date, make_date(2019, 7, 8)) + self.assertEqual(review.rating, 3) + self.assertEqual(review.published_date, make_date(2020, 10, 25)) self.assertEqual(review.privacy, "unlisted") diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index f76666a7..00bb0ffa 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -35,7 +35,7 @@ class LibrarythingImport(TestCase): with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( "bookwyrm.activitystreams.populate_stream_task.delay" ): - self.user = models.User.objects.create_user( + self.local_user = models.User.objects.create_user( "mmai", "mmai@mmai.mmai", "password", local=True ) work = models.Work.objects.create(title="Test Work") @@ -47,8 +47,10 @@ class LibrarythingImport(TestCase): def test_create_job(self, *_): """creates the import job entry and checks csv""" - import_job = self.importer.create_job(self.user, self.csv, False, "public") - self.assertEqual(import_job.user, self.user) + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + self.assertEqual(import_job.user, self.local_user) self.assertEqual(import_job.include_reviews, False) self.assertEqual(import_job.privacy, "public") @@ -63,12 +65,16 @@ class LibrarythingImport(TestCase): def test_create_retry_job(self, *_): """trying again with items that didn't import""" - import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) import_items = models.ImportItem.objects.filter(job=import_job).all()[:2] - retry = self.importer.create_retry_job(self.user, import_job, import_items) + retry = self.importer.create_retry_job( + self.local_user, import_job, import_items + ) self.assertNotEqual(import_job, retry) - self.assertEqual(retry.user, self.user) + self.assertEqual(retry.user, self.local_user) self.assertEqual(retry.include_reviews, False) self.assertEqual(retry.privacy, "unlisted") @@ -82,7 +88,9 @@ class LibrarythingImport(TestCase): @responses.activate def test_import_data(self, *_): """resolve entry""" - import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) book = models.Edition.objects.create(title="Test Book") with patch( @@ -97,30 +105,25 @@ class LibrarythingImport(TestCase): def test_handle_imported_book(self, *_): """librarything import added a book, this adds related connections""" - shelf = self.user.shelf_set.filter(identifier="read").first() + shelf = self.local_user.shelf_set.filter(identifier="read").first() self.assertIsNone(shelf.books.first()) - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv") - csv_file = open(datafile, "r", encoding=self.importer.encoding) - for index, entry in enumerate( - list(csv.DictReader(csv_file, delimiter=self.importer.delimiter)) - ): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, False, "public" + self.importer.service, self.local_user, import_item, False, "public" ) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) - readthrough = models.ReadThrough.objects.get(user=self.user) + readthrough = models.ReadThrough.objects.get(user=self.local_user) self.assertEqual(readthrough.book, self.book) self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) @@ -128,31 +131,30 @@ class LibrarythingImport(TestCase): def test_handle_imported_book_already_shelved(self, *_): """librarything import added a book, this adds related connections""" with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - shelf = self.user.shelf_set.filter(identifier="to-read").first() - models.ShelfBook.objects.create(shelf=shelf, user=self.user, book=self.book) - - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv") - csv_file = open(datafile, "r", encoding=self.importer.encoding) - for index, entry in enumerate( - list(csv.DictReader(csv_file, delimiter=self.importer.delimiter)) - ): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book + shelf = self.local_user.shelf_set.filter(identifier="to-read").first() + models.ShelfBook.objects.create( + shelf=shelf, user=self.local_user, book=self.book ) - break + + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, False, "public" + self.importer.service, self.local_user, import_item, False, "public" ) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) - self.assertIsNone(self.user.shelf_set.get(identifier="read").books.first()) + self.assertIsNone( + self.local_user.shelf_set.get(identifier="read").books.first() + ) - readthrough = models.ReadThrough.objects.get(user=self.user) + readthrough = models.ReadThrough.objects.get(user=self.local_user) self.assertEqual(readthrough.book, self.book) self.assertEqual(readthrough.start_date, make_date(2007, 4, 16)) self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8)) @@ -160,20 +162,16 @@ class LibrarythingImport(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """librarything review import""" - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv") - csv_file = open(datafile, "r", encoding=self.importer.encoding) - entry = list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))[0] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book - ) + import_job = self.importer.create_job(self.local_user, self.csv, True, "public") + import_item = import_job.items.filter(index=0).first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, True, "unlisted" + self.importer.service, self.local_user, import_item, True, "unlisted" ) - review = models.Review.objects.get(book=self.book, user=self.user) + review = models.Review.objects.get(book=self.book, user=self.local_user) self.assertEqual(review.content, "chef d'oeuvre") self.assertEqual(review.rating, 5) self.assertEqual(review.published_date, make_date(2007, 5, 8)) diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py index addf362c..91a5dfa6 100644 --- a/bookwyrm/tests/importers/test_storygraph_import.py +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -32,7 +32,7 @@ class StorygraphImport(TestCase): with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( "bookwyrm.activitystreams.populate_stream_task.delay" ): - self.user = models.User.objects.create_user( + self.local_user = models.User.objects.create_user( "mouse", "mouse@mouse.mouse", "password", local=True ) @@ -45,7 +45,9 @@ class StorygraphImport(TestCase): def test_create_job(self, *_): """creates the import job entry and checks csv""" - import_job = self.importer.create_job(self.user, self.csv, False, "public") + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) import_items = models.ImportItem.objects.filter(job=import_job).all() self.assertEqual(len(import_items), 2) @@ -57,12 +59,16 @@ class StorygraphImport(TestCase): def test_create_retry_job(self, *_): """trying again with items that didn't import""" - import_job = self.importer.create_job(self.user, self.csv, False, "unlisted") + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) import_items = models.ImportItem.objects.filter(job=import_job).all()[:2] - retry = self.importer.create_retry_job(self.user, import_job, import_items) + retry = self.importer.create_retry_job( + self.local_user, import_job, import_items + ) self.assertNotEqual(import_job, retry) - self.assertEqual(retry.user, self.user) + self.assertEqual(retry.user, self.local_user) self.assertEqual(retry.include_reviews, False) self.assertEqual(retry.privacy, "unlisted") @@ -75,22 +81,19 @@ class StorygraphImport(TestCase): def test_handle_imported_book(self, *_): """storygraph import added a book, this adds related connections""" - shelf = self.user.shelf_set.filter(identifier="to-read").first() + shelf = self.local_user.shelf_set.filter(identifier="to-read").first() self.assertIsNone(shelf.books.first()) - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/storygraph.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - for index, entry in enumerate(list(csv.DictReader(csv_file))): - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=index, data=entry, book=self.book - ) - break + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" + ) + import_item = import_job.items.first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, False, "public" + self.importer.service, self.local_user, import_item, False, "public" ) shelf.refresh_from_db() @@ -102,20 +105,18 @@ class StorygraphImport(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_rating(self, *_): """storygraph rating import""" - import_job = models.ImportJob.objects.create(user=self.user) - datafile = pathlib.Path(__file__).parent.joinpath("../data/storygraph.csv") - csv_file = open(datafile, "r") # pylint: disable=unspecified-encoding - entry = list(csv.DictReader(csv_file))[1] - entry = self.importer.parse_fields(entry) - import_item = models.ImportItem.objects.create( - job_id=import_job.id, index=0, data=entry, book=self.book + import_job = self.importer.create_job( + self.local_user, self.csv, False, "public" ) + import_item = import_job.items.filter(index=1).first() + import_item.book = self.book + import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): handle_imported_book( - self.importer.service, self.user, import_item, True, "unlisted" + self.importer.service, self.local_user, import_item, True, "unlisted" ) - review = models.ReviewRating.objects.get(book=self.book, user=self.user) + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) self.assertIsInstance(review, models.ReviewRating) self.assertEqual(review.rating, 5.0) self.assertEqual(review.published_date, make_date(2021, 5, 10)) From 4d574a3536c88d1a51307b4e288ef2185ed71b13 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 09:54:36 -0800 Subject: [PATCH 036/121] Process dates in librarything import --- bookwyrm/importers/importer.py | 1 + bookwyrm/importers/librarything_import.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index f6abef0e..8acfb147 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -35,6 +35,7 @@ class Importer: "date_started": ["date started", "started"], "date_finished": ["date finished", "last date read", "date read", "finished"], } + date_fields = ["date_added", "date_started", "date_finished"] def create_job(self, user, csv_file, include_reviews, privacy): """check over a csv and creates a database entry for the job""" diff --git a/bookwyrm/importers/librarything_import.py b/bookwyrm/importers/librarything_import.py index 3d42e539..12d841e9 100644 --- a/bookwyrm/importers/librarything_import.py +++ b/bookwyrm/importers/librarything_import.py @@ -1,4 +1,5 @@ """ handle reading a tsv from librarything """ +import re from . import Importer @@ -8,3 +9,11 @@ class LibrarythingImporter(Importer): service = "LibraryThing" delimiter = "\t" encoding = "ISO-8859-1" + + def normalize_row(self, entry, mappings): # pylint: disable=no-self-use + """use the dataclass to create the formatted row of data""" + normalized = {k: entry.get(v) for k, v in mappings.items()} + for date_field in self.date_fields: + date = normalized[date_field] + normalized[date_field] = re.sub(r"\[|\]", "", date) + return normalized From a95e031140bc188e172523add8d201034f75f534 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 11:33:12 -0800 Subject: [PATCH 037/121] Validate html in unit tests --- bookwyrm/tests/views/test_import.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bookwyrm/tests/views/test_import.py b/bookwyrm/tests/views/test_import.py index 2027d284..b29190e7 100644 --- a/bookwyrm/tests/views/test_import.py +++ b/bookwyrm/tests/views/test_import.py @@ -5,6 +5,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory +from bookwyrm.tests.validate_html import validate_html from bookwyrm import forms, models, views @@ -34,7 +35,7 @@ class ImportViews(TestCase): request.user = self.local_user result = view(request) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_import_status(self): @@ -47,7 +48,7 @@ class ImportViews(TestCase): async_result.return_value = [] result = view(request, import_job.id) self.assertIsInstance(result, TemplateResponse) - result.render() + validate_html(result.render()) self.assertEqual(result.status_code, 200) def test_start_import(self): @@ -58,7 +59,9 @@ class ImportViews(TestCase): form.data["privacy"] = "public" form.data["include_reviews"] = False csv_file = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - form.data["csv_file"] = SimpleUploadedFile( + form.data[ + "csv_file" + ] = SimpleUploadedFile( # pylint: disable=consider-using-with csv_file, open(csv_file, "rb").read(), content_type="text/csv" ) From 147dd95e8d00a209ac1af1f87f36175a8da1afca Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 11:59:15 -0800 Subject: [PATCH 038/121] Removes unused import --- bookwyrm/tests/importers/test_librarything_import.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index 00bb0ffa..aa52e2f1 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -1,5 +1,4 @@ """ testing import """ -import csv import pathlib from unittest.mock import patch import datetime From f3bcced0a019b119e02d2d191af41f49f9a65af8 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 12:29:38 -0800 Subject: [PATCH 039/121] Adds shelf mappings --- bookwyrm/importers/importer.py | 12 ++++++++++++ bookwyrm/importers/librarything_import.py | 7 +++++++ bookwyrm/tests/importers/test_librarything_import.py | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 8acfb147..0233ee3d 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -36,6 +36,11 @@ class Importer: "date_finished": ["date finished", "last date read", "date read", "finished"], } date_fields = ["date_added", "date_started", "date_finished"] + shelf_mapping_guesses = { + "to-read": ["to-read"], + "read": ["read"], + "reading": ["currently-reading", "reading"], + } def create_job(self, user, csv_file, include_reviews, privacy): """check over a csv and creates a database entry for the job""" @@ -66,8 +71,15 @@ class Importer: def create_item(self, job, index, data): """creates and saves an import item""" normalized = self.normalize_row(data, job.mappings) + normalized["shelf"] = self.get_shelf(normalized) ImportItem(job=job, index=index, data=data, normalized_data=normalized).save() + def get_shelf(self, normalized_row): + """determine which shelf to use""" + shelf_name = normalized_row["shelf"] + shelf = [s for (s, gs) in self.shelf_mapping_guesses if shelf_name in gs] + return shelf[0] if shelf else None + def normalize_row(self, entry, mappings): # pylint: disable=no-self-use """use the dataclass to create the formatted row of data""" return {k: entry.get(v) for k, v in mappings.items()} diff --git a/bookwyrm/importers/librarything_import.py b/bookwyrm/importers/librarything_import.py index 12d841e9..d6426de6 100644 --- a/bookwyrm/importers/librarything_import.py +++ b/bookwyrm/importers/librarything_import.py @@ -17,3 +17,10 @@ class LibrarythingImporter(Importer): date = normalized[date_field] normalized[date_field] = re.sub(r"\[|\]", "", date) return normalized + + def get_shelf(self, normalized_row): + if normalized_row["date_finished"]: + return "read" + if normalized_row["date_started"]: + return "reading" + return "to-read" diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index aa52e2f1..e9352896 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -172,6 +172,6 @@ class LibrarythingImport(TestCase): ) review = models.Review.objects.get(book=self.book, user=self.local_user) self.assertEqual(review.content, "chef d'oeuvre") - self.assertEqual(review.rating, 5) + self.assertEqual(review.rating, 4.5) self.assertEqual(review.published_date, make_date(2007, 5, 8)) self.assertEqual(review.privacy, "unlisted") From 59678348053f43f83912fa4e4ea11f046844f534 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 12:29:51 -0800 Subject: [PATCH 040/121] Adds migration --- .../migrations/0113_auto_20211110_2104.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 bookwyrm/migrations/0113_auto_20211110_2104.py diff --git a/bookwyrm/migrations/0113_auto_20211110_2104.py b/bookwyrm/migrations/0113_auto_20211110_2104.py new file mode 100644 index 00000000..572ba280 --- /dev/null +++ b/bookwyrm/migrations/0113_auto_20211110_2104.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.5 on 2021-11-10 21:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0112_auto_20211022_0844"), + ] + + operations = [ + migrations.AddField( + model_name="importitem", + name="normalized_data", + field=models.JSONField(default={}), + preserve_default=False, + ), + migrations.AddField( + model_name="importjob", + name="mappings", + field=models.JSONField(default={}), + preserve_default=False, + ), + ] From efcf7824dd47de77c3cc2e0bcc972bbd131b05af Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 12:39:12 -0800 Subject: [PATCH 041/121] iUpdates storygraph tests --- bookwyrm/importers/importer.py | 4 +++- bookwyrm/tests/importers/test_storygraph_import.py | 14 +++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 0233ee3d..cf7841e6 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -77,7 +77,9 @@ class Importer: def get_shelf(self, normalized_row): """determine which shelf to use""" shelf_name = normalized_row["shelf"] - shelf = [s for (s, gs) in self.shelf_mapping_guesses if shelf_name in gs] + shelf = [ + s for (s, gs) in self.shelf_mapping_guesses.items() if shelf_name in gs + ] return shelf[0] if shelf else None def normalize_row(self, entry, mappings): # pylint: disable=no-self-use diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py index 91a5dfa6..2f0fd7ef 100644 --- a/bookwyrm/tests/importers/test_storygraph_import.py +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -52,10 +52,12 @@ class StorygraphImport(TestCase): import_items = models.ImportItem.objects.filter(job=import_job).all() self.assertEqual(len(import_items), 2) self.assertEqual(import_items[0].index, 0) - self.assertEqual(import_items[0].data["Title"], "Always Coming Home") + self.assertEqual(import_items[0].normalized_data["title"], "Always Coming Home") self.assertEqual(import_items[1].index, 1) - self.assertEqual(import_items[1].data["Title"], "Subprime Attention Crisis") - self.assertEqual(import_items[1].data["My Rating"], 5.0) + self.assertEqual( + import_items[1].normalized_data["title"], "Subprime Attention Crisis" + ) + self.assertEqual(import_items[1].normalized_data["rating"], "5.0") def test_create_retry_job(self, *_): """trying again with items that didn't import""" @@ -75,9 +77,11 @@ class StorygraphImport(TestCase): retry_items = models.ImportItem.objects.filter(job=retry).all() self.assertEqual(len(retry_items), 2) self.assertEqual(retry_items[0].index, 0) - self.assertEqual(retry_items[0].data["Title"], "Always Coming Home") + self.assertEqual(retry_items[0].normalized_data["title"], "Always Coming Home") self.assertEqual(retry_items[1].index, 1) - self.assertEqual(retry_items[1].data["Title"], "Subprime Attention Crisis") + self.assertEqual( + retry_items[1].normalized_data["title"], "Subprime Attention Crisis" + ) def test_handle_imported_book(self, *_): """storygraph import added a book, this adds related connections""" From d807774c2d00fa5c3320b7be473330e4aee86161 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 12:53:48 -0800 Subject: [PATCH 042/121] Fixes label for privacy field --- bookwyrm/templates/import/import.html | 6 +++--- bookwyrm/tests/views/test_import.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bookwyrm/templates/import/import.html b/bookwyrm/templates/import/import.html index 81f0daa5..314a6861 100644 --- a/bookwyrm/templates/import/import.html +++ b/bookwyrm/templates/import/import.html @@ -46,10 +46,10 @@
-
diff --git a/bookwyrm/tests/views/test_import.py b/bookwyrm/tests/views/test_import.py index b29190e7..29f4902d 100644 --- a/bookwyrm/tests/views/test_import.py +++ b/bookwyrm/tests/views/test_import.py @@ -61,7 +61,8 @@ class ImportViews(TestCase): csv_file = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") form.data[ "csv_file" - ] = SimpleUploadedFile( # pylint: disable=consider-using-with + ] = SimpleUploadedFile( + # pylint: disable=consider-using-with csv_file, open(csv_file, "rb").read(), content_type="text/csv" ) From c744faf393c1b354781e65a3a28927176b7f07fd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 13:00:34 -0800 Subject: [PATCH 043/121] Fixes dictionary list html validity --- bookwyrm/templates/import/import_status.html | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 1c073944..91e48e34 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -10,18 +10,17 @@

{% trans "Import Status" %}

{% trans "Back to imports" %} + {% if task.failed %} +
{% trans "TASK FAILED" %}
+ {% endif %} +
-
-
{% trans "Import started:" %}
-
{{ job.created_date | naturaltime }}
-
+
{% trans "Import started:" %}
+
{{ job.created_date | naturaltime }}
+ {% if job.complete %} -
-
{% trans "Import completed:" %}
-
{{ task.date_done | naturaltime }}
-
- {% elif task.failed %} -
{% trans "TASK FAILED" %}
+
{% trans "Import completed:" %}
+
{{ task.date_done | naturaltime }}
{% endif %}
From 8c4e8361f2c69041c58dd28889da1405578a5e18 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 13:35:56 -0800 Subject: [PATCH 044/121] Fixes tests --- bookwyrm/tests/models/test_status_model.py | 50 ++++++++++++++-------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index f5013422..7d0dd138 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -2,7 +2,7 @@ from unittest.mock import patch from io import BytesIO import pathlib -from urllib.parse import urljoin +import re from django.http import Http404 from django.core.files.base import ContentFile @@ -191,9 +191,11 @@ class Status(TestCase): self.assertEqual(activity["sensitive"], False) self.assertIsInstance(activity["attachment"], list) self.assertEqual(activity["attachment"][0].type, "Document") - self.assertEqual( - activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), + self.assertTrue( + re.match( + r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", + activity["attachment"][0].url, + ) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -221,9 +223,11 @@ class Status(TestCase): f'test content

(comment on "Test Edition")

', ) self.assertEqual(activity["attachment"][0].type, "Document") - self.assertEqual( - activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), + self.assertTrue( + re.match( + r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", + activity["attachment"][0].url, + ) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -258,9 +262,11 @@ class Status(TestCase): f'a sickening sense

-- "Test Edition"

test content', ) self.assertEqual(activity["attachment"][0].type, "Document") - self.assertEqual( - activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), + self.assertTrue( + re.match( + r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", + activity["attachment"][0].url, + ) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -299,9 +305,11 @@ class Status(TestCase): ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document") - self.assertEqual( - activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), + self.assertTrue( + re.match( + r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", + activity["attachment"][0].url, + ) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -321,9 +329,11 @@ class Status(TestCase): ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document") - self.assertEqual( - activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), + self.assertTrue( + re.match( + r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", + activity["attachment"][0].url, + ) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") @@ -342,9 +352,11 @@ class Status(TestCase): f'rated {self.book.title}: 3 stars', ) self.assertEqual(activity["attachment"][0].type, "Document") - self.assertEqual( - activity["attachment"][0].url, - urljoin(settings.MEDIA_FULL_URL, self.book.cover.url.lstrip("/")), + self.assertTrue( + re.match( + r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", + activity["attachment"][0].url, + ) ) self.assertEqual(activity["attachment"][0].name, "Test Edition") From ffcaef055959b64576b9b01bbc0f84715cafb248 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 13:39:58 -0800 Subject: [PATCH 045/121] Python formatting --- bookwyrm/tests/views/test_import.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bookwyrm/tests/views/test_import.py b/bookwyrm/tests/views/test_import.py index 29f4902d..1411a5a8 100644 --- a/bookwyrm/tests/views/test_import.py +++ b/bookwyrm/tests/views/test_import.py @@ -59,11 +59,11 @@ class ImportViews(TestCase): form.data["privacy"] = "public" form.data["include_reviews"] = False csv_file = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") - form.data[ - "csv_file" - ] = SimpleUploadedFile( + form.data["csv_file"] = SimpleUploadedFile( # pylint: disable=consider-using-with - csv_file, open(csv_file, "rb").read(), content_type="text/csv" + csv_file, + open(csv_file, "rb").read(), + content_type="text/csv", ) request = self.factory.post("", form.data) From 50ab4e8248bbcb740292d50e4b4459f8eddffa7b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 14:08:00 -0800 Subject: [PATCH 046/121] Updates model tests --- bookwyrm/tests/models/test_import_model.py | 154 ++++++++++++--------- 1 file changed, 87 insertions(+), 67 deletions(-) diff --git a/bookwyrm/tests/models/test_import_model.py b/bookwyrm/tests/models/test_import_model.py index 0e5d6760..f48143d0 100644 --- a/bookwyrm/tests/models/test_import_model.py +++ b/bookwyrm/tests/models/test_import_model.py @@ -18,83 +18,68 @@ class ImportJob(TestCase): def setUp(self): """data is from a goodreads export of The Raven Tower""" - read_data = { - "Book Id": 39395857, - "Title": "The Raven Tower", - "Author": "Ann Leckie", - "Author l-f": "Leckie, Ann", - "Additional Authors": "", - "ISBN": '="0356506991"', - "ISBN13": '="9780356506999"', - "My Rating": 0, - "Average Rating": 4.06, - "Publisher": "Orbit", - "Binding": "Hardcover", - "Number of Pages": 416, - "Year Published": 2019, - "Original Publication Year": 2019, - "Date Read": "2019/04/12", - "Date Added": "2019/04/09", - "Bookshelves": "", - "Bookshelves with positions": "", - "Exclusive Shelf": "read", - "My Review": "", - "Spoiler": "", - "Private Notes": "", - "Read Count": 1, - "Recommended For": "", - "Recommended By": "", - "Owned Copies": 0, - "Original Purchase Date": "", - "Original Purchase Location": "", - "Condition": "", - "Condition Description": "", - "BCID": "", - } - currently_reading_data = read_data.copy() - currently_reading_data["Exclusive Shelf"] = "currently-reading" - currently_reading_data["Date Read"] = "" - - unknown_read_data = currently_reading_data.copy() - unknown_read_data["Exclusive Shelf"] = "read" - unknown_read_data["Date Read"] = "" - with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( "bookwyrm.activitystreams.populate_stream_task.delay" ): - user = models.User.objects.create_user( - "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True ) - job = models.ImportJob.objects.create(user=user) - self.item_1 = models.ImportItem.objects.create( - job=job, index=1, data=currently_reading_data - ) - self.item_2 = models.ImportItem.objects.create(job=job, index=2, data=read_data) - self.item_3 = models.ImportItem.objects.create( - job=job, index=3, data=unknown_read_data - ) + self.job = models.ImportJob.objects.create(user=self.local_user, mappings={}) def test_isbn(self): """it unquotes the isbn13 field from data""" - expected = "9780356506999" - item = models.ImportItem.objects.get(index=1) - self.assertEqual(item.isbn, expected) + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + }, + ) + self.assertEqual(item.isbn, "9780356506999") def test_shelf(self): """converts to the local shelf typology""" - expected = "reading" - self.assertEqual(self.item_1.shelf, expected) + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + "shelf": "reading", + }, + ) + self.assertEqual(item.shelf, "reading") def test_date_added(self): """converts to the local shelf typology""" expected = datetime.datetime(2019, 4, 9, 0, 0, tzinfo=timezone.utc) - item = models.ImportItem.objects.get(index=1) + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + "shelf": "reading", + "date_added": "2019/04/09", + }, + ) self.assertEqual(item.date_added, expected) def test_date_read(self): """converts to the local shelf typology""" expected = datetime.datetime(2019, 4, 12, 0, 0, tzinfo=timezone.utc) - item = models.ImportItem.objects.get(index=2) + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + "shelf": "reading", + "date_added": "2019/04/09", + "date_finished": "2019/04/12", + }, + ) self.assertEqual(item.date_read, expected) def test_currently_reading_reads(self): @@ -104,31 +89,66 @@ class ImportJob(TestCase): start_date=datetime.datetime(2019, 4, 9, 0, 0, tzinfo=timezone.utc) ) ] - actual = models.ImportItem.objects.get(index=1) - self.assertEqual(actual.reads[0].start_date, expected[0].start_date) - self.assertEqual(actual.reads[0].finish_date, expected[0].finish_date) + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + "shelf": "reading", + "date_added": "2019/04/09", + }, + ) + self.assertEqual(item.reads[0].start_date, expected[0].start_date) + self.assertIsNone(item.reads[0].finish_date) def test_read_reads(self): """infer read dates where available""" - actual = self.item_2 + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + "shelf": "reading", + "date_added": "2019/04/09", + "date_finished": "2019/04/12", + }, + ) self.assertEqual( - actual.reads[0].start_date, + item.reads[0].start_date, datetime.datetime(2019, 4, 9, 0, 0, tzinfo=timezone.utc), ) self.assertEqual( - actual.reads[0].finish_date, + item.reads[0].finish_date, datetime.datetime(2019, 4, 12, 0, 0, tzinfo=timezone.utc), ) def test_unread_reads(self): """handle books with no read dates""" expected = [] - actual = models.ImportItem.objects.get(index=3) - self.assertEqual(actual.reads, expected) + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + "shelf": "reading", + }, + ) + self.assertEqual(item.reads, expected) @responses.activate def test_get_book_from_isbn(self): """search and load books by isbn (9780356506999)""" + item = models.ImportItem.objects.create( + index=1, + job=self.job, + data={}, + normalized_data={ + "isbn_13": '="9780356506999"', + }, + ) connector_info = models.Connector.objects.create( identifier="openlibrary.org", name="OpenLibrary", @@ -177,6 +197,6 @@ class ImportJob(TestCase): with patch( "bookwyrm.connectors.openlibrary.Connector." "get_authors_from_data" ): - book = self.item_1.get_book_from_isbn() + book = item.get_book_from_isbn() self.assertEqual(book.title, "Sabriel") From be94818a10aa049ffc8e3ff0789dddd7a9706f72 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 14:27:29 -0800 Subject: [PATCH 047/121] Fixes views tests --- bookwyrm/tests/views/test_import.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bookwyrm/tests/views/test_import.py b/bookwyrm/tests/views/test_import.py index 1411a5a8..54f11f03 100644 --- a/bookwyrm/tests/views/test_import.py +++ b/bookwyrm/tests/views/test_import.py @@ -41,7 +41,7 @@ class ImportViews(TestCase): def test_import_status(self): """there are so many views, this just makes sure it LOADS""" view = views.ImportStatus.as_view() - import_job = models.ImportJob.objects.create(user=self.local_user) + import_job = models.ImportJob.objects.create(user=self.local_user, mappings={}) request = self.factory.get("") request.user = self.local_user with patch("bookwyrm.tasks.app.AsyncResult") as async_result: @@ -55,7 +55,7 @@ class ImportViews(TestCase): """retry failed items""" view = views.Import.as_view() form = forms.ImportForm() - form.data["source"] = "LibraryThing" + form.data["source"] = "Goodreads" form.data["privacy"] = "public" form.data["include_reviews"] = False csv_file = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") @@ -79,7 +79,7 @@ class ImportViews(TestCase): """retry failed items""" view = views.ImportStatus.as_view() import_job = models.ImportJob.objects.create( - user=self.local_user, privacy="unlisted" + user=self.local_user, privacy="unlisted", mappings={} ) request = self.factory.post("") request.user = self.local_user From f0ce236ffc4a17ea34d32f00065fdf727b6665c7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 14:28:01 -0800 Subject: [PATCH 048/121] Removes unused code --- bookwyrm/importers/importer.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index cf7841e6..69391e94 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -1,6 +1,5 @@ """ handle reading a csv from an external service, defaults are from Goodreads """ import csv -from dataclasses import dataclass import logging from django.utils import timezone @@ -202,23 +201,3 @@ def handle_imported_book(source, user, item, include_reviews, privacy): ) # only broadcast this review to other bookwyrm instances review.save(software="bookwyrm") - - -@dataclass -class ImportEntry: - """data extracted from a line in a csv""" - - title: str - authors: str = None - isbn_13: str = None - isbn_10: str = None - shelf: str = None - review_name: str = None - review_rating: float = None - review_body: str = None - review_cw: str = None - rating: float = None - date_added: str = None - date_started: str = None - date_finished: str = None - import_source: str = "Unknown" From 908c9dc689aefd70c278254b81611515e7ad67a9 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 14:39:53 -0800 Subject: [PATCH 049/121] Use many small tasks instead of one big task --- bookwyrm/importers/importer.py | 49 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 69391e94..8ad4cd2e 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -102,39 +102,40 @@ class Importer: def start_import(self, job): """initalizes a csv import job""" - result = import_data.delay(self.service, job.id) + result = start_import_task.delay(self.service, job.id) job.task_id = result.id job.save() @app.task(queue="low_priority") -def import_data(source, job_id): - """does the actual lookup work in a celery task""" +def start_import_task(source, job_id): + """trigger the child tasks for each row""" job = ImportJob.objects.get(id=job_id) + # these are sub-tasks so that one big task doesn't use up all the memory in celery + for item in job.items.values("id").all(): + import_item_task.delay(source, item.id) + + +@app.task(queue="low_priority") +def import_item_task(source, item_id): + """resolve a row into a book""" + item = models.ImportItem.objets.get(id=item_id) try: - for item in job.items.all(): - try: - item.resolve() - except Exception as err: # pylint: disable=broad-except - logger.exception(err) - item.fail_reason = _("Error loading book") - item.save() - continue + item.resolve() + except Exception as err: # pylint: disable=broad-except + logger.exception(err) + item.fail_reason = _("Error loading book") + item.save() + return - if item.book or item.book_guess: - item.save() + if item.book: + job = item.job + # shelves book and handles reviews + handle_imported_book(source, job.user, item, job.include_reviews, job.privacy) + else: + item.fail_reason = _("Could not find a match for book") - if item.book: - # shelves book and handles reviews - handle_imported_book( - source, job.user, item, job.include_reviews, job.privacy - ) - else: - item.fail_reason = _("Could not find a match for book") - item.save() - finally: - job.complete = True - job.save() + item.save() def handle_imported_book(source, user, item, include_reviews, privacy): From c33d791974df8a37de257baae37cf7fc86be88c9 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 15:17:32 -0800 Subject: [PATCH 050/121] adds tests for new task system --- bookwyrm/importers/importer.py | 6 ++--- bookwyrm/tests/importers/test_importer.py | 30 ++++++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 8ad4cd2e..d7f32c88 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -112,14 +112,14 @@ def start_import_task(source, job_id): """trigger the child tasks for each row""" job = ImportJob.objects.get(id=job_id) # these are sub-tasks so that one big task doesn't use up all the memory in celery - for item in job.items.values("id").all(): - import_item_task.delay(source, item.id) + for item in job.items.values_list("id", flat=True).all(): + import_item_task.delay(source, item) @app.task(queue="low_priority") def import_item_task(source, item_id): """resolve a row into a book""" - item = models.ImportItem.objets.get(id=item_id) + item = models.ImportItem.objects.get(id=item_id) try: item.resolve() except Exception as err: # pylint: disable=broad-except diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index b2f0284d..05377cce 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -10,7 +10,8 @@ import responses from bookwyrm import models from bookwyrm.importers import Importer -from bookwyrm.importers.importer import import_data, handle_imported_book +from bookwyrm.importers.importer import start_import_task, import_item_task +from bookwyrm.importers.importer import handle_imported_book def make_date(*args): @@ -105,29 +106,40 @@ class GenericImporter(TestCase): ) MockTask = namedtuple("Task", ("id")) mock_task = MockTask(7) - with patch("bookwyrm.importers.importer.import_data.delay") as start: + with patch("bookwyrm.importers.importer.start_import_task.delay") as start: start.return_value = mock_task self.importer.start_import(import_job) import_job.refresh_from_db() self.assertEqual(import_job.task_id, "7") @responses.activate - def test_import_data(self, *_): + def test_start_import_task(self, *_): """resolve entry""" import_job = self.importer.create_job( self.local_user, self.csv, False, "unlisted" ) - book = models.Edition.objects.create(title="Test Book") + with patch("bookwyrm.importers.importer.import_item_task.delay") as mock: + start_import_task(self.importer.service, import_job.id) + + self.assertEqual(mock.call_count, 4) + + @responses.activate + def test_import_item_task(self, *_): + """resolve entry""" + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) + + import_item = models.ImportItem.objects.get(job=import_job, index=0) with patch( "bookwyrm.models.import_job.ImportItem.get_book_from_isbn" ) as resolve: - resolve.return_value = book - with patch("bookwyrm.importers.importer.handle_imported_book"): - import_data(self.importer.service, import_job.id) + resolve.return_value = self.book + import_item_task(self.importer.service, import_item.id) + import_item.refresh_from_db() - import_item = models.ImportItem.objects.get(job=import_job, index=0) - self.assertEqual(import_item.book.id, book.id) + self.assertEqual(import_item.book.id, self.book.id) def test_handle_imported_book(self, *_): """import added a book, this adds related connections""" From 6aa57d4d34a441e38ccb00e435d0eb59beddca5a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 19:00:01 -0800 Subject: [PATCH 051/121] Set queue for broadcast task --- bookwyrm/models/activitypub_mixin.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index e1276d46..553b2a82 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -126,12 +126,15 @@ class ActivitypubMixin: # there OUGHT to be only one match return match.first() - def broadcast(self, activity, sender, software=None): + def broadcast(self, activity, sender, software=None, queue="medium_priority"): """send out an activity""" - broadcast_task.delay( - sender.id, - json.dumps(activity, cls=activitypub.ActivityEncoder), - self.get_recipients(software=software), + broadcast_task.apply_async( + args=( + sender.id, + json.dumps(activity, cls=activitypub.ActivityEncoder), + self.get_recipients(software=software), + ), + queue=queue, ) def get_recipients(self, software=None): @@ -374,9 +377,9 @@ class CollectionItemMixin(ActivitypubMixin): activity_serializer = activitypub.CollectionItem - def broadcast(self, activity, sender, software="bookwyrm"): + def broadcast(self, activity, sender, software="bookwyrm", queue="medium_priority"): """only send book collection updates to other bookwyrm instances""" - super().broadcast(activity, sender, software=software) + super().broadcast(activity, sender, software=software, queue=queue) @property def privacy(self): @@ -406,7 +409,7 @@ class CollectionItemMixin(ActivitypubMixin): # adding an obj to the collection activity = self.to_add_activity(self.user) - self.broadcast(activity, self.user) + self.broadcast(activity, self.user, queue="low_priority") def delete(self, *args, broadcast=True, **kwargs): """broadcast a remove activity""" From 9fee860b00899c56190c079e0f7bcb9134695295 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 19:10:22 -0800 Subject: [PATCH 052/121] Adds enum for queue names --- bookwyrm/models/activitypub_mixin.py | 27 ++++++++++++++------------- bookwyrm/tasks.py | 5 +++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index 553b2a82..402cb040 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -20,7 +20,7 @@ from django.utils.http import http_date from bookwyrm import activitypub from bookwyrm.settings import USER_AGENT, PAGE_LENGTH from bookwyrm.signatures import make_signature, make_digest -from bookwyrm.tasks import app +from bookwyrm.tasks import app, MEDIUM from bookwyrm.models.fields import ImageField, ManyToManyField logger = logging.getLogger(__name__) @@ -29,7 +29,6 @@ logger = logging.getLogger(__name__) PropertyField = namedtuple("PropertyField", ("set_activity_from_field")) - # pylint: disable=invalid-name def set_activity_from_property_field(activity, obj, field): """assign a model property value to the activity json""" @@ -126,7 +125,7 @@ class ActivitypubMixin: # there OUGHT to be only one match return match.first() - def broadcast(self, activity, sender, software=None, queue="medium_priority"): + def broadcast(self, activity, sender, software=None, queue=MEDIUM): """send out an activity""" broadcast_task.apply_async( args=( @@ -198,7 +197,7 @@ class ActivitypubMixin: class ObjectMixin(ActivitypubMixin): """add this mixin for object models that are AP serializable""" - def save(self, *args, created=None, software=None, **kwargs): + def save(self, *args, created=None, software=None, priority=MEDIUM, **kwargs): """broadcast created/updated/deleted objects as appropriate""" broadcast = kwargs.get("broadcast", True) # this bonus kwarg would cause an error in the base save method @@ -225,12 +224,14 @@ class ObjectMixin(ActivitypubMixin): # do we have a "pure" activitypub version of this for mastodon? if software != "bookwyrm" and hasattr(self, "pure_content"): pure_activity = self.to_create_activity(user, pure=True) - self.broadcast(pure_activity, user, software="other") + self.broadcast( + pure_activity, user, software="other", queue=priority + ) # set bookwyrm so that that type is also sent software = "bookwyrm" # sends to BW only if we just did a pure version for masto activity = self.to_create_activity(user) - self.broadcast(activity, user, software=software) + self.broadcast(activity, user, software=software, queue=priority) except AttributeError: # janky as heck, this catches the mutliple inheritence chain # for boosts and ignores this auxilliary broadcast @@ -254,7 +255,7 @@ class ObjectMixin(ActivitypubMixin): activity = self.to_delete_activity(user) else: activity = self.to_update_activity(user) - self.broadcast(activity, user) + self.broadcast(activity, user, queue=priority) def to_create_activity(self, user, **kwargs): """returns the object wrapped in a Create activity""" @@ -377,7 +378,7 @@ class CollectionItemMixin(ActivitypubMixin): activity_serializer = activitypub.CollectionItem - def broadcast(self, activity, sender, software="bookwyrm", queue="medium_priority"): + def broadcast(self, activity, sender, software="bookwyrm", queue=MEDIUM): """only send book collection updates to other bookwyrm instances""" super().broadcast(activity, sender, software=software, queue=queue) @@ -398,7 +399,7 @@ class CollectionItemMixin(ActivitypubMixin): return [] return [collection_field.user] - def save(self, *args, broadcast=True, **kwargs): + def save(self, *args, broadcast=True, priority=MEDIUM, **kwargs): """broadcast updated""" # first off, we want to save normally no matter what super().save(*args, **kwargs) @@ -409,7 +410,7 @@ class CollectionItemMixin(ActivitypubMixin): # adding an obj to the collection activity = self.to_add_activity(self.user) - self.broadcast(activity, self.user, queue="low_priority") + self.broadcast(activity, self.user, queue=priority) def delete(self, *args, broadcast=True, **kwargs): """broadcast a remove activity""" @@ -442,12 +443,12 @@ class CollectionItemMixin(ActivitypubMixin): class ActivityMixin(ActivitypubMixin): """add this mixin for models that are AP serializable""" - def save(self, *args, broadcast=True, **kwargs): + def save(self, *args, broadcast=True, priority=MEDIUM, **kwargs): """broadcast activity""" super().save(*args, **kwargs) user = self.user if hasattr(self, "user") else self.user_subject if broadcast and user.local: - self.broadcast(self.to_activity(), user) + self.broadcast(self.to_activity(), user, queue=priority) def delete(self, *args, broadcast=True, **kwargs): """nevermind, undo that activity""" @@ -504,7 +505,7 @@ def unfurl_related_field(related_field, sort_field=None): return related_field.remote_id -@app.task(queue="medium_priority") +@app.task(queue=MEDIUM) def broadcast_task(sender_id, activity, recipients): """the celery task for broadcast""" user_model = apps.get_model("bookwyrm.User", require_ready=True) diff --git a/bookwyrm/tasks.py b/bookwyrm/tasks.py index b860e018..09e1d267 100644 --- a/bookwyrm/tasks.py +++ b/bookwyrm/tasks.py @@ -9,3 +9,8 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "celerywyrm.settings") app = Celery( "tasks", broker=settings.CELERY_BROKER_URL, backend=settings.CELERY_RESULT_BACKEND ) + +# priorities +LOW = "low_priority" +MEDIUM = "medium_priority" +HIGH = "high_priority" From 3190ef4346f3a1a763bbf15ba05a69c00a138fd3 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Nov 2021 19:16:36 -0800 Subject: [PATCH 053/121] Deprioritize adding old statuses to timelines --- bookwyrm/activitystreams.py | 31 +++++++++++++++++++++---------- bookwyrm/importers/importer.py | 4 ++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index 4896e07d..a9ca17e2 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -7,7 +7,7 @@ from django.utils import timezone from bookwyrm import models from bookwyrm.redis_store import RedisStore, r -from bookwyrm.tasks import app +from bookwyrm.tasks import app, LOW, MEDIUM, HIGH class ActivityStream(RedisStore): @@ -277,7 +277,18 @@ def add_status_on_create(sender, instance, created, *args, **kwargs): def add_status_on_create_command(sender, instance, created): """runs this code only after the database commit completes""" - add_status_task.delay(instance.id, increment_unread=created) + priority=HIGH + # check if this is an old status, de-prioritize if so + # (this will happen if federation is very slow, or, more expectedly, on csv import) + one_day = 60 * 60 * 24 + if (instance.created_date - instance.published_date).seconds > one_day: + priority=LOW + + add_status_task.apply_async( + args=(instance.id,), + kwargs={"increment_unread": created}, + queue=priority, + ) if sender == models.Boost: handle_boost_task.delay(instance.id) @@ -409,7 +420,7 @@ def remove_statuses_on_unshelve(sender, instance, *args, **kwargs): # ---- TASKS -@app.task(queue="low_priority") +@app.task(queue=LOW) def add_book_statuses_task(user_id, book_id): """add statuses related to a book on shelve""" user = models.User.objects.get(id=user_id) @@ -417,7 +428,7 @@ def add_book_statuses_task(user_id, book_id): BooksStream().add_book_statuses(user, book) -@app.task(queue="low_priority") +@app.task(queue=LOW) def remove_book_statuses_task(user_id, book_id): """remove statuses about a book from a user's books feed""" user = models.User.objects.get(id=user_id) @@ -425,7 +436,7 @@ def remove_book_statuses_task(user_id, book_id): BooksStream().remove_book_statuses(user, book) -@app.task(queue="medium_priority") +@app.task(queue=MEDIUM) def populate_stream_task(stream, user_id): """background task for populating an empty activitystream""" user = models.User.objects.get(id=user_id) @@ -433,7 +444,7 @@ def populate_stream_task(stream, user_id): stream.populate_streams(user) -@app.task(queue="medium_priority") +@app.task(queue=MEDIUM) def remove_status_task(status_ids): """remove a status from any stream it might be in""" # this can take an id or a list of ids @@ -446,7 +457,7 @@ def remove_status_task(status_ids): stream.remove_object_from_related_stores(status) -@app.task(queue="high_priority") +@app.task(queue=HIGH) def add_status_task(status_id, increment_unread=False): """add a status to any stream it should be in""" status = models.Status.objects.get(id=status_id) @@ -458,7 +469,7 @@ def add_status_task(status_id, increment_unread=False): stream.add_status(status, increment_unread=increment_unread) -@app.task(queue="medium_priority") +@app.task(queue=MEDIUM) def remove_user_statuses_task(viewer_id, user_id, stream_list=None): """remove all statuses by a user from a viewer's stream""" stream_list = [streams[s] for s in stream_list] if stream_list else streams.values() @@ -468,7 +479,7 @@ def remove_user_statuses_task(viewer_id, user_id, stream_list=None): stream.remove_user_statuses(viewer, user) -@app.task(queue="medium_priority") +@app.task(queue=MEDIUM) def add_user_statuses_task(viewer_id, user_id, stream_list=None): """add all statuses by a user to a viewer's stream""" stream_list = [streams[s] for s in stream_list] if stream_list else streams.values() @@ -478,7 +489,7 @@ def add_user_statuses_task(viewer_id, user_id, stream_list=None): stream.add_user_statuses(viewer, user) -@app.task(queue="medium_priority") +@app.task(queue=MEDIUM) def handle_boost_task(boost_id): """remove the original post and other, earlier boosts""" instance = models.Status.objects.get(id=boost_id) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index d7f32c88..3e0b0c28 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ from bookwyrm import models from bookwyrm.models import ImportJob, ImportItem -from bookwyrm.tasks import app +from bookwyrm.tasks import app, LOW logger = logging.getLogger(__name__) @@ -201,4 +201,4 @@ def handle_imported_book(source, user, item, include_reviews, privacy): privacy=privacy, ) # only broadcast this review to other bookwyrm instances - review.save(software="bookwyrm") + review.save(software="bookwyrm", priority=LOW) From f71ef286b629c3e8744bb7d895b1a30f827ca484 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 08:55:47 -0800 Subject: [PATCH 054/121] Updates mocks --- bookwyrm/activitystreams.py | 4 ++-- bookwyrm/importers/importer.py | 4 ++-- bookwyrm/tests/importers/test_importer.py | 22 ++++++++++++------- .../importers/test_librarything_import.py | 6 ++--- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index a9ca17e2..e1a52d26 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -277,12 +277,12 @@ def add_status_on_create(sender, instance, created, *args, **kwargs): def add_status_on_create_command(sender, instance, created): """runs this code only after the database commit completes""" - priority=HIGH + priority = HIGH # check if this is an old status, de-prioritize if so # (this will happen if federation is very slow, or, more expectedly, on csv import) one_day = 60 * 60 * 24 if (instance.created_date - instance.published_date).seconds > one_day: - priority=LOW + priority = LOW add_status_task.apply_async( args=(instance.id,), diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 3e0b0c28..b32e2df7 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -151,9 +151,9 @@ def handle_imported_book(source, user, item, include_reviews, privacy): if item.shelf and not existing_shelf: desired_shelf = models.Shelf.objects.get(identifier=item.shelf, user=user) shelved_date = item.date_added or timezone.now() - models.ShelfBook.objects.create( + models.ShelfBook( book=item.book, shelf=desired_shelf, user=user, shelved_date=shelved_date - ) + ).save(priority=LOW) for read in item.reads: # check for an existing readthrough with the same dates diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index 05377cce..963eca54 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -136,7 +136,13 @@ class GenericImporter(TestCase): "bookwyrm.models.import_job.ImportItem.get_book_from_isbn" ) as resolve: resolve.return_value = self.book - import_item_task(self.importer.service, import_item.id) + + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: + import_item_task(self.importer.service, import_item.id) + kwargs = mock.call_args.kwargs + self.assertEqual(kwargs["queue"], "low_priority") import_item.refresh_from_db() self.assertEqual(import_item.book.id, self.book.id) @@ -153,7 +159,7 @@ class GenericImporter(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -163,7 +169,7 @@ class GenericImporter(TestCase): def test_handle_imported_book_already_shelved(self, *_): """import added a book, this adds related connections""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): shelf = self.local_user.shelf_set.filter(identifier="to-read").first() models.ShelfBook.objects.create( shelf=shelf, @@ -179,7 +185,7 @@ class GenericImporter(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -203,7 +209,7 @@ class GenericImporter(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -223,7 +229,7 @@ class GenericImporter(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): with patch("bookwyrm.models.Status.broadcast") as broadcast_mock: handle_imported_book( self.importer.service, @@ -249,7 +255,7 @@ class GenericImporter(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, True, "unlisted" ) @@ -267,7 +273,7 @@ class GenericImporter(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "unlisted" ) diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index e9352896..55bfef72 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -9,7 +9,7 @@ import responses from bookwyrm import models from bookwyrm.importers import LibrarythingImporter -from bookwyrm.importers.importer import import_data, handle_imported_book +from bookwyrm.importers.importer import start_import_task, handle_imported_book def make_date(*args): @@ -85,7 +85,7 @@ class LibrarythingImport(TestCase): self.assertEqual(retry_items[1].data["Book Id"], "5015319") @responses.activate - def test_import_data(self, *_): + def test_start_import_task(self, *_): """resolve entry""" import_job = self.importer.create_job( self.local_user, self.csv, False, "unlisted" @@ -97,7 +97,7 @@ class LibrarythingImport(TestCase): ) as resolve: resolve.return_value = book with patch("bookwyrm.importers.importer.handle_imported_book"): - import_data(self.importer.service, import_job.id) + start_import_task(self.importer.service, import_job.id) import_item = models.ImportItem.objects.get(job=import_job, index=0) self.assertEqual(import_item.book.id, book.id) From e6a251fdad509285139c5a0484c80182c7827856 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 09:17:00 -0800 Subject: [PATCH 055/121] Updates mocks across tests --- bookwyrm/models/book.py | 5 +- .../tests/activitypub/test_base_activity.py | 8 +-- .../activitystreams/test_abstractstream.py | 2 +- .../tests/activitystreams/test_booksstream.py | 2 +- .../tests/activitystreams/test_homestream.py | 2 +- .../tests/activitystreams/test_localstream.py | 2 +- .../tests/activitystreams/test_signals.py | 2 +- bookwyrm/tests/activitystreams/test_tasks.py | 10 +-- .../tests/importers/test_goodreads_import.py | 6 +- .../importers/test_librarything_import.py | 8 +-- .../tests/importers/test_storygraph_import.py | 4 +- .../tests/management/test_populate_streams.py | 2 +- .../tests/models/test_activitypub_mixin.py | 2 +- bookwyrm/tests/models/test_group.py | 6 +- bookwyrm/tests/models/test_list.py | 10 +-- .../tests/models/test_relationship_models.py | 20 +++--- bookwyrm/tests/models/test_shelf_model.py | 30 +++++---- bookwyrm/tests/models/test_user_model.py | 4 +- bookwyrm/tests/test_postgres.py | 2 +- bookwyrm/tests/test_suggested_users.py | 6 +- bookwyrm/tests/test_templatetags.py | 14 ++-- bookwyrm/tests/views/admin/test_reports.py | 6 +- bookwyrm/tests/views/admin/test_user_admin.py | 2 +- bookwyrm/tests/views/books/test_book.py | 8 +-- bookwyrm/tests/views/books/test_edit_book.py | 8 +-- bookwyrm/tests/views/books/test_editions.py | 4 +- .../tests/views/inbox/test_inbox_announce.py | 2 +- .../tests/views/inbox/test_inbox_block.py | 2 +- .../tests/views/inbox/test_inbox_create.py | 2 +- .../tests/views/inbox/test_inbox_follow.py | 24 ++++--- bookwyrm/tests/views/inbox/test_inbox_like.py | 2 +- .../tests/views/inbox/test_inbox_remove.py | 2 +- .../tests/views/inbox/test_inbox_update.py | 4 +- .../tests/views/preferences/test_block.py | 2 +- .../views/preferences/test_delete_user.py | 10 +-- .../tests/views/preferences/test_edit_user.py | 10 +-- bookwyrm/tests/views/shelf/test_shelf.py | 8 +-- .../tests/views/shelf/test_shelf_actions.py | 28 ++++---- bookwyrm/tests/views/test_author.py | 2 +- bookwyrm/tests/views/test_discover.py | 2 +- bookwyrm/tests/views/test_feed.py | 10 +-- bookwyrm/tests/views/test_follow.py | 16 +++-- bookwyrm/tests/views/test_get_started.py | 4 +- bookwyrm/tests/views/test_goal.py | 2 +- bookwyrm/tests/views/test_group.py | 2 +- bookwyrm/tests/views/test_helpers.py | 10 +-- bookwyrm/tests/views/test_interaction.py | 8 +-- bookwyrm/tests/views/test_list.py | 32 +++++---- bookwyrm/tests/views/test_list_actions.py | 66 +++++++++++-------- bookwyrm/tests/views/test_notifications.py | 2 +- bookwyrm/tests/views/test_outbox.py | 2 +- bookwyrm/tests/views/test_reading.py | 12 ++-- bookwyrm/tests/views/test_readthrough.py | 4 +- bookwyrm/tests/views/test_rss_feed.py | 2 +- bookwyrm/tests/views/test_search.py | 2 +- bookwyrm/tests/views/test_status.py | 8 +-- bookwyrm/tests/views/test_user.py | 8 ++- 57 files changed, 253 insertions(+), 212 deletions(-) diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 8ae75baf..d97a1b8a 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -66,9 +66,10 @@ class BookDataModel(ObjectMixin, BookWyrmModel): self.remote_id = None return super().save(*args, **kwargs) - def broadcast(self, activity, sender, software="bookwyrm"): + # pylint: disable=arguments-differ + def broadcast(self, activity, sender, software="bookwyrm", **kwargs): """only send book data updates to other bookwyrm instances""" - super().broadcast(activity, sender, software=software) + super().broadcast(activity, sender, software=software, **kwargs) class Book(BookDataModel): diff --git a/bookwyrm/tests/activitypub/test_base_activity.py b/bookwyrm/tests/activitypub/test_base_activity.py index 7117eb52..b951c7ab 100644 --- a/bookwyrm/tests/activitypub/test_base_activity.py +++ b/bookwyrm/tests/activitypub/test_base_activity.py @@ -146,7 +146,7 @@ class BaseActivity(TestCase): self.user.avatar.file # pylint: disable=pointless-statement # this would trigger a broadcast because it's a local user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): activity.to_model(model=models.User, instance=self.user) self.assertIsNotNone(self.user.avatar.file) self.assertEqual(self.user.name, "New Name") @@ -154,7 +154,7 @@ class BaseActivity(TestCase): def test_to_model_many_to_many(self, *_): """annoying that these all need special handling""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create( content="test status", user=self.user, @@ -186,7 +186,7 @@ class BaseActivity(TestCase): def test_to_model_one_to_many(self, *_): """these are reversed relationships, where the secondary object keys the primary object but not vice versa""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create( content="test status", user=self.user, @@ -224,7 +224,7 @@ class BaseActivity(TestCase): @responses.activate def test_set_related_field(self, *_): """celery task to add back-references to created objects""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create( content="test status", user=self.user, diff --git a/bookwyrm/tests/activitystreams/test_abstractstream.py b/bookwyrm/tests/activitystreams/test_abstractstream.py index f9674286..17a1b587 100644 --- a/bookwyrm/tests/activitystreams/test_abstractstream.py +++ b/bookwyrm/tests/activitystreams/test_abstractstream.py @@ -4,7 +4,7 @@ from django.test import TestCase from bookwyrm import activitystreams, models -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") diff --git a/bookwyrm/tests/activitystreams/test_booksstream.py b/bookwyrm/tests/activitystreams/test_booksstream.py index d6d94b73..347e7a94 100644 --- a/bookwyrm/tests/activitystreams/test_booksstream.py +++ b/bookwyrm/tests/activitystreams/test_booksstream.py @@ -4,7 +4,7 @@ from django.test import TestCase from bookwyrm import activitystreams, models -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") diff --git a/bookwyrm/tests/activitystreams/test_homestream.py b/bookwyrm/tests/activitystreams/test_homestream.py index 42a73f3c..d48bdfba 100644 --- a/bookwyrm/tests/activitystreams/test_homestream.py +++ b/bookwyrm/tests/activitystreams/test_homestream.py @@ -4,7 +4,7 @@ from django.test import TestCase from bookwyrm import activitystreams, models -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") diff --git a/bookwyrm/tests/activitystreams/test_localstream.py b/bookwyrm/tests/activitystreams/test_localstream.py index e6b05557..fa1a6741 100644 --- a/bookwyrm/tests/activitystreams/test_localstream.py +++ b/bookwyrm/tests/activitystreams/test_localstream.py @@ -4,7 +4,7 @@ from django.test import TestCase from bookwyrm import activitystreams, models -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") diff --git a/bookwyrm/tests/activitystreams/test_signals.py b/bookwyrm/tests/activitystreams/test_signals.py index eb70d28e..b3a99620 100644 --- a/bookwyrm/tests/activitystreams/test_signals.py +++ b/bookwyrm/tests/activitystreams/test_signals.py @@ -4,7 +4,7 @@ from django.test import TestCase from bookwyrm import activitystreams, models -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class ActivitystreamsSignals(TestCase): """using redis to build activity streams""" diff --git a/bookwyrm/tests/activitystreams/test_tasks.py b/bookwyrm/tests/activitystreams/test_tasks.py index 80b0b771..f5750763 100644 --- a/bookwyrm/tests/activitystreams/test_tasks.py +++ b/bookwyrm/tests/activitystreams/test_tasks.py @@ -34,7 +34,7 @@ class Activitystreams(TestCase): ) work = models.Work.objects.create(title="test work") self.book = models.Edition.objects.create(title="test book", parent_work=work) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.status = models.Status.objects.create( content="hi", user=self.local_user ) @@ -133,7 +133,7 @@ class Activitystreams(TestCase): @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_boost_to_another_timeline(self, *_): """boost from a non-follower doesn't remove original status from feed""" status = models.Status.objects.create(user=self.local_user, content="hi") @@ -155,7 +155,7 @@ class Activitystreams(TestCase): @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_boost_to_another_timeline_remote(self, *_): """boost from a remote non-follower doesn't remove original status from feed""" status = models.Status.objects.create(user=self.local_user, content="hi") @@ -177,7 +177,7 @@ class Activitystreams(TestCase): @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_boost_to_following_timeline(self, *_): """add a boost and deduplicate the boosted status on the timeline""" self.local_user.following.add(self.another_user) @@ -199,7 +199,7 @@ class Activitystreams(TestCase): @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_boost_to_same_timeline(self, *_): """add a boost and deduplicate the boosted status on the timeline""" status = models.Status.objects.create(user=self.local_user, content="hi") diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index 87624a0e..12b5578b 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -91,7 +91,7 @@ class GoodreadsImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -115,7 +115,7 @@ class GoodreadsImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, True, "unlisted" ) @@ -135,7 +135,7 @@ class GoodreadsImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, True, "unlisted" ) diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index 55bfef72..ed081aa2 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -114,7 +114,7 @@ class LibrarythingImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -129,7 +129,7 @@ class LibrarythingImport(TestCase): def test_handle_imported_book_already_shelved(self, *_): """librarything import added a book, this adds related connections""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): shelf = self.local_user.shelf_set.filter(identifier="to-read").first() models.ShelfBook.objects.create( shelf=shelf, user=self.local_user, book=self.book @@ -142,7 +142,7 @@ class LibrarythingImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -166,7 +166,7 @@ class LibrarythingImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, True, "unlisted" ) diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py index 2f0fd7ef..8002a3e1 100644 --- a/bookwyrm/tests/importers/test_storygraph_import.py +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -95,7 +95,7 @@ class StorygraphImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, False, "public" ) @@ -116,7 +116,7 @@ class StorygraphImport(TestCase): import_item.book = self.book import_item.save() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): handle_imported_book( self.importer.service, self.local_user, import_item, True, "unlisted" ) diff --git a/bookwyrm/tests/management/test_populate_streams.py b/bookwyrm/tests/management/test_populate_streams.py index ca21b0ee..5be1774d 100644 --- a/bookwyrm/tests/management/test_populate_streams.py +++ b/bookwyrm/tests/management/test_populate_streams.py @@ -6,7 +6,7 @@ from bookwyrm import models from bookwyrm.management.commands.populate_streams import populate_streams -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class Activitystreams(TestCase): """using redis to build activity streams""" diff --git a/bookwyrm/tests/models/test_activitypub_mixin.py b/bookwyrm/tests/models/test_activitypub_mixin.py index 911cca5c..91a1fe7c 100644 --- a/bookwyrm/tests/models/test_activitypub_mixin.py +++ b/bookwyrm/tests/models/test_activitypub_mixin.py @@ -21,7 +21,7 @@ from bookwyrm.settings import PAGE_LENGTH # pylint: disable=invalid-name @patch("bookwyrm.activitystreams.add_status_task.delay") -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class ActivitypubMixins(TestCase): """functionality shared across models""" diff --git a/bookwyrm/tests/models/test_group.py b/bookwyrm/tests/models/test_group.py index 33341d19..2dd3cee1 100644 --- a/bookwyrm/tests/models/test_group.py +++ b/bookwyrm/tests/models/test_group.py @@ -5,7 +5,7 @@ from django.test import TestCase from bookwyrm import models, settings -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class Group(TestCase): """some activitypub oddness ahead""" @@ -87,7 +87,7 @@ class Group(TestCase): def test_group_members_can_see_followers_only_lists(self, _): """follower-only group booklists should not be excluded from group booklist listing for group members who do not follower list owner""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): followers_list = models.List.objects.create( name="Followers List", curation="group", @@ -107,7 +107,7 @@ class Group(TestCase): def test_group_members_can_see_private_lists(self, _): """private group booklists should not be excluded from group booklist listing for group members""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): private_list = models.List.objects.create( name="Private List", diff --git a/bookwyrm/tests/models/test_list.py b/bookwyrm/tests/models/test_list.py index 0d749453..254005bc 100644 --- a/bookwyrm/tests/models/test_list.py +++ b/bookwyrm/tests/models/test_list.py @@ -5,7 +5,7 @@ from django.test import TestCase from bookwyrm import models, settings -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class List(TestCase): """some activitypub oddness ahead""" @@ -22,7 +22,7 @@ class List(TestCase): def test_remote_id(self, _): """shelves use custom remote ids""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): book_list = models.List.objects.create( name="Test List", user=self.local_user ) @@ -31,7 +31,7 @@ class List(TestCase): def test_to_activity(self, _): """jsonify it""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): book_list = models.List.objects.create( name="Test List", user=self.local_user ) @@ -45,7 +45,7 @@ class List(TestCase): def test_list_item(self, _): """a list entry""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): book_list = models.List.objects.create( name="Test List", user=self.local_user, privacy="unlisted" ) @@ -63,7 +63,7 @@ class List(TestCase): def test_list_item_pending(self, _): """a list entry""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): book_list = models.List.objects.create( name="Test List", user=self.local_user ) diff --git a/bookwyrm/tests/models/test_relationship_models.py b/bookwyrm/tests/models/test_relationship_models.py index 04dbe1a3..2b388398 100644 --- a/bookwyrm/tests/models/test_relationship_models.py +++ b/bookwyrm/tests/models/test_relationship_models.py @@ -33,11 +33,13 @@ class Relationship(TestCase): def test_user_follows_from_request(self, _): """convert a follow request into a follow""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: request = models.UserFollowRequest.objects.create( user_subject=self.local_user, user_object=self.remote_user ) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Follow") self.assertEqual( request.remote_id, "http://local.com/user/mouse#follows/%d" % request.id @@ -54,7 +56,7 @@ class Relationship(TestCase): def test_user_follows_from_request_custom_remote_id(self, _): """store a specific remote id for a relationship provided by remote""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): request = models.UserFollowRequest.objects.create( user_subject=self.local_user, user_object=self.remote_user, @@ -69,19 +71,19 @@ class Relationship(TestCase): self.assertEqual(rel.user_subject, self.local_user) self.assertEqual(rel.user_object, self.remote_user) - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_follow_request_activity(self, broadcast_mock, _): """accept a request and make it a relationship""" models.UserFollowRequest.objects.create( user_subject=self.local_user, user_object=self.remote_user, ) - activity = json.loads(broadcast_mock.call_args[0][1]) + activity = json.loads(broadcast_mock.call_args[1]["args"][1]) self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"], self.remote_user.remote_id) self.assertEqual(activity["type"], "Follow") - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_follow_request_accept(self, broadcast_mock, _): """accept a request and make it a relationship""" self.local_user.manually_approves_followers = True @@ -96,7 +98,7 @@ class Relationship(TestCase): ) request.accept() - activity = json.loads(broadcast_mock.call_args[0][1]) + activity = json.loads(broadcast_mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Accept") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["id"], "https://www.hi.com/") @@ -107,7 +109,7 @@ class Relationship(TestCase): self.assertEqual(rel.user_subject, self.remote_user) self.assertEqual(rel.user_object, self.local_user) - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_follow_request_reject(self, broadcast_mock, _): """accept a request and make it a relationship""" self.local_user.manually_approves_followers = True @@ -120,7 +122,7 @@ class Relationship(TestCase): ) request.reject() - activity = json.loads(broadcast_mock.call_args[0][1]) + activity = json.loads(broadcast_mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Reject") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["id"], request.remote_id) diff --git a/bookwyrm/tests/models/test_shelf_model.py b/bookwyrm/tests/models/test_shelf_model.py index fe179e88..0683fbef 100644 --- a/bookwyrm/tests/models/test_shelf_model.py +++ b/bookwyrm/tests/models/test_shelf_model.py @@ -27,7 +27,7 @@ class Shelf(TestCase): def test_remote_id(self, *_): """shelves use custom remote ids""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) @@ -36,7 +36,7 @@ class Shelf(TestCase): def test_to_activity(self, *_): """jsonify it""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) @@ -51,19 +51,23 @@ class Shelf(TestCase): def test_create_update_shelf(self, *_): """create and broadcast shelf creation""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Create") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["name"], "Test Shelf") shelf.name = "arthur russel" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: shelf.save() - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Update") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["name"], "arthur russel") @@ -71,27 +75,31 @@ class Shelf(TestCase): def test_shelve(self, *_): """create and broadcast shelf creation""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: shelf_book = models.ShelfBook.objects.create( shelf=shelf, user=self.local_user, book=self.book ) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["id"], shelf_book.remote_id) self.assertEqual(activity["target"], shelf.remote_id) self.assertEqual(shelf.books.first(), self.book) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: shelf_book.delete() self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Remove") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["id"], shelf_book.remote_id) diff --git a/bookwyrm/tests/models/test_user_model.py b/bookwyrm/tests/models/test_user_model.py index 528d3fdc..389928cd 100644 --- a/bookwyrm/tests/models/test_user_model.py +++ b/bookwyrm/tests/models/test_user_model.py @@ -165,12 +165,12 @@ class User(TestCase): """deactivate a user""" self.assertTrue(self.user.is_active) with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as broadcast_mock: self.user.delete() self.assertEqual(broadcast_mock.call_count, 1) - activity = json.loads(broadcast_mock.call_args[0][1]) + activity = json.loads(broadcast_mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["object"], self.user.remote_id) self.assertFalse(self.user.is_active) diff --git a/bookwyrm/tests/test_postgres.py b/bookwyrm/tests/test_postgres.py index 70775d47..62451257 100644 --- a/bookwyrm/tests/test_postgres.py +++ b/bookwyrm/tests/test_postgres.py @@ -5,7 +5,7 @@ from django.test import TestCase from bookwyrm import models -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class PostgresTriggers(TestCase): """special migrations, fancy stuff ya know""" diff --git a/bookwyrm/tests/test_suggested_users.py b/bookwyrm/tests/test_suggested_users.py index f625ac10..dce5d770 100644 --- a/bookwyrm/tests/test_suggested_users.py +++ b/bookwyrm/tests/test_suggested_users.py @@ -9,7 +9,7 @@ from bookwyrm import models from bookwyrm.suggested_users import suggested_users, get_annotated_users -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay") @@ -168,7 +168,7 @@ class SuggestedUsers(TestCase): remote_id="https://example.com/book/1", parent_work=work, ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): # 1 shared follow self.local_user.following.add(user_2) user_1.followers.add(user_2) @@ -213,7 +213,7 @@ class SuggestedUsers(TestCase): user.following.add(user_1) user.followers.add(self.local_user) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): for i in range(3): book = models.Edition.objects.create( title=i, diff --git a/bookwyrm/tests/test_templatetags.py b/bookwyrm/tests/test_templatetags.py index 5954ce27..ed4466f5 100644 --- a/bookwyrm/tests/test_templatetags.py +++ b/bookwyrm/tests/test_templatetags.py @@ -44,7 +44,7 @@ class TemplateTags(TestCase): def test_get_user_rating(self, *_): """get a user's most recent rating of a book""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.Review.objects.create(user=self.user, book=self.book, rating=3) self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 3) @@ -63,7 +63,7 @@ class TemplateTags(TestCase): utilities.get_user_identifier(self.remote_user), "rat@example.com" ) - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_get_replies(self, *_): """direct replies to a status""" parent = models.Review.objects.create( @@ -90,7 +90,7 @@ class TemplateTags(TestCase): def test_get_parent(self, *_): """get the reply parent of a status""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): parent = models.Review.objects.create( user=self.user, book=self.book, content="hi" ) @@ -107,7 +107,7 @@ class TemplateTags(TestCase): status = models.Review.objects.create(user=self.remote_user, book=self.book) self.assertFalse(interaction.get_user_liked(self.user, status)) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.Favorite.objects.create(user=self.user, status=status) self.assertTrue(interaction.get_user_liked(self.user, status)) @@ -116,13 +116,13 @@ class TemplateTags(TestCase): status = models.Review.objects.create(user=self.remote_user, book=self.book) self.assertFalse(interaction.get_user_boosted(self.user, status)) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.Boost.objects.create(user=self.user, boosted_status=status) self.assertTrue(interaction.get_user_boosted(self.user, status)) def test_get_boosted(self, *_): """load a boosted status""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Review.objects.create(user=self.remote_user, book=self.book) boost = models.Boost.objects.create(user=self.user, boosted_status=status) boosted = status_display.get_boosted(boost) @@ -166,7 +166,7 @@ class TemplateTags(TestCase): def test_related_status(self, *_): """gets the subclass model for a notification status""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create(content="hi", user=self.user) notification = models.Notification.objects.create( user=self.user, notification_type="MENTION", related_status=status diff --git a/bookwyrm/tests/views/admin/test_reports.py b/bookwyrm/tests/views/admin/test_reports.py index 2b063446..8b9fe9f5 100644 --- a/bookwyrm/tests/views/admin/test_reports.py +++ b/bookwyrm/tests/views/admin/test_reports.py @@ -151,10 +151,12 @@ class ReportViews(TestCase): request.user.is_superuser = True # de-activate - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.moderator_delete_user(request, self.rat.id) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.rat.refresh_from_db() diff --git a/bookwyrm/tests/views/admin/test_user_admin.py b/bookwyrm/tests/views/admin/test_user_admin.py index ef35c220..486fe45e 100644 --- a/bookwyrm/tests/views/admin/test_user_admin.py +++ b/bookwyrm/tests/views/admin/test_user_admin.py @@ -67,7 +67,7 @@ class UserAdminViews(TestCase): request.user = self.local_user request.user.is_superuser = True - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): result = view(request, self.local_user.id) self.assertIsInstance(result, TemplateResponse) diff --git a/bookwyrm/tests/views/books/test_book.py b/bookwyrm/tests/views/books/test_book.py index a078f161..561e2192 100644 --- a/bookwyrm/tests/views/books/test_book.py +++ b/bookwyrm/tests/views/books/test_book.py @@ -78,7 +78,7 @@ class BookViews(TestCase): self.assertIsInstance(result, ActivitypubResponse) self.assertEqual(result.status_code, 200) - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") def test_book_page_statuses(self, *_): """there are so many views, this just makes sure it LOADS""" @@ -169,7 +169,7 @@ class BookViews(TestCase): request.user = self.local_user with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: views.upload_cover(request, self.book.id) self.assertEqual(delay_mock.call_count, 1) @@ -188,7 +188,7 @@ class BookViews(TestCase): request.user = self.local_user with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: views.upload_cover(request, self.book.id) self.assertEqual(delay_mock.call_count, 1) @@ -202,7 +202,7 @@ class BookViews(TestCase): request = self.factory.post("", {"description": "new description hi"}) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.add_description(request, self.book.id) self.book.refresh_from_db() diff --git a/bookwyrm/tests/views/books/test_edit_book.py b/bookwyrm/tests/views/books/test_edit_book.py index 7bf5708f..cd957858 100644 --- a/bookwyrm/tests/views/books/test_edit_book.py +++ b/bookwyrm/tests/views/books/test_edit_book.py @@ -79,7 +79,7 @@ class EditBookViews(TestCase): request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, self.book.id) self.book.refresh_from_db() @@ -115,7 +115,7 @@ class EditBookViews(TestCase): request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, self.book.id) self.book.refresh_from_db() @@ -136,7 +136,7 @@ class EditBookViews(TestCase): request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, self.book.id) self.book.refresh_from_db() self.assertEqual(self.book.title, "New Title") @@ -207,7 +207,7 @@ class EditBookViews(TestCase): request.user = self.local_user with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: views.upload_cover(request, self.book.id) self.assertEqual(delay_mock.call_count, 1) diff --git a/bookwyrm/tests/views/books/test_editions.py b/bookwyrm/tests/views/books/test_editions.py index 2f41fe66..17f15654 100644 --- a/bookwyrm/tests/views/books/test_editions.py +++ b/bookwyrm/tests/views/books/test_editions.py @@ -111,7 +111,7 @@ class BookViews(TestCase): work = models.Work.objects.create(title="test work") edition1 = models.Edition.objects.create(title="first ed", parent_work=work) edition2 = models.Edition.objects.create(title="second ed", parent_work=work) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user) models.ShelfBook.objects.create( book=edition1, @@ -124,7 +124,7 @@ class BookViews(TestCase): self.assertEqual(models.ReadThrough.objects.get().book, edition1) request = self.factory.post("", {"edition": edition2.id}) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.switch_edition(request) self.assertEqual(models.ShelfBook.objects.get().book, edition2) diff --git a/bookwyrm/tests/views/inbox/test_inbox_announce.py b/bookwyrm/tests/views/inbox/test_inbox_announce.py index 3a108878..a291552d 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_announce.py +++ b/bookwyrm/tests/views/inbox/test_inbox_announce.py @@ -36,7 +36,7 @@ class InboxActivities(TestCase): outbox="https://example.com/users/rat/outbox", ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): with patch("bookwyrm.activitystreams.add_status_task.delay"): self.status = models.Status.objects.create( user=self.local_user, diff --git a/bookwyrm/tests/views/inbox/test_inbox_block.py b/bookwyrm/tests/views/inbox/test_inbox_block.py index ffd74dbd..f6898fc6 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_block.py +++ b/bookwyrm/tests/views/inbox/test_inbox_block.py @@ -40,7 +40,7 @@ class InboxBlock(TestCase): def test_handle_blocks(self): """create a "block" database entry from an activity""" self.local_user.followers.add(self.remote_user) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.UserFollowRequest.objects.create( user_subject=self.local_user, user_object=self.remote_user ) diff --git a/bookwyrm/tests/views/inbox/test_inbox_create.py b/bookwyrm/tests/views/inbox/test_inbox_create.py index 76fd366c..53b17d68 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_create.py +++ b/bookwyrm/tests/views/inbox/test_inbox_create.py @@ -10,7 +10,7 @@ from bookwyrm.activitypub import ActivitySerializerError # pylint: disable=too-many-public-methods -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") class InboxCreate(TestCase): """readthrough tests""" diff --git a/bookwyrm/tests/views/inbox/test_inbox_follow.py b/bookwyrm/tests/views/inbox/test_inbox_follow.py index 6b629c2f..71f101ca 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_follow.py +++ b/bookwyrm/tests/views/inbox/test_inbox_follow.py @@ -49,10 +49,12 @@ class InboxRelationships(TestCase): } self.assertFalse(models.UserFollowRequest.objects.exists()) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.inbox.activity_task(activity) self.assertEqual(mock.call_count, 1) - response_activity = json.loads(mock.call_args[0][1]) + response_activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(response_activity["type"], "Accept") # notification created @@ -77,17 +79,19 @@ class InboxRelationships(TestCase): "object": "https://example.com/user/mouse", } - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.inbox.activity_task(activity) # the follow relationship should exist follow = models.UserFollows.objects.get(user_object=self.local_user) self.assertEqual(follow.user_subject, self.remote_user) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.inbox.activity_task(activity) self.assertEqual(mock.call_count, 1) - response_activity = json.loads(mock.call_args[0][1]) + response_activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(response_activity["type"], "Accept") # the follow relationship should STILL exist @@ -109,7 +113,7 @@ class InboxRelationships(TestCase): broadcast=False, update_fields=["manually_approves_followers"] ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.inbox.activity_task(activity) # notification created @@ -132,7 +136,7 @@ class InboxRelationships(TestCase): self.local_user.save( broadcast=False, update_fields=["manually_approves_followers"] ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): request = models.UserFollowRequest.objects.create( user_subject=self.remote_user, user_object=self.local_user ) @@ -160,7 +164,7 @@ class InboxRelationships(TestCase): def test_unfollow(self): """remove a relationship""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): rel = models.UserFollows.objects.create( user_subject=self.remote_user, user_object=self.local_user ) @@ -186,7 +190,7 @@ class InboxRelationships(TestCase): @patch("bookwyrm.activitystreams.add_user_statuses_task.delay") def test_follow_accept(self, _): """a remote user approved a follow request from local""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): rel = models.UserFollowRequest.objects.create( user_subject=self.local_user, user_object=self.remote_user ) @@ -217,7 +221,7 @@ class InboxRelationships(TestCase): def test_follow_reject(self): """turn down a follow request""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): rel = models.UserFollowRequest.objects.create( user_subject=self.local_user, user_object=self.remote_user ) diff --git a/bookwyrm/tests/views/inbox/test_inbox_like.py b/bookwyrm/tests/views/inbox/test_inbox_like.py index db8f1fca..2f1b6629 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_like.py +++ b/bookwyrm/tests/views/inbox/test_inbox_like.py @@ -35,7 +35,7 @@ class InboxActivities(TestCase): outbox="https://example.com/users/rat/outbox", ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): with patch("bookwyrm.activitystreams.add_status_task.delay"): self.status = models.Status.objects.create( user=self.local_user, diff --git a/bookwyrm/tests/views/inbox/test_inbox_remove.py b/bookwyrm/tests/views/inbox/test_inbox_remove.py index cb4e4a16..55cc8120 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_remove.py +++ b/bookwyrm/tests/views/inbox/test_inbox_remove.py @@ -75,7 +75,7 @@ class InboxRemove(TestCase): def test_handle_remove_book_from_list(self): """listing a book""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): booklist = models.List.objects.create( name="test list", user=self.local_user, diff --git a/bookwyrm/tests/views/inbox/test_inbox_update.py b/bookwyrm/tests/views/inbox/test_inbox_update.py index 0efeac0c..248c1ad2 100644 --- a/bookwyrm/tests/views/inbox/test_inbox_update.py +++ b/bookwyrm/tests/views/inbox/test_inbox_update.py @@ -50,7 +50,7 @@ class InboxUpdate(TestCase): def test_update_list(self): """a new list""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): book_list = models.List.objects.create( name="hi", remote_id="https://example.com/list/22", user=self.local_user ) @@ -171,7 +171,7 @@ class InboxUpdate(TestCase): book = models.Work.objects.get(id=book.id) self.assertEqual(book.title, "Piranesi") - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") def test_update_status(self, *_): """edit a status""" diff --git a/bookwyrm/tests/views/preferences/test_block.py b/bookwyrm/tests/views/preferences/test_block.py index b23a6cbc..975142a1 100644 --- a/bookwyrm/tests/views/preferences/test_block.py +++ b/bookwyrm/tests/views/preferences/test_block.py @@ -9,7 +9,7 @@ from bookwyrm import models, views from bookwyrm.tests.validate_html import validate_html -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class BlockViews(TestCase): """view user and edit profile""" diff --git a/bookwyrm/tests/views/preferences/test_delete_user.py b/bookwyrm/tests/views/preferences/test_delete_user.py index 09f722f7..b6d87ccd 100644 --- a/bookwyrm/tests/views/preferences/test_delete_user.py +++ b/bookwyrm/tests/views/preferences/test_delete_user.py @@ -35,9 +35,9 @@ class DeleteUserViews(TestCase): self.book = models.Edition.objects.create( title="test", parent_work=models.Work.objects.create(title="test work") ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"), patch( - "bookwyrm.activitystreams.add_book_statuses_task.delay" - ): + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ), patch("bookwyrm.activitystreams.add_book_statuses_task.delay"): models.ShelfBook.objects.create( book=self.book, user=self.local_user, @@ -70,11 +70,11 @@ class DeleteUserViews(TestCase): self.assertIsNone(self.local_user.name) with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: view(request) self.assertEqual(delay_mock.call_count, 1) - activity = json.loads(delay_mock.call_args[0][1]) + activity = json.loads(delay_mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual( diff --git a/bookwyrm/tests/views/preferences/test_edit_user.py b/bookwyrm/tests/views/preferences/test_edit_user.py index b52875a9..7a845fbe 100644 --- a/bookwyrm/tests/views/preferences/test_edit_user.py +++ b/bookwyrm/tests/views/preferences/test_edit_user.py @@ -38,9 +38,9 @@ class EditUserViews(TestCase): self.book = models.Edition.objects.create( title="test", parent_work=models.Work.objects.create(title="test work") ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"), patch( - "bookwyrm.activitystreams.add_book_statuses_task.delay" - ): + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ), patch("bookwyrm.activitystreams.add_book_statuses_task.delay"): models.ShelfBook.objects.create( book=self.book, user=self.local_user, @@ -74,7 +74,7 @@ class EditUserViews(TestCase): self.assertIsNone(self.local_user.name) with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: view(request) self.assertEqual(delay_mock.call_count, 1) @@ -100,7 +100,7 @@ class EditUserViews(TestCase): request.user = self.local_user with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: view(request) self.assertEqual(delay_mock.call_count, 1) diff --git a/bookwyrm/tests/views/shelf/test_shelf.py b/bookwyrm/tests/views/shelf/test_shelf.py index 71df3631..ab88de0a 100644 --- a/bookwyrm/tests/views/shelf/test_shelf.py +++ b/bookwyrm/tests/views/shelf/test_shelf.py @@ -11,7 +11,7 @@ from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.tests.validate_html import validate_html -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @@ -39,7 +39,7 @@ class ShelfViews(TestCase): remote_id="https://example.com/book/1", parent_work=self.work, ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) @@ -142,7 +142,7 @@ class ShelfViews(TestCase): "", {"privacy": "public", "user": self.local_user.id, "name": "cool name"} ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, request.user.username, shelf.identifier) shelf.refresh_from_db() @@ -159,7 +159,7 @@ class ShelfViews(TestCase): "", {"privacy": "public", "user": self.local_user.id, "name": "cool name"} ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, request.user.username, shelf.identifier) self.assertEqual(shelf.name, "To Read") diff --git a/bookwyrm/tests/views/shelf/test_shelf_actions.py b/bookwyrm/tests/views/shelf/test_shelf_actions.py index 3efae0f4..1a7d56fd 100644 --- a/bookwyrm/tests/views/shelf/test_shelf_actions.py +++ b/bookwyrm/tests/views/shelf/test_shelf_actions.py @@ -9,7 +9,7 @@ from django.test.client import RequestFactory from bookwyrm import forms, models, views -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @@ -37,7 +37,7 @@ class ShelfActionViews(TestCase): remote_id="https://example.com/book/1", parent_work=self.work, ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) @@ -49,11 +49,13 @@ class ShelfActionViews(TestCase): "", {"book": self.book.id, "shelf": self.shelf.identifier} ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.shelve(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") item = models.ShelfBook.objects.get() @@ -69,7 +71,7 @@ class ShelfActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.shelve(request) # make sure the book is on the shelf self.assertEqual(shelf.books.get(), self.book) @@ -82,7 +84,7 @@ class ShelfActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.shelve(request) # make sure the book is on the shelf self.assertEqual(shelf.books.get(), self.book) @@ -95,7 +97,7 @@ class ShelfActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.shelve(request) # make sure the book is on the shelf self.assertEqual(shelf.books.get(), self.book) @@ -118,7 +120,7 @@ class ShelfActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.shelve(request) # make sure the book is on the shelf self.assertEqual(shelf.books.get(), self.book) @@ -126,7 +128,7 @@ class ShelfActionViews(TestCase): def test_unshelve(self, *_): """remove a book from a shelf""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ShelfBook.objects.create( book=self.book, user=self.local_user, shelf=self.shelf ) @@ -136,9 +138,11 @@ class ShelfActionViews(TestCase): self.assertEqual(self.shelf.books.count(), 1) request = self.factory.post("", {"book": self.book.id, "shelf": self.shelf.id}) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.unshelve(request) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Remove") self.assertEqual(activity["object"]["id"], item.remote_id) self.assertEqual(self.shelf.books.count(), 0) @@ -192,7 +196,7 @@ class ShelfActionViews(TestCase): def test_delete_shelf_has_book(self, *_): """delete a brand new custom shelf""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ShelfBook.objects.create( book=self.book, user=self.local_user, shelf=self.shelf ) diff --git a/bookwyrm/tests/views/test_author.py b/bookwyrm/tests/views/test_author.py index ccbfe549..32b1565e 100644 --- a/bookwyrm/tests/views/test_author.py +++ b/bookwyrm/tests/views/test_author.py @@ -111,7 +111,7 @@ class AuthorViews(TestCase): request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, author.id) author.refresh_from_db() self.assertEqual(author.name, "New Name") diff --git a/bookwyrm/tests/views/test_discover.py b/bookwyrm/tests/views/test_discover.py index 4b8927bc..b2a82241 100644 --- a/bookwyrm/tests/views/test_discover.py +++ b/bookwyrm/tests/views/test_discover.py @@ -41,7 +41,7 @@ class DiscoverViews(TestCase): self.assertEqual(result.status_code, 200) result.render() - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_status_task.delay") def test_discover_page(self, *_): """there are so many views, this just makes sure it LOADS""" diff --git a/bookwyrm/tests/views/test_feed.py b/bookwyrm/tests/views/test_feed.py index a6f220b5..5c6a4dd3 100644 --- a/bookwyrm/tests/views/test_feed.py +++ b/bookwyrm/tests/views/test_feed.py @@ -57,7 +57,7 @@ class FeedViews(TestCase): def test_status_page(self, *_): """there are so many views, this just makes sure it LOADS""" view = views.Status.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create(content="hi", user=self.local_user) request = self.factory.get("") request.user = self.local_user @@ -95,7 +95,7 @@ class FeedViews(TestCase): local=True, localname="rat", ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create(content="hi", user=another_user) request = self.factory.get("") @@ -115,7 +115,7 @@ class FeedViews(TestCase): image = Image.open(image_file) output = BytesIO() image.save(output, format=image.format) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Review.objects.create( content="hi", user=self.local_user, @@ -144,7 +144,7 @@ class FeedViews(TestCase): def test_replies_page(self, *_): """there are so many views, this just makes sure it LOADS""" view = views.Replies.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): status = models.Status.objects.create(content="hi", user=self.local_user) request = self.factory.get("") request.user = self.local_user @@ -171,7 +171,7 @@ class FeedViews(TestCase): result.render() self.assertEqual(result.status_code, 200) - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") def test_get_suggested_book(self, *_): """gets books the ~*~ algorithm ~*~ thinks you want to post about""" diff --git a/bookwyrm/tests/views/test_follow.py b/bookwyrm/tests/views/test_follow.py index 947c55cf..25b5a014 100644 --- a/bookwyrm/tests/views/test_follow.py +++ b/bookwyrm/tests/views/test_follow.py @@ -59,7 +59,7 @@ class FollowViews(TestCase): request.user = self.local_user self.assertEqual(models.UserFollowRequest.objects.count(), 0) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.follow(request) rel = models.UserFollowRequest.objects.get() @@ -86,7 +86,7 @@ class FollowViews(TestCase): request.user = self.local_user self.assertEqual(models.UserFollowRequest.objects.count(), 0) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.follow(request) rel = models.UserFollowRequest.objects.get() @@ -111,7 +111,7 @@ class FollowViews(TestCase): request.user = self.local_user self.assertEqual(models.UserFollowRequest.objects.count(), 0) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.follow(request) rel = models.UserFollows.objects.get() @@ -127,10 +127,12 @@ class FollowViews(TestCase): request.user = self.local_user self.remote_user.followers.add(self.local_user) self.assertEqual(self.remote_user.followers.count(), 1) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.unfollow(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args_list[0][0][1]) + activity = json.loads(mock.call_args_list[0][1]["args"][1]) self.assertEqual(activity["type"], "Undo") self.assertEqual(self.remote_user.followers.count(), 0) @@ -147,7 +149,7 @@ class FollowViews(TestCase): user_subject=self.remote_user, user_object=self.local_user ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.accept_follow_request(request) # request should be deleted self.assertEqual(models.UserFollowRequest.objects.filter(id=rel.id).count(), 0) @@ -166,7 +168,7 @@ class FollowViews(TestCase): user_subject=self.remote_user, user_object=self.local_user ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.delete_follow_request(request) # request should be deleted self.assertEqual(models.UserFollowRequest.objects.filter(id=rel.id).count(), 0) diff --git a/bookwyrm/tests/views/test_get_started.py b/bookwyrm/tests/views/test_get_started.py index ff441b57..6d1819a4 100644 --- a/bookwyrm/tests/views/test_get_started.py +++ b/bookwyrm/tests/views/test_get_started.py @@ -56,7 +56,7 @@ class GetStartedViews(TestCase): self.assertIsNone(self.local_user.name) with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: view(request) self.assertEqual(delay_mock.call_count, 1) @@ -98,7 +98,7 @@ class GetStartedViews(TestCase): self.assertFalse(self.local_user.shelfbook_set.exists()) with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as delay_mock: view(request) self.assertEqual(delay_mock.call_count, 1) diff --git a/bookwyrm/tests/views/test_goal.py b/bookwyrm/tests/views/test_goal.py index 557510d7..73207240 100644 --- a/bookwyrm/tests/views/test_goal.py +++ b/bookwyrm/tests/views/test_goal.py @@ -123,7 +123,7 @@ class GoalViews(TestCase): }, ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, self.local_user.localname, self.year) goal = models.AnnualGoal.objects.get() diff --git a/bookwyrm/tests/views/test_group.py b/bookwyrm/tests/views/test_group.py index c7e0a0f7..b18ce6b4 100644 --- a/bookwyrm/tests/views/test_group.py +++ b/bookwyrm/tests/views/test_group.py @@ -10,7 +10,7 @@ from bookwyrm import models, views, forms from bookwyrm.tests.validate_html import validate_html -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class GroupViews(TestCase): """view group and edit details""" diff --git a/bookwyrm/tests/views/test_helpers.py b/bookwyrm/tests/views/test_helpers.py index 8f7b45a3..1aae830f 100644 --- a/bookwyrm/tests/views/test_helpers.py +++ b/bookwyrm/tests/views/test_helpers.py @@ -55,7 +55,7 @@ class ViewsHelpers(TestCase): datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json") self.userdata = json.loads(datafile.read_bytes()) del self.userdata["icon"] - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.shelf = models.Shelf.objects.create( name="Test Shelf", identifier="test-shelf", user=self.local_user ) @@ -166,7 +166,7 @@ class ViewsHelpers(TestCase): def test_handle_reading_status_to_read(self, *_): """posts shelve activities""" shelf = self.local_user.shelf_set.get(identifier="to-read") - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.helpers.handle_reading_status( self.local_user, shelf, self.book, "public" ) @@ -178,7 +178,7 @@ class ViewsHelpers(TestCase): def test_handle_reading_status_reading(self, *_): """posts shelve activities""" shelf = self.local_user.shelf_set.get(identifier="reading") - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.helpers.handle_reading_status( self.local_user, shelf, self.book, "public" ) @@ -190,7 +190,7 @@ class ViewsHelpers(TestCase): def test_handle_reading_status_read(self, *_): """posts shelve activities""" shelf = self.local_user.shelf_set.get(identifier="read") - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.helpers.handle_reading_status( self.local_user, shelf, self.book, "public" ) @@ -201,7 +201,7 @@ class ViewsHelpers(TestCase): def test_handle_reading_status_other(self, *_): """posts shelve activities""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.helpers.handle_reading_status( self.local_user, self.shelf, self.book, "public" ) diff --git a/bookwyrm/tests/views/test_interaction.py b/bookwyrm/tests/views/test_interaction.py index e8b9353e..aa402952 100644 --- a/bookwyrm/tests/views/test_interaction.py +++ b/bookwyrm/tests/views/test_interaction.py @@ -7,7 +7,7 @@ from django.test.client import RequestFactory from bookwyrm import models, views -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.remove_status_task.delay") class InteractionViews(TestCase): """viewing and creating statuses""" @@ -74,7 +74,7 @@ class InteractionViews(TestCase): self.assertEqual(models.Favorite.objects.count(), 1) self.assertEqual(models.Notification.objects.count(), 1) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, status.id) self.assertEqual(models.Favorite.objects.count(), 0) self.assertEqual(models.Notification.objects.count(), 0) @@ -110,12 +110,12 @@ class InteractionViews(TestCase): status = models.Status.objects.create(user=self.local_user, content="hi") with patch( - "bookwyrm.models.activitypub_mixin.broadcast_task.delay" + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as broadcast_mock: view(request, status.id) self.assertEqual(broadcast_mock.call_count, 1) - activity = json.loads(broadcast_mock.call_args[0][1]) + activity = json.loads(broadcast_mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Announce") boost = models.Boost.objects.get() diff --git a/bookwyrm/tests/views/test_list.py b/bookwyrm/tests/views/test_list.py index 35befec6..c670ad18 100644 --- a/bookwyrm/tests/views/test_list.py +++ b/bookwyrm/tests/views/test_list.py @@ -61,7 +61,7 @@ class ListViews(TestCase): parent_work=work_four, ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.list = models.List.objects.create( name="Test List", user=self.local_user ) @@ -73,7 +73,7 @@ class ListViews(TestCase): def test_lists_page(self): """there are so many views, this just makes sure it LOADS""" view = views.Lists.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.List.objects.create(name="Public list", user=self.local_user) models.List.objects.create( name="Private list", privacy="direct", user=self.local_user @@ -96,7 +96,7 @@ class ListViews(TestCase): def test_saved_lists_page(self): """there are so many views, this just makes sure it LOADS""" view = views.SavedLists.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): booklist = models.List.objects.create( name="Public list", user=self.local_user ) @@ -116,7 +116,7 @@ class ListViews(TestCase): def test_saved_lists_page_empty(self): """there are so many views, this just makes sure it LOADS""" view = views.SavedLists.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.List.objects.create(name="Public list", user=self.local_user) models.List.objects.create( name="Private list", privacy="direct", user=self.local_user @@ -153,11 +153,13 @@ class ListViews(TestCase): }, ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: result = view(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Create") self.assertEqual(activity["actor"], self.local_user.remote_id) @@ -172,7 +174,7 @@ class ListViews(TestCase): view = views.List.as_view() request = self.factory.get("") request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -191,7 +193,7 @@ class ListViews(TestCase): def test_list_page_sorted(self): """there are so many views, this just makes sure it LOADS""" view = views.List.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): for (i, book) in enumerate([self.book, self.book_two, self.book_three]): models.ListItem.objects.create( book_list=self.list, @@ -253,7 +255,7 @@ class ListViews(TestCase): def test_list_page_logged_out(self): """there are so many views, this just makes sure it LOADS""" view = views.List.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -276,7 +278,7 @@ class ListViews(TestCase): view = views.List.as_view() request = self.factory.get("") request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -320,11 +322,13 @@ class ListViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: result = view(request, self.list.id) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Update") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["id"], self.list.remote_id) @@ -340,7 +344,7 @@ class ListViews(TestCase): def test_curate_page(self): """there are so many views, this just makes sure it LOADS""" view = views.Curate.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.List.objects.create(name="Public list", user=self.local_user) models.List.objects.create( name="Private list", privacy="direct", user=self.local_user @@ -360,7 +364,7 @@ class ListViews(TestCase): def test_user_lists_page(self): """there are so many views, this just makes sure it LOADS""" view = views.UserLists.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.List.objects.create(name="Public list", user=self.local_user) models.List.objects.create( name="Private list", privacy="direct", user=self.local_user diff --git a/bookwyrm/tests/views/test_list_actions.py b/bookwyrm/tests/views/test_list_actions.py index f7775d19..1d9f46b3 100644 --- a/bookwyrm/tests/views/test_list_actions.py +++ b/bookwyrm/tests/views/test_list_actions.py @@ -61,7 +61,7 @@ class ListActionViews(TestCase): parent_work=work_four, ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.list = models.List.objects.create( name="Test List", user=self.local_user ) @@ -71,7 +71,7 @@ class ListActionViews(TestCase): def test_delete_list(self): """delete an entire list""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -88,9 +88,11 @@ class ListActionViews(TestCase): ) request = self.factory.post("") request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.delete_list(request, self.list.id) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"]["id"], self.list.remote_id) @@ -110,7 +112,7 @@ class ListActionViews(TestCase): def test_curate_approve(self): """approve a pending item""" view = views.Curate.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): pending = models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -128,11 +130,13 @@ class ListActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: view(request, self.list.id) self.assertEqual(mock.call_count, 2) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["target"], self.list.remote_id) @@ -145,7 +149,7 @@ class ListActionViews(TestCase): def test_curate_reject(self): """approve a pending item""" view = views.Curate.as_view() - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): pending = models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -179,10 +183,12 @@ class ListActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.list.add_book(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["target"], self.list.remote_id) @@ -214,7 +220,7 @@ class ListActionViews(TestCase): }, ) request_two.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.list.add_book(request_one) views.list.add_book(request_two) @@ -256,7 +262,7 @@ class ListActionViews(TestCase): ) request_three.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.list.add_book(request_one) views.list.add_book(request_two) views.list.add_book(request_three) @@ -271,7 +277,7 @@ class ListActionViews(TestCase): remove_request = self.factory.post("", {"item": items[1].id}) remove_request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.list.remove_book(remove_request, self.list.id) items = self.list.listitem_set.order_by("order").all() self.assertEqual(items[0].book, self.book) @@ -293,7 +299,7 @@ class ListActionViews(TestCase): }, ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -330,7 +336,7 @@ class ListActionViews(TestCase): its order should be at the end of the approved books and before the remaining pending books. """ - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -370,7 +376,7 @@ class ListActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): view(request, self.list.id) items = self.list.listitem_set.order_by("order").all() @@ -422,7 +428,7 @@ class ListActionViews(TestCase): ) request_three.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.list.add_book(request_one) views.list.add_book(request_two) views.list.add_book(request_three) @@ -437,7 +443,7 @@ class ListActionViews(TestCase): set_position_request = self.factory.post("", {"position": 1}) set_position_request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.list.set_book_position(set_position_request, items[2].id) items = self.list.listitem_set.order_by("order").all() self.assertEqual(items[0].book, self.book_three) @@ -460,10 +466,12 @@ class ListActionViews(TestCase): ) request.user = self.rat - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.list.add_book(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") self.assertEqual(activity["actor"], self.rat.remote_id) self.assertEqual(activity["target"], self.list.remote_id) @@ -486,11 +494,13 @@ class ListActionViews(TestCase): ) request.user = self.rat - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.list.add_book(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") self.assertEqual(activity["actor"], self.rat.remote_id) @@ -516,10 +526,12 @@ class ListActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ) as mock: views.list.add_book(request) self.assertEqual(mock.call_count, 1) - activity = json.loads(mock.call_args[0][1]) + activity = json.loads(mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Add") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["target"], self.list.remote_id) @@ -532,7 +544,7 @@ class ListActionViews(TestCase): def test_remove_book(self): """take an item off a list""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): item = models.ListItem.objects.create( book_list=self.list, user=self.local_user, @@ -549,13 +561,13 @@ class ListActionViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.list.remove_book(request, self.list.id) self.assertFalse(self.list.listitem_set.exists()) def test_remove_book_unauthorized(self): """take an item off a list""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): item = models.ListItem.objects.create( book_list=self.list, user=self.local_user, book=self.book, order=1 ) diff --git a/bookwyrm/tests/views/test_notifications.py b/bookwyrm/tests/views/test_notifications.py index dd28a811..5df62b1d 100644 --- a/bookwyrm/tests/views/test_notifications.py +++ b/bookwyrm/tests/views/test_notifications.py @@ -25,7 +25,7 @@ class NotificationViews(TestCase): local=True, localname="mouse", ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.status = models.Status.objects.create( content="hi", user=self.local_user, diff --git a/bookwyrm/tests/views/test_outbox.py b/bookwyrm/tests/views/test_outbox.py index a1f62cc6..5c5d47b0 100644 --- a/bookwyrm/tests/views/test_outbox.py +++ b/bookwyrm/tests/views/test_outbox.py @@ -11,7 +11,7 @@ from bookwyrm.settings import USER_AGENT # pylint: disable=too-many-public-methods -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class OutboxView(TestCase): """sends out activities""" diff --git a/bookwyrm/tests/views/test_reading.py b/bookwyrm/tests/views/test_reading.py index 4e5206f6..4ec50165 100644 --- a/bookwyrm/tests/views/test_reading.py +++ b/bookwyrm/tests/views/test_reading.py @@ -64,7 +64,7 @@ class ReadingViews(TestCase): }, ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.ReadingStatus.as_view()(request, "start", self.book.id) self.assertEqual(shelf.books.get(), self.book) @@ -100,7 +100,7 @@ class ReadingViews(TestCase): }, ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.ReadingStatus.as_view()(request, "start", self.book.id) self.assertEqual(shelf.books.get(), self.book) @@ -124,7 +124,7 @@ class ReadingViews(TestCase): def test_start_reading_reshelve(self, *_): """begin a book""" to_read_shelf = self.local_user.shelf_set.get(identifier=models.Shelf.TO_READ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): models.ShelfBook.objects.create( shelf=to_read_shelf, book=self.book, user=self.local_user ) @@ -135,7 +135,7 @@ class ReadingViews(TestCase): request = self.factory.post("") request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.ReadingStatus.as_view()(request, "start", self.book.id) self.assertFalse(to_read_shelf.books.exists()) @@ -162,7 +162,7 @@ class ReadingViews(TestCase): ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.ReadingStatus.as_view()(request, "finish", self.book.id) self.assertEqual(shelf.books.get(), self.book) @@ -267,7 +267,7 @@ class ReadingViews(TestCase): }, ) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): views.update_progress(request, self.book.id) status = models.Comment.objects.get() diff --git a/bookwyrm/tests/views/test_readthrough.py b/bookwyrm/tests/views/test_readthrough.py index ef149ce3..5b554748 100644 --- a/bookwyrm/tests/views/test_readthrough.py +++ b/bookwyrm/tests/views/test_readthrough.py @@ -9,7 +9,7 @@ from bookwyrm import models @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay") -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.add_book_statuses_task.delay") @patch("bookwyrm.activitystreams.remove_book_statuses_task.delay") class ReadThrough(TestCase): @@ -32,7 +32,7 @@ class ReadThrough(TestCase): "cinco", "cinco@example.com", "seissiete", local=True, localname="cinco" ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): self.client.force_login(self.user) @patch("bookwyrm.activitystreams.remove_user_statuses_task.delay") diff --git a/bookwyrm/tests/views/test_rss_feed.py b/bookwyrm/tests/views/test_rss_feed.py index d4d11261..409c306d 100644 --- a/bookwyrm/tests/views/test_rss_feed.py +++ b/bookwyrm/tests/views/test_rss_feed.py @@ -6,7 +6,7 @@ from bookwyrm import models from bookwyrm.views import rss_feed -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.activitystreams.ActivityStream.get_activity_stream") @patch("bookwyrm.activitystreams.add_status_task.delay") class RssFeedView(TestCase): diff --git a/bookwyrm/tests/views/test_search.py b/bookwyrm/tests/views/test_search.py index 3299249a..f8045511 100644 --- a/bookwyrm/tests/views/test_search.py +++ b/bookwyrm/tests/views/test_search.py @@ -139,7 +139,7 @@ class Views(TestCase): def test_search_lists(self): """searches remote connectors""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): booklist = models.List.objects.create( user=self.local_user, name="test list" ) diff --git a/bookwyrm/tests/views/test_status.py b/bookwyrm/tests/views/test_status.py index db61b152..b5d7ac16 100644 --- a/bookwyrm/tests/views/test_status.py +++ b/bookwyrm/tests/views/test_status.py @@ -14,7 +14,7 @@ from bookwyrm.tests.validate_html import validate_html @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.remove_status_task.delay") -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") class StatusViews(TestCase): """viewing and creating statuses""" @@ -310,7 +310,7 @@ http://www.fish.com/""" with patch("bookwyrm.activitystreams.remove_status_task.delay") as redis_mock: view(request, status.id) self.assertTrue(redis_mock.called) - activity = json.loads(mock.call_args_list[1][0][1]) + activity = json.loads(mock.call_args_list[1][1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["object"]["type"], "Tombstone") status.refresh_from_db() @@ -344,7 +344,7 @@ http://www.fish.com/""" with patch("bookwyrm.activitystreams.remove_status_task.delay") as redis_mock: view(request, status.id) self.assertTrue(redis_mock.called) - activity = json.loads(mock.call_args_list[1][0][1]) + activity = json.loads(mock.call_args_list[1][1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["object"]["type"], "Tombstone") status.refresh_from_db() @@ -396,7 +396,7 @@ http://www.fish.com/""" request.user = self.local_user view(request, "comment", existing_status_id=status.id) - activity = json.loads(mock.call_args_list[1][0][1]) + activity = json.loads(mock.call_args_list[1][1]["args"][1]) self.assertEqual(activity["type"], "Update") self.assertEqual(activity["object"]["id"], status.remote_id) diff --git a/bookwyrm/tests/views/test_user.py b/bookwyrm/tests/views/test_user.py index 1183fa24..ddb029cc 100644 --- a/bookwyrm/tests/views/test_user.py +++ b/bookwyrm/tests/views/test_user.py @@ -34,9 +34,11 @@ class UserViews(TestCase): self.book = models.Edition.objects.create( title="test", parent_work=models.Work.objects.create(title="test work") ) - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"), patch( - "bookwyrm.suggested_users.rerank_suggestions_task.delay" - ), patch("bookwyrm.activitystreams.add_book_statuses_task.delay"): + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ), patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.add_book_statuses_task.delay" + ): models.ShelfBook.objects.create( book=self.book, user=self.local_user, From cad5a128ac94b76e0b59763ba6e934ec1946100a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 10:19:13 -0800 Subject: [PATCH 056/121] Fixes call to add_status signal --- bookwyrm/tests/activitystreams/test_signals.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bookwyrm/tests/activitystreams/test_signals.py b/bookwyrm/tests/activitystreams/test_signals.py index b3a99620..34aeb947 100644 --- a/bookwyrm/tests/activitystreams/test_signals.py +++ b/bookwyrm/tests/activitystreams/test_signals.py @@ -53,11 +53,12 @@ class ActivitystreamsSignals(TestCase): status = models.Status.objects.create( user=self.remote_user, content="hi", privacy="public" ) - with patch("bookwyrm.activitystreams.add_status_task.delay") as mock: + with patch("bookwyrm.activitystreams.add_status_task.apply_async") as mock: activitystreams.add_status_on_create_command(models.Status, status, False) self.assertEqual(mock.call_count, 1) - args = mock.call_args[0] - self.assertEqual(args[0], status.id) + args = mock.call_args[1] + self.assertEqual(args["args"][0], status.id) + self.assertEqual(args["queue"], "high_priority") def test_populate_streams_on_account_create(self, _): """create streams for a user""" From 309d289a6515a1da8e12874127c5ce2505979877 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 10:49:49 -0800 Subject: [PATCH 057/121] A few more mocks --- .../importers/test_librarything_import.py | 18 ------------------ bookwyrm/tests/models/test_status_model.py | 17 ++++++----------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index ed081aa2..1ec94bbb 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -84,24 +84,6 @@ class LibrarythingImport(TestCase): self.assertEqual(retry_items[1].index, 1) self.assertEqual(retry_items[1].data["Book Id"], "5015319") - @responses.activate - def test_start_import_task(self, *_): - """resolve entry""" - import_job = self.importer.create_job( - self.local_user, self.csv, False, "unlisted" - ) - book = models.Edition.objects.create(title="Test Book") - - with patch( - "bookwyrm.models.import_job.ImportItem.get_book_from_isbn" - ) as resolve: - resolve.return_value = book - with patch("bookwyrm.importers.importer.handle_imported_book"): - start_import_task(self.importer.service, import_job.id) - - import_item = models.ImportItem.objects.get(job=import_job, index=0) - self.assertEqual(import_item.book.id, book.id) - def test_handle_imported_book(self, *_): """librarything import added a book, this adds related connections""" shelf = self.local_user.shelf_set.filter(identifier="read").first() diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index 7d0dd138..822d837a 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -362,19 +362,15 @@ class Status(TestCase): def test_favorite(self, *_): """fav a status""" - real_broadcast = models.Favorite.broadcast - - def fav_broadcast_mock(_, activity, user): - """ok""" - self.assertEqual(user.remote_id, self.local_user.remote_id) - self.assertEqual(activity["type"], "Like") - - models.Favorite.broadcast = fav_broadcast_mock - status = models.Status.objects.create( content="test content", user=self.local_user ) - fav = models.Favorite.objects.create(status=status, user=self.local_user) + + with patch("bookwyrm.models.Favorite.broadcast") as mock: + fav = models.Favorite.objects.create(status=status, user=self.local_user) + args = mock.call_args[0] + self.assertEqual(args[1].remote_id, self.local_user.remote_id) + self.assertEqual(args[0]["type"], "Like") # can't fav a status twice with self.assertRaises(IntegrityError): @@ -384,7 +380,6 @@ class Status(TestCase): self.assertEqual(activity["type"], "Like") self.assertEqual(activity["actor"], self.local_user.remote_id) self.assertEqual(activity["object"], status.remote_id) - models.Favorite.broadcast = real_broadcast def test_boost(self, *_): """boosting, this one's a bit fussy""" From 843e9a7b320b0d2613805b700ccf78728b2e4275 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 11:03:47 -0800 Subject: [PATCH 058/121] Updates existing locales --- locale/de_DE/LC_MESSAGES/django.mo | Bin 57136 -> 61566 bytes locale/de_DE/LC_MESSAGES/django.po | 923 ++++++++++++++++----------- locale/es_ES/LC_MESSAGES/django.mo | Bin 57328 -> 61028 bytes locale/es_ES/LC_MESSAGES/django.po | 657 ++++++++++++------- locale/fr_FR/LC_MESSAGES/django.mo | Bin 52510 -> 62690 bytes locale/fr_FR/LC_MESSAGES/django.po | 713 +++++++++++++-------- locale/zh_Hans/LC_MESSAGES/django.mo | Bin 44096 -> 56859 bytes locale/zh_Hans/LC_MESSAGES/django.po | 587 +++++++++++------ locale/zh_Hant/LC_MESSAGES/django.mo | Bin 38249 -> 38249 bytes locale/zh_Hant/LC_MESSAGES/django.po | 553 ++++++++++------ 10 files changed, 2184 insertions(+), 1249 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/django.mo b/locale/de_DE/LC_MESSAGES/django.mo index 516685a0ec3dd82a26bd6b98ded4e934ba7e7bc8..19aa94f8fc132c768e7691d5fc774579469219ee 100644 GIT binary patch delta 24108 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 QP}Of5#Efum`zHDS0KL&pjsO4v diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po index 8fd930fe..d8352466 100644 --- a/locale/de_DE/LC_MESSAGES/django.po +++ b/locale/de_DE/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-15 22:03+0000\n" -"PO-Revision-Date: 2021-10-20 17:38\n" +"POT-Creation-Date: 2021-10-24 14:09+0000\n" +"PO-Revision-Date: 2021-10-26 22:29\n" "Last-Translator: Mouse Reeve \n" "Language-Team: German\n" "Language: de\n" @@ -19,7 +19,7 @@ msgstr "" #: bookwyrm/forms.py:242 msgid "A user with this email already exists." -msgstr "Es existiert bereits ein Account mit dieser E-Mail-Adresse." +msgstr "Es existiert bereits ein Benutzer*inkonto mit dieser E-Mail-Adresse." #: bookwyrm/forms.py:256 msgid "One Day" @@ -35,7 +35,7 @@ msgstr "Ein Monat" #: bookwyrm/forms.py:259 msgid "Does Not Expire" -msgstr "Läuft nicht aus" +msgstr "Läuft nicht ab" #: bookwyrm/forms.py:263 #, python-brace-format @@ -46,29 +46,29 @@ msgstr "{i}-mal verwendbar" msgid "Unlimited" msgstr "Unbegrenzt" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "Reihenfolge der Liste" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "Buchtitel" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 -#: bookwyrm/templates/shelf/shelf.html:168 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Bewertung" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "Sortieren nach" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "Aufsteigend" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "Absteigend" @@ -141,15 +141,15 @@ msgstr "%(value)s ist keine gültige remote_id" #: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 #, python-format msgid "%(value)s is not a valid username" -msgstr "%(value)s ist kein gültiger Username" +msgstr "%(value)s ist kein gültiger Benutzer*inname" #: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 msgid "username" -msgstr "Username" +msgstr "Benutzer*inname" #: bookwyrm/models/fields.py:186 msgid "A user with that username already exists." -msgstr "Dieser Benutzename ist bereits vergeben." +msgstr "Dieser Benutzer*inname ist bereits vergeben." #: bookwyrm/settings.py:118 msgid "Home Timeline" @@ -161,11 +161,11 @@ msgstr "Startseite" #: bookwyrm/settings.py:119 msgid "Books Timeline" -msgstr "Bücher-Zeitachse" +msgstr "Bücher-Zeitleiste" #: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "Bücher" @@ -191,7 +191,7 @@ msgstr "Português (Portugiesisch)" #: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" -msgstr "简体中文 (Vereinfachtes Chinesisch)" +msgstr "简体中文 (vereinfachtes Chinesisch)" #: bookwyrm/settings.py:171 msgid "繁體中文 (Traditional Chinese)" @@ -203,7 +203,7 @@ msgstr "Nicht gefunden" #: bookwyrm/templates/404.html:9 msgid "The page you requested doesn't seem to exist!" -msgstr "Die Seite die du angefordert hast scheint nicht zu existieren!" +msgstr "Die Seite, die du angefordert hast, scheint nicht zu existieren!" #: bookwyrm/templates/500.html:4 msgid "Oops!" @@ -215,25 +215,25 @@ msgstr "Serverfehler" #: bookwyrm/templates/500.html:9 msgid "Something went wrong! Sorry about that." -msgstr "Etwas lief schief. Entschuldigung!" +msgstr "Etwas ist schief gelaufen! Tut uns leid." #: bookwyrm/templates/author/author.html:17 #: bookwyrm/templates/author/author.html:18 msgid "Edit Author" -msgstr "Autor*in editieren" +msgstr "Autor*in bearbeiten" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "Alternative Namen:" #: bookwyrm/templates/author/author.html:45 msgid "Born:" -msgstr "Geboren" +msgstr "Geboren:" #: bookwyrm/templates/author/author.html:52 msgid "Died:" -msgstr "Gestorben" +msgstr "Gestorben:" #: bookwyrm/templates/author/author.html:61 msgid "Wikipedia" @@ -242,7 +242,7 @@ msgstr "Wikipedia" #: bookwyrm/templates/author/author.html:69 #: bookwyrm/templates/book/book.html:94 msgid "View on OpenLibrary" -msgstr "In OpenLibrary ansehen" +msgstr "Auf OpenLibrary ansehen" #: bookwyrm/templates/author/author.html:77 #: bookwyrm/templates/book/book.html:97 @@ -276,71 +276,72 @@ msgstr "Hinzugefügt:" msgid "Updated:" msgstr "Aktualisiert:" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "Zuletzt bearbeitet von:" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "Metadaten" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "Name:" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "Mehrere Werte durch Kommas getrennt eingeben." -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "Über mich:" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "Wikipedialink:" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "Geburtsdatum:" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "Todesdatum:" -#: bookwyrm/templates/author/edit_author.html:79 -msgid "Author Identifiers" -msgstr "Autor*innenidentifikatoren" - #: bookwyrm/templates/author/edit_author.html:81 +msgid "Author Identifiers" +msgstr "Autor*in-Identifikatoren" + +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "Openlibrary-Schlüssel:" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "Inventaire-ID:" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "Librarything-Schlüssel:" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "Goodreads-Schlüssel:" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -352,11 +353,13 @@ msgstr "Goodreads-Schlüssel:" msgid "Save" msgstr "Speichern" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -373,12 +376,12 @@ msgstr "von" #: bookwyrm/templates/book/book.html:55 bookwyrm/templates/book/book.html:56 msgid "Edit Book" -msgstr "Buch editieren" +msgstr "Buch bearbeiten" #: bookwyrm/templates/book/book.html:73 #: bookwyrm/templates/book/cover_modal.html:5 msgid "Add cover" -msgstr "Cover hinzufügen" +msgstr "Titelbild hinzufügen" #: bookwyrm/templates/book/book.html:77 msgid "Failed to load cover" @@ -388,8 +391,8 @@ msgstr "Fehler beim Laden des Titelbilds" #, python-format msgid "(%(review_count)s review)" msgid_plural "(%(review_count)s reviews)" -msgstr[0] "(%(review_count)s Bewertung)" -msgstr[1] "(%(review_count)s Bewertungen)" +msgstr[0] "(%(review_count)s Besprechung)" +msgstr[1] "(%(review_count)s Besprechungen)" #: bookwyrm/templates/book/book.html:129 msgid "Add Description" @@ -397,7 +400,7 @@ msgstr "Beschreibung hinzufügen" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "Beschreibung:" @@ -460,7 +463,7 @@ msgstr "Orte" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "Listen" @@ -470,7 +473,7 @@ msgstr "Zur Liste hinzufügen" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -483,7 +486,7 @@ msgstr "ISBN:" #: bookwyrm/templates/book/book_identifiers.html:15 #: bookwyrm/templates/book/edit/edit_book_form.html:232 msgid "OCLC Number:" -msgstr "OCLC Nummer:" +msgstr "OCLC-Nummer:" #: bookwyrm/templates/book/book_identifiers.html:22 #: bookwyrm/templates/book/edit/edit_book_form.html:240 @@ -498,13 +501,13 @@ msgstr "Titelbild hochladen:" #: bookwyrm/templates/book/cover_modal.html:23 #: bookwyrm/templates/book/edit/edit_book_form.html:148 msgid "Load cover from url:" -msgstr "Cover von URL laden:" +msgstr "Titelbild von URL laden:" #: bookwyrm/templates/book/edit/edit_book.html:5 #: bookwyrm/templates/book/edit/edit_book.html:11 #, python-format msgid "Edit \"%(book_title)s\"" -msgstr "\"%(book_title)s \" bearbeiten" +msgstr "„%(book_title)s“ bearbeiten" #: bookwyrm/templates/book/edit/edit_book.html:5 #: bookwyrm/templates/book/edit/edit_book.html:13 @@ -518,7 +521,7 @@ msgstr "Buchinfo bestätigen" #: bookwyrm/templates/book/edit/edit_book.html:55 #, python-format msgid "Is \"%(name)s\" an existing author?" -msgstr "Existiert \"%(name)s\" bereits als Autor:in?" +msgstr "Existiert „%(name)s“ bereits als Autor*in?" #: bookwyrm/templates/book/edit/edit_book.html:64 #, python-format @@ -527,23 +530,25 @@ msgstr "Autor*in von %(book_title)s" #: bookwyrm/templates/book/edit/edit_book.html:68 msgid "This is a new author" -msgstr "Neue:r Autor:in" +msgstr "Neue*r Autor*in" #: bookwyrm/templates/book/edit/edit_book.html:75 #, python-format msgid "Creating a new author: %(name)s" -msgstr "Neu als Autor:in erstellen: %(name)s" +msgstr "Als neue*r Autor*in erstellen: %(name)s" #: bookwyrm/templates/book/edit/edit_book.html:82 msgid "Is this an edition of an existing work?" -msgstr "Ist das eine Edition eines vorhandenen Werkes?" +msgstr "Ist das eine Ausgabe eines vorhandenen Werkes?" #: bookwyrm/templates/book/edit/edit_book.html:90 msgid "This is a new work" msgstr "Dies ist ein neues Werk." #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "Bestätigen" @@ -567,7 +572,7 @@ msgstr "Serie:" #: bookwyrm/templates/book/edit/edit_book_form.html:53 msgid "Series number:" -msgstr "Seriennummer:" +msgstr "Nummer in der Serie:" #: bookwyrm/templates/book/edit/edit_book_form.html:63 msgid "Languages:" @@ -601,7 +606,7 @@ msgstr "%(name)s entfernen" #: bookwyrm/templates/book/edit/edit_book_form.html:115 #, python-format msgid "Author page for %(name)s" -msgstr "Autor*innenseite für %(name)s" +msgstr "Autor*inseite für %(name)s" #: bookwyrm/templates/book/edit/edit_book_form.html:122 msgid "Add Authors:" @@ -612,7 +617,7 @@ msgid "John Doe, Jane Smith" msgstr "Max Mustermann, Maria Musterfrau" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "Titelbild" @@ -635,7 +640,7 @@ msgstr "Seiten:" #: bookwyrm/templates/book/edit/edit_book_form.html:197 msgid "Book Identifiers" -msgstr "Buchidentifikatoren" +msgstr "Buch-Identifikatoren" #: bookwyrm/templates/book/edit/edit_book_form.html:200 msgid "ISBN 13:" @@ -652,22 +657,22 @@ msgstr "OpenLibrary-ID:" #: bookwyrm/templates/book/editions/editions.html:4 #, python-format msgid "Editions of %(book_title)s" -msgstr "Editionen von %(book_title)s" +msgstr "Ausgaben von %(book_title)s" #: bookwyrm/templates/book/editions/editions.html:8 #, python-format msgid "Editions of \"%(work_title)s\"" -msgstr "Editionen von \"%(work_title)s\"" +msgstr "Ausgaben von \"%(work_title)s\"" #: bookwyrm/templates/book/editions/format_filter.html:8 #: bookwyrm/templates/book/editions/language_filter.html:8 msgid "Any" -msgstr "Beliebig" +msgstr "Beliebig(e)" #: bookwyrm/templates/book/editions/language_filter.html:5 #: bookwyrm/templates/preferences/edit_user.html:95 msgid "Language:" -msgstr "Sprache" +msgstr "Sprache:" #: bookwyrm/templates/book/editions/search_filter.html:5 msgid "Search editions" @@ -709,23 +714,23 @@ msgstr "bewertet" #: bookwyrm/templates/book/readthrough.html:8 msgid "Progress Updates:" -msgstr "Fortschrittsupdates:" +msgstr "Zwischenstände:" #: bookwyrm/templates/book/readthrough.html:13 msgid "finished" -msgstr "Abgeschlossen" +msgstr "abgeschlossen" #: bookwyrm/templates/book/readthrough.html:24 msgid "Show all updates" -msgstr "Zeige alle Updates" +msgstr "Zeige alle Zwischenstände" #: bookwyrm/templates/book/readthrough.html:40 msgid "Delete this progress update" -msgstr "Dieses Fortschrittsupdate löschen" +msgstr "Diesen Zwischenstand löschen" #: bookwyrm/templates/book/readthrough.html:51 msgid "started" -msgstr "Angefangen" +msgstr "angefangen" #: bookwyrm/templates/book/readthrough.html:58 #: bookwyrm/templates/book/readthrough.html:72 @@ -765,11 +770,11 @@ msgstr "Bestätige deine E-Mail-Adresse" #: bookwyrm/templates/confirm_email/confirm_email.html:13 msgid "A confirmation code has been sent to the email address you used to register your account." -msgstr "Der Bestätigungscode um deinen Account zu registrieren wurde an die E-Mail-Adresse gesendet." +msgstr "Der Bestätigungscode zur Registrierung eines Benutzer*inkontos wurde an deine E-Mail-Adresse gesendet." #: bookwyrm/templates/confirm_email/confirm_email.html:15 msgid "Sorry! We couldn't find that code." -msgstr "Sorry! Dieser Code ist uns nicht bekannt." +msgstr "Tut uns leid! Dieser Code ist uns nicht bekannt." #: bookwyrm/templates/confirm_email/confirm_email.html:19 #: bookwyrm/templates/settings/users/user_info.html:85 @@ -793,11 +798,11 @@ msgstr "Bestätigungslink erneut senden" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" -msgstr "E-Mail Adresse" +msgstr "E-Mail-Adresse:" #: bookwyrm/templates/confirm_email/resend_form.html:17 msgid "Resend link" @@ -823,7 +828,7 @@ msgstr "Verzeichnis" #: bookwyrm/templates/directory/directory.html:17 msgid "Make your profile discoverable to other BookWyrm users." -msgstr "Mach dein Profil entdeckbar für andere User" +msgstr "Mach dein Profil entdeckbar für andere BookWyrm-Benutzer*innen." #: bookwyrm/templates/directory/directory.html:24 #, python-format @@ -834,7 +839,7 @@ msgstr "Du kannst dich jederzeit in deinen Profileinstellun #: bookwyrm/templates/feed/goal_card.html:17 #: bookwyrm/templates/snippets/announcement.html:34 msgid "Dismiss message" -msgstr "Nachricht verwerfen" +msgstr "Nachricht schließen" #: bookwyrm/templates/directory/sort_filter.html:5 msgid "Order by" @@ -881,11 +886,11 @@ msgstr "Benutzer*in-Typ" #: bookwyrm/templates/directory/user_type_filter.html:8 msgid "BookWyrm users" -msgstr "Bookwyrmnutzer*innen" +msgstr "Bookwyrm-Benutzer*innen" #: bookwyrm/templates/directory/user_type_filter.html:12 msgid "All known users" -msgstr "Alle bekannten Nutzer*innen" +msgstr "Alle bekannten Benutzer*innen" #: bookwyrm/templates/discover/card-header.html:9 #, python-format @@ -916,18 +921,18 @@ msgstr "Entdecken" #: bookwyrm/templates/discover/discover.html:12 #, python-format msgid "See what's new in the local %(site_name)s community" -msgstr "Schau, was in der %(site_name)s Community neu ist" +msgstr "Was gibt es Neues in der %(site_name)s-Gemeinschaft" #: bookwyrm/templates/discover/large-book.html:52 #: bookwyrm/templates/discover/small-book.html:36 msgid "View status" -msgstr "Status ansehen" +msgstr "Status anzeigen" #: bookwyrm/templates/email/confirm/html_content.html:6 #: bookwyrm/templates/email/confirm/text_content.html:4 #, python-format msgid "One last step before you join %(site_name)s! Please confirm your email address by clicking the link below:" -msgstr "Als letzten Schritt bevor du %(site_name)s beitrittst - bestätige bitte deine Emailadresse indem du den Link klickst" +msgstr "Als letzten Schritt bevor du %(site_name)s beitrittst bestätige bitte deine E-Mail-Adresse, indem du den folgenden Link klickst:" #: bookwyrm/templates/email/confirm/html_content.html:11 msgid "Confirm Email" @@ -936,7 +941,7 @@ msgstr "E-Mail-Adresse bestätigen" #: bookwyrm/templates/email/confirm/html_content.html:15 #, python-format msgid "Or enter the code \"%(confirmation_code)s\" at login." -msgstr "Oder gibt den code \"%(confirmation_code)s\" beim Login ein." +msgstr "Oder gibt den Code „%(confirmation_code)s“ bei der Anmeldung ein." #: bookwyrm/templates/email/confirm/subject.html:2 msgid "Please confirm your email" @@ -945,12 +950,12 @@ msgstr "Bitte bestätige deine E-Mail-Adresse" #: bookwyrm/templates/email/confirm/text_content.html:10 #, python-format msgid "Or enter the code \"%(confirmation_code)s\" at login." -msgstr "Oder gibt den code \"%(confirmation_code)s\" beim Login ein." +msgstr "Oder gibt den Code „%(confirmation_code)s“ bei der Anmeldung ein." #: bookwyrm/templates/email/html_layout.html:15 #: bookwyrm/templates/email/text_layout.html:2 msgid "Hi there," -msgstr "Moin," +msgstr "Hallo," #: bookwyrm/templates/email/html_layout.html:21 #, python-format @@ -959,7 +964,7 @@ msgstr "BookWyrm wird auf über %(site_n #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format msgid "You're invited to join %(site_name)s! Click the link below to create an account." -msgstr "Du bist eingeladen, %(site_name)s beizutreten! Klicke auf den Link unten um einen Account zu erstellen." +msgstr "Du bist eingeladen, %(site_name)s beizutreten! Klicke auf den Link unten, um ein Benutzer*inkonto zu erstellen." #: bookwyrm/templates/email/invite/text_content.html:8 #, python-format @@ -990,15 +995,15 @@ msgstr "Erfahre mehr über %(site_name)s:" #: bookwyrm/templates/email/password_reset/text_content.html:4 #, python-format msgid "You requested to reset your %(site_name)s password. Click the link below to set a new password and log in to your account." -msgstr "Du hast beantragt, dein %(site_name)s Passwort zu ändern. Klicke auf den Link unten, um ein neues Passwort zu erstellen und dich einzuloggen." +msgstr "Du hast beantragt, dein %(site_name)s-Passwort zu ändern. Klicke auf den Link unten, um ein neues Passwort zu erstellen und dich anzumelden." #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" -msgstr "Passwort zurücksetzen!" +msgstr "Passwort zurücksetzen" #: bookwyrm/templates/email/password_reset/html_content.html:13 #: bookwyrm/templates/email/password_reset/text_content.html:8 @@ -1008,7 +1013,7 @@ msgstr "Falls du dein Passwort gar nicht zurücksetzen wolltest, kannst du diese #: bookwyrm/templates/email/password_reset/subject.html:2 #, python-format msgid "Reset your %(site_name)s password" -msgstr "Dein Passwort für %(site_name)s zurücksetzen" +msgstr "Passwort für %(site_name)s zurücksetzen" #: bookwyrm/templates/feed/direct_messages.html:8 #, python-format @@ -1031,23 +1036,23 @@ msgstr "Du hast momentan keine Nachrichten." #: bookwyrm/templates/feed/feed.html:22 #, python-format msgid "load 0 unread status(es)" -msgstr "lese 0 ungelesene Status" +msgstr "lade 0 ungelesene Statusmeldung(en)" #: bookwyrm/templates/feed/feed.html:38 msgid "There aren't any activities right now! Try following a user to get started" -msgstr "Hier sind noch keine Aktivitäten! Folge anderen, um loszulegen" +msgstr "Hier sind noch keine Aktivitäten! Folge Anderen, um loszulegen" #: bookwyrm/templates/feed/goal_card.html:6 #: bookwyrm/templates/feed/layout.html:90 #: bookwyrm/templates/user/goal_form.html:6 #, python-format msgid "%(year)s Reading Goal" -msgstr "%(year)s Leseziel" +msgstr "Leseziel für %(year)s" #: bookwyrm/templates/feed/goal_card.html:18 #, python-format msgid "You can set or change your reading goal any time from your profile page" -msgstr "Du kannst dein Leseziel jederzeit auf deiner Profilseite setzen oder ändern." +msgstr "Du kannst dein Leseziel jederzeit auf deiner Profilseite festlegen oder ändern" #: bookwyrm/templates/feed/layout.html:5 msgid "Updates" @@ -1060,7 +1065,7 @@ msgstr "Deine Bücher" #: bookwyrm/templates/feed/layout.html:14 msgid "There are no books here right now! Try searching for a book to get started" -msgstr "Hier sind noch keine Bücher! Versuche nach Büchern zu suchen um loszulegen" +msgstr "Hier sind noch keine Bücher! Versuche, nach Büchern zu suchen, um loszulegen" #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 @@ -1086,11 +1091,11 @@ msgstr "Wem noch folgen?" #: bookwyrm/templates/feed/suggested_users.html:9 msgid "Don't show suggested users" -msgstr "Keine vorgeschlagenen User anzeigen" +msgstr "Keine vorgeschlagenen Benutzer*innen anzeigen" #: bookwyrm/templates/feed/suggested_users.html:14 msgid "View directory" -msgstr "Zeige Verzeichnis" +msgstr "Verzeichnis anzeigen" #: bookwyrm/templates/get_started/book_preview.html:6 #, python-format @@ -1102,14 +1107,14 @@ msgid "What are you reading?" msgstr "Was liest du gerade?" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "Nach einem Buch suchen" #: bookwyrm/templates/get_started/books.html:11 #, python-format msgid "No books found for \"%(query)s\"" -msgstr "Keine Bücher für \"%(query)s\" gefunden" +msgstr "Keine Bücher für „%(query)s“ gefunden" #: bookwyrm/templates/get_started/books.html:11 #, python-format @@ -1120,8 +1125,9 @@ msgstr "Du kannst Bücher hinzufügen, wenn du %(site_name)s benutzt." #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1137,7 +1143,7 @@ msgid "Popular on %(site_name)s" msgstr "Auf %(site_name)s beliebt" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "Keine Bücher gefunden" @@ -1184,16 +1190,16 @@ msgstr "Fertigstellen" #: bookwyrm/templates/get_started/profile.html:15 #: bookwyrm/templates/preferences/edit_user.html:42 msgid "Display name:" -msgstr "Displayname:" +msgstr "Anzeigename:" #: bookwyrm/templates/get_started/profile.html:22 #: bookwyrm/templates/preferences/edit_user.html:49 msgid "Summary:" -msgstr "Bio:" +msgstr "Zusammenfassung:" #: bookwyrm/templates/get_started/profile.html:23 msgid "A little bit about you" -msgstr "Etwas über dich" +msgstr "Einige Angaben zu dir" #: bookwyrm/templates/get_started/profile.html:32 #: bookwyrm/templates/preferences/edit_user.html:27 @@ -1203,16 +1209,16 @@ msgstr "Avatar:" #: bookwyrm/templates/get_started/profile.html:42 #: bookwyrm/templates/preferences/edit_user.html:110 msgid "Manually approve followers:" -msgstr "Folgende manuell bestätigen" +msgstr "Follower*innen manuell bestätigen:" #: bookwyrm/templates/get_started/profile.html:48 #: bookwyrm/templates/preferences/edit_user.html:80 msgid "Show this account in suggested users:" -msgstr "Diesen Account in vorgeschlagenen Usern zeigen" +msgstr "Dieses Benutzer*inkonto in vorgeschlagene Benutzer*innen einschließen:" #: bookwyrm/templates/get_started/profile.html:52 msgid "Your account will show up in the directory, and may be recommended to other BookWyrm users." -msgstr "Dein Account wird im Verzeichnis gezeigt und möglicherweise anderen Usern vorgeschlagen." +msgstr "Dein Benutzer*inkonto wird im Verzeichnis gezeigt und möglicherweise anderen Benutzer*innen vorgeschlagen." #: bookwyrm/templates/get_started/users.html:11 msgid "Search for a user" @@ -1221,11 +1227,112 @@ msgstr "Benutzer*in suchen" #: bookwyrm/templates/get_started/users.html:13 #, python-format msgid "No users found for \"%(query)s\"" -msgstr "Keine Nutzer*innen für \"%(query)s\" gefunden" +msgstr "Keine Benutzer*innen für „%(query)s“ gefunden" + +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "Gruppe erstellen" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "Administriert von %(username)s" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "Diese Gruppe löschen?" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "Diese Aktion kann nicht rückgängig gemacht werden" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "Löschen" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "Gruppe bearbeiten" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "Mitglieder hinzufügen!" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "Gruppenname:" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "Gruppenbeschreibung:" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "Gruppe löschen" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "Hinzuzufügende*n Benutzer*in suchen" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "Diese Gruppe enthält keine Listen" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "Gruppe bearbeiten" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "Mitglieder können Bücher in den Buchlisten einer Gruppe hinzufügen und entfernen" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "Gruppe verlassen" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "Folgt dir" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "%(mutuals)s Follower*in, der*die du folgst" +msgstr[1] "%(mutuals)s Follower*innen, denen du folgst" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s Buch in deinen Regalen" +msgstr[1] "%(shared_books)s Bücher in deinen Regalen" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "Keine potentiellen Mitglieder für „%(user_query)s“ gefunden" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "Gruppenadministrator*in" #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "Bücher importieren" @@ -1239,11 +1346,11 @@ msgstr "Datei:" #: bookwyrm/templates/import/import.html:45 msgid "Include reviews" -msgstr "Bewertungen importieren" +msgstr "Besprechungen einschließen" #: bookwyrm/templates/import/import.html:50 msgid "Privacy setting for imported reviews:" -msgstr "Datenschutzeinstellung für importierte Bewertungen" +msgstr "Datenschutzeinstellung für importierte Besprechungen:" #: bookwyrm/templates/import/import.html:56 #: bookwyrm/templates/settings/federation/instance_blocklist.html:64 @@ -1252,7 +1359,7 @@ msgstr "Importieren" #: bookwyrm/templates/import/import.html:61 msgid "Recent Imports" -msgstr "Aktuelle Importe" +msgstr "Zuletzt importiert" #: bookwyrm/templates/import/import.html:63 msgid "No recent imports" @@ -1265,7 +1372,7 @@ msgstr "Importstatus" #: bookwyrm/templates/import/import_status.html:11 msgid "Back to imports" -msgstr "Zurück zu Importen" +msgstr "Zurück zu den Importen" #: bookwyrm/templates/import/import_status.html:15 msgid "Import started:" @@ -1277,7 +1384,7 @@ msgstr "Import abgeschlossen:" #: bookwyrm/templates/import/import_status.html:24 msgid "TASK FAILED" -msgstr "AUFGABE GESCHEITERT" +msgstr "IMPORTSCHRITT-FEHLER" #: bookwyrm/templates/import/import_status.html:32 msgid "Import still in progress." @@ -1285,7 +1392,7 @@ msgstr "Import läuft noch." #: bookwyrm/templates/import/import_status.html:34 msgid "(Hit reload to update!)" -msgstr "(Aktualisiere für ein Update!)" +msgstr "(Zur Aktualisierung „Neu laden” wählen)" #: bookwyrm/templates/import/import_status.html:41 msgid "Failed to load" @@ -1294,7 +1401,7 @@ msgstr "Laden fehlgeschlagen" #: bookwyrm/templates/import/import_status.html:50 #, python-format msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "Zum Ende der Liste springen, um die %(failed_count)s Einträge, deren Import fehlschlug, auszuwählen." +msgstr "Zum Ende der Liste springen, um die %(failed_count)s Einträge, die nicht importiert werden konnten, auszuwählen." #: bookwyrm/templates/import/import_status.html:62 #, python-format @@ -1303,11 +1410,11 @@ msgstr "Zeile %(index)s: %(title)s von %(author)s" #: bookwyrm/templates/import/import_status.html:82 msgid "Select all" -msgstr "Alle auswählen" +msgstr "Alle(s) auswählen" #: bookwyrm/templates/import/import_status.html:85 msgid "Retry items" -msgstr "Punkte erneut versuchen" +msgstr "Erneut versuchen" #: bookwyrm/templates/import/import_status.html:112 msgid "Successfully imported" @@ -1322,14 +1429,14 @@ msgid "Book" msgstr "Buch" #: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:150 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Titel" #: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:153 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Autor*in" @@ -1341,19 +1448,6 @@ msgstr "Importiert" msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Du kannst deine Goodreads-Daten von der Import/Export-Seite deines Goodreads-Kontos downloaden." -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "Erstelle einen Account" - -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "Zugiff verweigert" - -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "Sorry! Dieser Einladecode ist mehr gültig." - #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1369,9 +1463,23 @@ msgstr "Verhaltenskodex" msgid "Privacy Policy" msgstr "Datenschutzerklärung" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "Erstelle ein Benutzer*inkonto" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "Zugiff verweigert" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "Tut uns leid! Dieser Einladungscode ist mehr gültig." + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" -msgstr "Aktive Bücher" +msgstr "Zuletzt aktive Bücher" #: bookwyrm/templates/landing/layout.html:17 msgid "Decentralized" @@ -1388,7 +1496,7 @@ msgstr "Nichtkommerziell" #: bookwyrm/templates/landing/layout.html:45 #, python-format msgid "Join %(name)s" -msgstr "Tritt %(name)s bei" +msgstr "%(name)s beitreten" #: bookwyrm/templates/landing/layout.html:47 msgid "Request an Invitation" @@ -1405,7 +1513,54 @@ msgstr "Danke! Deine Anfrage ist eingegangen." #: bookwyrm/templates/landing/layout.html:82 msgid "Your Account" -msgstr "Dein Account" +msgstr "Dein Benutzer*inkonto" + +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "Anmeldung" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "Anmelden" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "Alles klar! E-Mail-Adresse bestätigt." + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "Benutzer*inname:" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "Passwort:" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "Passwort vergessen?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "Mehr über diese Seite" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "Passwort bestätigen:" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "Ein Link zum Zurücksetzen deines Passworts wird an deine E-Mail-Adresse geschickt" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "Passwort zurücksetzen" #: bookwyrm/templates/layout.html:13 #, python-format @@ -1418,7 +1573,7 @@ msgstr "Nach einem Buch, einem*r Benutzer*in oder einer Liste suchen" #: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62 msgid "Main navigation menu" -msgstr "Navigationshauptmenü" +msgstr "Navigations-Hauptmenü" #: bookwyrm/templates/layout.html:72 msgid "Feed" @@ -1454,25 +1609,10 @@ msgstr "Abmelden" msgid "Notifications" msgstr "Benachrichtigungen" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "Benutzer*inname:" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "Passwort" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "Passwort vergessen?" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "Anmelden" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "Beitreten" @@ -1487,7 +1627,7 @@ msgstr "Fehler beim veröffentlichen des Status" #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" -msgstr "Admin kontaktieren" +msgstr "Administrator*in kontaktieren" #: bookwyrm/templates/layout.html:238 msgid "Documentation" @@ -1513,10 +1653,15 @@ msgstr "Liste erstellen" #: bookwyrm/templates/lists/created_text.html:5 #, python-format +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "Erstellt von %(username)s und administriert von %(groupname)s" + +#: bookwyrm/templates/lists/created_text.html:7 +#, python-format msgid "Created and curated by %(username)s" msgstr "Erstellt und betreut von %(username)s" -#: bookwyrm/templates/lists/created_text.html:7 +#: bookwyrm/templates/lists/created_text.html:9 #, python-format msgid "Created by %(username)s" msgstr "Erstellt von %(username)s" @@ -1549,118 +1694,130 @@ msgstr "Ablehnen" msgid "Delete this list?" msgstr "Diese Liste löschen?" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "Diese Aktion kann nicht rückgängig gemacht werden" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "Löschen" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "Liste bearbeiten" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" msgstr "Listenkuratierung:" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "Geschlossen" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" msgstr "Nur du kannst Bücher hinzufügen oder entfernen" -#: bookwyrm/templates/lists/form.html:26 +#: bookwyrm/templates/lists/form.html:27 msgid "Curated" msgstr "Kuratiert" -#: bookwyrm/templates/lists/form.html:27 +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" -msgstr "Alle können Bücher vorschlagen, du kannst diese bestätigen" +msgstr "Jede*r kann Bücher vorschlagen, du musst diese bestätigen" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "Offen" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" -msgstr "Alle können Bücher hinzufügen" +msgstr "Jede*r kann Bücher hinzufügen" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "Gruppe" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "Gruppenmitglieder können Bücher zu dieser Liste hinzufügen und von dieser entfernen" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "Gruppe auswählen" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "Eine Gruppe auswählen" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "Du hast noch keine Gruppen!" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "Gruppe erstellen" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "Liste löschen" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" -msgstr "Du hast erfolgreich ein Buch für diese Liste vorgeschlagen!" +msgstr "Dein Buchvorschlag wurde dieser Liste hinzugefügt!" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" -msgstr "Du hast erfolgreich ein Buch zu dieser Liste hinzugefügt!" +msgstr "Du hast ein Buch zu dieser Liste hinzugefügt!" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "Diese Liste ist momentan leer" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "Hinzugefügt von %(username)s" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "Listenposition" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "Übernehmen" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "Entfernen" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "Liste sortieren" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" msgstr "Reihenfolge" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "Bücher hinzufügen" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "Bücher vorschlagen" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "suchen" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" -msgstr "Suche leeren" +msgstr "Suche zurücksetzen" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" -msgstr "Keine passenden Bücher zu \"%(query)s\" gefunden" +msgstr "Keine passenden Bücher zu „%(query)s“ gefunden" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "Vorschlagen" @@ -1672,35 +1829,23 @@ msgstr "Gespeichert" msgid "Your Lists" msgstr "Deine Listen" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "Alle Listen" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "Gespeicherte Listen" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "Anmeldung" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." -msgstr "Alles klar! E-Mai- Adresse bestätigt." - -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "Passwort:" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" -msgstr "Mehr über diese Seite" +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" +msgstr "hat deine Einladung angenommen, der Gruppe „%(group_name)s“ beizutreten" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format msgid "added %(book_title)s to your list \"%(list_name)s\"" -msgstr "%(book_title)s zu deiner Liste \"%(list_name)s\" hinzugefügt" +msgstr "%(book_title)s zu deiner Liste „%(list_name)s“ hinzugefügt" #: bookwyrm/templates/notifications/items/add.html:31 #, python-format @@ -1710,7 +1855,7 @@ msgstr "hat vorgeschlagen, %(book_title)s #: bookwyrm/templates/notifications/items/boost.html:19 #, python-format msgid "boosted your review of %(book_title)s" -msgstr "hat deine Bewertung von %(book_title)s geteilt" +msgstr "hat deine Besprechung von %(book_title)s geteilt" #: bookwyrm/templates/notifications/items/boost.html:25 #, python-format @@ -1730,11 +1875,11 @@ msgstr "hat deinen Status geteilt" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format msgid "liked your review of %(book_title)s" -msgstr "hat deine Besprechung von %(book_title)s favorisiert" +msgstr "hat deine Besprechung von %(book_title)s favorisiert" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "liked your comment on%(book_title)s" +msgid "liked your comment on %(book_title)s" msgstr "hat deinen Kommentar zu %(book_title)s favorisiert" #: bookwyrm/templates/notifications/items/fav.html:31 @@ -1753,17 +1898,32 @@ msgstr "folgt dir" #: bookwyrm/templates/notifications/items/follow_request.html:11 msgid "sent you a follow request" -msgstr "hat dir eine Folgeanfrage geschickt" +msgstr "möchte dir folgen" #: bookwyrm/templates/notifications/items/import.html:14 #, python-format msgid "Your import completed." msgstr "Dein Import ist fertig." +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "hat dich eingeladen, der Gruppe „%(group_name)s“ beizutreten" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "ist deiner Gruppe „%(group_name)s“ beigetreten" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "hat deine Gruppe „%(group_name)s“ verlassen" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" -msgstr "hat dich in einer Bewertung von %(book_title)s erwähnt" +msgstr "hat dich in einer Besprechung von %(book_title)s erwähnt" #: bookwyrm/templates/notifications/items/mention.html:26 #, python-format @@ -1780,10 +1940,20 @@ msgstr "hat dich in einem Zitat von %(book_titl msgid "mentioned you in a status" msgstr "hat dich in einem Status erwähnt" +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "wurde aus deiner Gruppe „%(group_name)s“ entfernt" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "Du wurdest aus der Gruppe „%(group_name)s“ entfernt" + #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format msgid "replied to your review of %(book_title)s" -msgstr "hat auf deine Bewertung von %(book_title)s geantwortet " +msgstr "hat auf deine Besprechung von %(book_title)s geantwortet" #: bookwyrm/templates/notifications/items/reply.html:27 #, python-format @@ -1805,6 +1975,21 @@ msgstr "hat auf deinen Status report needs moderation." msgstr "Eine neue Meldung muss moderiert werden." +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "hat die Sichtbarkeit von %(group_name)s geändert" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "hat den Namen von %(group_name)s geändert" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "hat die Beschreibung von %(group_name)s geändert" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" msgstr "Benachrichtigungen löschen" @@ -1821,29 +2006,15 @@ msgstr "Erwähnungen" msgid "You're all caught up!" msgstr "Du bist auf dem neusten Stand!" -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "Passwort bestätigen:" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "Ein Link zum Zurücksetzen deines Passworts wird an deine Mailadresse geschickt" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "Passwort zurücksetzen" - #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 #: bookwyrm/templates/preferences/layout.html:31 msgid "Blocked Users" -msgstr "Blockierte Nutzer*innen" +msgstr "Gesperrte Benutzer*innen" #: bookwyrm/templates/preferences/blocks.html:12 msgid "No users currently blocked." -msgstr "Momentan keine Nutzer*innen blockiert." +msgstr "Momentan keine Benutzer*innen gesperrt." #: bookwyrm/templates/preferences/change_password.html:4 #: bookwyrm/templates/preferences/change_password.html:7 @@ -1866,11 +2037,11 @@ msgstr "Benutzer*inkonto löschen" #: bookwyrm/templates/preferences/delete_user.html:12 msgid "Permanently delete account" -msgstr "Account permanent löschen" +msgstr "Benutzer*inkonto dauerhaft löschen" #: bookwyrm/templates/preferences/delete_user.html:14 msgid "Deleting your account cannot be undone. The username will not be available to register in the future." -msgstr "Das Löschen des Accounts kann nicht rückgängig gemacht werden. Der Username kann nicht erneut registriert werden." +msgstr "Das Löschen des Benutzer*inkonto kann nicht rückgängig gemacht werden. Der Benutzer*inname kann nicht erneut registriert werden." #: bookwyrm/templates/preferences/edit_user.html:4 #: bookwyrm/templates/preferences/edit_user.html:7 @@ -1896,7 +2067,7 @@ msgstr "Privatsphäre" #: bookwyrm/templates/preferences/edit_user.html:72 msgid "Show reading goal prompt in feed:" -msgstr "Zeige Leseziel-Abfrage im Feed:" +msgstr "Frage Leseziel im Feed ab:" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" @@ -1905,7 +2076,7 @@ msgstr "Zeige vorgeschlagene Benutzer*innen:" #: bookwyrm/templates/preferences/edit_user.html:85 #, python-format msgid "Your account will show up in the directory, and may be recommended to other BookWyrm users." -msgstr "Dein Account wird im directory angezeigt und eventuell anderen Usern empfohlen." +msgstr "Dein Benutzer*inkonto wird im Verzeichnis angezeigt und eventuell anderen Benutzer*innen empfohlen." #: bookwyrm/templates/preferences/edit_user.html:89 msgid "Preferred Timezone: " @@ -1926,17 +2097,17 @@ msgstr "Beziehungen" #: bookwyrm/templates/reading_progress/finish.html:5 #, python-format msgid "Finish \"%(book_title)s\"" -msgstr "\"%(book_title)s\" abschließen" +msgstr "„%(book_title)s“ abschließen" #: bookwyrm/templates/reading_progress/start.html:5 #, python-format msgid "Start \"%(book_title)s\"" -msgstr "\"%(book_title)s\" beginnen" +msgstr "„%(book_title)s“ beginnen" #: bookwyrm/templates/reading_progress/want.html:5 #, python-format msgid "Want to Read \"%(book_title)s\"" -msgstr "\"%(book_title)s\" auf Leseliste setzen" +msgstr "„%(book_title)s“ auf Leseliste setzen" #: bookwyrm/templates/search/book.html:47 #: bookwyrm/templates/settings/reports/reports.html:25 @@ -1958,7 +2129,7 @@ msgstr "Buch manuell hinzufügen" #: bookwyrm/templates/search/book.html:116 msgid "Log in to import or add books." -msgstr "Log dich ein, um Bücher zu importieren oder hinzuzufügen." +msgstr "Melde dich an, um Bücher zu importieren oder hinzuzufügen." #: bookwyrm/templates/search/layout.html:16 msgid "Search query" @@ -2000,7 +2171,7 @@ msgstr "Ankündigung bearbeiten" #: bookwyrm/templates/settings/announcements/announcement.html:35 msgid "Visible:" -msgstr "Sichtbar" +msgstr "Sichtbar:" #: bookwyrm/templates/settings/announcements/announcement.html:38 msgid "True" @@ -2184,7 +2355,7 @@ msgstr "E-Mail-Sperrliste" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:18 msgid "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." -msgstr "Wenn sich jemand mit einer E-Mail-Adresse von dieser Domain zu registrieren versucht, wird kein Account erstellt. Die Registrierung wird so aussehen als hätte sie funktioniert." +msgstr "Wenn sich jemand mit einer E-Mail-Adresse von dieser Domain zu registrieren versucht, wird kein Benutzer*inkonto erstellt. Es wird aber so aussehen, als ob die Registrierung funktioniert hätte." #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:25 msgid "Domain" @@ -2257,7 +2428,7 @@ msgid "Details" msgstr "Details" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "Aktivität" @@ -2268,7 +2439,7 @@ msgstr "Benutzer*innen:" #: bookwyrm/templates/settings/federation/instance.html:41 #: bookwyrm/templates/settings/federation/instance.html:47 msgid "View all" -msgstr "Alle anzeigen" +msgstr "Alle(s) anzeigen" #: bookwyrm/templates/settings/federation/instance.html:44 #: bookwyrm/templates/settings/users/user_info.html:56 @@ -2281,7 +2452,7 @@ msgstr "Folgen wir:" #: bookwyrm/templates/settings/federation/instance.html:55 msgid "Followed by them:" -msgstr "Folgen:" +msgstr "Folgen uns:" #: bookwyrm/templates/settings/federation/instance.html:60 msgid "Blocked by us:" @@ -2331,7 +2502,7 @@ msgstr "Sperrliste importieren" #: bookwyrm/templates/settings/federation/instance_blocklist.html:26 #: bookwyrm/templates/snippets/goal_progress.html:7 msgid "Success!" -msgstr "Erfolg!" +msgstr "Hat funktioniert!" #: bookwyrm/templates/settings/federation/instance_blocklist.html:30 msgid "Successfully blocked:" @@ -2339,7 +2510,7 @@ msgstr "Erfolgreich gesperrt:" #: bookwyrm/templates/settings/federation/instance_blocklist.html:32 msgid "Failed:" -msgstr "Fehlgeschlagen" +msgstr "Fehlgeschlagen:" #: bookwyrm/templates/settings/federation/instance_list.html:3 #: bookwyrm/templates/settings/federation/instance_list.html:5 @@ -2420,11 +2591,11 @@ msgstr "Ignorieren" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:97 msgid "Un-ignore" -msgstr "Un-ignorieren" +msgstr "Doch nicht ignorieren" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:108 msgid "Back to pending requests" -msgstr "Zurück zu ausstehenden Anfragen" +msgstr "Zurück zu den ausstehenden Anfragen" #: bookwyrm/templates/settings/invites/manage_invite_requests.html:110 msgid "View ignored requests" @@ -2436,11 +2607,11 @@ msgstr "Neue Einladung erzeugen" #: bookwyrm/templates/settings/invites/manage_invites.html:27 msgid "Expiry:" -msgstr "Ablaufen:" +msgstr "Ablaufdatum:" #: bookwyrm/templates/settings/invites/manage_invites.html:33 msgid "Use limit:" -msgstr "Begrenzte Benutzung" +msgstr "Verwendungslimit:" #: bookwyrm/templates/settings/invites/manage_invites.html:40 msgid "Create Invite" @@ -2452,15 +2623,15 @@ msgstr "Link" #: bookwyrm/templates/settings/invites/manage_invites.html:48 msgid "Expires" -msgstr "Läuft aus" +msgstr "Läuft ab am" #: bookwyrm/templates/settings/invites/manage_invites.html:49 msgid "Max uses" -msgstr "Maximale Benutzungen" +msgstr "Maximale Verwendungen" #: bookwyrm/templates/settings/invites/manage_invites.html:50 msgid "Times used" -msgstr "Mal benutzt" +msgstr "Anzahl Verwendungen" #: bookwyrm/templates/settings/invites/manage_invites.html:53 msgid "No active invites" @@ -2487,7 +2658,7 @@ msgstr "IP-Addressen-Sperrliste" #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:18 msgid "Any traffic from this IP address will get a 404 response when trying to access any part of the application." -msgstr "Jeder Datenverkehr von dieser IP-Adresse erhält eine 404-Antwort, wenn versucht wird, auf einen beliebigen Teil der Anwendung zuzugreifen." +msgstr "Datenverkehr von dieser IP-Adresse erhält eine 404-Antwort, wenn versucht wird, auf einen beliebigen Teil der Anwendung zuzugreifen." #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:24 msgid "Address" @@ -2499,7 +2670,7 @@ msgstr "Derzeit sind keine IP-Adressen gesperrt" #: bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html:6 msgid "You can block IP ranges using CIDR syntax." -msgstr "Du kannst IP-Bereiche mittels CIDR-Syntax blockieren." +msgstr "Du kannst IP-Bereiche mittels CIDR-Syntax sperren." #: bookwyrm/templates/settings/layout.html:4 msgid "Administration" @@ -2507,7 +2678,7 @@ msgstr "Administration" #: bookwyrm/templates/settings/layout.html:29 msgid "Manage Users" -msgstr "Nutzer*innen verwalten" +msgstr "Benutzer*innen verwalten" #: bookwyrm/templates/settings/layout.html:51 msgid "Moderation" @@ -2542,7 +2713,7 @@ msgstr "Zurück zu den Meldungen" #: bookwyrm/templates/settings/reports/report.html:23 msgid "Moderator Comments" -msgstr "Moderator:innenkommentare" +msgstr "Moderator*innenkommentare" #: bookwyrm/templates/settings/reports/report.html:41 #: bookwyrm/templates/snippets/create_status.html:28 @@ -2555,7 +2726,7 @@ msgstr "Gemeldete Statusmeldungen" #: bookwyrm/templates/settings/reports/report.html:48 msgid "No statuses reported" -msgstr "Keine Beiträge gemeldet" +msgstr "Keine Statusmeldungen gemeldet" #: bookwyrm/templates/settings/reports/report.html:54 msgid "Status has been deleted" @@ -2572,11 +2743,11 @@ msgstr "Gemeldet von %(username)s" #: bookwyrm/templates/settings/reports/report_preview.html:30 msgid "Re-open" -msgstr "Wiedereröffnen" +msgstr "Erneut öffnen" #: bookwyrm/templates/settings/reports/report_preview.html:32 msgid "Resolve" -msgstr "Lösen" +msgstr "Beheben" #: bookwyrm/templates/settings/reports/reports.html:6 #, python-format @@ -2609,7 +2780,7 @@ msgstr "Bilder" #: bookwyrm/templates/settings/site.html:12 #: bookwyrm/templates/settings/site.html:74 msgid "Footer Content" -msgstr "Inhalt des Footers" +msgstr "Inhalt der Fußzeile" #: bookwyrm/templates/settings/site.html:13 #: bookwyrm/templates/settings/site.html:98 @@ -2618,7 +2789,7 @@ msgstr "Registrierung" #: bookwyrm/templates/settings/site.html:24 msgid "Instance Name:" -msgstr "Instanzname" +msgstr "Instanzname:" #: bookwyrm/templates/settings/site.html:28 msgid "Tagline:" @@ -2626,7 +2797,7 @@ msgstr "Motto:" #: bookwyrm/templates/settings/site.html:32 msgid "Instance description:" -msgstr "Instanzbeschreibung" +msgstr "Instanzbeschreibung:" #: bookwyrm/templates/settings/site.html:36 msgid "Short description:" @@ -2642,7 +2813,7 @@ msgstr "Verhaltenskodex:" #: bookwyrm/templates/settings/site.html:45 msgid "Privacy Policy:" -msgstr "Datenschutzerklärung" +msgstr "Datenschutzerklärung:" #: bookwyrm/templates/settings/site.html:57 msgid "Logo:" @@ -2650,7 +2821,7 @@ msgstr "Logo:" #: bookwyrm/templates/settings/site.html:61 msgid "Logo small:" -msgstr "Logo klein" +msgstr "Kleines Logo:" #: bookwyrm/templates/settings/site.html:65 msgid "Favicon:" @@ -2658,11 +2829,11 @@ msgstr "Favicon:" #: bookwyrm/templates/settings/site.html:77 msgid "Support link:" -msgstr "Unterstützungslink" +msgstr "Support-Link:" #: bookwyrm/templates/settings/site.html:81 msgid "Support title:" -msgstr "Unterstützungstitel" +msgstr "Support-Titel:" #: bookwyrm/templates/settings/site.html:85 msgid "Admin email:" @@ -2670,7 +2841,7 @@ msgstr "E-Mail-Adresse des*r Administrator*in:" #: bookwyrm/templates/settings/site.html:89 msgid "Additional info:" -msgstr "Zusätzliche Info:" +msgstr "Zusätzliche Angaben:" #: bookwyrm/templates/settings/site.html:103 msgid "Allow registration" @@ -2682,15 +2853,15 @@ msgstr "Einladungsanfragen zulassen" #: bookwyrm/templates/settings/site.html:115 msgid "Require users to confirm email address" -msgstr "User müssen ihre E-Mail-Adresse bestätigen" +msgstr "Benutzer*innen müssen ihre E-Mail-Adresse bestätigen" #: bookwyrm/templates/settings/site.html:117 msgid "(Recommended if registration is open)" -msgstr "(Vorschlagen falls die Registrierung offen ist)" +msgstr "(empfohlen, falls Selbstregistrierung zulässig ist)" #: bookwyrm/templates/settings/site.html:120 msgid "Registration closed text:" -msgstr "Registrierungen geschlossen text" +msgstr "Hinweis, wenn Selbtregistrierung nicht erlaubt ist:" #: bookwyrm/templates/settings/site.html:124 msgid "Invite request text:" @@ -2699,12 +2870,12 @@ msgstr "Hinweis für Einladungsanfragen:" #: bookwyrm/templates/settings/users/delete_user_form.html:5 #: bookwyrm/templates/settings/users/user_moderation_actions.html:31 msgid "Permanently delete user" -msgstr "User permanent löschen" +msgstr "Benutzer*in dauerhaft löschen" #: bookwyrm/templates/settings/users/delete_user_form.html:12 #, python-format msgid "Are you sure you want to delete %(username)s's account? This action cannot be undone. To proceed, please enter your password to confirm deletion." -msgstr "Bist du sicher, dass du %(username)ss Account löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden. Zur Bestätigung gib bitte dein Passwort ein." +msgstr "Bist du sicher, dass du das Benutzer*inkonto „%(username)s“ löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden. Zur Bestätigung gib bitte dein Passwort ein." #: bookwyrm/templates/settings/users/delete_user_form.html:17 msgid "Your password:" @@ -2712,7 +2883,7 @@ msgstr "Dein Passwort:" #: bookwyrm/templates/settings/users/user.html:7 msgid "Back to users" -msgstr "Zurück zu Benutzer*innen" +msgstr "Zurück zu den Benutzer*innen" #: bookwyrm/templates/settings/users/user_admin.html:7 #, python-format @@ -2749,7 +2920,7 @@ msgstr "Inaktiv" #: bookwyrm/templates/settings/users/user_admin.html:52 #: bookwyrm/templates/settings/users/user_info.html:120 msgid "Not set" -msgstr "Nicht gesetzt" +msgstr "Nicht festgelegt" #: bookwyrm/templates/settings/users/user_info.html:16 msgid "View user profile" @@ -2805,7 +2976,7 @@ msgstr "Instanz anzeigen" #: bookwyrm/templates/settings/users/user_moderation_actions.html:5 msgid "Permanently deleted" -msgstr "Permanent gelöscht" +msgstr "Dauerhaft gelöscht" #: bookwyrm/templates/settings/users/user_moderation_actions.html:13 #: bookwyrm/templates/snippets/status/status_options.html:32 @@ -2833,57 +3004,70 @@ msgstr "Regal erstellen" msgid "Edit Shelf" msgstr "Regal bearbeiten" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "Alle Bücher" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "Regal erstellen" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "%(formatted_count)s Buch" msgstr[1] "%(formatted_count)s Bücher" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" -msgstr "(zeige %(start)s-%(end)s)" +msgstr "(Anzeige: %(start)s&endash;%(end)s)" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "Regal bearbeiten" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "Regal löschen" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "Ins Regal gestellt" -#: bookwyrm/templates/shelf/shelf.html:133 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "Gestartet" -#: bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:164 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "Abgeschlossen" -#: bookwyrm/templates/shelf/shelf.html:190 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "Dieses Regal ist leer." +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "Einladen" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "Einladung stornieren" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "@%(username)s entfernen" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" -msgstr "Von %(username)s veröffentlicht" +msgstr "Veröffentlicht von %(username)s" #: bookwyrm/templates/snippets/authors.html:22 #, python-format @@ -2913,7 +3097,7 @@ msgstr "Teilen zurücknehmen" #: bookwyrm/templates/snippets/create_status.html:17 msgid "Review" -msgstr "Bewerten" +msgstr "Besprechen" #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" @@ -2948,7 +3132,7 @@ msgstr "von %(pages)s Seiten" #: bookwyrm/templates/snippets/status/layout.html:52 #: bookwyrm/templates/snippets/status/layout.html:53 msgid "Reply" -msgstr "Antwort" +msgstr "Antworten" #: bookwyrm/templates/snippets/create_status/content_field.html:17 msgid "Content" @@ -2975,12 +3159,13 @@ msgstr "Kommentar:" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "Privat" #: bookwyrm/templates/snippets/create_status/post_options_block.html:21 msgid "Post" -msgstr "Absenden" +msgstr "Veröffentlichen" #: bookwyrm/templates/snippets/create_status/quotation.html:17 msgid "Quote:" @@ -3019,7 +3204,7 @@ msgstr "Diese Lesedaten löschen?" #: bookwyrm/templates/snippets/delete_readthrough_modal.html:7 #, python-format msgid "You are deleting this readthrough and its %(count)s associated progress updates." -msgstr "Du löscht diesen Leseforschritt und %(count)s zugehörige Fortschrittsupdates." +msgstr "Du löscht diesen Leseforschritt und %(count)s zugehörige Zwischenstände." #: bookwyrm/templates/snippets/fav_button.html:16 #: bookwyrm/templates/snippets/fav_button.html:17 @@ -3070,13 +3255,14 @@ msgid "Unfollow" msgstr "Entfolgen" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "Annehmen" #: bookwyrm/templates/snippets/form_rate_stars.html:20 #: bookwyrm/templates/snippets/stars.html:13 msgid "No rating" -msgstr "Kein Rating" +msgstr "Keine Bewertung" #: bookwyrm/templates/snippets/form_rate_stars.html:28 #, python-format @@ -3097,8 +3283,8 @@ msgstr[1] "%(rating)s Sterne" #, python-format msgid "set a goal to read %(counter)s book in %(year)s" msgid_plural "set a goal to read %(counter)s books in %(year)s" -msgstr[0] "Setze das Ziel, %(year)s %(counter)s Buch zu lesen" -msgstr[1] "Setze das Ziel, %(year)s %(counter)s Bücher zu lesen" +msgstr[0] "setze dir das Ziel, %(year)s %(counter)s Buch zu lesen" +msgstr[1] "setze dir das Ziel, %(year)s %(counter)s Bücher zu lesen" #: bookwyrm/templates/snippets/generated_status/rating.html:3 #, python-format @@ -3117,12 +3303,12 @@ msgstr[1] "Besprechung von „%(book_title)s“ (%(display_rating)s Sterne): %(r #: bookwyrm/templates/snippets/generated_status/review_pure_name.html:8 #, python-format msgid "Review of \"%(book_title)s\": %(review_title)s" -msgstr "Review von \"%(book_title)s\": %(review_title)s" +msgstr "Besprechung von „%(book_title)s“: %(review_title)s" #: bookwyrm/templates/snippets/goal_form.html:4 #, python-format msgid "Set a goal for how many books you'll finish reading in %(year)s, and track your progress throughout the year." -msgstr "Setze dir ein Ziel, wie viele Bücher du %(year)s lesen wirst und behalte deinen Fortschritt über's Jahr im Auge." +msgstr "Setze dir ein Ziel, wie viele Bücher du %(year)s lesen willst und behalte deinen Fortschritt während des Jahrs im Blick." #: bookwyrm/templates/snippets/goal_form.html:16 msgid "Reading goal:" @@ -3134,12 +3320,12 @@ msgstr "Bücher" #: bookwyrm/templates/snippets/goal_form.html:26 msgid "Goal privacy:" -msgstr "Sichtbarkeit des Ziels" +msgstr "Sichtbarkeit des Ziels:" #: bookwyrm/templates/snippets/goal_form.html:33 #: bookwyrm/templates/snippets/reading_modals/layout.html:13 msgid "Post to feed" -msgstr "Posten" +msgstr "Im Feed veröffentlichen" #: bookwyrm/templates/snippets/goal_form.html:37 msgid "Set goal" @@ -3148,7 +3334,7 @@ msgstr "Ziel setzen" #: bookwyrm/templates/snippets/goal_progress.html:9 #, python-format msgid "%(percent)s%% complete!" -msgstr "%(percent)s%% komplett!" +msgstr "%(percent)s%% geschafft!" #: bookwyrm/templates/snippets/goal_progress.html:12 #, python-format @@ -3181,20 +3367,23 @@ msgstr "Weiter" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "Öffentlich" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "Ungelistet" #: bookwyrm/templates/snippets/privacy-icons.html:12 msgid "Followers-only" -msgstr "Nur für Folgende" +msgstr "Nur für Follower*innen" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "Beitragssichtbarkeit" @@ -3202,11 +3391,11 @@ msgstr "Beitragssichtbarkeit" #: bookwyrm/templates/user/relationships/followers.html:6 #: bookwyrm/templates/user/relationships/layout.html:11 msgid "Followers" -msgstr "Folgende" +msgstr "Follower*innen" #: bookwyrm/templates/snippets/rate_action.html:4 msgid "Leave a rating" -msgstr "Raten" +msgstr "Bewerten" #: bookwyrm/templates/snippets/rate_action.html:19 msgid "Rate" @@ -3215,7 +3404,7 @@ msgstr "Bewerten" #: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:6 #, python-format msgid "Finish \"%(book_title)s\"" -msgstr "\"%(book_title)s\" abschließen" +msgstr "„%(book_title)s“ abschließen" #: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:23 #: bookwyrm/templates/snippets/reading_modals/start_reading_modal.html:20 @@ -3226,7 +3415,7 @@ msgstr "Zu lesen angefangen" #: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:31 #: bookwyrm/templates/snippets/readthrough_form.html:20 msgid "Finished reading" -msgstr "Zu Ende gelesen" +msgstr "Lesen abgeschlossen" #: bookwyrm/templates/snippets/reading_modals/form.html:9 msgid "(Optional)" @@ -3235,17 +3424,17 @@ msgstr "(Optional)" #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:5 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:50 msgid "Update progress" -msgstr "Update-Fortschritt" +msgstr "Zwischenstand" #: bookwyrm/templates/snippets/reading_modals/start_reading_modal.html:6 #, python-format msgid "Start \"%(book_title)s\"" -msgstr "\"%(book_title)s\" beginnen" +msgstr "„%(book_title)s“ beginnen" #: bookwyrm/templates/snippets/reading_modals/want_to_read_modal.html:6 #, python-format msgid "Want to Read \"%(book_title)s\"" -msgstr "\"%(book_title)s\" auf Leseliste setzen" +msgstr "„%(book_title)s“ auf Leseliste setzen" #: bookwyrm/templates/snippets/readthrough_form.html:14 msgid "Progress" @@ -3267,11 +3456,11 @@ msgstr "@%(username)s melden" #: bookwyrm/templates/snippets/report_modal.html:23 #, python-format msgid "This report will be sent to %(site_name)s's moderators for review." -msgstr "Diese Meldung wird an die Moderator:innen von %(site_name)s weitergeletiet." +msgstr "Diese Meldung wird an die Moderato*innen von %(site_name)s weitergeleitet." #: bookwyrm/templates/snippets/report_modal.html:24 msgid "More info about this report:" -msgstr "Weitere Angeben zu dieser Meldung:" +msgstr "Weitere Angaben zu dieser Meldung:" #: bookwyrm/templates/snippets/shelf_selector.html:4 msgid "Move book" @@ -3354,7 +3543,7 @@ msgstr "hat %(book)s bewertet:" #: bookwyrm/templates/snippets/status/headers/read.html:7 #, python-format msgid "finished reading %(book)s" -msgstr "hat %(book)s ausgelesen" +msgstr "hat %(book)s abgeschlossen" #: bookwyrm/templates/snippets/status/headers/reading.html:7 #, python-format @@ -3374,7 +3563,7 @@ msgstr "%(username)s hat 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\n" "Language-Team: Spanish\n" "Language: es\n" @@ -35,7 +35,7 @@ msgstr "Un mes" #: bookwyrm/forms.py:259 msgid "Does Not Expire" -msgstr "Nunca se vence" +msgstr "No expira" #: bookwyrm/forms.py:263 #, python-brace-format @@ -46,29 +46,29 @@ msgstr "{i} usos" msgid "Unlimited" msgstr "Sin límite" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "Orden de la lista" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "Título" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 -#: bookwyrm/templates/shelf/shelf.html:168 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Calificación" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "Ordenar por" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "Ascendente" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "Descendente" @@ -165,7 +165,7 @@ msgstr "Línea temporal de libros" #: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "Libros" @@ -223,7 +223,7 @@ msgid "Edit Author" msgstr "Editar Autor/Autora" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "Alias:" @@ -276,71 +276,72 @@ msgstr "Agregado:" msgid "Updated:" msgstr "Actualizado:" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "Editado más recientemente por:" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "Metadatos" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "Nombre:" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "Separar varios valores con comas." -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "Biografía:" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "Enlace de Wikipedia:" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "Fecha de nacimiento:" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "Fecha de muerte:" -#: bookwyrm/templates/author/edit_author.html:79 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Author Identifiers" msgstr "Identificadores de autor/autora" -#: bookwyrm/templates/author/edit_author.html:81 +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "Clave OpenLibrary:" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "ID Inventaire:" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "Clave Librarything:" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "Clave Goodreads:" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -352,11 +353,13 @@ msgstr "Clave Goodreads:" msgid "Save" msgstr "Guardar" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -397,7 +400,7 @@ msgstr "Agregar descripción" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "Descripción:" @@ -460,7 +463,7 @@ msgstr "Lugares" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "Listas" @@ -470,7 +473,7 @@ msgstr "Agregar a lista" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -543,7 +546,9 @@ msgid "This is a new work" msgstr "Esta es una obra nueva" #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "Confirmar" @@ -612,7 +617,7 @@ msgid "John Doe, Jane Smith" msgstr "Juan Nadie, Natalia Natalia" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "Portada" @@ -686,17 +691,17 @@ msgstr "%(pages)s páginas" #: bookwyrm/templates/book/publisher_info.html:38 #, python-format msgid "%(languages)s language" -msgstr "idioma %(languages)s" +msgstr "Idioma %(languages)s" #: bookwyrm/templates/book/publisher_info.html:65 #, python-format msgid "Published %(date)s by %(publisher)s." -msgstr "Publicado %(date)s por %(publisher)s." +msgstr "Publicado el %(date)s por %(publisher)s." #: bookwyrm/templates/book/publisher_info.html:67 #, python-format msgid "Published %(date)s" -msgstr "Publicado %(date)s" +msgstr "Publicado el %(date)s" #: bookwyrm/templates/book/publisher_info.html:69 #, python-format @@ -793,7 +798,7 @@ msgstr "Reenviar enlace de confirmación" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" @@ -842,7 +847,7 @@ msgstr "Ordenar por" #: bookwyrm/templates/directory/sort_filter.html:8 msgid "Recently active" -msgstr "Activ@ recientemente" +msgstr "Activo recientemente" #: bookwyrm/templates/directory/sort_filter.html:9 msgid "Suggested" @@ -921,7 +926,7 @@ msgstr "Ver que es nuevo en la comunidad local de %(site_name)s" #: bookwyrm/templates/discover/large-book.html:52 #: bookwyrm/templates/discover/small-book.html:36 msgid "View status" -msgstr "Ver status" +msgstr "Ver estado" #: bookwyrm/templates/email/confirm/html_content.html:6 #: bookwyrm/templates/email/confirm/text_content.html:4 @@ -993,10 +998,10 @@ msgid "You requested to reset your %(site_name)s password. Click the link below msgstr "Tú solicitaste reestablecer tu %(site_name)s contraseña. Haz clic en el enlace a continuación para establecer una nueva contraseña e ingresar a tu cuenta." #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" msgstr "Restablecer contraseña" @@ -1031,7 +1036,7 @@ msgstr "No tienes ningún mensaje en este momento." #: bookwyrm/templates/feed/feed.html:22 #, python-format msgid "load 0 unread status(es)" -msgstr "cargar 0 status(es) no leído(s)" +msgstr "cargar 0 estado(s) no leído(s)" #: bookwyrm/templates/feed/feed.html:38 msgid "There aren't any activities right now! Try following a user to get started" @@ -1042,12 +1047,12 @@ msgstr "¡No hay actividad ahora mismo! Sigue a otro usuario para empezar" #: bookwyrm/templates/user/goal_form.html:6 #, python-format msgid "%(year)s Reading Goal" -msgstr "%(year)s Meta de lectura" +msgstr "Objetivo de lectura de %(year)s" #: bookwyrm/templates/feed/goal_card.html:18 #, python-format msgid "You can set or change your reading goal any time from your profile page" -msgstr "Puedes establecer o cambiar tu meta de lectura en cualquier momento que desees desde tu perfil" +msgstr "Puedes establecer o cambiar tu objetivo de lectura en cualquier momento desde tu página de perfil" #: bookwyrm/templates/feed/layout.html:5 msgid "Updates" @@ -1077,7 +1082,7 @@ msgstr "Leyendo actualmente" #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 msgid "Read" -msgstr "Leido" +msgstr "Leído" #: bookwyrm/templates/feed/suggested_users.html:5 #: bookwyrm/templates/get_started/users.html:6 @@ -1102,7 +1107,7 @@ msgid "What are you reading?" msgstr "¿Qué estás leyendo?" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "Buscar libros" @@ -1120,8 +1125,9 @@ msgstr "Puedes agregar libros cuando comiences a usar %(site_name)s." #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1137,7 +1143,7 @@ msgid "Popular on %(site_name)s" msgstr "Popular en %(site_name)s" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "No se encontró ningún libro" @@ -1223,9 +1229,110 @@ msgstr "Buscar un usuario" msgid "No users found for \"%(query)s\"" msgstr "No se encontró ningún usuario correspondiente a \"%(query)s\"" +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "Crear grupo" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "Gestionado por %(username)s" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "¿Eliminar este grupo?" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "Esta acción no se puede deshacer" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "Eliminar" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "Editar Grupo" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "¡Agrega nuevos miembros!" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "Nombre del grupo:" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "Descripción del grupo:" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "Eliminar grupo" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "Buscar para agregar un usuario" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "Este grupo no tiene listas" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "Editar grupo" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "Los miembros pueden agregar y eliminar libros en las listas de libros de un grupo" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "Dejar grupo" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "Te sigue" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "%(mutuals)s seguidor que sigues" +msgstr[1] "%(mutuals)s seguidores que sigues" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s libro en tus estantes" +msgstr[1] "%(shared_books)s libros en tus estantes" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "No se encontraron miembros potenciales para «%(user_query)s»" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "Gestor" + #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "Importar libros" @@ -1261,7 +1368,7 @@ msgstr "No hay ninguna importación reciente" #: bookwyrm/templates/import/import_status.html:6 #: bookwyrm/templates/import/import_status.html:10 msgid "Import Status" -msgstr "Status de importación" +msgstr "Importar estado" #: bookwyrm/templates/import/import_status.html:11 msgid "Back to imports" @@ -1322,14 +1429,14 @@ msgid "Book" msgstr "Libro" #: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:150 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Título" #: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:153 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Autor/Autora" @@ -1341,19 +1448,6 @@ msgstr "Importado" msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Puede descargar sus datos de Goodreads desde la página de Importación/Exportación de su cuenta de Goodreads." -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "Crear una cuenta" - -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "Permiso denegado" - -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "¡Disculpa! Este código de invitación no queda válido." - #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1369,6 +1463,20 @@ msgstr "Código de conducta" msgid "Privacy Policy" msgstr "Política de privacidad" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "Crear una cuenta" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "Permiso denegado" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "¡Disculpa! Este código de invitación no queda válido." + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" msgstr "Libros recientes" @@ -1407,6 +1515,53 @@ msgstr "¡Gracias! Tu solicitud ha sido recibido." msgid "Your Account" msgstr "Tu cuenta" +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "Iniciar sesión" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "Iniciar sesión" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "¡Éxito! Dirección de correo electrónico confirmada." + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "Nombre de usuario:" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "Contraseña:" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "¿Olvidaste tu contraseña?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "Más sobre este sitio" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "Confirmar contraseña:" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "Un enlace para restablecer tu contraseña se enviará a tu dirección de correo electrónico" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "Restablecer contraseña" + #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" @@ -1454,40 +1609,25 @@ msgstr "Cerrar sesión" msgid "Notifications" msgstr "Notificaciones" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "Nombre de usuario:" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "contraseña" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "¿Olvidaste tu contraseña?" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "Iniciar sesión" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "Unirse" #: bookwyrm/templates/layout.html:221 msgid "Successfully posted status" -msgstr "Status publicado exitosamente" +msgstr "Estado publicado con éxito" #: bookwyrm/templates/layout.html:222 msgid "Error posting status" -msgstr "Error en publicar status" +msgstr "Error al publicar el estado" #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" -msgstr "Contactarse con administradores del sitio" +msgstr "Comuníquese con el administrador del sitio" #: bookwyrm/templates/layout.html:238 msgid "Documentation" @@ -1513,10 +1653,15 @@ msgstr "Crear lista" #: bookwyrm/templates/lists/created_text.html:5 #, python-format +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "Creado por %(username)s y gestionado por %(groupname)s" + +#: bookwyrm/templates/lists/created_text.html:7 +#, python-format msgid "Created and curated by %(username)s" msgstr "Agregado y comisariado por %(username)s" -#: bookwyrm/templates/lists/created_text.html:7 +#: bookwyrm/templates/lists/created_text.html:9 #, python-format msgid "Created by %(username)s" msgstr "Creado por %(username)s" @@ -1549,118 +1694,130 @@ msgstr "Desechar" msgid "Delete this list?" msgstr "¿Eliminar esta lista?" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "Esta acción no se puede deshacer" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "Eliminar" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "Editar lista" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" msgstr "Enumerar lista de comisariado:" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "Cerrado" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" msgstr "Solo tú puedes agregar a y sacar libros de esta lista" -#: bookwyrm/templates/lists/form.html:26 +#: bookwyrm/templates/lists/form.html:27 msgid "Curated" msgstr "De comisariado" -#: bookwyrm/templates/lists/form.html:27 +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" msgstr "Cualquier usuario puede sugerir libros, en cuanto lo hayas aprobado" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "Abrir" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" msgstr "Cualquer usuario puede agregar libros a esta lista" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "Grupo" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "Los miembros del grupo pueden agregar y eliminar de esta lista" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "Seleccionar grupo" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "Seleccionar un grupo" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "¡Aún no tienes ningún grupo!" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "Crear un grupo" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "Eliminar lista" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" msgstr "¡Has sugerido un libro para esta lista exitosamente!" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" msgstr "¡Has agregado un libro a esta lista exitosamente!" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "Esta lista está vacia" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "Agregado por %(username)s" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "Posición" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "Establecido" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "Quitar" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "Ordena la lista" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" msgstr "Dirección" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "Agregar libros" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "Sugerir libros" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "buscar" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" msgstr "Borrar búsqueda" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" msgstr "No se encontró ningún libro correspondiente a la búsqueda: \"%(query)s\"" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "Sugerir" @@ -1672,30 +1829,18 @@ msgstr "Guardado" msgid "Your Lists" msgstr "Tus listas" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "Todas las listas" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "Listas guardadas" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "Iniciar sesión" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." -msgstr "¡Éxito! Dirección de correo electrónico confirmada." - -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "Contraseña:" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" -msgstr "Más sobre este sitio" +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" +msgstr "aceptó su invitación para unirse al grupo «%(group_name)s»" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format @@ -1710,22 +1855,22 @@ msgstr "sugirió agregar %(book_title)s a #: bookwyrm/templates/notifications/items/boost.html:19 #, python-format msgid "boosted your review of %(book_title)s" -msgstr "respaldó tu reseña de %(book_title)s" +msgstr "impulsó tu reseña de %(book_title)s" #: bookwyrm/templates/notifications/items/boost.html:25 #, python-format msgid "boosted your comment on%(book_title)s" -msgstr "respaldó tu comentario en%(book_title)s" +msgstr "impulsó tu comentario en%(book_title)s" #: bookwyrm/templates/notifications/items/boost.html:31 #, python-format msgid "boosted your quote from %(book_title)s" -msgstr "respaldó tucita de %(book_title)s" +msgstr "impulsó tucita de %(book_title)s" #: bookwyrm/templates/notifications/items/boost.html:37 #, python-format msgid "boosted your status" -msgstr "respaldó tu status" +msgstr "impulsó tu estado" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format @@ -1734,7 +1879,7 @@ msgstr "le gustó tu reseña de %(book_title)s< #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "liked your comment on%(book_title)s" +msgid "liked your comment on %(book_title)s" msgstr "le gustó tu comentario sobre %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:31 @@ -1760,6 +1905,21 @@ msgstr "te quiere seguir" msgid "Your import completed." msgstr "Tu importación ha terminado." +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "te invitó a unirte al grupo «%(group_name)s»" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "se ha unido a tu grupo «%(group_name)s»" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "ha dejado tu grupo «%(group_name)s»" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" @@ -1778,7 +1938,17 @@ msgstr "te mencionó en una cita de %(book_titl #: bookwyrm/templates/notifications/items/mention.html:38 #, python-format msgid "mentioned you in a status" -msgstr "te mencionó en un status" +msgstr "te mencionó en un estado" + +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "ha sido eliminado de tu grupo «%(group_name)s»" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "Te han eliminado del grupo «%(group_name)s»" #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format @@ -1798,13 +1968,28 @@ msgstr "respondió a tu replied to your status" -msgstr "respondió a tu status" +msgstr "respondió a tu estado" #: bookwyrm/templates/notifications/items/report.html:15 #, python-format msgid "A new report needs moderation." msgstr "Un informe nuevo se requiere moderación." +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "ha cambiado el nivel de privacidad del grupo «%(group_name)s»" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "ha cambiado el nombre del grupo «%(group_name)s»" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "ha cambiado la descripción del grupo «%(group_name)s»" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" msgstr "Borrar notificaciones" @@ -1821,20 +2006,6 @@ msgstr "Menciones" msgid "You're all caught up!" msgstr "¡Estás al día!" -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "Confirmar contraseña:" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "Un enlace para restablecer tu contraseña se enviará a tu dirección de correo electrónico" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "Restablecer contraseña" - #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 #: bookwyrm/templates/preferences/layout.html:31 @@ -1870,7 +2041,7 @@ msgstr "Eliminar cuenta permanentemente" #: bookwyrm/templates/preferences/delete_user.html:14 msgid "Deleting your account cannot be undone. The username will not be available to register in the future." -msgstr "Eliminar tu cuenta no puede ser deshecho. El nombre de usuario no será disponible para registrar en el futuro." +msgstr "La eliminación de tu cuenta no se puede deshacer. El nombre de usuario no estará disponible para registrarse en el futuro." #: bookwyrm/templates/preferences/edit_user.html:4 #: bookwyrm/templates/preferences/edit_user.html:7 @@ -1896,7 +2067,7 @@ msgstr "Privacidad" #: bookwyrm/templates/preferences/edit_user.html:72 msgid "Show reading goal prompt in feed:" -msgstr "Mostrar sugerencia de meta de lectura en el feed:" +msgstr "Mostrar indicador de objetivo de lectura en el feed:" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" @@ -2025,7 +2196,7 @@ msgstr "Fecha final:" #: bookwyrm/templates/settings/announcements/announcement.html:60 #: bookwyrm/templates/settings/announcements/announcement_form.html:58 msgid "Active:" -msgstr "Activ@:" +msgstr "Activo:" #: bookwyrm/templates/settings/announcements/announcement_form.html:8 #: bookwyrm/templates/settings/announcements/announcements.html:8 @@ -2149,7 +2320,7 @@ msgstr "Actividad de inscripciones de usuarios" #: bookwyrm/templates/settings/dashboard/dashboard.html:112 msgid "Status activity" -msgstr "Actividad de status" +msgstr "Actividad de estado" #: bookwyrm/templates/settings/dashboard/dashboard.html:118 msgid "Works created" @@ -2161,7 +2332,7 @@ msgstr "Inscripciones" #: bookwyrm/templates/settings/dashboard/status_chart.html:11 msgid "Statuses posted" -msgstr "Statuses publicados" +msgstr "Estados publicados" #: bookwyrm/templates/settings/dashboard/user_chart.html:11 msgid "Total" @@ -2257,7 +2428,7 @@ msgid "Details" msgstr "Detalles" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "Actividad" @@ -2551,15 +2722,15 @@ msgstr "Comentario" #: bookwyrm/templates/settings/reports/report.html:46 msgid "Reported statuses" -msgstr "Statuses reportados" +msgstr "Estados reportados" #: bookwyrm/templates/settings/reports/report.html:48 msgid "No statuses reported" -msgstr "Ningún estatus reportado" +msgstr "No se reportaron estados" #: bookwyrm/templates/settings/reports/report.html:54 msgid "Status has been deleted" -msgstr "Status ha sido eliminado" +msgstr "El estado ha sido eliminado" #: bookwyrm/templates/settings/reports/report_preview.html:13 msgid "No notes provided" @@ -2833,53 +3004,66 @@ msgstr "Crear estante" msgid "Edit Shelf" msgstr "Editar estante" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "Todos los libros" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "Crear estante" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "%(formatted_count)s libro" msgstr[1] "%(formatted_count)s libros" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" msgstr "(mostrando %(start)s-%(end)s)" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "Editar estante" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "Eliminar estante" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "Archivado" -#: bookwyrm/templates/shelf/shelf.html:133 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "Empezado" -#: bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:164 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "Terminado" -#: bookwyrm/templates/shelf/shelf.html:190 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "Este estante está vacio." +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "Invitar" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "Anular la invitación" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "Eliminar a @%(username)s" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" @@ -2904,12 +3088,12 @@ msgstr "%(title)s por" #: bookwyrm/templates/snippets/boost_button.html:20 #: bookwyrm/templates/snippets/boost_button.html:21 msgid "Boost" -msgstr "Respaldar" +msgstr "Impulsar" #: bookwyrm/templates/snippets/boost_button.html:33 #: bookwyrm/templates/snippets/boost_button.html:34 msgid "Un-boost" -msgstr "Des-respaldar" +msgstr "Des-impulsar" #: bookwyrm/templates/snippets/create_status.html:17 msgid "Review" @@ -2948,7 +3132,7 @@ msgstr "de %(pages)s páginas" #: bookwyrm/templates/snippets/status/layout.html:52 #: bookwyrm/templates/snippets/status/layout.html:53 msgid "Reply" -msgstr "Respuesta" +msgstr "Responder" #: bookwyrm/templates/snippets/create_status/content_field.html:17 msgid "Content" @@ -2975,6 +3159,7 @@ msgstr "Comentario:" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "Privada" @@ -3050,7 +3235,7 @@ msgstr "Borrar filtros" #: bookwyrm/templates/snippets/follow_button.html:14 #, python-format msgid "Follow @%(username)s" -msgstr "Seguir @%(username)s" +msgstr "Seguir a @%(username)s" #: bookwyrm/templates/snippets/follow_button.html:16 msgid "Follow" @@ -3063,13 +3248,14 @@ msgstr "Des-enviar solicitud de seguidor" #: bookwyrm/templates/snippets/follow_button.html:30 #, python-format msgid "Unfollow @%(username)s" -msgstr "Dejar de seguir @%(username)s" +msgstr "Dejar de seguir a @%(username)s" #: bookwyrm/templates/snippets/follow_button.html:32 msgid "Unfollow" msgstr "Dejar de seguir" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "Aceptar" @@ -3097,7 +3283,7 @@ msgstr[1] "%(rating)s estrellas" #, python-format msgid "set a goal to read %(counter)s book in %(year)s" msgid_plural "set a goal to read %(counter)s books in %(year)s" -msgstr[0] "estableció una meta de leer %(counter)s libro en %(year)s" +msgstr[0] "establecer el objetivo de leer %(counter)s libro en %(year)s" msgstr[1] "estableció una meta de leer %(counter)s libros en %(year)s" #: bookwyrm/templates/snippets/generated_status/rating.html:3 @@ -3126,7 +3312,7 @@ msgstr "Establece una meta para cuantos libros leerás en %(year)s, y seguir tu #: bookwyrm/templates/snippets/goal_form.html:16 msgid "Reading goal:" -msgstr "Meta de lectura:" +msgstr "Objetivo de lectura:" #: bookwyrm/templates/snippets/goal_form.html:21 msgid "books" @@ -3134,12 +3320,12 @@ msgstr "libros" #: bookwyrm/templates/snippets/goal_form.html:26 msgid "Goal privacy:" -msgstr "Privacidad de meta:" +msgstr "Privacidad del objetivo:" #: bookwyrm/templates/snippets/goal_form.html:33 #: bookwyrm/templates/snippets/reading_modals/layout.html:13 msgid "Post to feed" -msgstr "Compartir con tu feed" +msgstr "Publicar en el feed" #: bookwyrm/templates/snippets/goal_form.html:37 msgid "Set goal" @@ -3148,7 +3334,7 @@ msgstr "Establecer meta" #: bookwyrm/templates/snippets/goal_progress.html:9 #, python-format msgid "%(percent)s%% complete!" -msgstr "%(percent)s%% terminado!" +msgstr "¡%(percent)s%% terminado!" #: bookwyrm/templates/snippets/goal_progress.html:12 #, python-format @@ -3181,12 +3367,14 @@ msgstr "Siguiente" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "Público" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "Privado" @@ -3195,6 +3383,7 @@ msgid "Followers-only" msgstr "Solo seguidores" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "Privacidad de publicación" @@ -3235,7 +3424,7 @@ msgstr "(Opcional)" #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:5 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:50 msgid "Update progress" -msgstr "Progreso de actualización" +msgstr "Actualizar progreso" #: bookwyrm/templates/snippets/reading_modals/start_reading_modal.html:6 #, python-format @@ -3306,7 +3495,7 @@ msgstr "Advertencia de contenido" #: bookwyrm/templates/snippets/status/content_status.html:79 msgid "Show status" -msgstr "Ver status" +msgstr "Mostrar estado" #: bookwyrm/templates/snippets/status/content_status.html:101 #, python-format @@ -3324,7 +3513,7 @@ msgstr "Abrir imagen en una nueva ventana" #: bookwyrm/templates/snippets/status/content_status.html:144 msgid "Hide status" -msgstr "Ocultar status" +msgstr "Ocultar estado" #: bookwyrm/templates/snippets/status/header.html:45 #, python-format @@ -3339,7 +3528,7 @@ msgstr "comentó en \"%(book)s\"" #: bookwyrm/templates/snippets/status/headers/note.html:15 #, python-format msgid "replied to %(username)s's status" -msgstr "respondió al status de %(username)s" +msgstr "respondió al estado de %(username)s" #: bookwyrm/templates/snippets/status/headers/quotation.html:2 #, python-format @@ -3379,12 +3568,12 @@ msgstr "Eliminar status" #: bookwyrm/templates/snippets/status/layout.html:56 #: bookwyrm/templates/snippets/status/layout.html:57 msgid "Boost status" -msgstr "Respaldar status" +msgstr "Impulsar estado" #: bookwyrm/templates/snippets/status/layout.html:60 #: bookwyrm/templates/snippets/status/layout.html:61 msgid "Like status" -msgstr "Me gusta status" +msgstr "Me gusta estado" #: bookwyrm/templates/snippets/status/status.html:10 msgid "boosted" @@ -3395,25 +3584,6 @@ msgstr "respaldó" msgid "More options" msgstr "Más opciones" -#: bookwyrm/templates/snippets/suggested_users.html:16 -#, python-format -msgid "%(mutuals)s follower you follow" -msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "%(mutuals)s seguidor que sigues" -msgstr[1] "%(mutuals)s seguidores que sigues" - -#: bookwyrm/templates/snippets/suggested_users.html:23 -#, python-format -msgid "%(shared_books)s book on your shelves" -msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "%(shared_books)s libro en tus estantes" -msgstr[1] "%(shared_books)s libros en tus estantes" - -#: bookwyrm/templates/snippets/suggested_users.html:31 -#: bookwyrm/templates/user/user_preview.html:36 -msgid "Follows you" -msgstr "Te sigue" - #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" msgstr "Cambiar a esta edición" @@ -3442,7 +3612,7 @@ msgstr "Los libros de %(username)s" #: bookwyrm/templates/user/goal.html:8 #, python-format msgid "%(year)s Reading Progress" -msgstr "%(year)s Progreso de la meta de lectura" +msgstr "Progreso de lectura de %(year)s" #: bookwyrm/templates/user/goal.html:12 msgid "Edit Goal" @@ -3451,7 +3621,7 @@ msgstr "Editar meta" #: bookwyrm/templates/user/goal.html:28 #, python-format msgid "%(name)s hasn't set a reading goal for %(year)s." -msgstr "%(name)s no ha establecido una meta de lectura para %(year)s." +msgstr "%(name)s no se ha fijado un objetivo de lectura para %(year)s." #: bookwyrm/templates/user/goal.html:40 #, python-format @@ -3463,17 +3633,34 @@ msgstr "Tus libros de %(year)s" msgid "%(username)s's %(year)s Books" msgstr "Los libros de %(username)s para %(year)s" -#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "Tus grupos" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "Grupos: %(username)s" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "Crear grupo" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "Perfil de usuario" -#: bookwyrm/templates/user/layout.html:44 +#: bookwyrm/templates/user/layout.html:45 msgid "Follow Requests" msgstr "Solicitudes de seguidor" -#: bookwyrm/templates/user/layout.html:69 +#: bookwyrm/templates/user/layout.html:70 msgid "Reading Goal" -msgstr "Meta de lectura" +msgstr "Objetivo de lectura" + +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "Grupos" #: bookwyrm/templates/user/lists.html:11 #, python-format @@ -3506,7 +3693,7 @@ msgstr "Editar perfil" #: bookwyrm/templates/user/user.html:33 #, python-format msgid "View all %(size)s" -msgstr "Ver todos los %(size)s" +msgstr "Ver los %(size)s" #: bookwyrm/templates/user/user.html:46 msgid "View all books" @@ -3514,7 +3701,7 @@ msgstr "Ver todos los libros" #: bookwyrm/templates/user/user.html:59 msgid "User Activity" -msgstr "Actividad de usuario" +msgstr "Actividad del usuario" #: bookwyrm/templates/user/user.html:63 msgid "RSS feed" @@ -3565,15 +3752,15 @@ msgstr "%(title)s: %(subtitle)s" msgid "Not a valid csv file" msgstr "No un archivo csv válido" -#: bookwyrm/views/login.py:69 +#: bookwyrm/views/landing/login.py:69 msgid "Username or password are incorrect" msgstr "Nombre de usuario o contraseña es incorrecta" -#: bookwyrm/views/password.py:32 +#: bookwyrm/views/landing/password.py:32 msgid "No user with that email address was found." msgstr "No se pudo encontrar un usuario con esa dirección de correo electrónico." -#: bookwyrm/views/password.py:41 +#: bookwyrm/views/landing/password.py:43 #, python-brace-format msgid "A password reset link was sent to {email}" msgstr "Un enlace para reestablecer tu contraseña se envió a {email}" diff --git a/locale/fr_FR/LC_MESSAGES/django.mo b/locale/fr_FR/LC_MESSAGES/django.mo index 5e8dfa6646b38fbed1bbc9159b926c2d9839f176..0bc2fb3dad8abed5091b5b7d7746a492e681d9cc 100644 GIT binary patch delta 24283 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 Qd3p9de&(`|{8j${07^Ri+5i9m diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po index 141bd894..5f9bc8b2 100644 --- a/locale/fr_FR/LC_MESSAGES/django.po +++ b/locale/fr_FR/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-15 22:03+0000\n" -"PO-Revision-Date: 2021-10-16 14:36\n" +"POT-Creation-Date: 2021-10-24 14:09+0000\n" +"PO-Revision-Date: 2021-11-12 17:16\n" "Last-Translator: Mouse Reeve \n" "Language-Team: French\n" "Language: fr\n" @@ -40,35 +40,35 @@ msgstr "Sans expiration" #: bookwyrm/forms.py:263 #, python-brace-format msgid "{i} uses" -msgstr "" +msgstr "{i} utilisations" #: bookwyrm/forms.py:264 msgid "Unlimited" msgstr "Sans limite" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "Ordre de la liste" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "Titre du livre" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 -#: bookwyrm/templates/shelf/shelf.html:168 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Note" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "Trier par" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "Ordre croissant" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "Ordre décroissant" @@ -98,7 +98,7 @@ msgstr "Suppression du modérateur" #: bookwyrm/models/base_model.py:21 msgid "Domain block" -msgstr "" +msgstr "Bloc de domaine" #: bookwyrm/models/book.py:232 msgid "Audiobook" @@ -118,7 +118,7 @@ msgstr "Couverture rigide" #: bookwyrm/models/book.py:236 msgid "Paperback" -msgstr "" +msgstr "Couverture souple" #: bookwyrm/models/federated_server.py:11 #: bookwyrm/templates/settings/federation/edit_instance.html:42 @@ -161,11 +161,11 @@ msgstr "Accueil" #: bookwyrm/settings.py:119 msgid "Books Timeline" -msgstr "" +msgstr "Actualité de mes livres" #: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "Livres" @@ -187,7 +187,7 @@ msgstr "Français" #: bookwyrm/settings.py:169 msgid "Português - Brasil (Brazilian Portuguese)" -msgstr "" +msgstr "Português - Brasil (Portugais brésilien)" #: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" @@ -223,7 +223,7 @@ msgid "Edit Author" msgstr "Modifier l’auteur ou autrice" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "Pseudonymes :" @@ -260,7 +260,7 @@ msgstr "Voir sur Goodreads" #: bookwyrm/templates/author/author.html:108 #, python-format msgid "Books by %(name)s" -msgstr "Livres par %(name)s" +msgstr "Livres de %(name)s" #: bookwyrm/templates/author/edit_author.html:5 msgid "Edit Author:" @@ -276,71 +276,72 @@ msgstr "Ajouté :" msgid "Updated:" msgstr "Mis à jour :" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "Dernière modification par :" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "Métadonnées" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "Nom :" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "Séparez plusieurs valeurs par une virgule." -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "Bio :" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "Wikipedia :" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "Date de naissance :" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "Date de décès :" -#: bookwyrm/templates/author/edit_author.html:79 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Author Identifiers" msgstr "Identifiants de l’auteur ou autrice" -#: bookwyrm/templates/author/edit_author.html:81 +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "Clé Openlibrary :" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "Identifiant Inventaire :" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "Clé Librarything :" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "Clé Goodreads :" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -352,11 +353,13 @@ msgstr "Clé Goodreads :" msgid "Save" msgstr "Enregistrer" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -369,7 +372,7 @@ msgstr "Annuler" #: bookwyrm/templates/landing/large-book.html:25 #: bookwyrm/templates/landing/small-book.html:18 msgid "by" -msgstr "par" +msgstr "de" #: bookwyrm/templates/book/book.html:55 bookwyrm/templates/book/book.html:56 msgid "Edit Book" @@ -397,7 +400,7 @@ msgstr "Ajouter une description" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "Description :" @@ -460,7 +463,7 @@ msgstr "Lieux" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "Listes" @@ -470,7 +473,7 @@ msgstr "Ajouter à la liste" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -543,7 +546,9 @@ msgid "This is a new work" msgstr "Il s’agit d’un nouvel ouvrage." #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "Confirmer" @@ -612,7 +617,7 @@ msgid "John Doe, Jane Smith" msgstr "Claude Dupont, Dominique Durand" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "Couverture" @@ -753,7 +758,7 @@ msgstr "Aide" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 msgid "Edit status" -msgstr "" +msgstr "Modifier le statut" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -793,7 +798,7 @@ msgstr "Envoyer le lien de confirmation de nouveau" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" @@ -890,22 +895,22 @@ msgstr "Tous les comptes connus" #: bookwyrm/templates/discover/card-header.html:9 #, python-format msgid "%(username)s rated %(book_title)s" -msgstr "" +msgstr "%(username)s a noté %(book_title)s" #: bookwyrm/templates/discover/card-header.html:13 #, python-format msgid "%(username)s reviewed %(book_title)s" -msgstr "" +msgstr "%(username)s a critiqué %(book_title)s" #: bookwyrm/templates/discover/card-header.html:17 #, python-format msgid "%(username)s commented on %(book_title)s" -msgstr "" +msgstr "%(username)s a commenté %(book_title)s" #: bookwyrm/templates/discover/card-header.html:21 #, python-format msgid "%(username)s quoted %(book_title)s" -msgstr "" +msgstr "%(username)s a cité un passage de %(book_title)s" #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 @@ -921,7 +926,7 @@ msgstr "Voir les nouveautés de la communauté locale %(site_name)s" #: bookwyrm/templates/discover/large-book.html:52 #: bookwyrm/templates/discover/small-book.html:36 msgid "View status" -msgstr "Afficher tous les status" +msgstr "Afficher le statut" #: bookwyrm/templates/email/confirm/html_content.html:6 #: bookwyrm/templates/email/confirm/text_content.html:4 @@ -974,7 +979,7 @@ msgstr "S’enregistrer maintenant" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format msgid "Learn more about %(site_name)s." -msgstr "" +msgstr "En savoir plus sur %(site_name)s." #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -984,7 +989,7 @@ msgstr "Vous avez reçu une invitation à rejoindre %(site_name)s ! Cliquez le #: bookwyrm/templates/email/invite/text_content.html:8 #, python-format msgid "Learn more about %(site_name)s:" -msgstr "" +msgstr "En savoir plus sur %(site_name)s :" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -993,10 +998,10 @@ msgid "You requested to reset your %(site_name)s password. Click the link below msgstr "Une demande de réinitialisation de votre mot de passe sur %(site_name)s a été initialisée. Cliquez le lien suivant pour définir un nouveau mot de passe et vous connecter à votre compte." #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" msgstr "Changez le mot de passe" @@ -1102,7 +1107,7 @@ msgid "What are you reading?" msgstr "Que lisez‑vous ?" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "Chercher un livre" @@ -1120,8 +1125,9 @@ msgstr "Vous pourrez ajouter des livres lorsque vous commencerez à utiliser %(s #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1137,7 +1143,7 @@ msgid "Popular on %(site_name)s" msgstr "Populaire sur %(site_name)s" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "Aucun livre trouvé" @@ -1223,9 +1229,110 @@ msgstr "Chercher un compte" msgid "No users found for \"%(query)s\"" msgstr "Aucun compte trouvé pour « %(query)s »" +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "Créer un Groupe" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "Géré par %(username)s" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "Supprimer ce groupe ?" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "Cette action ne peut pas être annulée" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "Supprimer" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "Modifier le Groupe" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "Ajouter de nouveaux membres !" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "Nom du groupe :" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "Description du groupe :" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "Supprimer le groupe" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "Chercher et ajouter un·e utilisateur·rice" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "Ce groupe n'a pas de liste" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "Modifier le groupe" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "Les membres peuvent ajouter et supprimer des livres sur les listes de livres d'un groupe" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "Quitter le groupe" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "Vous suit" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "%(mutuals)s abonné(e) que vous suivez" +msgstr[1] "%(mutuals)s abonné(e)s que vous suivez" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s livre sur vos étagères" +msgstr[1] "%(shared_books)s livres sur vos étagères" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "Aucun membre potentiel trouvé pour \"%(user_query)s\"" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "Responsable" + #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "Importer des livres" @@ -1299,7 +1406,7 @@ msgstr "Sauter en bas de liste pour sélectionner les %(failed_count)s items n #: bookwyrm/templates/import/import_status.html:62 #, python-format msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "Ligne %(index)s : %(title)s par %(author)s" +msgstr "Ligne %(index)s : %(title)s de %(author)s" #: bookwyrm/templates/import/import_status.html:82 msgid "Select all" @@ -1322,14 +1429,14 @@ msgid "Book" msgstr "Livre" #: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:150 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Titre" #: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:153 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Auteur/autrice" @@ -1339,20 +1446,7 @@ msgstr "Importé" #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." -msgstr "" - -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "Créer un compte" - -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "Autorisation refusée" - -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "Cette invitation n’est plus valide ; désolé !" +msgstr "Vous pouvez télécharger vos données GoodReads depuis la page Import/Export de votre compte GoodReads." #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format @@ -1369,6 +1463,20 @@ msgstr "Code de conduite" msgid "Privacy Policy" msgstr "Politique de vie privée" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "Créer un compte" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "Autorisation refusée" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "Cette invitation n’est plus valide ; désolé !" + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" msgstr "Livres récents" @@ -1407,6 +1515,53 @@ msgstr "Merci ! Votre demande a bien été reçue." msgid "Your Account" msgstr "Votre compte" +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "Connexion" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "Se connecter" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "Bravo ! L’adresse email a été confirmée." + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "Nom du compte :" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "Mot de passe :" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "Mot de passe oublié ?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "En savoir plus sur ce site" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "Confirmez le mot de passe :" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "Un lien pour changer votre mot de passe sera envoyé à votre addresse email" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "Changer de mot de passe" + #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" @@ -1454,25 +1609,10 @@ msgstr "Se déconnecter" msgid "Notifications" msgstr "Notifications" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "Nom du compte :" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "Mot de passe" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "Mot de passe oublié ?" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "Se connecter" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "Rejoindre" @@ -1513,10 +1653,15 @@ msgstr "Créer une liste" #: bookwyrm/templates/lists/created_text.html:5 #, python-format +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "Créé par %(username)s et géré par %(groupname)s" + +#: bookwyrm/templates/lists/created_text.html:7 +#, python-format msgid "Created and curated by %(username)s" msgstr "Créée et modérée par %(username)s" -#: bookwyrm/templates/lists/created_text.html:7 +#: bookwyrm/templates/lists/created_text.html:9 #, python-format msgid "Created by %(username)s" msgstr "Créée par %(username)s" @@ -1549,118 +1694,130 @@ msgstr "Rejeter" msgid "Delete this list?" msgstr "Supprimer cette liste ?" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "Cette action ne peut pas être annulée" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "Supprimer" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "Modifier la liste" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" msgstr "Modération de la liste :" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "Fermée" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" msgstr "Vous seulement pouvez ajouter ou retirer des livres dans cette liste" -#: bookwyrm/templates/lists/form.html:26 +#: bookwyrm/templates/lists/form.html:27 msgid "Curated" msgstr "Modérée" -#: bookwyrm/templates/lists/form.html:27 +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" msgstr "N’importe qui peut suggérer des livres, soumis à votre approbation" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "Ouvrir" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" msgstr "N’importe qui peut suggérer des livres" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "Groupe" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "Les membres du groupe peuvent ajouter et supprimer des livres de cette liste" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "Sélectionner un Groupe" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "Sélectionner un groupe" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "Vous n'avez pas encore de Groupe !" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "Créer un Groupe" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "Supprimer la liste" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" msgstr "Vous avez suggéré un livre à cette liste !" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" msgstr "Vous avez ajouté un livre à cette liste !" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "Cette liste est actuellement vide" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "Ajouté par %(username)s" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "Position" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "Appliquer" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "Retirer" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "Trier la liste" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" msgstr "Direction" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "Ajouter des livres" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "Suggérer des livres" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "chercher" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" msgstr "Vider la requête" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" msgstr "Aucun livre trouvé pour la requête « %(query)s »" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "Suggérer" @@ -1672,30 +1829,18 @@ msgstr "Sauvegardé" msgid "Your Lists" msgstr "Vos listes" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "Toutes les listes" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "Listes sauvegardées" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "Connexion" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." -msgstr "Bravo ! L’adresse email a été confirmée." - -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "Mot de passe :" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" -msgstr "En savoir plus sur ce site" +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" +msgstr "a accepté votre invitation à rejoindre le groupe \"%(group_name)s\"" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format @@ -1730,22 +1875,22 @@ msgstr "a partagé votre statut" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format msgid "liked your review of %(book_title)s" -msgstr "" +msgstr "a ajouté votre critique de %(book_title)s à ses favoris" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "liked your comment on%(book_title)s" -msgstr "" +msgid "liked your comment on %(book_title)s" +msgstr "a ajouté votre commentaire sur %(book_title)s à ses favoris" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format msgid "liked your quote from %(book_title)s" -msgstr "" +msgstr "a ajouté votre citation de %(book_title)s à ses favoris" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format msgid "liked your status" -msgstr "" +msgstr "a ajouté votre statut à ses favoris" #: bookwyrm/templates/notifications/items/follow.html:15 msgid "followed you" @@ -1760,6 +1905,21 @@ msgstr "vous a envoyé une demande d’abonnement" msgid "Your import completed." msgstr "Votre importation est terminée." +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "vous a invité·e à rejoindre le groupe \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "a rejoint votre groupe \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "a quitté votre groupe \"%(group_name)s\"" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" @@ -1780,6 +1940,16 @@ msgstr "vous a mentionné dans sa citation de % msgid "mentioned you in a status" msgstr "vous a mentionné dans son statut" +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "a été retiré·e de votre groupe \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "Vous avez été retiré·e du groupe \"%(group_name)s\"" + #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format msgid "replied to your review of %(book_title)s" @@ -1805,6 +1975,21 @@ msgstr "a répondu à votre report needs moderation." msgstr "Un nouveau signalement a besoin d’être traité." +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "a changé le niveau de confidentialité du groupe %(group_name)s" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "a changé le nom du groupe %(group_name)s" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "a changé la description du groupe %(group_name)s" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" msgstr "Supprimer les notifications" @@ -1821,20 +2006,6 @@ msgstr "Mentions" msgid "You're all caught up!" msgstr "Aucune nouvelle notification !" -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "Confirmez le mot de passe :" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "Un lien pour changer votre mot de passe sera envoyé à votre addresse email" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "Changer de mot de passe" - #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 #: bookwyrm/templates/preferences/layout.html:31 @@ -1896,7 +2067,7 @@ msgstr "Confidentialité" #: bookwyrm/templates/preferences/edit_user.html:72 msgid "Show reading goal prompt in feed:" -msgstr "" +msgstr "Afficher l'invite d'objectif de lecture dans le fil d'actualité :" #: bookwyrm/templates/preferences/edit_user.html:76 msgid "Show suggested users:" @@ -1936,7 +2107,7 @@ msgstr "Commencer \"%(book_title)s\"" #: bookwyrm/templates/reading_progress/want.html:5 #, python-format msgid "Want to Read \"%(book_title)s\"" -msgstr "" +msgstr "Je veux lire \"%(book_title)s\"" #: bookwyrm/templates/search/book.html:47 #: bookwyrm/templates/settings/reports/reports.html:25 @@ -2042,7 +2213,7 @@ msgstr "Contenu :" #: bookwyrm/templates/settings/announcements/announcement_form.html:30 msgid "Event date:" -msgstr "" +msgstr "Date de l'événement :" #: bookwyrm/templates/settings/announcements/announcements.html:3 #: bookwyrm/templates/settings/announcements/announcements.html:5 @@ -2086,54 +2257,54 @@ msgstr "inactive" #: bookwyrm/templates/settings/announcements/announcements.html:52 msgid "No announcements found" -msgstr "" +msgstr "Aucune annonce trouvée" #: bookwyrm/templates/settings/dashboard/dashboard.html:6 #: bookwyrm/templates/settings/dashboard/dashboard.html:8 #: bookwyrm/templates/settings/layout.html:26 msgid "Dashboard" -msgstr "" +msgstr "Tableau de bord" #: bookwyrm/templates/settings/dashboard/dashboard.html:15 #: bookwyrm/templates/settings/dashboard/dashboard.html:100 msgid "Total users" -msgstr "" +msgstr "Nombre total d'utilisateurs·rices" #: bookwyrm/templates/settings/dashboard/dashboard.html:21 #: bookwyrm/templates/settings/dashboard/user_chart.html:16 msgid "Active this month" -msgstr "" +msgstr "Actifs ce mois" #: bookwyrm/templates/settings/dashboard/dashboard.html:27 msgid "Statuses" -msgstr "" +msgstr "Statuts" #: bookwyrm/templates/settings/dashboard/dashboard.html:33 #: bookwyrm/templates/settings/dashboard/works_chart.html:11 msgid "Works" -msgstr "" +msgstr "Œuvres" #: bookwyrm/templates/settings/dashboard/dashboard.html:43 #, python-format msgid "%(display_count)s open report" msgid_plural "%(display_count)s open reports" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(display_count)s signalement ouvert" +msgstr[1] "%(display_count)s signalements ouverts" #: bookwyrm/templates/settings/dashboard/dashboard.html:54 #, python-format msgid "%(display_count)s invite request" msgid_plural "%(display_count)s invite requests" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(display_count)s demande d'invitation" +msgstr[1] "%(display_count)s demandes d'invitation" #: bookwyrm/templates/settings/dashboard/dashboard.html:65 msgid "Instance Activity" -msgstr "" +msgstr "Activité de l'instance" #: bookwyrm/templates/settings/dashboard/dashboard.html:83 msgid "Interval:" -msgstr "" +msgstr "Intervalle :" #: bookwyrm/templates/settings/dashboard/dashboard.html:87 msgid "Days" @@ -2145,15 +2316,15 @@ msgstr "Semaines" #: bookwyrm/templates/settings/dashboard/dashboard.html:106 msgid "User signup activity" -msgstr "" +msgstr "Nouvelles inscriptions" #: bookwyrm/templates/settings/dashboard/dashboard.html:112 msgid "Status activity" -msgstr "" +msgstr "Nouveaux statuts" #: bookwyrm/templates/settings/dashboard/dashboard.html:118 msgid "Works created" -msgstr "" +msgstr "Œuvres créées" #: bookwyrm/templates/settings/dashboard/registration_chart.html:10 msgid "Registrations" @@ -2161,26 +2332,26 @@ msgstr "Inscriptions" #: bookwyrm/templates/settings/dashboard/status_chart.html:11 msgid "Statuses posted" -msgstr "" +msgstr "Statuts publiés" #: bookwyrm/templates/settings/dashboard/user_chart.html:11 msgid "Total" -msgstr "" +msgstr "Total" #: bookwyrm/templates/settings/email_blocklist/domain_form.html:5 #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10 msgid "Add domain" -msgstr "" +msgstr "Ajouter un domaine" #: bookwyrm/templates/settings/email_blocklist/domain_form.html:11 msgid "Domain:" -msgstr "" +msgstr "Domaine :" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:5 #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:7 #: bookwyrm/templates/settings/layout.html:59 msgid "Email Blocklist" -msgstr "" +msgstr "Liste des e-mails bloqués" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:18 msgid "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." @@ -2188,7 +2359,7 @@ msgstr "Quand quelqu'un essaiera de s'inscrire avec un e-mail de ce domaine, auc #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:25 msgid "Domain" -msgstr "" +msgstr "Domaine" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:29 #: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:27 @@ -2199,12 +2370,12 @@ msgstr "Options" #, python-format msgid "%(display_count)s user" msgid_plural "%(display_count)s users" -msgstr[0] "%(display_count)s utilisateur" -msgstr[1] "%(display_count)s utilisateurs" +msgstr[0] "%(display_count)s utilisateur·rice" +msgstr[1] "%(display_count)s utilisateurs·rices" #: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:59 msgid "No email domains currently blocked" -msgstr "" +msgstr "Aucun domaine de messagerie n’est actuellement bloqué" #: bookwyrm/templates/settings/federation/edit_instance.html:3 #: bookwyrm/templates/settings/federation/edit_instance.html:6 @@ -2257,7 +2428,7 @@ msgid "Details" msgstr "Détails" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "Activité" @@ -2299,7 +2470,7 @@ msgstr "Modifier" #: bookwyrm/templates/settings/federation/instance.html:79 msgid "No notes" -msgstr "" +msgstr "Aucune note" #: bookwyrm/templates/settings/federation/instance.html:94 #: bookwyrm/templates/settings/users/user_moderation_actions.html:8 @@ -2499,7 +2670,7 @@ msgstr "Aucune adresse IP n'est actuellement bloquée" #: bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html:6 msgid "You can block IP ranges using CIDR syntax." -msgstr "" +msgstr "Vous pouvez bloquer des plages d'adresses IP en utilisant la syntaxe CIDR." #: bookwyrm/templates/settings/layout.html:4 msgid "Administration" @@ -2634,7 +2805,7 @@ msgstr "Description courte :" #: bookwyrm/templates/settings/site.html:37 msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." -msgstr "" +msgstr "Utilisé dans l'aperçu de l'instance sur joinbookwyrm.com. Ne prend pas en charge l'HTML ou le Markdown." #: bookwyrm/templates/settings/site.html:41 msgid "Code of conduct:" @@ -2761,7 +2932,7 @@ msgstr "Local" #: bookwyrm/templates/settings/users/user_info.html:38 msgid "Remote" -msgstr "" +msgstr "Distant·e" #: bookwyrm/templates/settings/users/user_info.html:47 msgid "User details" @@ -2789,7 +2960,7 @@ msgstr "Abonné(e)s approuvés manuellement :" #: bookwyrm/templates/settings/users/user_info.html:76 msgid "Discoverable:" -msgstr "" +msgstr "Visible publiquement :" #: bookwyrm/templates/settings/users/user_info.html:80 msgid "Deactivation reason:" @@ -2833,53 +3004,66 @@ msgstr "Créer une étagère" msgid "Edit Shelf" msgstr "Modifier l’étagère" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "Tous les livres" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "Créer une étagère" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "%(formatted_count)s livre" msgstr[1] "%(formatted_count)s livres" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" -msgstr "" +msgstr "(affichage de %(start)s-%(end)s)" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "Modifier l’étagère" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "Supprimer l’étagère" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "Date d’ajout" -#: bookwyrm/templates/shelf/shelf.html:133 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "Commencé" -#: bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:164 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "Terminé" -#: bookwyrm/templates/shelf/shelf.html:190 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "Cette étagère est vide" +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "Inviter" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "Annuler l'invitation" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "Retirer @%(username)s" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" @@ -2899,7 +3083,7 @@ msgstr "Pas de couverture" #: bookwyrm/templates/snippets/book_titleby.html:6 #, python-format msgid "%(title)s by" -msgstr "" +msgstr "%(title)s de" #: bookwyrm/templates/snippets/boost_button.html:20 #: bookwyrm/templates/snippets/boost_button.html:21 @@ -2921,7 +3105,7 @@ msgstr "Citation" #: bookwyrm/templates/snippets/create_status/comment.html:15 msgid "Some thoughts on the book" -msgstr "" +msgstr "Quelques réflexions sur ce livre" #: bookwyrm/templates/snippets/create_status/comment.html:27 #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 @@ -2956,7 +3140,7 @@ msgstr "Contenu" #: bookwyrm/templates/snippets/create_status/content_warning_field.html:10 msgid "Content warning:" -msgstr "" +msgstr "Avertissement sur le contenu :" #: bookwyrm/templates/snippets/create_status/content_warning_field.html:18 msgid "Spoilers ahead!" @@ -2975,6 +3159,7 @@ msgstr "Commentaire :" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "Privé" @@ -2989,24 +3174,24 @@ msgstr "Citation :" #: bookwyrm/templates/snippets/create_status/quotation.html:25 #, python-format msgid "An excerpt from '%(book_title)s'" -msgstr "" +msgstr "Un extrait de '%(book_title)s'" #: bookwyrm/templates/snippets/create_status/quotation.html:32 msgid "Position:" -msgstr "" +msgstr "Emplacement :" #: bookwyrm/templates/snippets/create_status/quotation.html:45 msgid "On page:" -msgstr "" +msgstr "À la page :" #: bookwyrm/templates/snippets/create_status/quotation.html:51 msgid "At percent:" -msgstr "" +msgstr "Au pourcentage :" #: bookwyrm/templates/snippets/create_status/review.html:25 #, python-format msgid "Your review of '%(book_title)s'" -msgstr "" +msgstr "Votre critique de '%(book_title)s'" #: bookwyrm/templates/snippets/create_status/review.html:40 msgid "Review:" @@ -3050,7 +3235,7 @@ msgstr "Annuler les filtres" #: bookwyrm/templates/snippets/follow_button.html:14 #, python-format msgid "Follow @%(username)s" -msgstr "" +msgstr "S'abonner à @%(username)s" #: bookwyrm/templates/snippets/follow_button.html:16 msgid "Follow" @@ -3063,13 +3248,14 @@ msgstr "Annuler la demande d’abonnement" #: bookwyrm/templates/snippets/follow_button.html:30 #, python-format msgid "Unfollow @%(username)s" -msgstr "" +msgstr "Se désabonner de @%(username)s" #: bookwyrm/templates/snippets/follow_button.html:32 msgid "Unfollow" msgstr "Se désabonner" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "Accepter" @@ -3082,8 +3268,8 @@ msgstr "Aucune note" #, python-format msgid "%(half_rating)s star" msgid_plural "%(half_rating)s stars" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(half_rating)s étoile" +msgstr[1] "%(half_rating)s étoiles" #: bookwyrm/templates/snippets/form_rate_stars.html:64 #: bookwyrm/templates/snippets/stars.html:7 @@ -3104,8 +3290,8 @@ msgstr[1] "souhaite lire %(counter)s livres en %(year)s" #, python-format msgid "rated %(title)s: %(display_rating)s star" msgid_plural "rated %(title)s: %(display_rating)s stars" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "a noté %(title)s : %(display_rating)s étoile" +msgstr[1] "a noté %(title)s : %(display_rating)s étoiles" #: bookwyrm/templates/snippets/generated_status/review_pure_name.html:4 #, python-format @@ -3153,7 +3339,7 @@ msgstr "%(percent)s%% terminé !" #: bookwyrm/templates/snippets/goal_progress.html:12 #, python-format msgid "You've read %(read_count)s of %(goal_count)s books." -msgstr "Vous avez lu %(read_count)s sur %(goal_count)s livres." +msgstr "Vous avez lu %(read_count)s livres sur %(goal_count)s." #: bookwyrm/templates/snippets/goal_progress.html:14 #, python-format @@ -3181,12 +3367,14 @@ msgstr "Suivante" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "Public" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "Non listé" @@ -3195,6 +3383,7 @@ msgid "Followers-only" msgstr "Abonnemé(e)s uniquement" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "Confidentialité du statut" @@ -3230,7 +3419,7 @@ msgstr "Lecture terminée le" #: bookwyrm/templates/snippets/reading_modals/form.html:9 msgid "(Optional)" -msgstr "" +msgstr "(Facultatif)" #: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:5 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:50 @@ -3302,21 +3491,21 @@ msgstr "Terminer la lecture" #: bookwyrm/templates/snippets/status/content_status.html:72 msgid "Content warning" -msgstr "" +msgstr "Avertissement sur le contenu" #: bookwyrm/templates/snippets/status/content_status.html:79 msgid "Show status" -msgstr "" +msgstr "Afficher le statut" #: bookwyrm/templates/snippets/status/content_status.html:101 #, python-format msgid "(Page %(page)s)" -msgstr "" +msgstr "(Page %(page)s)" #: bookwyrm/templates/snippets/status/content_status.html:103 #, python-format msgid "(%(percent)s%%)" -msgstr "" +msgstr "(%(percent)s%%)" #: bookwyrm/templates/snippets/status/content_status.html:125 msgid "Open image in new window" @@ -3324,12 +3513,12 @@ msgstr "Ouvrir l’image dans une nouvelle fenêtre" #: bookwyrm/templates/snippets/status/content_status.html:144 msgid "Hide status" -msgstr "" +msgstr "Masquer le statut" #: bookwyrm/templates/snippets/status/header.html:45 #, python-format msgid "edited %(date)s" -msgstr "" +msgstr "modifié le %(date)s" #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format @@ -3395,25 +3584,6 @@ msgstr "a partagé" msgid "More options" msgstr "Plus d’options" -#: bookwyrm/templates/snippets/suggested_users.html:16 -#, python-format -msgid "%(mutuals)s follower you follow" -msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "%(mutuals)s abonné(e) que vous suivez" -msgstr[1] "%(mutuals)s abonné(e)s que vous suivez" - -#: bookwyrm/templates/snippets/suggested_users.html:23 -#, python-format -msgid "%(shared_books)s book on your shelves" -msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "%(shared_books)s livre sur vos étagères" -msgstr[1] "%(shared_books)s livres sur vos étagères" - -#: bookwyrm/templates/snippets/suggested_users.html:31 -#: bookwyrm/templates/user/user_preview.html:36 -msgid "Follows you" -msgstr "Vous suit" - #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" msgstr "Changer vers cette édition" @@ -3463,18 +3633,35 @@ msgstr "Vos livres en %(year)s" msgid "%(username)s's %(year)s Books" msgstr "Livres de %(username)s en %(year)s" -#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "Vos Groupes" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "Groupes : %(username)s" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "Créer un groupe" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "Profil" -#: bookwyrm/templates/user/layout.html:44 +#: bookwyrm/templates/user/layout.html:45 msgid "Follow Requests" msgstr "Demandes d’abonnement" -#: bookwyrm/templates/user/layout.html:69 +#: bookwyrm/templates/user/layout.html:70 msgid "Reading Goal" msgstr "Défi lecture" +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "Groupes" + #: bookwyrm/templates/user/lists.html:11 #, python-format msgid "Lists: %(username)s" @@ -3565,15 +3752,15 @@ msgstr "%(title)s (%(subtitle)s)" msgid "Not a valid csv file" msgstr "Fichier CSV non valide" -#: bookwyrm/views/login.py:69 +#: bookwyrm/views/landing/login.py:69 msgid "Username or password are incorrect" msgstr "Identifiant ou mot de passe incorrect" -#: bookwyrm/views/password.py:32 +#: bookwyrm/views/landing/password.py:32 msgid "No user with that email address was found." msgstr "Aucun compte avec cette adresse email n’a été trouvé." -#: bookwyrm/views/password.py:41 +#: bookwyrm/views/landing/password.py:43 #, python-brace-format msgid "A password reset link was sent to {email}" msgstr "Un lien de réinitialisation a été envoyé à {email}." @@ -3581,5 +3768,5 @@ msgstr "Un lien de réinitialisation a été envoyé à {email}." #: bookwyrm/views/rss_feed.py:35 #, python-brace-format msgid "Status updates from {obj.display_name}" -msgstr "" +msgstr "Mises à jour de statut de {obj.display_name}" diff --git a/locale/zh_Hans/LC_MESSAGES/django.mo b/locale/zh_Hans/LC_MESSAGES/django.mo index 1d1227f8092b70c68bb692fb532759090142aa83..fbb1986cf8b43b014b8909367d9d5bc656e798f6 100644 GIT binary patch literal 56859 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 TiM1&CYiewpj(c2pw%Rq delta 16014 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 X)N0D=nC+kKF3kPe^N(dMUF`f9Y|Jc_ diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po index 2a070735..648ec791 100644 --- a/locale/zh_Hans/LC_MESSAGES/django.po +++ b/locale/zh_Hans/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-15 22:03+0000\n" -"PO-Revision-Date: 2021-10-16 14:36\n" +"POT-Creation-Date: 2021-10-24 14:09+0000\n" +"PO-Revision-Date: 2021-11-07 04:05\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Chinese Simplified\n" "Language: zh\n" @@ -46,29 +46,29 @@ msgstr "{i} 次使用" msgid "Unlimited" msgstr "不受限" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "列表顺序" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "书名" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 -#: bookwyrm/templates/shelf/shelf.html:168 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "评价" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "排序方式" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "升序" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "降序" @@ -114,7 +114,7 @@ msgstr "图像小说" #: bookwyrm/models/book.py:235 msgid "Hardcover" -msgstr "硬封面" +msgstr "精装" #: bookwyrm/models/book.py:236 msgid "Paperback" @@ -165,7 +165,7 @@ msgstr "书目时间线" #: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "书目" @@ -187,7 +187,7 @@ msgstr "Français(法语)" #: bookwyrm/settings.py:169 msgid "Português - Brasil (Brazilian Portuguese)" -msgstr "" +msgstr "葡萄牙语-巴西(巴西的葡语)" #: bookwyrm/settings.py:170 msgid "简体中文 (Simplified Chinese)" @@ -223,7 +223,7 @@ msgid "Edit Author" msgstr "编辑作者" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "别名:" @@ -276,71 +276,72 @@ msgstr "添加了:" msgid "Updated:" msgstr "更新了:" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "最后编辑人:" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "元数据" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "名称:" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "请用英文逗号(,)分隔多个值。" -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "简介:" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "维基百科链接:" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "出生日期:" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "死亡日期:" -#: bookwyrm/templates/author/edit_author.html:79 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Author Identifiers" msgstr "作者标识号:" -#: bookwyrm/templates/author/edit_author.html:81 +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "Openlibrary key:" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "Inventaire ID:" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "Librarything key:" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "Goodreads key:" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -352,11 +353,13 @@ msgstr "Goodreads key:" msgid "Save" msgstr "保存" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -396,7 +399,7 @@ msgstr "添加描述" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "描述:" @@ -459,7 +462,7 @@ msgstr "地点" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "列表" @@ -469,7 +472,7 @@ msgstr "添加到列表" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -542,7 +545,9 @@ msgid "This is a new work" msgstr "这是一个新的作品。" #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "确认" @@ -611,7 +616,7 @@ msgid "John Doe, Jane Smith" msgstr "张三, 李四" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "封面" @@ -752,7 +757,7 @@ msgstr "帮助" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 msgid "Edit status" -msgstr "" +msgstr "设置状态" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -792,7 +797,7 @@ msgstr "重新发送确认链接" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" @@ -887,22 +892,22 @@ msgstr "所有已知用户" #: bookwyrm/templates/discover/card-header.html:9 #, python-format msgid "%(username)s rated %(book_title)s" -msgstr "" +msgstr "%(username)s%(book_title)s 留下了评分" #: bookwyrm/templates/discover/card-header.html:13 #, python-format msgid "%(username)s reviewed %(book_title)s" -msgstr "" +msgstr "%(username)s 已评价 %(book_title)s" #: bookwyrm/templates/discover/card-header.html:17 #, python-format msgid "%(username)s commented on %(book_title)s" -msgstr "" +msgstr "%(username)s 评论了 %(book_title)s" #: bookwyrm/templates/discover/card-header.html:21 #, python-format msgid "%(username)s quoted %(book_title)s" -msgstr "" +msgstr "%(username)s 引用了 %(book_title)s" #: bookwyrm/templates/discover/discover.html:4 #: bookwyrm/templates/discover/discover.html:10 @@ -971,7 +976,7 @@ msgstr "立即加入" #: bookwyrm/templates/email/invite/html_content.html:15 #, python-format msgid "Learn more about %(site_name)s." -msgstr "" +msgstr "了解更多 关于 %(site_name)s" #: bookwyrm/templates/email/invite/text_content.html:4 #, python-format @@ -981,7 +986,7 @@ msgstr "你受邀请加入 %(site_name)s!点击下面的连接来创建帐号 #: bookwyrm/templates/email/invite/text_content.html:8 #, python-format msgid "Learn more about %(site_name)s:" -msgstr "" +msgstr "进一步了解 %(site_name)s" #: bookwyrm/templates/email/password_reset/html_content.html:6 #: bookwyrm/templates/email/password_reset/text_content.html:4 @@ -990,10 +995,10 @@ msgid "You requested to reset your %(site_name)s password. Click the link below msgstr "你请求重置你在 %(site_name)s 的密码。点击下面的链接来设置新密码并登录你的帐号。" #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" msgstr "重设密码" @@ -1099,7 +1104,7 @@ msgid "What are you reading?" msgstr "你在阅读什么?" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "搜索书目" @@ -1117,8 +1122,9 @@ msgstr "你可以在开始使用 %(site_name)s 后添加书目。" #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1134,7 +1140,7 @@ msgid "Popular on %(site_name)s" msgstr "%(site_name)s 上的热门" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "没有找到书目" @@ -1220,9 +1226,108 @@ msgstr "搜索用户" msgid "No users found for \"%(query)s\"" msgstr "没有找到 \"%(query)s\" 的用户" +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "创建群组" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "由 %(username)s 创建并策展" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "删除该群组" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "此操作无法被撤销" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "删除" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "编辑群组" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "添加新成员" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "群组名称" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "群组描述" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "删除群组" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "搜索或添加用户" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "这个群组没有任何列表" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "编辑群组" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "会员可以在群组的书籍列表中添加和删除书" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "退出群组" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "正在关注着你" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "%(mutuals)s 个你也关注的关注者" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s 本在你书架上也有的书" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "未找到可能的成员 \"%(user_query)s\"" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "管理员" + #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "导入书目" @@ -1319,14 +1424,14 @@ msgid "Book" msgstr "书目" #: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:150 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "标题" #: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:153 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "作者" @@ -1336,20 +1441,7 @@ msgstr "已导入" #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." -msgstr "" - -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "创建帐号" - -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "没有权限" - -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "抱歉!此邀请码已不再有效。" +msgstr "您可以从 导入/导出页面 下载或导出您的 Goodread 数据。" #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format @@ -1366,6 +1458,20 @@ msgstr "行为准则" msgid "Privacy Policy" msgstr "隐私政策" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "创建帐号" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "没有权限" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "抱歉!此邀请码已不再有效。" + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" msgstr "最近书目" @@ -1404,6 +1510,53 @@ msgstr "谢谢你!我们已经收到了你的请求。" msgid "Your Account" msgstr "你的帐号" +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "登录" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "登录" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "成功!邮箱地址已确认。" + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "用户名:" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "密码:" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "忘记了密码?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "更多关于本站点的信息" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "确认密码:" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "重设你的密码的链接将会被发送到你的邮箱地址" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "重设密码" + #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" @@ -1451,25 +1604,10 @@ msgstr "登出" msgid "Notifications" msgstr "通知" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "用户名:" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "密码" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "忘记了密码?" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "登录" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "加入" @@ -1510,10 +1648,15 @@ msgstr "创建列表" #: bookwyrm/templates/lists/created_text.html:5 #, python-format +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "由 %(username)s 创建,由 %(groupname)s 管理" + +#: bookwyrm/templates/lists/created_text.html:7 +#, python-format msgid "Created and curated by %(username)s" msgstr "由 %(username)s 创建并策展" -#: bookwyrm/templates/lists/created_text.html:7 +#: bookwyrm/templates/lists/created_text.html:9 #, python-format msgid "Created by %(username)s" msgstr "由 %(username)s 创建" @@ -1546,118 +1689,130 @@ msgstr "削除" msgid "Delete this list?" msgstr "删除此列表?" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "此操作无法被撤销" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "删除" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "编辑列表" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" msgstr "列表策展:" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "已关闭" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" msgstr "只有你可以在此列表中添加或移除书目" -#: bookwyrm/templates/lists/form.html:26 +#: bookwyrm/templates/lists/form.html:27 msgid "Curated" msgstr "策展" -#: bookwyrm/templates/lists/form.html:27 +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" msgstr "任何人都可以推荐书目、主题让你批准" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "开放" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" msgstr "任何人都可以向此列表中添加书目" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "组(Group)" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "群组成员可以添加并从列表中移除" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "选择群组" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "选择一个组" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "您目前尚未拥有任何群组!" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "创建一个群组" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "删除列表" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" msgstr "你成功向该列表推荐了一本书!" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" msgstr "你成功向此列表添加了一本书!" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "此列表当前是空的" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "由 %(username)s 添加" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "列表位置:" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "设定" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "移除" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "排序列表" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" msgstr "方向" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "添加书目" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "推荐书目" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "搜索" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" msgstr "清除搜索" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" msgstr "没有符合 “%(query)s” 请求的书目" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "推荐" @@ -1669,30 +1824,18 @@ msgstr "已保存" msgid "Your Lists" msgstr "你的列表" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "所有列表" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "保存的列表" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "登录" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." -msgstr "成功!邮箱地址已确认。" - -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "密码:" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" -msgstr "更多关于本站点的信息" +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" +msgstr "接受了您的邀请,加入 \"%(group_name)s\" 群组" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format @@ -1727,22 +1870,22 @@ msgstr "转发了你的 状态" #: bookwyrm/templates/notifications/items/fav.html:19 #, python-format msgid "liked your review of %(book_title)s" -msgstr "" +msgstr "喜欢了你 %(book_title)s 的书评" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "liked your comment on%(book_title)s" -msgstr "" +msgid "liked your comment on %(book_title)s" +msgstr "喜欢了你的 %(book_title)s 的评论" #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format msgid "liked your quote from %(book_title)s" -msgstr "" +msgstr "喜欢了你对 %(book_title)s 的引用" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format msgid "liked your status" -msgstr "" +msgstr "喜欢了你的 状态" #: bookwyrm/templates/notifications/items/follow.html:15 msgid "followed you" @@ -1757,6 +1900,21 @@ msgstr "向你发送了关注请求" msgid "Your import completed." msgstr "你的 导入 已完成。" +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "邀请您加入群组 \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "已加入您的群組 \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "退出了了您的群组 \"%(group_name)s\"" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" @@ -1777,6 +1935,16 @@ msgstr "在 %(book_title)s 的引用status" msgstr "在 状态 中提到了你" +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "已经从您的群组中将 \"%(group_name)s\" 移除" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "您已经被从 \"%(group_name)s\" 组中移除" + #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format msgid "replied to your review of %(book_title)s" @@ -1802,6 +1970,21 @@ msgstr "回复 了你的 report needs moderation." msgstr "有新的 报告 需要仲裁。" +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "更改了 %(group_name)s的隐私级别" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "更改了 %(group_name)s的名称" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "更改了 %(group_name)s的描述" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" msgstr "删除通知" @@ -1818,20 +2001,6 @@ msgstr "提及" msgid "You're all caught up!" msgstr "你什么也没错过!" -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "确认密码:" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "重设你的密码的链接将会被发送到你的邮箱地址" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "重设密码" - #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 #: bookwyrm/templates/preferences/layout.html:31 @@ -2251,7 +2420,7 @@ msgid "Details" msgstr "详细" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "活动" @@ -2628,7 +2797,7 @@ msgstr "简要描述:" #: bookwyrm/templates/settings/site.html:37 msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." -msgstr "" +msgstr "在 joinbookwyrm.com 上预览实例时使用。不支持 HTML 或 Markdown。" #: bookwyrm/templates/settings/site.html:41 msgid "Code of conduct:" @@ -2827,52 +2996,65 @@ msgstr "创建书架" msgid "Edit Shelf" msgstr "编辑书架" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "所有书目" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "创建书架" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "%(formatted_count)s 本书籍" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" msgstr "(正在显示 %(start)s 到 %(end)s)" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "编辑书架" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "删除书架" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "上架时间" -#: bookwyrm/templates/shelf/shelf.html:133 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "开始时间" -#: bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:164 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "完成时间" -#: bookwyrm/templates/shelf/shelf.html:190 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "此书架是空的。" +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "邀请" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "移除邀请" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "移除 @%(username)s" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" @@ -2967,6 +3149,7 @@ msgstr "评论:" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "私密" @@ -3062,6 +3245,7 @@ msgid "Unfollow" msgstr "取消关注" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "接受" @@ -3168,12 +3352,14 @@ msgstr "往后" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "公开" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "不公开" @@ -3182,6 +3368,7 @@ msgid "Followers-only" msgstr "仅关注者" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "发文隐私" @@ -3316,7 +3503,7 @@ msgstr "隐藏状态" #: bookwyrm/templates/snippets/status/header.html:45 #, python-format msgid "edited %(date)s" -msgstr "" +msgstr "在 %(date)s 已编辑" #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format @@ -3382,23 +3569,6 @@ msgstr "转发了" msgid "More options" msgstr "更多选项" -#: bookwyrm/templates/snippets/suggested_users.html:16 -#, python-format -msgid "%(mutuals)s follower you follow" -msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "%(mutuals)s 个你也关注的关注者" - -#: bookwyrm/templates/snippets/suggested_users.html:23 -#, python-format -msgid "%(shared_books)s book on your shelves" -msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "%(shared_books)s 本在你书架上也有的书" - -#: bookwyrm/templates/snippets/suggested_users.html:31 -#: bookwyrm/templates/user/user_preview.html:36 -msgid "Follows you" -msgstr "正在关注着你" - #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" msgstr "切换到此版本" @@ -3448,18 +3618,35 @@ msgstr "你 %(year)s 的书目" msgid "%(username)s's %(year)s Books" msgstr "%(username)s 在 %(year)s 的书目" -#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "您的群组" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "群组: %(username)s" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "创建群组" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "用户个人资料" -#: bookwyrm/templates/user/layout.html:44 +#: bookwyrm/templates/user/layout.html:45 msgid "Follow Requests" msgstr "关注请求" -#: bookwyrm/templates/user/layout.html:69 +#: bookwyrm/templates/user/layout.html:70 msgid "Reading Goal" msgstr "阅读目标" +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "群组" + #: bookwyrm/templates/user/lists.html:11 #, python-format msgid "Lists: %(username)s" @@ -3548,15 +3735,15 @@ msgstr "%(title)s:%(subtitle)s" msgid "Not a valid csv file" msgstr "不是有效的 csv 文件" -#: bookwyrm/views/login.py:69 +#: bookwyrm/views/landing/login.py:69 msgid "Username or password are incorrect" msgstr "用户名或密码不正确" -#: bookwyrm/views/password.py:32 +#: bookwyrm/views/landing/password.py:32 msgid "No user with that email address was found." msgstr "没有找到使用该邮箱的用户。" -#: bookwyrm/views/password.py:41 +#: bookwyrm/views/landing/password.py:43 #, python-brace-format msgid "A password reset link was sent to {email}" msgstr "密码重置连接已发送给 {email}" diff --git a/locale/zh_Hant/LC_MESSAGES/django.mo b/locale/zh_Hant/LC_MESSAGES/django.mo index 0bd0ad46e02da915244e3efe4ba13fe687a55bad..1f615988e85b86ceed992fb8f9eeca9fa4068e0d 100644 GIT binary patch delta 20 ccmaF4is|JlrVTHWSdB~+3@tXlO{y^h0Ay+i$p8QV delta 20 ccmaF4is|JlrVTHWSPjh-3{5t_O{y^h0AyMS#sB~S diff --git a/locale/zh_Hant/LC_MESSAGES/django.po b/locale/zh_Hant/LC_MESSAGES/django.po index 7b9d3b77..cdaa1f81 100644 --- a/locale/zh_Hant/LC_MESSAGES/django.po +++ b/locale/zh_Hant/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-15 22:03+0000\n" -"PO-Revision-Date: 2021-10-16 14:36\n" +"POT-Creation-Date: 2021-10-24 14:09+0000\n" +"PO-Revision-Date: 2021-10-24 18:36\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Chinese Traditional\n" "Language: zh\n" @@ -46,29 +46,29 @@ msgstr "" msgid "Unlimited" msgstr "不受限" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "列表順序" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "書名" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 -#: bookwyrm/templates/shelf/shelf.html:168 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "評價" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "排序方式" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "升序" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "降序" @@ -165,7 +165,7 @@ msgstr "" #: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "書目" @@ -223,7 +223,7 @@ msgid "Edit Author" msgstr "編輯作者" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "別名:" @@ -276,71 +276,72 @@ msgstr "新增了:" msgid "Updated:" msgstr "更新了:" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "最後編輯者:" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "元資料" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "名稱:" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "請用逗號(,)分隔多個值。" -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "簡介:" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "維基百科連結:" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "出生日期:" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "死亡日期:" -#: bookwyrm/templates/author/edit_author.html:79 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Author Identifiers" msgstr "作者標識號:" -#: bookwyrm/templates/author/edit_author.html:81 +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "Openlibrary key:" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "Inventaire ID:" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "Librarything key:" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "Goodreads key:" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -352,11 +353,13 @@ msgstr "Goodreads key:" msgid "Save" msgstr "儲存" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -396,7 +399,7 @@ msgstr "新增描述" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "描述:" @@ -459,7 +462,7 @@ msgstr "地點" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "列表" @@ -469,7 +472,7 @@ msgstr "新增到列表" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -542,7 +545,9 @@ msgid "This is a new work" msgstr "這是一個新的作品。" #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "確認" @@ -611,7 +616,7 @@ msgid "John Doe, Jane Smith" msgstr "John Doe, Jane Smith" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "封面" @@ -792,7 +797,7 @@ msgstr "" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" @@ -990,10 +995,10 @@ msgid "You requested to reset your %(site_name)s password. Click the link below msgstr "你請求重置你在 %(site_name)s 的密碼。點選下面的連結來設定新密碼並登入你的帳號。" #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" msgstr "重設密碼" @@ -1099,7 +1104,7 @@ msgid "What are you reading?" msgstr "你在閱讀什麼?" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "搜尋書目" @@ -1117,8 +1122,9 @@ msgstr "你可以在開始使用 %(site_name)s 後新增書目。" #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1134,7 +1140,7 @@ msgid "Popular on %(site_name)s" msgstr "%(site_name)s 上的熱門" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "沒有找到書目" @@ -1220,9 +1226,108 @@ msgstr "搜尋使用者" msgid "No users found for \"%(query)s\"" msgstr "沒有找到 \"%(query)s\" 的使用者" +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "刪除" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "%(mutuals)s 個你也關注的關注者" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s 本在你書架上也有的書" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "" + #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "匯入書目" @@ -1319,14 +1424,14 @@ msgid "Book" msgstr "書目" #: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:150 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "標題" #: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:153 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "作者" @@ -1338,19 +1443,6 @@ msgstr "已匯入" msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "" -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "建立帳號" - -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "沒有權限" - -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "抱歉!此邀請碼已不再有效。" - #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1366,6 +1458,20 @@ msgstr "行為準則" msgid "Privacy Policy" msgstr "隱私政策" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "建立帳號" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "沒有權限" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "抱歉!此邀請碼已不再有效。" + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" msgstr "最近書目" @@ -1404,6 +1510,53 @@ msgstr "謝謝你!我們已經受到了你的請求。" msgid "Your Account" msgstr "你的帳號" +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "登入" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "登入" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "" + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "使用者名稱:" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "密碼:" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "忘記了密碼?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "關於本網站的更多" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "確認密碼:" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "重設你的密碼的連結將會被發送到你的郵箱地址" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "重設密碼" + #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" @@ -1451,25 +1604,10 @@ msgstr "登出" msgid "Notifications" msgstr "通知" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "使用者名稱:" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "密碼" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "忘記了密碼?" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "登入" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "加入" @@ -1510,10 +1648,15 @@ msgstr "建立列表" #: bookwyrm/templates/lists/created_text.html:5 #, python-format +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "" + +#: bookwyrm/templates/lists/created_text.html:7 +#, python-format msgid "Created and curated by %(username)s" msgstr "由 %(username)s 建立並管理" -#: bookwyrm/templates/lists/created_text.html:7 +#: bookwyrm/templates/lists/created_text.html:9 #, python-format msgid "Created by %(username)s" msgstr "由 %(username)s 建立" @@ -1546,118 +1689,130 @@ msgstr "放棄" msgid "Delete this list?" msgstr "" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "刪除" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "編輯列表" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" msgstr "列表管理:" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "已關閉" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" msgstr "只有你可以在此列表中新增或移除書目" -#: bookwyrm/templates/lists/form.html:26 +#: bookwyrm/templates/lists/form.html:27 msgid "Curated" msgstr "管理" -#: bookwyrm/templates/lists/form.html:27 +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" msgstr "任何人都可以推薦書目、主題,但須經你的批准。" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" msgstr "任何人都可以向此列表新增書目" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" msgstr "你成功!向該列表推薦了一本書" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" msgstr "你成功在此列表新增了一本書!" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "此列表當前是空的" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "由 %(username)s 新增" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "列表位置:" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "設定" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "移除" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "排序列表" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" msgstr "方向" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "新增書目" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "推薦書目" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "搜尋" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" msgstr "清除搜尋" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" msgstr "沒有符合 \"%(query)s\" 請求的書目" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "推薦" @@ -1669,31 +1824,19 @@ msgstr "" msgid "Your Lists" msgstr "你的列表" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "登入" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" msgstr "" -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "密碼:" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" -msgstr "關於本網站的更多" - #: bookwyrm/templates/notifications/items/add.html:24 #, python-format msgid "added %(book_title)s to your list \"%(list_name)s\"" @@ -1731,7 +1874,7 @@ msgstr "" #: bookwyrm/templates/notifications/items/fav.html:25 #, python-format -msgid "liked your comment on%(book_title)s" +msgid "liked your comment on %(book_title)s" msgstr "" #: bookwyrm/templates/notifications/items/fav.html:31 @@ -1757,6 +1900,21 @@ msgstr "向你傳送了關注請求" msgid "Your import completed." msgstr "你的 匯入 已完成。" +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" @@ -1777,6 +1935,16 @@ msgstr "在 %(book_title)s 的引用status" msgstr "在 狀態 中提到了你" +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "" + #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format msgid "replied to your review of %(book_title)s" @@ -1802,6 +1970,21 @@ msgstr "回覆 了你的 report needs moderation." msgstr "有新的 舉報 需要仲裁。" +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" msgstr "刪除通知" @@ -1818,20 +2001,6 @@ msgstr "提及" msgid "You're all caught up!" msgstr "你什麼也沒錯過!" -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "確認密碼:" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "重設你的密碼的連結將會被發送到你的郵箱地址" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "重設密碼" - #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 #: bookwyrm/templates/preferences/layout.html:31 @@ -2251,7 +2420,7 @@ msgid "Details" msgstr "詳細" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "活動" @@ -2827,52 +2996,65 @@ msgstr "建立書架" msgid "Edit Shelf" msgstr "編輯書架" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "所有書目" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "建立書架" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" msgstr "" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "編輯書架" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "刪除書架" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "上架時間" -#: bookwyrm/templates/shelf/shelf.html:133 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "開始時間" -#: bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:164 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "完成時間" -#: bookwyrm/templates/shelf/shelf.html:190 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "此書架是空的。" +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" @@ -2967,6 +3149,7 @@ msgstr "評論:" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "私密" @@ -3062,6 +3245,7 @@ msgid "Unfollow" msgstr "取消關注" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "接受" @@ -3168,12 +3352,14 @@ msgstr "往後" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "公開" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "不公開" @@ -3182,6 +3368,7 @@ msgid "Followers-only" msgstr "僅關注者" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "發文隱私" @@ -3382,23 +3569,6 @@ msgstr "轉發了" msgid "More options" msgstr "更多選項" -#: bookwyrm/templates/snippets/suggested_users.html:16 -#, python-format -msgid "%(mutuals)s follower you follow" -msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "%(mutuals)s 個你也關注的關注者" - -#: bookwyrm/templates/snippets/suggested_users.html:23 -#, python-format -msgid "%(shared_books)s book on your shelves" -msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "%(shared_books)s 本在你書架上也有的書" - -#: bookwyrm/templates/snippets/suggested_users.html:31 -#: bookwyrm/templates/user/user_preview.html:36 -msgid "Follows you" -msgstr "" - #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" msgstr "切換到此版本" @@ -3448,18 +3618,35 @@ msgstr "你 %(year)s 的書目" msgid "%(username)s's %(year)s Books" msgstr "%(username)s 在 %(year)s 的書目" -#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "使用者使用者資料" -#: bookwyrm/templates/user/layout.html:44 +#: bookwyrm/templates/user/layout.html:45 msgid "Follow Requests" msgstr "關注請求" -#: bookwyrm/templates/user/layout.html:69 +#: bookwyrm/templates/user/layout.html:70 msgid "Reading Goal" msgstr "閱讀目標" +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "" + #: bookwyrm/templates/user/lists.html:11 #, python-format msgid "Lists: %(username)s" @@ -3548,15 +3735,15 @@ msgstr "" msgid "Not a valid csv file" msgstr "不是有效的 csv 檔案" -#: bookwyrm/views/login.py:69 +#: bookwyrm/views/landing/login.py:69 msgid "Username or password are incorrect" msgstr "" -#: bookwyrm/views/password.py:32 +#: bookwyrm/views/landing/password.py:32 msgid "No user with that email address was found." msgstr "沒有找到使用該郵箱的使用者。" -#: bookwyrm/views/password.py:41 +#: bookwyrm/views/landing/password.py:43 #, python-brace-format msgid "A password reset link was sent to {email}" msgstr "密碼重置連結已傳送給 {email}" From 1e8269b6c9847df68ea12e1b5d361ac7898b92f2 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 13:10:52 -0800 Subject: [PATCH 059/121] Refactors import status view --- bookwyrm/models/import_job.py | 2 +- bookwyrm/templates/import/import_status.html | 161 ++++++------------- bookwyrm/views/import_data.py | 36 +++-- 3 files changed, 75 insertions(+), 124 deletions(-) diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 6bca57f8..61b818fe 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -33,7 +33,7 @@ class ImportJob(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) created_date = models.DateTimeField(default=timezone.now) - task_id = models.CharField(max_length=100, null=True) + task_id = models.CharField(max_length=100, null=True) # TODO: deprecated include_reviews = models.BooleanField(default=True) mappings = models.JSONField() complete = models.BooleanField(default=False) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 91e48e34..029b081d 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -6,116 +6,42 @@ {% block title %}{% trans "Import Status" %}{% endblock %} {% block content %}{% spaceless %} -
+

{% trans "Import Status" %}

{% trans "Back to imports" %} - {% if task.failed %} -
{% trans "TASK FAILED" %}
+
+
+
{% trans "Import started:" %}
+
{{ job.created_date | naturaltime }}
+
+
+ + {% if not complete %} +
+
+ + {% trans "In progress" %} + + {% trans "Refresh" %} + +
+
+ {{ percent }}% + {{ percent }}% +
+
{% endif %} - -
-
{% trans "Import started:" %}
-
{{ job.created_date | naturaltime }}
- - {% if job.complete %} -
{% trans "Import completed:" %}
-
{{ task.date_done | naturaltime }}
- {% endif %} -
-
+
- {% if not job.complete %} -

- {% trans "Import still in progress." %} -
- {% trans "(Hit reload to update!)" %} -

- {% endif %} -
- -{% if failed_items %} -
-

{% trans "Failed to load" %}

- {% if not job.retry %} -
- {% csrf_token %} - - {% with failed_count=failed_items|length %} - {% if failed_count > 10 %} -

- - {% blocktrans %}Jump to the bottom of the list to select the {{ failed_count }} items which failed to import.{% endblocktrans %} - -

- {% endif %} - {% endwith %} - -
-
    - {% for item in failed_items %} -
  • - - -
  • - {% endfor %} -
-
- -
- - - - - -
-
- -
- - {% else %} -
    - {% for item in failed_items %} -
  • -

    - Line {{ item.index }}: - {{ item.data.Title }} by - {{ item.data.Author }} -

    -

    - {{ item.fail_reason }}. -

    -
  • - {% endfor %} -
- {% endif %} -
-{% endif %} - -
- {% if job.complete %} -

{% trans "Successfully imported" %}

- {% else %} -

{% trans "Import Progress" %}

- {% endif %} +

+ {% trans "Your Import" %} +

+ {% for item in items %} + {% endfor %}
- {% trans "Book" %} + {% trans "Row" %} {% trans "Title" %} @@ -124,16 +50,16 @@ {% trans "Author" %} + {% trans "Book" %} + + {% trans "Status" %}
- {% if item.book %} - - {% include 'snippets/book_cover.html' with book=item.book cover_class='is-h-s' size='small' %} - - {% endif %} + {{ item.index }} {{ item.data.Title }} @@ -143,15 +69,34 @@ {% if item.book %} - - {% trans "Imported" %} + + {% include 'snippets/book_cover.html' with book=item.book cover_class='is-h-s' size='small' %} + + {% endif %} + + {% if item.book %} + + {% trans "Imported" %} + + {% elif item.fail_reason %} + + + {{ item.fail_reason }} + {% else %} + + {% trans "Pending" %} {% endif %}
+ +
+ {% include 'snippets/pagination.html' with page=items %} +
{% endspaceless %}{% endblock %} {% block scripts %} diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py index 5e113be8..1dc9e6d8 100644 --- a/bookwyrm/views/import_data.py +++ b/bookwyrm/views/import_data.py @@ -1,8 +1,10 @@ """ import books from another app """ from io import TextIOWrapper +import math from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied +from django.core.paginator import Paginator from django.http import HttpResponseBadRequest from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse @@ -17,7 +19,7 @@ from bookwyrm.importers import ( GoodreadsImporter, StorygraphImporter, ) -from bookwyrm.tasks import app +from bookwyrm.settings import PAGE_LENGTH # pylint: disable= no-self-use @method_decorator(login_required, name="dispatch") @@ -82,21 +84,25 @@ class ImportStatus(View): if job.user != request.user: raise PermissionDenied() - try: - task = app.AsyncResult(job.task_id) - # triggers attribute error if the task won't load - task.status # pylint: disable=pointless-statement - except (ValueError, AttributeError): - task = None + items = job.items.order_by("index") + pending_items = items.filter(fail_reason__isnull=True, book__isnull=True) + item_count = items.count() or 1 - items = job.items.order_by("index").all() - failed_items = [i for i in items if i.fail_reason] - items = [i for i in items if not i.fail_reason] - return TemplateResponse( - request, - "import/import_status.html", - {"job": job, "items": items, "failed_items": failed_items, "task": task}, - ) + paginated = Paginator(items, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) + data = { + "job": job, + "items": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), + "complete": not pending_items.exists(), + "percent": math.floor( # pylint: disable=c-extension-no-member + (item_count - pending_items.count()) / item_count * 100 + ), + } + + return TemplateResponse(request, "import/import_status.html", data) def post(self, request, job_id): """retry lines from an import""" From 80c1954aa36398b096114816155b7d6ddbdb6afa Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 13:48:31 -0800 Subject: [PATCH 060/121] Fixes first_search_result behavior --- bookwyrm/book_search.py | 5 ++++- bookwyrm/models/import_job.py | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/bookwyrm/book_search.py b/bookwyrm/book_search.py index 6c89b61f..7b9b1230 100644 --- a/bookwyrm/book_search.py +++ b/bookwyrm/book_search.py @@ -82,6 +82,8 @@ def search_identifiers(query, *filters, return_first=False): *filters, reduce(operator.or_, (Q(**f) for f in or_filters)) ).distinct() if results.count() <= 1: + if return_first: + return results.first() return results # when there are multiple editions of the same work, pick the default. @@ -124,8 +126,9 @@ def search_title_author(query, min_confidence, *filters, return_first=False): result = default else: result = editions.first() + if return_first: - return result + return result[0] list_results.append(result) return list_results diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 61b818fe..9f011f1e 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -6,7 +6,7 @@ from django.db import models from django.utils import timezone from bookwyrm.connectors import connector_manager -from bookwyrm.models import ReadThrough, User, Book +from bookwyrm.models import ReadThrough, User, Book, Edition from .fields import PrivacyLevels @@ -79,6 +79,10 @@ class ImportItem(models.Model): self.isbn, min_confidence=0.999 ) if search_result: + # it's already in the right format + if isinstance(search_result, Edition): + return search_result + # it's just a search result, book needs to be created # raises ConnectorException return search_result.connector.get_or_create_book(search_result.key) return None @@ -183,9 +187,7 @@ class ImportItem(models.Model): def __repr__(self): # pylint: disable=consider-using-f-string - return "<{!r}Item {!r}>".format( - self.normalized_data["import_source"], self.normalized_data["title"] - ) + return "<{!r}Item {!r}>".format(self.index, self.normalized_data["title"]) def __str__(self): # pylint: disable=consider-using-f-string From 2a84c0a370e9adcc09d113bdcc831f270e38d36d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 13:59:54 -0800 Subject: [PATCH 061/121] title author search already working correctly with return first --- bookwyrm/book_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/book_search.py b/bookwyrm/book_search.py index 7b9b1230..e42a6d8c 100644 --- a/bookwyrm/book_search.py +++ b/bookwyrm/book_search.py @@ -128,7 +128,7 @@ def search_title_author(query, min_confidence, *filters, return_first=False): result = editions.first() if return_first: - return result[0] + return result list_results.append(result) return list_results From a65f07e0bff07493a8045a641fad75f2eda31cc0 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 14:36:28 -0800 Subject: [PATCH 062/121] Adds retry page --- bookwyrm/templates/import/import_status.html | 16 ++++++++++ bookwyrm/templates/import/troubleshoot.html | 30 +++++++++++++++++++ bookwyrm/urls.py | 1 + bookwyrm/views/__init__.py | 2 +- bookwyrm/views/import_data.py | 31 ++++++++++++++++++-- 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 bookwyrm/templates/import/troubleshoot.html diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 029b081d..59bebb41 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -32,12 +32,28 @@ {% endif %} + + {% if complete and fail_count %} +
+ {% blocktrans trimmed count counter=fail_count with display_counter=fail_count|intcomma %} + {{ display_counter }} item failed to import. + {% plural %} + {{ display_counter }} items failed to import. + {% endblocktrans %} + + {% trans "View and troubleshoot failed items." %} + +
+ {% endif %}

+ {% block page_title %} {% trans "Your Import" %} + {% endblock %}

+ {% block actions %}{% endblock %} + @@ -82,10 +85,13 @@ {{ item.index }} + + + + + + + +{% endblock %} + +{% block table_row %} + + + + + + + + + + +{% endblock %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 061163a1..1004e30b 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -243,6 +243,11 @@ urlpatterns = [ views.ImportTroubleshoot.as_view(), name="import-troubleshoot", ), + re_path( + r"^import/(\d+)/review/?$", + views.ImportManualReview.as_view(), + name="import-review", + ), # users re_path(rf"{USER_PATH}\.json$", views.User.as_view()), re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 21eeb39b..9fe09795 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -46,7 +46,8 @@ from .shelf.shelf_actions import shelve, unshelve # csv import from .imports.import_data import Import from .imports.import_status import ImportStatus -from .imports.troubelshoot import ImportTroubleshoot +from .imports.troubleshoot import ImportTroubleshoot +from .imports.manually_review import ImportManualReview # misc views from .author import Author, EditAuthor diff --git a/bookwyrm/views/imports/import_data.py b/bookwyrm/views/imports/import_data.py index 64cefc7f..7f6a4d13 100644 --- a/bookwyrm/views/imports/import_data.py +++ b/bookwyrm/views/imports/import_data.py @@ -10,7 +10,11 @@ from django.utils.translation import gettext_lazy as _ from django.views import View from bookwyrm import forms, models -from bookwyrm.importers import LibrarythingImporter, GoodreadsImporter, StorygraphImporter +from bookwyrm.importers import ( + LibrarythingImporter, + GoodreadsImporter, + StorygraphImporter, +) # pylint: disable= no-self-use @method_decorator(login_required, name="dispatch") diff --git a/bookwyrm/views/imports/manually_review.py b/bookwyrm/views/imports/manually_review.py new file mode 100644 index 00000000..286251d4 --- /dev/null +++ b/bookwyrm/views/imports/manually_review.py @@ -0,0 +1,39 @@ +""" verify books we're unsure about """ +from django.contrib.auth.decorators import login_required +from django.core.exceptions import PermissionDenied +from django.core.paginator import Paginator +from django.shortcuts import get_object_or_404 +from django.template.response import TemplateResponse +from django.utils.decorators import method_decorator +from django.views import View + +from bookwyrm import models +from bookwyrm.settings import PAGE_LENGTH + +# pylint: disable= no-self-use +@method_decorator(login_required, name="dispatch") +class ImportManualReview(View): + """problems items in an existing import""" + + def get(self, request, job_id): + """status of an import job""" + job = get_object_or_404(models.ImportJob, id=job_id) + if job.user != request.user: + raise PermissionDenied() + + items = job.items.order_by("index").filter( + book__isnull=True, book_guess__isnull=False + ) + + paginated = Paginator(items, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) + data = { + "job": job, + "items": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), + "complete": True, + } + + return TemplateResponse(request, "import/manual_review.html", data) diff --git a/bookwyrm/views/imports/troubleshoot.py b/bookwyrm/views/imports/troubleshoot.py index 48f2b998..f637b966 100644 --- a/bookwyrm/views/imports/troubleshoot.py +++ b/bookwyrm/views/imports/troubleshoot.py @@ -23,7 +23,7 @@ class ImportTroubleshoot(View): raise PermissionDenied() items = job.items.order_by("index").filter( - fail_reason__isnull=False, book_guess__isnull=False + fail_reason__isnull=False, book_guess__isnull=True ) paginated = Paginator(items, PAGE_LENGTH) From 40fff02eec549fffe17b3073ff3cfe194f9abdd7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 17:10:47 -0800 Subject: [PATCH 075/121] Approve or delete import guesses --- bookwyrm/importers/importer.py | 24 +++++++------- bookwyrm/migrations/0114_importjob_source.py | 19 +++++++++++ bookwyrm/models/import_job.py | 6 ++++ bookwyrm/templates/import/import_status.html | 7 ++-- bookwyrm/templates/import/manual_review.html | 19 +++++++++-- bookwyrm/urls.py | 25 ++++++++++++-- bookwyrm/views/__init__.py | 6 +++- bookwyrm/views/imports/manually_review.py | 34 +++++++++++++++++++- 8 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 bookwyrm/migrations/0114_importjob_source.py diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index b895d69a..6d0f6553 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) class Importer: """Generic class for csv data import from an outside service""" - service = "Unknown" + service = "Import" delimiter = "," encoding = "UTF-8" @@ -50,6 +50,7 @@ class Importer: include_reviews=include_reviews, privacy=privacy, mappings=self.create_row_mappings(csv_reader.fieldnames), + source=self.service, ) for index, entry in rows: @@ -108,16 +109,16 @@ class Importer: @app.task(queue="low_priority") -def start_import_task(source, job_id): +def start_import_task(job_id): """trigger the child tasks for each row""" job = ImportJob.objects.get(id=job_id) # these are sub-tasks so that one big task doesn't use up all the memory in celery for item in job.items.values_list("id", flat=True).all(): - import_item_task.delay(source, item) + import_item_task.delay(item) @app.task(queue="low_priority") -def import_item_task(source, item_id): +def import_item_task(item_id): """resolve a row into a book""" item = models.ImportItem.objects.get(id=item_id) try: @@ -128,17 +129,18 @@ def import_item_task(source, item_id): raise err if item.book: - job = item.job # shelves book and handles reviews - handle_imported_book(source, job.user, item, job.include_reviews, job.privacy) + handle_imported_book(item) else: item.fail_reason = _("Could not find a match for book") item.save() -def handle_imported_book(source, user, item, include_reviews, privacy): +def handle_imported_book(item): """process a csv and then post about it""" + job = item.job + user = job.user if isinstance(item.book, models.Work): item.book = item.book.default_edition if not item.book: @@ -167,7 +169,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): read.user = user read.save() - if include_reviews and (item.rating or item.review): + if job.include_reviews and (item.rating or item.review): # we don't know the publication date of the review, # but "now" is a bad guess published_date_guess = item.date_read or item.date_added @@ -176,7 +178,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): review_title = ( "Review of {!r} on {!r}".format( item.book.title, - source, + job.source, ) if item.review else "" @@ -188,7 +190,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): content=item.review, rating=item.rating, published_date=published_date_guess, - privacy=privacy, + privacy=job.privacy, ) else: # just a rating @@ -197,7 +199,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): book=item.book, rating=item.rating, published_date=published_date_guess, - privacy=privacy, + privacy=job.privacy, ) # only broadcast this review to other bookwyrm instances review.save(software="bookwyrm", priority=LOW) diff --git a/bookwyrm/migrations/0114_importjob_source.py b/bookwyrm/migrations/0114_importjob_source.py new file mode 100644 index 00000000..3ec1432e --- /dev/null +++ b/bookwyrm/migrations/0114_importjob_source.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2021-11-13 00:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0113_auto_20211110_2104"), + ] + + operations = [ + migrations.AddField( + model_name="importjob", + name="source", + field=models.CharField(default="Import", max_length=100), + preserve_default=False, + ), + ] diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index aa86910c..6b8f0b46 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -37,6 +37,7 @@ class ImportJob(models.Model): include_reviews = models.BooleanField(default=True) mappings = models.JSONField() complete = models.BooleanField(default=False) + source = models.CharField(max_length=100) privacy = models.CharField( max_length=255, default="public", choices=PrivacyLevels.choices ) @@ -62,6 +63,11 @@ class ImportItem(models.Model): def resolve(self): """try various ways to lookup a book""" + # we might be calling this after manually adding the book, + # so no need to do searches + if self.book: + return + if self.isbn: self.book = self.get_book_from_isbn() else: diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index b2e21d22..12465bd7 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -40,10 +40,11 @@ {% if manual_review_count %}
{% blocktrans trimmed count counter=manual_review_count with display_counter=manual_review_count|intcomma %} - {{ display_counter }} item needs manual review. + {{ display_counter }} item needs manual approval. {% plural %} - {{ display_counter }} items need manual review. + {{ display_counter }} items need manual approval. {% endblocktrans %} + {% trans "Review items" %}
{% endif %} @@ -55,7 +56,7 @@ {{ display_counter }} items failed to import. {% endblocktrans %} - {% trans "View and troubleshoot failed items." %} + {% trans "View and troubleshoot failed items" %} {% endif %} diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index c9c03869..30274ca0 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -52,9 +52,22 @@
- diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 1004e30b..d6a79c20 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -237,17 +237,36 @@ urlpatterns = [ re_path(r"^search/?$", views.Search.as_view(), name="search"), # imports re_path(r"^import/?$", views.Import.as_view(), name="import"), - re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view(), name="import-status"), re_path( - r"^import/(\d+)/failed/?$", + r"^import/(?P\d+)/?$", + views.ImportStatus.as_view(), + name="import-status", + ), + re_path( + r"^import/(?P\d+)/failed/?$", views.ImportTroubleshoot.as_view(), name="import-troubleshoot", ), re_path( - r"^import/(\d+)/review/?$", + r"^import/(?P\d+)/review/?$", views.ImportManualReview.as_view(), name="import-review", ), + re_path( + r"^import/(?P\d+)/review/?$", + views.ImportManualReview.as_view(), + name="import-review", + ), + re_path( + r"^import/(?P\d+)/review/(?P\d+)/approve/?$", + views.approve_import_item, + name="import-approve", + ), + re_path( + r"^import/(?P\d+)/review/(?P\d+)/delete/?$", + views.delete_import_item, + name="import-delete", + ), # users re_path(rf"{USER_PATH}\.json$", views.User.as_view()), re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 9fe09795..1a6fbdc6 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -47,7 +47,11 @@ from .shelf.shelf_actions import shelve, unshelve from .imports.import_data import Import from .imports.import_status import ImportStatus from .imports.troubleshoot import ImportTroubleshoot -from .imports.manually_review import ImportManualReview +from .imports.manually_review import ( + ImportManualReview, + approve_import_item, + delete_import_item, +) # misc views from .author import Author, EditAuthor diff --git a/bookwyrm/views/imports/manually_review.py b/bookwyrm/views/imports/manually_review.py index 286251d4..877e3ffc 100644 --- a/bookwyrm/views/imports/manually_review.py +++ b/bookwyrm/views/imports/manually_review.py @@ -2,12 +2,14 @@ from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse from django.utils.decorators import method_decorator from django.views import View +from django.views.decorators.http import require_POST from bookwyrm import models +from bookwyrm.importers.importer import import_item_task from bookwyrm.settings import PAGE_LENGTH # pylint: disable= no-self-use @@ -37,3 +39,33 @@ class ImportManualReview(View): } return TemplateResponse(request, "import/manual_review.html", data) + + +@login_required +@require_POST +# pylint: disable=unused-argument +def approve_import_item(request, job_id, item_id): + """we guessed right""" + item = get_object_or_404( + models.ImportItem, id=item_id, job__id=job_id, book_guess__isnull=False + ) + item.fail_reason = None + item.book = item.book_guess + item.save() + + # the good stuff - actually import the data + import_item_task.delay(item.id) + return redirect("import-review", job_id) + + +@login_required +@require_POST +# pylint: disable=unused-argument +def delete_import_item(request, job_id, item_id): + """we guessed right""" + item = get_object_or_404( + models.ImportItem, id=item_id, job__id=job_id, book_guess__isnull=False + ) + item.book_guess = None + item.save() + return redirect("import-review", job_id) From 08f4ad6cd47e1804c95259c19397c297e9857a19 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:02:42 -0800 Subject: [PATCH 076/121] Fixes call to import task --- bookwyrm/importers/importer.py | 4 ++-- bookwyrm/tests/importers/test_importer.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 6d0f6553..4908e166 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -101,9 +101,9 @@ class Importer: self.create_item(job, item.index, item.data) return job - def start_import(self, job): + def start_import(self, job): # pylint: disable=no-self-use """initalizes a csv import job""" - result = start_import_task.delay(self.service, job.id) + result = start_import_task.delay(job.id) job.task_id = result.id job.save() diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index 963eca54..b0863fbe 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -120,7 +120,7 @@ class GenericImporter(TestCase): ) with patch("bookwyrm.importers.importer.import_item_task.delay") as mock: - start_import_task(self.importer.service, import_job.id) + start_import_task(import_job.id) self.assertEqual(mock.call_count, 4) From c245ad09bba4350b92c572bc8fc08eca0faa0a0e Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:07:50 -0800 Subject: [PATCH 077/121] Make sure book is in the right format --- bookwyrm/importers/importer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 4908e166..71f02231 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -145,6 +145,8 @@ def handle_imported_book(item): item.book = item.book.default_edition if not item.book: return + if not isinstance(item.book, models.Edition): + item.book = item.book.edition existing_shelf = models.ShelfBook.objects.filter(book=item.book, user=user).exists() From acc32d579e24c70ac1a46394d8c9bc2d8b11ce38 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:22:35 -0800 Subject: [PATCH 078/121] Preview review in import preview --- bookwyrm/templates/import/import_status.html | 7 ++++ bookwyrm/templates/import/manual_review.html | 39 ++++++++++++-------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 12465bd7..0a3c9b47 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -84,6 +84,9 @@ + @@ -107,6 +110,10 @@ + + @@ -52,22 +55,28 @@ - + From 60fb1ac2e6fd09da622de64cbecbc87126455408 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:34:10 -0800 Subject: [PATCH 079/121] More flexible templates --- bookwyrm/templates/import/import_status.html | 17 ++++++-- bookwyrm/templates/import/manual_review.html | 44 ++++---------------- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 0a3c9b47..6c5560dc 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -70,7 +70,6 @@ {% block actions %}{% endblock %}
diff --git a/bookwyrm/templates/import/troubleshoot.html b/bookwyrm/templates/import/troubleshoot.html new file mode 100644 index 00000000..9b9b4827 --- /dev/null +++ b/bookwyrm/templates/import/troubleshoot.html @@ -0,0 +1,30 @@ +{% extends 'import/import_status.html' %} +{% load i18n %} + +{% block title %}{% trans "Import Troubleshooting" %}{% endblock %} + +{% block page_title %} +{% trans "Failed items" %} +{% endblock %} + +{% block actions %} +
+
+

+ {% trans "Re-trying an import can fix missing items in cases such as:" %} +

    +
  • {% trans "The book has been added to the instance since this import" %}
  • +
  • {% trans "A transient error or timeout caused the external data source to be unavailable." %}
  • +
  • {% trans "BookWyrm has been updated since this import with a bug fix" %}
  • +
+

+

+ {% trans "Contact your admin or open an issue if you are seeing unexpected failed items." %} +

+
+
+ {% csrf_token %} + +
+
+{% endblock %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 839d783f..350d6018 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -238,6 +238,7 @@ urlpatterns = [ # imports re_path(r"^import/?$", views.Import.as_view(), name="import"), re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view(), name="import-status"), + re_path(r"^import/(\d+)/failed/?$", views.ImportTroubleshoot.as_view(), name="import-troubleshoot"), # users re_path(rf"{USER_PATH}\.json$", views.User.as_view()), re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index e1dd8355..645b1307 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -62,7 +62,7 @@ from .group import ( accept_membership, reject_membership, ) -from .import_data import Import, ImportStatus +from .import_data import Import, ImportStatus, ImportTroubleshoot from .inbox import Inbox from .interaction import Favorite, Unfavorite, Boost, Unboost from .isbn import Isbn diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py index 1dc9e6d8..e0e90c2d 100644 --- a/bookwyrm/views/import_data.py +++ b/bookwyrm/views/import_data.py @@ -93,6 +93,7 @@ class ImportStatus(View): data = { "job": job, "items": page, + "fail_count": items.filter(fail_reason__isnull=False).count(), "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), @@ -104,12 +105,36 @@ class ImportStatus(View): return TemplateResponse(request, "import/import_status.html", data) + +@method_decorator(login_required, name="dispatch") +class ImportTroubleshoot(View): + """problems items in an existing import""" + + def get(self, request, job_id): + """status of an import job""" + job = get_object_or_404(models.ImportJob, id=job_id) + if job.user != request.user: + raise PermissionDenied() + + items = job.items.order_by("index").filter(fail_reason__isnull=False) + + paginated = Paginator(items, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) + data = { + "job": job, + "items": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), + "complete": True, + } + + return TemplateResponse(request, "import/troubleshoot.html", data) + def post(self, request, job_id): """retry lines from an import""" job = get_object_or_404(models.ImportJob, id=job_id) - items = [] - for item in request.POST.getlist("import_item"): - items.append(get_object_or_404(models.ImportItem, id=item)) + items = job.items.filter(fail_reason__isnull=False) importer = Importer() job = importer.create_retry_job( From d8197cdcfab9bea1243d5a4d57bfad46c05dab01 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 14:38:41 -0800 Subject: [PATCH 063/121] Indicate retry on status page --- bookwyrm/templates/import/import_status.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 59bebb41..ac8fead0 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -7,7 +7,11 @@ {% block content %}{% spaceless %}
+ {% if job.retry %} +

{% trans "Retry Status" %}

+ {% else %}

{% trans "Import Status" %}

+ {% endif %} {% trans "Back to imports" %}
@@ -33,7 +37,7 @@
{% endif %} - {% if complete and fail_count %} + {% if complete and fail_count and not job.retry %}
{% blocktrans trimmed count counter=fail_count with display_counter=fail_count|intcomma %} {{ display_counter }} item failed to import. From 60c777ed495c4d37ea615c2c1b258926f1335bcd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 14:41:25 -0800 Subject: [PATCH 064/121] Updates tests --- bookwyrm/tests/views/test_import.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bookwyrm/tests/views/test_import.py b/bookwyrm/tests/views/test_import.py index 54f11f03..4a6a8237 100644 --- a/bookwyrm/tests/views/test_import.py +++ b/bookwyrm/tests/views/test_import.py @@ -51,6 +51,19 @@ class ImportViews(TestCase): validate_html(result.render()) self.assertEqual(result.status_code, 200) + def test_import_troubleshoot_get(self): + """there are so many views, this just makes sure it LOADS""" + view = views.ImportTroubleshoot.as_view() + import_job = models.ImportJob.objects.create(user=self.local_user, mappings={}) + request = self.factory.get("") + request.user = self.local_user + with patch("bookwyrm.tasks.app.AsyncResult") as async_result: + async_result.return_value = [] + result = view(request, import_job.id) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + def test_start_import(self): """retry failed items""" view = views.Import.as_view() @@ -77,7 +90,7 @@ class ImportViews(TestCase): def test_retry_import(self): """retry failed items""" - view = views.ImportStatus.as_view() + view = views.ImportTroubleshoot.as_view() import_job = models.ImportJob.objects.create( user=self.local_user, privacy="unlisted", mappings={} ) From e09c02017c7efb041c410668b042e52a88e26e2e Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 14:46:39 -0800 Subject: [PATCH 065/121] Fixes title/author search handling --- bookwyrm/models/import_job.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 9f011f1e..aa86910c 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -94,6 +94,8 @@ class ImportItem(models.Model): search_term, min_confidence=0.1 ) if search_result: + if isinstance(search_result, Edition): + return (search_result, 1) # raises ConnectorException return ( search_result.connector.get_or_create_book(search_result.key), From 61eaf513105f7e413a81312d30c667e00f976b44 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 14:49:24 -0800 Subject: [PATCH 066/121] Fixes html validity --- bookwyrm/templates/import/troubleshoot.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bookwyrm/templates/import/troubleshoot.html b/bookwyrm/templates/import/troubleshoot.html index 9b9b4827..0be68383 100644 --- a/bookwyrm/templates/import/troubleshoot.html +++ b/bookwyrm/templates/import/troubleshoot.html @@ -12,12 +12,12 @@

{% trans "Re-trying an import can fix missing items in cases such as:" %} -

    -
  • {% trans "The book has been added to the instance since this import" %}
  • -
  • {% trans "A transient error or timeout caused the external data source to be unavailable." %}
  • -
  • {% trans "BookWyrm has been updated since this import with a bug fix" %}
  • -

+
    +
  • {% trans "The book has been added to the instance since this import" %}
  • +
  • {% trans "A transient error or timeout caused the external data source to be unavailable." %}
  • +
  • {% trans "BookWyrm has been updated since this import with a bug fix" %}
  • +

{% trans "Contact your admin or open an issue if you are seeing unexpected failed items." %}

From 6161f60695dced0368c92482369df27f1b359281 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 14:54:20 -0800 Subject: [PATCH 067/121] Use normalized data in table --- bookwyrm/templates/import/import_status.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index ac8fead0..5674aace 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -66,6 +66,9 @@
{% trans "Title" %} + {% trans "ISBN" %} + {% trans "Author" %} - {{ item.data.Title }} + {{ item.normalized_data.title }} - {{ item.data.Author }} + {{ item.isbn }} + + {{ item.normalized_data.authors }} {% if item.book %} From 1d0f6d5243bca717e489dda97e20e921d89f9126 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 15:06:23 -0800 Subject: [PATCH 068/121] Python formatting --- bookwyrm/urls.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 350d6018..061163a1 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -238,7 +238,11 @@ urlpatterns = [ # imports re_path(r"^import/?$", views.Import.as_view(), name="import"), re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view(), name="import-status"), - re_path(r"^import/(\d+)/failed/?$", views.ImportTroubleshoot.as_view(), name="import-troubleshoot"), + re_path( + r"^import/(\d+)/failed/?$", + views.ImportTroubleshoot.as_view(), + name="import-troubleshoot", + ), # users re_path(rf"{USER_PATH}\.json$", views.User.as_view()), re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), From b784dcdb46b70cf4f9e4567f27eae3dadea0aab7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 15:07:26 -0800 Subject: [PATCH 069/121] Removes uninformative test --- .../tests/importers/test_storygraph_import.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py index 8002a3e1..fdad5c71 100644 --- a/bookwyrm/tests/importers/test_storygraph_import.py +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -1,5 +1,4 @@ """ testing import """ -import csv import pathlib from unittest.mock import patch import datetime @@ -59,30 +58,6 @@ class StorygraphImport(TestCase): ) self.assertEqual(import_items[1].normalized_data["rating"], "5.0") - def test_create_retry_job(self, *_): - """trying again with items that didn't import""" - import_job = self.importer.create_job( - self.local_user, self.csv, False, "unlisted" - ) - import_items = models.ImportItem.objects.filter(job=import_job).all()[:2] - - retry = self.importer.create_retry_job( - self.local_user, import_job, import_items - ) - self.assertNotEqual(import_job, retry) - self.assertEqual(retry.user, self.local_user) - self.assertEqual(retry.include_reviews, False) - self.assertEqual(retry.privacy, "unlisted") - - retry_items = models.ImportItem.objects.filter(job=retry).all() - self.assertEqual(len(retry_items), 2) - self.assertEqual(retry_items[0].index, 0) - self.assertEqual(retry_items[0].normalized_data["title"], "Always Coming Home") - self.assertEqual(retry_items[1].index, 1) - self.assertEqual( - retry_items[1].normalized_data["title"], "Subprime Attention Crisis" - ) - def test_handle_imported_book(self, *_): """storygraph import added a book, this adds related connections""" shelf = self.local_user.shelf_set.filter(identifier="to-read").first() From 6a5a5983452769972c8a7a915060f7b9f67886c0 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 15:17:01 -0800 Subject: [PATCH 070/121] Raise errors when import items fail This should make is way easier to debug --- bookwyrm/importers/importer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index b32e2df7..b895d69a 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -123,10 +123,9 @@ def import_item_task(source, item_id): try: item.resolve() except Exception as err: # pylint: disable=broad-except - logger.exception(err) item.fail_reason = _("Error loading book") item.save() - return + raise err if item.book: job = item.job From 5558ed810ead73e34fb41d9ae76a7d1016465c84 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 15:34:48 -0800 Subject: [PATCH 071/121] Show manual review flag --- bookwyrm/templates/import/import_status.html | 10 ++++++++++ bookwyrm/views/import_data.py | 11 +++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 5674aace..01a1fec6 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -37,6 +37,16 @@ {% endif %} + {% if manual_review_count %} +
+ {% blocktrans trimmed count counter=manual_review_count with display_counter=manual_review_count|intcomma %} + {{ display_counter }} item needs manual review. + {% plural %} + {{ display_counter }} items need manual review. + {% endblocktrans %} +
+ {% endif %} + {% if complete and fail_count and not job.retry %}
{% blocktrans trimmed count counter=fail_count with display_counter=fail_count|intcomma %} diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py index e0e90c2d..907c77e2 100644 --- a/bookwyrm/views/import_data.py +++ b/bookwyrm/views/import_data.py @@ -93,7 +93,12 @@ class ImportStatus(View): data = { "job": job, "items": page, - "fail_count": items.filter(fail_reason__isnull=False).count(), + "manual_review_count": items.filter( + fail_reason__isnull=False, book_guess__isnull=False, book__isnull=True + ).count(), + "fail_count": items.filter( + fail_reason__isnull=False, book_guess__isnull=True + ).count(), "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), @@ -116,7 +121,9 @@ class ImportTroubleshoot(View): if job.user != request.user: raise PermissionDenied() - items = job.items.order_by("index").filter(fail_reason__isnull=False) + items = job.items.order_by("index").filter( + fail_reason__isnull=False, book_guess__isnull=False + ) paginated = Paginator(items, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) From be26e8363a7d9d5dada60b3fbda1bbaaf16f677f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 15:43:15 -0800 Subject: [PATCH 072/121] Create import directory --- bookwyrm/views/__init__.py | 4 +++- bookwyrm/views/imports/__init__.py | 0 bookwyrm/views/{ => imports}/import_data.py | 0 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 bookwyrm/views/imports/__init__.py rename bookwyrm/views/{ => imports}/import_data.py (100%) diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 645b1307..d169d126 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -43,6 +43,9 @@ from .shelf.shelf import Shelf from .shelf.shelf_actions import create_shelf, delete_shelf from .shelf.shelf_actions import shelve, unshelve +# csv import +from .imports.import_data import Import, ImportStatus, ImportTroubleshoot + # misc views from .author import Author, EditAuthor from .directory import Directory @@ -62,7 +65,6 @@ from .group import ( accept_membership, reject_membership, ) -from .import_data import Import, ImportStatus, ImportTroubleshoot from .inbox import Inbox from .interaction import Favorite, Unfavorite, Boost, Unboost from .isbn import Isbn diff --git a/bookwyrm/views/imports/__init__.py b/bookwyrm/views/imports/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/imports/import_data.py similarity index 100% rename from bookwyrm/views/import_data.py rename to bookwyrm/views/imports/import_data.py From 9bff27e61f2106ad9709e7907b0680fd4fdbe9e0 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 15:50:33 -0800 Subject: [PATCH 073/121] Separate import classes into files --- bookwyrm/views/__init__.py | 4 +- bookwyrm/views/imports/import_data.py | 92 +------------------------ bookwyrm/views/imports/import_status.py | 50 ++++++++++++++ bookwyrm/views/imports/troubleshoot.py | 54 +++++++++++++++ 4 files changed, 109 insertions(+), 91 deletions(-) create mode 100644 bookwyrm/views/imports/import_status.py create mode 100644 bookwyrm/views/imports/troubleshoot.py diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index d169d126..21eeb39b 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -44,7 +44,9 @@ from .shelf.shelf_actions import create_shelf, delete_shelf from .shelf.shelf_actions import shelve, unshelve # csv import -from .imports.import_data import Import, ImportStatus, ImportTroubleshoot +from .imports.import_data import Import +from .imports.import_status import ImportStatus +from .imports.troubelshoot import ImportTroubleshoot # misc views from .author import Author, EditAuthor diff --git a/bookwyrm/views/imports/import_data.py b/bookwyrm/views/imports/import_data.py index 907c77e2..64cefc7f 100644 --- a/bookwyrm/views/imports/import_data.py +++ b/bookwyrm/views/imports/import_data.py @@ -1,25 +1,16 @@ """ import books from another app """ from io import TextIOWrapper -import math from django.contrib.auth.decorators import login_required -from django.core.exceptions import PermissionDenied -from django.core.paginator import Paginator from django.http import HttpResponseBadRequest -from django.shortcuts import get_object_or_404, redirect +from django.shortcuts import redirect from django.template.response import TemplateResponse from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views import View from bookwyrm import forms, models -from bookwyrm.importers import ( - Importer, - LibrarythingImporter, - GoodreadsImporter, - StorygraphImporter, -) -from bookwyrm.settings import PAGE_LENGTH +from bookwyrm.importers import LibrarythingImporter, GoodreadsImporter, StorygraphImporter # pylint: disable= no-self-use @method_decorator(login_required, name="dispatch") @@ -72,82 +63,3 @@ class Import(View): return redirect(f"/import/{job.id}") return HttpResponseBadRequest() - - -@method_decorator(login_required, name="dispatch") -class ImportStatus(View): - """status of an existing import""" - - def get(self, request, job_id): - """status of an import job""" - job = get_object_or_404(models.ImportJob, id=job_id) - if job.user != request.user: - raise PermissionDenied() - - items = job.items.order_by("index") - pending_items = items.filter(fail_reason__isnull=True, book__isnull=True) - item_count = items.count() or 1 - - paginated = Paginator(items, PAGE_LENGTH) - page = paginated.get_page(request.GET.get("page")) - data = { - "job": job, - "items": page, - "manual_review_count": items.filter( - fail_reason__isnull=False, book_guess__isnull=False, book__isnull=True - ).count(), - "fail_count": items.filter( - fail_reason__isnull=False, book_guess__isnull=True - ).count(), - "page_range": paginated.get_elided_page_range( - page.number, on_each_side=2, on_ends=1 - ), - "complete": not pending_items.exists(), - "percent": math.floor( # pylint: disable=c-extension-no-member - (item_count - pending_items.count()) / item_count * 100 - ), - } - - return TemplateResponse(request, "import/import_status.html", data) - - -@method_decorator(login_required, name="dispatch") -class ImportTroubleshoot(View): - """problems items in an existing import""" - - def get(self, request, job_id): - """status of an import job""" - job = get_object_or_404(models.ImportJob, id=job_id) - if job.user != request.user: - raise PermissionDenied() - - items = job.items.order_by("index").filter( - fail_reason__isnull=False, book_guess__isnull=False - ) - - paginated = Paginator(items, PAGE_LENGTH) - page = paginated.get_page(request.GET.get("page")) - data = { - "job": job, - "items": page, - "page_range": paginated.get_elided_page_range( - page.number, on_each_side=2, on_ends=1 - ), - "complete": True, - } - - return TemplateResponse(request, "import/troubleshoot.html", data) - - def post(self, request, job_id): - """retry lines from an import""" - job = get_object_or_404(models.ImportJob, id=job_id) - items = job.items.filter(fail_reason__isnull=False) - - importer = Importer() - job = importer.create_retry_job( - request.user, - job, - items, - ) - importer.start_import(job) - return redirect(f"/import/{job.id}") diff --git a/bookwyrm/views/imports/import_status.py b/bookwyrm/views/imports/import_status.py new file mode 100644 index 00000000..2d18d656 --- /dev/null +++ b/bookwyrm/views/imports/import_status.py @@ -0,0 +1,50 @@ +""" import books from another app """ +import math + +from django.contrib.auth.decorators import login_required +from django.core.exceptions import PermissionDenied +from django.core.paginator import Paginator +from django.shortcuts import get_object_or_404 +from django.template.response import TemplateResponse +from django.utils.decorators import method_decorator +from django.views import View + +from bookwyrm import models +from bookwyrm.settings import PAGE_LENGTH + +# pylint: disable= no-self-use +@method_decorator(login_required, name="dispatch") +class ImportStatus(View): + """status of an existing import""" + + def get(self, request, job_id): + """status of an import job""" + job = get_object_or_404(models.ImportJob, id=job_id) + if job.user != request.user: + raise PermissionDenied() + + items = job.items.order_by("index") + pending_items = items.filter(fail_reason__isnull=True, book__isnull=True) + item_count = items.count() or 1 + + paginated = Paginator(items, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) + data = { + "job": job, + "items": page, + "manual_review_count": items.filter( + fail_reason__isnull=False, book_guess__isnull=False, book__isnull=True + ).count(), + "fail_count": items.filter( + fail_reason__isnull=False, book_guess__isnull=True + ).count(), + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), + "complete": not pending_items.exists(), + "percent": math.floor( # pylint: disable=c-extension-no-member + (item_count - pending_items.count()) / item_count * 100 + ), + } + + return TemplateResponse(request, "import/import_status.html", data) diff --git a/bookwyrm/views/imports/troubleshoot.py b/bookwyrm/views/imports/troubleshoot.py new file mode 100644 index 00000000..48f2b998 --- /dev/null +++ b/bookwyrm/views/imports/troubleshoot.py @@ -0,0 +1,54 @@ +""" import books from another app """ +from django.contrib.auth.decorators import login_required +from django.core.exceptions import PermissionDenied +from django.core.paginator import Paginator +from django.shortcuts import get_object_or_404, redirect +from django.template.response import TemplateResponse +from django.utils.decorators import method_decorator +from django.views import View + +from bookwyrm import models +from bookwyrm.importers import Importer +from bookwyrm.settings import PAGE_LENGTH + +# pylint: disable= no-self-use +@method_decorator(login_required, name="dispatch") +class ImportTroubleshoot(View): + """problems items in an existing import""" + + def get(self, request, job_id): + """status of an import job""" + job = get_object_or_404(models.ImportJob, id=job_id) + if job.user != request.user: + raise PermissionDenied() + + items = job.items.order_by("index").filter( + fail_reason__isnull=False, book_guess__isnull=False + ) + + paginated = Paginator(items, PAGE_LENGTH) + page = paginated.get_page(request.GET.get("page")) + data = { + "job": job, + "items": page, + "page_range": paginated.get_elided_page_range( + page.number, on_each_side=2, on_ends=1 + ), + "complete": True, + } + + return TemplateResponse(request, "import/troubleshoot.html", data) + + def post(self, request, job_id): + """retry lines from an import""" + job = get_object_or_404(models.ImportJob, id=job_id) + items = job.items.filter(fail_reason__isnull=False) + + importer = Importer() + job = importer.create_retry_job( + request.user, + job, + items, + ) + importer.start_import(job) + return redirect(f"/import/{job.id}") From 221cde9be4a8e33d617326c508f52b309b545109 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Nov 2021 16:23:56 -0800 Subject: [PATCH 074/121] Adds manual review view --- bookwyrm/templates/import/import_status.html | 4 ++ bookwyrm/templates/import/manual_review.html | 76 ++++++++++++++++++++ bookwyrm/urls.py | 5 ++ bookwyrm/views/__init__.py | 3 +- bookwyrm/views/imports/import_data.py | 6 +- bookwyrm/views/imports/manually_review.py | 39 ++++++++++ bookwyrm/views/imports/troubleshoot.py | 2 +- 7 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 bookwyrm/templates/import/manual_review.html create mode 100644 bookwyrm/views/imports/manually_review.py diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 01a1fec6..b2e21d22 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -69,6 +69,7 @@ {% block actions %}{% endblock %} + {% block table_headers %} + {% endblock %} {% for item in items %} + {% block table_row %} + {% endblock %} {% endfor %}
{% trans "Row" %} @@ -89,7 +90,9 @@ {% trans "Status" %}
{{ item.index }} @@ -126,6 +129,7 @@ {% endif %}
diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html new file mode 100644 index 00000000..c9c03869 --- /dev/null +++ b/bookwyrm/templates/import/manual_review.html @@ -0,0 +1,76 @@ +{% extends 'import/import_status.html' %} +{% load i18n %} +{% load utilities %} + +{% block title %}{% trans "Import Troubleshooting" %}{% endblock %} + +{% block page_title %} +{% trans "Review items" %} +{% endblock %} + +{% block actions %} +
+
+

+ {% trans "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." %} +

+
+
+{% endblock %} + +{% block table_headers %} +
+ {% trans "Row" %} + + {% trans "Title" %} + + {% trans "ISBN" %} + + {% trans "Author" %} + + {% trans "Actions" %} +
+ {{ item.index }} + + {{ item.normalized_data.title }} + + {{ item.isbn }} + + {{ item.normalized_data.authors }} + + + +
+
+ {% with guess=item.book_guess %} + +
+ {% include 'snippets/book_titleby.html' with book=guess %} +
+ {% endwith %} +
+
{{ item.normalized_data.authors }} - - + +
+ {% csrf_token %} + +
+ +
+ {% csrf_token %} + +
{% trans "Author" %} + {% trans "Review" %} + {% trans "Book" %} {{ item.normalized_data.authors }} +

{% include 'snippets/stars.html' with rating=item.rating %}

+

{{ item.review|truncatechars:100 }}

+
{% if item.book %} diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index 30274ca0..b7c8e3b9 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -32,6 +32,9 @@ {% trans "Author" %} + {% trans "Review" %} + {% trans "Actions" %} {{ item.normalized_data.authors }} -
- {% csrf_token %} - -
+
+

{% include 'snippets/stars.html' with rating=item.rating %}

+

{{ item.review|truncatechars:100 }}

+
+
+
+ {% csrf_token %} + +
-
- {% csrf_token %} - -
+
+ {% csrf_token %} + +
+
- {% block table_headers %} + + {% block import_cols_headers %} + {% endblock %} - {% endblock %} {% for item in items %} - {% block table_row %} + {% block index_col %} + {% endblock %} @@ -110,10 +114,14 @@ + + {% block import_cols %} + {% endblock %} - {% endblock %} + {% block action_row %}{% endblock %} {% endfor %}
{% trans "Row" %} @@ -84,23 +83,28 @@ {% trans "Author" %} + {% trans "Shelf" %} + {% trans "Review" %} {% trans "Book" %} {% trans "Status" %}
{{ item.index }} {{ item.normalized_data.title }} {{ item.normalized_data.authors }} + {{ item.normalized_data.shelf }} +

{% include 'snippets/stars.html' with rating=item.rating %}

{{ item.review|truncatechars:100 }}

{% if item.book %} @@ -136,8 +144,9 @@ {% trans "Pending" %} {% endif %}
diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index b7c8e3b9..833e9d7f 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -18,47 +18,19 @@ {% endblock %} -{% block table_headers %} - - - {% trans "Row" %} - - - {% trans "Title" %} - - - {% trans "ISBN" %} - - - {% trans "Author" %} - - - {% trans "Review" %} - +{% block import_cols_headers %} {% trans "Actions" %} - {% endblock %} -{% block table_row %} - +{% block index_col %} {{ item.index }} - - {{ item.normalized_data.title }} - - - {{ item.isbn }} - - - {{ item.normalized_data.authors }} - - -

{% include 'snippets/stars.html' with rating=item.rating %}

-

{{ item.review|truncatechars:100 }}

- +{% endblock %} + +{% block import_cols %}
@@ -78,9 +50,11 @@
- +{% endblock %} + +{% block action_row %} - +
{% with guess=item.book_guess %}
From 659d0f19ebdcbd4a5965abde30cefae5aab0d8b2 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:43:52 -0800 Subject: [PATCH 080/121] Improves import table ui adds table container, plays with display a bit --- bookwyrm/templates/import/import_status.html | 164 ++++++++++--------- bookwyrm/templates/import/manual_review.html | 41 ++--- 2 files changed, 103 insertions(+), 102 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 6c5560dc..5b33f394 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -69,86 +69,92 @@ {% endblock %} {% block actions %}{% endblock %} - - - - - - - - - {% block import_cols_headers %} - - - {% endblock %} - - {% for item in items %} - - {% block index_col %} - - {% endblock %} - - - - - - {% block import_cols %} - - + {% endblock %} + + {% block action_row %}{% endblock %} + {% endfor %} +
- {% trans "Row" %} - - {% trans "Title" %} - - {% trans "ISBN" %} - - {% trans "Author" %} - - {% trans "Shelf" %} - - {% trans "Review" %} - - {% trans "Book" %} - - {% trans "Status" %} -
- {{ item.index }} - - {{ item.normalized_data.title }} - - {{ item.isbn }} - - {{ item.normalized_data.authors }} - - {{ item.normalized_data.shelf }} - -

{% include 'snippets/stars.html' with rating=item.rating %}

-

{{ item.review|truncatechars:100 }}

-
- {% if item.book %} - - {% include 'snippets/book_cover.html' with book=item.book cover_class='is-h-s' size='small' %} - - {% endif %} - - {% if item.book %} - - {% trans "Imported" %} +
+ + + + + + + + + {% block import_cols_headers %} + + + {% endblock %} + + {% for item in items %} + + {% block index_col %} + + {% endblock %} + + + + + + {% block import_cols %} + + - {% endblock %} - - {% block action_row %}{% endblock %} - {% endfor %} -
+ {% trans "Row" %} + + {% trans "Title" %} + + {% trans "ISBN" %} + + {% trans "Author" %} + + {% trans "Shelf" %} + + {% trans "Review" %} + + {% trans "Book" %} + + {% trans "Status" %} +
+ {{ item.index }} + + {{ item.normalized_data.title }} + + {{ item.isbn }} + + {{ item.normalized_data.authors }} + + {{ item.normalized_data.shelf }} + +

{% include 'snippets/stars.html' with rating=item.rating %}

+

{{ item.review|truncatechars:100 }}

+
+ {% if item.book %} + + {% include 'snippets/book_cover.html' with book=item.book cover_class='is-h-s' size='small' %} + + {% endif %} + + {% if item.book %} + + {% trans "Imported" %} - {% elif item.fail_reason %} - - - {{ item.fail_reason }} - - {% else %} - - {% trans "Pending" %} - {% endif %} -
+ {% elif item.fail_reason %} + + + {% if item.book_guess %} + {% trans "Needs manual review" %} + {% else %} + {{ item.fail_reason }} + {% endif %} + + {% else %} + + {% trans "Pending" %} + {% endif %} +
+
diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index 833e9d7f..4661520b 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -19,9 +19,6 @@ {% endblock %} {% block import_cols_headers %} - - {% trans "Actions" %} - {% endblock %} {% block index_col %} @@ -31,30 +28,11 @@ {% endblock %} {% block import_cols %} - -
-
- {% csrf_token %} - -
- -
- {% csrf_token %} - -
-
- {% endblock %} {% block action_row %} - +
{% with guess=item.book_guess %}
@@ -64,6 +42,23 @@
{% include 'snippets/book_titleby.html' with book=guess %} +
+
+ {% csrf_token %} + +
+ +
+ {% csrf_token %} + +
+
{% endwith %}
From d3f23b4a0a5700b22107b13e800966906978b006 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:50:56 -0800 Subject: [PATCH 081/121] Updates calls in tests --- .../tests/importers/test_goodreads_import.py | 14 +++++--------- .../tests/importers/test_librarything_import.py | 16 +++++----------- .../tests/importers/test_storygraph_import.py | 9 +++------ 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index 12b5578b..44605700 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -92,9 +92,7 @@ class GoodreadsImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -116,9 +114,8 @@ class GoodreadsImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, True, "unlisted" - ) + handle_imported_book(import_item) + review = models.Review.objects.get(book=self.book, user=self.local_user) self.assertEqual(review.content, "mixed feelings") self.assertEqual(review.rating, 2) @@ -136,9 +133,8 @@ class GoodreadsImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, True, "unlisted" - ) + handle_imported_book(import_item) + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) self.assertIsInstance(review, models.ReviewRating) self.assertEqual(review.rating, 3) diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index 1ec94bbb..5745544e 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -5,11 +5,10 @@ import datetime import pytz from django.test import TestCase -import responses from bookwyrm import models from bookwyrm.importers import LibrarythingImporter -from bookwyrm.importers.importer import start_import_task, handle_imported_book +from bookwyrm.importers.importer import handle_imported_book def make_date(*args): @@ -97,9 +96,7 @@ class LibrarythingImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -125,9 +122,7 @@ class LibrarythingImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -149,9 +144,8 @@ class LibrarythingImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, True, "unlisted" - ) + handle_imported_book(import_item) + review = models.Review.objects.get(book=self.book, user=self.local_user) self.assertEqual(review.content, "chef d'oeuvre") self.assertEqual(review.rating, 4.5) diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py index fdad5c71..d11a8d88 100644 --- a/bookwyrm/tests/importers/test_storygraph_import.py +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -71,9 +71,7 @@ class StorygraphImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -92,9 +90,8 @@ class StorygraphImport(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, True, "unlisted" - ) + handle_imported_book(import_item) + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) self.assertIsInstance(review, models.ReviewRating) self.assertEqual(review.rating, 5.0) From 3bdda973bc620cd1c2f15125830ccb6acb1643c4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 09:52:09 -0800 Subject: [PATCH 082/121] Creates subdirectory for import views tests --- bookwyrm/tests/views/imports/__init__.py | 1 + bookwyrm/tests/views/{ => imports}/test_import.py | 0 2 files changed, 1 insertion(+) create mode 100644 bookwyrm/tests/views/imports/__init__.py rename bookwyrm/tests/views/{ => imports}/test_import.py (100%) diff --git a/bookwyrm/tests/views/imports/__init__.py b/bookwyrm/tests/views/imports/__init__.py new file mode 100644 index 00000000..b6e690fd --- /dev/null +++ b/bookwyrm/tests/views/imports/__init__.py @@ -0,0 +1 @@ +from . import * diff --git a/bookwyrm/tests/views/test_import.py b/bookwyrm/tests/views/imports/test_import.py similarity index 100% rename from bookwyrm/tests/views/test_import.py rename to bookwyrm/tests/views/imports/test_import.py From 232e051dcb51976086204b63da0237ce4675ff9a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 10:16:05 -0800 Subject: [PATCH 083/121] Fixes import job creates in tests --- bookwyrm/tests/importers/test_goodreads_import.py | 6 ++++-- bookwyrm/tests/importers/test_librarything_import.py | 4 +++- bookwyrm/tests/importers/test_storygraph_import.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index 44605700..b1600659 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -108,7 +108,9 @@ class GoodreadsImport(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """goodreads review import""" - import_job = self.importer.create_job(self.local_user, self.csv, True, "public") + import_job = self.importer.create_job( + self.local_user, self.csv, True, "unlisted" + ) import_item = import_job.items.get(index=2) import_item.book = self.book import_item.save() @@ -126,7 +128,7 @@ class GoodreadsImport(TestCase): def test_handle_imported_book_rating(self, *_): """goodreads rating import""" import_job = self.importer.create_job( - self.local_user, self.csv, False, "public" + self.local_user, self.csv, True, "unlisted" ) import_item = import_job.items.filter(index=0).first() import_item.book = self.book diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index 5745544e..804118ef 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -138,7 +138,9 @@ class LibrarythingImport(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """librarything review import""" - import_job = self.importer.create_job(self.local_user, self.csv, True, "public") + import_job = self.importer.create_job( + self.local_user, self.csv, True, "unlisted" + ) import_item = import_job.items.filter(index=0).first() import_item.book = self.book import_item.save() diff --git a/bookwyrm/tests/importers/test_storygraph_import.py b/bookwyrm/tests/importers/test_storygraph_import.py index d11a8d88..09cf32dc 100644 --- a/bookwyrm/tests/importers/test_storygraph_import.py +++ b/bookwyrm/tests/importers/test_storygraph_import.py @@ -83,7 +83,7 @@ class StorygraphImport(TestCase): def test_handle_imported_book_rating(self, *_): """storygraph rating import""" import_job = self.importer.create_job( - self.local_user, self.csv, False, "public" + self.local_user, self.csv, True, "unlisted" ) import_item = import_job.items.filter(index=1).first() import_item.book = self.book From 628f104b13e52a571eb2b88d095f42b9c778fb54 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 10:16:25 -0800 Subject: [PATCH 084/121] Separates out imports views tests --- bookwyrm/tests/views/imports/test_import.py | 32 ---------- .../views/imports/test_import_troubleshoot.py | 59 +++++++++++++++++++ 2 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 bookwyrm/tests/views/imports/test_import_troubleshoot.py diff --git a/bookwyrm/tests/views/imports/test_import.py b/bookwyrm/tests/views/imports/test_import.py index 4a6a8237..7de5e8c8 100644 --- a/bookwyrm/tests/views/imports/test_import.py +++ b/bookwyrm/tests/views/imports/test_import.py @@ -51,19 +51,6 @@ class ImportViews(TestCase): validate_html(result.render()) self.assertEqual(result.status_code, 200) - def test_import_troubleshoot_get(self): - """there are so many views, this just makes sure it LOADS""" - view = views.ImportTroubleshoot.as_view() - import_job = models.ImportJob.objects.create(user=self.local_user, mappings={}) - request = self.factory.get("") - request.user = self.local_user - with patch("bookwyrm.tasks.app.AsyncResult") as async_result: - async_result.return_value = [] - result = view(request, import_job.id) - self.assertIsInstance(result, TemplateResponse) - validate_html(result.render()) - self.assertEqual(result.status_code, 200) - def test_start_import(self): """retry failed items""" view = views.Import.as_view() @@ -87,22 +74,3 @@ class ImportViews(TestCase): job = models.ImportJob.objects.get() self.assertFalse(job.include_reviews) self.assertEqual(job.privacy, "public") - - def test_retry_import(self): - """retry failed items""" - view = views.ImportTroubleshoot.as_view() - import_job = models.ImportJob.objects.create( - user=self.local_user, privacy="unlisted", mappings={} - ) - request = self.factory.post("") - request.user = self.local_user - - with patch("bookwyrm.importers.Importer.start_import"): - view(request, import_job.id) - - self.assertEqual(models.ImportJob.objects.count(), 2) - retry_job = models.ImportJob.objects.last() - - self.assertTrue(retry_job.retry) - self.assertEqual(retry_job.user, self.local_user) - self.assertEqual(retry_job.privacy, "unlisted") diff --git a/bookwyrm/tests/views/imports/test_import_troubleshoot.py b/bookwyrm/tests/views/imports/test_import_troubleshoot.py new file mode 100644 index 00000000..5359cc1e --- /dev/null +++ b/bookwyrm/tests/views/imports/test_import_troubleshoot.py @@ -0,0 +1,59 @@ +""" test for app action functionality """ +from unittest.mock import patch +from django.template.response import TemplateResponse +from django.test import TestCase +from django.test.client import RequestFactory +from bookwyrm.tests.validate_html import validate_html + +from bookwyrm import models, views + + +class ImportTroubleshootViews(TestCase): + """goodreads import views""" + + def setUp(self): + """we need basic test data and mocks""" + self.factory = RequestFactory() + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse@local.com", + "mouse@mouse.mouse", + "password", + local=True, + localname="mouse", + ) + models.SiteSettings.objects.create() + + def test_import_troubleshoot_get(self): + """there are so many views, this just makes sure it LOADS""" + view = views.ImportTroubleshoot.as_view() + import_job = models.ImportJob.objects.create(user=self.local_user, mappings={}) + request = self.factory.get("") + request.user = self.local_user + with patch("bookwyrm.tasks.app.AsyncResult") as async_result: + async_result.return_value = [] + result = view(request, import_job.id) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_retry_import(self): + """retry failed items""" + view = views.ImportTroubleshoot.as_view() + import_job = models.ImportJob.objects.create( + user=self.local_user, privacy="unlisted", mappings={} + ) + request = self.factory.post("") + request.user = self.local_user + + with patch("bookwyrm.importers.Importer.start_import"): + view(request, import_job.id) + + self.assertEqual(models.ImportJob.objects.count(), 2) + retry_job = models.ImportJob.objects.last() + + self.assertTrue(retry_job.retry) + self.assertEqual(retry_job.user, self.local_user) + self.assertEqual(retry_job.privacy, "unlisted") From c3156a1de50e6d2376a749afd9e8830e965cd5fd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 10:22:28 -0800 Subject: [PATCH 085/121] Fixes import path in test --- bookwyrm/tests/views/imports/test_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/tests/views/imports/test_import.py b/bookwyrm/tests/views/imports/test_import.py index 7de5e8c8..b8b8b328 100644 --- a/bookwyrm/tests/views/imports/test_import.py +++ b/bookwyrm/tests/views/imports/test_import.py @@ -58,7 +58,7 @@ class ImportViews(TestCase): form.data["source"] = "Goodreads" form.data["privacy"] = "public" form.data["include_reviews"] = False - csv_file = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv") + csv_file = pathlib.Path(__file__).parent.joinpath("../../data/goodreads.csv") form.data["csv_file"] = SimpleUploadedFile( # pylint: disable=consider-using-with csv_file, From e77eea9c817ec9b836c370a632f2946b241677e2 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 10:52:11 -0800 Subject: [PATCH 086/121] Adds tests for import manual review --- .../tests/views/imports/test_import_review.py | 87 +++++++++++++++++++ bookwyrm/views/imports/manually_review.py | 1 + 2 files changed, 88 insertions(+) create mode 100644 bookwyrm/tests/views/imports/test_import_review.py diff --git a/bookwyrm/tests/views/imports/test_import_review.py b/bookwyrm/tests/views/imports/test_import_review.py new file mode 100644 index 00000000..2ab48468 --- /dev/null +++ b/bookwyrm/tests/views/imports/test_import_review.py @@ -0,0 +1,87 @@ +""" test for app action functionality """ +from unittest.mock import patch +from django.template.response import TemplateResponse +from django.test import TestCase +from django.test.client import RequestFactory +from bookwyrm.tests.validate_html import validate_html + +from bookwyrm import models, views + + +class ImportManualReviewViews(TestCase): + """goodreads import views""" + + def setUp(self): + """we need basic test data and mocks""" + self.factory = RequestFactory() + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse@local.com", + "mouse@mouse.mouse", + "password", + local=True, + localname="mouse", + ) + models.SiteSettings.objects.create() + self.job = models.ImportJob.objects.create(user=self.local_user, mappings={}) + + work = models.Work.objects.create(title="Test Work") + self.book = models.Edition.objects.create( + title="Example Edition", + remote_id="https://example.com/book/1", + parent_work=work, + ) + + def test_import_troubleshoot_get(self): + """there are so many views, this just makes sure it LOADS""" + view = views.ImportManualReview.as_view() + request = self.factory.get("") + request.user = self.local_user + with patch("bookwyrm.tasks.app.AsyncResult") as async_result: + async_result.return_value = [] + result = view(request, self.job.id) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_approve_item(self): + """a guess is correct""" + import_item = models.ImportItem.objects.create( + index=0, + job=self.job, + book_guess=self.book, + fail_reason="no match", + data={}, + normalized_data={}, + ) + request = self.factory.post("") + request.user = self.local_user + + with patch("bookwyrm.importers.importer.import_item_task.delay") as mock: + views.approve_import_item(request, self.job.id, import_item.id) + self.assertEqual(mock.call_count, 1) + import_item.refresh_from_db() + self.assertIsNone(import_item.fail_reason) + self.assertIsNone(import_item.book_guess) + self.assertEqual(import_item.book.id, self.book.id) + + def test_delete_item(self): + """a guess is correct""" + import_item = models.ImportItem.objects.create( + index=0, + job=self.job, + book_guess=self.book, + fail_reason="no match", + data={}, + normalized_data={}, + ) + request = self.factory.post("") + request.user = self.local_user + + views.delete_import_item(request, self.job.id, import_item.id) + import_item.refresh_from_db() + self.assertEqual(import_item.fail_reason, "no match") + self.assertIsNone(import_item.book_guess) + self.assertIsNone(import_item.book) diff --git a/bookwyrm/views/imports/manually_review.py b/bookwyrm/views/imports/manually_review.py index 877e3ffc..723fd4bb 100644 --- a/bookwyrm/views/imports/manually_review.py +++ b/bookwyrm/views/imports/manually_review.py @@ -51,6 +51,7 @@ def approve_import_item(request, job_id, item_id): ) item.fail_reason = None item.book = item.book_guess + item.book_guess = None item.save() # the good stuff - actually import the data From 4dae851da0b2d85cc35a08c05f34ec9bcedec66d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 11:15:58 -0800 Subject: [PATCH 087/121] Adds breadcrumbs --- bookwyrm/templates/feed/layout.html | 2 +- bookwyrm/templates/import/import_status.html | 37 ++++++++++++++------ bookwyrm/templates/import/manual_review.html | 6 ++++ bookwyrm/templates/import/troubleshoot.html | 6 ++++ 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/bookwyrm/templates/feed/layout.html b/bookwyrm/templates/feed/layout.html index 8d79781b..6e7ec849 100644 --- a/bookwyrm/templates/feed/layout.html +++ b/bookwyrm/templates/feed/layout.html @@ -9,7 +9,7 @@ {% if user.is_authenticated %}
-

{% trans "Your books" %}

+

{% trans "Your Books" %}

{% if not suggested_books %}

{% trans "There are no books here right now! Try searching for a book to get started" %}

{% else %} diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 5b33f394..f5f590e1 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -7,12 +7,32 @@ {% block content %}{% spaceless %}
- {% if job.retry %} -

{% trans "Retry Status" %}

- {% else %} -

{% trans "Import Status" %}

- {% endif %} - {% trans "Back to imports" %} +

+ {% block page_title %} + {% if job.retry %} + {% trans "Retry Status" %} + {% else %} + {% trans "Import Status" %} + {% endif %} + {% endblock %} +

+ +
@@ -63,11 +83,6 @@
-

- {% block page_title %} - {% trans "Your Import" %} - {% endblock %} -

{% block actions %}{% endblock %}
diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index 4661520b..53601fd1 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -8,6 +8,12 @@ {% trans "Review items" %} {% endblock %} +{% block breadcrumbs %} +
  • + {% trans "Review" %} +
  • +{% endblock %} + {% block actions %}
    diff --git a/bookwyrm/templates/import/troubleshoot.html b/bookwyrm/templates/import/troubleshoot.html index 0be68383..a96aaaea 100644 --- a/bookwyrm/templates/import/troubleshoot.html +++ b/bookwyrm/templates/import/troubleshoot.html @@ -7,6 +7,12 @@ {% trans "Failed items" %} {% endblock %} +{% block breadcrumbs %} +
  • + {% trans "Troubleshooting" %} +
  • +{% endblock %} + {% block actions %}
    From 644d9693300a73e66a40e4c9438bf7afbe411ea4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 11:22:07 -0800 Subject: [PATCH 088/121] Fixes importer tests --- bookwyrm/tests/importers/test_importer.py | 42 +++++++---------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index b0863fbe..45d87171 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -140,7 +140,7 @@ class GenericImporter(TestCase): with patch( "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" ) as mock: - import_item_task(self.importer.service, import_item.id) + import_item_task(import_item.id) kwargs = mock.call_args.kwargs self.assertEqual(kwargs["queue"], "low_priority") import_item.refresh_from_db() @@ -160,9 +160,7 @@ class GenericImporter(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -179,16 +177,14 @@ class GenericImporter(TestCase): ) import_job = self.importer.create_job( - self.local_user, self.csv, False, "unlisted" + self.local_user, self.csv, False, "public" ) import_item = import_job.items.first() import_item.book = self.book import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -210,12 +206,8 @@ class GenericImporter(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "public" - ) + handle_imported_book(import_item) + handle_imported_book(import_item) shelf.refresh_from_db() self.assertEqual(shelf.books.first(), self.book) @@ -224,20 +216,16 @@ class GenericImporter(TestCase): @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_review(self, *_): """review import""" - import_job = self.importer.create_job(self.local_user, self.csv, True, "public") + import_job = self.importer.create_job( + self.local_user, self.csv, True, "unlisted" + ) import_item = import_job.items.filter(index=3).first() import_item.book = self.book import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): with patch("bookwyrm.models.Status.broadcast") as broadcast_mock: - handle_imported_book( - self.importer.service, - self.local_user, - import_item, - True, - "unlisted", - ) + handle_imported_book(import_item) kwargs = broadcast_mock.call_args.kwargs self.assertEqual(kwargs["software"], "bookwyrm") review = models.Review.objects.get(book=self.book, user=self.local_user) @@ -249,16 +237,14 @@ class GenericImporter(TestCase): def test_handle_imported_book_rating(self, *_): """rating import""" import_job = self.importer.create_job( - self.local_user, self.csv, False, "public" + self.local_user, self.csv, True, "unlisted" ) import_item = import_job.items.filter(index=1).first() import_item.book = self.book import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, True, "unlisted" - ) + handle_imported_book(import_item) review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) self.assertIsInstance(review, models.ReviewRating) self.assertEqual(review.rating, 3.0) @@ -274,9 +260,7 @@ class GenericImporter(TestCase): import_item.save() with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - handle_imported_book( - self.importer.service, self.local_user, import_item, False, "unlisted" - ) + handle_imported_book(import_item) self.assertFalse( models.Review.objects.filter(book=self.book, user=self.local_user).exists() ) From 1e2dca402b251fc9ae969516d26de7294fd72af5 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 11:40:19 -0800 Subject: [PATCH 089/121] Adds null value to breadcrumb links --- bookwyrm/templates/import/manual_review.html | 2 +- bookwyrm/templates/import/troubleshoot.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index 53601fd1..2c487563 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -10,7 +10,7 @@ {% block breadcrumbs %}
  • - {% trans "Review" %} + {% trans "Review" %}
  • {% endblock %} diff --git a/bookwyrm/templates/import/troubleshoot.html b/bookwyrm/templates/import/troubleshoot.html index a96aaaea..d73be6d0 100644 --- a/bookwyrm/templates/import/troubleshoot.html +++ b/bookwyrm/templates/import/troubleshoot.html @@ -9,7 +9,7 @@ {% block breadcrumbs %}
  • - {% trans "Troubleshooting" %} + {% trans "Troubleshooting" %}
  • {% endblock %} From 1e46de4c9d4a184dedf127a4245fb75254a21b3f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 11:44:05 -0800 Subject: [PATCH 090/121] Associate imported review with import item --- bookwyrm/importers/importer.py | 2 ++ .../0115_importitem_linked_review.py | 24 +++++++++++++++++++ bookwyrm/models/import_job.py | 3 +++ bookwyrm/tests/importers/test_importer.py | 6 +++++ 4 files changed, 35 insertions(+) create mode 100644 bookwyrm/migrations/0115_importitem_linked_review.py diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 71f02231..a1c35ef7 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -205,3 +205,5 @@ def handle_imported_book(item): ) # only broadcast this review to other bookwyrm instances review.save(software="bookwyrm", priority=LOW) + item.linked_review = review + item.save() diff --git a/bookwyrm/migrations/0115_importitem_linked_review.py b/bookwyrm/migrations/0115_importitem_linked_review.py new file mode 100644 index 00000000..8cff9b8c --- /dev/null +++ b/bookwyrm/migrations/0115_importitem_linked_review.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.5 on 2021-11-13 19:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0114_importjob_source"), + ] + + operations = [ + migrations.AddField( + model_name="importitem", + name="linked_review", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="bookwyrm.review", + ), + ), + ] diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 6b8f0b46..ddd9eaec 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -60,6 +60,9 @@ class ImportItem(models.Model): related_name="book_guess", ) fail_reason = models.TextField(null=True) + linked_review = models.ForeignKey( + "Review", on_delete=models.SET_NULL, null=True, blank=True + ) def resolve(self): """try various ways to lookup a book""" diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index 45d87171..99cdcd28 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -233,6 +233,9 @@ class GenericImporter(TestCase): self.assertEqual(review.rating, 2.0) self.assertEqual(review.privacy, "unlisted") + import_item.refresh_from_db() + self.assertEqual(import_item.linked_review, review) + @patch("bookwyrm.activitystreams.add_status_task.delay") def test_handle_imported_book_rating(self, *_): """rating import""" @@ -250,6 +253,9 @@ class GenericImporter(TestCase): self.assertEqual(review.rating, 3.0) self.assertEqual(review.privacy, "unlisted") + import_item.refresh_from_db() + self.assertEqual(import_item.linked_review.id, review.id) + def test_handle_imported_book_reviews_disabled(self, *_): """review import""" import_job = self.importer.create_job( From 712d8ecfb437fcafafbfc971e796b8b01895c72b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 11:52:08 -0800 Subject: [PATCH 091/121] Don't show empty stars when there's no review --- bookwyrm/templates/import/import_status.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index f5f590e1..4871a4c7 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -134,8 +134,15 @@ {{ item.normalized_data.shelf }}
    {% block import_cols %} {% endblock %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index d6a79c20..6f658016 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -242,6 +242,11 @@ urlpatterns = [ views.ImportStatus.as_view(), name="import-status", ), + re_path( + r"^import/(?P\d+)/retry/(?P\d+)/?$", + views.ImportStatus.as_view(), + name="import-item-retry", + ), re_path( r"^import/(?P\d+)/failed/?$", views.ImportTroubleshoot.as_view(), diff --git a/bookwyrm/views/imports/import_status.py b/bookwyrm/views/imports/import_status.py index 7e7d5179..54174082 100644 --- a/bookwyrm/views/imports/import_status.py +++ b/bookwyrm/views/imports/import_status.py @@ -4,12 +4,14 @@ import math from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse +from django.utils import timezone from django.utils.decorators import method_decorator from django.views import View from bookwyrm import models +from bookwyrm.importers.importer import import_item_task from bookwyrm.settings import PAGE_LENGTH # pylint: disable= no-self-use @@ -40,10 +42,19 @@ class ImportStatus(View): "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), - "complete": not job.pending_items.exists(), "percent": math.floor( # pylint: disable=c-extension-no-member (item_count - job.pending_items.count()) / item_count * 100 ), + # hours since last import item update + "inactive_time": (job.updated_date - timezone.now()).seconds / 60 / 60, } return TemplateResponse(request, "import/import_status.html", data) + + def post(self, request, job_id, item_id): + """retry an item""" + item = get_object_or_404( + models.ImportItem, id=item_id, job__id=job_id, job__user=request.user + ) + import_item_task.delay(item.id) + return redirect("import-status", job_id) From 31f33518545453a5002c56e03a6da8968eff2633 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 10:22:26 -0800 Subject: [PATCH 107/121] Fixes bug comparing dates to nonetype --- bookwyrm/models/import_job.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 387261f0..97b93b97 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -205,7 +205,9 @@ class ImportItem(models.Model): if start_date and start_date is not None and not self.date_read: return [ReadThrough(start_date=start_date)] if self.date_read: - start_date = start_date if start_date < self.date_read else None + start_date = ( + start_date if start_date and start_date < self.date_read else None + ) return [ ReadThrough( start_date=start_date, From 9f6796bbf58b8831dc0e65b8ff472d98720c45f7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 10:29:12 -0800 Subject: [PATCH 108/121] Safer request for normalized data --- bookwyrm/models/import_job.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 97b93b97..c4679585 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -131,18 +131,18 @@ class ImportItem(models.Model): @property def title(self): """get the book title""" - return self.normalized_data["title"] + return self.normalized_data.get("title") @property def author(self): """get the book's authors""" - return self.normalized_data["authors"] + return self.normalized_data.get("authors") @property def isbn(self): """pulls out the isbn13 field from the csv line data""" - return unquote_string(self.normalized_data["isbn_13"]) or unquote_string( - self.normalized_data["isbn_10"] + return unquote_string(self.normalized_data.get("isbn_13")) or unquote_string( + self.normalized_data.get("isbn_10") ) @property @@ -153,13 +153,13 @@ class ImportItem(models.Model): @property def review(self): """a user-written review, to be imported with the book data""" - return self.normalized_data["review_body"] + return self.normalized_data.get("review_body") @property def rating(self): """x/5 star rating for a book""" if self.normalized_data.get("rating"): - return float(self.normalized_data["rating"]) + return float(self.normalized_data.get("rating")) return None @property @@ -167,7 +167,7 @@ class ImportItem(models.Model): """when the book was added to this dataset""" if self.normalized_data.get("date_added"): return timezone.make_aware( - dateutil.parser.parse(self.normalized_data["date_added"]) + dateutil.parser.parse(self.normalized_data.get("date_added")) ) return None @@ -176,7 +176,7 @@ class ImportItem(models.Model): """when the book was started""" if self.normalized_data.get("date_started"): return timezone.make_aware( - dateutil.parser.parse(self.normalized_data["date_started"]) + dateutil.parser.parse(self.normalized_data.get("date_started")) ) return None @@ -185,7 +185,7 @@ class ImportItem(models.Model): """the date a book was completed""" if self.normalized_data.get("date_finished"): return timezone.make_aware( - dateutil.parser.parse(self.normalized_data["date_finished"]) + dateutil.parser.parse(self.normalized_data.get("date_finished")) ) return None @@ -218,10 +218,10 @@ class ImportItem(models.Model): def __repr__(self): # pylint: disable=consider-using-f-string - return "<{!r} Item {!r}>".format(self.index, self.normalized_data["title"]) + return "<{!r} Item {!r}>".format(self.index, self.normalized_data.get("title")) def __str__(self): # pylint: disable=consider-using-f-string return "{} by {}".format( - self.normalized_data["title"], self.normalized_data["authors"] + self.normalized_data.get("title"), self.normalized_data.get("authors") ) From 14e2960d063a71471b52edbd8df9548d429d5559 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 10:58:46 -0800 Subject: [PATCH 109/121] Update legacy jobs --- bookwyrm/importers/importer.py | 14 ++++++++++ bookwyrm/templates/import/import_status.html | 29 ++++++++++++++++++-- bookwyrm/urls.py | 2 +- bookwyrm/views/__init__.py | 2 +- bookwyrm/views/imports/import_status.py | 26 ++++++++++++++---- 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 438ff7db..94e6734e 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -57,6 +57,20 @@ class Importer: self.create_item(job, index, entry) return job + def update_legacy_job(self, job): + """patch up a job that was in the old format""" + items = job.items + headers = list(items.first().data.keys()) + job.mappings = self.create_row_mappings(headers) + job.updated_date = timezone.now() + job.save() + + for item in items.all(): + normalized = self.normalize_row(item.data, job.mappings) + normalized["shelf"] = self.get_shelf(normalized) + item.normalized_data = normalized + item.save() + def create_row_mappings(self, headers): """guess what the headers mean""" mappings = {} diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 6c7d54b9..6370b866 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -57,7 +57,7 @@ {% endif %} - {% if manual_review_count %} + {% if manual_review_count and not legacy %}
    {% blocktrans trimmed count counter=manual_review_count with display_counter=manual_review_count|intcomma %} {{ display_counter }} item needs manual approval. @@ -68,7 +68,7 @@
    {% endif %} - {% if complete and fail_count and not job.retry %} + {% if job.complete and fail_count and not job.retry and not legacy %}
    {% blocktrans trimmed count counter=fail_count with display_counter=fail_count|intcomma %} {{ display_counter }} item failed to import. @@ -114,6 +114,15 @@ {% endblock %}
    + {% if legacy %} + + + + {% else %} {% for item in items %} {% block index_col %} @@ -171,7 +180,7 @@ {% trans "Pending" %} {# retry option if an item appears to be hanging #} - {% if job.created_date != job.updated_date and inactive_time > 0.24 %} + {% if job.created_date != job.updated_date and inactive_time > 24 %} {% csrf_token %} @@ -184,13 +193,27 @@ {% block action_row %}{% endblock %} {% endfor %} + {% endif %}
    + {% if item.rating %}

    {% include 'snippets/stars.html' with rating=item.rating %}

    + {% endif %} + {% if item.review %}

    {{ item.review|truncatechars:100 }}

    + {% endif %} + {% if item.linked_review %} + {% trans "View imported review" %} + {% endif %}
    From 8b7720c8b85264e533bc160991f36e533ed46461 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 11:54:28 -0800 Subject: [PATCH 092/121] Use "reject" instead of "delete" on review page --- bookwyrm/templates/import/manual_review.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index 2c487563..b6c2b6b2 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -61,7 +61,7 @@ {% csrf_token %} From a9622942cd71ebbc8ac2bf048ecd81e8e3bcb9db Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 12:11:07 -0800 Subject: [PATCH 093/121] Test correctly adding goodreads isbns --- bookwyrm/tests/importers/test_goodreads_import.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index b1600659..a51eca89 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -52,6 +52,11 @@ class GoodreadsImport(TestCase): self.assertEqual(len(import_items), 3) self.assertEqual(import_items[0].index, 0) self.assertEqual(import_items[0].data["Book Id"], "42036538") + self.assertEqual( + import_items[0].normalized_data["isbn_13"], '=""9781250313195"' + ) + self.assertEqual(import_items[0].normalized_data["isbn_10"], '=""1250313198"') + self.assertEqual(import_items[1].index, 1) self.assertEqual(import_items[1].data["Book Id"], "52691223") self.assertEqual(import_items[2].index, 2) From fb91c33682c2e42d8b365a24209aa41d7678301f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 12:24:16 -0800 Subject: [PATCH 094/121] Fixes isbn assignment for goodreads --- bookwyrm/importers/importer.py | 30 +++++++++---------- bookwyrm/models/import_job.py | 4 ++- bookwyrm/templates/import/import_status.html | 2 +- .../tests/importers/test_goodreads_import.py | 4 +-- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 71f02231..db13b652 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -20,20 +20,20 @@ class Importer: encoding = "UTF-8" # these are from Goodreads - row_mappings_guesses = { - "id": ["id", "book id"], - "title": ["title"], - "authors": ["author", "authors", "primary author"], - "isbn_13": ["isbn13", "isbn"], - "isbn_10": ["isbn10", "isbn"], - "shelf": ["shelf", "exclusive shelf", "read status"], - "review_name": ["review name"], - "review_body": ["my review", "review"], - "rating": ["my rating", "rating", "star rating"], - "date_added": ["date added", "entry date", "added"], - "date_started": ["date started", "started"], - "date_finished": ["date finished", "last date read", "date read", "finished"], - } + row_mappings_guesses = [ + ("id", ["id", "book id"]), + ("title", ["title"]), + ("authors", ["author", "authors", "primary author"]), + ("isbn_10", ["isbn10", "isbn"]), + ("isbn_13", ["isbn13", "isbn"]), + ("shelf", ["shelf", "exclusive shelf", "read status"]), + ("review_name", ["review name"]), + ("review_body", ["my review", "review"]), + ("rating", ["my rating", "rating", "star rating"]), + ("date_added", ["date added", "entry date", "added"]), + ("date_started", ["date started", "started"]), + ("date_finished", ["date finished", "last date read", "date read", "finished"]), + ] date_fields = ["date_added", "date_started", "date_finished"] shelf_mapping_guesses = { "to-read": ["to-read"], @@ -60,7 +60,7 @@ class Importer: def create_row_mappings(self, headers): """guess what the headers mean""" mappings = {} - for (key, guesses) in self.row_mappings_guesses.items(): + for (key, guesses) in self.row_mappings_guesses: value = [h for h in headers if h.lower() in guesses] value = value[0] if len(value) else None if value: diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 6b8f0b46..18565017 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -12,6 +12,8 @@ from .fields import PrivacyLevels def unquote_string(text): """resolve csv quote weirdness""" + if not text: + return None match = re.match(r'="([^"]*)"', text) if match: return match.group(1) @@ -122,7 +124,7 @@ class ImportItem(models.Model): @property def isbn(self): """pulls out the isbn13 field from the csv line data""" - return unquote_string(self.normalized_data["isbn_13"]) + return unquote_string(self.normalized_data["isbn_13"]) or unquote_string(self.normalized_data["isbn_10"]) @property def shelf(self): diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index f5f590e1..8208a2fa 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -125,7 +125,7 @@ {{ item.normalized_data.title }} - {{ item.isbn }} + {{ item.isbn|default:'' }} {{ item.normalized_data.authors }} diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index a51eca89..4a043b59 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -53,9 +53,9 @@ class GoodreadsImport(TestCase): self.assertEqual(import_items[0].index, 0) self.assertEqual(import_items[0].data["Book Id"], "42036538") self.assertEqual( - import_items[0].normalized_data["isbn_13"], '=""9781250313195"' + import_items[0].normalized_data["isbn_13"], '="9781250313195"' ) - self.assertEqual(import_items[0].normalized_data["isbn_10"], '=""1250313198"') + self.assertEqual(import_items[0].normalized_data["isbn_10"], '="1250313198"') self.assertEqual(import_items[1].index, 1) self.assertEqual(import_items[1].data["Book Id"], "52691223") From 7f06ee3844d0704bfa3947c0e70f623b70556dec Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 12:46:27 -0800 Subject: [PATCH 095/121] Fixes getting isbn for librarything imports --- bookwyrm/importers/importer.py | 2 +- bookwyrm/importers/librarything_import.py | 10 ++++++---- bookwyrm/tests/importers/test_librarything_import.py | 3 +++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index db13b652..ca63ae4a 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -25,7 +25,7 @@ class Importer: ("title", ["title"]), ("authors", ["author", "authors", "primary author"]), ("isbn_10", ["isbn10", "isbn"]), - ("isbn_13", ["isbn13", "isbn"]), + ("isbn_13", ["isbn13", "isbn", "isbns"]), ("shelf", ["shelf", "exclusive shelf", "read status"]), ("review_name", ["review name"]), ("review_body", ["my review", "review"]), diff --git a/bookwyrm/importers/librarything_import.py b/bookwyrm/importers/librarything_import.py index d6426de6..cff6ba7d 100644 --- a/bookwyrm/importers/librarything_import.py +++ b/bookwyrm/importers/librarything_import.py @@ -12,10 +12,12 @@ class LibrarythingImporter(Importer): def normalize_row(self, entry, mappings): # pylint: disable=no-self-use """use the dataclass to create the formatted row of data""" - normalized = {k: entry.get(v) for k, v in mappings.items()} - for date_field in self.date_fields: - date = normalized[date_field] - normalized[date_field] = re.sub(r"\[|\]", "", date) + remove_brackets = lambda v: re.sub(r"\[|\]", "", v) if v else None + normalized = { + k: remove_brackets(entry.get(v)) for k, v in mappings.items() + } + isbn_13 = normalized["isbn_13"].split(', ') + normalized["isbn_13"] = isbn_13[1] if len(isbn_13) > 0 else None return normalized def get_shelf(self, normalized_row): diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index 804118ef..f5d8d669 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -56,6 +56,9 @@ class LibrarythingImport(TestCase): self.assertEqual(len(import_items), 3) self.assertEqual(import_items[0].index, 0) self.assertEqual(import_items[0].data["Book Id"], "5498194") + self.assertEqual(import_items[0].normalized_data["isbn_13"], "9782070291342") + self.assertEqual(import_items[0].normalized_data["isbn_10"], "2070291340") + self.assertEqual(import_items[1].index, 1) self.assertEqual(import_items[1].data["Book Id"], "5015319") self.assertEqual(import_items[2].index, 2) From 32d0d8d0274150f5e10e28f81e7f73cd06265f26 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 13 Nov 2021 13:04:54 -0800 Subject: [PATCH 096/121] Expand librarything csv processing tests --- bookwyrm/tests/data/librarything.tsv | 2 +- bookwyrm/tests/importers/test_librarything_import.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bookwyrm/tests/data/librarything.tsv b/bookwyrm/tests/data/librarything.tsv index a707f2a9..68bbe48e 100644 --- a/bookwyrm/tests/data/librarything.tsv +++ b/bookwyrm/tests/data/librarything.tsv @@ -1,4 +1,4 @@ Book Id Title Sort Character Primary Author Primary Author Role Secondary Author Secondary Author Roles Publication Date Review Rating Comment Private Comment Summary Media Physical Description Weight Height Thickness Length Dimensions Page Count LCCN Acquired Date Started Date Read Barcode BCID Tags Collections Languages Original Languages LC Classification ISBN ISBNs Subjects Dewey Decimal Dewey Wording Other Call Number Copies Source Entry Date From Where OCLC Work id Lending Patron Lending Status Lending Start Lending End -5498194 Marelle 1 Cortázar, Julio Gallimard (1979), Poche 1979 chef d'oeuvre 4.5 Marelle by Julio Cortázar (1979) Broché 590 p.; 7.24 inches 1.28 pounds 7.24 inches 1.26 inches 4.96 inches 7.24 x 4.96 x 1.26 inches 590 [2007-04-16] [2007-05-08] roman, espagnol, expérimental, bohème, philosophie Your library French Spanish PQ7797 .C7145 [2070291340] 2070291340, 9782070291342 Cortazar, Julio. Rayuela 863 Literature > Spanish And Portuguese > Spanish fiction 1 Amazon.fr [2006-08-09] 57814 +5498194 Marelle 1 Cortazar, Julio Gallimard (1979), Poche 1979 chef d'oeuvre 4.5 Marelle by Julio Cortázar (1979) Broché 590 p.; 7.24 inches 1.28 pounds 7.24 inches 1.26 inches 4.96 inches 7.24 x 4.96 x 1.26 inches 590 [2007-04-16] [2007-05-08] roman, espagnol, expérimental, bohème, philosophie Your library French Spanish PQ7797 .C7145 [2070291340] 2070291340, 9782070291342 Cortazar, Julio. Rayuela 863 Literature > Spanish And Portuguese > Spanish fiction 1 Amazon.fr [2006-08-09] 57814 5015319 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) 1 Roubaud, Jacques Seuil (1989), Unknown Binding 1989 5 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) by Jacques Roubaud (1989) Broché 411 p.; 7.72 inches 0.88 pounds 7.72 inches 1.02 inches 5.43 inches 7.72 x 5.43 x 1.02 inches 411 Your library English PQ2678 .O77 [2020104725] 2020104725, 9782020104722 Autobiographical fiction|Roubaud, Jacques > Fiction 813 American And Canadian > Fiction > Literature 1 Amazon.com [2006-07-25] 478910 5015399 Le Maître et Marguerite 1 Boulgakov, Mikhaïl Pocket (1994), Poche 1994 Le Maître et Marguerite by Mikhaïl Boulgakov (1994) Broché 579 p.; 7.09 inches 0.66 pounds 7.09 inches 1.18 inches 4.33 inches 7.09 x 4.33 x 1.18 inches 579 Your library French PG3476 .B78 [2266062328] 2266062328, 9782266062329 Allegories|Bulgakov|Good and evil > Fiction|Humanities|Jerusalem > Fiction|Jesus Christ > Fiction|Literature|Mental illness > Fiction|Moscow (Russia) > Fiction|Novel|Pilate, Pontius, 1st cent. > Fiction|Political fiction|Russia > Fiction|Russian fiction|Russian publications (Form Entry)|Soviet Union > History > 1925-1953 > Fiction|literature 891.7342 1917-1945 > 1917-1991 (USSR) > Literature > Literature of other Indo-European languages > Other Languages > Russian > Russian Fiction 1 Amazon.fr [2006-07-25] 10151 diff --git a/bookwyrm/tests/importers/test_librarything_import.py b/bookwyrm/tests/importers/test_librarything_import.py index f5d8d669..49354b36 100644 --- a/bookwyrm/tests/importers/test_librarything_import.py +++ b/bookwyrm/tests/importers/test_librarything_import.py @@ -58,6 +58,11 @@ class LibrarythingImport(TestCase): self.assertEqual(import_items[0].data["Book Id"], "5498194") self.assertEqual(import_items[0].normalized_data["isbn_13"], "9782070291342") self.assertEqual(import_items[0].normalized_data["isbn_10"], "2070291340") + self.assertEqual(import_items[0].normalized_data["title"], "Marelle") + self.assertEqual(import_items[0].normalized_data["authors"], "Cortazar, Julio") + self.assertEqual(import_items[0].normalized_data["date_added"], "2006-08-09") + self.assertEqual(import_items[0].normalized_data["date_started"], "2007-04-16") + self.assertEqual(import_items[0].normalized_data["date_finished"], "2007-05-08") self.assertEqual(import_items[1].index, 1) self.assertEqual(import_items[1].data["Book Id"], "5015319") From 8495cf8a45e90b463e0e70b10956957f90ff4365 Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Sun, 14 Nov 2021 21:21:37 +1100 Subject: [PATCH 097/121] don't delete non-form data when editing authors fixes #1584 This is a temporary fix. As Mouse has suggested, ultimately it would be good to re-import data from one or more of the linked data sources if there is anything missing. --- bookwyrm/forms.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 298f73da..1ec51df9 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -201,12 +201,17 @@ class EditionForm(CustomForm): class AuthorForm(CustomForm): class Meta: model = models.Author - exclude = [ - "remote_id", - "origin_id", - "created_date", - "updated_date", - "search_vector", + fields = [ + "last_edited_by", + "name", + "aliases", + "bio", + "wikipedia_link", + "born", + "died", + "openlibrary_key", + "librarything_key", + "goodreads_key", ] From 3357953a538e070e3cc8515234aa4c18b9a74b03 Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Sun, 14 Nov 2021 21:26:23 +1100 Subject: [PATCH 098/121] whoops forgot inventaire_id --- bookwyrm/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 1ec51df9..847ca05c 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -210,6 +210,7 @@ class AuthorForm(CustomForm): "born", "died", "openlibrary_key", + "inventaire_id", "librarything_key", "goodreads_key", ] From 66ad8c3b25114977134269c8dcb0ce3d3bdc14f4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 07:11:33 -0800 Subject: [PATCH 099/121] Updates locale --- locale/en_US/LC_MESSAGES/django.po | 248 +++++++++++++++++++---------- 1 file changed, 161 insertions(+), 87 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index 7d8fc801..14bbb1b9 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-24 14:09+0000\n" +"POT-Creation-Date: 2021-11-14 15:08+0000\n" "PO-Revision-Date: 2021-02-28 17:19-0800\n" "Last-Translator: Mouse Reeve \n" "Language-Team: English \n" @@ -73,15 +73,16 @@ msgstr "" msgid "Descending" msgstr "" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "" @@ -101,23 +102,23 @@ msgstr "" msgid "Domain block" msgstr "" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "" @@ -134,21 +135,21 @@ msgstr "" msgid "Blocked" msgstr "" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "" -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "" -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "" @@ -893,22 +894,37 @@ msgstr "" msgid "All known users" msgstr "" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 #, python-format -msgid "%(username)s rated %(book_title)s" +msgid "%(username)s wants to read %(book_title)s" msgstr "" #: bookwyrm/templates/discover/card-header.html:13 #, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:23 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:27 +#, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" msgstr "" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "" @@ -1059,9 +1075,8 @@ msgstr "" msgid "Updates" msgstr "" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" msgstr "" #: bookwyrm/templates/feed/layout.html:14 @@ -1070,11 +1085,13 @@ msgstr "" #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "" @@ -1082,6 +1099,7 @@ msgstr "" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "" @@ -1367,88 +1385,161 @@ msgid "No recent imports" msgstr "" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" msgstr "" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "" - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" msgstr "" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." +msgid "Refresh" msgstr "" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" +msgstr[1] "" + +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" msgstr "" -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" +msgstr[1] "" + +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" msgstr "" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" msgstr "" -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "" - -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" -msgstr "" - -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "" - -#: bookwyrm/templates/import/import_status.html:122 +#: bookwyrm/templates/import/import_status.html:94 #: bookwyrm/templates/shelf/shelf.html:141 #: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "" -#: bookwyrm/templates/import/import_status.html:125 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 #: bookwyrm/templates/shelf/shelf.html:142 #: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "" +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1580,10 +1671,6 @@ msgstr "" msgid "Feed" msgstr "" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "" @@ -1683,10 +1770,6 @@ msgstr "" msgid "Suggested by" msgstr "" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "" @@ -2239,15 +2322,6 @@ msgstr "" msgid "End date" msgstr "" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "" @@ -3096,10 +3170,6 @@ msgstr "" msgid "Un-boost" msgstr "" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "" @@ -3526,7 +3596,7 @@ msgstr "" msgid "commented on %(book)s" msgstr "" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "" @@ -3605,7 +3675,11 @@ msgstr "" msgid "Show less" msgstr "" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "" @@ -3749,7 +3823,7 @@ msgstr "" msgid "%(title)s: %(subtitle)s" msgstr "" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "" From bdc3f6828ba476e4855f0b7922bd599240490182 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 07:11:48 -0800 Subject: [PATCH 100/121] Python formatting --- bookwyrm/importers/librarything_import.py | 6 ++---- bookwyrm/models/import_job.py | 6 ++++-- bookwyrm/tests/importers/test_goodreads_import.py | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bookwyrm/importers/librarything_import.py b/bookwyrm/importers/librarything_import.py index cff6ba7d..1b61a6f1 100644 --- a/bookwyrm/importers/librarything_import.py +++ b/bookwyrm/importers/librarything_import.py @@ -13,10 +13,8 @@ class LibrarythingImporter(Importer): def normalize_row(self, entry, mappings): # pylint: disable=no-self-use """use the dataclass to create the formatted row of data""" remove_brackets = lambda v: re.sub(r"\[|\]", "", v) if v else None - normalized = { - k: remove_brackets(entry.get(v)) for k, v in mappings.items() - } - isbn_13 = normalized["isbn_13"].split(', ') + normalized = {k: remove_brackets(entry.get(v)) for k, v in mappings.items()} + isbn_13 = normalized["isbn_13"].split(", ") normalized["isbn_13"] = isbn_13[1] if len(isbn_13) > 0 else None return normalized diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index b47379bb..753662d6 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -127,7 +127,9 @@ class ImportItem(models.Model): @property def isbn(self): """pulls out the isbn13 field from the csv line data""" - return unquote_string(self.normalized_data["isbn_13"]) or unquote_string(self.normalized_data["isbn_10"]) + return unquote_string(self.normalized_data["isbn_13"]) or unquote_string( + self.normalized_data["isbn_10"] + ) @property def shelf(self): @@ -200,7 +202,7 @@ class ImportItem(models.Model): def __repr__(self): # pylint: disable=consider-using-f-string - return "<{!r}Item {!r}>".format(self.index, self.normalized_data["title"]) + return "<{!r} Item {!r}>".format(self.index, self.normalized_data["title"]) def __str__(self): # pylint: disable=consider-using-f-string diff --git a/bookwyrm/tests/importers/test_goodreads_import.py b/bookwyrm/tests/importers/test_goodreads_import.py index 4a043b59..0a421df4 100644 --- a/bookwyrm/tests/importers/test_goodreads_import.py +++ b/bookwyrm/tests/importers/test_goodreads_import.py @@ -52,9 +52,7 @@ class GoodreadsImport(TestCase): self.assertEqual(len(import_items), 3) self.assertEqual(import_items[0].index, 0) self.assertEqual(import_items[0].data["Book Id"], "42036538") - self.assertEqual( - import_items[0].normalized_data["isbn_13"], '="9781250313195"' - ) + self.assertEqual(import_items[0].normalized_data["isbn_13"], '="9781250313195"') self.assertEqual(import_items[0].normalized_data["isbn_10"], '="1250313198"') self.assertEqual(import_items[1].index, 1) From 2748e0a8249fd6c04594093339d6faf22cb1141f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 07:50:19 -0800 Subject: [PATCH 101/121] Check for existing reviews/ratings on import items --- bookwyrm/importers/importer.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 1d350c6d..657ede05 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -171,21 +171,25 @@ def handle_imported_book(item): read.user = user read.save() - if job.include_reviews and (item.rating or item.review): + if job.include_reviews and (item.rating or item.review) and not item.linked_review: # we don't know the publication date of the review, # but "now" is a bad guess published_date_guess = item.date_read or item.date_added if item.review: # pylint: disable=consider-using-f-string - review_title = ( - "Review of {!r} on {!r}".format( - item.book.title, - job.source, - ) - if item.review - else "" + review_title = "Review of {!r} on {!r}".format( + item.book.title, + job.source, ) - review = models.Review( + existing = models.Review.objects.filter( + user=user, + book=item.book, + name=review_title, + rating=item.rating, + published_date=published_date_guess, + ).first() + + review = existing or models.Review( user=user, book=item.book, name=review_title, @@ -196,13 +200,20 @@ def handle_imported_book(item): ) else: # just a rating - review = models.ReviewRating( + existing = models.ReviewRating.objects.filter( + user=user, + book=item.book, + published_date=published_date_guess, + rating=item.rating, + ).first() + review = existing or models.ReviewRating( user=user, book=item.book, rating=item.rating, published_date=published_date_guess, privacy=job.privacy, ) + # only broadcast this review to other bookwyrm instances review.save(software="bookwyrm", priority=LOW) item.linked_review = review From 6cca3f97724dbab6eae49bb2d0a55c02b39b1347 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 07:57:13 -0800 Subject: [PATCH 102/121] Updates test data --- bookwyrm/tests/data/generic.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/tests/data/generic.csv b/bookwyrm/tests/data/generic.csv index 9c5b6f02..470ce7a8 100644 --- a/bookwyrm/tests/data/generic.csv +++ b/bookwyrm/tests/data/generic.csv @@ -1,4 +1,4 @@ -id,title,author,ISBN,rating,shelf,review,added,finished +id,title,author,ISBN13,rating,shelf,review,added,finished 38,Gideon the Ninth,Tamsyn Muir,"9781250313195",,read,,2021-11-10,2021-11-11 48,Harrow the Ninth,Tamsyn Muir,,3,read,,2021-11-10 23,Subcutanean,Aaron A. Reed,,,read,,2021-11-10 From 9e673834dc9294262acda328cba48cf371c022f6 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 08:23:26 -0800 Subject: [PATCH 103/121] Check for duplicates --- bookwyrm/importers/importer.py | 44 ++++++++++---------- bookwyrm/tests/importers/test_importer.py | 49 +++++++++++++++++++++++ 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 657ede05..05550429 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -181,40 +181,42 @@ def handle_imported_book(item): item.book.title, job.source, ) - existing = models.Review.objects.filter( + review = models.Review.objects.filter( user=user, book=item.book, name=review_title, rating=item.rating, published_date=published_date_guess, ).first() - - review = existing or models.Review( - user=user, - book=item.book, - name=review_title, - content=item.review, - rating=item.rating, - published_date=published_date_guess, - privacy=job.privacy, - ) + if not review: + review = models.Review( + user=user, + book=item.book, + name=review_title, + content=item.review, + rating=item.rating, + published_date=published_date_guess, + privacy=job.privacy, + ) + review.save(software="bookwyrm", priority=LOW) else: # just a rating - existing = models.ReviewRating.objects.filter( + review = models.ReviewRating.objects.filter( user=user, book=item.book, published_date=published_date_guess, rating=item.rating, ).first() - review = existing or models.ReviewRating( - user=user, - book=item.book, - rating=item.rating, - published_date=published_date_guess, - privacy=job.privacy, - ) + if not review: + review = models.ReviewRating( + user=user, + book=item.book, + rating=item.rating, + published_date=published_date_guess, + privacy=job.privacy, + ) + review.save(software="bookwyrm", priority=LOW) # only broadcast this review to other bookwyrm instances - review.save(software="bookwyrm", priority=LOW) item.linked_review = review - item.save() + item.save() diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index 99cdcd28..6996a92b 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -256,6 +256,55 @@ class GenericImporter(TestCase): import_item.refresh_from_db() self.assertEqual(import_item.linked_review.id, review.id) + @patch("bookwyrm.activitystreams.add_status_task.delay") + def test_handle_imported_book_rating_duplicate_with_link(self, *_): + """rating import twice""" + import_job = self.importer.create_job( + self.local_user, self.csv, True, "unlisted" + ) + import_item = import_job.items.filter(index=1).first() + import_item.book = self.book + import_item.save() + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): + handle_imported_book(import_item) + handle_imported_book(import_item) + + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) + self.assertIsInstance(review, models.ReviewRating) + self.assertEqual(review.rating, 3.0) + self.assertEqual(review.privacy, "unlisted") + + import_item.refresh_from_db() + self.assertEqual(import_item.linked_review.id, review.id) + + @patch("bookwyrm.activitystreams.add_status_task.delay") + def test_handle_imported_book_rating_duplicate_without_link(self, *_): + """rating import twice""" + import_job = self.importer.create_job( + self.local_user, self.csv, True, "unlisted" + ) + import_item = import_job.items.filter(index=1).first() + import_item.book = self.book + import_item.save() + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): + handle_imported_book(import_item) + import_item.refresh_from_db() + import_item.linked_review = None + import_item.save() + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): + handle_imported_book(import_item) + + review = models.ReviewRating.objects.get(book=self.book, user=self.local_user) + self.assertIsInstance(review, models.ReviewRating) + self.assertEqual(review.rating, 3.0) + self.assertEqual(review.privacy, "unlisted") + + import_item.refresh_from_db() + self.assertEqual(import_item.linked_review.id, review.id) + def test_handle_imported_book_reviews_disabled(self, *_): """review import""" import_job = self.importer.create_job( From 47b98ad0d9e87f05eb569cb278772a2c3a91042f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 09:04:12 -0800 Subject: [PATCH 104/121] Track completed items on job --- bookwyrm/importers/importer.py | 4 +++ .../migrations/0116_auto_20211114_1700.py | 32 +++++++++++++++++++ bookwyrm/models/import_job.py | 10 ++++-- 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 bookwyrm/migrations/0116_auto_20211114_1700.py diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 05550429..438ff7db 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -126,6 +126,7 @@ def import_item_task(item_id): except Exception as err: # pylint: disable=broad-except item.fail_reason = _("Error loading book") item.save() + item.update_job() raise err if item.book: @@ -135,6 +136,7 @@ def import_item_task(item_id): item.fail_reason = _("Could not find a match for book") item.save() + item.update_job() def handle_imported_book(item): @@ -144,6 +146,8 @@ def handle_imported_book(item): if isinstance(item.book, models.Work): item.book = item.book.default_edition if not item.book: + item.fail_reason = _("Error loading book") + item.save() return if not isinstance(item.book, models.Edition): item.book = item.book.edition diff --git a/bookwyrm/migrations/0116_auto_20211114_1700.py b/bookwyrm/migrations/0116_auto_20211114_1700.py new file mode 100644 index 00000000..ff71b89c --- /dev/null +++ b/bookwyrm/migrations/0116_auto_20211114_1700.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.5 on 2021-11-14 17:00 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('bookwyrm', '0115_importitem_linked_review'), + ] + + operations = [ + migrations.RemoveField( + model_name='importjob', + name='complete', + ), + migrations.RemoveField( + model_name='importjob', + name='task_id', + ), + migrations.AddField( + model_name='importjob', + name='completed_count', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='importjob', + name='updated_date', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + ] diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 753662d6..953ae394 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -35,10 +35,10 @@ class ImportJob(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) created_date = models.DateTimeField(default=timezone.now) - task_id = models.CharField(max_length=100, null=True) # TODO: deprecated include_reviews = models.BooleanField(default=True) mappings = models.JSONField() - complete = models.BooleanField(default=False) + updated_date = models.DateTimeField(default=timezone.now) + completed_count = models.IntegerField(default=0) source = models.CharField(max_length=100) privacy = models.CharField( max_length=255, default="public", choices=PrivacyLevels.choices @@ -66,6 +66,12 @@ class ImportItem(models.Model): "Review", on_delete=models.SET_NULL, null=True, blank=True ) + def update_job(self): + """this user is here! they are doing things!""" + self.job.completed_count += 1 + self.job.updated_date = timezone.now() + self.job.save() + def resolve(self): """try various ways to lookup a book""" # we might be calling this after manually adding the book, From f92863ad3ec562feed8baf88e9bcac4db463fbd1 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 09:56:23 -0800 Subject: [PATCH 105/121] Notify when import completes --- .../migrations/0116_auto_20211114_1700.py | 32 ----------------- .../migrations/0116_auto_20211114_1734.py | 23 ++++++++++++ bookwyrm/models/import_job.py | 18 +++++++--- bookwyrm/models/notification.py | 7 ++-- bookwyrm/tests/importers/test_importer.py | 35 ++++++++++++++++++- bookwyrm/views/imports/import_status.py | 5 ++- 6 files changed, 77 insertions(+), 43 deletions(-) delete mode 100644 bookwyrm/migrations/0116_auto_20211114_1700.py create mode 100644 bookwyrm/migrations/0116_auto_20211114_1734.py diff --git a/bookwyrm/migrations/0116_auto_20211114_1700.py b/bookwyrm/migrations/0116_auto_20211114_1700.py deleted file mode 100644 index ff71b89c..00000000 --- a/bookwyrm/migrations/0116_auto_20211114_1700.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-14 17:00 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('bookwyrm', '0115_importitem_linked_review'), - ] - - operations = [ - migrations.RemoveField( - model_name='importjob', - name='complete', - ), - migrations.RemoveField( - model_name='importjob', - name='task_id', - ), - migrations.AddField( - model_name='importjob', - name='completed_count', - field=models.IntegerField(default=0), - ), - migrations.AddField( - model_name='importjob', - name='updated_date', - field=models.DateTimeField(default=django.utils.timezone.now), - ), - ] diff --git a/bookwyrm/migrations/0116_auto_20211114_1734.py b/bookwyrm/migrations/0116_auto_20211114_1734.py new file mode 100644 index 00000000..1da001bd --- /dev/null +++ b/bookwyrm/migrations/0116_auto_20211114_1734.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.5 on 2021-11-14 17:34 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0115_importitem_linked_review"), + ] + + operations = [ + migrations.RemoveField( + model_name="importjob", + name="task_id", + ), + migrations.AddField( + model_name="importjob", + name="updated_date", + field=models.DateTimeField(default=django.utils.timezone.now), + ), + ] diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 953ae394..fbec88ca 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -38,13 +38,18 @@ class ImportJob(models.Model): include_reviews = models.BooleanField(default=True) mappings = models.JSONField() updated_date = models.DateTimeField(default=timezone.now) - completed_count = models.IntegerField(default=0) + complete = models.BooleanField(default=False) source = models.CharField(max_length=100) privacy = models.CharField( max_length=255, default="public", choices=PrivacyLevels.choices ) retry = models.BooleanField(default=False) + @property + def pending_items(self): + """items that haven't been processed yet""" + return self.items.filter(fail_reason__isnull=True, book__isnull=True) + class ImportItem(models.Model): """a single line of a csv being imported""" @@ -67,10 +72,13 @@ class ImportItem(models.Model): ) def update_job(self): - """this user is here! they are doing things!""" - self.job.completed_count += 1 - self.job.updated_date = timezone.now() - self.job.save() + """let the job know when the items get work done""" + job = self.job + job.updated_date = timezone.now() + job.save() + if not job.pending_items.exists() and not job.complete: + job.complete = True + job.save(update_fields=["complete"]) def resolve(self): """try various ways to lookup a book""" diff --git a/bookwyrm/models/notification.py b/bookwyrm/models/notification.py index 2f1aae4f..417bf759 100644 --- a/bookwyrm/models/notification.py +++ b/bookwyrm/models/notification.py @@ -157,9 +157,12 @@ def notify_user_on_unboost(sender, instance, *args, **kwargs): @receiver(models.signals.post_save, sender=ImportJob) # pylint: disable=unused-argument -def notify_user_on_import_complete(sender, instance, *args, **kwargs): +def notify_user_on_import_complete( + sender, instance, *args, update_fields=None, **kwargs +): """we imported your books! aren't you proud of us""" - if not instance.complete: + update_fields = update_fields or [] + if not instance.complete or "complete" not in update_fields: return Notification.objects.create( user=instance.user, diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index 6996a92b..3fbfa264 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -145,7 +145,40 @@ class GenericImporter(TestCase): self.assertEqual(kwargs["queue"], "low_priority") import_item.refresh_from_db() - self.assertEqual(import_item.book.id, self.book.id) + def test_complete_job(self, *_): + """test notification""" + import_job = self.importer.create_job( + self.local_user, self.csv, False, "unlisted" + ) + item = import_job.items[0] + item.update_job() + self.assertFalse( + models.Notification.objects.filter( + user=self.local_user, + related_import=import_job, + notification_type="IMPORT", + ).exists() + ) + + item = import_job.items[1] + item.update_job() + self.assertFalse( + models.Notification.objects.filter( + user=self.local_user, + related_import=import_job, + notification_type="IMPORT", + ).exists() + ) + + item = import_job.items[2] + item.update_job() + self.assertTrue( + models.Notification.objects.filter( + user=self.local_user, + related_import=import_job, + notification_type="IMPORT", + ).exists() + ) def test_handle_imported_book(self, *_): """import added a book, this adds related connections""" diff --git a/bookwyrm/views/imports/import_status.py b/bookwyrm/views/imports/import_status.py index 2d18d656..7e7d5179 100644 --- a/bookwyrm/views/imports/import_status.py +++ b/bookwyrm/views/imports/import_status.py @@ -24,7 +24,6 @@ class ImportStatus(View): raise PermissionDenied() items = job.items.order_by("index") - pending_items = items.filter(fail_reason__isnull=True, book__isnull=True) item_count = items.count() or 1 paginated = Paginator(items, PAGE_LENGTH) @@ -41,9 +40,9 @@ class ImportStatus(View): "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), - "complete": not pending_items.exists(), + "complete": not job.pending_items.exists(), "percent": math.floor( # pylint: disable=c-extension-no-member - (item_count - pending_items.count()) / item_count * 100 + (item_count - job.pending_items.count()) / item_count * 100 ), } From 8cede05d32895d2a4c73e3f208b13841651a881f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 10:20:14 -0800 Subject: [PATCH 106/121] Retry hanging items --- bookwyrm/models/import_job.py | 2 +- bookwyrm/templates/import/import_status.html | 15 ++++++++++++--- bookwyrm/urls.py | 5 +++++ bookwyrm/views/imports/import_status.py | 15 +++++++++++++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index fbec88ca..387261f0 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -35,9 +35,9 @@ class ImportJob(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) created_date = models.DateTimeField(default=timezone.now) + updated_date = models.DateTimeField(default=timezone.now) include_reviews = models.BooleanField(default=True) mappings = models.JSONField() - updated_date = models.DateTimeField(default=timezone.now) complete = models.BooleanField(default=False) source = models.CharField(max_length=100) privacy = models.CharField( diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 61b7b5cf..6c7d54b9 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -41,7 +41,7 @@ - {% if not complete %} + {% if not job.complete %}
    @@ -167,8 +167,17 @@ {% endif %} {% else %} - - {% trans "Pending" %} +
    + + {% trans "Pending" %} + {# retry option if an item appears to be hanging #} + {% if job.created_date != job.updated_date and inactive_time > 0.24 %} +
    + {% csrf_token %} + +
    + {% endif %} +
    {% endif %}
    +

    + {% trans "Import preview unavailable." %} +

    +
    + {% if legacy %} +
    + + {% csrf_token %} +

    + {% trans "This import is in an old format that is no longer supported. If you would like to troubleshoot missing items from this import, click the button below to update the import format." %} +

    + + +
    + {% endif %}
    +{% if not legacy %}
    {% include 'snippets/pagination.html' with page=items %}
    +{% endif %} {% endspaceless %}{% endblock %} {% block scripts %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 6f658016..514bb7e6 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -244,7 +244,7 @@ urlpatterns = [ ), re_path( r"^import/(?P\d+)/retry/(?P\d+)/?$", - views.ImportStatus.as_view(), + views.retry_item, name="import-item-retry", ), re_path( diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 1a6fbdc6..d79de424 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -45,7 +45,7 @@ from .shelf.shelf_actions import shelve, unshelve # csv import from .imports.import_data import Import -from .imports.import_status import ImportStatus +from .imports.import_status import ImportStatus, retry_item from .imports.troubleshoot import ImportTroubleshoot from .imports.manually_review import ( ImportManualReview, diff --git a/bookwyrm/views/imports/import_status.py b/bookwyrm/views/imports/import_status.py index 54174082..8e07a171 100644 --- a/bookwyrm/views/imports/import_status.py +++ b/bookwyrm/views/imports/import_status.py @@ -9,8 +9,10 @@ from django.template.response import TemplateResponse from django.utils import timezone from django.utils.decorators import method_decorator from django.views import View +from django.views.decorators.http import require_POST from bookwyrm import models +from bookwyrm.importers import GoodreadsImporter from bookwyrm.importers.importer import import_item_task from bookwyrm.settings import PAGE_LENGTH @@ -47,14 +49,26 @@ class ImportStatus(View): ), # hours since last import item update "inactive_time": (job.updated_date - timezone.now()).seconds / 60 / 60, + "legacy": not job.mappings, } return TemplateResponse(request, "import/import_status.html", data) - def post(self, request, job_id, item_id): - """retry an item""" - item = get_object_or_404( - models.ImportItem, id=item_id, job__id=job_id, job__user=request.user - ) - import_item_task.delay(item.id) + def post(self, request, job_id): + """bring a legacy import into the latest format""" + job = get_object_or_404(models.ImportJob, id=job_id) + if job.user != request.user: + raise PermissionDenied() + GoodreadsImporter().update_legacy_job(job) return redirect("import-status", job_id) + + +@login_required +@require_POST +def retry_item(request, job_id, item_id): + """retry an item""" + item = get_object_or_404( + models.ImportItem, id=item_id, job__id=job_id, job__user=request.user + ) + import_item_task.delay(item.id) + return redirect("import-status", job_id) From 8612cf654d2edec8b06abbf837bb4094d4673507 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 14 Nov 2021 11:31:47 -0800 Subject: [PATCH 110/121] Invalid href --- bookwyrm/templates/import/import_status.html | 2 +- bookwyrm/tests/importers/test_importer.py | 46 ++++++++------------ 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 6370b866..ddeeeb31 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -47,7 +47,7 @@ {% trans "In progress" %} - {% trans "Refresh" %} + {% trans "Refresh" %}
    diff --git a/bookwyrm/tests/importers/test_importer.py b/bookwyrm/tests/importers/test_importer.py index 3fbfa264..5c3b2031 100644 --- a/bookwyrm/tests/importers/test_importer.py +++ b/bookwyrm/tests/importers/test_importer.py @@ -1,5 +1,4 @@ """ testing import """ -from collections import namedtuple import pathlib from unittest.mock import patch import datetime @@ -104,13 +103,9 @@ class GenericImporter(TestCase): import_job = self.importer.create_job( self.local_user, self.csv, False, "unlisted" ) - MockTask = namedtuple("Task", ("id")) - mock_task = MockTask(7) - with patch("bookwyrm.importers.importer.start_import_task.delay") as start: - start.return_value = mock_task + with patch("bookwyrm.importers.importer.start_import_task.delay") as mock: self.importer.start_import(import_job) - import_job.refresh_from_db() - self.assertEqual(import_job.task_id, "7") + self.assertEqual(mock.call_count, 1) @responses.activate def test_start_import_task(self, *_): @@ -150,28 +145,25 @@ class GenericImporter(TestCase): import_job = self.importer.create_job( self.local_user, self.csv, False, "unlisted" ) - item = import_job.items[0] - item.update_job() - self.assertFalse( - models.Notification.objects.filter( - user=self.local_user, - related_import=import_job, - notification_type="IMPORT", - ).exists() - ) + items = import_job.items.all() + for item in items[:3]: + item.fail_reason = "hello" + item.save() + item.update_job() + self.assertFalse( + models.Notification.objects.filter( + user=self.local_user, + related_import=import_job, + notification_type="IMPORT", + ).exists() + ) - item = import_job.items[1] - item.update_job() - self.assertFalse( - models.Notification.objects.filter( - user=self.local_user, - related_import=import_job, - notification_type="IMPORT", - ).exists() - ) - - item = import_job.items[2] + item = items[3] + item.fail_reason = "hello" + item.save() item.update_job() + import_job.refresh_from_db() + self.assertTrue(import_job.complete) self.assertTrue( models.Notification.objects.filter( user=self.local_user, From 77ee1147d52133a02e4e0a9647fb892f6859bfba Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 09:03:00 -0800 Subject: [PATCH 111/121] Adds return_first tests to book_search --- bookwyrm/tests/test_book_search.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bookwyrm/tests/test_book_search.py b/bookwyrm/tests/test_book_search.py index 4b9a0681..16435fff 100644 --- a/bookwyrm/tests/test_book_search.py +++ b/bookwyrm/tests/test_book_search.py @@ -57,12 +57,24 @@ class BookSearch(TestCase): self.assertEqual(len(results), 1) self.assertEqual(results[0], self.second_edition) + def test_search_identifiers_return_first(self): + """search by unique identifiers""" + result = book_search.search_identifiers("hello", return_first=True) + self.assertEqual(result, self.second_edition) + def test_search_title_author(self): """search by unique identifiers""" results = book_search.search_title_author("Another", min_confidence=0) self.assertEqual(len(results), 1) self.assertEqual(results[0], self.second_edition) + def test_search_title_author_return_first(self): + """search by unique identifiers""" + results = book_search.search_title_author( + "Another", min_confidence=0, return_first=True + ) + self.assertEqual(results, self.second_edition) + def test_format_search_result(self): """format a search result""" result = book_search.format_search_result(self.first_edition) From 83e468a4f817ffcb00324ef099a8c8db1fe7e240 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 09:34:36 -0800 Subject: [PATCH 112/121] Fixes "indeterminate" state of progress indicator on screen reader --- bookwyrm/templates/import/import_status.html | 2 +- bookwyrm/views/imports/import_status.py | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index ddeeeb31..61763ec2 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -51,7 +51,7 @@
    - {{ percent }}% + {{ percent }} % {{ percent }}%
    diff --git a/bookwyrm/views/imports/import_status.py b/bookwyrm/views/imports/import_status.py index 8e07a171..26ff8cde 100644 --- a/bookwyrm/views/imports/import_status.py +++ b/bookwyrm/views/imports/import_status.py @@ -32,20 +32,25 @@ class ImportStatus(View): paginated = Paginator(items, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) + manual_review_count = items.filter( + fail_reason__isnull=False, book_guess__isnull=False, book__isnull=True + ).count() + fail_count = items.filter( + fail_reason__isnull=False, book_guess__isnull=True + ).count() + pending_item_count = job.pending_items.count() data = { "job": job, "items": page, - "manual_review_count": items.filter( - fail_reason__isnull=False, book_guess__isnull=False, book__isnull=True - ).count(), - "fail_count": items.filter( - fail_reason__isnull=False, book_guess__isnull=True - ).count(), + "manual_review_count": manual_review_count, + "fail_count": fail_count, "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), + "item_count": item_count, + "complete_count": item_count - pending_item_count, "percent": math.floor( # pylint: disable=c-extension-no-member - (item_count - job.pending_items.count()) / item_count * 100 + (item_count - pending_item_count) / item_count * 100 ), # hours since last import item update "inactive_time": (job.updated_date - timezone.now()).seconds / 60 / 60, From 30afe42b3ad0b6c78615187bc246d17b9e90f9f4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 09:41:05 -0800 Subject: [PATCH 113/121] Removes extra space in progress bar --- bookwyrm/templates/import/import_status.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 61763ec2..9b437969 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -51,7 +51,16 @@
    - {{ percent }} % + + {{ percent }} % + {{ percent }}%
    From 905035011343cf4c45b8819af7a1d3229c417db1 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 10:18:46 -0800 Subject: [PATCH 114/121] Adds lithuanian locale --- locale/lt_LT/LC_MESSAGES/django.mo | Bin 0 -> 61077 bytes locale/lt_LT/LC_MESSAGES/django.po | 3884 ++++++++++++++++++++++++++++ 2 files changed, 3884 insertions(+) create mode 100644 locale/lt_LT/LC_MESSAGES/django.mo create mode 100644 locale/lt_LT/LC_MESSAGES/django.po diff --git a/locale/lt_LT/LC_MESSAGES/django.mo b/locale/lt_LT/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..9d11576fd32656bb9d97df42d532f370e888691f GIT binary patch literal 61077 zcmd^|cbpwn`TwtU5_$&#C&54h*$trxETNz0+xS%_m$cAoHD1M=RBv( zOUrbG{?IpL* zEeP(TpvrkWsOR4Tw}KPl7Vt1Q8qR}iiYGv&?_wx_ABIZzjZpc#1uC37;1=+HsC0e< zDxHr*rRz!Ob5QB~4b*f0fQo0-F`+)EK(&(++y-6-vbri#~@uacRy4(PdJ}} z8(@AOD&4<^O6Qvv2LD^YEivx}m5zg;{Lh3c=cA#@aWPc5Cqen^gj>ThR6Z|&dhSxV z6}%Fv9zPA$PVa{Dw*`&<61W>wxVJ;u-whSsLvH^#R63t>^Dm*&|68d1Z$Rf(x<^B$ z=gm<5CqRY!HYk5np!`jTN>>|HK02Y=K`&If*4+MVsB~Ne74B8eo80~$xC{1=z-{19 zoqvD|ca!5nd|N@q7eU1{8A_hDK&9{PQ0aRo+!l62rT+}5c;5q6{vUPw8==Z+B~KEe>*cCRF>YLZ#zbsPyKJ58-VBRSvsA)!SR4(lg1;Q=rl{ z+s(&8rRSYc^-}|BE2kNCmq4ISZRJ~jSRgWv(JO~xv_o4hf=lnI4zn9&-34@*D z-wG=H9iYO03sm_|a2^I#ZpT8k_2q4Mz%RJh-T%Fk0!<^Cen zbL*WP(z7X4{&$C}k7hR?36-8!XyxMO0#yDgZhtmZxF3WH=Mzx*z8k81z6h0$Z$ahj z2T=9-B2@eO6IA#co)XO4L6yr`sOQH+#dj#&56*^GFHq%qE>t={3U`CoLzUMmw|@w# zyuJw)&JW%Fd8qh*4HeE`+Zob-i15|n63>ELa zQ1LtrRlbiwrQ><1^7=hg`KYLrcug}Ac;aA}P@Z0c6xIrPrHxnv73!vgV&Uq@-a|Nh$o(44@oefov zS3|YiJE6+)VK@H(D!dor&hTZZbZt)JRbG3-P2sUn>3#=P{@>~BgZp7#0abptL8aqC zxC#84^LuXpG*tcn3@YC*!*Ou^p77iR=VYko4}nVWG^lhef=cfxa5T(AwU5)_Tj2#z z{o&KlTyN{a>fy&P; zsQ8X^`#e;6mZAK=-+47u{5M1SU+LyAx%pwJa(u%1Yp8VR`oeP?!96f;4&`q$oCIe< zmD4h)^uGrxf0sbDgR9^KcsGR!=dtZwDUx$e!T=L{EMN& z{|HomJ_bJsZ-f)!?eI*v75os?^VdU_^KH)0LzUwLQ0aIMsy_Y?Djge^LwNf@ zrF(yh;x5PXFN-iG) zHD0zt^_PCA^qm2fj*FneyBzAdYoY4vHaFi5C1(bq+SAie{@1UDbdQF5ZaX)>73%pW zsPrA+_J>22&upl2DL|8BP~lw-m7W`+p1T7o-20%)`)g3;^nG|Dd=|=oOD&Y+QPA24 zRK7c*{GS2$h8MslxDp->f8-p$EXeT+lpc5^oB_WKmG2FghxWJ|+y?W0P~+!JI1RSD z`zN8w_dclhzZ$9>z7CbY@4EdDq00YRsCN1yR61XB{uL_!>z^K;+uXT5RC(+Mw}nkm z&rO9pz@yz9Lp{IL%~h!9&T{hypwjbEsPelGu7;n1N`HAEtfQR;<^Cj8x&FfW3fvC! zn^uH)c7RGp1l2w#xcxk+a###i4ky9AU!2`e?~~h4p8N>4^+7x0p)KlR6Hj`)oT}2`@H}vKes}q?+Z}nu?p@CABKweS@BL?+zJPQ<(vD&JQ? z)$=Ey%4?;Y{}+}qKMdai4>>RRyBsQi*F)9M?NHC%50#FuLdlmOLxuk;Tm&~eKh%2+ z748zK{67WvhT|^?<<|yP{wKouFop}@N;m%#s(j{M$d~}nhLhoWa2t3x+z>tjRUY4j zs`np3)x+~p>Du+(#0e)rg})H0oNj>A;fql5?|V@&zY9*q{2)|#8@z`$1viJv|A|of z$vf|K_gCS**w1}$kRxY8mB$0l9WD;***)-J?7sw+k3U0|^XT`5adBt3A?6mi2|N<2 zoaVUwv2Y*E$2-r5AHlp5%Kx7o6Her4o zDu3%=72@3;MwkzU2g8%#GI%*G!_BVFX9qk0^K$q$_!+2tJ_noOrvDS#*THZb%!fm@gJYd1 z!mTjJZZ1Q$hZWBA-TwV>2kb9%^Q}@Fz(_d$jK5LEws!udSh7V{sSqplC`yF>YJ zhKheG+!`)~JHt-6BYYQBydQ;=;cZak-jAW|Ux3O_?uHP~CUAF*J42=8V7M7P3T^<8 zgKA%Icl!cV`j$cYzZmWfuZF7c)llhq8Oq=0G{UiP3{?A=;cSHp_avzF^uP{SfqTO5 z!^7Y|q2ihH@$mdSco*gqpu*qm6QNuqI12M#Q02QH+yx#ABe)p887_DCOQFi;8n_|+ z6nqQ(3{*LP1CE71hTFmQJ{iW#9ij4ZAXGU`g^F*1+aC{=-VS%KxcxbBL+sxVRZky* zs=r&{+u;|X`tOdPA}z3qKO4cvpyGK7D%@YX`Oi@4+VG}Oe!D=K$3o@nAgJ=03RTX> zxw+HbPj{XN)t=rDm7Z&%%5f!BKJSBy|6wpojcwf>U|Q7u%81J|5CSq z7u*>0l~Dckdbht9Zi4xHP;%*MsPz8@ZV1=^bO>itsC?|^=7~`0JrXMY3*EiLS$3Wa zXyv(_Qc&_!T%2=57h{xCyG>FM*rFyP)!MKO6Zp~~lR=krkU{K?Imt_=Cw1)AJ+PKA1Ik(={S?eTOsf7tm6sCYjQ4}?#^(Qvan zg8fcV<#rHMy5>TaOAI%MMYsh#6RJGl2URa0fy&=4Q0e*#l>euo{J-G#e}k)T<9&U+wi#vn+?oj4& zQ0?bHsD5}1RJ_Nzd%^7opyE3ls{Ss5Du+v;;=9)EKLNMFd>d4Ge;F!&PeRr6D^Ts= zpHStz{aqoQW8l`9kA&O6g;3>~hg-q3pyK%;+!kIARUV&)2f$TO>3bQfeEtTNudVM6 z{d^xN_XSYlEQZSe$xz|}Q04UlI14@lmA`TK1o<`z zs-EXT)!#c{1iPTZzX)y%uX6j_-2PFha(fo)xxYY#yTRu|xorWPFpq-@rwwig^Kg5( z0xH}~-2M~Jd!3I$h4&m(et+Zke{uUQJ|D_|C#ZVc2g=_pxDQ+i_k{0)iuaT5ejk+o zhoRE-6R7fi8LGV3|3WDLQBd`=4V3?hP~*XDxCnN_aqwQK@P7=IpI4y#Z*p%4Zx5*O z_lBz17ASwyoQt8-u@ox5XTlxfyP?9l4k{n_Le=ZDQ0?FasP^|)sP?`2ec_zaE>QJx zJXCqKLxo>-`*Wb$%lqKr@Y7J~`6b*N{v9fvd;M=n_awM8<|Cl$?L?^fOK=7}2P*#6 zQ2qM5Q1LzscY-g%9pHvv4BQo}9QTK+r&g%=&x8v15~%XJ0V*Azg(}~Bq1y4Ia6Ehr z?gIY-RSrAdAHvxOsy!b8o8bcISy17A2EGk`5h~u7p~`3DRUw|8VGHJFsBjB#S9qqo zUj-G;?NIG|6;%1W2={{g#T(a{CfgIey*k zpM=Wy&!F1pub|q&OK=7JD?9?$zD(PN_dtbvFI4+{2rAwepwjs&l>5#Pgy;5v^DuWn zmH!=3>3IMu-`{{r&kIoT{s~4f_h1;`$G|Zdr$V*c4ybZH1FBp<1eNX^pwe?Ql)p!z z>i>DD^lbA`h<8`0a^DMDe}O93X;AfAgG$c@Q04UzsPtav{4~__cSEJ)5vY223M#x! z2SYx#hKhF=_-6Q4sBn&i@;4VM{5Gg?PKA5G3RJtk0xExZKt1;$R6f56_55Q{&p++% zzl5sym!bNDCeIPr5z78xsPdl)74BlFd@g}%=gZvv!%+Tjgk#}KD1YCDCLf$HK!x*r zsPt{{aLC7&Q0dN@oo!+$*8{4?>m0bI!j)wWF=S62jl#c?48Ab7ApKlnXov^XjjLeE$Q= zee~A?cYu-?W8C~UH&2C|V80lueVhu_kIQg8JkRZKh05nDxBo7bzrR71!|1Pv{2c-n z&RpjyQ1x^g)bsC$qu}*0g117|?>FGa@JCSL{v4{@{{TDSt5D_F@s03&4Jy2IoR>kB z+s#n@;Y-d(oj-yR_Af$(zxg*qeeVYM!u&S489W6l+#Yv7+s#)(mEUKe^7jCI3w#W! zoL+&FBfC8s@^cWB|D)h;@MNg^J;TjcJHG(;#Qq7m1^fq8`ZoJk$nUOD_0#O;gP_W3 z4%`wR>-I66h`9pg|9a=Aq0(_D+z>tj_53&C=J0X25qu6#fWLxj|J!{#q< zAHs2P`|pN+a44LE`DCc_zX__o9)VlKr=ZIDH?R$E_PrqI-w74pGB_Gu2vy%7cJp;m z`Md>g3-5wfUQqe{Ce-skfGW3NxcwiX!pl7t^1B(db^ui_2SJtRRJbiX5h|WOXz795 zVZIP5oU5VI^(nXwybBUV?qN4y{dkxk-Ub!kmQRFy?*ZliK&bY16jXh6LDj=D*aRirR@_V6fF`*{K?ed~We_}dIBKf6Qa<6yWQY=tWSJlp|Z2vx7wK#d!phf2ry zpycICQ2w^~LFk9$pyHhc)&3X2Nw5Z$zE47x-{+yq=>ZtQZ$p*;OYq(BRVaV&elqY9 zxFhDPq4IYp)bsbkS@0|HFu3Ip!+dc* z>;0I%4Y!4FhZjNl`zcgBzk^Cw?%5E|_E6>hR=77j6slaipz?P*RQdl1o&$dlKLWdc zLRrASK$YKFKMn2j0;qT|fycpL!;|3D=R!Zd4!#NVlTi763aVa4|17l2?VaPH!aWoo z0H?YAa;WDnh6?{acm&+)=ja}=70!hphR4DmLDlD^=R-Wp;0c(|fxEyb;Uf47JO8U9>{gJxgz_ym!f<*ScLl}n4gAc z`SbF2I(F?Dv+VTS8oN*6_5l1`#{ELv{($`)uIx|kbTT}akvxLAMkfK=2N-!Q+{{H{%EdM*zExIdw@%2L=om_PPq-q-*?;%V{R+%-wnS8 zkL9`DxG!@>nE%(q{vrGTX8zf41~)x!n1{`$xo*e&1^oRk@r2zW-2VVOwb?0d|2OWd z*u6XB??udNpI6C09OU{Y_XoiPaJw3|LjC4&b=X~yMLUz9FSz-5o_UV@Kf3vB%v<0- z#h-l-{J!6R5kDVxf3uuN!_VUW7M{~@Y=E`3o7_%TPvPfNy0ClN!+RF{%`s1u2mdX= zZ2-Fkm`8K{iE9I{k7NH6_z-Ry*Y<&rbNvGQ)8OB^&g7cL^(X9#P``I_ZB2f@gWZ;H z_YLmv9cfg-<{STLZd%)wkcE@df z!Z-xGFLVDZu2Gov`w;FQgMY?OzkM-J@{BGd>bPS*8I*R*`arL?XOPzm%Q*hq| zG9={ggIja$%>CxXwJrCDd6-{;*Kp0lF8Mv^ZvAj`EZ1}MW<2{g?mx(-ce+;d%<|+p zcpBI5xaM*Va($9Zza8Lo4@2hlFdyk|GXDycHiRu7OqKz^-lOv?DRXG`(r%bV8gu3 z&Fxt1#dQ|u(fIq^5P$!J+kMzy%eAXNd^~=>hgrXUxW5Trg84mM3pvc)kqojak2QxxWXViFp}Z0q0}) zL#`dUE|4dFr*f^2+e)sDsN;ih`zv;Dg8J>}EWtc}-YkDyZI~n6&*#4Cem=&%ey3o5 zg6nDSpTVvfeiMEj?hAL}`Wp9Nhx+{izh{Ko+^*QY?DqR%wWcVJgRoKmjC%}o=>$ffCbu#8HF&_#y#BKvvgm1>}M)y~e=QaF3f&C(`dwEWC zsOSBeEi?X4!tSeFcjISg-Mia#?mMx&mFooTevP?>`|NL9%>UrJpX(HN_$c=~aeuv= zzkpf4pTI8?#<#fiyOwKv@}b{WZg&p%2jFje_-n4cxUb;01^3BsQ=VCh{kQ#@``qFC z&R=4`W8&F)IR5qfDQ>%R|K?z{zrV!19%23&-iG^6-R)i6-^|tP=63G&i{KqxU*bBB z>#sbQ{BFgp-+5ebwL9`g*w^^|&*69Rb38nU=dSl>tM2{)ZmYSU3On5{!Hf8v$@M~F z0jK%><@i6C`wek#gFoc@EZ3`C`uzj<8(-_n+MVp6gAd>jmug z#Q*Q%`=Neg0_<-;f1kwfX8!E6-0#ozyqo_B|H8EtyMtjT9PiJc!2QkKf7i{zr?I;c z-iY77a{nmT(BFG;({B^5_u_t+nP@|9KR@%#F4$d#dk5SaZjo{G$8tAe_mRW^wbDJcC&BG8f1dlp%$EGaNq%2;zsoW2kDpuN zdGM25N4eX#-S0z~oBY0m`@Ohk;V;j1D%T9muX0_2pO>J1f5QF)+;2i2-syH9h8tkk z?;f|){hfY)IQPBW_wwvGx7!&1yJ5bE>rC7a!@kD7epkRbet!t|AHeQ0%>M`P;Ch7X z9b8|{R&(kQQYtqt`@HAxZa2Rqu6i4TDSq|anESV5w-5JkgWGYHxN_LPi))td`P~I4!;`T48uwp@|BLxE?k+zQ zu=^s{SnOVbzrk)N5^*p5Eaq*nZ|2&L`(3dAB=+Cn+7P>MLjB$wkb4e4@8`M`a~C`r zKl=TTQ}0(u5orponZeizn|e0&c*&iTyH0w-QE5a%)jIQa_qj# zbsN|1m>i^=I7pj#8~y&6g)sqOMYJZ)thHJfdY~1lvNf8{5u8rN1{G zIMo6om_l(`p_-4%`O|9oO7%58DBz(%l=|~U0_ZQ5M|wWvI?}_nie+JFn6j4lu2Q)# zu1=^-jK+=ckGt~~l59-fs`<_|Gwr2P&l(*R+>*Gr>(p{wr7Q@jQjJx7!!6~tH!gP9 zJjUc6>%LmG7WXO{Nd-g$rJ5T?bgzu);Z)D@YXvldzaaq@Bs3u%an(w4|KxI&7@+UtKbxGWKwC zh)deIN?pXF0!?i!V^b!@hpSXGfflSLLiMbr=ot`iQg=CDspQ5}3iVo_ zAagmttdL)x<~{7w$n0~dbxR={jdQudc)&w~%%+r$k^2N@4XH@7SFO6+cLIYom ziP0sC9;z%UEmwI`+4Ni*WYf6u1fc*&tUhlKs-dWTV7Q-2`Obn;Xmy3>j@?Xgw^Z4}TOqeSid3myq$eYDN`Y6!)r<2crDx#+}`}Md@oOgl7y-nN5!}-?+ERnuT-oqaT`5X zEuirZe-BHWx-=dX&I`l z9_cL697q$lg(9M=*g=XgsA~H1eeI~WW6Z+3MJIhbgg|5JrSRlL<7pqr*5g`5Pk6$z zU;`RTU!mAlG7B5er*`&Pg7_QiK~5M_1u!?Yw-8tIDu&)(UC}&MquqHHvFj<8mKR-> zk)J*)L@Kg*Qwq_FHRM?TOR}D}=FWVq!j~wcdGPpaK2gp$H9k^WK4ijTQDd7`AaYa5 zXRk)7z84;f%%eK;C?J$}#z1hzYN2U*smyGKgkq$npuV9ecXbsyGTE!Q zgOI^)Q@jt@_W;sT=|`!^N6VKWCaUED=`7SOonH-D+F(EGiv~-{j<_F1qQfK%#Znw7 z74uO?TvRIyDqs*Y^q|l<>OLy9ZVJB|d`*ndfS2YwOeUGYAmDNd%`i8W7zU!QLN9WN z9GW#*E9Xs8Rcd}CX|1?A^V0C6DNGJZ#cp&J)l(uI-4gYb>}WE2M6^iKR`nzi8B?01 zC?C~|o#doBT2zWiMh7xyV#L&eMix=(i9k%{4)MCI4e=8grBPwxp@~dxYBh3&lond+ zER@tJxl`YDL~}X`xX@LwOq$DD^pWHtO?G^S6td_JloKsAF1O^S#T`BNM`e>XYO4!5 z$+p3O>S}W@pHf%SXM+f?o2~ihrWNR?(+Xt-sw4sTy(LAczeqi2bewuuYC<+x;k)l3 zEo$^$6JYw&P#R{jNOL9gaQr~IFIrM!SW0xEN_C(&f7sZLQg5l;678}7eh2N}*>UJt zpAs#pR{JY0lO{3sH^~+zM;TeIVWl3j=Zi!rB3ep?310_|TEizzrK_CJ(;nkxjG1wJ zFKy&RX3%Q#w18^4&|ahUF=q|s#S;~lCK{BsBthK_L$&s1G`mTCC8BL2NCtr9IN^yL}jLUAyj4=|_j>c;~j*#2sP05uasx!97M9&3Kx zRVa3b(OTI#Lbe@z7i9VpmJ9MxtB>Hh>9q86+U0`DXRXsU&&Gqzwx_F15Cqdp#m-ts zHJ6wv37Lu2rb%+pbjdAhMU7xQBi$rnq^w1Ako&34OsOTQ$p+xml=M7BVc~>a`6wMU znZ&8C{+RD7ojp?ZBcn8~5I|Lu)EO<0%SCnCA?EO`XQI~2c!;F1@{A)itd1qt58KHF zPiick&iIH74v4aHI{Y6WQJwb<~2k-Z*|IdRU`0rQ3wChYt6z)4RIdnjW!4^~FU5-OyMp zr}gK|mtH$aF2sXP7 zvcL@a$P^vf(~?ZvL*NCIG@x+nV$_TSmE4R(iO^_6`>2%Ynpmddb4Ff^Rb`e)R_K|0 zS6u65@SsyO4xxQ?49LGmI7eFj!8Quj?*^-Wzo)q}x`dHh39(T!w28zJxlZg?gEW^4=cu?woxGV9&k{N|chqW_kLPHk1(a55>D}4+giKZ&EmcJtvfVrBf zpOrsNB+<`uGfEvbNm`S6m>7B%Ag5-Y-cL3#n`$2rwcAh~41`v)1y~lTP{m9$qr-*4 za3%{)IsJ)PUzCJPmVU+t+XmTY{#>=m+_wa;lJeos*6_^3Y?D}aYhustSj;ooWQHIC z?KZS;yQLY`y<{4yIfj-UZbQ4)J*CfBtfkOwHY`DjQ>H4J&Gw}8Y_e`~Cbcj`g@;Mg!LDg^b(ux?W4c0(-=*wg2QsR^5cKBsa5MMuq4t#iondpc1Sk3Di@3 zY@b=_k01JMsW%$m)*mw;S~4Lwa~aj|%2=rOO3sMnm_sJ%95b9c9kynDt5AH zn^2DE$ZU^IEEk5IC6XF8tZWQIs`MpK6Bt9KlvnqGo}1n^m5RLsZnOkiAhyJUEsI3u zDA7JWE6I$h8<}0~2K3M;-M=Bv;+xQKi0oPJilJGjm^tZuQMfpRHTA|~ZuSp1 z{Oz*zbQU@#Dt%}VL(Z^Ou!-hX(}F^GvBVtJvY(eIXwJW`24s_J_NFk)uRKL$nhMg`?k(|117?D#8 zjd!WDJW|ap4d_nNE@o_t=FBi7dcAC=-q*{sVV5|n=1->!1y7`AM%!xErm zY|t2wkPoXoxrH%ALrB)A28JVYN0*iqBZl0G(a|xhAZ>lj@33Twp+9xS(@YCW%gq}3 z!NipAO5303o3<_K9b#o(jO4O{&WHO@RW8EJRjD#Lgl{HpMOH^CkaQqR=9~2i@VN1@ zmMqh;%}k_Y$fUSA8{7zfEIK+VJGST>2w-Bz*ll8CUZK4lmj@_0iqeG3yh0CI;7>Ao z&0`T63-WhNnZYL9+608bn84-2fM>(DnJ6OchKh`N=~xyPxN2ouhFQ|bQJ;NTuOv}{ zfU3s5rEccrcc!#%4O$=VQ3-6)X`_47jLYz)om@)l);FA7r(o=Z`sFYdK8 zbhA1#Kc@1F46EH?B8qxe!;)giPQs$jjOM4fLUN@q*@A~@V$D3KnUJri1jEp2QGG^K z7ptbM1n*^q!65*xVfY$d#wM(;)%Tl~{KR)~WAyg38#K=O@#!kMN^X8w7*6GXM)*s6 zu?3Jgv<%-HHu0nfS@l9IW}cr;^Gsa06Nssu~A zKxnhpdDXP>iKW(-vo2wwhP=qJWJ~3W27`1+?efpp7-mXX5NH}$>kAMI5GOe{edxK`mL>n5q)FLsTYH}4TuKFEMLL@lABmS?asbI@ z{m~S`V@mz(B^^VbwLes^gqy@AJ&eN=oeYw4?G0mzjqzEt4J+ZCMkRMl*_YC5uThf$=uKqG667f>3D{Yh#(MKV;1_`wdS5I zreYVC)DuRM=@m>`WuQ@0VkXLL6^WKn9mz?}Z3O#Hvtx+>k%x+2WW^%ax@4fjI!|xJ z6q>|Hxa3-UW33LXv2t^*rT$tkYrX{2pa$SjuGYG158YCUnxbjtxWZ2Hc(|g#mRm7$ zXO@a4;57`@Nj1wQ(`&`ydu(s9Pj7JgQHj%OJ%=V+NOCDa z>AGXYkv;x_Fhn|~wej~wDUSBwCj8v7WtWCJx%@Fq?^1ofEbFXya>x2OC1%#?+A{L@M8g zYI``f9Ox%)c^w0YtElW6k$pj(Wn)H{#=5Mp0=A)+vtDj%JOp3{*ZgJds_X4-k~y7W zdEL)q;G-^wxUXW-A5+8msE?Ukfh~BQAJvYk9pX{6F~@b<@~nQ5NY{#ju|?Lba7MOt zib0`C=aLw+a@1lsgYbZB*R;{SIM~r;tCGn7AoOhN9uA;RG<^nf#>ROvc?&xk)g?@N zyO-!7lhhA+SE{NOmu|wsN1M$ksBSj?wXBy}{lIED>nofq34@9JEKirT%+Sk{Rj}3s zKinm$v^@25e1&5pq*&uD`M1^lurw6z8&~$-M_AqPZ5JLMvi^@R*031lAQe&1Oi!Ud zD05OQb8V%rDto7-%Wrs2>uwx!pk$bZqg$wK)RE04EDbD|UcX#tHO543rE-~}!;Vu) z1E&aOGbS9;2*XoQ?O6FNY89Jp>vA|zoccXp+8-OV>3+BA^3vSQOEsD{pkS-k+w2Z$ z9H)Cja>7D&<9b_LSp4FY$P${wm|UCI&xVPl#Cna3)!{J>)9y8l*umPXF}NTB&6Z?j z?6sGVH3);NHqqUjp6UyVq&+Tw-ju??cSfmwX>%epHKd*8k5f+;EDB^yYMmUs% zL#>_VEW!3ac%)|MfhY~XiDqhTQ<9{tl~o@xbXdxLbk(%x;M3tWfMDAnG{TBE)zC_L zFq{wynueP!RmuL;5Em)pZ8bzfnYHY;TE7O=A%coVm{B?_!Oy80#}@!7^*KF`TDrtmtzI7Yr1-06+#ChBp# zVj-Jyfv$+%a{3fqtzu(}R6D7@VOUWpIu1rwT9+b}Y*N(wa)qLh0^{O002) zzg7)or;Mg?T(QY|pWWCwA#B3#gsghCJPRw*R76`$^M-sJb8!-;m$s6|haX@5Np6Oy z#>a7t#wHZUF@Z;VkX+qq?w1QQCM^zysZ=mCWQCgwtZz|bOS7r3uLU8Jr~oX}(?G(| zjMS;H*iho#`;?oYt_(ssg**b$E7{aC6x=5U>%(RGcgL*h-^ zKu&9V75?_p9`Xs*;2K2AnB4I?;I4)p1`JbXkHA)qoF@^XobHv@%%ka<8Plq@wPk~O z{Wx?uIyq6JiJzQJC0YlmqDymDmX;g>3iEvxhMgI<-obfZ1{+1TM4LpCo*LGUUt(L+ zE=i6rpi?5#<6L@Y)6JIL@%Sg#`WtzSxf2;Il)E zsgybjN^mNC5;dvW0!&se!=PzhG#!*&tTYsK{iz@u5L0Z5fRCk=J2&`L%F_!uke2EX+=qx(;bGJRk#x~e9)9kJ$v^g0VBu_dvoIp__R{So#k z6uF*SyRS8tN{}8j<3y5Kq3?xEQgg`84(jToT+)|E?fjS;YHDM$LbYr`l@T`$Z^n|| zayDExsC5WEbXx0yh;o)TBt?}ujox-ZJ{YD+eGj*xJ%{^wrl^|t29aFRq@N+3?y2c^ zri~`~Nrz3v9TcZNP8Fh^GML**ZPhysXyiTU*Nid^{|$z1#Thagl7m8SktWT!B^d3M zMRyw7m4lBU9<_ov>@6A#Ft}H86d-SFk?8@IzBMdcFjXN~3$*V1`Z_#;+7fu^MH<^P zRiP|qq=gwXQ%rkzP?1}D)Pox0mPO`iGiF3hPP=Ye24xWE9p!Rpr22w|J`DxTo&W`}48%tpglZjSxW5P`%>^j90)UnVX za&EAfKz$CF+BJO5A+u_s{T3f7tqH%8{-fZ+Bn)AMB+D$9sz+xs5(e8uyz!j_oj}UW z_H#pnQfK*B!|?O?;gwF)9A0mVBv}%f^F|A(H{a#j!5ayokR1F=#!@OPm3b=jHxj6w zHE`jlMFp3tnbKLa1X$|}`0EzfItqYw3vAuG|GI^h>HMbqPvP)((NzAXepXfv8_Py% zKJMd1wQAgcDrXy{B<#zM9^MY;jYLyVLLlS$luSkD+<%KY_(nJIfy-!y5ce~m{b226pKh6a|7_H%xA|D`|s!fnrg^dep{VZzd@HV>sOz1ZC}r?V+p=ZjPXTGJdA&N?*BuXGnWo2J#eD@}_^EzzjfW11GS45~%JrWtgS zmT13y_nX`_nSTdHlMim$cmGj3`P@W=#R`W-^xp4$y+g|aIPcI#Q|yIWd!P2ma-ov1 z^*0yu)x&uxd={%|5rVdb9YEf|<-M7>n7Xt?z130k=ggm3PweF8eMe0XFNroSLhov^ zLo1Uwd{8K|km!d7R_opxb9C0}{IGdmJ2N zF7}(Ta@YZf@^-mg4jW$_H+kQ~4x1e9xo2ee`yM{|km$@aBiZjeW%A?+(Gk(UQA=<< z{ILDxQEpQXJ7mIIADYZVcpP)szJ#z}OSJ!lLr0y^G`(C}PWqa>mRjmnGt8}tBlgh& z2OYBC{)c29Op4SBuKtYP18RvTy(6jVCR=vvZBEQ5v+-UiPO`HE`APZWsmHZVnm7H_ z`7_(vrp}((HmP$dRaI*4FJ+=qn3jmi2OhZps9d(=R`NZAS1gV76)KxK+ehZyvM_?> z>bpj;jEDJ*@s&tX3zrqd{&KwfhJikm)Bd<;84IXYJ+>xag>~_@`mS~H6|X(4xVo%R zj#;6LD?M?6gVe(lWnM}wg1VoQ%4=xK*WDAzPC~Ns92!!Qeb|v=z`kH*J;DQpHH8$O zU{$Fv=G$2fU(~Dg>Jw(ci2*fTmgy-DbgLLfcCpNl>||*e*~y-2{N~>3ReTGed-bZ$ zT6ORWqFalrO5+VW4}KsN4I9(xm;HL;-u9T&B|V79DEoE4c6Ol4gBM1tSM`;;$<3O) za7I90kmYOiLM59Q#lEIsvVPKJWY!zJBT{QUwesLf7A7slOIP2j0`ZR$4)J2`>Gz>aMr~J`q;ZD^i+CFYJdK~PtUm&y&{}?>?kZ{Bb=6@ z7h%*AU4xAJLz8vQ(Y{UCP8+JmWAK!!YeO5>$POyP*X5G5q#++TwB8b_PN>daG^l|h z12=(>;Ex(<^vIi|BYGsLf5{`e#>r^$k5LMpt&FFi2^J`&zl6 zcEh+}{W;@ExmyKNsTsYju1rg5OQlF*^$o<+u->bpT78$6aa8e{fH{EO!{@(HjVzrR z7d4A)15ywsONRJK?d_Nw85uyP7xUe5Z=uAHSS{#{h2fu09PX7Sh4YGEpvR&y%1{lb=wb0@wL_jk#g1YQ&EVSceEX-9#!I0xA`wcS#Mtd z<-F1inQ9JRIJ6Aar_*}K3J9w_)J8SYYP?Jzm!f7upU;Y-5vGXAapu<-muiD(GOgs_ z7WCjs&eqZk|1+T@qLZG92>VZ@pSQA6sC^_Dp!=^qlFSz#k6XP;a{$J+;@}nKIDF`F zO;7hw1NL#qX!Q*R#!atm@B2Z%5AVz2yjp!k8g_my9iW?YgSRt#sPqh!2|cTM6nN`Y zV*s5ZkC5yq$+~Z^(IDuZ6*M|zo2D_EIZ%@Y^qHO#V?}fB=%5Cq&*_Lbqd|?QS?@?6 z=EQXgK^bfMl4nzj0k=2qug0ral{yDk=2lqg2kd4Xr9vA7{Eixv4$Xfn^kqk)kIRWTS8J3RDToF*tKP3je(paHj?= z#b7sjRh`zXuTZ2`YG;ENHk#3cQuo;Bu~|QM#Expt+9@28nm$KUS$#vkmsF)z43v_B zbqmBFvLW?C7vLOfTtg27W!l}Cj9*PwRh2sXl8L8Zl980CrM!$0spa~FWKeH1pOUmP%zj%Ei+C(^bkfmmjI<{6Rn6yBBRYB6?gHP*ye2CIo3w?*SyH>Bu z2CwLiGQQ`9J{oGCuBYV-j`Gkf+{ML*sYkgv$cu7Z>xpeF|LHl`HEIt(J?9z<**mJ$ zTykH?EkFyik5G2#U5eZ^R#AKKGL4OCorgnQArnYIyByHd5lg0T#x(g}ih#yI3!A7y zi2K4(O-Y|Zw5(9!Epffm%u9}yNFS`EM-)0$z(I0ZiAY`6*cy6=nlMdI6VK3|XsdIr zSx4p(NqB3oMgT%*&RLC_A<^3mq(==n6_a$lMRB1=4{+A6TBhH&CL$EKsT?6H z^psKSG{W#8vWbIssP1e3D5XjTAwGB^-=x$Wf~0xOadq_#)C2V$Ehv$k&X~4S9lQ#8 zJCR(mA8$R3gS(vhNhPJquZIII^>o)LUD8wGh$$a=M7ZhzAnD@USSl%|7s_~4Wx~Mp zg%!b$X8&HL2?2du(tC*AsAq84mH@g6%;(it3b{3?e5r);K=f>XO^Kxzleu%^Mj6`I zE3LOxDF;3kGq`f~DoRjw82SViTCdHi^3_P*wP+(8>@(5 zvR-GCRJvh>g)mcTLz0@K+4Mto9oFEJk&3sYRRt<<>eJK$>%me)@|j~&$yZBW*H9|aC*|JdYOtkqN6-(#hy?ef+guqglQP(1? zr6XuG-WhXqN6>f{$0kS;xqt{|9xqAZs~1Qf8iB0`SySbJBat@PDw0KTF@3}r-8qI1@VKMmG(kyRhx*3mvIWHi3I{%BGt9V=aP$_AM# zm^>6ZzdWU1at^{}5#vFTb>qR?EB&TF(K3)uy%NQ0a%*(p1&HvdYOX(CHo#I0FRWAe z)lyGg^=8a84Tf=PZVj~;y^SGARg_2plAPFemuvllSEa_V;?Izo@luyU$wNUm&v@}r zEV@+8r-2TMW3Ay;3Z&J_IT9iy1)I+DC_2)7(7sd+z&qM0VQa0gC zBguG{mo+`WD5_DUVdiFMN$L-nF?3Cfg3{3vjVbKX#Z#TOK0e?jtm|lIftKFKB41dF zWvPY?F%r_1k&iR1M<4>acqdK6NNSNtu2fYv1zozQG{(UzQjzWoWY!tsn<%P`nE4{Y zgbu#6R_s>8mcqJvmD(F&&)0;CGvErC*y~^Qv~hv2cc8$a?lSldt~3o-k2C1uz|*FA zxqMQBO+2L)p1N{6$69CIqTY7uc4@O_aI4#TS@<7R@A7XuicsNWu^KK zkE3LSR5sdYb+N3AuR2QMtu3W0SJIW+8&n~i`O#Yo9t#6wD47y(=}fI6%m+m=A{Czty7PkTOQ!^L{g_SGJk57W8_jhQ4UNGAR>OiyyKmjkkbYHn zXbdlAp2OYN@C+&EJ~J=a~b)pS8UQ%NWP$8X5TEa&zIJpnwczjnCjtcMZ`(f zhSeC7V4T z;MX@*(5$r&QrFh%-$0$ClXBWY7`&aqA9X57-)YufZK@{)J!#EIY)b{*BvltIYJ`g~ za-eJ^PVyv)&M=W^E`^>FJ(x+F-T|%@G!B@mklZFS75Y*0(7Fgrxi+d`_1&So<9AF(ypH7YVcIrY?W;%IFd%DUN+0mTB?ndr54bE$roM|Q> zLUU11ux4fkqoA2s*a(1iD<6eI6nIsHwh>CXO%rL@i z628o-3q8JFrvj5}oI9HNT19(lt-QKvQy-g=n||1eNJf$4BR36`UPhDpDo>bSgq?gz zx>kbfyl zns$eE3{;4);DIJc9$195VKSK~my(APYX7vZ=Y>gV#AwEwkd?FxaIYl|YXP==Fp_jW zg8q&0=P<&&xIN~ZUTt&>hM{zUBr&IREKR(4Il-2P(tFAy%-N2vX)%XXBrB4d&4sla zDkia1Ijrehtw*!($KHKdfuY)buGY9jVvEU5;R~%b#&MdGExaOcXaa*5Hs|K(ORY9M zrFY4ynJPM{2%1JN(^4*(MsOAU!>Zvi`(&iGp|C8#guk8{gj;wg&pb%nDSP%vqtws% zTBl&rfE5fH21&IU_)TfIZAIQGFb-8yo34v9dupU76d^^P#BQPA~$238pAa~hTr zSS6(2X57&sHDZ<9*)Iqi!<99BQdqqj>Sfh(QWSh)O>ra)XbJ5c@F4|JJB>=g~qH3z2Bpw=QAHe-}7bOj<#wkd7SK8LLfiEX-fn3k~GPK{;1RhyO4 z^9Qfc5F1d}I2MFWuUwn&PUwgad-FOIShr&;9{L`okofB_ttgmA?KT?dT&fj@2;;%q zQG!Tmy4_-Fwolt8PUZXxzKLWXgi(HI0SFcrVq;$LL_grAbm|i6?KfJG4*L!01dKyW zDz$H?HD{aqT9Z;UN*j(0VGX&ENHJeRrj=?H&xRYf3c`!+G0uXRvQuxHR)$QnkV7Pm z;gneQh2Hcw9RTPe;Y+!c61t+MQ8j%VOS)QE99Q26%iXaiJd_ZrEadwt>Ul#KF0tlo z8CrUVR53qE>Ry5ZCPS*+{u4K#j;^CxVloxN_>HLWvCxYnb1*F&|_>1 zE?EytZM8GR7k`TrGR;8+}0g%ae`NZKhQcf`pT zijDQN@TJ_aN0ojx*S52glOZ&}x`P(;HKiUPxJGMsrL+yCWfdt$bYkmoVRBztCUv2R z;+r_urLxwn)rW0NWdut0bXgGbfH<mz$T`Qw|HKvo#49OkXxXQ`yk%fj;!l-L_2u9hIK5F#bJY=y7#nW ztB>m=`ja*0#Ax~t;n*29B&Ur7_L(&u9l9o2f_**SGm(Yl)@LP|<*&wkQhA6B&CBbH zx{NA5RoAY5ls%)SZ&9V&DM{K)JcQ#7ETgibg379C2IG0H;v<|oN%E;)WzV1GyHR{Z zh3_l$s#GOztU$v-5w-fHKvjfV%d3Soykb6;Fp@>XO1oK>T8_ztpv8tYmv@rOP+834 zLZEaIYmz>jPFl>+MeACUA&Lt{(o2;53?NXZg$0i^gCS2O(bhlKJxNK@e6@y`9rYDp zn+u|txfY~W?G-|IP45`rl2XFjJ_*dn@7OBVFm4l6R>A0JwgbZW#W@MdQMTIY#;rci zBW*Q2cUKrCB+c295rX`-3Aij#C^DxU_(E_Z-ZVU9TbV5f(Da#w*DAJ08BS+lwR%;) z55d6-J(E1!SEcs!I1vN|lfE`w@M%~>%&@EJBWUiJ0{{19l~n=`)N0Mk?|3#3iz0@ zCjYIjo~a9kl_uI)7!I87vKxV z^;2#NFEOE$Pc1UR;L~pCZD8^ROW<+zB=*>bySO7$6D z%v(r_Z>WV$i!eF*J8UV%*XOmCg7=!~3pZ~dr@Cb{9rAWBP&ZklY3P!}j9m7x~&O{pvO zPDK$ljC`4KZ-KtGtHqpT*0nJ68bJ{KQrZbCbIK1>Iv36Or{6HMsKmsaTfk|JUN*GT zn`B{96_)6lnOh1zOklHiM`oLH)>+{U*b%=3+W0sd-2`2C#9l z25a9tX_TZ556&Sy{bf6Nq+LG0M+T*WE7_hf_1BIX`-X#t-H|@p-0+Ue1nONDz1;}M z^euwq{SFOsROcHDy7K>D*lR0Gnn3!-27`^4I(Xvc+mgbeL zVjvm(Egiq1!hAK00QLTkR?}U<7-Ht@a_KuOw!n~TeW}OPLlO_c*OHluhc8vlPrfxQ zP$bVvgoi}(u&qU~0EV6~F&*_WLqf4{>;u_N8{UXQgcmezB_qDem)J<7bt@HKLC5P* zAf09tiC}X{ft>|ErbIgJ^Ik3mYe)&5OMxVCn72ZD@>LE^nai(<((*|5zO}z zl_ojE=SL>=npwtUIQC}KVXY!G=Y~x~G8W!pNl06*oJ?0x^G!{Y-sVS38(dXob`Tsq!u6$nL6mys%IPt}d45QI4D8=oQ8;MLk~>NL%F z<^u`R_deV{ojS3bYCiE1S0!$2FE1=ckMMDBf1D$J{#1ICZiJ%?<`(fbAj)14Ajqcr zDN!Bl;NZrX+zhhD|KTz*t8)0>J^b3X(FI%3+veFaTSdm`LbKm>%(55?bYD4`hE_FG1^h9 zdc9x<^3pL0z&YUUybQZvzK_Z_KGI>QRQE=(` zg(2F3*BQElUTF&F;I;2S!jpk5jftv6vsZPPrh_!8H@)mVJs}x;gCfQfWThmH%WhId zN%^-!DO0Q7S@rHkvihOr8mlY&FtUasGw~LHc4SnEiKUw2)@(&; zy&_rXr<|=+*mr4oEjVurFkXn94rWaf?Z|pPjU6p334l;L8J&vkcvP3=S*+G;)}{^W zfIAa)+Xmyi`cm_VTB|F~k&Ygu($iEY(~I=2a35n?-BRB{(+RJ^*cxZjj_6)$9Cq;3 zz80?a4YR5;cC+3P#zjVcdy@~*NMGdyunn0Kl*2z7u0u@9VJUnq+>*`cnS3&wxtT|5 z?~8}!2d&|RSoID$Nws}K_F(Mf9!oIEK2ye1x^~P^?}K37HK^M409cOFYw2|l!6FoD zecFi};az?-C59y$Z8}2;zEv{z-bsLlypTZ6~DzL-vC!g}@nDnaYqbTm$WAkDKj zw0;dp_95~{7lgKiuw+R63EzZm3?}J>gykavS}ff>)Sku7rV0yGbiYhk)Km5Z(GaJG zgA7C5vY#-giifynzGW`Q#)Pz+$`Qu9O!_EuuYEdeI0QK#fv1zEVLO!Om#9S}&eKQs zwiZ2YSg`3X20D+L7@e?+rmKT@4Mt5X8gz&;xw+b#(0UzuEGH}Ir9G}LxAU3@{mGZr zOlPN#Z0AyEGPHYE()c#gi=iA#41GhLTT;^ACSa&-SdCL~d<&h{68tEwtfMdzSmHO6YZRtBv-O+qqeW7k(hKJnhGW0TRX{s z6vDFopztkph9&mG2d`?*Nzo@XCbe|$u+nOxa!8wZSwm{j$?AGwa)RXF^HucNPT5jk zob1u((Stb=%38cff3Da|WGvtZjc#Z>sAi8CmLS zPrLi!tLgUgBzj-+zJrI#{4IN?tzjKnZEDE2M51h^5rv$wOKAHNT1X6^OrbucYf&9z zq{5^MJIG+Uy}rA zwD+aTv#Z>R`kzjDIrEtDpG^%xwlUcpW1vxQO(d+s5B8L2T-Es{J_t0k zq7E6R>$Vq>jIyRP4cni6z5X-BmaH2m+qI$mfC(P0P3I z{uLdaOh8@Bt}&^gnc2EaCWeO3r~9`Rkc*sCrqbMdNGAt~L%5A$c{5EMlLm4i9D@W?o|vt?j&w=L z22o0WxM#&92-|<9c+f{u&DX-YYKSO(tZ#uT!F|Yp_{alDrW2`o2}q_ES0s)gl~i zPhVo_4?9@i_t&BHnHs*PXkXVbgG$3__nKmTT|=xF|GzVxp|BX1O_nRM!#^7u>pS)7 zM?~u;%iFN8)rUO=77nti9uk88oQ(mL6K?hfIye1%8EG9p7w3aU@?yn04l*so;|H;Z zX;O71%FlYF`jd`Y$%l6kZUCj<|*UP){ zWryq#qq70EN;+*1&C)-0Xo`Qnx4Ut$T+5iB9177+QfA-}+u?+g?8n!|hIU!%U-Fi^ z$Xq!yJ1 wbSA6|X1*$`|0}>O`A)!)|1O}`ACpg(EcBugyDK_kJHr1Lz$e0J0q*|)01t2C(f|Me literal 0 HcmV?d00001 diff --git a/locale/lt_LT/LC_MESSAGES/django.po b/locale/lt_LT/LC_MESSAGES/django.po new file mode 100644 index 00000000..e17f1a05 --- /dev/null +++ b/locale/lt_LT/LC_MESSAGES/django.po @@ -0,0 +1,3884 @@ +msgid "" +msgstr "" +"Project-Id-Version: bookwyrm\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-14 15:08+0000\n" +"PO-Revision-Date: 2021-11-15 18:03\n" +"Last-Translator: Mouse Reeve \n" +"Language-Team: Lithuanian\n" +"Language: lt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && (n%100>19 || n%100<11) ? 0 : (n%10>=2 && n%10<=9) && (n%100>19 || n%100<11) ? 1 : n%1!=0 ? 2: 3);\n" +"X-Crowdin-Project: bookwyrm\n" +"X-Crowdin-Project-ID: 479239\n" +"X-Crowdin-Language: lt\n" +"X-Crowdin-File: /[bookwyrm-social.bookwyrm] main/locale/en_US/LC_MESSAGES/django.po\n" +"X-Crowdin-File-ID: 1553\n" + +#: bookwyrm/forms.py:242 +msgid "A user with this email already exists." +msgstr "Vartotojas su šiuo el. pašto adresu jau yra." + +#: bookwyrm/forms.py:256 +msgid "One Day" +msgstr "Diena" + +#: bookwyrm/forms.py:257 +msgid "One Week" +msgstr "Savaitė" + +#: bookwyrm/forms.py:258 +msgid "One Month" +msgstr "Mėnuo" + +#: bookwyrm/forms.py:259 +msgid "Does Not Expire" +msgstr "Galiojimas nesibaigia" + +#: bookwyrm/forms.py:263 +#, python-brace-format +msgid "{i} uses" +msgstr "{i} naudoja" + +#: bookwyrm/forms.py:264 +msgid "Unlimited" +msgstr "Neribota" + +#: bookwyrm/forms.py:332 +msgid "List Order" +msgstr "Sąrašo užsakymas" + +#: bookwyrm/forms.py:333 +msgid "Book Title" +msgstr "Knygos antraštė" + +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 +#: bookwyrm/templates/snippets/create_status/review.html:33 +msgid "Rating" +msgstr "Įvertinimas" + +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 +msgid "Sort By" +msgstr "Rūšiuoti pagal" + +#: bookwyrm/forms.py:340 +msgid "Ascending" +msgstr "Didėjančia tvarka" + +#: bookwyrm/forms.py:341 +msgid "Descending" +msgstr "Mažėjančia tvarka" + +#: bookwyrm/importers/importer.py:127 +msgid "Error loading book" +msgstr "Klaida įkeliant knygą" + +#: bookwyrm/importers/importer.py:135 +msgid "Could not find a match for book" +msgstr "Nepavyko rasti tokios knygos" + +#: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 +msgid "Pending" +msgstr "Laukiama" + +#: bookwyrm/models/base_model.py:18 +msgid "Self deletion" +msgstr "Išsitrina savaime" + +#: bookwyrm/models/base_model.py:19 +msgid "Moderator suspension" +msgstr "Moderatorius nutraukė" + +#: bookwyrm/models/base_model.py:20 +msgid "Moderator deletion" +msgstr "Moderatorius ištrynė" + +#: bookwyrm/models/base_model.py:21 +msgid "Domain block" +msgstr "Blokuoti pagal domeną" + +#: bookwyrm/models/book.py:233 +msgid "Audiobook" +msgstr "Audioknyga" + +#: bookwyrm/models/book.py:234 +msgid "eBook" +msgstr "Elektroninė knyga" + +#: bookwyrm/models/book.py:235 +msgid "Graphic novel" +msgstr "Grafinė novelė" + +#: bookwyrm/models/book.py:236 +msgid "Hardcover" +msgstr "Knyga kietais viršeliais" + +#: bookwyrm/models/book.py:237 +msgid "Paperback" +msgstr "Knyga minkštais viršeliais" + +#: bookwyrm/models/federated_server.py:11 +#: bookwyrm/templates/settings/federation/edit_instance.html:42 +#: bookwyrm/templates/settings/federation/instance_list.html:19 +msgid "Federated" +msgstr "Susijungę" + +#: bookwyrm/models/federated_server.py:12 +#: bookwyrm/templates/settings/federation/edit_instance.html:43 +#: bookwyrm/templates/settings/federation/instance.html:10 +#: bookwyrm/templates/settings/federation/instance_list.html:23 +msgid "Blocked" +msgstr "Užblokuota" + +#: bookwyrm/models/fields.py:29 +#, python-format +msgid "%(value)s is not a valid remote_id" +msgstr "%(value)s yra negaliojantis remote_id" + +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 +#, python-format +msgid "%(value)s is not a valid username" +msgstr "%(value)s yra negaliojantis naudotojo vardas" + +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 +msgid "username" +msgstr "naudotojo vardas" + +#: bookwyrm/models/fields.py:188 +msgid "A user with that username already exists." +msgstr "Toks naudotojo vardas jau egzistuoja." + +#: bookwyrm/settings.py:118 +msgid "Home Timeline" +msgstr "Pagrindinė siena" + +#: bookwyrm/settings.py:118 +msgid "Home" +msgstr "Pagrindinis" + +#: bookwyrm/settings.py:119 +msgid "Books Timeline" +msgstr "Knygų siena" + +#: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 +#: bookwyrm/templates/search/layout.html:42 +#: bookwyrm/templates/user/layout.html:88 +msgid "Books" +msgstr "Knygos" + +#: bookwyrm/settings.py:165 +msgid "English" +msgstr "Anglų" + +#: bookwyrm/settings.py:166 +msgid "Deutsch (German)" +msgstr "Deutsch (Vokiečių)" + +#: bookwyrm/settings.py:167 +msgid "Español (Spanish)" +msgstr "Español (Ispanų)" + +#: bookwyrm/settings.py:168 +msgid "Français (French)" +msgstr "Français (Prancūzų)" + +#: bookwyrm/settings.py:169 +msgid "Português - Brasil (Brazilian Portuguese)" +msgstr "Português - Brasil (Portugalų–brazilų)" + +#: bookwyrm/settings.py:170 +msgid "简体中文 (Simplified Chinese)" +msgstr "简体中文 (Supaprastinta kinų)" + +#: bookwyrm/settings.py:171 +msgid "繁體中文 (Traditional Chinese)" +msgstr "繁體中文 (Tradicinė kinų)" + +#: bookwyrm/templates/404.html:4 bookwyrm/templates/404.html:8 +msgid "Not Found" +msgstr "Nerasta" + +#: bookwyrm/templates/404.html:9 +msgid "The page you requested doesn't seem to exist!" +msgstr "Jūsų ieškomas puslapis neegzistuoja." + +#: bookwyrm/templates/500.html:4 +msgid "Oops!" +msgstr "Oi!" + +#: bookwyrm/templates/500.html:8 +msgid "Server Error" +msgstr "Serverio klaida" + +#: bookwyrm/templates/500.html:9 +msgid "Something went wrong! Sorry about that." +msgstr "Kažkas nepavyko. Atsiprašome." + +#: bookwyrm/templates/author/author.html:17 +#: bookwyrm/templates/author/author.html:18 +msgid "Edit Author" +msgstr "Keisti autorių" + +#: bookwyrm/templates/author/author.html:34 +#: bookwyrm/templates/author/edit_author.html:43 +msgid "Aliases:" +msgstr "Pseudonimai:" + +#: bookwyrm/templates/author/author.html:45 +msgid "Born:" +msgstr "Gimęs:" + +#: bookwyrm/templates/author/author.html:52 +msgid "Died:" +msgstr "Mirė:" + +#: bookwyrm/templates/author/author.html:61 +msgid "Wikipedia" +msgstr "Wikipedia" + +#: bookwyrm/templates/author/author.html:69 +#: bookwyrm/templates/book/book.html:94 +msgid "View on OpenLibrary" +msgstr "Žiūrėti „OpenLibrary“" + +#: bookwyrm/templates/author/author.html:77 +#: bookwyrm/templates/book/book.html:97 +msgid "View on Inventaire" +msgstr "Žiūrėti „Inventaire“" + +#: bookwyrm/templates/author/author.html:85 +msgid "View on LibraryThing" +msgstr "Žiūrėti „LibraryThing“" + +#: bookwyrm/templates/author/author.html:93 +msgid "View on Goodreads" +msgstr "Žiūrėti „Goodreads“" + +#: bookwyrm/templates/author/author.html:108 +#, python-format +msgid "Books by %(name)s" +msgstr "%(name)s knygos" + +#: bookwyrm/templates/author/edit_author.html:5 +msgid "Edit Author:" +msgstr "Keisti autorių:" + +#: bookwyrm/templates/author/edit_author.html:13 +#: bookwyrm/templates/book/edit/edit_book.html:18 +msgid "Added:" +msgstr "Pridėta:" + +#: bookwyrm/templates/author/edit_author.html:14 +#: bookwyrm/templates/book/edit/edit_book.html:21 +msgid "Updated:" +msgstr "Atnaujinta:" + +#: bookwyrm/templates/author/edit_author.html:16 +#: bookwyrm/templates/book/edit/edit_book.html:25 +msgid "Last edited by:" +msgstr "Pastarąjį kartą redagavo:" + +#: bookwyrm/templates/author/edit_author.html:33 +#: bookwyrm/templates/book/edit/edit_book_form.html:15 +msgid "Metadata" +msgstr "Meta duomenys" + +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 +msgid "Name:" +msgstr "Vardas:" + +#: bookwyrm/templates/author/edit_author.html:45 +#: bookwyrm/templates/book/edit/edit_book_form.html:65 +#: bookwyrm/templates/book/edit/edit_book_form.html:79 +#: bookwyrm/templates/book/edit/edit_book_form.html:124 +msgid "Separate multiple values with commas." +msgstr "Reikšmes atskirkite kableliais." + +#: bookwyrm/templates/author/edit_author.html:52 +msgid "Bio:" +msgstr "Biografija:" + +#: bookwyrm/templates/author/edit_author.html:59 +msgid "Wikipedia link:" +msgstr "Nuoroda į wikipediją:" + +#: bookwyrm/templates/author/edit_author.html:65 +msgid "Birth date:" +msgstr "Gimimo data:" + +#: bookwyrm/templates/author/edit_author.html:73 +msgid "Death date:" +msgstr "Mirties data:" + +#: bookwyrm/templates/author/edit_author.html:81 +msgid "Author Identifiers" +msgstr "Autoriaus identifikatoriai" + +#: bookwyrm/templates/author/edit_author.html:83 +msgid "Openlibrary key:" +msgstr "„Openlibrary“ raktas:" + +#: bookwyrm/templates/author/edit_author.html:91 +#: bookwyrm/templates/book/edit/edit_book_form.html:224 +msgid "Inventaire ID:" +msgstr "„Inventaire“ ID:" + +#: bookwyrm/templates/author/edit_author.html:99 +msgid "Librarything key:" +msgstr "„Librarything“ raktas:" + +#: bookwyrm/templates/author/edit_author.html:107 +msgid "Goodreads key:" +msgstr "„Goodreads“ raktas:" + +#: bookwyrm/templates/author/edit_author.html:118 +#: bookwyrm/templates/book/book.html:140 +#: bookwyrm/templates/book/edit/edit_book.html:110 +#: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 +#: bookwyrm/templates/lists/bookmark_button.html:15 +#: bookwyrm/templates/lists/form.html:75 +#: bookwyrm/templates/preferences/edit_user.html:124 +#: bookwyrm/templates/settings/announcements/announcement_form.html:69 +#: bookwyrm/templates/settings/federation/edit_instance.html:74 +#: bookwyrm/templates/settings/federation/instance.html:87 +#: bookwyrm/templates/settings/site.html:134 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:64 +#: bookwyrm/templates/shelf/form.html:25 +#: bookwyrm/templates/snippets/reading_modals/layout.html:18 +msgid "Save" +msgstr "Išsaugoti" + +#: bookwyrm/templates/author/edit_author.html:119 +#: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 +#: bookwyrm/templates/book/cover_modal.html:32 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 +#: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 +#: bookwyrm/templates/lists/delete_list_modal.html:17 +#: bookwyrm/templates/settings/federation/instance.html:88 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 +#: bookwyrm/templates/snippets/report_modal.html:34 +msgid "Cancel" +msgstr "Atšaukti" + +#: bookwyrm/templates/book/book.html:47 +#: bookwyrm/templates/discover/large-book.html:22 +#: bookwyrm/templates/landing/large-book.html:25 +#: bookwyrm/templates/landing/small-book.html:18 +msgid "by" +msgstr " " + +#: bookwyrm/templates/book/book.html:55 bookwyrm/templates/book/book.html:56 +msgid "Edit Book" +msgstr "Redaguoti knygą" + +#: bookwyrm/templates/book/book.html:73 +#: bookwyrm/templates/book/cover_modal.html:5 +msgid "Add cover" +msgstr "Pridėti viršelį" + +#: bookwyrm/templates/book/book.html:77 +msgid "Failed to load cover" +msgstr "Nepavyko įkelti viršelio" + +#: bookwyrm/templates/book/book.html:117 +#, python-format +msgid "(%(review_count)s review)" +msgid_plural "(%(review_count)s reviews)" +msgstr[0] "(%(review_count)s atsiliepimas)" +msgstr[1] "(%(review_count)s atsiliepimai)" +msgstr[2] "(%(review_count)s atsiliepimų)" +msgstr[3] "(%(review_count)s atsiliepimai)" + +#: bookwyrm/templates/book/book.html:129 +msgid "Add Description" +msgstr "Pridėti aprašymą" + +#: bookwyrm/templates/book/book.html:136 +#: bookwyrm/templates/book/edit/edit_book_form.html:34 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 +msgid "Description:" +msgstr "Aprašymas:" + +#: bookwyrm/templates/book/book.html:150 +#, python-format +msgid "%(count)s editions" +msgstr "%(count)s leidimai (-ų)" + +#: bookwyrm/templates/book/book.html:158 +#, python-format +msgid "This edition is on your %(shelf_name)s shelf." +msgstr "Šis leidimas yra jūsų %(shelf_name)s lentynoje." + +#: bookwyrm/templates/book/book.html:164 +#, python-format +msgid "A different edition of this book is on your %(shelf_name)s shelf." +msgstr "kitas šios knygos leidimas yra jūsų %(shelf_name)s lentynoje." + +#: bookwyrm/templates/book/book.html:175 +msgid "Your reading activity" +msgstr "Jūsų skaitymo veikla" + +#: bookwyrm/templates/book/book.html:178 +msgid "Add read dates" +msgstr "Pridėti skaitymo datas" + +#: bookwyrm/templates/book/book.html:187 +msgid "Create" +msgstr "Kurti" + +#: bookwyrm/templates/book/book.html:197 +msgid "You don't have any reading activity for this book." +msgstr "Šios knygos neskaitote." + +#: bookwyrm/templates/book/book.html:218 +msgid "Reviews" +msgstr "Apžvalgos" + +#: bookwyrm/templates/book/book.html:223 +msgid "Your reviews" +msgstr "Tavo atsiliepimai" + +#: bookwyrm/templates/book/book.html:229 +msgid "Your comments" +msgstr "Tavo komentarai" + +#: bookwyrm/templates/book/book.html:235 +msgid "Your quotes" +msgstr "Jūsų citatos" + +#: bookwyrm/templates/book/book.html:271 +msgid "Subjects" +msgstr "Temos" + +#: bookwyrm/templates/book/book.html:283 +msgid "Places" +msgstr "Vietos" + +#: bookwyrm/templates/book/book.html:294 bookwyrm/templates/layout.html:75 +#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 +#: bookwyrm/templates/search/layout.html:25 +#: bookwyrm/templates/search/layout.html:50 +#: bookwyrm/templates/user/layout.html:82 +msgid "Lists" +msgstr "Sąrašai" + +#: bookwyrm/templates/book/book.html:305 +msgid "Add to list" +msgstr "Pridėti prie sąrašo" + +#: bookwyrm/templates/book/book.html:315 +#: bookwyrm/templates/book/cover_modal.html:31 +#: bookwyrm/templates/lists/list.html:182 +#: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 +#: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 +msgid "Add" +msgstr "Pridėti" + +#: bookwyrm/templates/book/book_identifiers.html:8 +msgid "ISBN:" +msgstr "ISBN:" + +#: bookwyrm/templates/book/book_identifiers.html:15 +#: bookwyrm/templates/book/edit/edit_book_form.html:232 +msgid "OCLC Number:" +msgstr "OCLC numeris:" + +#: bookwyrm/templates/book/book_identifiers.html:22 +#: bookwyrm/templates/book/edit/edit_book_form.html:240 +msgid "ASIN:" +msgstr "ASIN:" + +#: bookwyrm/templates/book/cover_modal.html:17 +#: bookwyrm/templates/book/edit/edit_book_form.html:143 +msgid "Upload cover:" +msgstr "Įkelti viršelį:" + +#: bookwyrm/templates/book/cover_modal.html:23 +#: bookwyrm/templates/book/edit/edit_book_form.html:148 +msgid "Load cover from url:" +msgstr "Įkelti viršelį iš url:" + +#: bookwyrm/templates/book/edit/edit_book.html:5 +#: bookwyrm/templates/book/edit/edit_book.html:11 +#, python-format +msgid "Edit \"%(book_title)s\"" +msgstr "Redaguoti „%(book_title)s“" + +#: bookwyrm/templates/book/edit/edit_book.html:5 +#: bookwyrm/templates/book/edit/edit_book.html:13 +msgid "Add Book" +msgstr "Pridėti knygą" + +#: bookwyrm/templates/book/edit/edit_book.html:47 +msgid "Confirm Book Info" +msgstr "Patvirtinti knygos informaciją" + +#: bookwyrm/templates/book/edit/edit_book.html:55 +#, python-format +msgid "Is \"%(name)s\" an existing author?" +msgstr "Yra „%(name)s“ egzistuojantis autorius?" + +#: bookwyrm/templates/book/edit/edit_book.html:64 +#, python-format +msgid "Author of %(book_title)s" +msgstr "%(book_title)s autorius" + +#: bookwyrm/templates/book/edit/edit_book.html:68 +msgid "This is a new author" +msgstr "Tai naujas autorius" + +#: bookwyrm/templates/book/edit/edit_book.html:75 +#, python-format +msgid "Creating a new author: %(name)s" +msgstr "Kuriamas naujas autorius: %(name)s" + +#: bookwyrm/templates/book/edit/edit_book.html:82 +msgid "Is this an edition of an existing work?" +msgstr "Ar tai egzistuojančio darbo leidimas?" + +#: bookwyrm/templates/book/edit/edit_book.html:90 +msgid "This is a new work" +msgstr "Tai naujas darbas" + +#: bookwyrm/templates/book/edit/edit_book.html:97 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 +msgid "Confirm" +msgstr "Patvirtinti" + +#: bookwyrm/templates/book/edit/edit_book.html:99 +#: bookwyrm/templates/feed/status.html:9 +msgid "Back" +msgstr "Atgal" + +#: bookwyrm/templates/book/edit/edit_book_form.html:18 +#: bookwyrm/templates/snippets/create_status/review.html:16 +msgid "Title:" +msgstr "Pavadinimas:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:26 +msgid "Subtitle:" +msgstr "Paantraštė:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:44 +msgid "Series:" +msgstr "Serija:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:53 +msgid "Series number:" +msgstr "Serijos numeris:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:63 +msgid "Languages:" +msgstr "Kalbos:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:74 +msgid "Publication" +msgstr "Paskelbimas" + +#: bookwyrm/templates/book/edit/edit_book_form.html:77 +msgid "Publisher:" +msgstr "Leidėjas:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:86 +msgid "First published date:" +msgstr "Pirmoji publikavimo data:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:94 +msgid "Published date:" +msgstr "Publikavimo data:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:104 +msgid "Authors" +msgstr "Autoriai" + +#: bookwyrm/templates/book/edit/edit_book_form.html:112 +#, python-format +msgid "Remove %(name)s" +msgstr "Pašalinti %(name)s" + +#: bookwyrm/templates/book/edit/edit_book_form.html:115 +#, python-format +msgid "Author page for %(name)s" +msgstr "Autoriaus puslapis %(name)s" + +#: bookwyrm/templates/book/edit/edit_book_form.html:122 +msgid "Add Authors:" +msgstr "Pridėti autorius:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:123 +msgid "John Doe, Jane Smith" +msgstr "John Doe, Jane Smith" + +#: bookwyrm/templates/book/edit/edit_book_form.html:132 +#: bookwyrm/templates/shelf/shelf.html:140 +msgid "Cover" +msgstr "Viršelis" + +#: bookwyrm/templates/book/edit/edit_book_form.html:161 +msgid "Physical Properties" +msgstr "Fizinės savybės" + +#: bookwyrm/templates/book/edit/edit_book_form.html:166 +#: bookwyrm/templates/book/editions/format_filter.html:5 +msgid "Format:" +msgstr "Formatas:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:177 +msgid "Format details:" +msgstr "Informacija apie formatą:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:187 +msgid "Pages:" +msgstr "Puslapiai:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:197 +msgid "Book Identifiers" +msgstr "Knygos identifikatoriai" + +#: bookwyrm/templates/book/edit/edit_book_form.html:200 +msgid "ISBN 13:" +msgstr "ISBN 13:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:208 +msgid "ISBN 10:" +msgstr "ISBN 10:" + +#: bookwyrm/templates/book/edit/edit_book_form.html:216 +msgid "Openlibrary ID:" +msgstr "„Openlibrary“ ID:" + +#: bookwyrm/templates/book/editions/editions.html:4 +#, python-format +msgid "Editions of %(book_title)s" +msgstr "Knygos %(book_title)s leidimai" + +#: bookwyrm/templates/book/editions/editions.html:8 +#, python-format +msgid "Editions of \"%(work_title)s\"" +msgstr "\"%(work_title)s\" leidimai" + +#: bookwyrm/templates/book/editions/format_filter.html:8 +#: bookwyrm/templates/book/editions/language_filter.html:8 +msgid "Any" +msgstr "Bet kas" + +#: bookwyrm/templates/book/editions/language_filter.html:5 +#: bookwyrm/templates/preferences/edit_user.html:95 +msgid "Language:" +msgstr "Kalba:" + +#: bookwyrm/templates/book/editions/search_filter.html:5 +msgid "Search editions" +msgstr "Paieškos leidimai" + +#: bookwyrm/templates/book/publisher_info.html:23 +#, python-format +msgid "%(format)s, %(pages)s pages" +msgstr "%(format)s, %(pages)s psl." + +#: bookwyrm/templates/book/publisher_info.html:25 +#, python-format +msgid "%(pages)s pages" +msgstr "%(pages)s psl." + +#: bookwyrm/templates/book/publisher_info.html:38 +#, python-format +msgid "%(languages)s language" +msgstr "%(languages)s kalba" + +#: bookwyrm/templates/book/publisher_info.html:65 +#, python-format +msgid "Published %(date)s by %(publisher)s." +msgstr "Publikuota %(date)s, %(publisher)s." + +#: bookwyrm/templates/book/publisher_info.html:67 +#, python-format +msgid "Published %(date)s" +msgstr "Publikuota %(date)s" + +#: bookwyrm/templates/book/publisher_info.html:69 +#, python-format +msgid "Published by %(publisher)s." +msgstr "Publikavo %(publisher)s." + +#: bookwyrm/templates/book/rating.html:13 +msgid "rated it" +msgstr "įvertino" + +#: bookwyrm/templates/book/readthrough.html:8 +msgid "Progress Updates:" +msgstr "Informacija apie progresą:" + +#: bookwyrm/templates/book/readthrough.html:13 +msgid "finished" +msgstr "baigta" + +#: bookwyrm/templates/book/readthrough.html:24 +msgid "Show all updates" +msgstr "Rodyti visus naujinius" + +#: bookwyrm/templates/book/readthrough.html:40 +msgid "Delete this progress update" +msgstr "Ištrinti progreso naujinį" + +#: bookwyrm/templates/book/readthrough.html:51 +msgid "started" +msgstr "pradėta" + +#: bookwyrm/templates/book/readthrough.html:58 +#: bookwyrm/templates/book/readthrough.html:72 +msgid "Edit read dates" +msgstr "Redaguoti skaitymo datas" + +#: bookwyrm/templates/book/readthrough.html:62 +msgid "Delete these read dates" +msgstr "Ištrinti šias skaitymo datas" + +#: bookwyrm/templates/components/inline_form.html:8 +#: bookwyrm/templates/components/modal.html:11 +#: bookwyrm/templates/components/tooltip.html:7 +#: bookwyrm/templates/feed/layout.html:71 +#: bookwyrm/templates/get_started/layout.html:20 +#: bookwyrm/templates/get_started/layout.html:53 +#: bookwyrm/templates/search/book.html:49 +#: bookwyrm/templates/snippets/announcement.html:18 +msgid "Close" +msgstr "Uždaryti" + +#: bookwyrm/templates/components/tooltip.html:3 +msgid "Help" +msgstr "Pagalba" + +#: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 +msgid "Edit status" +msgstr "Redagavimo būsena" + +#: bookwyrm/templates/confirm_email/confirm_email.html:4 +msgid "Confirm email" +msgstr "Patvirtinti el. paštą" + +#: bookwyrm/templates/confirm_email/confirm_email.html:7 +msgid "Confirm your email address" +msgstr "Patvirtinkite el. pašto adresą" + +#: bookwyrm/templates/confirm_email/confirm_email.html:13 +msgid "A confirmation code has been sent to the email address you used to register your account." +msgstr "Į paskyros registracijai naudotą el. paštą buvo išsiųstas patvirtinimo kodas." + +#: bookwyrm/templates/confirm_email/confirm_email.html:15 +msgid "Sorry! We couldn't find that code." +msgstr "Deja, šio kodo neradome." + +#: bookwyrm/templates/confirm_email/confirm_email.html:19 +#: bookwyrm/templates/settings/users/user_info.html:85 +msgid "Confirmation code:" +msgstr "Patvirtinimo kodas:" + +#: bookwyrm/templates/confirm_email/confirm_email.html:25 +#: bookwyrm/templates/landing/layout.html:73 +#: bookwyrm/templates/settings/dashboard/dashboard.html:93 +#: bookwyrm/templates/snippets/report_modal.html:33 +msgid "Submit" +msgstr "Siųsti" + +#: bookwyrm/templates/confirm_email/confirm_email.html:32 +msgid "Can't find your code?" +msgstr "Nerandate savo kodo?" + +#: bookwyrm/templates/confirm_email/resend_form.html:4 +msgid "Resend confirmation link" +msgstr "Dar kartą išsiųsti patvirtinimo nuorodą" + +#: bookwyrm/templates/confirm_email/resend_form.html:11 +#: bookwyrm/templates/landing/layout.html:67 +#: bookwyrm/templates/landing/password_reset_request.html:18 +#: bookwyrm/templates/preferences/edit_user.html:56 +#: bookwyrm/templates/snippets/register_form.html:13 +msgid "Email address:" +msgstr "El. pašto adresas:" + +#: bookwyrm/templates/confirm_email/resend_form.html:17 +msgid "Resend link" +msgstr "Dar kartą išsiųsti nuorodą" + +#: bookwyrm/templates/directory/community_filter.html:5 +msgid "Community" +msgstr "Bendruomenė" + +#: bookwyrm/templates/directory/community_filter.html:8 +msgid "Local users" +msgstr "Vietiniai nariai" + +#: bookwyrm/templates/directory/community_filter.html:12 +msgid "Federated community" +msgstr "Sujungta bendruomenė" + +#: bookwyrm/templates/directory/directory.html:4 +#: bookwyrm/templates/directory/directory.html:9 +#: bookwyrm/templates/layout.html:101 +msgid "Directory" +msgstr "Bendruomenė" + +#: bookwyrm/templates/directory/directory.html:17 +msgid "Make your profile discoverable to other BookWyrm users." +msgstr "Savo paskyrą leiskite atrasti kitiems „BookWyrm“ nariems." + +#: bookwyrm/templates/directory/directory.html:24 +#, python-format +msgid "You can opt-out at any time in your profile settings." +msgstr "Tai galite visada atšaukti paskyros nustatymuose." + +#: bookwyrm/templates/directory/directory.html:29 +#: bookwyrm/templates/feed/goal_card.html:17 +#: bookwyrm/templates/snippets/announcement.html:34 +msgid "Dismiss message" +msgstr "Pašalinti pranešimą" + +#: bookwyrm/templates/directory/sort_filter.html:5 +msgid "Order by" +msgstr "Rūšiuoti pagal" + +#: bookwyrm/templates/directory/sort_filter.html:8 +msgid "Recently active" +msgstr "Neseniai aktyvus" + +#: bookwyrm/templates/directory/sort_filter.html:9 +msgid "Suggested" +msgstr "Pasiūlyta" + +#: bookwyrm/templates/directory/user_card.html:17 +#: bookwyrm/templates/directory/user_card.html:18 +#: bookwyrm/templates/user/user_preview.html:16 +#: bookwyrm/templates/user/user_preview.html:17 +msgid "Locked account" +msgstr "Užrakinta paskyra" + +#: bookwyrm/templates/directory/user_card.html:40 +msgid "follower you follow" +msgid_plural "followers you follow" +msgstr[0] "jūs sekate" +msgstr[1] "jūs sekate" +msgstr[2] "jūs sekate" +msgstr[3] "jūs sekate" + +#: bookwyrm/templates/directory/user_card.html:47 +msgid "book on your shelves" +msgid_plural "books on your shelves" +msgstr[0] "jūsų lentynoje esanti knyga" +msgstr[1] "jūsų lentynose esančios knygos" +msgstr[2] "jūsų lentynose esančios knygos" +msgstr[3] "jūsų lentynose esančios knygos" + +#: bookwyrm/templates/directory/user_card.html:55 +msgid "posts" +msgstr "įrašai" + +#: bookwyrm/templates/directory/user_card.html:61 +msgid "last active" +msgstr "pastarąjį kartą aktyvus" + +#: bookwyrm/templates/directory/user_type_filter.html:5 +msgid "User type" +msgstr "Naudotojo tipas" + +#: bookwyrm/templates/directory/user_type_filter.html:8 +msgid "BookWyrm users" +msgstr "„BookWyrm“ nariai" + +#: bookwyrm/templates/directory/user_type_filter.html:12 +msgid "All known users" +msgstr "Visi žinomi nariai" + +#: bookwyrm/templates/discover/card-header.html:8 +#, python-format +msgid "%(username)s wants to read %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:23 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "%(username)s įvertino %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:27 +#, python-format +msgid "%(username)s reviewed %(book_title)s" +msgstr "%(username)s peržiūrėjo %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:31 +#, python-format +msgid "%(username)s commented on %(book_title)s" +msgstr "%(username)s pakomentavo prie knygos %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:35 +#, python-format +msgid "%(username)s quoted %(book_title)s" +msgstr "%(username)s citavo %(book_title)s" + +#: bookwyrm/templates/discover/discover.html:4 +#: bookwyrm/templates/discover/discover.html:10 +#: bookwyrm/templates/layout.html:78 +msgid "Discover" +msgstr "Atraskite" + +#: bookwyrm/templates/discover/discover.html:12 +#, python-format +msgid "See what's new in the local %(site_name)s community" +msgstr "Kas naujo mūsų %(site_name)s bendruomenėje" + +#: bookwyrm/templates/discover/large-book.html:52 +#: bookwyrm/templates/discover/small-book.html:36 +msgid "View status" +msgstr "Peržiūrėti būseną" + +#: bookwyrm/templates/email/confirm/html_content.html:6 +#: bookwyrm/templates/email/confirm/text_content.html:4 +#, python-format +msgid "One last step before you join %(site_name)s! Please confirm your email address by clicking the link below:" +msgstr "Paskutinis žingsnis prieš prisijungiant prie %(site_name)s! Patvirtinkite el. pašto adresą spausdami nuorodą žemiau:" + +#: bookwyrm/templates/email/confirm/html_content.html:11 +msgid "Confirm Email" +msgstr "Patvirtinti el. paštą" + +#: bookwyrm/templates/email/confirm/html_content.html:15 +#, python-format +msgid "Or enter the code \"%(confirmation_code)s\" at login." +msgstr "Arba suveskite kodą \"%(confirmation_code)s\" prisijungimo metu." + +#: bookwyrm/templates/email/confirm/subject.html:2 +msgid "Please confirm your email" +msgstr "Patvirtinkite savo el. pašto adresą" + +#: bookwyrm/templates/email/confirm/text_content.html:10 +#, python-format +msgid "Or enter the code \"%(confirmation_code)s\" at login." +msgstr "Arba suveskite kodą \"%(confirmation_code)s\" prisijungimo metu." + +#: bookwyrm/templates/email/html_layout.html:15 +#: bookwyrm/templates/email/text_layout.html:2 +msgid "Hi there," +msgstr "Labas!" + +#: bookwyrm/templates/email/html_layout.html:21 +#, python-format +msgid "BookWyrm hosted on %(site_name)s" +msgstr "BookWyrm talpinamas %(site_name)s" + +#: bookwyrm/templates/email/html_layout.html:23 +msgid "Email preference" +msgstr "El. pašto nustatymai" + +#: bookwyrm/templates/email/invite/html_content.html:6 +#: bookwyrm/templates/email/invite/subject.html:2 +#, python-format +msgid "You're invited to join %(site_name)s!" +msgstr "Esate pakviesti prisijungti prie %(site_name)s!" + +#: bookwyrm/templates/email/invite/html_content.html:9 +msgid "Join Now" +msgstr "Prisijungti dabar" + +#: bookwyrm/templates/email/invite/html_content.html:15 +#, python-format +msgid "Learn more about %(site_name)s." +msgstr "Sužinoti daugiau apie %(site_name)s." + +#: bookwyrm/templates/email/invite/text_content.html:4 +#, python-format +msgid "You're invited to join %(site_name)s! Click the link below to create an account." +msgstr "Esate pakviesti prisijungti prie %(site_name)s! Spauskite nuorodą žemiau, kad sukurtumėte paskyrą." + +#: bookwyrm/templates/email/invite/text_content.html:8 +#, python-format +msgid "Learn more about %(site_name)s:" +msgstr "Sužinoti daugiau apie %(site_name)s:" + +#: bookwyrm/templates/email/password_reset/html_content.html:6 +#: bookwyrm/templates/email/password_reset/text_content.html:4 +#, python-format +msgid "You requested to reset your %(site_name)s password. Click the link below to set a new password and log in to your account." +msgstr "Prašėte pakeisti %(site_name)s slaptažodį. Spauskite žemiau, kad pakeisti slaptažodį ir prisijungti prie savo paskyros." + +#: bookwyrm/templates/email/password_reset/html_content.html:9 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 +msgid "Reset Password" +msgstr "Atstatyti slaptažodį" + +#: bookwyrm/templates/email/password_reset/html_content.html:13 +#: bookwyrm/templates/email/password_reset/text_content.html:8 +msgid "If you didn't request to reset your password, you can ignore this email." +msgstr "Jei nenorite pakeisti savo slaptažodžio - ignoruokite šį laišką." + +#: bookwyrm/templates/email/password_reset/subject.html:2 +#, python-format +msgid "Reset your %(site_name)s password" +msgstr "Keisti %(site_name)s slaptažodį" + +#: bookwyrm/templates/feed/direct_messages.html:8 +#, python-format +msgid "Direct Messages with %(username)s" +msgstr "Asmeninis susirašinėjimas su %(username)s" + +#: bookwyrm/templates/feed/direct_messages.html:10 +#: bookwyrm/templates/layout.html:111 +msgid "Direct Messages" +msgstr "Asmeninės žinutės" + +#: bookwyrm/templates/feed/direct_messages.html:13 +msgid "All messages" +msgstr "Visos žinutės" + +#: bookwyrm/templates/feed/direct_messages.html:22 +msgid "You have no messages right now." +msgstr "Neturite žinučių." + +#: bookwyrm/templates/feed/feed.html:22 +#, python-format +msgid "load 0 unread status(es)" +msgstr "įkelti 0 neperskaitytų būsena" + +#: bookwyrm/templates/feed/feed.html:38 +msgid "There aren't any activities right now! Try following a user to get started" +msgstr "Šiuo metu įrašų nėra. Norėdami matyti, sekite narį." + +#: bookwyrm/templates/feed/goal_card.html:6 +#: bookwyrm/templates/feed/layout.html:90 +#: bookwyrm/templates/user/goal_form.html:6 +#, python-format +msgid "%(year)s Reading Goal" +msgstr "%(year)s skaitymo tikslas" + +#: bookwyrm/templates/feed/goal_card.html:18 +#, python-format +msgid "You can set or change your reading goal any time from your profile page" +msgstr "Bet kuriuo metu galite pakeisti savo skaitymo tikslą savo paskyros puslapyje" + +#: bookwyrm/templates/feed/layout.html:5 +msgid "Updates" +msgstr "Atnaujinimai" + +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" +msgstr "Jūsų knygos" + +#: bookwyrm/templates/feed/layout.html:14 +msgid "There are no books here right now! Try searching for a book to get started" +msgstr "Knygų neturite. Raskite knygą ir pradėkite." + +#: bookwyrm/templates/feed/layout.html:25 +#: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 +msgid "To Read" +msgstr "Perskaityti" + +#: bookwyrm/templates/feed/layout.html:26 +#: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 +msgid "Currently Reading" +msgstr "Šiuo metu skaitoma" + +#: bookwyrm/templates/feed/layout.html:27 +#: bookwyrm/templates/shelf/shelf.html:42 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 +msgid "Read" +msgstr "Perskaičiau" + +#: bookwyrm/templates/feed/suggested_users.html:5 +#: bookwyrm/templates/get_started/users.html:6 +msgid "Who to follow" +msgstr "Ką sekti" + +#: bookwyrm/templates/feed/suggested_users.html:9 +msgid "Don't show suggested users" +msgstr "Nerodyti siūlomų vartotojų" + +#: bookwyrm/templates/feed/suggested_users.html:14 +msgid "View directory" +msgstr "Žiūrėti katalogą" + +#: bookwyrm/templates/get_started/book_preview.html:6 +#, python-format +msgid "Have you read %(book_title)s?" +msgstr "Ar skaitėte „%(book_title)s“?" + +#: bookwyrm/templates/get_started/books.html:6 +msgid "What are you reading?" +msgstr "Ką skaitome?" + +#: bookwyrm/templates/get_started/books.html:9 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 +msgid "Search for a book" +msgstr "Ieškoti knygos" + +#: bookwyrm/templates/get_started/books.html:11 +#, python-format +msgid "No books found for \"%(query)s\"" +msgstr "Pagal paiešką „%(query)s“ knygos nerasta" + +#: bookwyrm/templates/get_started/books.html:11 +#, python-format +msgid "You can add books when you start using %(site_name)s." +msgstr "Kai pradedate naudotis %(site_name)s, galite pridėti knygų." + +#: bookwyrm/templates/get_started/books.html:16 +#: bookwyrm/templates/get_started/books.html:17 +#: bookwyrm/templates/get_started/users.html:18 +#: bookwyrm/templates/get_started/users.html:19 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/search/layout.html:4 +#: bookwyrm/templates/search/layout.html:9 +msgid "Search" +msgstr "Paieška" + +#: bookwyrm/templates/get_started/books.html:27 +msgid "Suggested Books" +msgstr "Siūlomos knygos" + +#: bookwyrm/templates/get_started/books.html:46 +#, python-format +msgid "Popular on %(site_name)s" +msgstr "%(site_name)s populiaru" + +#: bookwyrm/templates/get_started/books.html:58 +#: bookwyrm/templates/lists/list.html:155 +msgid "No books found" +msgstr "Knygų nerasta" + +#: bookwyrm/templates/get_started/books.html:63 +#: bookwyrm/templates/get_started/profile.html:54 +msgid "Save & continue" +msgstr "Išsaugoti ir tęsti" + +#: bookwyrm/templates/get_started/layout.html:5 +#: bookwyrm/templates/landing/layout.html:5 +msgid "Welcome" +msgstr "Sveiki atvykę" + +#: bookwyrm/templates/get_started/layout.html:15 +#, python-format +msgid "Welcome to %(site_name)s!" +msgstr "Sveiki atvykę į %(site_name)s!" + +#: bookwyrm/templates/get_started/layout.html:17 +msgid "These are some first steps to get you started." +msgstr "Pirmieji žingsniai, norint pradėti." + +#: bookwyrm/templates/get_started/layout.html:31 +#: bookwyrm/templates/get_started/profile.html:6 +msgid "Create your profile" +msgstr "Susikurkite paskyrą" + +#: bookwyrm/templates/get_started/layout.html:35 +msgid "Add books" +msgstr "Pridėti knygas" + +#: bookwyrm/templates/get_started/layout.html:39 +msgid "Find friends" +msgstr "Rasti draugų" + +#: bookwyrm/templates/get_started/layout.html:45 +msgid "Skip this step" +msgstr "Praleisti šį žingsnį" + +#: bookwyrm/templates/get_started/layout.html:49 +msgid "Finish" +msgstr "Baigti" + +#: bookwyrm/templates/get_started/profile.html:15 +#: bookwyrm/templates/preferences/edit_user.html:42 +msgid "Display name:" +msgstr "Rodyti vardą:" + +#: bookwyrm/templates/get_started/profile.html:22 +#: bookwyrm/templates/preferences/edit_user.html:49 +msgid "Summary:" +msgstr "Santrauka:" + +#: bookwyrm/templates/get_started/profile.html:23 +msgid "A little bit about you" +msgstr "Šiek tiek apie jus" + +#: bookwyrm/templates/get_started/profile.html:32 +#: bookwyrm/templates/preferences/edit_user.html:27 +msgid "Avatar:" +msgstr "Avataras:" + +#: bookwyrm/templates/get_started/profile.html:42 +#: bookwyrm/templates/preferences/edit_user.html:110 +msgid "Manually approve followers:" +msgstr "Noriu tvirtinti sekėjus:" + +#: bookwyrm/templates/get_started/profile.html:48 +#: bookwyrm/templates/preferences/edit_user.html:80 +msgid "Show this account in suggested users:" +msgstr "Paskyrą įtraukti į siūlomus narius:" + +#: bookwyrm/templates/get_started/profile.html:52 +msgid "Your account will show up in the directory, and may be recommended to other BookWyrm users." +msgstr "Jūsų paskyra atsiras kataloge ir gali būti rekomenduota kitiems „BookWyrm“ nariams." + +#: bookwyrm/templates/get_started/users.html:11 +msgid "Search for a user" +msgstr "Ieškoti naudotojo" + +#: bookwyrm/templates/get_started/users.html:13 +#, python-format +msgid "No users found for \"%(query)s\"" +msgstr "Pagal paiešką „%(query)s“ nieko nerasta" + +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "Sukurti grupę" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "Tvarko %(username)s" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "Ištrinti šią grupę?" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "Nebegalite atšaukti šio veiksmo" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "Ištrinti" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "Redaguoti grupę" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "Pridėkite naujų narių!" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "Grupės pavadinimas:" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "Grupės aprašymas:" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "Ištrinti grupę" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "Ieškokite, kad pridėtumėte naudotoją" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "Šioje grupėje nėra sąrašų" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "Redaguoti grupę" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "Nariai gali pridėti ir pašalinti knygas grupės knygų sąrašuose" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "Išeiti iš grupės" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "Jus seka" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "Sekate %(mutuals)s bendrą žmogų" +msgstr[1] "Sekate %(mutuals)s bendrus žmones" +msgstr[2] "Sekate %(mutuals)s bendrų žmonių" +msgstr[3] "Sekate %(mutuals)s bendrų žmonių" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s knyga jūsų lentynoje" +msgstr[1] "%(shared_books)s knygos jūsų lentynose" +msgstr[2] "%(shared_books)s knygų jūsų lentynose" +msgstr[3] "%(shared_books)s knygų jūsų lentynose" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "Užklausa „%(user_query)s“ nerasta potencialių narių" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "Vadovas" + +#: bookwyrm/templates/import/import.html:5 +#: bookwyrm/templates/import/import.html:9 +#: bookwyrm/templates/shelf/shelf.html:61 +msgid "Import Books" +msgstr "Importuoti knygas" + +#: bookwyrm/templates/import/import.html:18 +msgid "Data source:" +msgstr "Duomenų šaltinis:" + +#: bookwyrm/templates/import/import.html:37 +msgid "Data file:" +msgstr "Duomenų failas:" + +#: bookwyrm/templates/import/import.html:45 +msgid "Include reviews" +msgstr "Įtraukti atsiliepimus" + +#: bookwyrm/templates/import/import.html:50 +msgid "Privacy setting for imported reviews:" +msgstr "Privatumo nustatymai svarbiems atsiliepimams:" + +#: bookwyrm/templates/import/import.html:56 +#: bookwyrm/templates/settings/federation/instance_blocklist.html:64 +msgid "Import" +msgstr "Importuoti" + +#: bookwyrm/templates/import/import.html:61 +msgid "Recent Imports" +msgstr "Pastaruoju metu importuota" + +#: bookwyrm/templates/import/import.html:63 +msgid "No recent imports" +msgstr "Pastaruoju metu neimportuota" + +#: bookwyrm/templates/import/import_status.html:6 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 +msgid "Import Status" +msgstr "Importavimo būsena" + +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 +msgid "Import started:" +msgstr "Importavimas prasidėjo:" + +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:50 +msgid "Refresh" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:62 +#, python-format +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:94 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 +msgid "Title" +msgstr "Pavadinimas" + +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 +msgid "Author" +msgstr "Autorius" + +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "Peržiūra" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "Knyga" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "Būsena" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 +msgid "Imported" +msgstr "Importuota" + +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "Patvirtinti" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + +#: bookwyrm/templates/import/tooltip.html:6 +msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." +msgstr "Galite atsisiųsti savo „Goodreads“ duomenis iš Importavimo ir eksportavimo puslapio, esančio jūsų „Goodreads“ paskyroje." + +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + +#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 +#, python-format +msgid "About %(site_name)s" +msgstr "Apie %(site_name)s" + +#: bookwyrm/templates/landing/about.html:10 +#: bookwyrm/templates/landing/about.html:20 +msgid "Code of Conduct" +msgstr "Elgesio kodeksas" + +#: bookwyrm/templates/landing/about.html:13 +#: bookwyrm/templates/landing/about.html:29 +msgid "Privacy Policy" +msgstr "Privatumo politika" + +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "Kurti paskyrą" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "Prieiga draudžiama" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "Deja, šis pakvietimo kodas nebegalioja." + +#: bookwyrm/templates/landing/landing.html:6 +msgid "Recent Books" +msgstr "Naujos knygos" + +#: bookwyrm/templates/landing/layout.html:17 +msgid "Decentralized" +msgstr "Decentralizuota" + +#: bookwyrm/templates/landing/layout.html:23 +msgid "Friendly" +msgstr "Draugiškas" + +#: bookwyrm/templates/landing/layout.html:29 +msgid "Anti-Corporate" +msgstr "Nekorporacinis" + +#: bookwyrm/templates/landing/layout.html:45 +#, python-format +msgid "Join %(name)s" +msgstr "Prisijunkite prie %(name)s" + +#: bookwyrm/templates/landing/layout.html:47 +msgid "Request an Invitation" +msgstr "Prašyti kvietimo" + +#: bookwyrm/templates/landing/layout.html:49 +#, python-format +msgid "%(name)s registration is closed" +msgstr "%(name)s – registracija uždaryta" + +#: bookwyrm/templates/landing/layout.html:60 +msgid "Thank you! Your request has been received." +msgstr "Dėkojame, jūsų prašymas gautas." + +#: bookwyrm/templates/landing/layout.html:82 +msgid "Your Account" +msgstr "Jūsų paskyra" + +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "Prisijungti" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "Prisijunkite" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "Džiugu, el. pašto adresas patvirtintas." + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "Naudotojo vardas:" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "Slaptažodis:" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "Pamiršote slaptažodį?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "Daugiau apie šią svetainę" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "Patvirtinti slaptažodį:" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "Jūsų el. pašto adresu bus išsiųsta nuoroda pakeisti slaptažodį" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "Atstatyti slaptažodį" + +#: bookwyrm/templates/layout.html:13 +#, python-format +msgid "%(site_name)s search" +msgstr "%(site_name)s paieška" + +#: bookwyrm/templates/layout.html:43 +msgid "Search for a book, user, or list" +msgstr "Ieškoti knygos, naudotojo arba sąrašo" + +#: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62 +msgid "Main navigation menu" +msgstr "Pagrindinis navigacijos meniu" + +#: bookwyrm/templates/layout.html:72 +msgid "Feed" +msgstr "Srautas" + +#: bookwyrm/templates/layout.html:116 +msgid "Settings" +msgstr "Nustatymai" + +#: bookwyrm/templates/layout.html:125 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:15 +#: bookwyrm/templates/settings/invites/manage_invites.html:3 +#: bookwyrm/templates/settings/invites/manage_invites.html:15 +#: bookwyrm/templates/settings/layout.html:40 +msgid "Invites" +msgstr "Pakvietimai" + +#: bookwyrm/templates/layout.html:132 +msgid "Admin" +msgstr "Administratorius" + +#: bookwyrm/templates/layout.html:139 +msgid "Log out" +msgstr "Atsijungti" + +#: bookwyrm/templates/layout.html:147 bookwyrm/templates/layout.html:148 +#: bookwyrm/templates/notifications/notifications_page.html:5 +#: bookwyrm/templates/notifications/notifications_page.html:10 +msgid "Notifications" +msgstr "Pranešimai" + +#: bookwyrm/templates/layout.html:175 +msgid "password" +msgstr "slaptažodis" + +#: bookwyrm/templates/layout.html:187 +msgid "Join" +msgstr "Prisijungti" + +#: bookwyrm/templates/layout.html:221 +msgid "Successfully posted status" +msgstr "Būsena publikuota sėkmingai" + +#: bookwyrm/templates/layout.html:222 +msgid "Error posting status" +msgstr "Klaida, publikuojant būseną" + +#: bookwyrm/templates/layout.html:234 +msgid "Contact site admin" +msgstr "Puslapio administratorius" + +#: bookwyrm/templates/layout.html:238 +msgid "Documentation" +msgstr "Dokumentacija" + +#: bookwyrm/templates/layout.html:245 +#, python-format +msgid "Support %(site_name)s on %(support_title)s" +msgstr "Paremkite %(site_name)s per %(support_title)s" + +#: bookwyrm/templates/layout.html:249 +msgid "BookWyrm's source code is freely available. You can contribute or report issues on GitHub." +msgstr "„BookWyrm“ šaltinio kodas yra laisvai prieinamas. Galite prisidėti arba pranešti apie klaidas per GitHub." + +#: bookwyrm/templates/lists/bookmark_button.html:30 +msgid "Un-save" +msgstr "Nebesaugoti" + +#: bookwyrm/templates/lists/create_form.html:5 +#: bookwyrm/templates/lists/lists.html:20 +msgid "Create List" +msgstr "Sukurti sąrašą" + +#: bookwyrm/templates/lists/created_text.html:5 +#, python-format +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "Sukūrė %(username)s, tvarko %(groupname)s" + +#: bookwyrm/templates/lists/created_text.html:7 +#, python-format +msgid "Created and curated by %(username)s" +msgstr "Sukūrė ir kuruoja %(username)s" + +#: bookwyrm/templates/lists/created_text.html:9 +#, python-format +msgid "Created by %(username)s" +msgstr "Sukūrė %(username)s" + +#: bookwyrm/templates/lists/curate.html:8 +msgid "Pending Books" +msgstr "Patvirtinimo laukiančios knygos" + +#: bookwyrm/templates/lists/curate.html:11 +msgid "Go to list" +msgstr "Eiti į sąrašą" + +#: bookwyrm/templates/lists/curate.html:15 +msgid "You're all set!" +msgstr "Viskas atlikta!" + +#: bookwyrm/templates/lists/curate.html:45 +msgid "Suggested by" +msgstr "Pasiūlė" + +#: bookwyrm/templates/lists/curate.html:63 +msgid "Discard" +msgstr "Atmesti" + +#: bookwyrm/templates/lists/delete_list_modal.html:4 +msgid "Delete this list?" +msgstr "Ištrinti šį sąrašą?" + +#: bookwyrm/templates/lists/edit_form.html:5 +#: bookwyrm/templates/lists/layout.html:16 +msgid "Edit List" +msgstr "Redaguoti sąrašą" + +#: bookwyrm/templates/lists/form.html:19 +msgid "List curation:" +msgstr "Sąrašo kuravimas:" + +#: bookwyrm/templates/lists/form.html:22 +msgid "Closed" +msgstr "Uždaryta" + +#: bookwyrm/templates/lists/form.html:23 +msgid "Only you can add and remove books to this list" +msgstr "Tik jūs galite pridėti ar pašalinti knygas iš šio sąrašo" + +#: bookwyrm/templates/lists/form.html:27 +msgid "Curated" +msgstr "Kuruojama" + +#: bookwyrm/templates/lists/form.html:28 +msgid "Anyone can suggest books, subject to your approval" +msgstr "Knygas gali siūlyti visi, tačiau jūs turėsite patvirtinti" + +#: bookwyrm/templates/lists/form.html:32 +msgctxt "curation type" +msgid "Open" +msgstr "Atidaryti" + +#: bookwyrm/templates/lists/form.html:33 +msgid "Anyone can add books to this list" +msgstr "Visi gali pridėti knygų į sąrašą" + +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "Grupė" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "Grupės nariai gali pridėti ir išimti iš sąrašo" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "Pasirinkti grupę" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "Pasirinkite grupę" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "Dar neturite grupių!" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "Sukurti grupę" + +#: bookwyrm/templates/lists/form.html:81 +msgid "Delete list" +msgstr "Ištrinti sąrašą" + +#: bookwyrm/templates/lists/list.html:21 +msgid "You successfully suggested a book for this list!" +msgstr "Sėkmingai pasiūlėte knygą šiam sąrašui!" + +#: bookwyrm/templates/lists/list.html:23 +msgid "You successfully added a book to this list!" +msgstr "Sėkmingai pridėjote knygą į šį sąrašą!" + +#: bookwyrm/templates/lists/list.html:29 +msgid "This list is currently empty" +msgstr "Šiuo metu sąrašas tuščias" + +#: bookwyrm/templates/lists/list.html:67 +#, python-format +msgid "Added by %(username)s" +msgstr "Pridėjo %(username)s" + +#: bookwyrm/templates/lists/list.html:76 +msgid "List position" +msgstr "Sąrašo pozicija" + +#: bookwyrm/templates/lists/list.html:82 +msgid "Set" +msgstr "Nustatyti" + +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 +#: bookwyrm/templates/snippets/shelf_selector.html:26 +msgid "Remove" +msgstr "Pašalinti" + +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 +msgid "Sort List" +msgstr "Rūšiuoti sąrašą" + +#: bookwyrm/templates/lists/list.html:116 +msgid "Direction" +msgstr "Kryptis" + +#: bookwyrm/templates/lists/list.html:130 +msgid "Add Books" +msgstr "Pridėti knygų" + +#: bookwyrm/templates/lists/list.html:132 +msgid "Suggest Books" +msgstr "Siūlyti knygų" + +#: bookwyrm/templates/lists/list.html:143 +msgid "search" +msgstr "paieška" + +#: bookwyrm/templates/lists/list.html:149 +msgid "Clear search" +msgstr "Išvalyti paiešką" + +#: bookwyrm/templates/lists/list.html:154 +#, python-format +msgid "No books found matching the query \"%(query)s\"" +msgstr "Pagal paiešką „%(query)s“ knygų nerasta" + +#: bookwyrm/templates/lists/list.html:182 +msgid "Suggest" +msgstr "Siūlyti" + +#: bookwyrm/templates/lists/list_items.html:15 +msgid "Saved" +msgstr "Išsaugota" + +#: bookwyrm/templates/lists/lists.html:14 bookwyrm/templates/user/lists.html:9 +msgid "Your Lists" +msgstr "Jūsų sąrašai" + +#: bookwyrm/templates/lists/lists.html:36 +msgid "All Lists" +msgstr "Visi sąrašai" + +#: bookwyrm/templates/lists/lists.html:40 +msgid "Saved Lists" +msgstr "Išsaugoti sąrašai" + +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" +msgstr "priėmė jūsų kvietimą prisijungti prie grupės „%(group_name)s“" + +#: bookwyrm/templates/notifications/items/add.html:24 +#, python-format +msgid "added %(book_title)s to your list \"%(list_name)s\"" +msgstr "į jūsų sąrašą „%(list_name)s“ pridėta %(book_title)s" + +#: bookwyrm/templates/notifications/items/add.html:31 +#, python-format +msgid "suggested adding %(book_title)s to your list \"%(list_name)s\"" +msgstr "į sąrašą „%(list_name)s\" patariama pridėti %(book_title)s" + +#: bookwyrm/templates/notifications/items/boost.html:19 +#, python-format +msgid "boosted your review of %(book_title)s" +msgstr "populiarėja jūsų atsiliepimas apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/boost.html:25 +#, python-format +msgid "boosted your comment on%(book_title)s" +msgstr "populiarėja jūsų komentaras apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/boost.html:31 +#, python-format +msgid "boosted your quote from %(book_title)s" +msgstr "populiarėja jūsų citata iš %(book_title)s" + +#: bookwyrm/templates/notifications/items/boost.html:37 +#, python-format +msgid "boosted your status" +msgstr "populiarėja jūsų būsena" + +#: bookwyrm/templates/notifications/items/fav.html:19 +#, python-format +msgid "liked your review of %(book_title)s" +msgstr "patiko jūsų atsiliepimas apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/fav.html:25 +#, python-format +msgid "liked your comment on %(book_title)s" +msgstr "patiko jūsų komentaras apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/fav.html:31 +#, python-format +msgid "liked your quote from %(book_title)s" +msgstr "patiko jūsų citata iš %(book_title)s" + +#: bookwyrm/templates/notifications/items/fav.html:37 +#, python-format +msgid "liked your status" +msgstr "patiko jūsų būsena" + +#: bookwyrm/templates/notifications/items/follow.html:15 +msgid "followed you" +msgstr "pradėjo jus sekti" + +#: bookwyrm/templates/notifications/items/follow_request.html:11 +msgid "sent you a follow request" +msgstr "nori jus sekti" + +#: bookwyrm/templates/notifications/items/import.html:14 +#, python-format +msgid "Your import completed." +msgstr "Jūsų importas baigtas." + +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "jus pakvietė prisijungti prie grupės „%(group_name)s“" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "prisijungė prie jūsų grupės „%(group_name)s“" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "paliko jūsų grupę „%(group_name)s“" + +#: bookwyrm/templates/notifications/items/mention.html:20 +#, python-format +msgid "mentioned you in a review of %(book_title)s" +msgstr "paminėjo jus atsiliepime apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/mention.html:26 +#, python-format +msgid "mentioned you in a comment on %(book_title)s" +msgstr "paminėjo jus komentare apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/mention.html:32 +#, python-format +msgid "mentioned you in a quote from %(book_title)s" +msgstr "paminėjo jus citatoje iš %(book_title)s" + +#: bookwyrm/templates/notifications/items/mention.html:38 +#, python-format +msgid "mentioned you in a status" +msgstr "paminėjo jus būsenoje" + +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "buvo pašalintas iš jūsų grupės „%(group_name)s“" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "Buvote pašalintas iš grupės „%(group_name)s“" + +#: bookwyrm/templates/notifications/items/reply.html:21 +#, python-format +msgid "replied to your review of %(book_title)s" +msgstr "atsakė į jūsų atsiliepimą apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/reply.html:27 +#, python-format +msgid "replied to your comment on %(book_title)s" +msgstr "atsakė į jūsų komentarą apie %(book_title)s" + +#: bookwyrm/templates/notifications/items/reply.html:33 +#, python-format +msgid "replied to your quote from %(book_title)s" +msgstr "atsakė į jūsų citatą iš %(book_title)s" + +#: bookwyrm/templates/notifications/items/reply.html:39 +#, python-format +msgid "replied to your status" +msgstr "atsakė į jūsų būseną" + +#: bookwyrm/templates/notifications/items/report.html:15 +#, python-format +msgid "A new report needs moderation." +msgstr "Reikia moderuoti pranešimą." + +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "pakeitė privatumo lygį grupei %(group_name)s" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "pakeitė %(group_name)s pavadinimą" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "pakeitė %(group_name)s aprašymą" + +#: bookwyrm/templates/notifications/notifications_page.html:18 +msgid "Delete notifications" +msgstr "Ištrinti pranešimus" + +#: bookwyrm/templates/notifications/notifications_page.html:29 +msgid "All" +msgstr "Visi" + +#: bookwyrm/templates/notifications/notifications_page.html:33 +msgid "Mentions" +msgstr "Paminėjimai" + +#: bookwyrm/templates/notifications/notifications_page.html:45 +msgid "You're all caught up!" +msgstr "Viską peržiūrėjote!" + +#: bookwyrm/templates/preferences/blocks.html:4 +#: bookwyrm/templates/preferences/blocks.html:7 +#: bookwyrm/templates/preferences/layout.html:31 +msgid "Blocked Users" +msgstr "Blokuoti nariai" + +#: bookwyrm/templates/preferences/blocks.html:12 +msgid "No users currently blocked." +msgstr "Blokuotų narių nėra." + +#: bookwyrm/templates/preferences/change_password.html:4 +#: bookwyrm/templates/preferences/change_password.html:7 +#: bookwyrm/templates/preferences/change_password.html:21 +#: bookwyrm/templates/preferences/layout.html:20 +msgid "Change Password" +msgstr "Keisti slaptažodį" + +#: bookwyrm/templates/preferences/change_password.html:14 +msgid "New password:" +msgstr "Naujas slaptažodis:" + +#: bookwyrm/templates/preferences/delete_user.html:4 +#: bookwyrm/templates/preferences/delete_user.html:7 +#: bookwyrm/templates/preferences/delete_user.html:26 +#: bookwyrm/templates/preferences/layout.html:24 +#: bookwyrm/templates/settings/users/delete_user_form.html:23 +msgid "Delete Account" +msgstr "Pašalinti paskyrą" + +#: bookwyrm/templates/preferences/delete_user.html:12 +msgid "Permanently delete account" +msgstr "Visam laikui ištrinti paskyrą" + +#: bookwyrm/templates/preferences/delete_user.html:14 +msgid "Deleting your account cannot be undone. The username will not be available to register in the future." +msgstr "Nebegalėsite atstatyti ištrintos paskyros. Ateityje nebegalėsite naudoti šio naudotojo vardo." + +#: bookwyrm/templates/preferences/edit_user.html:4 +#: bookwyrm/templates/preferences/edit_user.html:7 +#: bookwyrm/templates/preferences/layout.html:15 +msgid "Edit Profile" +msgstr "Redaguoti profilį" + +#: bookwyrm/templates/preferences/edit_user.html:12 +#: bookwyrm/templates/preferences/edit_user.html:25 +#: bookwyrm/templates/settings/users/user_info.html:7 +msgid "Profile" +msgstr "Profilis" + +#: bookwyrm/templates/preferences/edit_user.html:13 +#: bookwyrm/templates/preferences/edit_user.html:68 +msgid "Display preferences" +msgstr "Vaizdo nustatymai" + +#: bookwyrm/templates/preferences/edit_user.html:14 +#: bookwyrm/templates/preferences/edit_user.html:106 +msgid "Privacy" +msgstr "Privatumas" + +#: bookwyrm/templates/preferences/edit_user.html:72 +msgid "Show reading goal prompt in feed:" +msgstr "Rodyti skaitymo tikslą sienoje:" + +#: bookwyrm/templates/preferences/edit_user.html:76 +msgid "Show suggested users:" +msgstr "Rodyti siūlomus narius:" + +#: bookwyrm/templates/preferences/edit_user.html:85 +#, python-format +msgid "Your account will show up in the directory, and may be recommended to other BookWyrm users." +msgstr "Jūsų paskyra atsiras kataloge ir gali būti rekomenduota kitiems „BookWyrm“ nariams." + +#: bookwyrm/templates/preferences/edit_user.html:89 +msgid "Preferred Timezone: " +msgstr "Laiko juosta: " + +#: bookwyrm/templates/preferences/edit_user.html:116 +msgid "Default post privacy:" +msgstr "Numatytasis įrašo privatumas:" + +#: bookwyrm/templates/preferences/layout.html:11 +msgid "Account" +msgstr "Paskyra" + +#: bookwyrm/templates/preferences/layout.html:27 +msgid "Relationships" +msgstr "Sąsajos" + +#: bookwyrm/templates/reading_progress/finish.html:5 +#, python-format +msgid "Finish \"%(book_title)s\"" +msgstr "Užbaigti „%(book_title)s“" + +#: bookwyrm/templates/reading_progress/start.html:5 +#, python-format +msgid "Start \"%(book_title)s\"" +msgstr "Pradėti „%(book_title)s“" + +#: bookwyrm/templates/reading_progress/want.html:5 +#, python-format +msgid "Want to Read \"%(book_title)s\"" +msgstr "Noriu perskaityti „%(book_title)s“" + +#: bookwyrm/templates/search/book.html:47 +#: bookwyrm/templates/settings/reports/reports.html:25 +#: bookwyrm/templates/snippets/announcement.html:16 +msgid "Open" +msgstr "Atidaryti" + +#: bookwyrm/templates/search/book.html:85 +msgid "Import book" +msgstr "Importuoti knygą" + +#: bookwyrm/templates/search/book.html:107 +msgid "Load results from other catalogues" +msgstr "Įkelti rezultatus iš kitų katalogų" + +#: bookwyrm/templates/search/book.html:111 +msgid "Manually add book" +msgstr "Pridėti knygą" + +#: bookwyrm/templates/search/book.html:116 +msgid "Log in to import or add books." +msgstr "Prisijunkite, kad importuotumėte arba pridėtumėte knygas." + +#: bookwyrm/templates/search/layout.html:16 +msgid "Search query" +msgstr "Paieškos užklausa" + +#: bookwyrm/templates/search/layout.html:19 +msgid "Search type" +msgstr "Paieškos tipas" + +#: bookwyrm/templates/search/layout.html:23 +#: bookwyrm/templates/search/layout.html:46 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:27 +#: bookwyrm/templates/settings/federation/instance_list.html:44 +#: bookwyrm/templates/settings/layout.html:34 +#: bookwyrm/templates/settings/users/user_admin.html:3 +#: bookwyrm/templates/settings/users/user_admin.html:10 +msgid "Users" +msgstr "Nariai" + +#: bookwyrm/templates/search/layout.html:58 +#, python-format +msgid "No results found for \"%(query)s\"" +msgstr "Pagal paiešką „%(query)s“ nieko nerasta" + +#: bookwyrm/templates/settings/announcements/announcement.html:3 +#: bookwyrm/templates/settings/announcements/announcement.html:6 +msgid "Announcement" +msgstr "Pranešimas" + +#: bookwyrm/templates/settings/announcements/announcement.html:7 +#: bookwyrm/templates/settings/federation/instance.html:13 +msgid "Back to list" +msgstr "Atgal į sąrašą" + +#: bookwyrm/templates/settings/announcements/announcement.html:11 +#: bookwyrm/templates/settings/announcements/announcement_form.html:6 +msgid "Edit Announcement" +msgstr "Redaguoti pranešimą" + +#: bookwyrm/templates/settings/announcements/announcement.html:35 +msgid "Visible:" +msgstr "Matoma:" + +#: bookwyrm/templates/settings/announcements/announcement.html:38 +msgid "True" +msgstr "Tiesa" + +#: bookwyrm/templates/settings/announcements/announcement.html:40 +msgid "False" +msgstr "Netiesa" + +#: bookwyrm/templates/settings/announcements/announcement.html:47 +#: bookwyrm/templates/settings/announcements/announcement_form.html:40 +#: bookwyrm/templates/settings/dashboard/dashboard.html:71 +msgid "Start date:" +msgstr "Pradžios data:" + +#: bookwyrm/templates/settings/announcements/announcement.html:54 +#: bookwyrm/templates/settings/announcements/announcement_form.html:49 +#: bookwyrm/templates/settings/dashboard/dashboard.html:77 +msgid "End date:" +msgstr "Pabaigos data:" + +#: bookwyrm/templates/settings/announcements/announcement.html:60 +#: bookwyrm/templates/settings/announcements/announcement_form.html:58 +msgid "Active:" +msgstr "Aktyvu:" + +#: bookwyrm/templates/settings/announcements/announcement_form.html:8 +#: bookwyrm/templates/settings/announcements/announcements.html:8 +msgid "Create Announcement" +msgstr "Sukurti pranešimą" + +#: bookwyrm/templates/settings/announcements/announcement_form.html:16 +msgid "Preview:" +msgstr "Peržiūra:" + +#: bookwyrm/templates/settings/announcements/announcement_form.html:23 +msgid "Content:" +msgstr "Turinys:" + +#: bookwyrm/templates/settings/announcements/announcement_form.html:30 +msgid "Event date:" +msgstr "Įvykio data:" + +#: bookwyrm/templates/settings/announcements/announcements.html:3 +#: bookwyrm/templates/settings/announcements/announcements.html:5 +#: bookwyrm/templates/settings/layout.html:72 +msgid "Announcements" +msgstr "Pranešimai" + +#: bookwyrm/templates/settings/announcements/announcements.html:22 +#: bookwyrm/templates/settings/federation/instance_list.html:36 +msgid "Date added" +msgstr "Pridėjimo data" + +#: bookwyrm/templates/settings/announcements/announcements.html:26 +msgid "Preview" +msgstr "Peržiūrėti" + +#: bookwyrm/templates/settings/announcements/announcements.html:30 +msgid "Start date" +msgstr "Pradžios data" + +#: bookwyrm/templates/settings/announcements/announcements.html:34 +msgid "End date" +msgstr "Pabaigos data" + +#: bookwyrm/templates/settings/announcements/announcements.html:48 +msgid "active" +msgstr "aktyvus" + +#: bookwyrm/templates/settings/announcements/announcements.html:48 +msgid "inactive" +msgstr "neaktyvus" + +#: bookwyrm/templates/settings/announcements/announcements.html:52 +msgid "No announcements found" +msgstr "Pranešimų nerasta" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:6 +#: bookwyrm/templates/settings/dashboard/dashboard.html:8 +#: bookwyrm/templates/settings/layout.html:26 +msgid "Dashboard" +msgstr "Suvestinė" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:15 +#: bookwyrm/templates/settings/dashboard/dashboard.html:100 +msgid "Total users" +msgstr "Iš viso naudotojų" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:21 +#: bookwyrm/templates/settings/dashboard/user_chart.html:16 +msgid "Active this month" +msgstr "Aktyvūs šį mėnesį" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:27 +msgid "Statuses" +msgstr "Būsenos" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:33 +#: bookwyrm/templates/settings/dashboard/works_chart.html:11 +msgid "Works" +msgstr "Darbai" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:43 +#, python-format +msgid "%(display_count)s open report" +msgid_plural "%(display_count)s open reports" +msgstr[0] "%(display_count)s atvira ataskaita" +msgstr[1] "%(display_count)s atviros ataskaitos" +msgstr[2] "%(display_count)s atviros ataskaitos" +msgstr[3] "%(display_count)s atvirų ataskaitų" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:54 +#, python-format +msgid "%(display_count)s invite request" +msgid_plural "%(display_count)s invite requests" +msgstr[0] "%(display_count)s prašymas pakviesti" +msgstr[1] "%(display_count)s prašymai pakviesti" +msgstr[2] "%(display_count)s prašymų pakviesti" +msgstr[3] "%(display_count)s prašymai pakviesti" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:65 +msgid "Instance Activity" +msgstr "Pavyzdinė veikla" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:83 +msgid "Interval:" +msgstr "Intervalas:" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:87 +msgid "Days" +msgstr "Dienos" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:88 +msgid "Weeks" +msgstr "Savaitės" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:106 +msgid "User signup activity" +msgstr "Naudotojo prisijungimo veikla" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:112 +msgid "Status activity" +msgstr "Būsenos veikla" + +#: bookwyrm/templates/settings/dashboard/dashboard.html:118 +msgid "Works created" +msgstr "Darbai sukurti" + +#: bookwyrm/templates/settings/dashboard/registration_chart.html:10 +msgid "Registrations" +msgstr "Registracijos" + +#: bookwyrm/templates/settings/dashboard/status_chart.html:11 +msgid "Statuses posted" +msgstr "Būsenos publikuotos" + +#: bookwyrm/templates/settings/dashboard/user_chart.html:11 +msgid "Total" +msgstr "Iš viso" + +#: bookwyrm/templates/settings/email_blocklist/domain_form.html:5 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10 +msgid "Add domain" +msgstr "Pridėti domeną" + +#: bookwyrm/templates/settings/email_blocklist/domain_form.html:11 +msgid "Domain:" +msgstr "Domenas:" + +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:5 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:7 +#: bookwyrm/templates/settings/layout.html:59 +msgid "Email Blocklist" +msgstr "El. pašto blokavimo sąrašas" + +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:18 +msgid "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." +msgstr "Jei kažkas bandys registruotis prie šio domeno šiuo el. pašto adresu, paskyra nebus sukurta. Registracijos pricesas bus suveikęs." + +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:25 +msgid "Domain" +msgstr "Domenas" + +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:29 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:27 +msgid "Options" +msgstr "Parinktys" + +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:38 +#, python-format +msgid "%(display_count)s user" +msgid_plural "%(display_count)s users" +msgstr[0] "%(display_count)s narys" +msgstr[1] "%(display_count)s nariai" +msgstr[2] "%(display_count)s narių" +msgstr[3] "%(display_count)s nariai" + +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:59 +msgid "No email domains currently blocked" +msgstr "Šiuo metu neblokuojamas nė vienas el. pašto domenas" + +#: bookwyrm/templates/settings/federation/edit_instance.html:3 +#: bookwyrm/templates/settings/federation/edit_instance.html:6 +#: bookwyrm/templates/settings/federation/edit_instance.html:20 +#: bookwyrm/templates/settings/federation/instance_blocklist.html:3 +#: bookwyrm/templates/settings/federation/instance_blocklist.html:20 +#: bookwyrm/templates/settings/federation/instance_list.html:9 +#: bookwyrm/templates/settings/federation/instance_list.html:10 +msgid "Add instance" +msgstr "Pridėti serverį" + +#: bookwyrm/templates/settings/federation/edit_instance.html:7 +#: bookwyrm/templates/settings/federation/instance_blocklist.html:7 +msgid "Back to instance list" +msgstr "Grįžti į serverių sąrašą" + +#: bookwyrm/templates/settings/federation/edit_instance.html:16 +#: bookwyrm/templates/settings/federation/instance_blocklist.html:16 +msgid "Import block list" +msgstr "Importuoti blokuojamų sąrašą" + +#: bookwyrm/templates/settings/federation/edit_instance.html:30 +msgid "Instance:" +msgstr "Serveris:" + +#: bookwyrm/templates/settings/federation/edit_instance.html:39 +#: bookwyrm/templates/settings/federation/instance.html:28 +#: bookwyrm/templates/settings/users/user_info.html:106 +msgid "Status:" +msgstr "Būsena:" + +#: bookwyrm/templates/settings/federation/edit_instance.html:52 +#: bookwyrm/templates/settings/federation/instance.html:22 +#: bookwyrm/templates/settings/users/user_info.html:100 +msgid "Software:" +msgstr "Programinė įranga:" + +#: bookwyrm/templates/settings/federation/edit_instance.html:61 +#: bookwyrm/templates/settings/federation/instance.html:25 +#: bookwyrm/templates/settings/users/user_info.html:103 +msgid "Version:" +msgstr "Versija:" + +#: bookwyrm/templates/settings/federation/edit_instance.html:70 +msgid "Notes:" +msgstr "Užrašai:" + +#: bookwyrm/templates/settings/federation/instance.html:19 +msgid "Details" +msgstr "Išsami informacija" + +#: bookwyrm/templates/settings/federation/instance.html:35 +#: bookwyrm/templates/user/layout.html:64 +msgid "Activity" +msgstr "Veikla" + +#: bookwyrm/templates/settings/federation/instance.html:38 +msgid "Users:" +msgstr "Nariai:" + +#: bookwyrm/templates/settings/federation/instance.html:41 +#: bookwyrm/templates/settings/federation/instance.html:47 +msgid "View all" +msgstr "Žiūrėti viską" + +#: bookwyrm/templates/settings/federation/instance.html:44 +#: bookwyrm/templates/settings/users/user_info.html:56 +msgid "Reports:" +msgstr "Pranešimai:" + +#: bookwyrm/templates/settings/federation/instance.html:50 +msgid "Followed by us:" +msgstr "Sekame:" + +#: bookwyrm/templates/settings/federation/instance.html:55 +msgid "Followed by them:" +msgstr "Seka:" + +#: bookwyrm/templates/settings/federation/instance.html:60 +msgid "Blocked by us:" +msgstr "Blokuojame:" + +#: bookwyrm/templates/settings/federation/instance.html:72 +#: bookwyrm/templates/settings/users/user_info.html:110 +msgid "Notes" +msgstr "Užrašai" + +#: bookwyrm/templates/settings/federation/instance.html:75 +#: bookwyrm/templates/snippets/status/status_options.html:24 +msgid "Edit" +msgstr "Redaguoti" + +#: bookwyrm/templates/settings/federation/instance.html:79 +msgid "No notes" +msgstr "Užrašų nėra" + +#: bookwyrm/templates/settings/federation/instance.html:94 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:8 +msgid "Actions" +msgstr "Veiksmai" + +#: bookwyrm/templates/settings/federation/instance.html:98 +#: bookwyrm/templates/snippets/block_button.html:5 +msgid "Block" +msgstr "Blokuoti" + +#: bookwyrm/templates/settings/federation/instance.html:99 +msgid "All users from this instance will be deactivated." +msgstr "Visi šio serverio nariai bus deaktyvuoti." + +#: bookwyrm/templates/settings/federation/instance.html:104 +#: bookwyrm/templates/snippets/block_button.html:10 +msgid "Un-block" +msgstr "Atblokuoti" + +#: bookwyrm/templates/settings/federation/instance.html:105 +msgid "All users from this instance will be re-activated." +msgstr "Visi šio serverio nariai bus vėl aktyvuoti." + +#: bookwyrm/templates/settings/federation/instance_blocklist.html:6 +msgid "Import Blocklist" +msgstr "Importuoti blokuojamų sąrašą" + +#: bookwyrm/templates/settings/federation/instance_blocklist.html:26 +#: bookwyrm/templates/snippets/goal_progress.html:7 +msgid "Success!" +msgstr "Valio!" + +#: bookwyrm/templates/settings/federation/instance_blocklist.html:30 +msgid "Successfully blocked:" +msgstr "Sėkmingai užblokuota:" + +#: bookwyrm/templates/settings/federation/instance_blocklist.html:32 +msgid "Failed:" +msgstr "Nepavyko:" + +#: bookwyrm/templates/settings/federation/instance_list.html:3 +#: bookwyrm/templates/settings/federation/instance_list.html:5 +#: bookwyrm/templates/settings/layout.html:45 +msgid "Federated Instances" +msgstr "Susijungę serveriai" + +#: bookwyrm/templates/settings/federation/instance_list.html:32 +#: bookwyrm/templates/settings/users/server_filter.html:5 +msgid "Instance name" +msgstr "Serverio pavadinimas" + +#: bookwyrm/templates/settings/federation/instance_list.html:40 +msgid "Software" +msgstr "Programinė įranga" + +#: bookwyrm/templates/settings/federation/instance_list.html:63 +msgid "No instances found" +msgstr "Serverių nerasta" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:4 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:11 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:25 +#: bookwyrm/templates/settings/invites/manage_invites.html:11 +msgid "Invite Requests" +msgstr "Kvietimo prašymai" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:23 +msgid "Ignored Invite Requests" +msgstr "Ignoruoti kvietimo prašymai" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:35 +msgid "Date requested" +msgstr "Prašymo data" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:39 +msgid "Date accepted" +msgstr "Priėmimo data" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:42 +msgid "Email" +msgstr "El. paštas" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:47 +msgid "Action" +msgstr "Veiksmas" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:50 +msgid "No requests" +msgstr "Prašymų nėra" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:59 +#: bookwyrm/templates/settings/invites/status_filter.html:16 +msgid "Accepted" +msgstr "Priimta" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:61 +#: bookwyrm/templates/settings/invites/status_filter.html:12 +msgid "Sent" +msgstr "Išsiųsta" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:63 +#: bookwyrm/templates/settings/invites/status_filter.html:8 +msgid "Requested" +msgstr "Užklausta" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:73 +msgid "Send invite" +msgstr "Siųsti pakvietimą" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:75 +msgid "Re-send invite" +msgstr "Pakartotinai siųsti pakvietimą" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:95 +msgid "Ignore" +msgstr "Ignoruoti" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:97 +msgid "Un-ignore" +msgstr "Nebeignoruoti" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:108 +msgid "Back to pending requests" +msgstr "Grįžti į laukiančius prašymus" + +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:110 +msgid "View ignored requests" +msgstr "Žiūrėti ignoruotus prašymus" + +#: bookwyrm/templates/settings/invites/manage_invites.html:21 +msgid "Generate New Invite" +msgstr "Sugeneruoti naują pakvietimą" + +#: bookwyrm/templates/settings/invites/manage_invites.html:27 +msgid "Expiry:" +msgstr "Galiojimo pabaiga:" + +#: bookwyrm/templates/settings/invites/manage_invites.html:33 +msgid "Use limit:" +msgstr "Naudojimo limitas:" + +#: bookwyrm/templates/settings/invites/manage_invites.html:40 +msgid "Create Invite" +msgstr "Sukurti pakvietimą" + +#: bookwyrm/templates/settings/invites/manage_invites.html:47 +msgid "Link" +msgstr "Nuoroda" + +#: bookwyrm/templates/settings/invites/manage_invites.html:48 +msgid "Expires" +msgstr "Baigia galioti" + +#: bookwyrm/templates/settings/invites/manage_invites.html:49 +msgid "Max uses" +msgstr "Maks. naudojimų" + +#: bookwyrm/templates/settings/invites/manage_invites.html:50 +msgid "Times used" +msgstr "Kartų naudota" + +#: bookwyrm/templates/settings/invites/manage_invites.html:53 +msgid "No active invites" +msgstr "Nėra aktyvių pakvietimų" + +#: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:5 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:10 +msgid "Add IP address" +msgstr "Pridėti IP adresą" + +#: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:11 +msgid "Use IP address blocks with caution, and consider using blocks only temporarily, as IP addresses are often shared or change hands. If you block your own IP, you will not be able to access this page." +msgstr "Atsargiai naudokite IP adresų blokus. Rekomenduojame juos naudoti tik laikinai, nes IP adresai dažnu atveju yra bendrinami arba pereina kitiems. Jei užblokuosite savo IP, nebegalėsite pasiekti šio puslapio." + +#: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:18 +msgid "IP Address:" +msgstr "IP adresas:" + +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:5 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:7 +#: bookwyrm/templates/settings/layout.html:63 +msgid "IP Address Blocklist" +msgstr "Juodasis IP adresų sąrašas" + +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:18 +msgid "Any traffic from this IP address will get a 404 response when trying to access any part of the application." +msgstr "Bandant pasiekti bet kurią programėlės dalį, šiam IP adresui visada matysis 404 klaidos kodas." + +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:24 +msgid "Address" +msgstr "Adresas" + +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:46 +msgid "No IP addresses currently blocked" +msgstr "Šiuo metu neblokuojamas joks IP adresas" + +#: bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html:6 +msgid "You can block IP ranges using CIDR syntax." +msgstr "IP rėžius galite blokuoti naudodami CIDR sintaksę." + +#: bookwyrm/templates/settings/layout.html:4 +msgid "Administration" +msgstr "Administravimas" + +#: bookwyrm/templates/settings/layout.html:29 +msgid "Manage Users" +msgstr "Tvarkyti naudotojus" + +#: bookwyrm/templates/settings/layout.html:51 +msgid "Moderation" +msgstr "Moderavimas" + +#: bookwyrm/templates/settings/layout.html:55 +#: bookwyrm/templates/settings/reports/reports.html:8 +#: bookwyrm/templates/settings/reports/reports.html:17 +msgid "Reports" +msgstr "Pranešimai" + +#: bookwyrm/templates/settings/layout.html:68 +msgid "Instance Settings" +msgstr "Serverio nustatymai" + +#: bookwyrm/templates/settings/layout.html:76 +#: bookwyrm/templates/settings/site.html:4 +#: bookwyrm/templates/settings/site.html:6 +msgid "Site Settings" +msgstr "Puslapio nustatymai" + +#: bookwyrm/templates/settings/reports/report.html:5 +#: bookwyrm/templates/settings/reports/report.html:8 +#: bookwyrm/templates/settings/reports/report_preview.html:6 +#, python-format +msgid "Report #%(report_id)s: %(username)s" +msgstr "Pranešti apie #%(report_id)s: %(username)s" + +#: bookwyrm/templates/settings/reports/report.html:9 +msgid "Back to reports" +msgstr "Atgal į pranešimus" + +#: bookwyrm/templates/settings/reports/report.html:23 +msgid "Moderator Comments" +msgstr "Moderatoriaus komentarai" + +#: bookwyrm/templates/settings/reports/report.html:41 +#: bookwyrm/templates/snippets/create_status.html:28 +msgid "Comment" +msgstr "Komentuoti" + +#: bookwyrm/templates/settings/reports/report.html:46 +msgid "Reported statuses" +msgstr "Praneštos būsenos" + +#: bookwyrm/templates/settings/reports/report.html:48 +msgid "No statuses reported" +msgstr "Nepranešta apie būsenas" + +#: bookwyrm/templates/settings/reports/report.html:54 +msgid "Status has been deleted" +msgstr "Būsena ištrinta" + +#: bookwyrm/templates/settings/reports/report_preview.html:13 +msgid "No notes provided" +msgstr "Užrašų nepateikta" + +#: bookwyrm/templates/settings/reports/report_preview.html:20 +#, python-format +msgid "Reported by %(username)s" +msgstr "Pranešė %(username)s" + +#: bookwyrm/templates/settings/reports/report_preview.html:30 +msgid "Re-open" +msgstr "Atidaryti pakartotinai" + +#: bookwyrm/templates/settings/reports/report_preview.html:32 +msgid "Resolve" +msgstr "Išspręsti" + +#: bookwyrm/templates/settings/reports/reports.html:6 +#, python-format +msgid "Reports: %(instance_name)s" +msgstr "Pranešimai: %(instance_name)s" + +#: bookwyrm/templates/settings/reports/reports.html:14 +#, python-format +msgid "Reports: %(instance_name)s" +msgstr "Pranešimai: %(instance_name)s" + +#: bookwyrm/templates/settings/reports/reports.html:28 +msgid "Resolved" +msgstr "Išspręsta" + +#: bookwyrm/templates/settings/reports/reports.html:37 +msgid "No reports found." +msgstr "Pranešimų nerasta." + +#: bookwyrm/templates/settings/site.html:10 +#: bookwyrm/templates/settings/site.html:21 +msgid "Instance Info" +msgstr "Serverio informacija" + +#: bookwyrm/templates/settings/site.html:11 +#: bookwyrm/templates/settings/site.html:54 +msgid "Images" +msgstr "Paveikslėliai" + +#: bookwyrm/templates/settings/site.html:12 +#: bookwyrm/templates/settings/site.html:74 +msgid "Footer Content" +msgstr "Poraštės turinys" + +#: bookwyrm/templates/settings/site.html:13 +#: bookwyrm/templates/settings/site.html:98 +msgid "Registration" +msgstr "Registracija" + +#: bookwyrm/templates/settings/site.html:24 +msgid "Instance Name:" +msgstr "Serverio pavadinimas:" + +#: bookwyrm/templates/settings/site.html:28 +msgid "Tagline:" +msgstr "Žymos linija:" + +#: bookwyrm/templates/settings/site.html:32 +msgid "Instance description:" +msgstr "Serverio aprašymas:" + +#: bookwyrm/templates/settings/site.html:36 +msgid "Short description:" +msgstr "Trumpas aprašymas:" + +#: bookwyrm/templates/settings/site.html:37 +msgid "Used when the instance is previewed on joinbookwyrm.com. Does not support HTML or Markdown." +msgstr "Naudota, kai turinys buvo peržiūrimas per joinbookwyrm.com. Nepalaiko HTML arba „Markdown“." + +#: bookwyrm/templates/settings/site.html:41 +msgid "Code of conduct:" +msgstr "Elgesio kodeksas:" + +#: bookwyrm/templates/settings/site.html:45 +msgid "Privacy Policy:" +msgstr "Privatumo politika:" + +#: bookwyrm/templates/settings/site.html:57 +msgid "Logo:" +msgstr "Logotipas:" + +#: bookwyrm/templates/settings/site.html:61 +msgid "Logo small:" +msgstr "Mažas logotipas:" + +#: bookwyrm/templates/settings/site.html:65 +msgid "Favicon:" +msgstr "Puslapio ikonėlė:" + +#: bookwyrm/templates/settings/site.html:77 +msgid "Support link:" +msgstr "Paramos nuoroda:" + +#: bookwyrm/templates/settings/site.html:81 +msgid "Support title:" +msgstr "Paramos pavadinimas:" + +#: bookwyrm/templates/settings/site.html:85 +msgid "Admin email:" +msgstr "Administratoriaus el. paštas:" + +#: bookwyrm/templates/settings/site.html:89 +msgid "Additional info:" +msgstr "Papildoma informacija:" + +#: bookwyrm/templates/settings/site.html:103 +msgid "Allow registration" +msgstr "Leisti registruotis" + +#: bookwyrm/templates/settings/site.html:109 +msgid "Allow invite requests" +msgstr "Leisti prašyti kvietimų" + +#: bookwyrm/templates/settings/site.html:115 +msgid "Require users to confirm email address" +msgstr "Reikalauti el. pašto patvirtinimo" + +#: bookwyrm/templates/settings/site.html:117 +msgid "(Recommended if registration is open)" +msgstr "(Rekomenduojama, jei leidžiama registruotis)" + +#: bookwyrm/templates/settings/site.html:120 +msgid "Registration closed text:" +msgstr "Užrakintos registracijos tekstas:" + +#: bookwyrm/templates/settings/site.html:124 +msgid "Invite request text:" +msgstr "Kvietimo prašymo tekstas:" + +#: bookwyrm/templates/settings/users/delete_user_form.html:5 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:31 +msgid "Permanently delete user" +msgstr "Visam laikui ištrinti vartotoją" + +#: bookwyrm/templates/settings/users/delete_user_form.html:12 +#, python-format +msgid "Are you sure you want to delete %(username)s's account? This action cannot be undone. To proceed, please enter your password to confirm deletion." +msgstr "Ar tikrai norite ištrinti %(username)s paskyrą? To negalėsite atšaukti. Norėdami tęsti, įveskite savo slaptažodį, kad patvirtintumėte sprendimą trinti." + +#: bookwyrm/templates/settings/users/delete_user_form.html:17 +msgid "Your password:" +msgstr "Jūsų slaptažodis:" + +#: bookwyrm/templates/settings/users/user.html:7 +msgid "Back to users" +msgstr "Atgal į vartotojų sąrašą" + +#: bookwyrm/templates/settings/users/user_admin.html:7 +#, python-format +msgid "Users: %(instance_name)s" +msgstr "Vartotojai: %(instance_name)s" + +#: bookwyrm/templates/settings/users/user_admin.html:22 +#: bookwyrm/templates/settings/users/username_filter.html:5 +msgid "Username" +msgstr "Vartotojo vardas" + +#: bookwyrm/templates/settings/users/user_admin.html:26 +msgid "Date Added" +msgstr "Pridėjimo data" + +#: bookwyrm/templates/settings/users/user_admin.html:30 +msgid "Last Active" +msgstr "Paskutinį kartą aktyvus" + +#: bookwyrm/templates/settings/users/user_admin.html:38 +msgid "Remote instance" +msgstr "Nutolęs serveris" + +#: bookwyrm/templates/settings/users/user_admin.html:47 +#: bookwyrm/templates/settings/users/user_info.html:24 +msgid "Active" +msgstr "Aktyvus" + +#: bookwyrm/templates/settings/users/user_admin.html:47 +#: bookwyrm/templates/settings/users/user_info.html:28 +msgid "Inactive" +msgstr "Neaktyvus" + +#: bookwyrm/templates/settings/users/user_admin.html:52 +#: bookwyrm/templates/settings/users/user_info.html:120 +msgid "Not set" +msgstr "Nenustatytas" + +#: bookwyrm/templates/settings/users/user_info.html:16 +msgid "View user profile" +msgstr "Peržiūrėti vartotojo profilį" + +#: bookwyrm/templates/settings/users/user_info.html:36 +msgid "Local" +msgstr "Vietinis" + +#: bookwyrm/templates/settings/users/user_info.html:38 +msgid "Remote" +msgstr "Nutolęs" + +#: bookwyrm/templates/settings/users/user_info.html:47 +msgid "User details" +msgstr "Vartotojo duomenys" + +#: bookwyrm/templates/settings/users/user_info.html:51 +msgid "Email:" +msgstr "El. paštas:" + +#: bookwyrm/templates/settings/users/user_info.html:61 +msgid "(View reports)" +msgstr "(Peržiūrėti ataskaitas)" + +#: bookwyrm/templates/settings/users/user_info.html:67 +msgid "Blocked by count:" +msgstr "Užblokavę:" + +#: bookwyrm/templates/settings/users/user_info.html:70 +msgid "Last active date:" +msgstr "Paskutinį kartą aktyvus:" + +#: bookwyrm/templates/settings/users/user_info.html:73 +msgid "Manually approved followers:" +msgstr "Patvirtinti sekėjai:" + +#: bookwyrm/templates/settings/users/user_info.html:76 +msgid "Discoverable:" +msgstr "Aptinkama:" + +#: bookwyrm/templates/settings/users/user_info.html:80 +msgid "Deactivation reason:" +msgstr "Išjungimo priežastis:" + +#: bookwyrm/templates/settings/users/user_info.html:95 +msgid "Instance details" +msgstr "Serverio informacija" + +#: bookwyrm/templates/settings/users/user_info.html:117 +msgid "View instance" +msgstr "Peržiūrėti serverį" + +#: bookwyrm/templates/settings/users/user_moderation_actions.html:5 +msgid "Permanently deleted" +msgstr "Visam laikui ištrintas" + +#: bookwyrm/templates/settings/users/user_moderation_actions.html:13 +#: bookwyrm/templates/snippets/status/status_options.html:32 +#: bookwyrm/templates/snippets/user_options.html:13 +msgid "Send direct message" +msgstr "Siųsti asmeninę žinutę" + +#: bookwyrm/templates/settings/users/user_moderation_actions.html:20 +msgid "Suspend user" +msgstr "Laikinai išjungti vartotoją" + +#: bookwyrm/templates/settings/users/user_moderation_actions.html:25 +msgid "Un-suspend user" +msgstr "Atblokuoti narį" + +#: bookwyrm/templates/settings/users/user_moderation_actions.html:47 +msgid "Access level:" +msgstr "Priėjimo lygis:" + +#: bookwyrm/templates/shelf/create_shelf_form.html:5 +msgid "Create Shelf" +msgstr "Sukurti lentyną" + +#: bookwyrm/templates/shelf/edit_shelf_form.html:5 +msgid "Edit Shelf" +msgstr "Redaguoti lentyną" + +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 +msgid "All books" +msgstr "Visos knygos" + +#: bookwyrm/templates/shelf/shelf.html:69 +msgid "Create shelf" +msgstr "Sukurti lentyną" + +#: bookwyrm/templates/shelf/shelf.html:90 +#, python-format +msgid "%(formatted_count)s book" +msgid_plural "%(formatted_count)s books" +msgstr[0] "%(formatted_count)s knyga" +msgstr[1] "%(formatted_count)s knygos" +msgstr[2] "%(formatted_count)s knygų" +msgstr[3] "%(formatted_count)s knygos" + +#: bookwyrm/templates/shelf/shelf.html:97 +#, python-format +msgid "(showing %(start)s-%(end)s)" +msgstr "(rodoma %(start)s–%(end)s)" + +#: bookwyrm/templates/shelf/shelf.html:109 +msgid "Edit shelf" +msgstr "Redaguoti lentyną" + +#: bookwyrm/templates/shelf/shelf.html:117 +msgid "Delete shelf" +msgstr "Ištrinti lentyną" + +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 +msgid "Shelved" +msgstr "Sudėta į lentynas" + +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 +msgid "Started" +msgstr "Pradėta" + +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 +msgid "Finished" +msgstr "Baigta" + +#: bookwyrm/templates/shelf/shelf.html:203 +msgid "This shelf is empty." +msgstr "Ši lentyna tuščia." + +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "Pakviesti" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "Atšaukti kvietimą" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "Pašalinti @%(username)s" + +#: bookwyrm/templates/snippets/announcement.html:31 +#, python-format +msgid "Posted by %(username)s" +msgstr "Publikavo %(username)s" + +#: bookwyrm/templates/snippets/authors.html:22 +#, python-format +msgid "and %(remainder_count_display)s other" +msgid_plural "and %(remainder_count_display)s others" +msgstr[0] "ir %(remainder_count_display)s kitas" +msgstr[1] "ir %(remainder_count_display)s kiti" +msgstr[2] "ir %(remainder_count_display)s kitų" +msgstr[3] "ir %(remainder_count_display)s kitų" + +#: bookwyrm/templates/snippets/book_cover.html:61 +msgid "No cover" +msgstr "Nėra viršelio" + +#: bookwyrm/templates/snippets/book_titleby.html:6 +#, python-format +msgid "%(title)s by" +msgstr "%(title)s" + +#: bookwyrm/templates/snippets/boost_button.html:20 +#: bookwyrm/templates/snippets/boost_button.html:21 +msgid "Boost" +msgstr "Populiarinti" + +#: bookwyrm/templates/snippets/boost_button.html:33 +#: bookwyrm/templates/snippets/boost_button.html:34 +msgid "Un-boost" +msgstr "Nepopuliarinti" + +#: bookwyrm/templates/snippets/create_status.html:39 +msgid "Quote" +msgstr "Citata" + +#: bookwyrm/templates/snippets/create_status/comment.html:15 +msgid "Some thoughts on the book" +msgstr "Mintys apie knygą" + +#: bookwyrm/templates/snippets/create_status/comment.html:27 +#: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:15 +msgid "Progress:" +msgstr "Progresas:" + +#: bookwyrm/templates/snippets/create_status/comment.html:53 +#: bookwyrm/templates/snippets/progress_field.html:18 +msgid "pages" +msgstr "puslapiai" + +#: bookwyrm/templates/snippets/create_status/comment.html:59 +#: bookwyrm/templates/snippets/progress_field.html:23 +msgid "percent" +msgstr "procentai" + +#: bookwyrm/templates/snippets/create_status/comment.html:66 +#, python-format +msgid "of %(pages)s pages" +msgstr "iš %(pages)s psl." + +#: bookwyrm/templates/snippets/create_status/content_field.html:17 +#: bookwyrm/templates/snippets/status/layout.html:34 +#: bookwyrm/templates/snippets/status/layout.html:52 +#: bookwyrm/templates/snippets/status/layout.html:53 +msgid "Reply" +msgstr "Atsakyti" + +#: bookwyrm/templates/snippets/create_status/content_field.html:17 +msgid "Content" +msgstr "Turinys" + +#: bookwyrm/templates/snippets/create_status/content_warning_field.html:10 +msgid "Content warning:" +msgstr "Įspėjimas dėl turinio:" + +#: bookwyrm/templates/snippets/create_status/content_warning_field.html:18 +msgid "Spoilers ahead!" +msgstr "Galimas turinio atskleidimas!" + +#: bookwyrm/templates/snippets/create_status/content_warning_toggle.html:13 +msgid "Include spoiler alert" +msgstr "Įdėti įspėjimą apie turinio atskleidimą" + +#: bookwyrm/templates/snippets/create_status/layout.html:48 +#: bookwyrm/templates/snippets/reading_modals/form.html:7 +msgid "Comment:" +msgstr "Komentuoti:" + +#: bookwyrm/templates/snippets/create_status/post_options_block.html:8 +#: bookwyrm/templates/snippets/privacy-icons.html:15 +#: bookwyrm/templates/snippets/privacy-icons.html:16 +#: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 +msgid "Private" +msgstr "Privatu" + +#: bookwyrm/templates/snippets/create_status/post_options_block.html:21 +msgid "Post" +msgstr "Publikuoti" + +#: bookwyrm/templates/snippets/create_status/quotation.html:17 +msgid "Quote:" +msgstr "Citata:" + +#: bookwyrm/templates/snippets/create_status/quotation.html:25 +#, python-format +msgid "An excerpt from '%(book_title)s'" +msgstr "Ištrauka iš „%(book_title)s“" + +#: bookwyrm/templates/snippets/create_status/quotation.html:32 +msgid "Position:" +msgstr "Pozicija:" + +#: bookwyrm/templates/snippets/create_status/quotation.html:45 +msgid "On page:" +msgstr "Puslapyje:" + +#: bookwyrm/templates/snippets/create_status/quotation.html:51 +msgid "At percent:" +msgstr "Proc.:" + +#: bookwyrm/templates/snippets/create_status/review.html:25 +#, python-format +msgid "Your review of '%(book_title)s'" +msgstr "Jūsų apžvalga apie „%(book_title)s“" + +#: bookwyrm/templates/snippets/create_status/review.html:40 +msgid "Review:" +msgstr "Atsiliepimas:" + +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:4 +msgid "Delete these read dates?" +msgstr "Ištrinti šias skaitymo datas?" + +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:7 +#, python-format +msgid "You are deleting this readthrough and its %(count)s associated progress updates." +msgstr "Trinate tai, kas perskaityta ir %(count)s susietų progreso naujinių." + +#: bookwyrm/templates/snippets/fav_button.html:16 +#: bookwyrm/templates/snippets/fav_button.html:17 +msgid "Like" +msgstr "Mėgti" + +#: bookwyrm/templates/snippets/fav_button.html:30 +#: bookwyrm/templates/snippets/fav_button.html:31 +msgid "Un-like" +msgstr "Nebemėgti" + +#: bookwyrm/templates/snippets/filters_panel/filters_panel.html:7 +msgid "Show filters" +msgstr "Rodyti filtrus" + +#: bookwyrm/templates/snippets/filters_panel/filters_panel.html:9 +msgid "Hide filters" +msgstr "Slėpti filtrus" + +#: bookwyrm/templates/snippets/filters_panel/filters_panel.html:22 +msgid "Apply filters" +msgstr "Taikyti filtrus" + +#: bookwyrm/templates/snippets/filters_panel/filters_panel.html:26 +msgid "Clear filters" +msgstr "Valyti filtrus" + +#: bookwyrm/templates/snippets/follow_button.html:14 +#, python-format +msgid "Follow @%(username)s" +msgstr "Sekti @%(username)s" + +#: bookwyrm/templates/snippets/follow_button.html:16 +msgid "Follow" +msgstr "Sekti" + +#: bookwyrm/templates/snippets/follow_button.html:25 +msgid "Undo follow request" +msgstr "Atšaukti prašymus sekti" + +#: bookwyrm/templates/snippets/follow_button.html:30 +#, python-format +msgid "Unfollow @%(username)s" +msgstr "Nebesekti @%(username)s" + +#: bookwyrm/templates/snippets/follow_button.html:32 +msgid "Unfollow" +msgstr "Nebesekti" + +#: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 +msgid "Accept" +msgstr "Sutikti" + +#: bookwyrm/templates/snippets/form_rate_stars.html:20 +#: bookwyrm/templates/snippets/stars.html:13 +msgid "No rating" +msgstr "Įvertinimų nėra" + +#: bookwyrm/templates/snippets/form_rate_stars.html:28 +#, python-format +msgid "%(half_rating)s star" +msgid_plural "%(half_rating)s stars" +msgstr[0] "%(half_rating)s žvaigždutė" +msgstr[1] "%(half_rating)s žvaigždutės" +msgstr[2] "%(half_rating)s žvaigždutės" +msgstr[3] "%(half_rating)s žvaigždučių" + +#: bookwyrm/templates/snippets/form_rate_stars.html:64 +#: bookwyrm/templates/snippets/stars.html:7 +#, python-format +msgid "%(rating)s star" +msgid_plural "%(rating)s stars" +msgstr[0] "%(rating)s žvaigždutė" +msgstr[1] "%(rating)s žvaigždutės" +msgstr[2] "%(rating)s žvaigždutės" +msgstr[3] "%(rating)s žvaigždučių" + +#: bookwyrm/templates/snippets/generated_status/goal.html:2 +#, python-format +msgid "set a goal to read %(counter)s book in %(year)s" +msgid_plural "set a goal to read %(counter)s books in %(year)s" +msgstr[0] "nustatė tikslą perskaityti %(counter)s knygą %(year)s m." +msgstr[1] "nustatė tikslą perskaityti %(counter)s knygas %(year)s m." +msgstr[2] "nustatė tikslą perskaityti %(counter)s knygų %(year)s m." +msgstr[3] "nustatė tikslą perskaityti %(counter)s knygas %(year)s m." + +#: bookwyrm/templates/snippets/generated_status/rating.html:3 +#, python-format +msgid "rated %(title)s: %(display_rating)s star" +msgid_plural "rated %(title)s: %(display_rating)s stars" +msgstr[0] "įvertinta %(title)s: %(display_rating)s žvaigždute" +msgstr[1] "įvertinta %(title)s: %(display_rating)s žvaigždutėmis" +msgstr[2] "įvertinta %(title)s: %(display_rating)s žvaigždutėmis" +msgstr[3] "įvertinta %(title)s: %(display_rating)s žvaigždutėmis" + +#: bookwyrm/templates/snippets/generated_status/review_pure_name.html:4 +#, python-format +msgid "Review of \"%(book_title)s\" (%(display_rating)s star): %(review_title)s" +msgid_plural "Review of \"%(book_title)s\" (%(display_rating)s stars): %(review_title)s" +msgstr[0] "Knygos „%(book_title)s“ (%(display_rating)s žvaigždutė) apžvalga: %(review_title)s" +msgstr[1] "Knygos „%(book_title)s“ (%(display_rating)s žvaigždutės) apžvalga: %(review_title)s" +msgstr[2] "Knygos „%(book_title)s“ (%(display_rating)s žvaigždutės) apžvalga: %(review_title)s" +msgstr[3] "Knygos „%(book_title)s“ (%(display_rating)s žvaigždutės) apžvalga: %(review_title)s" + +#: bookwyrm/templates/snippets/generated_status/review_pure_name.html:8 +#, python-format +msgid "Review of \"%(book_title)s\": %(review_title)s" +msgstr "Knygos „%(book_title)s“ apžvalga: %(review_title)s" + +#: bookwyrm/templates/snippets/goal_form.html:4 +#, python-format +msgid "Set a goal for how many books you'll finish reading in %(year)s, and track your progress throughout the year." +msgstr "Nusistatykite tikslą, kiek knygų perskaitysite %(year)s m. ir metų eigoje sekite savo progresą." + +#: bookwyrm/templates/snippets/goal_form.html:16 +msgid "Reading goal:" +msgstr "Skaitymo tikslai:" + +#: bookwyrm/templates/snippets/goal_form.html:21 +msgid "books" +msgstr "knygos" + +#: bookwyrm/templates/snippets/goal_form.html:26 +msgid "Goal privacy:" +msgstr "Tikslo privatumas:" + +#: bookwyrm/templates/snippets/goal_form.html:33 +#: bookwyrm/templates/snippets/reading_modals/layout.html:13 +msgid "Post to feed" +msgstr "Skelbti" + +#: bookwyrm/templates/snippets/goal_form.html:37 +msgid "Set goal" +msgstr "Nustatyti tikslą" + +#: bookwyrm/templates/snippets/goal_progress.html:9 +#, python-format +msgid "%(percent)s%% complete!" +msgstr "%(percent)s%% baigta!" + +#: bookwyrm/templates/snippets/goal_progress.html:12 +#, python-format +msgid "You've read %(read_count)s of %(goal_count)s books." +msgstr "Perskaitėte %(read_count)s iš %(goal_count)s knygų." + +#: bookwyrm/templates/snippets/goal_progress.html:14 +#, python-format +msgid "%(username)s has read %(read_count)s of %(goal_count)s books." +msgstr "%(username)s perskaitė %(read_count)s iš %(goal_count)s knygų." + +#: bookwyrm/templates/snippets/page_text.html:8 +#, python-format +msgid "page %(page)s of %(total_pages)s" +msgstr "%(page)s psl. iš %(total_pages)s" + +#: bookwyrm/templates/snippets/page_text.html:14 +#, python-format +msgid "page %(page)s" +msgstr "%(page)s psl." + +#: bookwyrm/templates/snippets/pagination.html:12 +msgid "Previous" +msgstr "Ankstesnis" + +#: bookwyrm/templates/snippets/pagination.html:23 +msgid "Next" +msgstr "Kitas" + +#: bookwyrm/templates/snippets/privacy-icons.html:3 +#: bookwyrm/templates/snippets/privacy-icons.html:4 +#: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 +msgid "Public" +msgstr "Viešas" + +#: bookwyrm/templates/snippets/privacy-icons.html:7 +#: bookwyrm/templates/snippets/privacy-icons.html:8 +#: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 +msgid "Unlisted" +msgstr "Nėra sąraše" + +#: bookwyrm/templates/snippets/privacy-icons.html:12 +msgid "Followers-only" +msgstr "Tik sekėjai" + +#: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 +msgid "Post privacy" +msgstr "Įrašo privatumas" + +#: bookwyrm/templates/snippets/privacy_select.html:17 +#: bookwyrm/templates/user/relationships/followers.html:6 +#: bookwyrm/templates/user/relationships/layout.html:11 +msgid "Followers" +msgstr "Sekėjai" + +#: bookwyrm/templates/snippets/rate_action.html:4 +msgid "Leave a rating" +msgstr "Palikti įvertinimą" + +#: bookwyrm/templates/snippets/rate_action.html:19 +msgid "Rate" +msgstr "Įvertinti" + +#: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:6 +#, python-format +msgid "Finish \"%(book_title)s\"" +msgstr "Užbaigti „%(book_title)s“" + +#: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:23 +#: bookwyrm/templates/snippets/reading_modals/start_reading_modal.html:20 +#: bookwyrm/templates/snippets/readthrough_form.html:7 +msgid "Started reading" +msgstr "Pradėta skaityti" + +#: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:31 +#: bookwyrm/templates/snippets/readthrough_form.html:20 +msgid "Finished reading" +msgstr "Baigta skaityti" + +#: bookwyrm/templates/snippets/reading_modals/form.html:9 +msgid "(Optional)" +msgstr "(Nebūtina)" + +#: bookwyrm/templates/snippets/reading_modals/progress_update_modal.html:5 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:50 +msgid "Update progress" +msgstr "Atnaujinti progresą" + +#: bookwyrm/templates/snippets/reading_modals/start_reading_modal.html:6 +#, python-format +msgid "Start \"%(book_title)s\"" +msgstr "Pradėti „%(book_title)s“" + +#: bookwyrm/templates/snippets/reading_modals/want_to_read_modal.html:6 +#, python-format +msgid "Want to Read \"%(book_title)s\"" +msgstr "Noriu perskaityti „%(book_title)s“" + +#: bookwyrm/templates/snippets/readthrough_form.html:14 +msgid "Progress" +msgstr "Progresas" + +#: bookwyrm/templates/snippets/register_form.html:32 +msgid "Sign Up" +msgstr "Registruotis" + +#: bookwyrm/templates/snippets/report_button.html:6 +msgid "Report" +msgstr "Pranešti" + +#: bookwyrm/templates/snippets/report_modal.html:6 +#, python-format +msgid "Report @%(username)s" +msgstr "Pranešti apie @%(username)s" + +#: bookwyrm/templates/snippets/report_modal.html:23 +#, python-format +msgid "This report will be sent to %(site_name)s's moderators for review." +msgstr "Šis pranešimas bus nusiųstas peržiūrėti %(site_name)s puslapio moderatoriams." + +#: bookwyrm/templates/snippets/report_modal.html:24 +msgid "More info about this report:" +msgstr "Daugiau informacijos apie šį pranešimą:" + +#: bookwyrm/templates/snippets/shelf_selector.html:4 +msgid "Move book" +msgstr "Perkelti knygą" + +#: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown.html:5 +msgid "More shelves" +msgstr "Daugiau lentynų" + +#: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:17 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:24 +msgid "Start reading" +msgstr "Pradėti skaityti" + +#: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:29 +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:36 +msgid "Want to read" +msgstr "Noriu perskaityti" + +#: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:62 +#, python-format +msgid "Remove from %(name)s" +msgstr "Pašalinti iš %(name)s" + +#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:30 +msgid "Finish reading" +msgstr "Baigti skaityti" + +#: bookwyrm/templates/snippets/status/content_status.html:72 +msgid "Content warning" +msgstr "Įspėjimas dėl turinio" + +#: bookwyrm/templates/snippets/status/content_status.html:79 +msgid "Show status" +msgstr "Rodyti būseną" + +#: bookwyrm/templates/snippets/status/content_status.html:101 +#, python-format +msgid "(Page %(page)s)" +msgstr "(Psl. %(page)s)" + +#: bookwyrm/templates/snippets/status/content_status.html:103 +#, python-format +msgid "(%(percent)s%%)" +msgstr "(%(percent)s%%)" + +#: bookwyrm/templates/snippets/status/content_status.html:125 +msgid "Open image in new window" +msgstr "Atidaryti paveikslėlį naujame lange" + +#: bookwyrm/templates/snippets/status/content_status.html:144 +msgid "Hide status" +msgstr "Slėpti būseną" + +#: bookwyrm/templates/snippets/status/header.html:45 +#, python-format +msgid "edited %(date)s" +msgstr "redaguota %(date)s" + +#: bookwyrm/templates/snippets/status/headers/comment.html:2 +#, python-format +msgid "commented on %(book)s" +msgstr "komentuota %(book)s" + +#: bookwyrm/templates/snippets/status/headers/note.html:8 +#, python-format +msgid "replied to %(username)s's status" +msgstr "atsakyta į %(username)s būseną" + +#: bookwyrm/templates/snippets/status/headers/quotation.html:2 +#, python-format +msgid "quoted %(book)s" +msgstr "pacitavo %(book)s" + +#: bookwyrm/templates/snippets/status/headers/rating.html:3 +#, python-format +msgid "rated %(book)s:" +msgstr "įvertinta %(book)s:" + +#: bookwyrm/templates/snippets/status/headers/read.html:7 +#, python-format +msgid "finished reading %(book)s" +msgstr "baigti skaityti %(book)s" + +#: bookwyrm/templates/snippets/status/headers/reading.html:7 +#, python-format +msgid "started reading %(book)s" +msgstr "pradėjo skaityti %(book)s" + +#: bookwyrm/templates/snippets/status/headers/review.html:3 +#, python-format +msgid "reviewed %(book)s" +msgstr "apžvelgė %(book)s" + +#: bookwyrm/templates/snippets/status/headers/to_read.html:7 +#, python-format +msgid "%(username)s wants to read %(book)s" +msgstr "%(username)s nori perskaityti %(book)s" + +#: bookwyrm/templates/snippets/status/layout.html:24 +#: bookwyrm/templates/snippets/status/status_options.html:17 +msgid "Delete status" +msgstr "Ištrinti įrašą" + +#: bookwyrm/templates/snippets/status/layout.html:56 +#: bookwyrm/templates/snippets/status/layout.html:57 +msgid "Boost status" +msgstr "Pagreitinti būseną" + +#: bookwyrm/templates/snippets/status/layout.html:60 +#: bookwyrm/templates/snippets/status/layout.html:61 +msgid "Like status" +msgstr "Mėgti būseną" + +#: bookwyrm/templates/snippets/status/status.html:10 +msgid "boosted" +msgstr "pagreitinta" + +#: bookwyrm/templates/snippets/status/status_options.html:7 +#: bookwyrm/templates/snippets/user_options.html:7 +msgid "More options" +msgstr "Daugiau parinkčių" + +#: bookwyrm/templates/snippets/switch_edition_button.html:5 +msgid "Switch to this edition" +msgstr "Perjungti į šį leidimą" + +#: bookwyrm/templates/snippets/table-sort-header.html:6 +msgid "Sorted ascending" +msgstr "Surūšiuota didėjimo tvarka" + +#: bookwyrm/templates/snippets/table-sort-header.html:10 +msgid "Sorted descending" +msgstr "Surūšiuota mažėjimo tvarka" + +#: bookwyrm/templates/snippets/trimmed_text.html:17 +msgid "Show more" +msgstr "Rodyti daugiau" + +#: bookwyrm/templates/snippets/trimmed_text.html:35 +msgid "Show less" +msgstr "Rodyti mažiau" + +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "Jūsų knygos" + +#: bookwyrm/templates/user/books_header.html:15 +#, python-format +msgid "%(username)s's books" +msgstr "%(username)s – knygos" + +#: bookwyrm/templates/user/goal.html:8 +#, python-format +msgid "%(year)s Reading Progress" +msgstr "%(year)s m. skaitymo progresas" + +#: bookwyrm/templates/user/goal.html:12 +msgid "Edit Goal" +msgstr "Redaguoti tikslą" + +#: bookwyrm/templates/user/goal.html:28 +#, python-format +msgid "%(name)s hasn't set a reading goal for %(year)s." +msgstr "%(name)s nenustatė %(year)s m. skaitymo tikslo." + +#: bookwyrm/templates/user/goal.html:40 +#, python-format +msgid "Your %(year)s Books" +msgstr "Jūsų %(year)s m. knygos" + +#: bookwyrm/templates/user/goal.html:42 +#, python-format +msgid "%(username)s's %(year)s Books" +msgstr "%(username)s – %(year)s m. knygos" + +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "Jūsų grupės" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "Grupės: %(username)s" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "Sukurti grupę" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 +msgid "User Profile" +msgstr "Naudotojo paskyra" + +#: bookwyrm/templates/user/layout.html:45 +msgid "Follow Requests" +msgstr "Sekti prašymus" + +#: bookwyrm/templates/user/layout.html:70 +msgid "Reading Goal" +msgstr "Skaitymo tikslas" + +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "Grupės" + +#: bookwyrm/templates/user/lists.html:11 +#, python-format +msgid "Lists: %(username)s" +msgstr "Sąrašai: %(username)s" + +#: bookwyrm/templates/user/lists.html:17 bookwyrm/templates/user/lists.html:29 +msgid "Create list" +msgstr "Sukurti sąrašą" + +#: bookwyrm/templates/user/relationships/followers.html:12 +#, python-format +msgid "%(username)s has no followers" +msgstr "%(username)s neturi sekėjų" + +#: bookwyrm/templates/user/relationships/following.html:6 +#: bookwyrm/templates/user/relationships/layout.html:15 +msgid "Following" +msgstr "Sekama" + +#: bookwyrm/templates/user/relationships/following.html:12 +#, python-format +msgid "%(username)s isn't following any users" +msgstr "%(username)s nieko neseka" + +#: bookwyrm/templates/user/user.html:16 +msgid "Edit profile" +msgstr "Redaguoti paskyrą" + +#: bookwyrm/templates/user/user.html:33 +#, python-format +msgid "View all %(size)s" +msgstr "Žiūrėti visus %(size)s" + +#: bookwyrm/templates/user/user.html:46 +msgid "View all books" +msgstr "Žiūrėti visas knygas" + +#: bookwyrm/templates/user/user.html:59 +msgid "User Activity" +msgstr "Naudotojo aktyvumas" + +#: bookwyrm/templates/user/user.html:63 +msgid "RSS feed" +msgstr "RSS srautas" + +#: bookwyrm/templates/user/user.html:74 +msgid "No activities yet!" +msgstr "Įrašų dar nėra" + +#: bookwyrm/templates/user/user_preview.html:22 +#, python-format +msgid "Joined %(date)s" +msgstr "Joined %(date)s" + +#: bookwyrm/templates/user/user_preview.html:26 +#, python-format +msgid "%(counter)s follower" +msgid_plural "%(counter)s followers" +msgstr[0] "%(counter)s sekėjas" +msgstr[1] "%(counter)s sekėjai" +msgstr[2] "%(counter)s sekėjų" +msgstr[3] "%(counter)s sekėjai" + +#: bookwyrm/templates/user/user_preview.html:27 +#, python-format +msgid "%(counter)s following" +msgstr "%(counter)s seka" + +#: bookwyrm/templates/user/user_preview.html:34 +#, python-format +msgid "%(mutuals_display)s follower you follow" +msgid_plural "%(mutuals_display)s followers you follow" +msgstr[0] "%(mutuals_display)s sekėjas, kurį sekate jūs" +msgstr[1] "%(mutuals_display)s sekėjai, kuriuos sekate jūs" +msgstr[2] "%(mutuals_display)s sekėjai, kuriuos sekate jūs" +msgstr[3] "%(mutuals_display)s sekėjai, kuriuos sekate jūs" + +#: bookwyrm/templates/user/user_preview.html:38 +msgid "No followers you follow" +msgstr "Jūs nieko nesekate" + +#: bookwyrm/templates/widgets/clearable_file_input_with_warning.html:28 +msgid "File exceeds maximum size: 10MB" +msgstr "Failas viršijo maksimalų dydį: 10 MB" + +#: bookwyrm/templatetags/utilities.py:31 +#, python-format +msgid "%(title)s: %(subtitle)s" +msgstr "%(title)s: %(subtitle)s" + +#: bookwyrm/views/imports/import_data.py:64 +msgid "Not a valid csv file" +msgstr "Netinkamas csv failas" + +#: bookwyrm/views/landing/login.py:69 +msgid "Username or password are incorrect" +msgstr "Naudotojo vardas arba slaptažodis neteisingi" + +#: bookwyrm/views/landing/password.py:32 +msgid "No user with that email address was found." +msgstr "Šiuo el. pašto adresu nerastas nei vienas narys." + +#: bookwyrm/views/landing/password.py:43 +#, python-brace-format +msgid "A password reset link was sent to {email}" +msgstr "Slaptažodžio atstatymo nuoroda išsiųsta į {email}" + +#: bookwyrm/views/rss_feed.py:35 +#, python-brace-format +msgid "Status updates from {obj.display_name}" +msgstr "Būsenos atnaujinimai iš {obj.display_name}" + From 7d66013b52ee206829ead146259b831687fd2d37 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 10:26:00 -0800 Subject: [PATCH 115/121] Adds Lithuanian to the user options --- .../0117_alter_user_preferred_language.py | 32 +++++++++++++++++++ bookwyrm/settings.py | 1 + 2 files changed, 33 insertions(+) create mode 100644 bookwyrm/migrations/0117_alter_user_preferred_language.py diff --git a/bookwyrm/migrations/0117_alter_user_preferred_language.py b/bookwyrm/migrations/0117_alter_user_preferred_language.py new file mode 100644 index 00000000..c892b051 --- /dev/null +++ b/bookwyrm/migrations/0117_alter_user_preferred_language.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.5 on 2021-11-15 18:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0116_auto_20211114_1734"), + ] + + operations = [ + migrations.AlterField( + model_name="user", + name="preferred_language", + field=models.CharField( + blank=True, + choices=[ + ("en-us", "English"), + ("de-de", "Deutsch (German)"), + ("es-es", "Español (Spanish)"), + ("fr-fr", "Français (French)"), + ("lt-lt", "lietuvių (Lithuanian)"), + ("pt-br", "Português - Brasil (Brazilian Portuguese)"), + ("zh-hans", "简体中文 (Simplified Chinese)"), + ("zh-hant", "繁體中文 (Traditional Chinese)"), + ], + max_length=255, + null=True, + ), + ), + ] diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 44d65cca..d469e6fe 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -166,6 +166,7 @@ LANGUAGES = [ ("de-de", _("Deutsch (German)")), ("es-es", _("Español (Spanish)")), ("fr-fr", _("Français (French)")), + ("lt-lt", _("lietuvių (Lithuanian)")), ("pt-br", _("Português - Brasil (Brazilian Portuguese)")), ("zh-hans", _("简体中文 (Simplified Chinese)")), ("zh-hant", _("繁體中文 (Traditional Chinese)")), From bcfe13bb4ef7f9710003d1394571b65c36016158 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 11:27:27 -0800 Subject: [PATCH 116/121] Sort followers/following lists by if you follow the user --- bookwyrm/views/user.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/bookwyrm/views/user.py b/bookwyrm/views/user.py index b7ab1d3c..082408f9 100644 --- a/bookwyrm/views/user.py +++ b/bookwyrm/views/user.py @@ -1,6 +1,7 @@ """ non-interactive pages """ from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator +from django.db.models import Q, Count from django.http import Http404 from django.shortcuts import redirect from django.template.response import TemplateResponse @@ -105,9 +106,8 @@ class Followers(View): if is_api_request(request): return ActivitypubResponse(user.to_followers_activity(**request.GET)) - paginated = Paginator( - user.followers.order_by("-created_date").all(), PAGE_LENGTH - ) + followers = annotate_if_follows(request.user, user.followers) + paginated = Paginator(followers.all(), PAGE_LENGTH) data = { "user": user, "is_self": request.user.id == user.id, @@ -126,9 +126,8 @@ class Following(View): if is_api_request(request): return ActivitypubResponse(user.to_following_activity(**request.GET)) - paginated = Paginator( - user.following.order_by("-created_date").all(), PAGE_LENGTH - ) + following = annotate_if_follows(request.user, user.following) + paginated = Paginator(following.all(), PAGE_LENGTH) data = { "user": user, "is_self": request.user.id == user.id, @@ -137,6 +136,16 @@ class Following(View): return TemplateResponse(request, "user/relationships/following.html", data) +def annotate_if_follows(user, queryset): + """Sort a list of users by if you follow them""" + if not user.is_authenticated: + return queryset.order_by("-created_date") + + return queryset.annotate( + request_user_follows=Count("followers", filter=Q(followers=user)) + ).order_by("-request_user_follows", "-created_date") + + class Groups(View): """list of user's groups view""" From 5e5cb262907f0f8a3f3a6364ce17e32ce9877fbd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 11:41:29 -0800 Subject: [PATCH 117/121] Preserve spaces between links --- bookwyrm/templates/import/manual_review.html | 6 ++++-- bookwyrm/templates/snippets/book_titleby.html | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index b6c2b6b2..7e429a0f 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -46,8 +46,10 @@ {% include 'snippets/book_cover.html' with book=guess cover_class='is-h-s' size='small' %} -
    - {% include 'snippets/book_titleby.html' with book=guess %} +
    +

    + {% include 'snippets/book_titleby.html' with book=guess %} +

    {% csrf_token %} diff --git a/bookwyrm/templates/snippets/book_titleby.html b/bookwyrm/templates/snippets/book_titleby.html index 1c2bb176..6dbaeb26 100644 --- a/bookwyrm/templates/snippets/book_titleby.html +++ b/bookwyrm/templates/snippets/book_titleby.html @@ -5,11 +5,9 @@ {% if book.authors.exists %} {% blocktrans trimmed with path=book.local_path title=book|book_title %} {{ title }} by -{% endblocktrans %} -{% include 'snippets/authors.html' with book=book limit=3 %} +{% endblocktrans %} {% include 'snippets/authors.html' with book=book limit=3 %} {% else %} {{ book|book_title }} {% endif %} - {% endspaceless %} From f22ae235748c6ec12c3f08af823df720890cbd30 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 15 Nov 2021 13:30:11 -0800 Subject: [PATCH 118/121] Safer call to get preview image or books --- bookwyrm/models/status.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index a52af123..c7c0a425 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -189,8 +189,10 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): if hasattr(activity, "name"): activity.name = self.pure_name activity.type = self.pure_type - books = [getattr(self, "book", None)] + list(self.mention_books.all()) - if len(books) == 1 and books[0].preview_image: + book = getattr(self, "book", None) + books = [book] if book else [] + books += list(self.mention_books.all()) + if len(books) == 1 and getattr(books[0], "preview_image", None): covers = [ activitypub.Document( url=fields.get_absolute_url(books[0].preview_image), From 77f29a621f80e5ce1a80bf166e57528309333e7d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 16 Nov 2021 09:29:58 -0800 Subject: [PATCH 119/121] Adds command to pull locale updates --- bw-dev | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bw-dev b/bw-dev index 35f3694e..f50800cf 100755 --- a/bw-dev +++ b/bw-dev @@ -108,6 +108,17 @@ case "$CMD" in compilemessages) runweb django-admin compilemessages --ignore venv $@ ;; + update_locales) + git fetch origin l10n_main:l10n_main + git checkout l10n_main locale/de_DE + git checkout l10n_main locale/es_ES + git checkout l10n_main locale/fr_FR + git checkout l10n_main locale/lt_LT + git checkout l10n_main locale/pt_BR + git checkout l10n_main locale/zh_Hans + git checkout l10n_main locale/zh_Hant + runweb django-admin compilemessages --ignore venv + ;; build) docker-compose build ;; From a1830ac70165c90036bf97ef3d4f0d9fc25eac7b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 16 Nov 2021 09:30:11 -0800 Subject: [PATCH 120/121] Adds updates locales --- locale/de_DE/LC_MESSAGES/django.mo | Bin 61566 -> 60544 bytes locale/de_DE/LC_MESSAGES/django.po | 258 +++++--- locale/es_ES/LC_MESSAGES/django.mo | Bin 61028 -> 60011 bytes locale/es_ES/LC_MESSAGES/django.po | 258 +++++--- locale/fr_FR/LC_MESSAGES/django.mo | Bin 62690 -> 62284 bytes locale/fr_FR/LC_MESSAGES/django.po | 260 +++++--- locale/lt_LT/LC_MESSAGES/django.mo | Bin 61077 -> 61085 bytes locale/lt_LT/LC_MESSAGES/django.po | 8 +- locale/pt_BR/LC_MESSAGES/django.mo | Bin 56435 -> 62235 bytes locale/pt_BR/LC_MESSAGES/django.po | 849 +++++++++++++++++---------- locale/zh_Hans/LC_MESSAGES/django.mo | Bin 56859 -> 56164 bytes locale/zh_Hans/LC_MESSAGES/django.po | 286 +++++---- locale/zh_Hant/LC_MESSAGES/django.mo | Bin 38249 -> 37625 bytes locale/zh_Hant/LC_MESSAGES/django.po | 252 +++++--- 14 files changed, 1399 insertions(+), 772 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/django.mo b/locale/de_DE/LC_MESSAGES/django.mo index 19aa94f8fc132c768e7691d5fc774579469219ee..f49377602bd22295101586a22cb0e70402ea5905 100644 GIT binary patch delta 17240 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 uFVrXn}?MPHnO3i$$5XwRbtxQ`m} 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 diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po index d8352466..6ebce09a 100644 --- a/locale/de_DE/LC_MESSAGES/django.po +++ b/locale/de_DE/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-24 14:09+0000\n" -"PO-Revision-Date: 2021-10-26 22:29\n" +"POT-Creation-Date: 2021-11-14 15:08+0000\n" +"PO-Revision-Date: 2021-11-15 18:03\n" "Last-Translator: Mouse Reeve \n" "Language-Team: German\n" "Language: de\n" @@ -72,15 +72,16 @@ msgstr "Aufsteigend" msgid "Descending" msgstr "Absteigend" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "Fehler beim Laden des Buches" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "Keine Übereinstimmung für das Buch gefunden" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "Ausstehend" @@ -100,23 +101,23 @@ msgstr "Moderator*in löschen" msgid "Domain block" msgstr "Domainsperrung" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "Hörbuch" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "E-Book" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "Graphic Novel" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "Hardcover" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "Taschenbuch" @@ -133,21 +134,21 @@ msgstr "Föderiert" msgid "Blocked" msgstr "Blockiert" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s ist keine gültige remote_id" -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s ist kein gültiger Benutzer*inname" -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "Benutzer*inname" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "Dieser Benutzer*inname ist bereits vergeben." @@ -892,22 +893,37 @@ msgstr "Bookwyrm-Benutzer*innen" msgid "All known users" msgstr "Alle bekannten Benutzer*innen" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 +#, python-format +msgid "%(username)s wants to read %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:23 #, python-format msgid "%(username)s rated %(book_title)s" msgstr "%(username)s hat %(book_title)s bewertet" -#: bookwyrm/templates/discover/card-header.html:13 +#: bookwyrm/templates/discover/card-header.html:27 #, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "%(username)s hat %(book_title)s besprochen" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" msgstr "%(username)s hat %(book_title)s kommentiert" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "%(username)s hat %(book_title)s zitiert" @@ -1058,9 +1074,8 @@ msgstr "Du kannst dein Leseziel jederzeit auf deiner Profil msgid "Updates" msgstr "Updates" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" msgstr "Deine Bücher" #: bookwyrm/templates/feed/layout.html:14 @@ -1069,11 +1084,13 @@ msgstr "Hier sind noch keine Bücher! Versuche, nach Büchern zu suchen, um losz #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "Zu lesen" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "Lese ich gerade" @@ -1081,6 +1098,7 @@ msgstr "Lese ich gerade" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "Gelesen" @@ -1366,88 +1384,161 @@ msgid "No recent imports" msgstr "Keine aktuellen Importe" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "Importstatus" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" -msgstr "Zurück zu den Importen" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" +msgstr "" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "Import gestartet:" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "Import abgeschlossen:" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "IMPORTSCHRITT-FEHLER" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "Import läuft noch." - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "(Zur Aktualisierung „Neu laden” wählen)" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" -msgstr "Laden fehlgeschlagen" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "Zum Ende der Liste springen, um die %(failed_count)s Einträge, die nicht importiert werden konnten, auszuwählen." +msgid "Refresh" +msgstr "" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "Zeile %(index)s: %(title)s von %(author)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" +msgstr[1] "" -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" -msgstr "Alle(s) auswählen" +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" -msgstr "Erneut versuchen" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" +msgstr[1] "" -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "Erfolgreich importiert" +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" -msgstr "Import-Fortschritt" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "" -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "Buch" - -#: bookwyrm/templates/import/import_status.html:122 +#: bookwyrm/templates/import/import_status.html:94 #: bookwyrm/templates/shelf/shelf.html:141 #: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Titel" -#: bookwyrm/templates/import/import_status.html:125 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 #: bookwyrm/templates/shelf/shelf.html:142 #: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Autor*in" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "Besprechen" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "Buch" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "Status" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "Importiert" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "Bestätigen" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Du kannst deine Goodreads-Daten von der Import/Export-Seite deines Goodreads-Kontos downloaden." +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1579,10 +1670,6 @@ msgstr "Navigations-Hauptmenü" msgid "Feed" msgstr "Feed" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "Deine Bücher" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "Einstellungen" @@ -1682,10 +1769,6 @@ msgstr "Du bist soweit!" msgid "Suggested by" msgstr "Vorgeschlagen von" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "Bestätigen" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "Ablehnen" @@ -2238,15 +2321,6 @@ msgstr "Startdatum" msgid "End date" msgstr "Enddatum" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "Status" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "aktiv" @@ -3095,10 +3169,6 @@ msgstr "Teilen" msgid "Un-boost" msgstr "Teilen zurücknehmen" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "Besprechen" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "Zitieren" @@ -3525,7 +3595,7 @@ msgstr "%(date)s bearbeitet" msgid "commented on %(book)s" msgstr "hat %(book)s kommentiert" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "hat auf die Statusmeldung von %(username)s geantwortet" @@ -3604,7 +3674,11 @@ msgstr "Mehr anzeigen" msgid "Show less" msgstr "Weniger anzeigen" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "Deine Bücher" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "Bücher von %(username)s" @@ -3748,7 +3822,7 @@ msgstr "Datei überschreitet die maximale Größe von 10MB" msgid "%(title)s: %(subtitle)s" msgstr "%(title)s: %(subtitle)s" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "Keine gültige CSV-Datei" diff --git a/locale/es_ES/LC_MESSAGES/django.mo b/locale/es_ES/LC_MESSAGES/django.mo index c844411e582bf1629c5192f308b505dea717d723..9614a669c3aa133bb13af01293952c2b0790dfc1 100644 GIT binary patch delta 17240 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 iM0=#p+W9zhYVwKTbgA1g}_ma{nLqK1~b& delta 18058 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 diff --git a/locale/es_ES/LC_MESSAGES/django.po b/locale/es_ES/LC_MESSAGES/django.po index d776e11d..b5fc1ce3 100644 --- a/locale/es_ES/LC_MESSAGES/django.po +++ b/locale/es_ES/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-24 14:09+0000\n" -"PO-Revision-Date: 2021-10-26 20:55\n" +"POT-Creation-Date: 2021-11-14 15:08+0000\n" +"PO-Revision-Date: 2021-11-15 18:03\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Spanish\n" "Language: es\n" @@ -72,15 +72,16 @@ msgstr "Ascendente" msgid "Descending" msgstr "Descendente" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "Error en cargar libro" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "No se pudo encontrar el libro" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "Pendiente" @@ -100,23 +101,23 @@ msgstr "Eliminación de moderador" msgid "Domain block" msgstr "Bloqueo de dominio" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "Audio libro" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "Libro electrónico" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "Novela gráfica" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "Tapa dura" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "Tapa blanda" @@ -133,21 +134,21 @@ msgstr "Federalizado" msgid "Blocked" msgstr "Bloqueado" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s no es un remote_id válido" -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s no es un usuario válido" -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "nombre de usuario" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "Ya existe un usuario con ese nombre." @@ -892,22 +893,37 @@ msgstr "Usuarios de BookWyrm" msgid "All known users" msgstr "Todos los usuarios conocidos" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 +#, python-format +msgid "%(username)s wants to read %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:23 #, python-format msgid "%(username)s rated %(book_title)s" msgstr "%(username)s calificó %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:13 +#: bookwyrm/templates/discover/card-header.html:27 #, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "%(username)s reseñó %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" msgstr "%(username)s comentó en %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "%(username)s citó %(book_title)s" @@ -1058,9 +1074,8 @@ msgstr "Puedes establecer o cambiar tu objetivo de lectura en cualquier momento msgid "Updates" msgstr "Actualizaciones" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" msgstr "Tus libros" #: bookwyrm/templates/feed/layout.html:14 @@ -1069,11 +1084,13 @@ msgstr "¡No hay ningún libro aquí ahorita! Busca a un libro para empezar" #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "Para leer" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "Leyendo actualmente" @@ -1081,6 +1098,7 @@ msgstr "Leyendo actualmente" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "Leído" @@ -1366,88 +1384,161 @@ msgid "No recent imports" msgstr "No hay ninguna importación reciente" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "Importar estado" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" -msgstr "Volver a las importaciones" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" +msgstr "" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "Importación ha empezado:" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "Importación ha terminado:" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "TAREA FALLÓ" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "Importación todavia en progreso." - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "(¡Refresca para actualizar!)" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" -msgstr "No se pudo cargar" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "Saltar al final de la lista para seleccionar los %(failed_count)s artículos que no se pudieron importar." +msgid "Refresh" +msgstr "" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "Renglón %(index)s: %(title)s por %(author)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" +msgstr[1] "" -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" -msgstr "Seleccionar todo" +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" -msgstr "Reintentar ítems" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" +msgstr[1] "" -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "Importado exitosamente" +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" -msgstr "Progreso de importación" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "" -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "Libro" - -#: bookwyrm/templates/import/import_status.html:122 +#: bookwyrm/templates/import/import_status.html:94 #: bookwyrm/templates/shelf/shelf.html:141 #: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Título" -#: bookwyrm/templates/import/import_status.html:125 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 #: bookwyrm/templates/shelf/shelf.html:142 #: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Autor/Autora" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "Reseña" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "Libro" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "Estado" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "Importado" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "Aprobar" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Puede descargar sus datos de Goodreads desde la página de Importación/Exportación de su cuenta de Goodreads." +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1579,10 +1670,6 @@ msgstr "Menú de navigación central" msgid "Feed" msgstr "Actividad" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "Tus libros" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "Configuración" @@ -1682,10 +1769,6 @@ msgstr "¡Está todo listo!" msgid "Suggested by" msgstr "Sugerido por" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "Aprobar" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "Desechar" @@ -2238,15 +2321,6 @@ msgstr "Fecha de inicio" msgid "End date" msgstr "Fecha final" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "Estado" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "activo" @@ -3095,10 +3169,6 @@ msgstr "Impulsar" msgid "Un-boost" msgstr "Des-impulsar" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "Reseña" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "Cita" @@ -3525,7 +3595,7 @@ msgstr "editado %(date)s" msgid "commented on %(book)s" msgstr "comentó en \"%(book)s\"" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "respondió al estado de %(username)s" @@ -3604,7 +3674,11 @@ msgstr "Mostrar más" msgid "Show less" msgstr "Mostrar menos" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "Tus libros" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "Los libros de %(username)s" @@ -3748,7 +3822,7 @@ msgstr "Archivo excede el tamaño máximo: 10MB" msgid "%(title)s: %(subtitle)s" msgstr "%(title)s: %(subtitle)s" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "No un archivo csv válido" diff --git a/locale/fr_FR/LC_MESSAGES/django.mo b/locale/fr_FR/LC_MESSAGES/django.mo index 0bc2fb3dad8abed5091b5b7d7746a492e681d9cc..1e81ac6e6aa1e751a387956c9466e48433254d03 100644 GIT binary patch delta 16872 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 delta 17749 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 diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po index 5f9bc8b2..ee9850e2 100644 --- a/locale/fr_FR/LC_MESSAGES/django.po +++ b/locale/fr_FR/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-24 14:09+0000\n" -"PO-Revision-Date: 2021-11-12 17:16\n" +"POT-Creation-Date: 2021-11-14 15:08+0000\n" +"PO-Revision-Date: 2021-11-15 22:27\n" "Last-Translator: Mouse Reeve \n" "Language-Team: French\n" "Language: fr\n" @@ -72,15 +72,16 @@ msgstr "Ordre croissant" msgid "Descending" msgstr "Ordre décroissant" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "Erreur lors du chargement du livre" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "Impossible de trouver une correspondance pour le livre" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "En attente" @@ -100,23 +101,23 @@ msgstr "Suppression du modérateur" msgid "Domain block" msgstr "Bloc de domaine" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "Livre audio" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "eBook" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "Roman Graphique" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "Couverture rigide" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "Couverture souple" @@ -133,21 +134,21 @@ msgstr "Fédéré" msgid "Blocked" msgstr "Bloqué" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s n’est pas une remote_id valide." -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s n’est pas un nom de compte valide." -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "nom du compte :" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "Ce nom est déjà associé à un compte." @@ -892,22 +893,37 @@ msgstr "Comptes BookWyrm" msgid "All known users" msgstr "Tous les comptes connus" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 +#, python-format +msgid "%(username)s wants to read %(book_title)s" +msgstr "%(username)s veut lire %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "%(username)s a terminé %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "%(username)s a commencé %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:23 #, python-format msgid "%(username)s rated %(book_title)s" msgstr "%(username)s a noté %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:13 +#: bookwyrm/templates/discover/card-header.html:27 #, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "%(username)s a critiqué %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" msgstr "%(username)s a commenté %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "%(username)s a cité un passage de %(book_title)s" @@ -1058,10 +1074,9 @@ msgstr "Vous pouvez définir ou changer votre défi lecture à n’importe quel msgid "Updates" msgstr "Mises à jour" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" -msgstr "Vos livres" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" +msgstr "Vos Livres" #: bookwyrm/templates/feed/layout.html:14 msgid "There are no books here right now! Try searching for a book to get started" @@ -1069,11 +1084,13 @@ msgstr "Aucun livre ici pour l’instant ! Cherchez un livre pour commencer" #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "À lire" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "Lectures en cours" @@ -1081,6 +1098,7 @@ msgstr "Lectures en cours" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "Lu" @@ -1366,88 +1384,161 @@ msgid "No recent imports" msgstr "Aucune importation récente" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "Statut de l’importation" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" -msgstr "Retourner à l’importation" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" +msgstr "" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "Début de l’importation :" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "Fin de l’importation :" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "la tâche a échoué" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "L’importation est toujours en cours" - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "(Rechargez la page pour la mettre à jour !)" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" -msgstr "Éléments non importés" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "Sauter en bas de liste pour sélectionner les %(failed_count)s items n’ayant pu être importés." +msgid "Refresh" +msgstr "" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "Ligne %(index)s : %(title)s de %(author)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" +msgstr[1] "" -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" -msgstr "Tout sélectionner" +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" -msgstr "Réessayer l’importation de ces éléments" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" +msgstr[1] "" -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "Importation réussie" +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" -msgstr "Importation en cours" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "" -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "Livre" - -#: bookwyrm/templates/import/import_status.html:122 +#: bookwyrm/templates/import/import_status.html:94 #: bookwyrm/templates/shelf/shelf.html:141 #: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Titre" -#: bookwyrm/templates/import/import_status.html:125 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 #: bookwyrm/templates/shelf/shelf.html:142 #: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Auteur/autrice" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "Critique" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "Livre" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "Statut" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "Importé" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "Approuver" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Vous pouvez télécharger vos données GoodReads depuis la page Import/Export de votre compte GoodReads." +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1579,10 +1670,6 @@ msgstr "Menu de navigation principal " msgid "Feed" msgstr "Fil d’actualité" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "Vos Livres" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "Paramètres" @@ -1682,10 +1769,6 @@ msgstr "Aucun livre en attente de validation !" msgid "Suggested by" msgstr "Suggéré par" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "Approuver" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "Rejeter" @@ -2238,15 +2321,6 @@ msgstr "Date de début" msgid "End date" msgstr "Date de fin" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "Statut" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "active" @@ -3095,10 +3169,6 @@ msgstr "Partager" msgid "Un-boost" msgstr "Annuler le partage" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "Critique" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "Citation" @@ -3525,7 +3595,7 @@ msgstr "modifié le %(date)s" msgid "commented on %(book)s" msgstr "a commenté %(book)s" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "a répondu au statut de %(username)s" @@ -3604,7 +3674,11 @@ msgstr "Déplier" msgid "Show less" msgstr "Replier" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "Vos livres" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "Livres de %(username)s" @@ -3748,7 +3822,7 @@ msgstr "Ce fichier dépasse la taille limite : 10 Mo" msgid "%(title)s: %(subtitle)s" msgstr "%(title)s (%(subtitle)s)" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "Fichier CSV non valide" diff --git a/locale/lt_LT/LC_MESSAGES/django.mo b/locale/lt_LT/LC_MESSAGES/django.mo index 9d11576fd32656bb9d97df42d532f370e888691f..ea2d76b14c61ccef786c478e234431b32a354d4c 100644 GIT binary patch delta 4425 zcmXZe3v|zA9LMqB46`-teskGI&1(HO9E6kH9djuPt(j7)S-BOV!cQr4XQ&lj4monV zvcJ0_HutbetTM5gG1pp09gNm-?;`jCX?^$C#1W#Z?tiVQCZQJW?aGV(G;pmU8(GNRgDE7br9EM@|B8KB+RKEq-3YTwi z+}a89X{b-bVQh}Yw!=+qOg$>kJWoW`Q!pC4Vm*8gHSr7f`9#}33pL?fY=Ai!h$~R@ zeVyl;2S3mdLcZw_ zBI3n%c+jPw71p3aRBxl%SrcqQy*=uA8fu`Cs0d`DCYXX+&>Yl4mZMU**0vX-c3x!t z4HdC6?1Szl3ff`HCdX-tolv5&36Xakher4OY*!B`s5b`EEAjHbO-% z3O$&D8fOHCF~2jOLO2Z{pax!H9ymW(3#=umguP$8~Cg}mWblY%hRp=*lj-vJe|!8ioR;X_z}n&2{O zL-$bqL$;amJgD*8p&#=*y(p;T0P9H9LMEYhJR2i%32LBqsGSs`a$AKu3)QFy)ZK1Q zc_cng{XtYpUPMJ?3~KzT=<2~d3OY0&;S=aZE$DB21nciG5om*2c_)meDbeCLcR%Q7gVm&Q45%j8h8b2;%`y6VFxP21*p?p zf{Az*W6*D>JqxID+M^D2Dkfu^HG3!V*T9=;=z#lCFVvty6uiq!)Dk;W?}!TVL~M$) z?enit<7`Ep@`I=d-NLpQwA-YxC+hidto<;#6coZ7RLH(Y<$NP5HM>xuJA?}5ueQA$ zwe#z!L;4TuEZo6&uIKy0Ch-@qULq0DQKnsdrXIxs24pr0LP+IupPCa zBdDF9LM^BoHL=fL^PLF5Sn5d_kNr`HcPuKhAEF}rDY9_a`IdrKl!xkAf;s~?Q7dj* zU?xsLg}x0cbiGia9e~QMi(1ekRKz|*EqI;Pi+X<-Y9YnwVScBAf(8sNG&^a6n)pF% zg2||X`l0#_MNKdQHBc6|#ObKhy$ZF%?Wp&DMy2R9>ix5*_b=)>^E-c2Q0{9`w;^Dk z31vL0y$31{F@K1UC3M@8ZiYG-#*3u$n`{FV$y^=pTk_$5?iCLJLDTB%Ee z240QL@fa!+*Q|99n#0rtH9;3^Dr%siIL?R2;A7N}A2K`lJ8YhZTjNmQjU-#|c9{69 zp+5}`a3t!GWTEc!G)%+=wtW+7rw48O8C1V|N6ZexQ9JC38fU0=G%8i^qTc@)1MnM{ zLL7xns2ra{KdeLzd;=Bw`#1{ye>M}0MGfGh2ApU80u{MD)U7yVEwNT&9PPJI$9hyHe1O(@`IiEL18MVlr+*{qnhh4`J*Ha~pbND)mfM3O1lpR*b<|feQ7% z_zXt;V!rvWq2`;3?gJDSQ&7%VqU!5VJKcz(xD#tbhT8FI)Bt6u$o*y8@1x!iIB9kq zfwgA=6`Ag+i1x=&9CecTYoaMM)UF7_s4qqhv<9`X^%#OXkvW_qTVL}lf7MXmf*LUT zl-aol)xRt1&^?Vx*#uN7W}*jkP7!~FYBvqaeKG1#m7osMIn)9ho;Lj=P&-OQ?W6~W zVLB=W<1qpkqwf1!)bEfzsD+fGzMOYZ{i56wb01Ss6Awb&f;3D)J=X$%M1^nXcM?{L%t=Alxs8udI6UuAx$jKV+~QqP*u=3+AS z4XBl#L+$W}t=FJ(82BfDhG8>I#~f6@tEh=?qZSr$&Wsa_3VAX}Q+g)dT}Xgg1y)kqbm4JV;c6yPjN6-pmO@yMKjS1Ort&rWAHot8qOR?2TlcFn zw{~_rM-ij9Y0!(MSNM)&C2HqAubLMpqRzrJyn*v=`@7f7f;_L8(f7w3Xsy0O#bvt!#+uplt(b4#z-s3XIWKA3$m)M=c pKJUZcob}ah{ImA0dc81rOkwV_Zso1LyUVhD>J;XB`(79r@IPR{CG`LR delta 4417 zcmXZf2~d_r9LMqXMCFo8kWhIg)I88KkJLh(BF!T_qRS&AF*+y@>{1`fl*ukMoz&8) zCd(rWc{RlhwGym^%#b8PO}rqj6r0jA`~KK<96!7J?6b%Jzx#lzYnm;uX;u*A^I95X zUM?^u8kb@R+<@V@4}-B9L$J=Z|BbQK12-Gf8oOWs_Q41oh;48bM&jKVg^!{7y?|YD z@n+*SPEbgL7P1>VVyWwJ3PY)f6x!z5M&4&&Qw!N=HRt5^93SQ45-nTF7El3g33^J5W0> zaejx2*kMdX?+69$FmbCfao8J`>rtp2-jDH^iJBk}BXFf_-{jiMQIR`_dhZfy+~%L! z$c11p>WQdv#$zP&n`soHXm}PiaK3$D-gg!`%TWW?qIP`FwO?}WA=_*UVo<5;f$Eov zJ#ZX$!THF=#ss8&Dx{w%w+n4JtL^sQx`s5xX5H;6s>*MW_jm zqBe99)xTAd9WMbjes>IDelv)IIu3KDqZaZAYR7Xi8W*7kT7%k25h}OGP-meIbqFt` zPI>SSV}@WXDkXQLBJv<={A~2}AeVv;&C7TTu178C40gwVQHQYWXLjYiu|4%jnTEJY?!1<_koa zP^Y;ZyI}>!;uTaRVm`Oy^gtczWK6;_&Uv2`e+~Q*4gGN^YT^b|hysf3MD1}9^(555 zSr~_N-Sd^GaXvwGJ)uH_!`(gr)qMm_D zK>=z(dr&*yk6KV2YT~~z9?ciVbOImxFa>pZGf4f3tr<~k9vO#Y9XcAi}_781r6BxE4!00 z)WorP4PJ{HXeg@RNYn)5Q3FlE1e}FB-EX3HSb%!(3sj2AQ14fu-v3$8nctkDpxig0 zZiCrtLm7=~ABal9ZK#3MQ9FGYb=v2+=Sxui-^0%M398>gZ2V3*>rmrdz{dOEyu|J# z6t#l{R4Rs`UYvl6$Rt#1rlZcl8`ujAP?0!-+SvuvLjJ>!7+7libw^En4=OT`loEff zG=~NayaM&YSExwTIxnLRQ`kN`L0@MwYM_yr-Gs>C0P1_cwmZLqdLHCR`s9UkiS?;XCc-qgR z#`l8DY|cAiR~q_b5Kcx7{HS~Gx%vuJ#6Cjpa1ZLfSD+$w5%oRkP;NIg0M&mucECxf z6hCL{o>}D{Y{xDBY) zj)SoAETAGY02R>`jKF&1l^h}y|O zjKnll3Z`K@T!^~wZ=-&OY(p*N2h^AIJgQ%aci7&?MAXEos9P`wdt(l2fooAA+=hzO z9*oBWs1%&XMR*m}Z&8&sAET+SLT#`R^?nhiqPLI2%@jg^B3w8c`D+&Nj}F)5YJRiD zb*Nt$f`4{DBQTBn5L5(S#rgOiYT>>bdw9p97Lbcd!3xy#b(qQg<}iiPG$bFfp?wvT zsDFrBX(eihCtbY(mBXe-x#Jjt_hBBY-*MDLf1nm-j@fadP$9n-yW?OC)BVq+pdCJq zN`;K6t!4s#uzLcO>cHNg%{#W8iI4C>lmaP_OG zThzMVUb}GYLw#sH@y{SeGicC@hkoThoOl$q^TB8Bi;tqt!fgB<7r6FW=j?*!;`6lo zQD>vyZ#G4#*qQn`?1VX}l=zzR4}bES`0KAfFtPc-L4ErAi(f1AwM@yL{@7l>|E+?C l>jR&hI(_D}tl3%Fv!}i>yta?O^mtyArn9sBX(vXv`5(yrAmac4 diff --git a/locale/lt_LT/LC_MESSAGES/django.po b/locale/lt_LT/LC_MESSAGES/django.po index e17f1a05..82d7cbbe 100644 --- a/locale/lt_LT/LC_MESSAGES/django.po +++ b/locale/lt_LT/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-14 15:08+0000\n" -"PO-Revision-Date: 2021-11-15 18:03\n" +"PO-Revision-Date: 2021-11-15 19:25\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Lithuanian\n" "Language: lt\n" @@ -172,7 +172,7 @@ msgstr "Knygos" #: bookwyrm/settings.py:165 msgid "English" -msgstr "Anglų" +msgstr "English (Anglų)" #: bookwyrm/settings.py:166 msgid "Deutsch (German)" @@ -3647,7 +3647,7 @@ msgstr "įvertinta %(book)s:" #: bookwyrm/templates/snippets/status/headers/read.html:7 #, python-format msgid "finished reading %(book)s" -msgstr "baigti skaityti %(book)s" +msgstr "baigė skaityti %(book)s" #: bookwyrm/templates/snippets/status/headers/reading.html:7 #, python-format @@ -3681,7 +3681,7 @@ msgstr "Mėgti būseną" #: bookwyrm/templates/snippets/status/status.html:10 msgid "boosted" -msgstr "pagreitinta" +msgstr "iškėlė" #: bookwyrm/templates/snippets/status/status_options.html:7 #: bookwyrm/templates/snippets/user_options.html:7 diff --git a/locale/pt_BR/LC_MESSAGES/django.mo b/locale/pt_BR/LC_MESSAGES/django.mo index 1ebe5280048618f0a5075466d95c52dd815070ec..dd430b3ae3e17cf023d1419fd26eefc3f9d498bd 100644 GIT binary patch delta 23083 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 OCLf&M$c01U*#8544r|;1 delta 18160 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\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt\n" @@ -46,41 +46,42 @@ msgstr "{i} usos" msgid "Unlimited" msgstr "Ilimitado" -#: bookwyrm/forms.py:326 +#: bookwyrm/forms.py:332 msgid "List Order" msgstr "Ordem de inserção" -#: bookwyrm/forms.py:327 +#: bookwyrm/forms.py:333 msgid "Book Title" msgstr "Título do livro" -#: bookwyrm/forms.py:328 bookwyrm/templates/shelf/shelf.html:136 -#: bookwyrm/templates/shelf/shelf.html:168 +#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149 +#: bookwyrm/templates/shelf/shelf.html:181 #: bookwyrm/templates/snippets/create_status/review.html:33 msgid "Rating" msgstr "Avaliação" -#: bookwyrm/forms.py:330 bookwyrm/templates/lists/list.html:109 +#: bookwyrm/forms.py:336 bookwyrm/templates/lists/list.html:110 msgid "Sort By" msgstr "Organizar por" -#: bookwyrm/forms.py:334 +#: bookwyrm/forms.py:340 msgid "Ascending" msgstr "Crescente" -#: bookwyrm/forms.py:335 +#: bookwyrm/forms.py:341 msgid "Descending" msgstr "Decrescente" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "Erro ao carregar livro" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "Não foi possível encontrar o livro" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "Pendente" @@ -100,23 +101,23 @@ msgstr "Exclusão de moderador" msgid "Domain block" msgstr "Bloqueio de domínio" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "Audiolivro" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "e-book" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "Graphic novel" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "Capa dura" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "Capa mole" @@ -133,21 +134,21 @@ msgstr "Federado" msgid "Blocked" msgstr "Bloqueado" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s não é um remote_id válido" -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s não é um nome de usuário válido" -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "nome de usuário" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "Já existe um usuário com este nome." @@ -165,7 +166,7 @@ msgstr "Linha do tempo dos livros" #: bookwyrm/settings.py:119 bookwyrm/templates/search/layout.html:21 #: bookwyrm/templates/search/layout.html:42 -#: bookwyrm/templates/user/layout.html:81 +#: bookwyrm/templates/user/layout.html:88 msgid "Books" msgstr "Livros" @@ -223,7 +224,7 @@ msgid "Edit Author" msgstr "Editar autor" #: bookwyrm/templates/author/author.html:34 -#: bookwyrm/templates/author/edit_author.html:41 +#: bookwyrm/templates/author/edit_author.html:43 msgid "Aliases:" msgstr "Pseudônimos:" @@ -276,71 +277,72 @@ msgstr "Adicionado:" msgid "Updated:" msgstr "Atualizado:" -#: bookwyrm/templates/author/edit_author.html:15 +#: bookwyrm/templates/author/edit_author.html:16 #: bookwyrm/templates/book/edit/edit_book.html:25 msgid "Last edited by:" msgstr "Editado pela última vez por:" -#: bookwyrm/templates/author/edit_author.html:31 +#: bookwyrm/templates/author/edit_author.html:33 #: bookwyrm/templates/book/edit/edit_book_form.html:15 msgid "Metadata" msgstr "Metadados" -#: bookwyrm/templates/author/edit_author.html:33 -#: bookwyrm/templates/lists/form.html:8 bookwyrm/templates/shelf/form.html:9 +#: bookwyrm/templates/author/edit_author.html:35 +#: bookwyrm/templates/lists/form.html:9 bookwyrm/templates/shelf/form.html:9 msgid "Name:" msgstr "Nome:" -#: bookwyrm/templates/author/edit_author.html:43 +#: bookwyrm/templates/author/edit_author.html:45 #: bookwyrm/templates/book/edit/edit_book_form.html:65 #: bookwyrm/templates/book/edit/edit_book_form.html:79 #: bookwyrm/templates/book/edit/edit_book_form.html:124 msgid "Separate multiple values with commas." msgstr "Separe com vírgulas." -#: bookwyrm/templates/author/edit_author.html:50 +#: bookwyrm/templates/author/edit_author.html:52 msgid "Bio:" msgstr "Sobre mim:" -#: bookwyrm/templates/author/edit_author.html:57 +#: bookwyrm/templates/author/edit_author.html:59 msgid "Wikipedia link:" msgstr "Link da Wikipédia:" -#: bookwyrm/templates/author/edit_author.html:63 +#: bookwyrm/templates/author/edit_author.html:65 msgid "Birth date:" msgstr "Data de nascimento:" -#: bookwyrm/templates/author/edit_author.html:71 +#: bookwyrm/templates/author/edit_author.html:73 msgid "Death date:" msgstr "Data da morte:" -#: bookwyrm/templates/author/edit_author.html:79 +#: bookwyrm/templates/author/edit_author.html:81 msgid "Author Identifiers" msgstr "Identificadores do autor" -#: bookwyrm/templates/author/edit_author.html:81 +#: bookwyrm/templates/author/edit_author.html:83 msgid "Openlibrary key:" msgstr "Chave Openlibrary:" -#: bookwyrm/templates/author/edit_author.html:89 +#: bookwyrm/templates/author/edit_author.html:91 #: bookwyrm/templates/book/edit/edit_book_form.html:224 msgid "Inventaire ID:" msgstr "ID Inventaire:" -#: bookwyrm/templates/author/edit_author.html:97 +#: bookwyrm/templates/author/edit_author.html:99 msgid "Librarything key:" msgstr "Chave Librarything:" -#: bookwyrm/templates/author/edit_author.html:105 +#: bookwyrm/templates/author/edit_author.html:107 msgid "Goodreads key:" msgstr "Chave Goodreads:" -#: bookwyrm/templates/author/edit_author.html:116 +#: bookwyrm/templates/author/edit_author.html:118 #: bookwyrm/templates/book/book.html:140 #: bookwyrm/templates/book/edit/edit_book.html:110 #: bookwyrm/templates/book/readthrough.html:76 +#: bookwyrm/templates/groups/form.html:24 #: bookwyrm/templates/lists/bookmark_button.html:15 -#: bookwyrm/templates/lists/form.html:44 +#: bookwyrm/templates/lists/form.html:75 #: bookwyrm/templates/preferences/edit_user.html:124 #: bookwyrm/templates/settings/announcements/announcement_form.html:69 #: bookwyrm/templates/settings/federation/edit_instance.html:74 @@ -352,11 +354,13 @@ msgstr "Chave Goodreads:" msgid "Save" msgstr "Salvar" -#: bookwyrm/templates/author/edit_author.html:117 +#: bookwyrm/templates/author/edit_author.html:119 #: bookwyrm/templates/book/book.html:141 bookwyrm/templates/book/book.html:190 #: bookwyrm/templates/book/cover_modal.html:32 -#: bookwyrm/templates/book/edit/edit_book.html:111 +#: bookwyrm/templates/book/edit/edit_book.html:112 +#: bookwyrm/templates/book/edit/edit_book.html:115 #: bookwyrm/templates/book/readthrough.html:77 +#: bookwyrm/templates/groups/delete_group_modal.html:17 #: bookwyrm/templates/lists/delete_list_modal.html:17 #: bookwyrm/templates/settings/federation/instance.html:88 #: bookwyrm/templates/snippets/delete_readthrough_modal.html:17 @@ -397,7 +401,7 @@ msgstr "Adicionar descrição" #: bookwyrm/templates/book/book.html:136 #: bookwyrm/templates/book/edit/edit_book_form.html:34 -#: bookwyrm/templates/lists/form.html:12 bookwyrm/templates/shelf/form.html:17 +#: bookwyrm/templates/lists/form.html:13 bookwyrm/templates/shelf/form.html:17 msgid "Description:" msgstr "Descrição:" @@ -430,7 +434,7 @@ msgstr "Criar" #: bookwyrm/templates/book/book.html:197 msgid "You don't have any reading activity for this book." -msgstr "Você não tem nenhuma atividade de leitura para este livro." +msgstr "Você ainda não registrou seu progresso para este livro." #: bookwyrm/templates/book/book.html:218 msgid "Reviews" @@ -460,7 +464,7 @@ msgstr "Lugares" #: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12 #: bookwyrm/templates/search/layout.html:25 #: bookwyrm/templates/search/layout.html:50 -#: bookwyrm/templates/user/layout.html:75 +#: bookwyrm/templates/user/layout.html:82 msgid "Lists" msgstr "Listas" @@ -470,7 +474,7 @@ msgstr "Adicionar à lista" #: bookwyrm/templates/book/book.html:315 #: bookwyrm/templates/book/cover_modal.html:31 -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 #: bookwyrm/templates/settings/email_blocklist/domain_form.html:26 #: bookwyrm/templates/settings/ip_blocklist/ip_address_form.html:32 msgid "Add" @@ -543,7 +547,9 @@ msgid "This is a new work" msgstr "É uma nova obra" #: bookwyrm/templates/book/edit/edit_book.html:97 -#: bookwyrm/templates/password_reset.html:30 +#: bookwyrm/templates/groups/members.html:16 +#: bookwyrm/templates/landing/password_reset.html:30 +#: bookwyrm/templates/snippets/remove_from_group_button.html:16 msgid "Confirm" msgstr "Confirmar" @@ -612,7 +618,7 @@ msgid "John Doe, Jane Smith" msgstr "Fulano da Silva, Sicrano de Oliveira" #: bookwyrm/templates/book/edit/edit_book_form.html:132 -#: bookwyrm/templates/shelf/shelf.html:127 +#: bookwyrm/templates/shelf/shelf.html:140 msgid "Cover" msgstr "Capa" @@ -686,7 +692,7 @@ msgstr "%(pages)s páginas" #: bookwyrm/templates/book/publisher_info.html:38 #, python-format msgid "%(languages)s language" -msgstr "%(languages)s idioma" +msgstr "Língua: %(languages)s" #: bookwyrm/templates/book/publisher_info.html:65 #, python-format @@ -793,7 +799,7 @@ msgstr "Reenviar link de confirmação" #: bookwyrm/templates/confirm_email/resend_form.html:11 #: bookwyrm/templates/landing/layout.html:67 -#: bookwyrm/templates/password_reset_request.html:18 +#: bookwyrm/templates/landing/password_reset_request.html:18 #: bookwyrm/templates/preferences/edit_user.html:56 #: bookwyrm/templates/snippets/register_form.html:13 msgid "Email address:" @@ -828,7 +834,7 @@ msgstr "Listar seu perfil no diretório para que outros usuários da BookWyrm te #: bookwyrm/templates/directory/directory.html:24 #, python-format msgid "You can opt-out at any time in your profile settings." -msgstr "Você pode cancelar isto a qualquer momento em suas configurações de perfil." +msgstr "Você pode desabilitar esta opção a qualquer momento em suas configurações de perfil." #: bookwyrm/templates/directory/directory.html:29 #: bookwyrm/templates/feed/goal_card.html:17 @@ -887,22 +893,37 @@ msgstr "Usuários da BookWyrm" msgid "All known users" msgstr "Todos os usuários conhecidos" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 +#, python-format +msgid "%(username)s wants to read %(book_title)s" +msgstr "%(username)s quer ler %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "%(username)s terminou de ler %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "%(username)s começou a ler %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:23 #, python-format msgid "%(username)s rated %(book_title)s" msgstr "%(username)s avaliou %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:13 +#: bookwyrm/templates/discover/card-header.html:27 #, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "%(username)s resenhou %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" -msgstr "%(username)s comentou sobre %(book_title)s" +msgstr "%(username)s comentou %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "%(username)s citou %(book_title)s" @@ -993,10 +1014,10 @@ msgid "You requested to reset your %(site_name)s password. Click the link below msgstr "Você solicitou a redefinição de sua senha no %(site_name)s. Clique no link abaixo para definir uma nova senha e entrar no site." #: bookwyrm/templates/email/password_reset/html_content.html:9 -#: bookwyrm/templates/password_reset.html:4 -#: bookwyrm/templates/password_reset.html:10 -#: bookwyrm/templates/password_reset_request.html:4 -#: bookwyrm/templates/password_reset_request.html:10 +#: bookwyrm/templates/landing/password_reset.html:4 +#: bookwyrm/templates/landing/password_reset.html:10 +#: bookwyrm/templates/landing/password_reset_request.html:4 +#: bookwyrm/templates/landing/password_reset_request.html:10 msgid "Reset Password" msgstr "Redefinir senha" @@ -1042,7 +1063,7 @@ msgstr "Não há nenhuma atividade! Tente seguir um usuário para começar" #: bookwyrm/templates/user/goal_form.html:6 #, python-format msgid "%(year)s Reading Goal" -msgstr "Meta de leitura de %(year)s" +msgstr "Meta de leitura para %(year)s" #: bookwyrm/templates/feed/goal_card.html:18 #, python-format @@ -1053,9 +1074,8 @@ msgstr "Você pode definir ou alterar sua meta de leitura a qualquer momento em msgid "Updates" msgstr "Atualizações" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" msgstr "Seus livros" #: bookwyrm/templates/feed/layout.html:14 @@ -1064,11 +1084,13 @@ msgstr "Não há nenhum livro aqui! Tente pesquisar livros para começar" #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "Quero ler" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "Lendo atualmente" @@ -1076,6 +1098,7 @@ msgstr "Lendo atualmente" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "Lido" @@ -1102,7 +1125,7 @@ msgid "What are you reading?" msgstr "O que você está lendo?" #: bookwyrm/templates/get_started/books.html:9 -#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137 +#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:138 msgid "Search for a book" msgstr "Pesquisar livro" @@ -1120,8 +1143,9 @@ msgstr "Você pode adicionar livros quando começar a usar o %(site_name)s." #: bookwyrm/templates/get_started/books.html:17 #: bookwyrm/templates/get_started/users.html:18 #: bookwyrm/templates/get_started/users.html:19 -#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52 -#: bookwyrm/templates/lists/list.html:141 +#: bookwyrm/templates/groups/group.html:19 +#: bookwyrm/templates/groups/group.html:20 bookwyrm/templates/layout.html:51 +#: bookwyrm/templates/layout.html:52 bookwyrm/templates/lists/list.html:142 #: bookwyrm/templates/search/layout.html:4 #: bookwyrm/templates/search/layout.html:9 msgid "Search" @@ -1137,7 +1161,7 @@ msgid "Popular on %(site_name)s" msgstr "Popular em %(site_name)s" #: bookwyrm/templates/get_started/books.html:58 -#: bookwyrm/templates/lists/list.html:154 +#: bookwyrm/templates/lists/list.html:155 msgid "No books found" msgstr "Nenhum livro encontrado" @@ -1212,7 +1236,7 @@ msgstr "Mostrar conta nas sugestões de usuários:" #: bookwyrm/templates/get_started/profile.html:52 msgid "Your account will show up in the directory, and may be recommended to other BookWyrm users." -msgstr "Sua conta aparecerá no diretório e pode ser recomendada para outros usuários da BookWyrm." +msgstr "Sua conta aparecerá no diretório e poderá ser recomendada para outros usuários da BookWyrm." #: bookwyrm/templates/get_started/users.html:11 msgid "Search for a user" @@ -1223,9 +1247,110 @@ msgstr "Procurar usuário" msgid "No users found for \"%(query)s\"" msgstr "Nenhum usuário encontrado para \"%(query)s\"" +#: bookwyrm/templates/groups/create_form.html:5 +msgid "Create Group" +msgstr "Criar grupo" + +#: bookwyrm/templates/groups/created_text.html:4 +#, python-format +msgid "Managed by %(username)s" +msgstr "Gerenciado por %(username)s" + +#: bookwyrm/templates/groups/delete_group_modal.html:4 +msgid "Delete this group?" +msgstr "Deletar grupo?" + +#: bookwyrm/templates/groups/delete_group_modal.html:7 +#: bookwyrm/templates/lists/delete_list_modal.html:7 +msgid "This action cannot be un-done" +msgstr "Esta ação não pode ser desfeita" + +#: bookwyrm/templates/groups/delete_group_modal.html:15 +#: bookwyrm/templates/lists/delete_list_modal.html:15 +#: bookwyrm/templates/settings/announcements/announcement.html:20 +#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 +#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 +#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 +#: bookwyrm/templates/snippets/follow_request_buttons.html:12 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:13 +msgid "Delete" +msgstr "Excluir" + +#: bookwyrm/templates/groups/edit_form.html:5 +msgid "Edit Group" +msgstr "Editar grupo" + +#: bookwyrm/templates/groups/find_users.html:6 +msgid "Add new members!" +msgstr "Adicione novos membros!" + +#: bookwyrm/templates/groups/form.html:8 +msgid "Group Name:" +msgstr "Nome do grupo:" + +#: bookwyrm/templates/groups/form.html:12 +msgid "Group Description:" +msgstr "Descrição do grupo:" + +#: bookwyrm/templates/groups/form.html:30 +msgid "Delete group" +msgstr "Excluir grupo" + +#: bookwyrm/templates/groups/group.html:15 +msgid "Search to add a user" +msgstr "Pesquisar usuário para adicionar" + +#: bookwyrm/templates/groups/group.html:36 +msgid "This group has no lists" +msgstr "Este grupo não tem listas" + +#: bookwyrm/templates/groups/layout.html:16 +msgid "Edit group" +msgstr "Editar grupo" + +#: bookwyrm/templates/groups/members.html:8 +msgid "Members can add and remove books on a group's book lists" +msgstr "Membros podem adicionar ou remover livros nas listas de seu grupo" + +#: bookwyrm/templates/groups/members.html:19 +msgid "Leave group" +msgstr "Sair do grupo" + +#: bookwyrm/templates/groups/members.html:41 +#: bookwyrm/templates/groups/suggested_users.html:32 +#: bookwyrm/templates/snippets/suggested_users.html:31 +#: bookwyrm/templates/user/user_preview.html:36 +msgid "Follows you" +msgstr "Segue você" + +#: bookwyrm/templates/groups/suggested_users.html:17 +#: bookwyrm/templates/snippets/suggested_users.html:16 +#, python-format +msgid "%(mutuals)s follower you follow" +msgid_plural "%(mutuals)s followers you follow" +msgstr[0] "%(mutuals)s seguidor que você segue" +msgstr[1] "%(mutuals)s seguidores que você segue" + +#: bookwyrm/templates/groups/suggested_users.html:24 +#: bookwyrm/templates/snippets/suggested_users.html:23 +#, python-format +msgid "%(shared_books)s book on your shelves" +msgid_plural "%(shared_books)s books on your shelves" +msgstr[0] "%(shared_books)s livro em sua estante" +msgstr[1] "%(shared_books)s livros em suas estantes" + +#: bookwyrm/templates/groups/suggested_users.html:40 +#, python-format +msgid "No potential members found for \"%(user_query)s\"" +msgstr "Nenhum membro em potencial encontrado para \"%(user_query)s\"" + +#: bookwyrm/templates/groups/user_groups.html:15 +msgid "Manager" +msgstr "Gerente" + #: bookwyrm/templates/import/import.html:5 #: bookwyrm/templates/import/import.html:9 -#: bookwyrm/templates/shelf/shelf.html:57 +#: bookwyrm/templates/shelf/shelf.html:61 msgid "Import Books" msgstr "Importar livros" @@ -1259,100 +1384,160 @@ msgid "No recent imports" msgstr "Nenhuma importação recente" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "Status da importação" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" -msgstr "Voltar às importações" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" +msgstr "Status da nova tentativa" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "Importações" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "Importação iniciada:" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "Importação concluída:" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "FALHA NA EXECUÇÃO" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "Importação em andamento." - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "(Recarregue para atualizar!)" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" -msgstr "Falha ao carregar" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "Em curso" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "Vá ao fim da lista para selecionar os %(failed_count)s itens que não foram importados." +msgid "Refresh" +msgstr "Atualizar" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "Linha %(index)s: %(title)s de %(author)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "%(display_counter)s item precisa de aprovação manual." +msgstr[1] "%(display_counter)s itens precisam de aprovação manual." -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" -msgstr "Selecionar todos" +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" +msgstr "Revisar itens" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" -msgstr "Tentar novamente" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "Falha ao importar %(display_counter)s item." +msgstr[1] "Falha ao importar %(display_counter)s itens." -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "Importado com sucesso" +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" +msgstr "Ver e solucionar importações fracassadas" -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" -msgstr "Progresso da importação" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "Linha" -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "Livro" - -#: bookwyrm/templates/import/import_status.html:122 -#: bookwyrm/templates/shelf/shelf.html:128 -#: bookwyrm/templates/shelf/shelf.html:150 +#: bookwyrm/templates/import/import_status.html:94 +#: bookwyrm/templates/shelf/shelf.html:141 +#: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "Título" -#: bookwyrm/templates/import/import_status.html:125 -#: bookwyrm/templates/shelf/shelf.html:129 -#: bookwyrm/templates/shelf/shelf.html:153 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "ISBN" + +#: bookwyrm/templates/import/import_status.html:100 +#: bookwyrm/templates/shelf/shelf.html:142 +#: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "Autor" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "Estante" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "Resenhar" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "Livro" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "Publicação" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "Visualizar resenha importada" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "Importado" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "Precisa de resenha manual" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "Solução de problemas de importação" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "Aprovar uma sugestão adicionará permanentemente o livro sugerido às suas estantes e associará suas datas de leitura, resenhas e avaliações aos do livro." + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "Aprovar" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "Rejeitar" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "Você pode baixar seus dados do Goodreads na página de Importar/Exportar da sua conta." -#: bookwyrm/templates/invite.html:4 bookwyrm/templates/invite.html:8 -#: bookwyrm/templates/login.html:49 -msgid "Create an Account" -msgstr "Criar conta" +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "Itens cuja importação falhou" -#: bookwyrm/templates/invite.html:21 -msgid "Permission Denied" -msgstr "Permissão negada" +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "Solução de problemas" -#: bookwyrm/templates/invite.html:22 -msgid "Sorry! This invite code is no longer valid." -msgstr "Desculpe! Este convite não é mais válido." +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "Tentar uma importação novamente pode corrigir itens faltantes como:" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "O livro foi adicionado à sua instância desde esta importação" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "Um erro temporário ou timeout fez com que a fonte de dados externa ficasse inacessível." + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "Desde a importação a BookWyrm foi atualizada com uma correção de bugs" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "Fale com a administração ou crie um problema se você perceber itens com erros inesperados." #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format @@ -1369,6 +1554,20 @@ msgstr "Código de conduta" msgid "Privacy Policy" msgstr "Política de privacidade" +#: bookwyrm/templates/landing/invite.html:4 +#: bookwyrm/templates/landing/invite.html:8 +#: bookwyrm/templates/landing/login.html:49 +msgid "Create an Account" +msgstr "Criar conta" + +#: bookwyrm/templates/landing/invite.html:21 +msgid "Permission Denied" +msgstr "Permissão negada" + +#: bookwyrm/templates/landing/invite.html:22 +msgid "Sorry! This invite code is no longer valid." +msgstr "Desculpe! Este convite não é mais válido." + #: bookwyrm/templates/landing/landing.html:6 msgid "Recent Books" msgstr "Livros recentes" @@ -1407,6 +1606,53 @@ msgstr "Obrigado! Sua solicitação foi recebida." msgid "Your Account" msgstr "Sua conta" +#: bookwyrm/templates/landing/login.html:4 +msgid "Login" +msgstr "Entrar" + +#: bookwyrm/templates/landing/login.html:7 +#: bookwyrm/templates/landing/login.html:37 bookwyrm/templates/layout.html:179 +msgid "Log in" +msgstr "Entrar" + +#: bookwyrm/templates/landing/login.html:15 +msgid "Success! Email address confirmed." +msgstr "Endereço de e-mail confirmado com sucesso." + +#: bookwyrm/templates/landing/login.html:21 bookwyrm/templates/layout.html:170 +#: bookwyrm/templates/snippets/register_form.html:4 +msgid "Username:" +msgstr "Usuário:" + +#: bookwyrm/templates/landing/login.html:27 +#: bookwyrm/templates/landing/password_reset.html:17 +#: bookwyrm/templates/layout.html:174 +#: bookwyrm/templates/snippets/register_form.html:22 +msgid "Password:" +msgstr "Senha:" + +#: bookwyrm/templates/landing/login.html:40 bookwyrm/templates/layout.html:176 +msgid "Forgot your password?" +msgstr "Esqueceu sua senha?" + +#: bookwyrm/templates/landing/login.html:62 +msgid "More about this site" +msgstr "Mais sobre este site" + +#: bookwyrm/templates/landing/password_reset.html:23 +#: bookwyrm/templates/preferences/change_password.html:18 +#: bookwyrm/templates/preferences/delete_user.html:20 +msgid "Confirm password:" +msgstr "Confirmar senha:" + +#: bookwyrm/templates/landing/password_reset_request.html:14 +msgid "A link to reset your password will be sent to your email address" +msgstr "Um link para redefinir sua senha será enviada para seu e-mail" + +#: bookwyrm/templates/landing/password_reset_request.html:28 +msgid "Reset password" +msgstr "Redefinir senha" + #: bookwyrm/templates/layout.html:13 #, python-format msgid "%(site_name)s search" @@ -1424,10 +1670,6 @@ msgstr "Menu de navegação principal" msgid "Feed" msgstr "Novidades" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "Seus livros" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "Configurações" @@ -1454,25 +1696,10 @@ msgstr "Sair" msgid "Notifications" msgstr "Notificações" -#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174 -#: bookwyrm/templates/login.html:21 -#: bookwyrm/templates/snippets/register_form.html:4 -msgid "Username:" -msgstr "Usuário:" - #: bookwyrm/templates/layout.html:175 msgid "password" msgstr "senha" -#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40 -msgid "Forgot your password?" -msgstr "Esqueceu sua senha?" - -#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7 -#: bookwyrm/templates/login.html:37 -msgid "Log in" -msgstr "Entrar" - #: bookwyrm/templates/layout.html:187 msgid "Join" msgstr "Registrar" @@ -1487,7 +1714,7 @@ msgstr "Erro ao publicar" #: bookwyrm/templates/layout.html:234 msgid "Contact site admin" -msgstr "Contatar administração" +msgstr "Falar com a administração" #: bookwyrm/templates/layout.html:238 msgid "Documentation" @@ -1513,11 +1740,16 @@ msgstr "Criar lista" #: bookwyrm/templates/lists/created_text.html:5 #, python-format -msgid "Created and curated by %(username)s" -msgstr "Criada e curada por %(username)s" +msgid "Created by %(username)s and managed by %(groupname)s" +msgstr "Criada por %(username)s e gerenciada por %(groupname)s" #: bookwyrm/templates/lists/created_text.html:7 #, python-format +msgid "Created and curated by %(username)s" +msgstr "Criada e organizada por %(username)s" + +#: bookwyrm/templates/lists/created_text.html:9 +#, python-format msgid "Created by %(username)s" msgstr "Criada por %(username)s" @@ -1537,10 +1769,6 @@ msgstr "Tudo pronto!" msgid "Suggested by" msgstr "Sugerido por" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "Aprovar" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "Descartar" @@ -1549,118 +1777,130 @@ msgstr "Descartar" msgid "Delete this list?" msgstr "Deletar esta lista?" -#: bookwyrm/templates/lists/delete_list_modal.html:7 -msgid "This action cannot be un-done" -msgstr "Esta ação não pode ser desfeita" - -#: bookwyrm/templates/lists/delete_list_modal.html:15 -#: bookwyrm/templates/settings/announcements/announcement.html:20 -#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:49 -#: bookwyrm/templates/settings/ip_blocklist/ip_blocklist.html:36 -#: bookwyrm/templates/snippets/delete_readthrough_modal.html:15 -#: bookwyrm/templates/snippets/follow_request_buttons.html:12 -msgid "Delete" -msgstr "Excluir" - #: bookwyrm/templates/lists/edit_form.html:5 #: bookwyrm/templates/lists/layout.html:16 msgid "Edit List" msgstr "Editar lista" -#: bookwyrm/templates/lists/form.html:18 +#: bookwyrm/templates/lists/form.html:19 msgid "List curation:" -msgstr "Curadoria da lista:" +msgstr "Configuração da lista:" -#: bookwyrm/templates/lists/form.html:21 +#: bookwyrm/templates/lists/form.html:22 msgid "Closed" msgstr "Fechada" -#: bookwyrm/templates/lists/form.html:22 +#: bookwyrm/templates/lists/form.html:23 msgid "Only you can add and remove books to this list" -msgstr "Só você pode adicionar ou remover livros nesta lista" - -#: bookwyrm/templates/lists/form.html:26 -msgid "Curated" -msgstr "Curado" +msgstr "Só você pode adicionar ou remover livros" #: bookwyrm/templates/lists/form.html:27 +msgid "Curated" +msgstr "Moderada" + +#: bookwyrm/templates/lists/form.html:28 msgid "Anyone can suggest books, subject to your approval" msgstr "Qualquer pessoa pode sugerir livros para sua aprovação" -#: bookwyrm/templates/lists/form.html:31 +#: bookwyrm/templates/lists/form.html:32 msgctxt "curation type" msgid "Open" msgstr "Aberta" -#: bookwyrm/templates/lists/form.html:32 +#: bookwyrm/templates/lists/form.html:33 msgid "Anyone can add books to this list" msgstr "Qualquer pessoa pode adicionar livros à lista" -#: bookwyrm/templates/lists/form.html:50 +#: bookwyrm/templates/lists/form.html:37 +msgid "Group" +msgstr "Grupo" + +#: bookwyrm/templates/lists/form.html:38 +msgid "Group members can add to and remove from this list" +msgstr "Membros do grupo podem adicionar e remover itens da lista" + +#: bookwyrm/templates/lists/form.html:41 +msgid "Select Group" +msgstr "Selecionar grupo" + +#: bookwyrm/templates/lists/form.html:45 +msgid "Select a group" +msgstr "Selecione um grupo" + +#: bookwyrm/templates/lists/form.html:56 +msgid "You don't have any Groups yet!" +msgstr "Você ainda não tem nenhum Grupo!" + +#: bookwyrm/templates/lists/form.html:58 +msgid "Create a Group" +msgstr "Criar grupo" + +#: bookwyrm/templates/lists/form.html:81 msgid "Delete list" msgstr "Excluir lista" -#: bookwyrm/templates/lists/list.html:20 +#: bookwyrm/templates/lists/list.html:21 msgid "You successfully suggested a book for this list!" msgstr "Você sugeriu um livro para esta lista com sucesso!" -#: bookwyrm/templates/lists/list.html:22 +#: bookwyrm/templates/lists/list.html:23 msgid "You successfully added a book to this list!" msgstr "Você adicionou um livro a esta lista com sucesso!" -#: bookwyrm/templates/lists/list.html:28 +#: bookwyrm/templates/lists/list.html:29 msgid "This list is currently empty" msgstr "Esta lista está vazia" -#: bookwyrm/templates/lists/list.html:66 +#: bookwyrm/templates/lists/list.html:67 #, python-format msgid "Added by %(username)s" msgstr "Adicionado por %(username)s" -#: bookwyrm/templates/lists/list.html:75 +#: bookwyrm/templates/lists/list.html:76 msgid "List position" msgstr "Posição na lista" -#: bookwyrm/templates/lists/list.html:81 +#: bookwyrm/templates/lists/list.html:82 msgid "Set" msgstr "Definir" -#: bookwyrm/templates/lists/list.html:91 +#: bookwyrm/templates/lists/list.html:92 +#: bookwyrm/templates/snippets/remove_from_group_button.html:19 #: bookwyrm/templates/snippets/shelf_selector.html:26 msgid "Remove" msgstr "Remover" -#: bookwyrm/templates/lists/list.html:105 -#: bookwyrm/templates/lists/list.html:122 +#: bookwyrm/templates/lists/list.html:106 +#: bookwyrm/templates/lists/list.html:123 msgid "Sort List" msgstr "Ordenar lista" -#: bookwyrm/templates/lists/list.html:115 +#: bookwyrm/templates/lists/list.html:116 msgid "Direction" -msgstr "Direção" +msgstr "Sentido" -#: bookwyrm/templates/lists/list.html:129 +#: bookwyrm/templates/lists/list.html:130 msgid "Add Books" msgstr "Adicionar livros" -#: bookwyrm/templates/lists/list.html:131 +#: bookwyrm/templates/lists/list.html:132 msgid "Suggest Books" msgstr "Sugerir livros" -#: bookwyrm/templates/lists/list.html:142 +#: bookwyrm/templates/lists/list.html:143 msgid "search" msgstr "pesquisar" -#: bookwyrm/templates/lists/list.html:148 +#: bookwyrm/templates/lists/list.html:149 msgid "Clear search" msgstr "Limpar pesquisa" -#: bookwyrm/templates/lists/list.html:153 +#: bookwyrm/templates/lists/list.html:154 #, python-format msgid "No books found matching the query \"%(query)s\"" msgstr "Nenhum livro encontrado para \"%(query)s\"" -#: bookwyrm/templates/lists/list.html:181 +#: bookwyrm/templates/lists/list.html:182 msgid "Suggest" msgstr "Sugerir" @@ -1672,30 +1912,18 @@ msgstr "Salvo" msgid "Your Lists" msgstr "Suas listas" -#: bookwyrm/templates/lists/lists.html:35 +#: bookwyrm/templates/lists/lists.html:36 msgid "All Lists" msgstr "Todas as listas" -#: bookwyrm/templates/lists/lists.html:39 +#: bookwyrm/templates/lists/lists.html:40 msgid "Saved Lists" msgstr "Listas salvas" -#: bookwyrm/templates/login.html:4 -msgid "Login" -msgstr "Entrar" - -#: bookwyrm/templates/login.html:15 -msgid "Success! Email address confirmed." -msgstr "Endereço de e-mail confirmado com sucesso." - -#: bookwyrm/templates/login.html:27 bookwyrm/templates/password_reset.html:17 -#: bookwyrm/templates/snippets/register_form.html:22 -msgid "Password:" -msgstr "Senha:" - -#: bookwyrm/templates/login.html:62 -msgid "More about this site" -msgstr "Mais sobre este site" +#: bookwyrm/templates/notifications/items/accept.html:16 +#, python-format +msgid "accepted your invitation to join group \"%(group_name)s\"" +msgstr "aceitou seu convite para participar do grupo \"%(group_name)s\"" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format @@ -1734,7 +1962,7 @@ msgstr "curtiu sua resenha de %(book_title)scomment on%(book_title)s" +msgid "liked your comment on %(book_title)s" msgstr "curtiu seu comentário sobre %(book_title)s" #: bookwyrm/templates/notifications/items/fav.html:31 @@ -1760,6 +1988,21 @@ msgstr "pediu para te seguir" msgid "Your import completed." msgstr "Sua importação foi concluída." +#: bookwyrm/templates/notifications/items/invite.html:15 +#, python-format +msgid "invited you to join the group \"%(group_name)s\"" +msgstr "te convidou para participar do grupo \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/join.html:16 +#, python-format +msgid "has joined your group \"%(group_name)s\"" +msgstr "entrou em seu grupo \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/leave.html:16 +#, python-format +msgid "has left your group \"%(group_name)s\"" +msgstr "saiu de seu grupo \"%(group_name)s\"" + #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format msgid "mentioned you in a review of %(book_title)s" @@ -1780,6 +2023,16 @@ msgstr "te mencionou em uma citação de %(book msgid "mentioned you in a status" msgstr "te mencionou em uma publicação" +#: bookwyrm/templates/notifications/items/remove.html:17 +#, python-format +msgid "has been removed from your group \"%(group_name)s\"" +msgstr "foi excluído(a) de seu grupo \"%(group_name)s\"" + +#: bookwyrm/templates/notifications/items/remove.html:23 +#, python-format +msgid "You have been removed from the \"%(group_name)s\" group" +msgstr "Você foi excluído(a) do grupo \"%(group_name)s\"" + #: bookwyrm/templates/notifications/items/reply.html:21 #, python-format msgid "replied to your review of %(book_title)s" @@ -1805,9 +2058,24 @@ msgstr "respondeu à sua report needs moderation." msgstr "Uma nova denúncia para moderação." +#: bookwyrm/templates/notifications/items/update.html:16 +#, python-format +msgid "has changed the privacy level for %(group_name)s" +msgstr "mudou o nível de privacidade de %(group_name)s" + +#: bookwyrm/templates/notifications/items/update.html:20 +#, python-format +msgid "has changed the name of %(group_name)s" +msgstr "mudou o nome de %(group_name)s" + +#: bookwyrm/templates/notifications/items/update.html:24 +#, python-format +msgid "has changed the description of %(group_name)s" +msgstr "mudou a descrição de %(group_name)s" + #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" -msgstr "Excluir notificações" +msgstr "Limpar notificações" #: bookwyrm/templates/notifications/notifications_page.html:29 msgid "All" @@ -1819,21 +2087,7 @@ msgstr "Menções" #: bookwyrm/templates/notifications/notifications_page.html:45 msgid "You're all caught up!" -msgstr "Você se atualizou!" - -#: bookwyrm/templates/password_reset.html:23 -#: bookwyrm/templates/preferences/change_password.html:18 -#: bookwyrm/templates/preferences/delete_user.html:20 -msgid "Confirm password:" -msgstr "Confirmar senha:" - -#: bookwyrm/templates/password_reset_request.html:14 -msgid "A link to reset your password will be sent to your email address" -msgstr "Um link para redefinir sua senha será enviada para seu e-mail" - -#: bookwyrm/templates/password_reset_request.html:28 -msgid "Reset password" -msgstr "Redefinir senha" +msgstr "Nenhuma notificação nova!" #: bookwyrm/templates/preferences/blocks.html:4 #: bookwyrm/templates/preferences/blocks.html:7 @@ -1887,7 +2141,7 @@ msgstr "Perfil" #: bookwyrm/templates/preferences/edit_user.html:13 #: bookwyrm/templates/preferences/edit_user.html:68 msgid "Display preferences" -msgstr "Preferências visuais" +msgstr "Preferências da interface" #: bookwyrm/templates/preferences/edit_user.html:14 #: bookwyrm/templates/preferences/edit_user.html:106 @@ -1905,7 +2159,7 @@ msgstr "Mostrar sugestões de usuários:" #: bookwyrm/templates/preferences/edit_user.html:85 #, python-format msgid "Your account will show up in the directory, and may be recommended to other BookWyrm users." -msgstr "Sua conta aparecerá no diretório e pode ser recomendada para outros usuários da BookWyrm." +msgstr "Sua conta aparecerá no diretório e poderá ser recomendada para outros usuários da BookWyrm." #: bookwyrm/templates/preferences/edit_user.html:89 msgid "Preferred Timezone: " @@ -2067,15 +2321,6 @@ msgstr "Data de início" msgid "End date" msgstr "Data final" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "Publicação" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "ativo" @@ -2257,7 +2502,7 @@ msgid "Details" msgstr "Detalhes" #: bookwyrm/templates/settings/federation/instance.html:35 -#: bookwyrm/templates/user/layout.html:63 +#: bookwyrm/templates/user/layout.html:64 msgid "Activity" msgstr "Atividade" @@ -2833,53 +3078,66 @@ msgstr "Criar estante" msgid "Edit Shelf" msgstr "Editar estante" -#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf.py:55 +#: bookwyrm/templates/shelf/shelf.html:28 bookwyrm/views/shelf/shelf.py:53 msgid "All books" msgstr "Todos os livros" -#: bookwyrm/templates/shelf/shelf.html:55 +#: bookwyrm/templates/shelf/shelf.html:69 msgid "Create shelf" msgstr "Criar estante" -#: bookwyrm/templates/shelf/shelf.html:77 +#: bookwyrm/templates/shelf/shelf.html:90 #, python-format msgid "%(formatted_count)s book" msgid_plural "%(formatted_count)s books" msgstr[0] "%(formatted_count)s livro" msgstr[1] "%(formatted_count)s livros" -#: bookwyrm/templates/shelf/shelf.html:84 +#: bookwyrm/templates/shelf/shelf.html:97 #, python-format msgid "(showing %(start)s-%(end)s)" msgstr "(mostrando %(start)s-%(end)s)" -#: bookwyrm/templates/shelf/shelf.html:96 +#: bookwyrm/templates/shelf/shelf.html:109 msgid "Edit shelf" msgstr "Editar estante" -#: bookwyrm/templates/shelf/shelf.html:104 +#: bookwyrm/templates/shelf/shelf.html:117 msgid "Delete shelf" msgstr "Excluir estante" -#: bookwyrm/templates/shelf/shelf.html:132 -#: bookwyrm/templates/shelf/shelf.html:158 +#: bookwyrm/templates/shelf/shelf.html:145 +#: bookwyrm/templates/shelf/shelf.html:171 msgid "Shelved" msgstr "Adicionado" -#: bookwyrm/templates/shelf/shelf.html:133 -#: bookwyrm/templates/shelf/shelf.html:161 +#: bookwyrm/templates/shelf/shelf.html:146 +#: bookwyrm/templates/shelf/shelf.html:174 msgid "Started" msgstr "Iniciado" -#: bookwyrm/templates/shelf/shelf.html:134 -#: bookwyrm/templates/shelf/shelf.html:164 +#: bookwyrm/templates/shelf/shelf.html:147 +#: bookwyrm/templates/shelf/shelf.html:177 msgid "Finished" msgstr "Terminado" -#: bookwyrm/templates/shelf/shelf.html:190 +#: bookwyrm/templates/shelf/shelf.html:203 msgid "This shelf is empty." msgstr "Esta estante está vazia." +#: bookwyrm/templates/snippets/add_to_group_button.html:15 +msgid "Invite" +msgstr "Convidar" + +#: bookwyrm/templates/snippets/add_to_group_button.html:24 +msgid "Uninvite" +msgstr "Desconvidar" + +#: bookwyrm/templates/snippets/add_to_group_button.html:28 +#, python-format +msgid "Remove @%(username)s" +msgstr "Excluir @%(username)s" + #: bookwyrm/templates/snippets/announcement.html:31 #, python-format msgid "Posted by %(username)s" @@ -2911,10 +3169,6 @@ msgstr "Compartilhar" msgid "Un-boost" msgstr "Descompartilhar" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "Resenhar" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "Citar" @@ -2975,6 +3229,7 @@ msgstr "Comentário:" #: bookwyrm/templates/snippets/privacy-icons.html:15 #: bookwyrm/templates/snippets/privacy-icons.html:16 #: bookwyrm/templates/snippets/privacy_select.html:20 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:17 msgid "Private" msgstr "Particular" @@ -3070,6 +3325,7 @@ msgid "Unfollow" msgstr "Deixar de seguir" #: bookwyrm/templates/snippets/follow_request_buttons.html:7 +#: bookwyrm/templates/snippets/join_invitation_buttons.html:8 msgid "Accept" msgstr "Aceitar" @@ -3181,12 +3437,14 @@ msgstr "Próxima" #: bookwyrm/templates/snippets/privacy-icons.html:3 #: bookwyrm/templates/snippets/privacy-icons.html:4 #: bookwyrm/templates/snippets/privacy_select.html:11 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:11 msgid "Public" msgstr "Público" #: bookwyrm/templates/snippets/privacy-icons.html:7 #: bookwyrm/templates/snippets/privacy-icons.html:8 #: bookwyrm/templates/snippets/privacy_select.html:14 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:14 msgid "Unlisted" msgstr "Não listado" @@ -3195,6 +3453,7 @@ msgid "Followers-only" msgstr "Apenas seguidores" #: bookwyrm/templates/snippets/privacy_select.html:6 +#: bookwyrm/templates/snippets/privacy_select_no_followers.html:6 msgid "Post privacy" msgstr "Privacidade da publicação" @@ -3329,14 +3588,14 @@ msgstr "Esconder publicação" #: bookwyrm/templates/snippets/status/header.html:45 #, python-format msgid "edited %(date)s" -msgstr "editado em %(date)s" +msgstr "editado %(date)s" #: bookwyrm/templates/snippets/status/headers/comment.html:2 #, python-format msgid "commented on %(book)s" -msgstr "comentou sobre %(book)s" +msgstr "comentou %(book)s" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "respondeu à publicação de %(username)s" @@ -3388,32 +3647,13 @@ msgstr "Curtir publicação" #: bookwyrm/templates/snippets/status/status.html:10 msgid "boosted" -msgstr "compartilhado" +msgstr "compartilhou" #: bookwyrm/templates/snippets/status/status_options.html:7 #: bookwyrm/templates/snippets/user_options.html:7 msgid "More options" msgstr "Mais opções" -#: bookwyrm/templates/snippets/suggested_users.html:16 -#, python-format -msgid "%(mutuals)s follower you follow" -msgid_plural "%(mutuals)s followers you follow" -msgstr[0] "%(mutuals)s seguidor você segue" -msgstr[1] "%(mutuals)s seguidores você segue" - -#: bookwyrm/templates/snippets/suggested_users.html:23 -#, python-format -msgid "%(shared_books)s book on your shelves" -msgid_plural "%(shared_books)s books on your shelves" -msgstr[0] "%(shared_books)s livro em sua estante" -msgstr[1] "%(shared_books)s livros em suas estantes" - -#: bookwyrm/templates/snippets/suggested_users.html:31 -#: bookwyrm/templates/user/user_preview.html:36 -msgid "Follows you" -msgstr "Segue você" - #: bookwyrm/templates/snippets/switch_edition_button.html:5 msgid "Switch to this edition" msgstr "Trocar para esta edição" @@ -3434,7 +3674,11 @@ msgstr "Mostrar mais" msgid "Show less" msgstr "Mostrar menos" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "Seus livros" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "livros de %(username)s" @@ -3463,18 +3707,35 @@ msgstr "Seus livros de %(year)s" msgid "%(username)s's %(year)s Books" msgstr "Livros de %(username)s de %(year)s" -#: bookwyrm/templates/user/layout.html:18 bookwyrm/templates/user/user.html:10 +#: bookwyrm/templates/user/groups.html:9 +msgid "Your Groups" +msgstr "Seus grupos" + +#: bookwyrm/templates/user/groups.html:11 +#, python-format +msgid "Groups: %(username)s" +msgstr "Grupos: %(username)s" + +#: bookwyrm/templates/user/groups.html:17 +msgid "Create group" +msgstr "Criar grupo" + +#: bookwyrm/templates/user/layout.html:19 bookwyrm/templates/user/user.html:10 msgid "User Profile" msgstr "Perfil do usuário" -#: bookwyrm/templates/user/layout.html:44 +#: bookwyrm/templates/user/layout.html:45 msgid "Follow Requests" msgstr "Solicitações para seguir" -#: bookwyrm/templates/user/layout.html:69 +#: bookwyrm/templates/user/layout.html:70 msgid "Reading Goal" msgstr "Meta de leitura" +#: bookwyrm/templates/user/layout.html:76 +msgid "Groups" +msgstr "Grupos" + #: bookwyrm/templates/user/lists.html:11 #, python-format msgid "Lists: %(username)s" @@ -3527,7 +3788,7 @@ msgstr "Nenhuma atividade ainda!" #: bookwyrm/templates/user/user_preview.html:22 #, python-format msgid "Joined %(date)s" -msgstr "Membro desde %(date)s" +msgstr "Entrou %(date)s" #: bookwyrm/templates/user/user_preview.html:26 #, python-format @@ -3561,19 +3822,19 @@ msgstr "Arquivo excede o tamanho máximo: 10MB" msgid "%(title)s: %(subtitle)s" msgstr "%(title)s: %(subtitle)s" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "Não é um arquivo csv válido" -#: bookwyrm/views/login.py:69 +#: bookwyrm/views/landing/login.py:69 msgid "Username or password are incorrect" msgstr "Nome de usuário ou senha incorretos" -#: bookwyrm/views/password.py:32 +#: bookwyrm/views/landing/password.py:32 msgid "No user with that email address was found." msgstr "Não há nenhum usuário com este e-mail." -#: bookwyrm/views/password.py:41 +#: bookwyrm/views/landing/password.py:43 #, python-brace-format msgid "A password reset link was sent to {email}" msgstr "Um link para redefinição da senha foi enviado para {email}" diff --git a/locale/zh_Hans/LC_MESSAGES/django.mo b/locale/zh_Hans/LC_MESSAGES/django.mo index fbb1986cf8b43b014b8909367d9d5bc656e798f6..5034e6daf6eb4eede9107ade7dc1a3146f0de539 100644 GIT binary patch delta 17515 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 delta 18306 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�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\n" "Language-Team: Chinese Simplified\n" "Language: zh\n" @@ -72,15 +72,16 @@ msgstr "升序" msgid "Descending" msgstr "降序" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "加载书籍时出错" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "找不到匹配的书" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "待处理" @@ -100,23 +101,23 @@ msgstr "仲裁员删除" msgid "Domain block" msgstr "域名屏蔽" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "有声书籍" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "电子书" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "图像小说" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "精装" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "平装" @@ -133,21 +134,21 @@ msgstr "跨站" msgid "Blocked" msgstr "已屏蔽" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s 不是有效的 remote_id" -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s 不是有效的用户名" -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "用户名" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "已经存在使用该用户名的用户。" @@ -757,7 +758,7 @@ msgstr "帮助" #: bookwyrm/templates/compose.html:5 bookwyrm/templates/compose.html:8 msgid "Edit status" -msgstr "设置状态" +msgstr "编辑状态" #: bookwyrm/templates/confirm_email/confirm_email.html:4 msgid "Confirm email" @@ -889,22 +890,37 @@ msgstr "BookWyrm 用户" msgid "All known users" msgstr "所有已知用户" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 +#, python-format +msgid "%(username)s wants to read %(book_title)s" +msgstr "%(username)s 想要阅读 %(book_title)s" + +#: bookwyrm/templates/discover/card-header.html:13 +#, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:23 #, python-format msgid "%(username)s rated %(book_title)s" msgstr "%(username)s%(book_title)s 留下了评分" -#: bookwyrm/templates/discover/card-header.html:13 +#: bookwyrm/templates/discover/card-header.html:27 #, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "%(username)s 已评价 %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" msgstr "%(username)s 评论了 %(book_title)s" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "%(username)s 引用了 %(book_title)s" @@ -1055,9 +1071,8 @@ msgstr "你可以在任何时候从你的个人资料页面 msgid "Updates" msgstr "更新" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" msgstr "你的书目" #: bookwyrm/templates/feed/layout.html:14 @@ -1066,11 +1081,13 @@ msgstr "现在这里还没有任何书目!尝试着从搜索某本书开始吧 #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "想读" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "在读" @@ -1078,6 +1095,7 @@ msgstr "在读" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "读过" @@ -1233,11 +1251,11 @@ msgstr "创建群组" #: bookwyrm/templates/groups/created_text.html:4 #, python-format msgid "Managed by %(username)s" -msgstr "由 %(username)s 创建并策展" +msgstr "由 %(username)s 管理" #: bookwyrm/templates/groups/delete_group_modal.html:4 msgid "Delete this group?" -msgstr "删除该群组" +msgstr "删除该群组?" #: bookwyrm/templates/groups/delete_group_modal.html:7 #: bookwyrm/templates/lists/delete_list_modal.html:7 @@ -1261,11 +1279,11 @@ msgstr "编辑群组" #: bookwyrm/templates/groups/find_users.html:6 msgid "Add new members!" -msgstr "添加新成员" +msgstr "添加新成员!" #: bookwyrm/templates/groups/form.html:8 msgid "Group Name:" -msgstr "群组名称" +msgstr "群组名称:" #: bookwyrm/templates/groups/form.html:12 msgid "Group Description:" @@ -1289,7 +1307,7 @@ msgstr "编辑群组" #: bookwyrm/templates/groups/members.html:8 msgid "Members can add and remove books on a group's book lists" -msgstr "会员可以在群组的书籍列表中添加和删除书" +msgstr "成员可以在群组的书籍列表中添加和删除书籍" #: bookwyrm/templates/groups/members.html:19 msgid "Leave group" @@ -1361,88 +1379,159 @@ msgid "No recent imports" msgstr "无最近的导入" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "导入状态" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" -msgstr "回到导入" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" +msgstr "" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "导入开始:" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "导入完成:" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "任务失败" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "还在导入中。" - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "(按下重新加载来更新!)" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" -msgstr "加载失败" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "跳转至列表底部来选取 %(failed_count)s 个导入失败的项目。" +msgid "Refresh" +msgstr "" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" -msgstr "第 %(index)s 行: %(author)s 所著的 %(title)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" -msgstr "全选" +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" -msgstr "重试项目" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "成功导入了" +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" +msgstr "" -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" -msgstr "导入进度" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "" -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "书目" - -#: bookwyrm/templates/import/import_status.html:122 +#: bookwyrm/templates/import/import_status.html:94 #: bookwyrm/templates/shelf/shelf.html:141 #: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "标题" -#: bookwyrm/templates/import/import_status.html:125 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 #: bookwyrm/templates/shelf/shelf.html:142 #: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "作者" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "书评" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "书目" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "状态" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "已导入" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "批准" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "您可以从 导入/导出页面 下载或导出您的 Goodread 数据。" +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1574,10 +1663,6 @@ msgstr "主导航菜单" msgid "Feed" msgstr "动态" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "你的书目" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "设置" @@ -1677,10 +1762,6 @@ msgstr "都弄好了!" msgid "Suggested by" msgstr "推荐来自" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "批准" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "削除" @@ -1737,7 +1818,7 @@ msgstr "选择群组" #: bookwyrm/templates/lists/form.html:45 msgid "Select a group" -msgstr "选择一个组" +msgstr "选择一个群组" #: bookwyrm/templates/lists/form.html:56 msgid "You don't have any Groups yet!" @@ -1835,7 +1916,7 @@ msgstr "保存的列表" #: bookwyrm/templates/notifications/items/accept.html:16 #, python-format msgid "accepted your invitation to join group \"%(group_name)s\"" -msgstr "接受了您的邀请,加入 \"%(group_name)s\" 群组" +msgstr "接受了您的邀请,加入了 \"%(group_name)s\" 群组" #: bookwyrm/templates/notifications/items/add.html:24 #, python-format @@ -1880,7 +1961,7 @@ msgstr "喜欢了你的 %(book_title)s #: bookwyrm/templates/notifications/items/fav.html:31 #, python-format msgid "liked your quote from %(book_title)s" -msgstr "喜欢了你对 %(book_title)s 的引用" +msgstr "喜欢了你的 %(book_title)s 的引用" #: bookwyrm/templates/notifications/items/fav.html:37 #, python-format @@ -1908,12 +1989,12 @@ msgstr "邀请您加入群组 \"%(group_name)s\"" #: bookwyrm/templates/notifications/items/join.html:16 #, python-format msgid "has joined your group \"%(group_name)s\"" -msgstr "已加入您的群組 \"%(group_name)s\"" +msgstr "已加入您的群组 \"%(group_name)s\"" #: bookwyrm/templates/notifications/items/leave.html:16 #, python-format msgid "has left your group \"%(group_name)s\"" -msgstr "退出了了您的群组 \"%(group_name)s\"" +msgstr "退出了您的群组 \"%(group_name)s\"" #: bookwyrm/templates/notifications/items/mention.html:20 #, python-format @@ -1938,7 +2019,7 @@ msgstr "在 状态 中提到了你" #: bookwyrm/templates/notifications/items/remove.html:17 #, python-format msgid "has been removed from your group \"%(group_name)s\"" -msgstr "已经从您的群组中将 \"%(group_name)s\" 移除" +msgstr "已被从您的 \"%(group_name)s\" 群组中移出" #: bookwyrm/templates/notifications/items/remove.html:23 #, python-format @@ -1973,17 +2054,17 @@ msgstr "有新的 报告 需要仲裁。" #: bookwyrm/templates/notifications/items/update.html:16 #, python-format msgid "has changed the privacy level for %(group_name)s" -msgstr "更改了 %(group_name)s的隐私级别" +msgstr "更改了 %(group_name)s 的隐私级别" #: bookwyrm/templates/notifications/items/update.html:20 #, python-format msgid "has changed the name of %(group_name)s" -msgstr "更改了 %(group_name)s的名称" +msgstr "更改了 %(group_name)s 的名称" #: bookwyrm/templates/notifications/items/update.html:24 #, python-format msgid "has changed the description of %(group_name)s" -msgstr "更改了 %(group_name)s的描述" +msgstr "更改了 %(group_name)s 的描述" #: bookwyrm/templates/notifications/notifications_page.html:18 msgid "Delete notifications" @@ -2233,15 +2314,6 @@ msgstr "开始日期" msgid "End date" msgstr "结束日期" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "状态" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "活跃" @@ -3085,10 +3157,6 @@ msgstr "转发" msgid "Un-boost" msgstr "取消转发" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "书评" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "引用" @@ -3510,7 +3578,7 @@ msgstr "在 %(date)s 已编辑" msgid "commented on %(book)s" msgstr "评论了 %(book)s" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "回复了 %(username)s状态" @@ -3589,7 +3657,11 @@ msgstr "显示更多" msgid "Show less" msgstr "显示更少" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "你的书目" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "%(username)s 的书目" @@ -3731,7 +3803,7 @@ msgstr "文件超过了最大大小: 10MB" msgid "%(title)s: %(subtitle)s" msgstr "%(title)s:%(subtitle)s" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "不是有效的 csv 文件" diff --git a/locale/zh_Hant/LC_MESSAGES/django.mo b/locale/zh_Hant/LC_MESSAGES/django.mo index 1f615988e85b86ceed992fb8f9eeca9fa4068e0d..baefb103cead9162e9927bcfbee60ac4ab807d82 100644 GIT binary patch delta 12711 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 Y<~ir{m(M*AkUM4G$^1v2En4#b0JPuIM*si- delta 13329 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 diff --git a/locale/zh_Hant/LC_MESSAGES/django.po b/locale/zh_Hant/LC_MESSAGES/django.po index cdaa1f81..9b6e27b8 100644 --- a/locale/zh_Hant/LC_MESSAGES/django.po +++ b/locale/zh_Hant/LC_MESSAGES/django.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: bookwyrm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-24 14:09+0000\n" -"PO-Revision-Date: 2021-10-24 18:36\n" +"POT-Creation-Date: 2021-11-14 15:08+0000\n" +"PO-Revision-Date: 2021-11-15 18:02\n" "Last-Translator: Mouse Reeve \n" "Language-Team: Chinese Traditional\n" "Language: zh\n" @@ -72,15 +72,16 @@ msgstr "升序" msgid "Descending" msgstr "降序" -#: bookwyrm/importers/importer.py:75 +#: bookwyrm/importers/importer.py:127 msgid "Error loading book" msgstr "" -#: bookwyrm/importers/importer.py:88 +#: bookwyrm/importers/importer.py:135 msgid "Could not find a match for book" msgstr "" #: bookwyrm/models/base_model.py:17 +#: bookwyrm/templates/import/import_status.html:171 msgid "Pending" msgstr "" @@ -100,23 +101,23 @@ msgstr "" msgid "Domain block" msgstr "" -#: bookwyrm/models/book.py:232 +#: bookwyrm/models/book.py:233 msgid "Audiobook" msgstr "" -#: bookwyrm/models/book.py:233 +#: bookwyrm/models/book.py:234 msgid "eBook" msgstr "" -#: bookwyrm/models/book.py:234 +#: bookwyrm/models/book.py:235 msgid "Graphic novel" msgstr "" -#: bookwyrm/models/book.py:235 +#: bookwyrm/models/book.py:236 msgid "Hardcover" msgstr "" -#: bookwyrm/models/book.py:236 +#: bookwyrm/models/book.py:237 msgid "Paperback" msgstr "" @@ -133,21 +134,21 @@ msgstr "跨站" msgid "Blocked" msgstr "已封鎖" -#: bookwyrm/models/fields.py:27 +#: bookwyrm/models/fields.py:29 #, python-format msgid "%(value)s is not a valid remote_id" msgstr "%(value)s 不是有效的 remote_id" -#: bookwyrm/models/fields.py:36 bookwyrm/models/fields.py:45 +#: bookwyrm/models/fields.py:38 bookwyrm/models/fields.py:47 #, python-format msgid "%(value)s is not a valid username" msgstr "%(value)s 不是有效的使用者名稱" -#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171 +#: bookwyrm/models/fields.py:183 bookwyrm/templates/layout.html:171 msgid "username" msgstr "使用者名稱" -#: bookwyrm/models/fields.py:186 +#: bookwyrm/models/fields.py:188 msgid "A user with that username already exists." msgstr "已經存在使用該名稱的使用者。" @@ -889,22 +890,37 @@ msgstr "BookWyrm 使用者" msgid "All known users" msgstr "所有已知使用者" -#: bookwyrm/templates/discover/card-header.html:9 +#: bookwyrm/templates/discover/card-header.html:8 #, python-format -msgid "%(username)s rated %(book_title)s" +msgid "%(username)s wants to read %(book_title)s" msgstr "" #: bookwyrm/templates/discover/card-header.html:13 #, python-format +msgid "%(username)s finished reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:18 +#, python-format +msgid "%(username)s started reading %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:23 +#, python-format +msgid "%(username)s rated %(book_title)s" +msgstr "" + +#: bookwyrm/templates/discover/card-header.html:27 +#, python-format msgid "%(username)s reviewed %(book_title)s" msgstr "" -#: bookwyrm/templates/discover/card-header.html:17 +#: bookwyrm/templates/discover/card-header.html:31 #, python-format msgid "%(username)s commented on %(book_title)s" msgstr "" -#: bookwyrm/templates/discover/card-header.html:21 +#: bookwyrm/templates/discover/card-header.html:35 #, python-format msgid "%(username)s quoted %(book_title)s" msgstr "" @@ -1055,9 +1071,8 @@ msgstr "你可以在任何時候從你的使用者資料頁 msgid "Updates" msgstr "更新" -#: bookwyrm/templates/feed/layout.html:12 -#: bookwyrm/templates/user/books_header.html:3 -msgid "Your books" +#: bookwyrm/templates/feed/layout.html:12 bookwyrm/templates/layout.html:106 +msgid "Your Books" msgstr "你的書目" #: bookwyrm/templates/feed/layout.html:14 @@ -1066,11 +1081,13 @@ msgstr "現在這裡還沒有任何書目!嘗試著從搜尋某本書開始吧 #: bookwyrm/templates/feed/layout.html:25 #: bookwyrm/templates/shelf/shelf.html:38 +#: bookwyrm/templates/user/books_header.html:4 msgid "To Read" msgstr "想讀" #: bookwyrm/templates/feed/layout.html:26 #: bookwyrm/templates/shelf/shelf.html:40 +#: bookwyrm/templates/user/books_header.html:6 msgid "Currently Reading" msgstr "在讀" @@ -1078,6 +1095,7 @@ msgstr "在讀" #: bookwyrm/templates/shelf/shelf.html:42 #: bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html:23 #: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:12 +#: bookwyrm/templates/user/books_header.html:8 msgid "Read" msgstr "讀過" @@ -1361,88 +1379,159 @@ msgid "No recent imports" msgstr "無最近的匯入" #: bookwyrm/templates/import/import_status.html:6 -#: bookwyrm/templates/import/import_status.html:10 +#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:29 msgid "Import Status" msgstr "匯入狀態" -#: bookwyrm/templates/import/import_status.html:11 -msgid "Back to imports" +#: bookwyrm/templates/import/import_status.html:13 +#: bookwyrm/templates/import/import_status.html:27 +msgid "Retry Status" msgstr "" -#: bookwyrm/templates/import/import_status.html:15 +#: bookwyrm/templates/import/import_status.html:22 +msgid "Imports" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:39 msgid "Import started:" msgstr "匯入開始:" -#: bookwyrm/templates/import/import_status.html:20 -msgid "Import completed:" -msgstr "匯入完成:" - -#: bookwyrm/templates/import/import_status.html:24 -msgid "TASK FAILED" -msgstr "任務失敗" - -#: bookwyrm/templates/import/import_status.html:32 -msgid "Import still in progress." -msgstr "還在匯入中。" - -#: bookwyrm/templates/import/import_status.html:34 -msgid "(Hit reload to update!)" -msgstr "(按下重新載入來更新!)" - -#: bookwyrm/templates/import/import_status.html:41 -msgid "Failed to load" -msgstr "載入失敗" +#: bookwyrm/templates/import/import_status.html:48 +msgid "In progress" +msgstr "" #: bookwyrm/templates/import/import_status.html:50 -#, python-format -msgid "Jump to the bottom of the list to select the %(failed_count)s items which failed to import." -msgstr "跳轉至列表底部來選取 %(failed_count)s 個匯入失敗的項目。" +msgid "Refresh" +msgstr "" #: bookwyrm/templates/import/import_status.html:62 #, python-format -msgid "Line %(index)s: %(title)s by %(author)s" +msgid "%(display_counter)s item needs manual approval." +msgid_plural "%(display_counter)s items need manual approval." +msgstr[0] "" + +#: bookwyrm/templates/import/import_status.html:67 +#: bookwyrm/templates/import/manual_review.html:8 +msgid "Review items" msgstr "" -#: bookwyrm/templates/import/import_status.html:82 -msgid "Select all" -msgstr "全選" +#: bookwyrm/templates/import/import_status.html:73 +#, python-format +msgid "%(display_counter)s item failed to import." +msgid_plural "%(display_counter)s items failed to import." +msgstr[0] "" -#: bookwyrm/templates/import/import_status.html:85 -msgid "Retry items" -msgstr "重試項目" - -#: bookwyrm/templates/import/import_status.html:112 -msgid "Successfully imported" -msgstr "成功匯入了" - -#: bookwyrm/templates/import/import_status.html:114 -msgid "Import Progress" +#: bookwyrm/templates/import/import_status.html:79 +msgid "View and troubleshoot failed items" msgstr "" -#: bookwyrm/templates/import/import_status.html:119 -msgid "Book" -msgstr "書目" +#: bookwyrm/templates/import/import_status.html:91 +msgid "Row" +msgstr "" -#: bookwyrm/templates/import/import_status.html:122 +#: bookwyrm/templates/import/import_status.html:94 #: bookwyrm/templates/shelf/shelf.html:141 #: bookwyrm/templates/shelf/shelf.html:163 msgid "Title" msgstr "標題" -#: bookwyrm/templates/import/import_status.html:125 +#: bookwyrm/templates/import/import_status.html:97 +msgid "ISBN" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:100 #: bookwyrm/templates/shelf/shelf.html:142 #: bookwyrm/templates/shelf/shelf.html:166 msgid "Author" msgstr "作者" -#: bookwyrm/templates/import/import_status.html:148 +#: bookwyrm/templates/import/import_status.html:103 +msgid "Shelf" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:106 +#: bookwyrm/templates/import/manual_review.html:13 +#: bookwyrm/templates/snippets/create_status.html:17 +msgid "Review" +msgstr "書評" + +#: bookwyrm/templates/import/import_status.html:110 +msgid "Book" +msgstr "書目" + +#: bookwyrm/templates/import/import_status.html:113 +#: bookwyrm/templates/settings/announcements/announcements.html:38 +#: bookwyrm/templates/settings/federation/instance_list.html:46 +#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 +#: bookwyrm/templates/settings/invites/status_filter.html:5 +#: bookwyrm/templates/settings/users/user_admin.html:34 +#: bookwyrm/templates/settings/users/user_info.html:20 +msgid "Status" +msgstr "狀態" + +#: bookwyrm/templates/import/import_status.html:144 +msgid "View imported review" +msgstr "" + +#: bookwyrm/templates/import/import_status.html:158 msgid "Imported" msgstr "已匯入" +#: bookwyrm/templates/import/import_status.html:164 +msgid "Needs manual review" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:5 +#: bookwyrm/templates/import/troubleshoot.html:4 +msgid "Import Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:21 +msgid "Approving a suggestion will permanently add the suggested book to your shelves and associate your reading dates, reviews, and ratings with that book." +msgstr "" + +#: bookwyrm/templates/import/manual_review.html:56 +#: bookwyrm/templates/lists/curate.html:57 +msgid "Approve" +msgstr "批准" + +#: bookwyrm/templates/import/manual_review.html:64 +msgid "Reject" +msgstr "" + #: bookwyrm/templates/import/tooltip.html:6 msgid "You can download your Goodreads data from the Import/Export page of your Goodreads account." msgstr "" +#: bookwyrm/templates/import/troubleshoot.html:7 +msgid "Failed items" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:12 +msgid "Troubleshooting" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:20 +msgid "Re-trying an import can fix missing items in cases such as:" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:23 +msgid "The book has been added to the instance since this import" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:24 +msgid "A transient error or timeout caused the external data source to be unavailable." +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:25 +msgid "BookWyrm has been updated since this import with a bug fix" +msgstr "" + +#: bookwyrm/templates/import/troubleshoot.html:28 +msgid "Contact your admin or open an issue if you are seeing unexpected failed items." +msgstr "" + #: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230 #, python-format msgid "About %(site_name)s" @@ -1574,10 +1663,6 @@ msgstr "主導航選單" msgid "Feed" msgstr "動態" -#: bookwyrm/templates/layout.html:106 -msgid "Your Books" -msgstr "你的書目" - #: bookwyrm/templates/layout.html:116 msgid "Settings" msgstr "設定" @@ -1677,10 +1762,6 @@ msgstr "都弄好了!" msgid "Suggested by" msgstr "推薦來自" -#: bookwyrm/templates/lists/curate.html:57 -msgid "Approve" -msgstr "批准" - #: bookwyrm/templates/lists/curate.html:63 msgid "Discard" msgstr "放棄" @@ -2233,15 +2314,6 @@ msgstr "開始日期" msgid "End date" msgstr "結束日期" -#: bookwyrm/templates/settings/announcements/announcements.html:38 -#: bookwyrm/templates/settings/federation/instance_list.html:46 -#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44 -#: bookwyrm/templates/settings/invites/status_filter.html:5 -#: bookwyrm/templates/settings/users/user_admin.html:34 -#: bookwyrm/templates/settings/users/user_info.html:20 -msgid "Status" -msgstr "狀態" - #: bookwyrm/templates/settings/announcements/announcements.html:48 msgid "active" msgstr "啟用" @@ -3085,10 +3157,6 @@ msgstr "轉發" msgid "Un-boost" msgstr "取消轉發" -#: bookwyrm/templates/snippets/create_status.html:17 -msgid "Review" -msgstr "書評" - #: bookwyrm/templates/snippets/create_status.html:39 msgid "Quote" msgstr "引用" @@ -3510,7 +3578,7 @@ msgstr "" msgid "commented on %(book)s" msgstr "" -#: bookwyrm/templates/snippets/status/headers/note.html:15 +#: bookwyrm/templates/snippets/status/headers/note.html:8 #, python-format msgid "replied to %(username)s's status" msgstr "回覆了 %(username)s狀態" @@ -3589,7 +3657,11 @@ msgstr "顯示更多" msgid "Show less" msgstr "顯示更少" -#: bookwyrm/templates/user/books_header.html:5 +#: bookwyrm/templates/user/books_header.html:10 +msgid "Your books" +msgstr "你的書目" + +#: bookwyrm/templates/user/books_header.html:15 #, python-format msgid "%(username)s's books" msgstr "%(username)s 的書目" @@ -3731,7 +3803,7 @@ msgstr "檔案超過了最大大小: 10MB" msgid "%(title)s: %(subtitle)s" msgstr "" -#: bookwyrm/views/import_data.py:67 +#: bookwyrm/views/imports/import_data.py:64 msgid "Not a valid csv file" msgstr "不是有效的 csv 檔案" From a26302683f335d9f6f76fed9c1f1d79b4a793e85 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 16 Nov 2021 09:31:57 -0800 Subject: [PATCH 121/121] List command in echo --- bw-dev | 1 + 1 file changed, 1 insertion(+) diff --git a/bw-dev b/bw-dev index f50800cf..5da97170 100755 --- a/bw-dev +++ b/bw-dev @@ -173,6 +173,7 @@ case "$CMD" in echo " collectstatic" echo " makemessages" echo " compilemessages [locale]" + echo " update_locales" echo " build" echo " clean" echo " black"