goon/app/resolve/movie_match.py
goon-foss a4486980a4 style: myslniki em/en z komentarzy i docstringow (calosc repo)
1745 wystapien w 291 plikach (backend, scripts, mobile). Dominowal jeden ksztalt,
' — ' laczace zdania, wiec poszedl przecinek; myslnik otwierajacy mysl zdjety;
zakresy 0–5 i A–Z na dywiz.

Podzial komentarz/kod robil skaner stanow, nie regex, zeby nie ruszyc myslnika
stojacego w stringu albo w wyrazeniu regularnym. Potwierdzone maszynowo: po wycieciu
komentarzy z wersji HEAD i z dysku obie sa identyczne we wszystkich 291 plikach,
czyli poza komentarzami nie zmienilo sie nic. 233 pliki .py parsuja sie bez bledu,
tsc ma te same 11 bledow w tych samych 3 plikach co przed zmiana.

Dwie rzeczy wymagaly poprawki po automacie: wyrownane listy definicji, gdzie myslnik
byl separatorem kolumny (37 miejsc, kolumna przywrocona), i trzy przeniesienia mysli
do nowej linii, gdzie zostawal wiszacy przecinek na poczatku (poprawione recznie).

Bundla nie republikuje: Hermes i tak nie niesie komentarzy, wiec dla uzytkownika
to zero zmiany.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:51:57 +02:00

60 lines
2.1 KiB
Python

"""Wyszukiwanie kandydatów do dedup movies, blocking + fuzzy title prefilter.
Strategia: nie chcemy O(N) score'ować wszystkich filmów dla każdego nowego.
Blocking: kandydat musi mieć title trigram similarity ≥0.4 (pg_trgm) z incoming title,
ALBO (same studio AND year w oknie ±1). Plus pierwsze X (np. 50) filmów z każdego
zbioru, wystarczająco szeroko żeby nie zgubić matchów, dostatecznie wąsko żeby
score'ować szybko.
"""
from __future__ import annotations
import uuid
from datetime import date
from sqlalchemy import or_, select
from sqlalchemy.orm import Session
from app.models.movie import Movie
def find_movie_candidates(
session: Session,
*,
title_normalized: str,
studio_id: uuid.UUID | None,
release_year: int | None,
limit: int = 50,
) -> list[Movie]:
"""Zwraca kandydatów do score'owania.
Trigram threshold: pg_trgm `%` operator domyślnie ma similarity ≥0.3, zniżamy
do 0.4 dla jakości. Plus secondary blocking po (studio_id, year ±1) jako safety
net dla SEO tytułów (np. mangoporn często ma "Watch Cece adult..." vs paradisehill
"Cece", trigram złapie, ale gdyby nie, studio+year wystarczają).
"""
from sqlalchemy import literal
q = session.execute(
select(Movie)
.where(
or_(
# pg_trgm `%`, wymagamy minimum bound 0.4 (set per-session pewnie low default).
# Bezpieczniej użyć similarity() funkcji bezpośrednio z literalnym threshold.
Movie.title_normalized.op("%")(literal(title_normalized)),
# Studio + year ±1 jako fallback blocking
_studio_year_block(studio_id, release_year),
)
)
.limit(limit)
).scalars()
return list(q)
def _studio_year_block(studio_id: uuid.UUID | None, release_year: int | None):
from sqlalchemy import and_, false
if studio_id is None or release_year is None:
return false()
return and_(
Movie.studio_id == studio_id,
Movie.release_year.between(release_year - 1, release_year + 1),
)