1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-23 14:59:36 +02:00

feat: enhance city and region visit tracking, update AdventureModal and serializers for improved data handling

This commit is contained in:
Sean Morley 2025-01-09 19:40:23 -05:00
parent 013a2cc751
commit de8764499b
4 changed files with 82 additions and 33 deletions

View file

@ -8,7 +8,7 @@ from django.db.models.functions import Lower
from rest_framework.response import Response from rest_framework.response import Response
from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage, Category from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage, Category
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from worldtravel.models import VisitedRegion, Region, Country from worldtravel.models import VisitedCity, VisitedRegion, Region, Country, City
from .serializers import AdventureImageSerializer, AdventureSerializer, CategorySerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer from .serializers import AdventureImageSerializer, AdventureSerializer, CategorySerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from django.db.models import Q from django.db.models import Q
@ -1159,41 +1159,48 @@ class ReverseGeocodeViewSet(viewsets.ViewSet):
Returns a dictionary containing the region name, country name, and ISO code if found. Returns a dictionary containing the region name, country name, and ISO code if found.
""" """
iso_code = None iso_code = None
town = None town_city_or_county = None
city = None
county = None
display_name = None display_name = None
country_code = None country_code = None
city = None
# town = None
# city = None
# county = None
if 'address' in data.keys(): if 'address' in data.keys():
keys = data['address'].keys() keys = data['address'].keys()
for key in keys: for key in keys:
if key.find("ISO") != -1: if key.find("ISO") != -1:
iso_code = data['address'][key] iso_code = data['address'][key]
if 'town' in keys: if 'town' in keys:
town = data['address']['town'] town_city_or_county = data['address']['town']
if 'county' in keys: if 'county' in keys:
county = data['address']['county'] town_city_or_county = data['address']['county']
if 'city' in keys: if 'city' in keys:
city = data['address']['city'] town_city_or_county = data['address']['city']
if not iso_code: if not iso_code:
return {"error": "No region found"} return {"error": "No region found"}
region = Region.objects.filter(id=iso_code).first() region = Region.objects.filter(id=iso_code).first()
visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first()
is_visited = False
region_visited = False
city_visited = False
country_code = iso_code[:2] country_code = iso_code[:2]
if region: if region:
if city: if town_city_or_county:
display_name = f"{city}, {region.name}, {country_code}" display_name = f"{town_city_or_county}, {region.name}, {country_code}"
elif town: city = City.objects.filter(name__contains=town_city_or_county, region=region).first()
display_name = f"{town}, {region.name}, {country_code}" visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first()
elif county:
display_name = f"{county}, {region.name}, {country_code}"
if visited_region: if visited_region:
is_visited = True region_visited = True
if visited_city:
city_visited = True
if region: if region:
return {"id": iso_code, "region": region.name, "country": region.country.name, "is_visited": is_visited, "display_name": display_name} return {"region_id": iso_code, "region": region.name, "country": region.country.name, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited}
return {"error": "No region found"} return {"error": "No region found"}
@action(detail=False, methods=['get']) @action(detail=False, methods=['get'])

View file

@ -137,6 +137,10 @@ class VisitedCityViewSet(viewsets.ModelViewSet):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
self.perform_create(serializer) self.perform_create(serializer)
# if the region is not visited, visit it
region = serializer.validated_data['city'].region
if not VisitedRegion.objects.filter(user_id=request.user.id, region=region).exists():
VisitedRegion.objects.create(user_id=request.user, region=region)
headers = self.get_success_headers(serializer.data) headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

View file

