Skip to content

Commit

Permalink
fix: update RegionCard component to handle undefined visited state an…
Browse files Browse the repository at this point in the history
…d refactor global search API to return structured results
  • Loading branch information
seanmorley15 committed Jan 18, 2025
1 parent d60945d commit f10e171
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 221 deletions.
92 changes: 47 additions & 45 deletions backend/server/adventures/views/global_search_view.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,73 @@

from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.db.models import Q
from django.contrib.postgres.search import SearchVector, SearchQuery
from adventures.models import Adventure, Collection
from adventures.serializers import AdventureSerializer, CollectionSerializer
from django.db.models import Q
from adventures.utils import pagination
from worldtravel.models import Country, Region, City
from worldtravel.serializers import CountrySerializer, RegionSerializer, CitySerializer
from worldtravel.models import Country, Region, City, VisitedCity, VisitedRegion
from worldtravel.serializers import CountrySerializer, RegionSerializer, CitySerializer, VisitedCitySerializer, VisitedRegionSerializer
from users.models import CustomUser as User
from users.serializers import CustomUserDetailsSerializer as UserSerializer

class GlobalSearchView(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
pagination_class = pagination.StandardResultsSetPagination

def list(self, request):
search_term = request.query_params.get('query', '')
# print(f"Searching for: {search_term}") # For debugging

search_term = request.query_params.get('query', '').strip()
if not search_term:
return Response({"error": "Search query is required"}, status=400)

# Search for adventures
adventures = Adventure.objects.filter(
(Q(name__icontains=search_term) | Q(description__icontains=search_term) | Q(location__icontains=search_term)) & Q(user_id=request.user.id)
)
# Initialize empty results
results = {
"adventures": [],
"collections": [],
"users": [],
"countries": [],
"regions": [],
"cities": [],
"visited_regions": [],
"visited_cities": []
}

# Search for collections
# Adventures: Full-Text Search
adventures = Adventure.objects.annotate(
search=SearchVector('name', 'description', 'location')
).filter(search=SearchQuery(search_term), user_id=request.user)
results["adventures"] = AdventureSerializer(adventures, many=True).data

# Collections: Partial Match Search
collections = Collection.objects.filter(
Q(name__icontains=search_term) & Q(user_id=request.user.id)
Q(name__icontains=search_term) & Q(user_id=request.user)
)
results["collections"] = CollectionSerializer(collections, many=True).data

# Search for users
# Users: Public Profiles Only
users = User.objects.filter(
(Q(username__icontains=search_term) | Q(first_name__icontains=search_term) | Q(last_name__icontains=search_term)) & Q(public_profile=True)
(Q(username__icontains=search_term) |
Q(first_name__icontains=search_term) |
Q(last_name__icontains=search_term)) & Q(public_profile=True)
)
results["users"] = UserSerializer(users, many=True).data

# Search for countries
countries = Country.objects.filter(
Q(name__icontains=search_term) | Q(country_code__icontains=search_term)
)
# Countries: Full-Text Search
countries = Country.objects.annotate(
search=SearchVector('name', 'country_code')
).filter(search=SearchQuery(search_term))
results["countries"] = CountrySerializer(countries, many=True).data

# Search for regions
regions = Region.objects.filter(
Q(name__icontains=search_term) | Q(country__name__icontains=search_term)
)
# Regions and Cities: Partial Match Search
regions = Region.objects.filter(Q(name__icontains=search_term))
results["regions"] = RegionSerializer(regions, many=True).data

# Search for cities
cities = City.objects.filter(
Q(name__icontains=search_term) | Q(region__name__icontains=search_term) | Q(region__country__name__icontains=search_term)
)
cities = City.objects.filter(Q(name__icontains=search_term))
results["cities"] = CitySerializer(cities, many=True).data

# Visited Regions and Cities
visited_regions = VisitedRegion.objects.filter(user_id=request.user)
results["visited_regions"] = VisitedRegionSerializer(visited_regions, many=True).data

# Serialize the results
adventure_serializer = AdventureSerializer(adventures, many=True)
collection_serializer = CollectionSerializer(collections, many=True)
user_serializer = UserSerializer(users, many=True)
country_serializer = CountrySerializer(countries, many=True)
region_serializer = RegionSerializer(regions, many=True)
city_serializer = CitySerializer(cities, many=True)
visited_cities = VisitedCity.objects.filter(user_id=request.user)
results["visited_cities"] = VisitedCitySerializer(visited_cities, many=True).data

return Response({
"adventures": adventure_serializer.data,
"collections": collection_serializer.data,
"users": user_serializer.data,
"countries": country_serializer.data,
"regions": region_serializer.data,
"cities": city_serializer.data
})

return Response(results)
6 changes: 3 additions & 3 deletions frontend/src/lib/components/RegionCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { t } from 'svelte-i18n';
export let region: Region;
export let visited: boolean;
export let visited: boolean | undefined;
function goToCity() {
console.log(region);
Expand Down Expand Up @@ -64,12 +64,12 @@
</div>
<div class="card-actions justify-end">
<!-- <button class="btn btn-info" on:click={moreInfo}>More Info</button> -->
{#if !visited}
{#if !visited && visited !== undefined}
<button class="btn btn-primary" on:click={markVisited}
>{$t('adventures.mark_visited')}</button
>
{/if}
{#if visited}
{#if visited && visited !== undefined}
<button class="btn btn-warning" on:click={removeVisit}>{$t('adventures.remove')}</button>
{/if}
{#if region.num_cities > 0}
Expand Down
43 changes: 14 additions & 29 deletions frontend/src/routes/search/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,36 @@ const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';

export const load = (async (event) => {
const query = event.url.searchParams.get('query');
const property = event.url.searchParams.get('property') || 'all';

if (!query) {
return { data: [] };
}

let sessionId = event.cookies.get('sessionid');

let res = await fetch(
`${serverEndpoint}/api/adventures/search/?query=${query}&property=${property}`,
{
headers: {
'Content-Type': 'application/json',
Cookie: `sessionid=${sessionId}`
}
}
);

if (!res.ok) {
console.error('Failed to fetch search data');
let error = await res.json();
return { error: error.error };
}

let adventures: Adventure[] = await res.json();

let osmRes = await fetch(`https://nominatim.openstreetmap.org/search?q=${query}&format=jsonv2`, {
let res = await fetch(`${serverEndpoint}/api/search/?query=${query}`, {
headers: {
'User-Agent': `AdventureLog / ${appVersion} `
'Content-Type': 'application/json',
Cookie: `sessionid=${sessionId}`
}
});

if (!osmRes.ok) {
console.error('Failed to fetch OSM data');
if (!res.ok) {
console.error('Failed to fetch search data');
let error = await res.json();
return { error: error.error };
}

let osmData = (await osmRes.json()) as OpenStreetMapPlace[];
let data = await res.json();

return {
props: {
adventures,
query,
osmData
}
adventures: data.adventures,
collections: data.collections,
users: data.users,
countries: data.countries,
regions: data.regions,
cities: data.cities,
visited_cities: data.visited_cities,
visited_regions: data.visited_regions
};
}) satisfies PageServerLoad;
Loading

0 comments on commit f10e171

Please sign in to comment.