diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 1ce81f5..17925b3 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -20,7 +20,7 @@ from django.contrib.auth import get_user_model from icalendar import Calendar, Event, vText, vCalAddress from django.http import HttpResponse from datetime import datetime -from django.db.models import Min +from django.db.models import Max User = get_user_model() @@ -54,9 +54,9 @@ class AdventureViewSet(viewsets.ModelViewSet): if order_by == 'date': # order by the earliest visit object associated with the adventure - queryset = queryset.annotate(earliest_visit=Min('visits__start_date')) - queryset = queryset.filter(earliest_visit__isnull=False) - ordering = 'earliest_visit' + queryset = queryset.annotate(latest_visit=Max('visits__start_date')) + queryset = queryset.filter(latest_visit__isnull=False) + ordering = 'latest_visit' # Apply case-insensitive sorting for the 'name' field elif order_by == 'name': queryset = queryset.annotate(lower_name=Lower('name')) @@ -1163,6 +1163,7 @@ class ReverseGeocodeViewSet(viewsets.ViewSet): display_name = None country_code = None city = None + visited_city = None # town = None # city = None @@ -1221,6 +1222,8 @@ class ReverseGeocodeViewSet(viewsets.ViewSet): # searches through all of the users adventures, if the serialized data is_visited, is true, runs reverse geocode on the adventures and if a region is found, marks it as visited. Use the extractIsoCode function to get the region new_region_count = 0 new_regions = {} + new_city_count = 0 + new_cities = {} adventures = Adventure.objects.filter(user_id=self.request.user) serializer = AdventureSerializer(adventures, many=True) for adventure, serialized_adventure in zip(adventures, serializer.data): @@ -1234,17 +1237,25 @@ class ReverseGeocodeViewSet(viewsets.ViewSet): data = response.json() except requests.exceptions.JSONDecodeError: return Response({"error": "Invalid response from geocoding service"}, status=400) - region = self.extractIsoCode(data) - if 'error' not in region: - region = Region.objects.filter(id=region['id']).first() + extracted_region = self.extractIsoCode(data) + if 'error' not in extracted_region: + region = Region.objects.filter(id=extracted_region['region_id']).first() visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() if not visited_region: visited_region = VisitedRegion(region=region, user_id=self.request.user) visited_region.save() new_region_count += 1 new_regions[region.id] = region.name - return Response({"new_regions": new_region_count, "regions": new_regions}) + if extracted_region['city_id'] is not None: + city = City.objects.filter(id=extracted_region['city_id']).first() + visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() + if not visited_city: + visited_city = VisitedCity(city=city, user_id=self.request.user) + visited_city.save() + new_city_count += 1 + new_cities[city.id] = city.name + return Response({"new_regions": new_region_count, "regions": new_regions, "new_cities": new_city_count, "cities": new_cities}) from django.http import HttpResponse from rest_framework import viewsets diff --git a/backend/server/requirements.txt b/backend/server/requirements.txt index 0e2ccbf..4400512 100644 --- a/backend/server/requirements.txt +++ b/backend/server/requirements.txt @@ -1,4 +1,5 @@ Django==5.0.11 +Django==5.0.10 djangorestframework>=3.15.2 django-allauth==0.63.3 drf-yasg==1.21.4 diff --git a/backend/server/users/forms.py b/backend/server/users/forms.py deleted file mode 100644 index 266bfd0..0000000 --- a/backend/server/users/forms.py +++ /dev/null @@ -1,17 +0,0 @@ -from django import forms - -class CustomSignupForm(forms.Form): - first_name = forms.CharField(max_length=30, required=True) - last_name = forms.CharField(max_length=30, required=True) - - def signup(self, request, user): - # Delay the import to avoid circular import - from allauth.account.forms import SignupForm - - # No need to call super() from CustomSignupForm; use the SignupForm directly if needed - user.first_name = self.cleaned_data['first_name'] - user.last_name = self.cleaned_data['last_name'] - - # Save the user instance - user.save() - return user \ No newline at end of file diff --git a/backend/server/users/tests.py b/backend/server/users/tests.py index 7ce503c..08b71aa 100644 --- a/backend/server/users/tests.py +++ b/backend/server/users/tests.py @@ -1,3 +1,84 @@ -from django.test import TestCase +from rest_framework.test import APITestCase +from .models import CustomUser +from uuid import UUID -# Create your tests here. +from allauth.account.models import EmailAddress + +class UserAPITestCase(APITestCase): + + def setUp(self): + # Signup a new user + response = self.client.post('/_allauth/browser/v1/auth/signup', { + 'username': 'testuser', + 'email': 'testuser@example.com', + 'password': 'testpassword', + 'first_name': 'Test', + 'last_name': 'User', + }, format='json') + self.assertEqual(response.status_code, 200) + + def test_001_user(self): + # Fetch user metadata + response = self.client.get('/auth/user-metadata/', format='json') + self.assertEqual(response.status_code, 200) + data = response.json() + print(data) + self.assertEqual(data['username'], 'testuser') + self.assertEqual(data['email'], 'testuser@example.com') + self.assertEqual(data['first_name'], 'Test') + self.assertEqual(data['last_name'], 'User') + self.assertEqual(data['public_profile'], False) + self.assertEqual(data['profile_pic'], None) + self.assertEqual(UUID(data['uuid']), CustomUser.objects.get(username='testuser').uuid) + self.assertEqual(data['is_staff'], False) + self.assertEqual(data['has_password'], True) + + def test_002_user_update(self): + try: + userModel = CustomUser.objects.get(username='testuser2') + except: + userModel = None + + self.assertEqual(userModel, None) + # Update user metadata + response = self.client.patch('/auth/update-user/', { + 'username': 'testuser2', + 'first_name': 'Test2', + 'last_name': 'User2', + 'public_profile': True, + }, format='json') + self.assertEqual(response.status_code, 200) + + data = response.json() + # Note that the email field is not updated because that is a seperate endpoint + userModel = CustomUser.objects.get(username='testuser2') + print(userModel) + self.assertEqual(data['username'], 'testuser2') + self.assertEqual(data['email'], 'testuser@example.com') + self.assertEqual(data['first_name'], 'Test2') + self.assertEqual(data['last_name'], 'User2') + self.assertEqual(data['public_profile'], True) + self.assertEqual(data['profile_pic'], None) + self.assertEqual(UUID(data['uuid']), CustomUser.objects.get(username='testuser2').uuid) + self.assertEqual(data['is_staff'], False) + self.assertEqual(data['has_password'], True) + + def test_003_user_add_email(self): + # Update user email + response = self.client.post('/_allauth/browser/v1/account/email', { + 'email': 'testuser2@example.com', + }, format='json') + self.assertEqual(response.status_code, 200) + + data = response.json() + email_data = data['data'][0] + + self.assertEqual(email_data['email'], 'testuser2@example.com') + self.assertEqual(email_data['primary'], False) + self.assertEqual(email_data['verified'], False) + + emails = EmailAddress.objects.filter(user=CustomUser.objects.get(username='testuser')) + self.assertEqual(emails.count(), 2) + # assert email are testuser@example and testuser2@example.com + self.assertEqual(emails[1].email, 'testuser@example.com') + self.assertEqual(emails[0].email, 'testuser2@example.com') diff --git a/documentation/docs/install/unraid.md b/documentation/docs/install/unraid.md index 313ec03..cb84455 100644 --- a/documentation/docs/install/unraid.md +++ b/documentation/docs/install/unraid.md @@ -13,19 +13,17 @@ It is recommended to install applications in this order. - To find the Database Application, search for `PostGIS` on the Unraid App Store and fill out the fields as follows: - Ensure that the POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB are set in the PostGIS container if not add them custom variables -![/static/img/unraid-config-2.png](/static/img/unraid-config-2.png) +![/static/img/unraid-config-2.png](/unraid-config-2.png) ## Backend - Cache Configuration: This option is useful only if your appdata share is stored on a cache drive, which is used to speed up read/write operations for your containerized applications. - Note: if your running the server in a docker network that is other than "host" (for example "bridge") than you need to add the IP of the host machine in the CSRF Trusted Origins variable. -![/static/img/unraid-config-1.png](/static/img/unraid-config-1.png) +![/static/img/unraid-config-1.png](/unraid-config-1.png) ## Frontend - By default, the frontend connects to the backend using `http://server:8000`. This will work if both the frontend and backend are on the same network. Otherwise, you’ll need to configure it to use the exposed port (default: 8016). -![/static/img/unraid-config-3.png](/static/img/unraid-config-3.png) - - +![/static/img/unraid-config-3.png](/unraid-config-3.png) diff --git a/documentation/public/unraid-config-1.png b/documentation/public/unraid-config-1.png new file mode 100644 index 0000000..36df25c Binary files /dev/null and b/documentation/public/unraid-config-1.png differ diff --git a/documentation/static/img/unraid-config-2.png b/documentation/public/unraid-config-2.png similarity index 100% rename from documentation/static/img/unraid-config-2.png rename to documentation/public/unraid-config-2.png diff --git a/documentation/public/unraid-config-3.png b/documentation/public/unraid-config-3.png new file mode 100644 index 0000000..6a0e49b Binary files /dev/null and b/documentation/public/unraid-config-3.png differ diff --git a/documentation/static/img/unraid-config-1.png b/documentation/static/img/unraid-config-1.png deleted file mode 100644 index bf4f50b..0000000 Binary files a/documentation/static/img/unraid-config-1.png and /dev/null differ diff --git a/documentation/static/img/unraid-config-3.png b/documentation/static/img/unraid-config-3.png deleted file mode 100644 index dd483a9..0000000 Binary files a/documentation/static/img/unraid-config-3.png and /dev/null differ diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index d6ba6b7..6fcc628 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -57,11 +57,14 @@ class="block input input-bordered w-full max-w-xs" />
{#if $page.form?.mfa_required} - +
{/if}