1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-19 12:59:36 +02:00
AdventureLog/backend/server/adventures/serializers.py
Sean Morley 1323d91e32 Add timezone support for visits, transportation, and lodging
- Introduced TIMEZONES constant in models.py to store valid timezone options.
- Updated Visit, Transportation, and Lodging models to include timezone fields.
- Modified serializers to include timezone fields in VisitSerializer, TransportationSerializer, and LodgingSerializer.
- Enhanced DateRangeCollapse component to handle timezone selection and formatting.
- Implemented timezone formatting functions in LodgingCard and TransportationCard components.
- Updated LodgingModal and TransportationModal to bind timezone data.
- Added VALID_TIMEZONES to dateUtils for consistent timezone management across the application.
2025-05-10 11:59:56 -04:00

319 lines
No EOL
13 KiB
Python
Raw Blame History

from django.utils import timezone
import os
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category, Attachment, Lodging
from rest_framework import serializers
from main.utils import CustomModelSerializer
from users.serializers import CustomUserDetailsSerializer
class AdventureImageSerializer(CustomModelSerializer):
class Meta:
model = AdventureImage
fields = ['id', 'image', 'adventure', 'is_primary', 'user_id']
read_only_fields = ['id', 'user_id']
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.image:
public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
#print(public_url)
# remove any ' from the url
public_url = public_url.replace("'", "")
representation['image'] = f"{public_url}/media/{instance.image.name}"
return representation
class AttachmentSerializer(CustomModelSerializer):
extension = serializers.SerializerMethodField()
class Meta:
model = Attachment
fields = ['id', 'file', 'adventure', 'extension', 'name', 'user_id']
read_only_fields = ['id', 'user_id']
def get_extension(self, obj):
return obj.file.name.split('.')[-1]
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.file:
public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
#print(public_url)
# remove any ' from the url
public_url = public_url.replace("'", "")
representation['file'] = f"{public_url}/media/{instance.file.name}"
return representation
class CategorySerializer(serializers.ModelSerializer):
num_adventures = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ['id', 'name', 'display_name', 'icon', 'num_adventures']
read_only_fields = ['id', 'num_adventures']
def validate_name(self, value):
return value.lower()
def create(self, validated_data):
user = self.context['request'].user
validated_data['name'] = validated_data['name'].lower()
return Category.objects.create(user_id=user, **validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
if 'name' in validated_data:
instance.name = validated_data['name'].lower()
instance.save()
return instance
def get_num_adventures(self, obj):
return Adventure.objects.filter(category=obj, user_id=obj.user_id).count()
class VisitSerializer(serializers.ModelSerializer):
class Meta:
model = Visit
fields = ['id', 'start_date', 'end_date', 'timezone', 'notes']
read_only_fields = ['id']
class AdventureSerializer(CustomModelSerializer):
images = AdventureImageSerializer(many=True, read_only=True)
visits = VisitSerializer(many=True, read_only=False, required=False)
attachments = AttachmentSerializer(many=True, read_only=True)
category = CategorySerializer(read_only=False, required=False)
is_visited = serializers.SerializerMethodField()
user = serializers.SerializerMethodField()
class Meta:
model = Adventure
fields = [
'id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location',
'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'longitude',
'latitude', 'visits', 'is_visited', 'category', 'attachments', 'user'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'is_visited', 'user']
def validate_category(self, category_data):
if isinstance(category_data, Category):
return category_data
if category_data:
user = self.context['request'].user
name = category_data.get('name', '').lower()
existing_category = Category.objects.filter(user_id=user, name=name).first()
if existing_category:
return existing_category
category_data['name'] = name
return category_data
def get_or_create_category(self, category_data):
user = self.context['request'].user
if isinstance(category_data, Category):
return category_data
if isinstance(category_data, dict):
name = category_data.get('name', '').lower()
display_name = category_data.get('display_name', name)
icon = category_data.get('icon', '<EFBFBD>')
else:
name = category_data.name.lower()
display_name = category_data.display_name
icon = category_data.icon
category, created = Category.objects.get_or_create(
user_id=user,
name=name,
defaults={
'display_name': display_name,
'icon': icon
}
)
return category
def get_user(self, obj):
user = obj.user_id
return CustomUserDetailsSerializer(user).data
def get_is_visited(self, obj):
current_date = timezone.now().date()
for visit in obj.visits.all():
start_date = visit.start_date.date() if isinstance(visit.start_date, timezone.datetime) else visit.start_date
end_date = visit.end_date.date() if isinstance(visit.end_date, timezone.datetime) else visit.end_date
if start_date and end_date and (start_date <= current_date):
return True
elif start_date and not end_date and (start_date <= current_date):
return True
return False
def create(self, validated_data):
visits_data = validated_data.pop('visits', None)
category_data = validated_data.pop('category', None)
print(category_data)
adventure = Adventure.objects.create(**validated_data)
for visit_data in visits_data:
Visit.objects.create(adventure=adventure, **visit_data)
if category_data:
category = self.get_or_create_category(category_data)
adventure.category = category
adventure.save()
return adventure
def update(self, instance, validated_data):
has_visits = 'visits' in validated_data
visits_data = validated_data.pop('visits', [])
category_data = validated_data.pop('category', None)
for attr, value in validated_data.items():
setattr(instance, attr, value)
if category_data:
category = self.get_or_create_category(category_data)
instance.category = category
instance.save()
if has_visits:
current_visits = instance.visits.all()
current_visit_ids = set(current_visits.values_list('id', flat=True))
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:
new_visit = Visit.objects.create(adventure=instance, **visit_data)
updated_visit_ids.add(new_visit.id)
visits_to_delete = current_visit_ids - updated_visit_ids
instance.visits.filter(id__in=visits_to_delete).delete()
return instance
class TransportationSerializer(CustomModelSerializer):
class Meta:
model = Transportation
fields = [
'id', 'user_id', 'type', 'name', 'description', 'rating',
'link', 'date', 'flight_number', 'from_location', 'to_location',
'is_public', 'collection', 'created_at', 'updated_at', 'end_date', 'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude', 'start_timezone', 'end_timezone'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
class LodgingSerializer(CustomModelSerializer):
class Meta:
model = Lodging
fields = [
'id', 'user_id', 'name', 'description', 'rating', 'link', 'check_in', 'check_out',
'reservation_number', 'price', 'latitude', 'longitude', 'location', 'is_public',
'collection', 'created_at', 'updated_at', 'type', 'timezone'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
class NoteSerializer(CustomModelSerializer):
class Meta:
model = Note
fields = [
'id', 'user_id', 'name', 'content', 'date', 'links',
'is_public', 'collection', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
class ChecklistItemSerializer(CustomModelSerializer):
class Meta:
model = ChecklistItem
fields = [
'id', 'user_id', 'name', 'is_checked', 'checklist', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'checklist']
class ChecklistSerializer(CustomModelSerializer):
items = ChecklistItemSerializer(many=True, source='checklistitem_set')
class Meta:
model = Checklist
fields = [
'id', 'user_id', 'name', 'date', 'is_public', 'collection', 'created_at', 'updated_at', 'items'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
def create(self, validated_data):
items_data = validated_data.pop('checklistitem_set')
checklist = Checklist.objects.create(**validated_data)
for item_data in items_data:
ChecklistItem.objects.create(checklist=checklist, **item_data)
return checklist
def update(self, instance, validated_data):
items_data = validated_data.pop('checklistitem_set', [])
# Update Checklist fields
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
# Get current items
current_items = instance.checklistitem_set.all()
current_item_ids = set(current_items.values_list('id', flat=True))
# Update or create items
updated_item_ids = set()
for item_data in items_data:
item_id = item_data.get('id')
if item_id:
if item_id in current_item_ids:
item = current_items.get(id=item_id)
for attr, value in item_data.items():
setattr(item, attr, value)
item.save()
updated_item_ids.add(item_id)
else:
# If ID is provided but doesn't exist, create new item
ChecklistItem.objects.create(checklist=instance, **item_data)
else:
# If no ID is provided, create new item
ChecklistItem.objects.create(checklist=instance, **item_data)
# Delete items that are not in the updated data
items_to_delete = current_item_ids - updated_item_ids
instance.checklistitem_set.filter(id__in=items_to_delete).delete()
return instance
def validate(self, data):
# Check if the collection is public and the checklist is not
collection = data.get('collection')
is_public = data.get('is_public', False)
if collection and collection.is_public and not is_public:
raise serializers.ValidationError(
'Checklists associated with a public collection must be public.'
)
return data
class CollectionSerializer(CustomModelSerializer):
adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set')
transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set')
notes = NoteSerializer(many=True, read_only=True, source='note_set')
checklists = ChecklistSerializer(many=True, read_only=True, source='checklist_set')
lodging = LodgingSerializer(many=True, read_only=True, source='lodging_set')
class Meta:
model = Collection
fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with', 'link', 'lodging']
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
def to_representation(self, instance):
representation = super().to_representation(instance)
# Make it display the user uuid for the shared users instead of the PK
shared_uuids = []
for user in instance.shared_with.all():
shared_uuids.append(str(user.uuid))
representation['shared_with'] = shared_uuids
return representation