#!/usr/bin/env python3 """ Score API Client for Mice Game Client module to integrate with the FastAPI score server """ import requests import json from typing import Optional, List, Dict, Any import time class ScoreAPIClient: """Client for communicating with the Mice Game Score API""" def __init__(self, api_base_url: str = "http://localhost:8000", timeout: int = 5): """ Initialize the API client Args: api_base_url: Base URL of the API server timeout: Request timeout in seconds """ self.api_base_url = api_base_url.rstrip('/') self.timeout = timeout def _make_request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Optional[Dict[str, Any]]: """ Make HTTP request to API Args: method: HTTP method (GET, POST, etc.) endpoint: API endpoint data: Request data for POST requests Returns: Response JSON or None if error """ url = f"{self.api_base_url}{endpoint}" try: if method.upper() == "GET": response = requests.get(url, timeout=self.timeout) elif method.upper() == "POST": response = requests.post(url, json=data, timeout=self.timeout) else: raise ValueError(f"Unsupported HTTP method: {method}") if response.status_code == 200: return response.json() elif response.status_code in [400, 404, 409]: # Client errors - return the error details return {"error": True, "status": response.status_code, "detail": response.json()} else: return {"error": True, "status": response.status_code, "detail": "Server error"} except requests.exceptions.ConnectionError: return {"error": True, "detail": "Could not connect to score server"} except requests.exceptions.Timeout: return {"error": True, "detail": "Request timeout"} except Exception as e: return {"error": True, "detail": str(e)} def signup_user(self, device_id: str, user_id: str) -> Dict[str, Any]: """ Register a new user Args: device_id: Device identifier user_id: User identifier Returns: Response dictionary with success/error status """ endpoint = f"/signup/{device_id}/{user_id}" response = self._make_request("POST", endpoint) if response is None: return {"success": False, "message": "Failed to connect to server"} if response.get("error"): return {"success": False, "message": response.get("detail", "Unknown error")} return response def submit_score(self, device_id: str, user_id: str, score: int, game_completed: bool = True) -> Dict[str, Any]: """ Submit a score for a user Args: device_id: Device identifier user_id: User identifier score: Game score game_completed: Whether the game was completed Returns: Response dictionary with success/error status """ endpoint = f"/score/{device_id}/{user_id}" data = { "user_id": user_id, "device_id": device_id, "score": score, "game_completed": game_completed } response = self._make_request("POST", endpoint, data) if response is None: return {"success": False, "message": "Failed to connect to server"} if response.get("error"): return {"success": False, "message": response.get("detail", "Unknown error")} return response def get_device_users(self, device_id: str) -> List[Dict[str, Any]]: """ Get all users registered for a device Args: device_id: Device identifier Returns: List of user dictionaries """ endpoint = f"/users/{device_id}" response = self._make_request("GET", endpoint) if response is None: return [] # Check if it's an error response (dict with error field) or success (list) if isinstance(response, dict) and response.get("error"): return [] # If it's a list (successful response), return it if isinstance(response, list): return response return [] def get_user_scores(self, device_id: str, user_id: str, limit: int = 10) -> List[Dict[str, Any]]: """ Get recent scores for a user Args: device_id: Device identifier user_id: User identifier limit: Maximum number of scores to return Returns: List of score dictionaries """ endpoint = f"/scores/{device_id}/{user_id}?limit={limit}" response = self._make_request("GET", endpoint) if response is None: return [] # Check if it's an error response (dict with error field) or success (list) if isinstance(response, dict) and response.get("error"): return [] # If it's a list (successful response), return it if isinstance(response, list): return response return [] def get_leaderboard(self, device_id: str, limit: int = 10) -> List[Dict[str, Any]]: """ Get leaderboard for a device Args: device_id: Device identifier limit: Maximum number of entries to return Returns: List of leaderboard entries """ endpoint = f"/leaderboard/{device_id}?limit={limit}" response = self._make_request("GET", endpoint) if response is None: return [] # Check if it's an error response (dict with error field) or success (list) if isinstance(response, dict) and response.get("error"): return [] # If it's a list (successful response), return it if isinstance(response, list): return response return [] def get_global_leaderboard(self, limit: int = 10) -> List[Dict[str, Any]]: """ Get global leaderboard across all devices Args: limit: Maximum number of entries to return Returns: List of global leaderboard entries """ endpoint = f"/leaderboard/global/top?limit={limit}" response = self._make_request("GET", endpoint) if response is None: return [] # Check if it's an error response (dict with error field) or success (list) if isinstance(response, dict) and response.get("error"): return [] # If it's a list (successful response), return it if isinstance(response, list): return response return [] def is_server_available(self) -> bool: """ Check if the API server is available Returns: True if server is reachable, False otherwise """ response = self._make_request("GET", "/") return response is not None and not response.get("error") def user_exists(self, device_id: str, user_id: str) -> bool: """ Check if a user is registered for a device Args: device_id: Device identifier user_id: User identifier Returns: True if user exists, False otherwise """ users = self.get_device_users(device_id) return any(user["user_id"] == user_id for user in users) # Convenience functions for easy integration def create_api_client(api_url: str = "http://localhost:8000") -> ScoreAPIClient: """Create and return an API client instance""" return ScoreAPIClient(api_url) def test_connection(api_url: str = "http://localhost:8000") -> bool: """Test if the API server is available""" client = ScoreAPIClient(api_url) return client.is_server_available() # Example usage and testing if __name__ == "__main__": # Example usage print("Testing Score API Client...") # Create client client = ScoreAPIClient() # Test server connection if not client.is_server_available(): print("ERROR: API server is not available. Start it with: python score_api.py") exit(1) print("API server is available!") # Example device and user device_id = "DEV-CLIENT01" user_id = "ClientTestUser" # Test user signup print(f"\nTesting user signup: {user_id}") result = client.signup_user(device_id, user_id) print(f"Signup result: {result}") # Test score submission print(f"\nTesting score submission...") result = client.submit_score(device_id, user_id, 1750, True) print(f"Score submission result: {result}") # Test getting users print(f"\nGetting users for device {device_id}:") users = client.get_device_users(device_id) for user in users: print(f" User: {user['user_id']}, Best Score: {user['best_score']}") # Test getting user scores print(f"\nGetting scores for {user_id}:") scores = client.get_user_scores(device_id, user_id) for score in scores: print(f" Score: {score['score']}, Time: {score['timestamp']}") # Test leaderboard print(f"\nLeaderboard for device {device_id}:") leaderboard = client.get_leaderboard(device_id) for entry in leaderboard: print(f" Rank {entry['rank']}: {entry['user_id']} - {entry['best_score']} pts") print("\nClient testing completed!")