diff --git a/backend/server/adventures/migrations/0019_collection_updated_at.py b/backend/server/adventures/migrations/0019_collection_updated_at.py new file mode 100644 index 0000000..5a991fd --- /dev/null +++ b/backend/server/adventures/migrations/0019_collection_updated_at.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.7 on 2024-08-05 17:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0018_note_links'), + ] + + operations = [ + migrations.AddField( + model_name='collection', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/backend/server/adventures/models.py b/backend/server/adventures/models.py index d169c4a..d138307 100644 --- a/backend/server/adventures/models.py +++ b/backend/server/adventures/models.py @@ -71,6 +71,8 @@ class Collection(models.Model): created_at = models.DateTimeField(auto_now_add=True) start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + # if connected adventures are private and collection is public, raise an error def clean(self): diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index dbb69b3..2c0ab10 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -98,4 +98,5 @@ class CollectionSerializer(serializers.ModelSerializer): class Meta: model = Collection # fields are all plus the adventures field - fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes'] + fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at'] + read_only_fields = ['id', 'created_at', 'updated_at'] diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index e36302c..58b45a0 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -217,9 +217,9 @@ class CollectionViewSet(viewsets.ModelViewSet): order_by = self.request.query_params.get('order_by', 'name') order_direction = self.request.query_params.get('order_direction', 'asc') - valid_order_by = ['name'] + valid_order_by = ['name', 'upated_at'] if order_by not in valid_order_by: - order_by = 'name' + order_by = 'updated_at' if order_direction not in ['asc', 'desc']: order_direction = 'asc' @@ -228,13 +228,15 @@ class CollectionViewSet(viewsets.ModelViewSet): if order_by == 'name': queryset = queryset.annotate(lower_name=Lower('name')) ordering = 'lower_name' + if order_direction == 'desc': + ordering = f'-{ordering}' else: - ordering = order_by + order_by == 'updated_at' + ordering = 'updated_at' + if order_direction == 'asc': + ordering = '-updated_at' - if order_direction == 'desc': - ordering = f'-{ordering}' - - print(f"Ordering by: {ordering}") # For debugging + #print(f"Ordering by: {ordering}") # For debugging return queryset.order_by(ordering) diff --git a/backend/server/worldtravel/management/commands/worldtravel-seed.py b/backend/server/worldtravel/management/commands/worldtravel-seed.py index c2dbf0d..6712fe1 100644 --- a/backend/server/worldtravel/management/commands/worldtravel-seed.py +++ b/backend/server/worldtravel/management/commands/worldtravel-seed.py @@ -1,10 +1,36 @@ # myapp/management/commands/seed.py +import os from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model +import requests from worldtravel.models import Country, Region from django.db import transaction +from django.conf import settings + +media_root = settings.MEDIA_ROOT + +def saveCountryFlag(country_code): + flags_dir = os.path.join(media_root, 'flags') + + # Check if the flags directory exists, if not, create it + if not os.path.exists(flags_dir): + os.makedirs(flags_dir) + + # Check if the flag already exists in the media folder + flag_path = os.path.join(flags_dir, f'{country_code}.png') + if os.path.exists(flag_path): + print(f'Flag for {country_code} already exists') + return + + res = requests.get(f'https://flagcdn.com/h240/{country_code}.png') + if res.status_code == 200: + with open(flag_path, 'wb') as f: + f.write(res.content) + print(f'Flag for {country_code} downloaded') + else: + print(f'Error downloading flag for {country_code}') class Command(BaseCommand): help = 'Imports the world travel data' @@ -528,8 +554,10 @@ class Command(BaseCommand): defaults={'name': name, 'continent': continent} ) if created: + saveCountryFlag(country_code) self.stdout.write(f'Inserted {name} into worldtravel countries') else: + saveCountryFlag(country_code) self.stdout.write(f'Updated {name} in worldtravel countries') def sync_regions(self, regions): diff --git a/backend/server/worldtravel/serializers.py b/backend/server/worldtravel/serializers.py index cf1731b..b82d5dd 100644 --- a/backend/server/worldtravel/serializers.py +++ b/backend/server/worldtravel/serializers.py @@ -1,13 +1,24 @@ +import os from .models import Country, Region, VisitedRegion from rest_framework import serializers class CountrySerializer(serializers.ModelSerializer): + def get_public_url(self, obj): + return os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/') + + flag_url = serializers.SerializerMethodField() + + def get_flag_url(self, obj): + public_url = self.get_public_url(obj) + return public_url + '/media/' + 'flags/' + obj.country_code + '.png' + class Meta: model = Country fields = '__all__' # Serialize all fields of the Adventure model class RegionSerializer(serializers.ModelSerializer): + flag_url = '' class Meta: model = Region fields = '__all__' # Serialize all fields of the Adventure model diff --git a/backend/server/worldtravel/views.py b/backend/server/worldtravel/views.py index 0880f0a..e574b3a 100644 --- a/backend/server/worldtravel/views.py +++ b/backend/server/worldtravel/views.py @@ -16,7 +16,7 @@ from django.contrib.staticfiles import finders def regions_by_country(request, country_code): # require authentication country = get_object_or_404(Country, country_code=country_code) - regions = Region.objects.filter(country=country) + regions = Region.objects.filter(country=country).order_by('name') serializer = RegionSerializer(regions, many=True) return Response(serializer.data) diff --git a/docker-compose.yml b/docker-compose.yml index a6df3fa..f4a195d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,6 @@ services: - PUBLIC_SERVER_URL=http://server:8000 - ORIGIN=http://localhost:8080 - BODY_SIZE_LIMIT=Infinity - - ENABLE_ANALYTICS=false ports: - "8080:3000" depends_on: diff --git a/frontend/src/lib/components/CountryCard.svelte b/frontend/src/lib/components/CountryCard.svelte index 42a59d0..e03dba3 100644 --- a/frontend/src/lib/components/CountryCard.svelte +++ b/frontend/src/lib/components/CountryCard.svelte @@ -1,14 +1,14 @@ @@ -17,15 +17,10 @@ >
- No image available + No image available
-

