World Travel Improvements (#925)

* Security Patch Django 5.2.8

* Fix Menus on Safari Browser

* Enhance touch support and event handling for emoji picker and dropdown

* Add touch and pointer event handling to category selection for better mobile support

* Add PWA support for iOS/Safari with touch icons

* Refactor event listener for dropdown to use non-capturing 'click' for improved compatibility on Safari

* Enhance country and region description fetching from Wikipedia

- Refactor `generate_description_view.py` to improve candidate page selection and description retrieval.
- Update `CategoryDropdown.svelte` to simplify emoji selection handling and improve dropdown behavior.
- Add new translation keys in `en.json` for UI elements related to country descriptions.
- Modify `+page.svelte` and `+page.server.ts` in world travel routes to fetch and display country and region descriptions.
- Implement a toggle for showing full descriptions in the UI.

* Update Unraid installation documentation with improved variable formatting and additional resources

* Implement cache invalidation for visited regions and cities to ensure updated visit lists

* Add ClusterMap component for enhanced geographical data visualization
This commit is contained in:
Sean Morley
2025-12-07 11:46:44 -05:00
committed by GitHub
parent 5d799ceacc
commit 037b45fc17
17 changed files with 998 additions and 240 deletions

View File

@@ -14,6 +14,26 @@ from adventures.models import Location
# Cache TTL
CACHE_TTL = 60 * 60 * 24 # 1 day
def invalidate_visit_caches_for_region_and_user(region, user):
"""Invalidate cached visit lists for a given region and user.
Removes both the per-region and per-country per-user cache keys so
UI calls will refetch updated visited lists.
"""
try:
if region is None or user is None:
return
# per-region cache
cache.delete(f"visits_by_region_{region.id}_{user.id}")
# per-country cache (region -> country -> country_code)
country_code = getattr(region.country, 'country_code', None)
if country_code:
cache.delete(f"visits_by_country_{country_code}_{user.id}")
except Exception:
# Avoid raising cache-related exceptions; best-effort invalidation
pass
@cache_page(CACHE_TTL)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
@@ -138,13 +158,22 @@ class VisitedRegionViewSet(viewsets.ModelViewSet):
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
# Invalidate caches for this region and its country for the user
try:
region = serializer.validated_data.get('region')
invalidate_visit_caches_for_region_and_user(region, request.user)
except Exception:
pass
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def destroy(self, request, **kwargs):
region = get_object_or_404(Region, id=kwargs['pk'])
visited_region = VisitedRegion.objects.filter(user=request.user.id, region=region)
if visited_region.exists():
# capture region before deleting so we can invalidate caches
affected_region = visited_region.first().region
visited_region.delete()
invalidate_visit_caches_for_region_and_user(affected_region, request.user)
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response({"error": "Visited region not found."}, status=status.HTTP_404_NOT_FOUND)
@@ -164,9 +193,14 @@ class VisitedCityViewSet(viewsets.ModelViewSet):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
# Ensure a VisitedRegion exists for the city and invalidate caches
region = serializer.validated_data['city'].region
if not VisitedRegion.objects.filter(user=request.user.id, region=region).exists():
VisitedRegion.objects.create(user=request.user, region=region)
try:
invalidate_visit_caches_for_region_and_user(region, request.user)
except Exception:
pass
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
@@ -174,7 +208,9 @@ class VisitedCityViewSet(viewsets.ModelViewSet):
city = get_object_or_404(City, id=kwargs['pk'])
visited_city = VisitedCity.objects.filter(user=request.user.id, city=city)
if visited_city.exists():
region = city.region
visited_city.delete()
invalidate_visit_caches_for_region_and_user(region, request.user)
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response({"error": "Visited city not found."}, status=status.HTTP_404_NOT_FOUND)