Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2021-11-17 10:44:19 -08:00
commit db5ec248ef
15 changed files with 4102 additions and 73 deletions

View file

@ -67,7 +67,7 @@ class Connector(AbstractConnector):
extracted = list(data.get("entities").values())
try:
data = extracted[0]
except KeyError:
except (KeyError, IndexError):
raise ConnectorException("Invalid book data")
# flatten the data so that images, uri, and claims are on the same level
return {
@ -128,6 +128,7 @@ class Connector(AbstractConnector):
def load_edition_data(self, work_uri):
"""get a list of editions for a work"""
# pylint: disable=line-too-long
url = f"{self.books_url}?action=reverse-claims&property=wdt:P629&value={work_uri}&sort=true"
return get_data(url)

View file

@ -0,0 +1,33 @@
# Generated by Django 3.2.5 on 2021-11-17 18:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0117_alter_user_preferred_language"),
]
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)"),
("gl-es", "Galego (Galician)"),
("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,
),
),
]

View file

@ -165,8 +165,9 @@ LANGUAGES = [
("en-us", _("English")),
("de-de", _("Deutsch (German)")),
("es-es", _("Español (Spanish)")),
("gl-es", _("Galego (Galician)")),
("fr-fr", _("Français (French)")),
("lt-lt", _("lietuvių (Lithuanian)")),
("lt-lt", _("Lietuvių (Lithuanian)")),
("pt-br", _("Português - Brasil (Brazilian Portuguese)")),
("zh-hans", _("简体中文 (Simplified Chinese)")),
("zh-hant", _("繁體中文 (Traditional Chinese)")),

View file

@ -1,11 +1,14 @@
""" testing book data connectors """
import json
import pathlib
from unittest.mock import patch
from django.test import TestCase
import responses
from bookwyrm import models
from bookwyrm.connectors.inventaire import Connector, get_language_code
from bookwyrm.connectors.connector_manager import ConnectorException
class Inventaire(TestCase):
@ -48,6 +51,44 @@ class Inventaire(TestCase):
self.assertEqual(result["wdt:P31"], ["wd:Q3331189"])
self.assertEqual(result["uri"], "isbn:9780375757853")
@responses.activate
def test_get_book_data_invalid(self):
"""error if there isn't any entity data"""
responses.add(
responses.GET,
"https://test.url/ok",
json={
"entities": {},
"redirects": {},
},
)
with self.assertRaises(ConnectorException):
self.connector.get_book_data("https://test.url/ok")
@responses.activate
def test_search(self):
"""min confidence filtering"""
responses.add(
responses.GET,
"https://inventaire.io/search?q=hi",
json={
"results": [
{
"_score": 200,
"label": "hello",
},
{
"_score": 100,
"label": "hi",
},
],
},
)
results = self.connector.search("hi", min_confidence=0.5)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].title, "hello")
def test_format_search_result(self):
"""json to search result objs"""
search_file = pathlib.Path(__file__).parent.joinpath(
@ -157,6 +198,88 @@ class Inventaire(TestCase):
"https://covers.inventaire.io/img/entities/12345",
)
def test_isbn_search_empty(self):
"""another search type"""
search_results = {}
results = self.connector.parse_isbn_search_data(search_results)
self.assertEqual(results, [])
def test_isbn_search_no_title(self):
"""another search type"""
search_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_isbn_search.json"
)
search_results = json.loads(search_file.read_bytes())
search_results["entities"]["isbn:9782290349229"]["claims"]["wdt:P1476"] = None
result = self.connector.format_isbn_search_result(
search_results.get("entities")
)
self.assertIsNone(result)
def test_is_work_data(self):
"""is it a work"""
work_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_work.json"
)
work_data = json.loads(work_file.read_bytes())
with patch("bookwyrm.connectors.inventaire.get_data") as get_data_mock:
get_data_mock.return_value = work_data
formatted = self.connector.get_book_data("hi")
self.assertTrue(self.connector.is_work_data(formatted))
edition_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_edition.json"
)
edition_data = json.loads(edition_file.read_bytes())
with patch("bookwyrm.connectors.inventaire.get_data") as get_data_mock:
get_data_mock.return_value = edition_data
formatted = self.connector.get_book_data("hi")
self.assertFalse(self.connector.is_work_data(formatted))
@responses.activate
def test_get_edition_from_work_data(self):
"""load edition"""
responses.add(
responses.GET,
"https://inventaire.io/?action=by-uris&uris=hello",
json={"entities": {}},
)
data = {"uri": "blah"}
with patch(
"bookwyrm.connectors.inventaire.Connector.load_edition_data"
) as loader_mock, patch(
"bookwyrm.connectors.inventaire.Connector.get_book_data"
) as getter_mock:
loader_mock.return_value = {"uris": ["hello"]}
self.connector.get_edition_from_work_data(data)
self.assertTrue(getter_mock.called)
with patch(
"bookwyrm.connectors.inventaire.Connector.load_edition_data"
) as loader_mock:
loader_mock.return_value = {"uris": []}
with self.assertRaises(ConnectorException):
self.connector.get_edition_from_work_data(data)
@responses.activate
def test_get_work_from_edition_data(self):
"""load work"""
responses.add(
responses.GET,
"https://inventaire.io/?action=by-uris&uris=hello",
)
data = {"wdt:P629": ["hello"]}
with patch("bookwyrm.connectors.inventaire.Connector.get_book_data") as mock:
self.connector.get_work_from_edition_data(data)
self.assertEqual(mock.call_count, 1)
args = mock.call_args[0]
self.assertEqual(args[0], "https://inventaire.io?action=by-uris&uris=hello")
data = {"wdt:P629": [None]}
with self.assertRaises(ConnectorException):
self.connector.get_work_from_edition_data(data)
def test_get_language_code(self):
"""get english or whatever is in reach"""
options = {

View file

@ -108,7 +108,8 @@ def instance_info(_):
"thumbnail": logo,
"languages": ["en"],
"registrations": site.allow_registration,
"approval_required": site.allow_registration and site.allow_invite_requests,
"approval_required": not site.allow_registration
and site.allow_invite_requests,
"email": site.admin_email,
}
)

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-14 15:08+0000\n"
"POT-Creation-Date: 2021-11-17 18:03+0000\n"
"PO-Revision-Date: 2021-02-28 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n"
@ -18,71 +18,71 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: bookwyrm/forms.py:242
#: bookwyrm/forms.py:248
msgid "A user with this email already exists."
msgstr ""
#: bookwyrm/forms.py:256
#: bookwyrm/forms.py:262
msgid "One Day"
msgstr ""
#: bookwyrm/forms.py:257
#: bookwyrm/forms.py:263
msgid "One Week"
msgstr ""
#: bookwyrm/forms.py:258
#: bookwyrm/forms.py:264
msgid "One Month"
msgstr ""
#: bookwyrm/forms.py:259
#: bookwyrm/forms.py:265
msgid "Does Not Expire"
msgstr ""
#: bookwyrm/forms.py:263
#: bookwyrm/forms.py:269
#, python-brace-format
msgid "{i} uses"
msgstr ""
#: bookwyrm/forms.py:264
#: bookwyrm/forms.py:270
msgid "Unlimited"
msgstr ""
#: bookwyrm/forms.py:332
#: bookwyrm/forms.py:338
msgid "List Order"
msgstr ""
#: bookwyrm/forms.py:333
#: bookwyrm/forms.py:339
msgid "Book Title"
msgstr ""
#: bookwyrm/forms.py:334 bookwyrm/templates/shelf/shelf.html:149
#: bookwyrm/forms.py:340 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:336 bookwyrm/templates/lists/list.html:110
#: bookwyrm/forms.py:342 bookwyrm/templates/lists/list.html:110
msgid "Sort By"
msgstr ""
#: bookwyrm/forms.py:340
#: bookwyrm/forms.py:346
msgid "Ascending"
msgstr ""
#: bookwyrm/forms.py:341
#: bookwyrm/forms.py:347
msgid "Descending"
msgstr ""
#: bookwyrm/importers/importer.py:127
#: bookwyrm/importers/importer.py:141 bookwyrm/importers/importer.py:163
msgid "Error loading book"
msgstr ""
#: bookwyrm/importers/importer.py:135
#: bookwyrm/importers/importer.py:150
msgid "Could not find a match for book"
msgstr ""
#: bookwyrm/models/base_model.py:17
#: bookwyrm/templates/import/import_status.html:171
#: bookwyrm/templates/import/import_status.html:190
msgid "Pending"
msgstr ""
@ -184,18 +184,26 @@ msgid "Español (Spanish)"
msgstr ""
#: bookwyrm/settings.py:168
msgid "Français (French)"
msgid "Galego (Galician)"
msgstr ""
#: bookwyrm/settings.py:169
msgid "Português - Brasil (Brazilian Portuguese)"
msgid "Français (French)"
msgstr ""
#: bookwyrm/settings.py:170
msgid "简体中文 (Simplified Chinese)"
msgid "Lietuvių (Lithuanian)"
msgstr ""
#: bookwyrm/settings.py:171
msgid "Português - Brasil (Brazilian Portuguese)"
msgstr ""
#: bookwyrm/settings.py:172
msgid "简体中文 (Simplified Chinese)"
msgstr ""
#: bookwyrm/settings.py:173
msgid "繁體中文 (Traditional Chinese)"
msgstr ""
@ -1411,64 +1419,64 @@ msgstr ""
msgid "Refresh"
msgstr ""
#: bookwyrm/templates/import/import_status.html:62
#: bookwyrm/templates/import/import_status.html:71
#, python-format
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/import_status.html:76
#: bookwyrm/templates/import/manual_review.html:8
msgid "Review items"
msgstr ""
#: bookwyrm/templates/import/import_status.html:73
#: bookwyrm/templates/import/import_status.html:82
#, 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
#: bookwyrm/templates/import/import_status.html:88
msgid "View and troubleshoot failed items"
msgstr ""
#: bookwyrm/templates/import/import_status.html:91
#: bookwyrm/templates/import/import_status.html:100
msgid "Row"
msgstr ""
#: bookwyrm/templates/import/import_status.html:94
#: bookwyrm/templates/import/import_status.html:103
#: bookwyrm/templates/shelf/shelf.html:141
#: bookwyrm/templates/shelf/shelf.html:163
msgid "Title"
msgstr ""
#: bookwyrm/templates/import/import_status.html:97
#: bookwyrm/templates/import/import_status.html:106
msgid "ISBN"
msgstr ""
#: bookwyrm/templates/import/import_status.html:100
#: bookwyrm/templates/import/import_status.html:109
#: bookwyrm/templates/shelf/shelf.html:142
#: bookwyrm/templates/shelf/shelf.html:166
msgid "Author"
msgstr ""
#: bookwyrm/templates/import/import_status.html:103
#: bookwyrm/templates/import/import_status.html:112
msgid "Shelf"
msgstr ""
#: bookwyrm/templates/import/import_status.html:106
#: bookwyrm/templates/import/import_status.html:115
#: bookwyrm/templates/import/manual_review.html:13
#: bookwyrm/templates/snippets/create_status.html:17
msgid "Review"
msgstr ""
#: bookwyrm/templates/import/import_status.html:110
#: bookwyrm/templates/import/import_status.html:119
msgid "Book"
msgstr ""
#: bookwyrm/templates/import/import_status.html:113
#: bookwyrm/templates/import/import_status.html:122
#: bookwyrm/templates/settings/announcements/announcements.html:38
#: bookwyrm/templates/settings/federation/instance_list.html:46
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:44
@ -1478,18 +1486,34 @@ msgstr ""
msgid "Status"
msgstr ""
#: bookwyrm/templates/import/import_status.html:144
#: bookwyrm/templates/import/import_status.html:130
msgid "Import preview unavailable."
msgstr ""
#: bookwyrm/templates/import/import_status.html:162
msgid "View imported review"
msgstr ""
#: bookwyrm/templates/import/import_status.html:158
#: bookwyrm/templates/import/import_status.html:176
msgid "Imported"
msgstr ""
#: bookwyrm/templates/import/import_status.html:164
#: bookwyrm/templates/import/import_status.html:182
msgid "Needs manual review"
msgstr ""
#: bookwyrm/templates/import/import_status.html:195
msgid "Retry"
msgstr ""
#: bookwyrm/templates/import/import_status.html:213
msgid "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."
msgstr ""
#: bookwyrm/templates/import/import_status.html:215
msgid "Update import"
msgstr ""
#: bookwyrm/templates/import/manual_review.html:5
#: bookwyrm/templates/import/troubleshoot.html:4
msgid "Import Troubleshooting"
@ -1499,12 +1523,12 @@ msgstr ""
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/import/manual_review.html:58
#: bookwyrm/templates/lists/curate.html:57
msgid "Approve"
msgstr ""
#: bookwyrm/templates/import/manual_review.html:64
#: bookwyrm/templates/import/manual_review.html:66
msgid "Reject"
msgstr ""

