add logging for opensky user, update limits iterations 4000credits/d

This commit is contained in:
VALLONGOL 2025-06-10 10:27:25 +02:00
parent 9890da2eef
commit 51a4bc3382
2 changed files with 36 additions and 33 deletions

View File

@ -14,7 +14,7 @@ be clearly documented.
# --- API Configuration --- # --- API Configuration ---
# Base URL for the OpenSky Network API's 'states/all' endpoint. # Base URL for the OpenSky Network API's 'states/all' endpoint.
OPENSKY_API_URL: str = "https://opensky-network.org/api/states/all" # Used for direct calls if OAuth2 implemented OPENSKY_API_URL: str = "https://opensky-network.org/api/states/all" # Used for direct calls with OAuth2
# Default timeout for API requests in seconds. # Default timeout for API requests in seconds.
DEFAULT_API_TIMEOUT_SECONDS: int = 15 DEFAULT_API_TIMEOUT_SECONDS: int = 15
@ -22,24 +22,19 @@ DEFAULT_API_TIMEOUT_SECONDS: int = 15
# --- OpenSky Network API Authentication Configuration --- # --- OpenSky Network API Authentication Configuration ---
# Set to True to attempt authentication with OpenSky Network API using OAuth2 Client Credentials. # Set to True to attempt authentication with OpenSky Network API using OAuth2 Client Credentials.
# If False, or if Client ID/Secret are not provided, anonymous access will be used. # If False, or if Client ID/Secret are not provided, anonymous access will be used.
USE_OPENSKY_CREDENTIALS: bool = False # MODIFIED: Default to False, set to True if you want to use OAuth2 USE_OPENSKY_CREDENTIALS: bool = True # Set this to True to enable OAuth2 authentication
# MODIFIED: Changed from USERNAME/PASSWORD to CLIENT_ID/CLIENT_SECRET for OAuth2
# Your OpenSky Network API Client ID. # Your OpenSky Network API Client ID.
# Best stored as an environment variable (e.g., OPENSKY_CLIENT_ID). OPENSKY_CLIENT_ID: Optional[str] = "luca.vallongo@gmail.com-api-client" # os.getenv("OPENSKY_CLIENT_ID")
OPENSKY_CLIENT_ID: Optional[str] ="luca.vallongo@gmail.com-api-client" #os.getenv("OPENSKY_CLIENT_ID", None)
# Your OpenSky Network API Client Secret. # Your OpenSky Network API Client Secret.
# Best stored as an environment variable (e.g., OPENSKY_CLIENT_SECRET). OPENSKY_CLIENT_SECRET: Optional[str] = "heEkoeugbCmfKml5wTxk9ywoPFW1Z3vW" # os.getenv("OPENSKY_CLIENT_SECRET")
OPENSKY_CLIENT_SECRET: Optional[str] ="dGBGjEDLmqGGsZmdd5FOH8hOfIcekMjK" #os.getenv("OPENSKY_CLIENT_SECRET", None)
# OpenSky Network Token URL (used for OAuth2 Client Credentials Flow) # OpenSky Network Token URL (used for OAuth2 Client Credentials Flow)
# This is a standard Keycloak token endpoint format. # MODIFICATION: Corrected the entire URL to match the official documentation, including the 'auth.' subdomain.
OPENSKY_TOKEN_URL: str = "https://opensky-network.org/auth/realms/opensky/protocol/openid-connect/token" OPENSKY_TOKEN_URL: str = "https://auth.opensky-network.org/auth/realms/opensky-network/protocol/openid-connect/token"
# MODIFIED: Removed OPENSKY_USERNAME and OPENSKY_PASSWORD as they are for Basic Auth (being deprecated) # Removed OPENSKY_USERNAME and OPENSKY_PASSWORD as they are for Basic Auth (being deprecated)
# OPENSKY_USERNAME: Optional[str] = os.getenv("OPENSKY_USERNAME", None)
# OPENSKY_PASSWORD: Optional[str] = os.getenv("OPENSKY_PASSWORD", None)
# --- ADS-B Exchange API Configuration (Placeholder for future) --- # --- ADS-B Exchange API Configuration (Placeholder for future) ---
# LIVE_DATA_SOURCE: str = "OPENSKY" # Options: "OPENSKY", "ADSB_EXCHANGE", "MOCK" # LIVE_DATA_SOURCE: str = "OPENSKY" # Options: "OPENSKY", "ADSB_EXCHANGE", "MOCK"

View File

