diff --git a/app/scheduler/junk_performers.py b/app/scheduler/junk_performers.py index 608b8d5..498cd2d 100644 --- a/app/scheduler/junk_performers.py +++ b/app/scheduler/junk_performers.py @@ -129,6 +129,32 @@ def find_junk_performers( return list(ids) +# Jednotokenowy performer jest ZBĘDNY na scenie, która ma już osobę pełnoimienną +# zawierającą to samo słowo: „Devine" obok „Shalina Devine", „Kitana" obok „Kitana A". +# To rozbite zapisy tej samej osoby, powstałe z parsowania tytułów przez tube-search — +# jeden wpis nazwiskiem łapie potem wszystkie sceny z tym nazwiskiem, należące do +# zupełnie innych ludzi (audyt 2026-07-27: 62 tys. takich przypisań, 2156 wpisów). +# +# Kasowanie jest DOWODLIWIE BEZSTRATNE: warunek wymaga, żeby konkretna osoba już była +# przypisana do tej sceny, więc informacja o obsadzie zostaje. Żadna scena nie może +# przez to zostać bez obsady (sprawdzone: 0). +# +# Ograniczone do `kind='scraper'` — przypisania z kanonu zostają nietknięte (73 takie). +_PRUNE_SURNAME_SQL = """ +DELETE FROM scene_performers sp +USING sources s, performers p +WHERE p.id = sp.performer_id + AND p.canonical_name !~ ' ' + AND length(p.canonical_name) >= 4 + AND s.id = sp.source_id + AND s.kind = 'scraper' + AND EXISTS ( + SELECT 1 FROM scene_performers sp2 JOIN performers q ON q.id = sp2.performer_id + WHERE sp2.scene_id = sp.scene_id AND q.id <> p.id AND q.canonical_name ~ ' ' + AND q.canonical_name ~* ('\\m' || p.canonical_name || '\\M') + ) +""" + _PRUNE_ARTIFACT_SQL = """ DELETE FROM scene_performers sp USING sources s, performers p @@ -148,8 +174,9 @@ WHERE length(trim(p.canonical_name)) <= :max_len def prune_artifact_name_attributions( session: Session, *, max_len: int = MAX_ARTIFACT_NAME_LEN ) -> tuple[int, int]: - """Zdejmij scraperowe przypisania z nazw 1-2 znakowych i usuń tych, którym nic nie - zostało. Kanoniczne przypisania zostają nietknięte — patrz MAX_ARTIFACT_NAME_LEN. + """Zdejmij scraperowe przypisania w dwóch przypadkach i usuń tych, którym nic nie + zostało: (1) nazwy 1-2 znakowe, (2) jednotokenowy performer na scenie, która ma już + osobę pełnoimienną zawierającą to słowo. Kanoniczne przypisania zostają nietknięte. Wymaga wypełnionego `scene_performers.source_id` (migracja 0026): bez provenance nie dałoby się odróżnić „Cj" z obsady TPDB od „Cj" wyłuskanego z tytułu przez tube-search. @@ -157,9 +184,23 @@ def prune_artifact_name_attributions( pruned = int( session.execute(text(_PRUNE_ARTIFACT_SQL), {"max_len": max_len}).rowcount or 0 ) + pruned += int(session.execute(text(_PRUNE_SURNAME_SQL)).rowcount or 0) dropped = int( session.execute(text(_DROP_EMPTY_ARTIFACT_SQL), {"max_len": max_len}).rowcount or 0 ) + dropped += int( + session.execute( + text( + "DELETE FROM performers p WHERE p.canonical_name !~ ' ' " + "AND length(p.canonical_name) >= 4 " + "AND NOT EXISTS (SELECT 1 FROM scene_performers sp " + "WHERE sp.performer_id = p.id) " + "AND NOT EXISTS (SELECT 1 FROM performer_external_refs r " + "JOIN sources s ON s.id = r.source_id WHERE r.performer_id = p.id " + "AND s.kind IN ('tpdb','stashdb'))" + ) + ).rowcount or 0 + ) return pruned, dropped