app.bettersight.io/backend/repositories/comparable_repository.py

88 lines
3.2 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})
def delete(self, match_id):
return pb_client.delete(COLLECTION, match_id)
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]
def delete(self, match_id):
return self._records.pop(match_id, None) is not None
comparable_repository = MockComparableRepository() if USE_MOCK_REPOSITORIES else ComparableRepository()