Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions alembic/versions/20260624_5ca948100902_drop_coveragerecords_and_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
"""Drop coveragerecords and equivalentscoveragerecords tables

The CoverageProvider machinery that read and wrote ``coveragerecords`` was
retired in the previous release, and the equivalent-identifiers refresh that
wrote ``equivalentscoveragerecords`` moved to a Celery task before that. No
running code (current or N-1) references either table, so both are dropped here
along with their shared ``coverage_status`` enum type.

The ``timestamps`` table and its separate ``service_type`` enum are unaffected.

Revision ID: 5ca948100902
Revises: a6c85605404c
Create Date: 2026-06-24 22:47:03.130552+00:00

"""

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "5ca948100902"
down_revision = "a6c85605404c"
branch_labels = None
depends_on = None

# The status enum shared by the two dropped tables. We manage the Postgres type
# explicitly (create_type=False) so it is created/dropped exactly once rather
# than once per table.
coverage_status = postgresql.ENUM(
"success",
"transient failure",
"persistent failure",
"registered",
name="coverage_status",
create_type=False,
)


def upgrade() -> None:
op.drop_index(
op.f("ix_equivalentscoveragerecords_equivalency_id"),
table_name="equivalentscoveragerecords",
)
op.drop_index(
op.f("ix_equivalentscoveragerecords_operation"),
table_name="equivalentscoveragerecords",
)
op.drop_index(
op.f("ix_equivalentscoveragerecords_status"),
table_name="equivalentscoveragerecords",
)
op.drop_index(
op.f("ix_equivalentscoveragerecords_timestamp"),
table_name="equivalentscoveragerecords",
)
op.drop_table("equivalentscoveragerecords")

op.drop_index(
op.f("ix_coveragerecords_data_source_id_operation_identifier_id"),
table_name="coveragerecords",
)
op.drop_index(op.f("ix_coveragerecords_exception"), table_name="coveragerecords")
op.drop_index(
op.f("ix_coveragerecords_identifier_id"), table_name="coveragerecords"
)
op.drop_index(op.f("ix_coveragerecords_status"), table_name="coveragerecords")
op.drop_index(op.f("ix_coveragerecords_timestamp"), table_name="coveragerecords")
op.drop_index(
"ix_identifier_id_data_source_id_operation", table_name="coveragerecords"
)
op.drop_index(
"ix_identifier_id_data_source_id_operation_collection_id",
table_name="coveragerecords",
)
op.drop_table("coveragerecords")

# The coverage_status enum was used only by the two tables just dropped.
coverage_status.drop(op.get_bind(), checkfirst=False)


def downgrade() -> None:
coverage_status.create(op.get_bind(), checkfirst=False)

op.create_table(
"coveragerecords",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("identifier_id", sa.Integer(), nullable=True),
sa.Column("data_source_id", sa.Integer(), nullable=True),
sa.Column("operation", sa.String(length=255), nullable=True),
sa.Column("timestamp", sa.DateTime(timezone=True), nullable=True),
sa.Column("status", coverage_status, nullable=True),
sa.Column("exception", sa.Unicode(), nullable=True),
sa.Column("collection_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["collection_id"],
["collections.id"],
name=op.f("coveragerecords_collection_id_fkey"),
),
sa.ForeignKeyConstraint(
["data_source_id"],
["datasources.id"],
name=op.f("coveragerecords_data_source_id_fkey"),
),
sa.ForeignKeyConstraint(
["identifier_id"],
["identifiers.id"],
name=op.f("coveragerecords_identifier_id_fkey"),
),
sa.PrimaryKeyConstraint("id", name=op.f("coveragerecords_pkey")),
)
op.create_index(
"ix_identifier_id_data_source_id_operation_collection_id",
"coveragerecords",
["identifier_id", "data_source_id", "operation", "collection_id"],
unique=True,
)
op.create_index(
"ix_identifier_id_data_source_id_operation",
"coveragerecords",
["identifier_id", "data_source_id", "operation"],
unique=True,
postgresql_where=sa.text("collection_id IS NULL"),
)
op.create_index(
op.f("ix_coveragerecords_timestamp"),
"coveragerecords",
["timestamp"],
unique=False,
)
op.create_index(
op.f("ix_coveragerecords_status"), "coveragerecords", ["status"], unique=False
)
op.create_index(
op.f("ix_coveragerecords_identifier_id"),
"coveragerecords",
["identifier_id"],
unique=False,
)
op.create_index(
op.f("ix_coveragerecords_exception"),
"coveragerecords",
["exception"],
unique=False,
)
op.create_index(
op.f("ix_coveragerecords_data_source_id_operation_identifier_id"),
"coveragerecords",
["data_source_id", "operation", "identifier_id"],
unique=False,
)

