diff --git a/.github/workflows/backend-release.yml b/.github/workflows/backend-release.yml
index e12143c..696f8b5 100644
--- a/.github/workflows/backend-release.yml
+++ b/.github/workflows/backend-release.yml
@@ -2,7 +2,7 @@ name: Upload the tagged release backend image to GHCR and Docker Hub
on:
release:
- types: [published]
+ types: [released]
env:
IMAGE_NAME: "adventurelog-backend"
diff --git a/.github/workflows/frontend-release.yml b/.github/workflows/frontend-release.yml
index a73af32..bb7fc6b 100644
--- a/.github/workflows/frontend-release.yml
+++ b/.github/workflows/frontend-release.yml
@@ -2,7 +2,7 @@ name: Upload tagged release frontend image to GHCR and Docker Hub
on:
release:
- types: [published]
+ types: [released]
env:
IMAGE_NAME: "adventurelog-frontend"
diff --git a/backend/server/adventures/admin.py b/backend/server/adventures/admin.py
index 70eedc9..b7df19d 100644
--- a/backend/server/adventures/admin.py
+++ b/backend/server/adventures/admin.py
@@ -1,28 +1,20 @@
import os
from django.contrib import admin
from django.utils.html import mark_safe
-from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage
+from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage, Visit
from worldtravel.models import Country, Region, VisitedRegion
class AdventureAdmin(admin.ModelAdmin):
- list_display = ('name', 'type', 'user_id', 'date', 'is_public', 'image_display')
+ list_display = ('name', 'type', 'user_id', 'is_public')
list_filter = ('type', 'user_id', 'is_public')
-
- def image_display(self, obj):
- if obj.image:
- public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
- public_url = public_url.replace("'", "")
- return mark_safe(f' self.end_date:
+ raise ValidationError('The start date must be before or equal to the end date.')
+
+ def __str__(self):
+ return f"{self.adventure.name} - {self.start_date} to {self.end_date}"
class Adventure(models.Model):
#id = models.AutoField(primary_key=True)
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
- type = models.CharField(max_length=100, choices=ADVENTURE_TYPES)
+ type = models.CharField(max_length=100, choices=ADVENTURE_TYPES, default='general')
name = models.CharField(max_length=200)
location = models.CharField(max_length=200, blank=True, null=True)
activity_types = ArrayField(models.CharField(
@@ -44,9 +76,6 @@ class Adventure(models.Model):
description = models.TextField(blank=True, null=True)
rating = models.FloatField(blank=True, null=True)
link = models.URLField(blank=True, null=True, max_length=2083)
- image = ResizedImageField(force_format="WEBP", quality=75, null=True, blank=True, upload_to='images/')
- date = models.DateField(blank=True, null=True)
- end_date = models.DateField(blank=True, null=True)
is_public = models.BooleanField(default=False)
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)
@@ -54,6 +83,12 @@ class Adventure(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
+ # DEPRECATED FIELDS - TO BE REMOVED IN FUTURE VERSIONS
+ # Migrations performed in this version will remove these fields
+ # image = ResizedImageField(force_format="WEBP", quality=75, null=True, blank=True, upload_to='images/')
+ # date = models.DateField(blank=True, null=True)
+ # end_date = models.DateField(blank=True, null=True)
+
def clean(self):
if self.date and self.end_date and self.date > self.end_date:
raise ValidationError('The start date must be before the end date. Start date: ' + str(self.date) + ' End date: ' + str(self.end_date))
diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py
index c012976..77d8d49 100644
--- a/backend/server/adventures/serializers.py
+++ b/backend/server/adventures/serializers.py
@@ -31,6 +31,46 @@ class AdventureSerializer(serializers.ModelSerializer):
representation = super().to_representation(instance)
return representation
+ def create(self, validated_data):
+ visits_data = validated_data.pop('visits', [])
+ adventure = Adventure.objects.create(**validated_data)
+ for visit_data in visits_data:
+ Visit.objects.create(adventure=adventure, **visit_data)
+ return adventure
+
+ def update(self, instance, validated_data):
+ visits_data = validated_data.pop('visits', [])
+
+ # Update Adventure fields
+ for attr, value in validated_data.items():
+ setattr(instance, attr, value)
+ instance.save()
+
+ # Get current visits
+ current_visits = instance.visits.all()
+ current_visit_ids = set(current_visits.values_list('id', flat=True))
+
+ # Update or create visits
+ updated_visit_ids = set()
+ for visit_data in visits_data:
+ visit_id = visit_data.get('id')
+ if visit_id and visit_id in current_visit_ids:
+ visit = current_visits.get(id=visit_id)
+ for attr, value in visit_data.items():
+ setattr(visit, attr, value)
+ visit.save()
+ updated_visit_ids.add(visit_id)
+ else:
+ # If no ID is provided or ID doesn't exist, create new visit
+ new_visit = Visit.objects.create(adventure=instance, **visit_data)
+ updated_visit_ids.add(new_visit.id)
+
+ # Delete visits that are not in the updated data
+ visits_to_delete = current_visit_ids - updated_visit_ids
+ instance.visits.filter(id__in=visits_to_delete).delete()
+
+ return instance
+
class TransportationSerializer(serializers.ModelSerializer):
class Meta:
diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py
index 5a9dab6..7f90f73 100644
--- a/backend/server/adventures/views.py
+++ b/backend/server/adventures/views.py
@@ -5,7 +5,7 @@ from rest_framework.decorators import action
from rest_framework import viewsets
from django.db.models.functions import Lower
from rest_framework.response import Response
-from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage
+from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage, ADVENTURE_TYPES
from django.core.exceptions import PermissionDenied
from worldtravel.models import VisitedRegion, Region, Country
from .serializers import AdventureImageSerializer, AdventureSerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer
@@ -104,7 +104,10 @@ class AdventureViewSet(viewsets.ModelViewSet):
@action(detail=False, methods=['get'])
def filtered(self, request):
types = request.query_params.get('types', '').split(',')
- valid_types = ['visited', 'planned']
+ # handle case where types is all
+ if 'all' in types:
+ types = [t[0] for t in ADVENTURE_TYPES]
+ valid_types = [t[0] for t in ADVENTURE_TYPES]
types = [t for t in types if t in valid_types]
if not types:
@@ -113,7 +116,7 @@ class AdventureViewSet(viewsets.ModelViewSet):
queryset = Adventure.objects.none()
for adventure_type in types:
- if adventure_type in ['visited', 'planned']:
+ if adventure_type in valid_types:
queryset |= Adventure.objects.filter(
type=adventure_type, user_id=request.user.id)
@@ -125,23 +128,21 @@ class AdventureViewSet(viewsets.ModelViewSet):
def all(self, request):
if not request.user.is_authenticated:
return Response({"error": "User is not authenticated"}, status=400)
- # include_collections = request.query_params.get('include_collections', 'false')
- # if include_collections not in ['true', 'false']:
- # include_collections = 'false'
+ include_collections = request.query_params.get('include_collections', 'false')
+ if include_collections not in ['true', 'false']:
+ include_collections = 'false'
- # if include_collections == 'true':
- # queryset = Adventure.objects.filter(
- # Q(is_public=True) | Q(user_id=request.user.id)
- # )
- # else:
- # queryset = Adventure.objects.filter(
- # Q(is_public=True) | Q(user_id=request.user.id), collection=None
- # )
- allowed_types = ['visited', 'planned']
+ if include_collections == 'true':
+ queryset = Adventure.objects.filter(
+ Q(is_public=True) | Q(user_id=request.user.id)
+ )
+ else:
+ queryset = Adventure.objects.filter(
+ Q(is_public=True) | Q(user_id=request.user.id), collection=None
+ )
queryset = Adventure.objects.filter(
- Q(user_id=request.user.id) & Q(type__in=allowed_types)
+ Q(user_id=request.user.id)
)
-
queryset = self.apply_sorting(queryset)
serializer = self.get_serializer(queryset, many=True)
diff --git a/backend/server/templates/home.html b/backend/server/templates/home.html
index 3f01ae6..7b90001 100644
--- a/backend/server/templates/home.html
+++ b/backend/server/templates/home.html
@@ -1,10 +1,10 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-
Welcome to the server side of AdventureLog!
-This site is only ment for administrative users
-+ Admin Site + API Docs +
+{adventure.location}
{/if} - {#if adventure.date && adventure.date !== ''} -- {new Date(adventure.date).toLocaleDateString(undefined, { - timeZone: 'UTC' - })}{adventure.end_date && adventure.end_date !== '' - ? ' - ' + - new Date(adventure.end_date).toLocaleDateString(undefined, { timeZone: 'UTC' }) - : ''} +
+ {adventure.visits.length} + {adventure.visits.length > 1 ? 'visits' : 'visit'}