@ -365,15 +365,31 @@
async function markVisited() { async function markVisited() {
console.log(reverseGeocodePlace); console.log(reverseGeocodePlace);
if (reverseGeocodePlace) { if (reverseGeocodePlace) {
let res = await fetch(`/worldtravel?/markVisited`, { if (!reverseGeocodePlace.region_visited && reverseGeocodePlace.region_id) {
method: 'POST', let region_res = await fetch(`/api/visitedregion`, {
body: JSON.stringify({ regionId: reverseGeocodePlace.id }) headers: { 'Content-Type': 'application/json' },
}); method: 'POST',
if (res.ok) { body: JSON.stringify({ region: reverseGeocodePlace.region_id })
reverseGeocodePlace.is_visited = true; });
addToast('success', `Visit to ${reverseGeocodePlace.region} marked`); if (region_res.ok) {
} else { reverseGeocodePlace.region_visited = true;
addToast('error', `Failed to mark visit to ${reverseGeocodePlace.region}`); addToast('success', `Visit to ${reverseGeocodePlace.region} marked`);
} else {
addToast('error', `Failed to mark visit to ${reverseGeocodePlace.region}`);
}
}
if (!reverseGeocodePlace.city_visited && reverseGeocodePlace.city_id != null) {
let city_res = await fetch(`/api/visitedcity`, {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({ city: reverseGeocodePlace.city_id })
});
if (city_res.ok) {
reverseGeocodePlace.city_visited = true;
addToast('success', `Visit to ${reverseGeocodePlace.city} marked`);
} else {
addToast('error', `Failed to mark visit to ${reverseGeocodePlace.city}`);
}
} }
} }
} }
@ -542,7 +558,10 @@
addToast('error', $t('adventures.adventure_update_error')); addToast('error', $t('adventures.adventure_update_error'));
} }
} }
if (adventure.is_visited && !reverseGeocodePlace?.is_visited) { if (
(adventure.is_visited && !reverseGeocodePlace?.region_visited) ||
!reverseGeocodePlace?.city_visited
) {
markVisited(); markVisited();
} }
imageSearch = adventure.name; imageSearch = adventure.name;
@ -785,19 +804,33 @@ it would also work to just use on:click on the MapLibre component itself. -->
</MapLibre> </MapLibre>
{#if reverseGeocodePlace} {#if reverseGeocodePlace}
<div class="mt-2"> <div class="mt-2">
<p>{reverseGeocodePlace.region}, {reverseGeocodePlace.country}</p>
<p> <p>
{reverseGeocodePlace.is_visited {reverseGeocodePlace.city
? reverseGeocodePlace.city + ', '
: ''}{reverseGeocodePlace.region},
{reverseGeocodePlace.country}
</p>
<p>
{reverseGeocodePlace.region}:
{reverseGeocodePlace.region_visited
? $t('adventures.visited') ? $t('adventures.visited')
: $t('adventures.not_visited')} : $t('adventures.not_visited')}
</p> </p>
{#if reverseGeocodePlace.city}
<p>
{reverseGeocodePlace.city}:
{reverseGeocodePlace.city_visited
? $t('adventures.visited')
: $t('adventures.not_visited')}
</p>
{/if}
</div> </div>
{#if !reverseGeocodePlace.is_visited && !willBeMarkedVisited} {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && !willBeMarkedVisited)}
<button type="button" class="btn btn-neutral" on:click={markVisited}> <button type="button" class="btn btn-neutral" on:click={markVisited}>
{$t('adventures.mark_visited')} {$t('adventures.mark_visited')}
</button> </button>
{/if} {/if}
{#if !reverseGeocodePlace.is_visited && willBeMarkedVisited} {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && willBeMarkedVisited)}
<div role="alert" class="alert alert-info mt-2"> <div role="alert" class="alert alert-info mt-2">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -813,7 +846,9 @@ it would also work to just use on:click on the MapLibre component itself. -->
></path> ></path>
</svg> </svg>
<span <span
>{reverseGeocodePlace.region}, >{reverseGeocodePlace.city
? reverseGeocodePlace.city + ', '
: ''}{reverseGeocodePlace.region},
{reverseGeocodePlace.country} {reverseGeocodePlace.country}
{$t('adventures.will_be_marked')}</span {$t('adventures.will_be_marked')}</span
> >

View file

@ -201,11 +201,14 @@ export type Background = {
}; };
export type ReverseGeocode = { export type ReverseGeocode = {
id: string; region_id: string;
region: string; region: string;
country: string; country: string;
is_visited: boolean; region_visited: boolean;
city_visited: boolean;
display_name: string; display_name: string;
city: string;
city_id: string;
}; };
export type Category = { export type Category = {