feat: canonicalise author names
Extractors wrote whatever the file said, so one person held several rows: "Sam Newman" beside "Newman, Sam;" beside "Sam Newman.epub", the last because the upload path never stripped the file extension. Tidy on write, in the validator and in the uniqueness lookup alike, and merge the rows that collide.
This commit is contained in:
@@ -895,6 +895,72 @@ class TestDuplicateBooks:
|
||||
assert "title-author" in possible.candidates[0].matched_on
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestAuthorNames:
|
||||
"""One person is one row, however the file happened to spell them."""
|
||||
|
||||
async def test_a_variant_spelling_reuses_the_existing_author(
|
||||
self, books_service: BookService, test_library: m.Library
|
||||
) -> None:
|
||||
"""
|
||||
`as_unique_async` looks a name up before inserting it, so the lookup and the
|
||||
insert have to tidy identically. If they disagree, every variant misses the
|
||||
existing row and then collides with it on the unique index.
|
||||
"""
|
||||
first = await store_book(
|
||||
books_service, test_library, title="Building Microservices", authors=["Sam Newman"]
|
||||
)
|
||||
second = await store_book(
|
||||
books_service,
|
||||
test_library,
|
||||
title="Monolith to Microservices",
|
||||
authors=["Newman, Sam;"],
|
||||
)
|
||||
|
||||
assert [author.name for author in first.authors] == ["Sam Newman"]
|
||||
assert [author.name for author in second.authors] == ["Sam Newman"]
|
||||
assert first.authors[0].id == second.authors[0].id
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("written", "stored"),
|
||||
[
|
||||
("Newman, Sam;", "Sam Newman"),
|
||||
("Sam Newman.epub", "Sam Newman"),
|
||||
(" Sam Newman ", "Sam Newman"),
|
||||
# Two people in one string is left exactly as it was found.
|
||||
("Dave Thomas, Andy Hunt", "Dave Thomas, Andy Hunt"),
|
||||
],
|
||||
)
|
||||
async def test_the_stored_name_is_the_tidy_one(
|
||||
self,
|
||||
books_service: BookService,
|
||||
test_library: m.Library,
|
||||
written: str,
|
||||
stored: str,
|
||||
) -> None:
|
||||
book = await store_book(
|
||||
books_service, test_library, title="Some Book", authors=[written]
|
||||
)
|
||||
|
||||
assert [author.name for author in book.authors] == [stored]
|
||||
|
||||
async def test_an_upload_does_not_keep_the_file_extension(
|
||||
self, books_service: BookService, test_library: m.Library
|
||||
) -> None:
|
||||
"""
|
||||
A filename is the fallback when the file declares no author of its own, and
|
||||
"Franz Kafka.epub" is not a person.
|
||||
"""
|
||||
result = await books_service.create_many_from_files(
|
||||
BooksCreateFromFiles(files=[upload(EPUB, "Some Unknown Book - Ada Lovelace.epub")]),
|
||||
test_library,
|
||||
)
|
||||
|
||||
book = await books_service.get(result.books[0].id)
|
||||
for author in book.authors:
|
||||
assert not author.name.endswith((".epub", ".pdf", ".mobi"))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestDuplicateBookGroups:
|
||||
"""The pass over a library someone already has."""
|
||||
|
||||
Reference in New Issue
Block a user