From 16a77720038e25f3bc57cf417e6b1440fe6b0295 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 22 Mar 2025 12:25:53 -0400 Subject: [PATCH] feat: Add additional adventure type and endpoint for sunrise/sunset information --- .../server/adventures/views/adventure_view.py | 71 ++++---- frontend/src/lib/types.ts | 9 ++ frontend/src/locales/en.json | 2 + .../routes/adventures/[id]/+page.server.ts | 6 +- .../src/routes/adventures/[id]/+page.svelte | 153 ++++++++++++------ 5 files changed, 146 insertions(+), 95 deletions(-) diff --git a/backend/server/adventures/views/adventure_view.py b/backend/server/adventures/views/adventure_view.py index 2f7e1f1..55beac3 100644 --- a/backend/server/adventures/views/adventure_view.py +++ b/backend/server/adventures/views/adventure_view.py @@ -10,6 +10,7 @@ from adventures.models import Adventure, Category, Transportation, Lodging from adventures.permissions import IsOwnerOrSharedWithFullAccess from adventures.serializers import AdventureSerializer, TransportationSerializer, LodgingSerializer from adventures.utils import pagination +import requests class AdventureViewSet(viewsets.ModelViewSet): serializer_class = AdventureSerializer @@ -170,48 +171,38 @@ class AdventureViewSet(viewsets.ModelViewSet): serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) - # @action(detail=True, methods=['post']) - # def convert(self, request, pk=None): - # """ - # Convert an Adventure instance into a Transportation or Lodging instance. - # Expects a JSON body with "target_type": "transportation" or "lodging". - # """ - # adventure = self.get_object() - # target_type = request.data.get("target_type", "").lower() + @action(detail=True, methods=['get'], url_path='additional-info') + def additional_info(self, request, pk=None): + adventure = self.get_object() - # if target_type not in ["transportation", "lodging"]: - # return Response( - # {"error": "Invalid target type. Must be 'transportation' or 'lodging'."}, - # status=400 - # ) - # if not adventure.collection: - # return Response( - # {"error": "Adventure must be part of a collection to be converted."}, - # status=400 - # ) + # Permission check: owner or shared collection member + if adventure.user_id != request.user: + if not (adventure.collection and adventure.collection.shared_with.filter(id=request.user.id).exists()): + return Response({"error": "User does not have permission to access this adventure"}, + status=status.HTTP_403_FORBIDDEN) - # # Define the overlapping fields that both the Adventure and target models share. - # overlapping_fields = ["name", "description", "is_public", 'collection'] + serializer = self.get_serializer(adventure) + response_data = serializer.data - # # Gather the overlapping data from the adventure instance. - # conversion_data = {} - # for field in overlapping_fields: - # if hasattr(adventure, field): - # conversion_data[field] = getattr(adventure, field) + visits = response_data.get('visits', []) + sun_times = [] - # # Make sure to include the user reference - # conversion_data["user_id"] = adventure.user_id + for visit in visits: + date = visit.get('start_date') + if date and adventure.longitude and adventure.latitude: + api_url = f'https://api.sunrisesunset.io/json?lat={adventure.latitude}&lng={adventure.longitude}&date={date}' + res = requests.get(api_url) + if res.status_code == 200: + data = res.json() + results = data.get('results', {}) + if results.get('sunrise') and results.get('sunset'): + sun_times.append({ + "date": date, + "visit_id": visit.get('id'), + "sunrise": results.get('sunrise'), + "sunset": results.get('sunset') + }) + - # # Convert the adventure instance within an atomic transaction. - # with transaction.atomic(): - # if target_type == "transportation": - # new_instance = Transportation.objects.create(**conversion_data) - # serializer = TransportationSerializer(new_instance) - # else: # target_type == "lodging" - # new_instance = Lodging.objects.create(**conversion_data) - # serializer = LodgingSerializer(new_instance) - - # # Optionally, delete the original adventure to avoid duplicates. - # adventure.delete() - - # return Response(serializer.data) + response_data['sun_times'] = sun_times + return Response(response_data) \ No newline at end of file diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index fd0eadb..69735a3 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -44,6 +44,15 @@ export type Adventure = { user?: User | null; }; +export type AdditionalAdventure = Adventure & { + sun_times: { + date: string; + visit_id: string; + sunrise: string; + sunset: string; + }[]; +}; + export type Country = { id: number; name: string; diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 72d1883..65c7288 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -90,6 +90,8 @@ "visits": "Visits", "create_new": "Create New...", "adventure": "Adventure", + "additional_info": "Additional Information", + "sunrise_sunset": "Sunrise & Sunset", "count_txt": "results matching your search", "sort": "Sort", "order_by": "Order By", diff --git a/frontend/src/routes/adventures/[id]/+page.server.ts b/frontend/src/routes/adventures/[id]/+page.server.ts index eed47ba..140ca46 100644 --- a/frontend/src/routes/adventures/[id]/+page.server.ts +++ b/frontend/src/routes/adventures/[id]/+page.server.ts @@ -1,11 +1,11 @@ import type { PageServerLoad } from './$types'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; -import type { Adventure, Collection } from '$lib/types'; +import type { AdditionalAdventure, Adventure, Collection } from '$lib/types'; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; export const load = (async (event) => { const id = event.params as { id: string }; - let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, { + let request = await fetch(`${endpoint}/api/adventures/${id.id}/additional-info/`, { headers: { Cookie: `sessionid=${event.cookies.get('sessionid')}` }, @@ -19,7 +19,7 @@ export const load = (async (event) => { } }; } else { - let adventure = (await request.json()) as Adventure; + let adventure = (await request.json()) as AdditionalAdventure; let collection: Collection | null = null; if (adventure.collection) { diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index 6d88e0e..f11f450 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -1,5 +1,5 @@