Binary file not shown.

View file

@ -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 22:27\n"
"PO-Revision-Date: 2021-11-16 23:06\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: French\n"
"Language: fr\n"
@ -1392,11 +1392,11 @@ msgstr "Statut de limportation"
#: bookwyrm/templates/import/import_status.html:13
#: bookwyrm/templates/import/import_status.html:27
msgid "Retry Status"
msgstr ""
msgstr "Statut de la nouvelle tentative"
#: bookwyrm/templates/import/import_status.html:22
msgid "Imports"
msgstr ""
msgstr "Importations"
#: bookwyrm/templates/import/import_status.html:39
msgid "Import started:"
@ -1404,38 +1404,38 @@ msgstr "Début de limportation:"
#: bookwyrm/templates/import/import_status.html:48
msgid "In progress"
msgstr ""
msgstr "En cours de traitement"
#: bookwyrm/templates/import/import_status.html:50
msgid "Refresh"
msgstr ""
msgstr "Actualiser"
#: 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[0] "%(display_counter)s élément a besoin d'être approuvé manuellement."
msgstr[1] "%(display_counter)s éléments ont besoin d'être approuvés manuellement."
#: bookwyrm/templates/import/import_status.html:67
#: bookwyrm/templates/import/manual_review.html:8
msgid "Review items"
msgstr ""
msgstr "Vérifier les é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] ""
msgstr[0] "%(display_counter)s élément n'a pas pu être importé."
msgstr[1] "%(display_counter)s éléments n'ont pas pu être importés."
#: bookwyrm/templates/import/import_status.html:79
msgid "View and troubleshoot failed items"
msgstr ""
msgstr "Afficher et corriger les importations ayant échoué"
#: bookwyrm/templates/import/import_status.html:91
msgid "Row"
msgstr ""
msgstr "Ligne"
#: bookwyrm/templates/import/import_status.html:94
#: bookwyrm/templates/shelf/shelf.html:141
@ -1445,7 +1445,7 @@ msgstr "Titre"
#: bookwyrm/templates/import/import_status.html:97
msgid "ISBN"
msgstr ""
msgstr "ISBN"
#: bookwyrm/templates/import/import_status.html:100
#: bookwyrm/templates/shelf/shelf.html:142
@ -1455,7 +1455,7 @@ msgstr "Auteur/autrice"
#: bookwyrm/templates/import/import_status.html:103
msgid "Shelf"
msgstr ""
msgstr "Étagère"
#: bookwyrm/templates/import/import_status.html:106
#: bookwyrm/templates/import/manual_review.html:13
@ -1479,7 +1479,7 @@ msgstr "Statut"
#: bookwyrm/templates/import/import_status.html:144
msgid "View imported review"
msgstr ""
msgstr "Afficher la critique importée"
#: bookwyrm/templates/import/import_status.html:158
msgid "Imported"
@ -1487,16 +1487,16 @@ msgstr "Importé"
#: bookwyrm/templates/import/import_status.html:164
msgid "Needs manual review"
msgstr ""
msgstr "Nécessite une vérification manuelle"
#: bookwyrm/templates/import/manual_review.html:5
#: bookwyrm/templates/import/troubleshoot.html:4
msgid "Import Troubleshooting"
msgstr ""
msgstr "Résolution des problèmes d'importation"
#: 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 ""
msgstr "Approuver une suggestion ajoutera définitivement le livre suggéré à vos étagères et associera vos dates, critiques et notes de lecture à ce livre."
#: bookwyrm/templates/import/manual_review.html:56
#: bookwyrm/templates/lists/curate.html:57
@ -1505,7 +1505,7 @@ msgstr "Approuver"
#: bookwyrm/templates/import/manual_review.html:64
msgid "Reject"
msgstr ""
msgstr "Rejeter"
#: bookwyrm/templates/import/tooltip.html:6
msgid "You can download your Goodreads data from the <a href=\"https://www.goodreads.com/review/import\" target=\"_blank\" rel=\"noopener\">Import/Export page</a> of your Goodreads account."
@ -1513,31 +1513,31 @@ msgstr "Vous pouvez télécharger vos données GoodReads depuis la page <a href=
#: bookwyrm/templates/import/troubleshoot.html:7
msgid "Failed items"
msgstr ""
msgstr "Éléments dont l'importation a échoué"
#: bookwyrm/templates/import/troubleshoot.html:12
msgid "Troubleshooting"
msgstr ""
msgstr "Résolution des problèmes"
#: bookwyrm/templates/import/troubleshoot.html:20
msgid "Re-trying an import can fix missing items in cases such as:"
msgstr ""
msgstr "Une nouvelle tentative d'importation peut corriger les éléments manquants dans les cas suivants :"
#: bookwyrm/templates/import/troubleshoot.html:23
msgid "The book has been added to the instance since this import"
msgstr ""
msgstr "Le livre a été ajouté à l'instance depuis cette importation"
#: bookwyrm/templates/import/troubleshoot.html:24
msgid "A transient error or timeout caused the external data source to be unavailable."
msgstr ""
msgstr "Une erreur momentanée ou un timeout a rendu la source de données externe indisponible."
#: bookwyrm/templates/import/troubleshoot.html:25
msgid "BookWyrm has been updated since this import with a bug fix"
msgstr ""
msgstr "BookWyrm a été mis à jour depuis cette importation avec une correction de bug"
#: bookwyrm/templates/import/troubleshoot.html:28
msgid "Contact your admin or <a href='https://github.com/bookwyrm-social/bookwyrm/issues'>open an issue</a> if you are seeing unexpected failed items."
msgstr ""
msgstr "Contactez votre administrateur·ice ou <a href='https://github.com/bookwyrm-social/bookwyrm/issues'>signalez un problème</a> si vous voyez des éléments inattendus qui ont échoué."
#: bookwyrm/templates/landing/about.html:7 bookwyrm/templates/layout.html:230
#, python-format

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -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 19:25\n"
"PO-Revision-Date: 2021-11-16 17:33\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: Lithuanian\n"
"Language: lt\n"
@ -902,17 +902,17 @@ msgstr "Visi žinomi nariai"
#: bookwyrm/templates/discover/card-header.html:8
#, python-format
msgid "<a href=\"%(user_path)s\">%(username)s</a> wants to read <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr ""
msgstr "<a href=\"%(user_path)s\">%(username)s</a> nori perskaityti <a href=\"%(book_path)s\">%(book_title)s</a>"
#: bookwyrm/templates/discover/card-header.html:13
#, python-format
msgid "<a href=\"%(user_path)s\">%(username)s</a> finished reading <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr ""
msgstr "<a href=\"%(user_path)s\">%(username)s</a> perskaitė <a href=\"%(book_path)s\">%(book_title)s</a>"
#: bookwyrm/templates/discover/card-header.html:18
#, python-format
msgid "<a href=\"%(user_path)s\">%(username)s</a> started reading <a href=\"%(book_path)s\">%(book_title)s</a>"
msgstr ""
msgstr "<a href=\"%(user_path)s\">%(username)s</a> pradėjo skaityti <a href=\"%(book_path)s\">%(book_title)s</a>"
#: bookwyrm/templates/discover/card-header.html:23
#, python-format
@ -1449,7 +1449,7 @@ msgstr ""
#: bookwyrm/templates/import/import_status.html:91
msgid "Row"
msgstr ""
msgstr "Eilutė"
#: bookwyrm/templates/import/import_status.html:94
#: bookwyrm/templates/shelf/shelf.html:141
@ -1459,7 +1459,7 @@ msgstr "Pavadinimas"
#: bookwyrm/templates/import/import_status.html:97
msgid "ISBN"
msgstr ""
msgstr "ISBN"
#: bookwyrm/templates/import/import_status.html:100
#: bookwyrm/templates/shelf/shelf.html:142
@ -1519,7 +1519,7 @@ msgstr "Patvirtinti"
#: bookwyrm/templates/import/manual_review.html:64
msgid "Reject"
msgstr ""
msgstr "Atmesti"
#: bookwyrm/templates/import/tooltip.html:6
msgid "You can download your Goodreads data from the <a href=\"https://www.goodreads.com/review/import\" target=\"_blank\" rel=\"noopener\">Import/Export page</a> of your Goodreads account."

Binary file not shown.

Binary file not shown.