""" Serla Analytics - Python SDK Usage: from serla import Serla serla = Serla('sk_live_YOUR_API_KEY') serla.send('user_signup', {'plan': 'pro'}) """ import requests from typing import Dict, Any, Optional import json class Serla: """Serla Analytics SDK for Python""" def __init__( self, api_key: str, endpoint: str = 'https://serla.dev/api/events/ingest', debug: bool = False ): """ Initialize Serla SDK Args: api_key: Your Serla API key endpoint: API endpoint URL (default: https://serla.dev/api/events/ingest) debug: Enable debug logging (default: False) """ if not api_key: raise ValueError('Serla: API key is required') self.api_key = api_key self.endpoint = endpoint self.pageview_endpoint = 'https://serla.dev/api/pageviews/ingest' self.debug = debug self.user_id: Optional[str] = None self.session_id: Optional[str] = None self._log(f'Initialized with endpoint: {self.endpoint}') def send( self, event_name: str, metadata: Optional[Dict[str, Any]] = None, user_id: Optional[str] = None, session_id: Optional[str] = None ) -> Dict[str, Any]: """ Send an event to Serla Args: event_name: Name of the event (e.g., 'user_signup', 'purchase') metadata: Optional metadata dictionary user_id: Optional userId for this event (overrides identify()) session_id: Optional sessionId for this event (overrides set_session()) Returns: Response data from the API Raises: ValueError: If event_name is invalid requests.HTTPError: If the API request fails """ if not event_name or not isinstance(event_name, str): raise ValueError('Serla: Event name is required and must be a string') payload: Dict[str, Any] = { 'eventName': event_name, 'metadata': metadata or {}, } # Use parameter if provided, otherwise fall back to instance variable if user_id is not None: payload['userId'] = user_id elif self.user_id: payload['userId'] = self.user_id if session_id is not None: payload['sessionId'] = session_id elif self.session_id: payload['sessionId'] = self.session_id self._log(f'Sending event: {event_name}', metadata) try: response = requests.post( self.endpoint, headers={ 'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}', }, json=payload, timeout=10 ) response.raise_for_status() data = response.json() self._log('Event sent successfully:', data) return data except requests.exceptions.RequestException as error: self._log(f'Error sending event: {error}') raise def pageview( self, url: str, path: Optional[str] = None, referrer: Optional[str] = None ) -> Dict[str, Any]: """ Track a pageview Args: url: Full URL of the page (required) path: Page path (optional, extracted from url if not provided) referrer: Referrer URL Returns: API response data """ # Ensure we have a session ID if not self.session_id: import time timestamp = str(int(time.time() * 1000)) import random random_str = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=10)) self.session_id = f'{timestamp}-{random_str}' payload: Dict[str, Any] = { 'url': url, 'sessionId': self.session_id, } if self.user_id: payload['userId'] = self.user_id if referrer: payload['referrer'] = referrer self._log(f'Tracking pageview: {url}') try: response = requests.post( self.pageview_endpoint, headers={ 'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}', }, json=payload, timeout=10 ) response.raise_for_status() data = response.json() self._log('Pageview tracked successfully:', data) return data except requests.exceptions.RequestException as error: self._log(f'Error tracking pageview: {error}') raise def identify(self, user_id: Optional[str]) -> None: """ Identify a user for all subsequent events Args: user_id: User identifier (or None to clear) """ self.user_id = user_id self._log(f'User identified: {user_id}') def set_session(self, session_id: Optional[str]) -> None: """ Set a session ID for all subsequent events Args: session_id: Session identifier (or None to clear) """ self.session_id = session_id self._log(f'Session set: {session_id}') def get_user_id(self) -> Optional[str]: """Get the current user ID""" return self.user_id def get_session_id(self) -> Optional[str]: """Get the current session ID""" return self.session_id def _log(self, *args) -> None: """Log debug messages if debug mode is enabled""" if self.debug: print('[Serla]', *args) # Factory function def create_serla(api_key: str, **kwargs) -> Serla: """ Create a Serla instance Args: api_key: Your Serla API key **kwargs: Additional options (endpoint, debug) Returns: Serla instance """ return Serla(api_key, **kwargs)