@ -9,6 +9,7 @@ import time
import threading import threading
from queue import Queue, Empty as QueueEmpty, Full as QueueFull from queue import Queue, Empty as QueueEmpty, Full as QueueFull
import random import random
import os # Import added for environment variable checks
from typing import Dict, Any, List, Optional, Tuple from typing import Dict, Any, List, Optional, Tuple
# Import OpenSkyApi for anonymous access (fallback) # Import OpenSkyApi for anonymous access (fallback)
@ -68,7 +69,7 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
self.token_expires_at: float = 0.0 self.token_expires_at: float = 0.0
self.api_client_anonymous: Optional[OpenSkyApi] = None self.api_client_anonymous: Optional[OpenSkyApi] = None
self.api_timeout = getattr(app_config, "DEFAULT_API_TIMEOUT_SECONDS", 15) # Questo timeout si userà per le chiamate OAuth dirette self.api_timeout = getattr(app_config, "DEFAULT_API_TIMEOUT_SECONDS", 15) # This timeout is used for direct OAuth calls
self.use_oauth = getattr(app_config, "USE_OPENSKY_CREDENTIALS", False) self.use_oauth = getattr(app_config, "USE_OPENSKY_CREDENTIALS", False)
if self.use_oauth: if self.use_oauth:
@ -87,13 +88,9 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
if not self.use_oauth: if not self.use_oauth:
try: try:
# MODIFICATO: Rimosso l'argomento 'timeout' dalla chiamata al costruttore di OpenSkyApi # The opensky-api library does not accept a 'timeout' argument in its constructor.
# PERCHÉ: La libreria opensky-api non accetta 'timeout' nel suo costruttore. self.api_client_anonymous = OpenSkyApi() # Timeout is not a valid argument here
# DOVE: Inizializzazione di self.api_client_anonymous.
# COME: Eliminato `timeout=self.api_timeout`. La libreria usa il suo timeout interno (di solito 5s per requests).
self.api_client_anonymous = OpenSkyApi() # Timeout non è un argomento valido qui
module_logger.info( module_logger.info(
# MODIFICATO: Aggiornato il messaggio di log per riflettere che il timeout della libreria è interno.
f"{self.name}: OpenSkyApi client (anonymous access) initialized (uses internal timeout)." f"{self.name}: OpenSkyApi client (anonymous access) initialized (uses internal timeout)."
) )
except Exception as e: except Exception as e:
@ -117,22 +114,32 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
module_logger.error(f"{self.name}: Cannot get OAuth token, client ID/secret/URL missing.") module_logger.error(f"{self.name}: Cannot get OAuth token, client ID/secret/URL missing.")
return False return False
data = { # Manually URL-encode the payload to ensure correct formatting
from urllib.parse import urlencode
payload_dict = {
"grant_type": "client_credentials", "grant_type": "client_credentials",
"client_id": self.client_id, "client_id": self.client_id,
"client_secret": self.client_secret, "client_secret": self.client_secret,
} }
headers = {"Content-Type": "application/x-www-form-urlencoded"} # The urlencode function will handle converting this to the correct string format
encoded_payload = urlencode(payload_dict)
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
}
module_logger.info(f"{self.name}: Requesting OAuth2 access token from {self.token_url}...") module_logger.info(f"{self.name}: Requesting OAuth2 access token from {self.token_url}...")
try: try:
response = requests.post(self.token_url, data=data, headers=headers, timeout=self.api_timeout) # Pass the manually encoded string to the 'data' parameter
response.raise_for_status() # Raise HTTPError for bad responses response = requests.post(self.token_url, data=encoded_payload, headers=headers, timeout=self.api_timeout, verify=False)
response.raise_for_status()
token_data = response.json() token_data = response.json()
self.access_token = token_data.get("access_token") self.access_token = token_data.get("access_token")
expires_in = token_data.get("expires_in", 300) # Default to 5 minutes if not specified expires_in = token_data.get("expires_in", 300)
self.token_expires_at = time.time() + expires_in - 60 # Subtract 60s buffer self.token_expires_at = time.time() + expires_in - 60
if self.access_token: if self.access_token:
module_logger.info(f"{self.name}: Successfully obtained OAuth2 access token. Expires in ~{expires_in // 60} minutes.") module_logger.info(f"{self.name}: Successfully obtained OAuth2 access token. Expires in ~{expires_in // 60} minutes.")
@ -142,14 +149,17 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
return False return False
except requests.exceptions.HTTPError as http_err: except requests.exceptions.HTTPError as http_err:
status_code = http_err.response.status_code if http_err.response else "N/A" status_code = http_err.response.status_code if http_err.response else "N/A"
error_text = http_err.response.text if http_err.response else "No response text" try:
module_logger.error(f"{self.name}: HTTP error {status_code} obtaining OAuth token: {error_text}", exc_info=False) error_details = http_err.response.json()
except requests.exceptions.RequestException as req_err: error_text = f"JSON Response: {error_details}"
module_logger.error(f"{self.name}: Network error obtaining OAuth token: {req_err}", exc_info=True) except requests.exceptions.JSONDecodeError:
error_text = f"Non-JSON Response: {http_err.response.text.strip()}"
module_logger.error(f"{self.name}: HTTP error {status_code} obtaining OAuth token: {error_text}", exc_info=True)
except Exception as e: except Exception as e:
module_logger.error(f"{self.name}: Unexpected error obtaining OAuth token: {e}", exc_info=True) module_logger.error(f"{self.name}: Unexpected error obtaining OAuth token: {e}", exc_info=True)
self.access_token = None # Ensure token is cleared on failure self.access_token = None
return False return False
def _is_token_valid(self) -> bool: def _is_token_valid(self) -> bool:
@ -167,7 +177,6 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
def _convert_opensky_api_state_to_canonical(self, sv_opensky_lib: Any) -> Optional[CanonicalFlightState]: def _convert_opensky_api_state_to_canonical(self, sv_opensky_lib: Any) -> Optional[CanonicalFlightState]:
"""Converts an OpenSkyApi library StateVector object to a CanonicalFlightState object.""" """Converts an OpenSkyApi library StateVector object to a CanonicalFlightState object."""
# This is the same conversion logic as before when using the opensky-api library
try: try:
if sv_opensky_lib.icao24 is None: return None if sv_opensky_lib.icao24 is None: return None
primary_ts = sv_opensky_lib.time_position if sv_opensky_lib.time_position is not None else sv_opensky_lib.last_contact primary_ts = sv_opensky_lib.time_position if sv_opensky_lib.time_position is not None else sv_opensky_lib.last_contact
@ -233,10 +242,8 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
module_logger.error(f"{self.name}: Unexpected error converting direct API state: {e_conv}. List: {raw_state_list}", exc_info=True) module_logger.error(f"{self.name}: Unexpected error converting direct API state: {e_conv}. List: {raw_state_list}", exc_info=True)
return None return None
def _perform_api_request(self) -> Dict[str, Any]: def _perform_api_request(self) -> Dict[str, Any]:
if app_config.USE_MOCK_OPENSKY_API: if app_config.USE_MOCK_OPENSKY_API:
# ... (Mock logic remains the same as your previous version) ...
module_logger.info(f"{self.name}: Using MOCK API data as per configuration.") module_logger.info(f"{self.name}: Using MOCK API data as per configuration.")
self._send_status_to_queue(STATUS_FETCHING, "Generating mock flight data...") self._send_status_to_queue(STATUS_FETCHING, "Generating mock flight data...")
if app_config.MOCK_API_ERROR_SIMULATION == "RATE_LIMITED": if app_config.MOCK_API_ERROR_SIMULATION == "RATE_LIMITED":
@ -279,6 +286,7 @@ class OpenSkyLiveAdapter(BaseLiveDataAdapter):
module_logger.error(f"{self.name}: {err_msg}") module_logger.error(f"{self.name}: {err_msg}")
# If token fails, we could try anonymous as a fallback or just fail this attempt # If token fails, we could try anonymous as a fallback or just fail this attempt
if not self.api_client_anonymous: # If anonymous is not even an option if not self.api_client_anonymous: # If anonymous is not even an option
self._consecutive_api_errors += 1
return {"error_type": STATUS_API_ERROR_TEMPORARY, "message": err_msg, "status_code": "OAUTH_TOKEN_FAILURE", "delay": self._calculate_next_backoff_delay(), "consecutive_errors": self._consecutive_api_errors} return {"error_type": STATUS_API_ERROR_TEMPORARY, "message": err_msg, "status_code": "OAUTH_TOKEN_FAILURE", "delay": self._calculate_next_backoff_delay(), "consecutive_errors": self._consecutive_api_errors}
else: # Fallback to anonymous for this attempt else: # Fallback to anonymous for this attempt
module_logger.warning(f"{self.name}: Attempting anonymous access due to OAuth token failure.") module_logger.warning(f"{self.name}: Attempting anonymous access due to OAuth token failure.")