Source code for fairdm.contrib.contributors.services.matching
"""Fuzzy name matching that finds potential duplicate people."""
from __future__ import annotations
from typing import TYPE_CHECKING
from rapidfuzz.fuzz import token_sort_ratio
if TYPE_CHECKING:
from fairdm.contrib.contributors.models import Person
[docs]
def find_duplicate_candidates(person: Person, threshold: float = 0.85) -> list[dict]:
"""Find people whose names closely match this person's name.
Scores use rapidfuzz ``token_sort_ratio``, so reordered names such as
"Smith, John" and "John Smith" score alike.
Args:
person: The person to find duplicates for.
threshold: Minimum similarity score between 0 and 1.
Returns:
``{"person": Person, "score": float}`` dicts with a score from 0 to 1, highest
first. The person itself is excluded.
"""
from fairdm.contrib.contributors.models import Person as PersonModel
query_name = (person.name or "").strip()
if not query_name:
return []
candidates: list[dict] = []
for candidate in PersonModel.objects.exclude(pk=person.pk).only("pk", "name"):
candidate_name = (candidate.name or "").strip()
if not candidate_name:
continue
raw_score = token_sort_ratio(query_name, candidate_name)
score = raw_score / 100.0
if score >= threshold:
candidates.append({"person": candidate, "score": score})
candidates.sort(key=lambda c: c["score"], reverse=True)
return candidates