82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
import uuid
|
|
from urllib.parse import urlparse
|
|
from repositories.pocketbase_client import pb_client
|
|
from repositories.repo_config import USE_MOCK_REPOSITORIES
|
|
|
|
COLLECTION = 'competitors'
|
|
|
|
|
|
def _extract_domain(url):
|
|
"""Strips scheme/path/www from a URL down to a bare domain for dedup comparisons."""
|
|
netloc = urlparse(url if '://' in url else f'//{url}').netloc or url
|
|
return netloc.lower().lstrip('www.')
|
|
|
|
|
|
class CompetitorRepository:
|
|
"""Data access for the `competitors` collection."""
|
|
|
|
def get_by_domain(self, tenant_id, domain):
|
|
"""Used by CSV bulk import to skip duplicate competitors within a tenant."""
|
|
candidates = pb_client.list(COLLECTION, filter_str=f'tenant_id = "{tenant_id}"', per_page=200)
|
|
return next((c for c in candidates if _extract_domain(c.get('website', '')) == domain), None)
|
|
|
|
def get_by_id(self, competitor_id):
|
|
return pb_client.get_one(COLLECTION, competitor_id)
|
|
|
|
def list_for_tenant(self, tenant_id, active_only=True):
|
|
"""Returns a tenant's competitors, active-only by default."""
|
|
filter_str = f'tenant_id = "{tenant_id}"'
|
|
if active_only:
|
|
filter_str += ' && active = true'
|
|
return pb_client.list(COLLECTION, filter_str=filter_str, per_page=200)
|
|
|
|
def create(self, data):
|
|
return pb_client.create(COLLECTION, data)
|
|
|
|
def update(self, competitor_id, data):
|
|
return pb_client.update(COLLECTION, competitor_id, data)
|
|
|
|
def delete(self, competitor_id):
|
|
"""Hard-deletes a competitor record — GDPR erasure only. Normal removal soft-deactivates via update()."""
|
|
return pb_client.delete(COLLECTION, competitor_id)
|
|
|
|
|
|
class MockCompetitorRepository:
|
|
"""In-memory stand-in for CompetitorRepository."""
|
|
|
|
def __init__(self):
|
|
self._records = {}
|
|
|
|
def get_by_domain(self, tenant_id, domain):
|
|
return next((
|
|
r for r in self._records.values()
|
|
if r.get('tenant_id') == tenant_id and _extract_domain(r.get('website', '')) == domain
|
|
), None)
|
|
|
|
def get_by_id(self, competitor_id):
|
|
return self._records.get(competitor_id)
|
|
|
|
def list_for_tenant(self, tenant_id, active_only=True):
|
|
return [
|
|
r for r in self._records.values()
|
|
if r.get('tenant_id') == tenant_id and (not active_only or r.get('active'))
|
|
]
|
|
|
|
def create(self, data):
|
|
record_id = data.get('id') or str(uuid.uuid4())
|
|
record = {'id': record_id, 'active': True, 'change_detected': False, **data}
|
|
self._records[record_id] = record
|
|
return record
|
|
|
|
def update(self, competitor_id, data):
|
|
if competitor_id not in self._records:
|
|
return None
|
|
self._records[competitor_id] = {**self._records[competitor_id], **data}
|
|
return self._records[competitor_id]
|
|
|
|
def delete(self, competitor_id):
|
|
return self._records.pop(competitor_id, None) is not None
|
|
|
|
|
|
competitor_repository = MockCompetitorRepository() if USE_MOCK_REPOSITORIES else CompetitorRepository()
|