82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
import uuid
|
|
from repositories.pocketbase_client import pb_client
|
|
from repositories.repo_config import USE_MOCK_REPOSITORIES
|
|
|
|
COLLECTION = 'comparable_matches'
|
|
|
|
|
|
class ComparableRepository:
|
|
"""Data access for the `comparable_matches` collection (Phase 6)."""
|
|
|
|
def create(self, data):
|
|
return pb_client.create(COLLECTION, data)
|
|
|
|
def update(self, match_id, data):
|
|
return pb_client.update(COLLECTION, match_id, data)
|
|
|
|
def get_by_client_and_competitor_product(self, tenant_id, client_product, competitor_product):
|
|
"""
|
|
Used to upsert on repeat matching runs (the Sunday-night n8n
|
|
workflow) instead of duplicating a row for the same pair every
|
|
week — first_matched/last_matched only make sense if the same
|
|
pair is updated in place, not recreated.
|
|
"""
|
|
safe_client = client_product.replace('"', '\\"')
|
|
return pb_client.get_first(
|
|
COLLECTION,
|
|
f'tenant_id = "{tenant_id}" && client_product = "{safe_client}" '
|
|
f'&& competitor_product = "{competitor_product}"'
|
|
)
|
|
|
|
def list_for_tenant(self, tenant_id, include_dismissed=False):
|
|
filter_str = f'tenant_id = "{tenant_id}"'
|
|
if not include_dismissed:
|
|
filter_str += ' && dismissed = false'
|
|
return pb_client.list(COLLECTION, filter_str=filter_str, sort='-similarity_score', per_page=200)
|
|
|
|
def dismiss(self, match_id):
|
|
return pb_client.update(COLLECTION, match_id, {'dismissed': True})
|
|
|
|
|
|
class MockComparableRepository:
|
|
"""In-memory stand-in for ComparableRepository."""
|
|
|
|
def __init__(self):
|
|
self._records = {}
|
|
|
|
def create(self, data):
|
|
record_id = data.get('id') or str(uuid.uuid4())
|
|
record = {'id': record_id, 'dismissed': False, **data}
|
|
self._records[record_id] = record
|
|
return record
|
|
|
|
def update(self, match_id, data):
|
|
if match_id not in self._records:
|
|
return None
|
|
self._records[match_id] = {**self._records[match_id], **data}
|
|
return self._records[match_id]
|
|
|
|
def get_by_client_and_competitor_product(self, tenant_id, client_product, competitor_product):
|
|
return next((
|
|
r for r in self._records.values()
|
|
if r.get('tenant_id') == tenant_id and r.get('client_product') == client_product
|
|
and r.get('competitor_product') == competitor_product
|
|
), None)
|
|
|
|
def list_for_tenant(self, tenant_id, include_dismissed=False):
|
|
items = [
|
|
r for r in self._records.values()
|
|
if r.get('tenant_id') == tenant_id and (include_dismissed or not r.get('dismissed'))
|
|
]
|
|
items.sort(key=lambda r: r.get('similarity_score') or 0, reverse=True)
|
|
return items
|
|
|
|
def dismiss(self, match_id):
|
|
if match_id not in self._records:
|
|
return None
|
|
self._records[match_id]['dismissed'] = True
|
|
return self._records[match_id]
|
|
|
|
|
|
comparable_repository = MockComparableRepository() if USE_MOCK_REPOSITORIES else ComparableRepository()
|