38 lines
1.4 KiB
Python
38 lines
1.4 KiB
Python
"""Entity matching (section 14): decides whether an incoming provider
|
|||
|
|
record corresponds to an existing Aurora entity.
|
||
|
|
|
||
|
|
Core 0.1 policy — deliberately conservative:
|
||
|
|
|
||
|
|
- An exact external-ID match (this provider has already been linked to an
|
||
|
|
Aurora entity of this type) is the only case treated as MatchConfidence
|
||
|
|
EXACT and reused.
|
||
|
|
- Anything else creates a *new* Aurora entity (MatchConfidence NEW) rather
|
||
|
|
than guessing via fuzzy title matching. Silently merging two providers'
|
||
|
|
differing descriptions of "the same" title is exactly what section 14
|
||
|
|
prohibits.
|
||
|
|
|
||
|
|
Fuzzy candidate matching (title + year similarity, surfaced for human
|
||
|
|
review) is genuinely deferred — see docs/architecture.md — until Core has
|
||
|
|
an admin review workflow to act on AMBIGUOUS matches.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import uuid
|
||
|
|
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
|
|
||
|
|
from app.models.enums import MatchConfidence
|
||
|
|
from app.repositories.reference_repository import find_entity_by_external_id
|
||
|
|
|
||
|
|
|
||
|
|
async def match_entity(
|
||
|
|
session: AsyncSession, *, provider_id: uuid.UUID, entity_type: str, external_id: str
|
||
|
|
) -> tuple[uuid.UUID | None, MatchConfidence]:
|
||
|
|
"""Return (existing_aurora_id_or_None, confidence)."""
|
||
|
|
|
||
|
|
existing = await find_entity_by_external_id(session, provider_id, entity_type, external_id)
|
||
|
|
if existing is not None:
|
||
|
|
return existing, MatchConfidence.EXACT
|
||
|
|
return None, MatchConfidence.NEW
|