1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-20 21:39:37 +02:00

feat: enhance AdventureImage model with custom upload path and add latitude/longitude fields to Country model

This commit is contained in:
Sean Morley 2025-01-01 19:27:33 -05:00
parent 67f6af8ca3
commit 5d12d103fc
11 changed files with 115 additions and 4 deletions

View file

@ -0,0 +1,20 @@
# Generated by Django 5.0.8 on 2025-01-01 21:40
import adventures.models
import django_resized.forms
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('adventures', '0015_transportation_destination_latitude_and_more'),
]
operations = [
migrations.AlterField(
model_name='adventureimage',
name='image',
field=django_resized.forms.ResizedImageField(crop=None, force_format='WEBP', keep_meta=True, quality=75, scale=None, size=[1920, 1080], upload_to=adventures.models.PathAndRename('images/')),
),
]

View file

@ -1,7 +1,9 @@
from collections.abc import Collection from collections.abc import Collection
import os
from typing import Iterable from typing import Iterable
import uuid import uuid
from django.db import models from django.db import models
from django.utils.deconstruct import deconstructible
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
@ -257,11 +259,26 @@ class ChecklistItem(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@deconstructible
class PathAndRename:
def __init__(self, path):
self.path = path
def __call__(self, instance, filename):
ext = filename.split('.')[-1]
# Generate a new UUID for the filename
filename = f"{uuid.uuid4()}.{ext}"
return os.path.join(self.path, filename)
class AdventureImage(models.Model): class AdventureImage(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True) id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
user_id = models.ForeignKey( user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id) User, on_delete=models.CASCADE, default=default_user_id)
image = ResizedImageField(force_format="WEBP", quality=75, upload_to='images/') image = ResizedImageField(
force_format="WEBP",
quality=75,
upload_to=PathAndRename('images/') # Use the callable class here
)
adventure = models.ForeignKey(Adventure, related_name='images', on_delete=models.CASCADE) adventure = models.ForeignKey(Adventure, related_name='images', on_delete=models.CASCADE)
def __str__(self): def __str__(self):

View file

@ -68,6 +68,8 @@ class Command(BaseCommand):
country_name = country['name'] country_name = country['name']
country_subregion = country['subregion'] country_subregion = country['subregion']
country_capital = country['capital'] country_capital = country['capital']
longitude = round(float(country['longitude']), 6) if country['longitude'] else None
latitude = round(float(country['latitude']), 6) if country['latitude'] else None
processed_country_codes.add(country_code) processed_country_codes.add(country_code)
@ -76,13 +78,17 @@ class Command(BaseCommand):
country_obj.name = country_name country_obj.name = country_name
country_obj.subregion = country_subregion country_obj.subregion = country_subregion
country_obj.capital = country_capital country_obj.capital = country_capital
country_obj.longitude = longitude
country_obj.latitude = latitude
countries_to_update.append(country_obj) countries_to_update.append(country_obj)
else: else:
country_obj = Country( country_obj = Country(
name=country_name, name=country_name,
country_code=country_code, country_code=country_code,
subregion=country_subregion, subregion=country_subregion,
capital=country_capital capital=country_capital,
longitude=longitude,
latitude=latitude
) )
countries_to_create.append(country_obj) countries_to_create.append(country_obj)

View file

