mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-02 03:27:02 -05:00
feat: Implement initial schema and add various API, service, and management command enhancements across the application.
This commit is contained in:
@@ -127,9 +127,7 @@ class RideSearchService:
|
||||
|
||||
# Apply text search with ranking
|
||||
if filters.get("global_search"):
|
||||
queryset, search_rank = self._apply_full_text_search(
|
||||
queryset, filters["global_search"]
|
||||
)
|
||||
queryset, search_rank = self._apply_full_text_search(queryset, filters["global_search"])
|
||||
search_metadata["search_applied"] = True
|
||||
search_metadata["search_term"] = filters["global_search"]
|
||||
else:
|
||||
@@ -176,9 +174,7 @@ class RideSearchService:
|
||||
"applied_filters": self._get_applied_filters_summary(filters),
|
||||
}
|
||||
|
||||
def _apply_full_text_search(
|
||||
self, queryset, search_term: str
|
||||
) -> tuple[models.QuerySet, models.Expression]:
|
||||
def _apply_full_text_search(self, queryset, search_term: str) -> tuple[models.QuerySet, models.Expression]:
|
||||
"""
|
||||
Apply PostgreSQL full-text search with ranking and fuzzy matching.
|
||||
"""
|
||||
@@ -201,17 +197,14 @@ class RideSearchService:
|
||||
search_query = SearchQuery(search_term, config="english")
|
||||
|
||||
# Calculate search rank
|
||||
search_rank = SearchRank(
|
||||
search_vector, search_query, weights=self.SEARCH_RANK_WEIGHTS
|
||||
)
|
||||
search_rank = SearchRank(search_vector, search_query, weights=self.SEARCH_RANK_WEIGHTS)
|
||||
|
||||
# Apply trigram similarity for fuzzy matching on name
|
||||
trigram_similarity = TrigramSimilarity("name", search_term)
|
||||
|
||||
# Combine full-text search with trigram similarity
|
||||
queryset = queryset.annotate(trigram_similarity=trigram_similarity).filter(
|
||||
Q(search_vector=search_query)
|
||||
| Q(trigram_similarity__gte=self.TRIGRAM_SIMILARITY_THRESHOLD)
|
||||
Q(search_vector=search_query) | Q(trigram_similarity__gte=self.TRIGRAM_SIMILARITY_THRESHOLD)
|
||||
)
|
||||
|
||||
# Use the greatest of search rank and trigram similarity for final ranking
|
||||
@@ -219,36 +212,22 @@ class RideSearchService:
|
||||
|
||||
return queryset, final_rank
|
||||
|
||||
def _apply_basic_info_filters(
|
||||
self, queryset, filters: dict[str, Any]
|
||||
) -> models.QuerySet:
|
||||
def _apply_basic_info_filters(self, queryset, filters: dict[str, Any]) -> models.QuerySet:
|
||||
"""Apply basic information filters."""
|
||||
|
||||
# Category filter (multi-select)
|
||||
if filters.get("category"):
|
||||
categories = (
|
||||
filters["category"]
|
||||
if isinstance(filters["category"], list)
|
||||
else [filters["category"]]
|
||||
)
|
||||
categories = filters["category"] if isinstance(filters["category"], list) else [filters["category"]]
|
||||
queryset = queryset.filter(category__in=categories)
|
||||
|
||||
# Status filter (multi-select)
|
||||
if filters.get("status"):
|
||||
statuses = (
|
||||
filters["status"]
|
||||
if isinstance(filters["status"], list)
|
||||
else [filters["status"]]
|
||||
)
|
||||
statuses = filters["status"] if isinstance(filters["status"], list) else [filters["status"]]
|
||||
queryset = queryset.filter(status__in=statuses)
|
||||
|
||||
# Park filter (multi-select)
|
||||
if filters.get("park"):
|
||||
parks = (
|
||||
filters["park"]
|
||||
if isinstance(filters["park"], list)
|
||||
else [filters["park"]]
|
||||
)
|
||||
parks = filters["park"] if isinstance(filters["park"], list) else [filters["park"]]
|
||||
if isinstance(parks[0], str): # If slugs provided
|
||||
queryset = queryset.filter(park__slug__in=parks)
|
||||
else: # If IDs provided
|
||||
@@ -256,11 +235,7 @@ class RideSearchService:
|
||||
|
||||
# Park area filter (multi-select)
|
||||
if filters.get("park_area"):
|
||||
areas = (
|
||||
filters["park_area"]
|
||||
if isinstance(filters["park_area"], list)
|
||||
else [filters["park_area"]]
|
||||
)
|
||||
areas = filters["park_area"] if isinstance(filters["park_area"], list) else [filters["park_area"]]
|
||||
if isinstance(areas[0], str): # If slugs provided
|
||||
queryset = queryset.filter(park_area__slug__in=areas)
|
||||
else: # If IDs provided
|
||||
@@ -297,9 +272,7 @@ class RideSearchService:
|
||||
|
||||
return queryset
|
||||
|
||||
def _apply_height_safety_filters(
|
||||
self, queryset, filters: dict[str, Any]
|
||||
) -> models.QuerySet:
|
||||
def _apply_height_safety_filters(self, queryset, filters: dict[str, Any]) -> models.QuerySet:
|
||||
"""Apply height and safety requirement filters."""
|
||||
|
||||
# Minimum height range
|
||||
@@ -320,9 +293,7 @@ class RideSearchService:
|
||||
|
||||
return queryset
|
||||
|
||||
def _apply_performance_filters(
|
||||
self, queryset, filters: dict[str, Any]
|
||||
) -> models.QuerySet:
|
||||
def _apply_performance_filters(self, queryset, filters: dict[str, Any]) -> models.QuerySet:
|
||||
"""Apply performance metric filters."""
|
||||
|
||||
# Capacity range
|
||||
@@ -337,13 +308,9 @@ class RideSearchService:
|
||||
if filters.get("duration_range"):
|
||||
duration_range = filters["duration_range"]
|
||||
if duration_range.get("min") is not None:
|
||||
queryset = queryset.filter(
|
||||
ride_duration_seconds__gte=duration_range["min"]
|
||||
)
|
||||
queryset = queryset.filter(ride_duration_seconds__gte=duration_range["min"])
|
||||
if duration_range.get("max") is not None:
|
||||
queryset = queryset.filter(
|
||||
ride_duration_seconds__lte=duration_range["max"]
|
||||
)
|
||||
queryset = queryset.filter(ride_duration_seconds__lte=duration_range["max"])
|
||||
|
||||
# Rating range
|
||||
if filters.get("rating_range"):
|
||||
@@ -355,17 +322,13 @@ class RideSearchService:
|
||||
|
||||
return queryset
|
||||
|
||||
def _apply_relationship_filters(
|
||||
self, queryset, filters: dict[str, Any]
|
||||
) -> models.QuerySet:
|
||||
def _apply_relationship_filters(self, queryset, filters: dict[str, Any]) -> models.QuerySet:
|
||||
"""Apply relationship filters (manufacturer, designer, ride model)."""
|
||||
|
||||
# Manufacturer filter (multi-select)
|
||||
if filters.get("manufacturer"):
|
||||
manufacturers = (
|
||||
filters["manufacturer"]
|
||||
if isinstance(filters["manufacturer"], list)
|
||||
else [filters["manufacturer"]]
|
||||
filters["manufacturer"] if isinstance(filters["manufacturer"], list) else [filters["manufacturer"]]
|
||||
)
|
||||
if isinstance(manufacturers[0], str): # If slugs provided
|
||||
queryset = queryset.filter(manufacturer__slug__in=manufacturers)
|
||||
@@ -374,11 +337,7 @@ class RideSearchService:
|
||||
|
||||
# Designer filter (multi-select)
|
||||
if filters.get("designer"):
|
||||
designers = (
|
||||
filters["designer"]
|
||||
if isinstance(filters["designer"], list)
|
||||
else [filters["designer"]]
|
||||
)
|
||||
designers = filters["designer"] if isinstance(filters["designer"], list) else [filters["designer"]]
|
||||
if isinstance(designers[0], str): # If slugs provided
|
||||
queryset = queryset.filter(designer__slug__in=designers)
|
||||
else: # If IDs provided
|
||||
@@ -386,11 +345,7 @@ class RideSearchService:
|
||||
|
||||
# Ride model filter (multi-select)
|
||||
if filters.get("ride_model"):
|
||||
models_list = (
|
||||
filters["ride_model"]
|
||||
if isinstance(filters["ride_model"], list)
|
||||
else [filters["ride_model"]]
|
||||
)
|
||||
models_list = filters["ride_model"] if isinstance(filters["ride_model"], list) else [filters["ride_model"]]
|
||||
if isinstance(models_list[0], str): # If slugs provided
|
||||
queryset = queryset.filter(ride_model__slug__in=models_list)
|
||||
else: # If IDs provided
|
||||
@@ -398,9 +353,7 @@ class RideSearchService:
|
||||
|
||||
return queryset
|
||||
|
||||
def _apply_roller_coaster_filters(
|
||||
self, queryset, filters: dict[str, Any]
|
||||
) -> models.QuerySet:
|
||||
def _apply_roller_coaster_filters(self, queryset, filters: dict[str, Any]) -> models.QuerySet:
|
||||
"""Apply roller coaster specific filters."""
|
||||
queryset = self._apply_numeric_range_filter(
|
||||
queryset, filters, "height_ft_range", "rollercoasterstats__height_ft"
|
||||
@@ -426,14 +379,8 @@ class RideSearchService:
|
||||
|
||||
# Coaster type filter (multi-select)
|
||||
if filters.get("coaster_type"):
|
||||
types = (
|
||||
filters["coaster_type"]
|
||||
if isinstance(filters["coaster_type"], list)
|
||||
else [filters["coaster_type"]]
|
||||
)
|
||||
queryset = queryset.filter(
|
||||
rollercoasterstats__roller_coaster_type__in=types
|
||||
)
|
||||
types = filters["coaster_type"] if isinstance(filters["coaster_type"], list) else [filters["coaster_type"]]
|
||||
queryset = queryset.filter(rollercoasterstats__roller_coaster_type__in=types)
|
||||
|
||||
# Propulsion system filter (multi-select)
|
||||
if filters.get("propulsion_system"):
|
||||
@@ -457,18 +404,12 @@ class RideSearchService:
|
||||
if filters.get(filter_key):
|
||||
range_filter = filters[filter_key]
|
||||
if range_filter.get("min") is not None:
|
||||
queryset = queryset.filter(
|
||||
**{f"{field_name}__gte": range_filter["min"]}
|
||||
)
|
||||
queryset = queryset.filter(**{f"{field_name}__gte": range_filter["min"]})
|
||||
if range_filter.get("max") is not None:
|
||||
queryset = queryset.filter(
|
||||
**{f"{field_name}__lte": range_filter["max"]}
|
||||
)
|
||||
queryset = queryset.filter(**{f"{field_name}__lte": range_filter["max"]})
|
||||
return queryset
|
||||
|
||||
def _apply_company_filters(
|
||||
self, queryset, filters: dict[str, Any]
|
||||
) -> models.QuerySet:
|
||||
def _apply_company_filters(self, queryset, filters: dict[str, Any]) -> models.QuerySet:
|
||||
"""Apply company-related filters."""
|
||||
|
||||
# Manufacturer roles filter
|
||||
@@ -518,13 +459,9 @@ class RideSearchService:
|
||||
return queryset.order_by("-search_rank", "name")
|
||||
|
||||
# Apply the sorting
|
||||
return queryset.order_by(
|
||||
sort_field, "name"
|
||||
) # Always add name as secondary sort
|
||||
return queryset.order_by(sort_field, "name") # Always add name as secondary sort
|
||||
|
||||
def _add_search_highlights(
|
||||
self, results: list[Ride], search_term: str
|
||||
) -> list[Ride]:
|
||||
def _add_search_highlights(self, results: list[Ride], search_term: str) -> list[Ride]:
|
||||
"""Add search highlights to results using SearchHeadline."""
|
||||
|
||||
if not search_term or not results:
|
||||
@@ -601,9 +538,7 @@ class RideSearchService:
|
||||
else:
|
||||
raise ValueError(f"Unknown filter key: {filter_key}")
|
||||
|
||||
def get_search_suggestions(
|
||||
self, query: str, limit: int = 10
|
||||
) -> list[dict[str, Any]]:
|
||||
def get_search_suggestions(self, query: str, limit: int = 10) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Get search suggestions for autocomplete functionality.
|
||||
"""
|
||||
@@ -686,17 +621,11 @@ class RideSearchService:
|
||||
# Apply context filters to narrow down options
|
||||
if context_filters:
|
||||
temp_filters = context_filters.copy()
|
||||
temp_filters.pop(
|
||||
filter_type, None
|
||||
) # Remove the filter we're getting options for
|
||||
temp_filters.pop(filter_type, None) # Remove the filter we're getting options for
|
||||
base_queryset = self._apply_all_filters(base_queryset, temp_filters)
|
||||
|
||||
if filter_type == "park":
|
||||
return list(
|
||||
base_queryset.values("park__name", "park__slug")
|
||||
.distinct()
|
||||
.order_by("park__name")
|
||||
)
|
||||
return list(base_queryset.values("park__name", "park__slug").distinct().order_by("park__name"))
|
||||
|
||||
elif filter_type == "manufacturer":
|
||||
return list(
|
||||
|
||||
Reference in New Issue
Block a user