op.create_table(
"equivalentscoveragerecords",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("equivalency_id", sa.Integer(), nullable=False),
sa.Column("operation", sa.String(length=255), nullable=True),
sa.Column("timestamp", sa.DateTime(timezone=True), nullable=True),
sa.Column("status", coverage_status, nullable=True),
sa.Column("exception", sa.Unicode(), nullable=True),
sa.ForeignKeyConstraint(
["equivalency_id"],
["equivalents.id"],
name=op.f("equivalentscoveragerecords_equivalency_id_fkey"),
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id", name=op.f("equivalentscoveragerecords_pkey")),
sa.UniqueConstraint(
"equivalency_id",
"operation",
name=op.f("equivalentscoveragerecords_equivalency_id_operation_key"),
),
)
op.create_index(
op.f("ix_equivalentscoveragerecords_timestamp"),
"equivalentscoveragerecords",
["timestamp"],
unique=False,
)
op.create_index(
op.f("ix_equivalentscoveragerecords_status"),
"equivalentscoveragerecords",
["status"],
unique=False,
)
op.create_index(
op.f("ix_equivalentscoveragerecords_operation"),
"equivalentscoveragerecords",
["operation"],
unique=False,
)
op.create_index(
op.f("ix_equivalentscoveragerecords_equivalency_id"),
"equivalentscoveragerecords",
["equivalency_id"],
unique=False,
)
36 changes: 9 additions & 27 deletions src/palace/manager/api/admin/controller/work_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
INVALID_RATING,
INVALID_SERIES_POSITION,
METADATA_REFRESH_FAILURE,
METADATA_REFRESH_PENDING,
MISSING_CUSTOM_LIST,
UNKNOWN_LANGUAGE,
UNKNOWN_MEDIUM,
Expand All @@ -31,7 +30,6 @@
)
from palace.manager.api.problem_details import (
LIBRARY_NOT_FOUND,
REMOTE_INTEGRATION_FAILED,
)
from palace.manager.api.util.flask import get_request_library
from palace.manager.core.classifier import NO_NUMBER, NO_VALUE, genres
Expand Down Expand Up @@ -448,39 +446,23 @@ def unsuppress(
)

def refresh_metadata(
self, identifier_type: str, identifier: str, provider: Any | None = None
self, identifier_type: str, identifier: str
) -> Response | ProblemDetail:
"""Refresh the metadata for a book from the content server"""
"""Refresh the metadata for a book from the content server.

Metadata refresh used to run through the per-source CoverageProvider
machinery, which has been retired. No provider is wired up, so this
endpoint is retained for API compatibility but no longer performs a
refresh; it always reports failure.
"""
library = get_request_library()
self.require_librarian(library)

work = self.load_work(library, identifier_type, identifier)
if isinstance(work, ProblemDetail):
return work

if provider is None:
return METADATA_REFRESH_FAILURE

assert work.presentation_edition is not None
primary_identifier = work.presentation_edition.primary_identifier
try:
record = provider.ensure_coverage(primary_identifier, force=True)
except Exception:
# The coverage provider may raise an HTTPIntegrationException.
return REMOTE_INTEGRATION_FAILED

if record.exception:
# There was a coverage failure.
if str(record.exception).startswith("201") or str(
record.exception
).startswith("202"):
# A 201/202 error means it's never looked up this work before
# so it's started the resolution process or looking for sources.
return METADATA_REFRESH_PENDING
# Otherwise, it just doesn't know anything.
return METADATA_REFRESH_FAILURE

return Response("", 200)
return METADATA_REFRESH_FAILURE

def classifications(
self, identifier_type: str, identifier: str
Expand Down
4 changes: 1 addition & 3 deletions src/palace/manager/celery/tasks/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ def bibliographic_apply(
load_from_id(session, Collection, collection_id) if collection_id else None
)

bibliographic.apply(
session, edition, collection, replace, create_coverage_record=False
)
bibliographic.apply(session, edition, collection, replace)


class ApplyBibliographicCallable(Protocol):
Expand Down
Loading