@ -0,0 +1,23 @@
# Generated by Django 5.0.8 on 2025-01-02 00:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('worldtravel', '0010_country_capital'),
]
operations = [
migrations.AddField(
model_name='country',
name='latitude',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
migrations.AddField(
model_name='country',
name='longitude',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
]

View file

@ -15,6 +15,8 @@ class Country(models.Model):
country_code = models.CharField(max_length=2, unique=True) #iso2 code country_code = models.CharField(max_length=2, unique=True) #iso2 code
subregion = models.CharField(max_length=100, blank=True, null=True) subregion = models.CharField(max_length=100, blank=True, null=True)
capital = models.CharField(max_length=100, blank=True, null=True) capital = models.CharField(max_length=100, blank=True, null=True)
longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
class Meta: class Meta:
verbose_name = "Country" verbose_name = "Country"

View file

@ -29,7 +29,7 @@ class CountrySerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Country model = Country
fields = '__all__' fields = '__all__'
read_only_fields = ['id', 'name', 'country_code', 'subregion', 'flag_url', 'num_regions', 'num_visits'] read_only_fields = ['id', 'name', 'country_code', 'subregion', 'flag_url', 'num_regions', 'num_visits', 'longitude', 'latitude', 'capital']
class RegionSerializer(serializers.ModelSerializer): class RegionSerializer(serializers.ModelSerializer):

View file

@ -1057,6 +1057,7 @@ it would also work to just use on:click on the MapLibre component itself. -->
<div class="flex flex-wrap gap-4 mr-4 mt-2"> <div class="flex flex-wrap gap-4 mr-4 mt-2">
{#each immichImages as image} {#each immichImages as image}
<div class="flex flex-col items-center gap-2"> <div class="flex flex-col items-center gap-2">
<!-- svelte-ignore a11y-img-redundant-alt -->
<img <img
src={`/immich/${image.id}`} src={`/immich/${image.id}`}
alt="Image from Immich" alt="Image from Immich"

View file

@ -50,6 +50,8 @@ export type Country = {
capital: string; capital: string;
num_regions: number; num_regions: number;
num_visits: number; num_visits: number;
longitude: number | null;
latitude: number | null;
}; };
export type Region = { export type Region = {

View file

@ -231,6 +231,7 @@
"to": "To", "to": "To",
"start": "Start", "start": "Start",
"end": "End", "end": "End",
"show_map": "Show Map",
"emoji_picker": "Emoji Picker", "emoji_picker": "Emoji Picker",
"download_calendar": "Download Calendar", "download_calendar": "Download Calendar",
"date_information": "Date Information", "date_information": "Date Information",

View file

@ -1,8 +1,10 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation';
import CountryCard from '$lib/components/CountryCard.svelte'; import CountryCard from '$lib/components/CountryCard.svelte';
import type { Country } from '$lib/types'; import type { Country } from '$lib/types';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { MapLibre, Marker } from 'svelte-maplibre';
export let data: PageData; export let data: PageData;
console.log(data); console.log(data);
@ -12,6 +14,7 @@
let filteredCountries: Country[] = []; let filteredCountries: Country[] = [];
const allCountries: Country[] = data.props?.countries || []; const allCountries: Country[] = data.props?.countries || [];
let worldSubregions: string[] = []; let worldSubregions: string[] = [];
let showMap: boolean = false;
worldSubregions = [...new Set(allCountries.map((country) => country.subregion))]; worldSubregions = [...new Set(allCountries.map((country) => country.subregion))];
// remove blank subregions // remove blank subregions
@ -96,6 +99,16 @@
<option value={subregion}>{subregion}</option> <option value={subregion}>{subregion}</option>
{/each} {/each}
</select> </select>
<!-- borderd checkbox -->
<div class="flex items-center justify-center ml-4">
<input
type="checkbox"
class="checkbox checkbox-bordered"
bind:checked={showMap}
aria-label={$t('adventures.show_map')}
/>
<span class="ml-2">{$t('adventures.show_map')}</span>
</div>
</div> </div>
<div class="flex items-center justify-center mb-4"> <div class="flex items-center justify-center mb-4">
@ -115,6 +128,33 @@
{/if} {/if}
</div> </div>
{#if showMap}
<div class="mt-4 mb-4 flex justify-center">
<!-- checkbox to toggle marker -->
<MapLibre
style="https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"
class="aspect-[9/16] max-h-[70vh] sm:aspect-video sm:max-h-full w-10/12 rounded-lg"
standardControls
zoom={2}
>
{#each filteredCountries as country}
{#if country.latitude && country.longitude}
<Marker
lngLat={[country.longitude, country.latitude]}
class="grid px-2 py-1 place-items-center rounded-full border border-gray-200 bg-green-200 text-black focus:outline-6 focus:outline-black"
on:click={() => goto(`/worldtravel/${country.country_code}`)}
>
<span class="text-xs">
{country.name}
</span>
</Marker>
{/if}
{/each}
</MapLibre>
</div>
{/if}
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center"> <div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each filteredCountries as country} {#each filteredCountries as country}
<CountryCard {country} /> <CountryCard {country} />

View file

@ -161,7 +161,6 @@
{/if} {/if}
{/each} {/each}
</MapLibre> </MapLibre>
<!-- button to clear to and from location -->
</div> </div>
<svelte:head> <svelte:head>