""" Backend API Client Handles communication with Django backend """ import requests import logging from typing import Optional, Dict, Any from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry logger = logging.getLogger(__name__) class BackendClient: def __init__(self, base_url: str, timeout: int = 5, max_retries: int = 3): self.base_url = base_url self.timeout = timeout self.max_retries = max_retries self.session = self._create_session() def _create_session(self) -> requests.Session: """Create a requests session with retry logic""" session = requests.Session() retry_strategy = Retry( total=self.max_retries, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], allowed_methods=["HEAD", "GET", "OPTIONS", "POST"] ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) return session def post_reading(self, port: str, data: str) -> bool: """Post a serial port reading to the backend""" try: url = f"{self.base_url}/api/readings/" payload = { 'port': port, 'data': data } response = self.session.post( url, json=payload, timeout=self.timeout ) if response.status_code in [200, 201]: logger.info(f"Successfully posted reading to backend") return True else: logger.warning(f"Backend returned status {response.status_code}") return False except requests.exceptions.RequestException as e: logger.error(f"Error posting reading to backend: {e}") return False def get_latest_reading(self, port: Optional[str] = None) -> Optional[Dict[str, Any]]: """Get the latest reading from backend""" try: url = f"{self.base_url}/api/readings/latest/" params = {'port': port} if port else {} response = self.session.get( url, params=params, timeout=self.timeout ) if response.status_code == 200: return response.json() else: logger.warning(f"Backend returned status {response.status_code}") return None except requests.exceptions.RequestException as e: logger.error(f"Error getting latest reading: {e}") return None def get_readings(self, port: Optional[str] = None, limit: int = 10) -> Optional[list]: """Get readings from backend""" try: url = f"{self.base_url}/api/readings/" params = {'limit': limit} if port: params['port'] = port response = self.session.get( url, params=params, timeout=self.timeout ) if response.status_code == 200: return response.json() else: logger.warning(f"Backend returned status {response.status_code}") return None except requests.exceptions.RequestException as e: logger.error(f"Error getting readings: {e}") return None def health_check(self) -> bool: """Check if backend is available""" try: url = f"{self.base_url}/api/health/" response = self.session.get(url, timeout=self.timeout) return response.status_code == 200 except Exception as e: logger.error(f"Backend health check failed: {e}") return False