"""Data-access helpers for signal source repair service.""" from __future__ import annotations from .shared import Optional, SignalSourceRegistry, User, and_, select class SignalSourceRepairDataAccessMixin: """Database read helpers for repair workflow.""" async def _load_registry( self, *, tenant_id: int, project_id: int, registry_id: int, ) -> Optional[SignalSourceRegistry]: return ( await self.db.execute( select(SignalSourceRegistry) .where( and_( SignalSourceRegistry.tenant_id == int(tenant_id), SignalSourceRegistry.project_id == int(project_id), SignalSourceRegistry.id == int(registry_id), ) ) .limit(1) ) ).scalar_one_or_none() async def _find_reusable_signal_id( self, *, tenant_id: int, project_id: int, row: SignalSourceRegistry, ) -> Optional[int]: target_key = self._normalize_identifier_key(row.source_identifier) if not target_key: return None candidates = ( await self.db.execute( select(SignalSourceRegistry) .where( and_( SignalSourceRegistry.tenant_id == int(tenant_id), SignalSourceRegistry.project_id == int(project_id), SignalSourceRegistry.id != int(row.id), SignalSourceRegistry.signal_id.is_not(None), ) ) .order_by(SignalSourceRegistry.created_at.desc(), SignalSourceRegistry.id.desc()) .limit(50) ) ).scalars().all() for candidate in candidates: current_key = self._normalize_identifier_key(candidate.source_identifier) if current_key and current_key == target_key: return self._safe_int(candidate.signal_id) for candidate in candidates: current_key = self._normalize_identifier_key(candidate.source_identifier) if current_key and ( current_key.endswith(target_key) or target_key.endswith(current_key) ): return self._safe_int(candidate.signal_id) return None async def _active_user_ids(self, *, tenant_id: int) -> list[int]: rows = ( await self.db.execute( select(User.id).where( and_( User.tenant_id == int(tenant_id), User.is_active.is_(True), ) ) ) ).scalars().all() return [int(item) for item in rows if item is not None]