{countryName}

+

{country.name}

diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index f3aafe8..050747d 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -34,6 +34,7 @@ export type Country = { name: string; country_code: string; continent: string; + flag_url: string; }; export type Region = { diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index 44a4621..ab64208 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -249,7 +249,10 @@ {#if data.props.collection}

Collection

-

{data.props.collection.name}

+ {data.props.collection.name}
{/if}
diff --git a/frontend/src/routes/collections/+page.server.ts b/frontend/src/routes/collections/+page.server.ts index 655f80c..90bce78 100644 --- a/frontend/src/routes/collections/+page.server.ts +++ b/frontend/src/routes/collections/+page.server.ts @@ -17,7 +17,7 @@ export const load = (async (event) => { let previous = null; let count = 0; let adventures: Adventure[] = []; - let initialFetch = await fetch(`${serverEndpoint}/api/collections/`, { + let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, { headers: { Cookie: `${event.cookies.get('auth')}` } diff --git a/frontend/src/routes/collections/+page.svelte b/frontend/src/routes/collections/+page.svelte index cdb9e02..4b9810b 100644 --- a/frontend/src/routes/collections/+page.svelte +++ b/frontend/src/routes/collections/+page.svelte @@ -23,8 +23,6 @@ let resultsPerPage: number = 25; - let currentView: string = 'cards'; - let next: string | null = data.props.next || null; let previous: string | null = data.props.previous || null; let count = data.props.count || 0; @@ -176,18 +174,18 @@ > {sidebarOpen ? 'Close Filters' : 'Open Filters'} - {#if currentView == 'cards'} -
- {#each collections as collection} - - {/each} -
- {/if} + +
+ {#each collections as collection} + + {/each} +
+
{#if next || previous}
@@ -246,27 +244,9 @@ value="name" hidden /> - +
-

View

-
- (currentView = 'cards')} - checked - /> - (currentView = 'table')} - /> -
diff --git a/frontend/src/routes/worldtravel/+page.svelte b/frontend/src/routes/worldtravel/+page.svelte index a76be71..29d3433 100644 --- a/frontend/src/routes/worldtravel/+page.svelte +++ b/frontend/src/routes/worldtravel/+page.svelte @@ -13,7 +13,7 @@
{#each countries as country} - + {/each}