|
- import json
- import logging
- from typing import Optional
- import functools
- from enum import Enum
-
- from ergast_service import ErgastService
- from models.circuit import Circuit
- from models.country import Country
- from models.locality import Locality
- from models.model_builder import parse_circuits_json
- from models.track_layout import TrackLayout
-
-
- # Define supported formats
- class Format(str, Enum):
- PNG = "png"
- SVG = "svg"
- GEOJSON = "geojson"
-
-
- class CircuitService:
- def __init__(self, circuit_data_path: str = "circuits/circuits.json"):
- self.circuit_data_path = circuit_data_path
- self.circuit_data = self._load_circuit_data()
- self._layout_cache = {}
- self._circuit_file_cache = {}
- self._grand_prix_cache = {}
- self.ergast_service = ErgastService(self)
-
- # Configure logging
- logging.basicConfig(level=logging.INFO)
- self.logger = logging.getLogger(__name__)
-
- @functools.lru_cache()
- def _load_circuit_data(self) -> dict[str, Country]:
- """Load and cache circuit data"""
- with open(self.circuit_data_path) as f:
- return parse_circuits_json(json.load(f))
-
- def find_layout_slug_for_year(self, circuit: Circuit, year: int) -> Optional[str]:
- """Find the appropriate layout slug for a given year"""
- cache_key = f"{circuit.name}-{year}"
-
- if cache_key in self._layout_cache:
- return self._layout_cache[cache_key]
-
- for layout_slug, _ in circuit.layouts.items():
- # Check different layout slug formats
- if '-' in layout_slug:
- # Handle range format
- start_year, *end = layout_slug.split('-')
- start_year = int(start_year)
- if not end[0]:
- # Format: 1967- (no end year)
- if year >= start_year:
- self._layout_cache[cache_key] = layout_slug
- return layout_slug
- else:
- # Format: 1967-1968
- end_year = int(end[0])
- if start_year <= year <= end_year:
- self._layout_cache[cache_key] = layout_slug
- return layout_slug
- else:
- # Format: 1967 (single year)
- if year == int(layout_slug):
- self._layout_cache[cache_key] = layout_slug
- return layout_slug
- return None
-
- def get_countries_list(self) -> Optional[list[Country]]:
- """Get list of all available countries with their slugs"""
- return [country for _, country in self.circuit_data.items()]
-
- def get_country_details(self, country_slug: str) -> Optional[Country]:
- """Get details for a specific country"""
- return self.circuit_data.get(country_slug)
-
- def get_localities_list(self, country_slug: str) -> Optional[list[Locality]]:
- """Get list of cities for a specific country"""
- return [locality for _, locality in self.circuit_data[country_slug].localities.items()]
-
- def get_locality_details(self, country_slug: str, locality_slug: str) -> Optional[Locality]:
- """Get details for a specific city"""
- return self.circuit_data[country_slug].localities.get(locality_slug)
-
- def get_circuits_list(self, country_slug: str, locality_slug: str) -> Optional[list[Circuit]]:
- """Get list of circuits for a specific city"""
- return [circuit_data for _, circuit_data in self.circuit_data[country_slug].localities.get(locality_slug).circuits.items()]
-
- def get_circuit_details(self, country_slug: str, locality_slug: str, circuit_slug: str) -> Optional[Circuit]:
- """Get circuit details and available formats"""
- return self.circuit_data[country_slug].localities.get(locality_slug).circuits.get(circuit_slug)
-
- def get_layout_details(self, country_slug: str, locality_slug: str, circuit_slug: str, layout_slug: str) -> Optional[TrackLayout]:
- """Get details for a specific layout"""
- return self.circuit_data[country_slug].localities.get(locality_slug).circuits.get(circuit_slug).layouts.get(
- layout_slug)
-
- def get_circuit_layout_by_ergast_data(self, grand_prix_name: str, season: int) -> Optional[TrackLayout]:
- """
- Find and return the circuit layout file path based on Ergast F1 API race data format.
- """
- country_slug, locality_slug, circuit_slug = self.ergast_service.find_slugs_for_grand_prix(grand_prix_name, season)
- if country_slug is None or locality_slug is None or circuit_slug is None:
- self.logger.error(f"Error finding circuit layout in Ergast for grand prix {grand_prix_name} in season {season}")
- return None
-
- circuit = self.get_circuit_details(country_slug, locality_slug, circuit_slug)
- if circuit is None:
- self.logger.error(f"Error finding circuit details for Ergast country, locality and circuit slug: {country_slug}, {locality_slug}, {circuit_slug}")
- return None
-
- layout_slug = self.find_layout_slug_for_year(circuit, season)
- if layout_slug is None:
- self.logger.error(f"Error finding layout slug for year {season} for circuit {circuit_slug}")
- return None
-
- return self.get_layout_details(country_slug, locality_slug, circuit_slug, layout_slug)
|