feat: add configurable status mappings for metadata providers
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 6s

Add a status_mappings table to replace hardcoded provider status
normalization. Users can now configure how provider statuses (e.g.
"releasing", "finie") map to target statuses (e.g. "ongoing", "ended")
via the Settings > Integrations page.

- Migration 0038: status_mappings table with pre-seeded mappings
- Migration 0039: re-normalize existing series_metadata.status values
- API: CRUD endpoints for status mappings, DB-based normalize function
- API: new GET /series/provider-statuses endpoint
- Backoffice: StatusMappingsCard component with create target, assign,
  and delete capabilities
- Fix all clippy warnings across the API crate
- Fix missing OpenAPI schema refs (MetadataStats, ProviderCount)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 12:44:22 +01:00
parent bfc1c76fe2
commit cfc98819ab
25 changed files with 706 additions and 129 deletions

View File

@@ -0,0 +1,24 @@
-- Status mappings: many provider statuses → one target status (existing in series_metadata.status)
CREATE TABLE status_mappings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
provider_status TEXT NOT NULL UNIQUE,
mapped_status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Pre-populate with current hardcoded mappings from normalize_series_status
INSERT INTO status_mappings (provider_status, mapped_status) VALUES
-- AniList
('finished', 'ended'),
('releasing', 'ongoing'),
('not_yet_released', 'upcoming'),
('cancelled', 'cancelled'),
('hiatus', 'hiatus'),
-- Bédéthèque (French)
('finie', 'ended'),
('terminée', 'ended'),
('en cours', 'ongoing'),
('suspendue', 'hiatus'),
('annulée', 'cancelled'),
('arrêtée', 'cancelled');

View File

@@ -0,0 +1,19 @@
-- Re-normalize series_metadata.status using the status_mappings table.
-- Batch sync was not calling normalize_series_status before, so raw provider
-- values like "Série en cours" ended up in the DB alongside "ongoing".
-- Exact match
UPDATE series_metadata sm
SET status = m.mapped_status, updated_at = NOW()
FROM status_mappings m
WHERE LOWER(sm.status) = m.provider_status
AND sm.status IS NOT NULL
AND LOWER(sm.status) != m.mapped_status;
-- Substring match (for values like "Série en cours" containing "en cours")
UPDATE series_metadata sm
SET status = m.mapped_status, updated_at = NOW()
FROM status_mappings m
WHERE LOWER(sm.status) LIKE '%' || m.provider_status || '%'
AND sm.status IS NOT NULL
AND LOWER(sm.status) != m.mapped_status;