1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-19 21:09:37 +02:00
AdventureLog/backend/server/adventures/models.py

321 lines
15 KiB
Python
Raw Normal View History

from collections.abc import Collection
import os
from typing import Iterable
import uuid
2024-07-08 11:44:39 -04:00
from django.db import models
from django.utils.deconstruct import deconstructible
from adventures.managers import AdventureManager
2024-07-08 11:44:39 -04:00
from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import ArrayField
from django.forms import ValidationError
2024-07-18 11:07:24 -04:00
from django_resized import ResizedImageField
ADVENTURE_TYPES = [
2024-09-22 10:29:23 -04:00
('general', 'General 🌍'),
2024-09-24 17:05:16 -04:00
('outdoor', 'Outdoor 🏞️'),
2024-09-22 10:29:23 -04:00
('lodging', 'Lodging 🛌'),
('dining', 'Dining 🍽️'),
('activity', 'Activity 🏄'),
('attraction', 'Attraction 🎢'),
('shopping', 'Shopping 🛍️'),
('nightlife', 'Nightlife 🌃'),
('event', 'Event 🎉'),
('transportation', 'Transportation 🚗'),
('culture', 'Culture 🎭'),
('water_sports', 'Water Sports 🚤'),
('hiking', 'Hiking 🥾'),
('wildlife', 'Wildlife 🦒'),
('historical_sites', 'Historical Sites 🏛️'),
('music_concerts', 'Music & Concerts 🎶'),
('fitness', 'Fitness 🏋️'),
('art_museums', 'Art & Museums 🎨'),
('festivals', 'Festivals 🎪'),
('spiritual_journeys', 'Spiritual Journeys 🧘‍♀️'),
('volunteer_work', 'Volunteer Work 🤝'),
('other', 'Other')
]
2024-07-08 11:44:39 -04:00
2024-07-27 19:04:55 -04:00
TRANSPORTATION_TYPES = [
('car', 'Car'),
('plane', 'Plane'),
('train', 'Train'),
('bus', 'Bus'),
('boat', 'Boat'),
('bike', 'Bike'),
('walking', 'Walking'),
('other', 'Other')
]
2024-07-08 11:44:39 -04:00
# Assuming you have a default user ID you want to use
default_user_id = 1 # Replace with an actual user ID
User = get_user_model()
class Visit(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
adventure = models.ForeignKey('Adventure', on_delete=models.CASCADE, related_name='visits')
2024-09-22 14:12:41 -04:00
start_date = models.DateField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
notes = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
if self.start_date > 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}"
2024-07-08 11:44:39 -04:00
class Adventure(models.Model):
#id = models.AutoField(primary_key=True)
2024-08-11 09:37:12 -04:00
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
2024-07-08 11:44:39 -04:00
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
category = models.ForeignKey('Category', on_delete=models.SET_NULL, blank=True, null=True)
2024-07-08 11:44:39 -04:00
name = models.CharField(max_length=200)
location = models.CharField(max_length=200, blank=True, null=True)
activity_types = ArrayField(models.CharField(
max_length=100), blank=True, null=True)
description = models.TextField(blank=True, null=True)
rating = models.FloatField(blank=True, null=True)
link = models.URLField(blank=True, null=True, max_length=2083)
2024-07-08 11:44:39 -04:00
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)
2024-07-15 09:36:07 -04:00
collection = models.ForeignKey('Collection', on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
2024-07-19 09:05:47 -04:00
updated_at = models.DateTimeField(auto_now=True)
objects = AdventureManager()
2024-09-22 14:12:41 -04:00
# 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)
# type = models.CharField(max_length=100, choices=ADVENTURE_TYPES, default='general')
2024-09-22 14:12:41 -04:00
def clean(self):
2024-07-15 09:36:07 -04:00
if self.collection:
if self.collection.is_public and not self.is_public:
raise ValidationError('Adventures associated with a public collection must be public. Collection: ' + self.trip.name + ' Adventure: ' + self.name)
if self.user_id != self.collection.user_id:
raise ValidationError('Adventures must be associated with collections owned by the same user. Collection owner: ' + self.collection.user_id.username + ' Adventure owner: ' + self.user_id.username)
if self.category:
if self.user_id != self.category.user_id:
raise ValidationError('Adventures must be associated with categories owned by the same user. Category owner: ' + self.category.user_id.username + ' Adventure owner: ' + self.user_id.username)
def save(self, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: Iterable[str] | None = None) -> None:
"""
Saves the current instance. If the instance is being inserted for the first time, it will be created in the database.
If it already exists, it will be updated.
"""
if force_insert and force_update:
raise ValueError("Cannot force both insert and updating in model saving.")
if not self.category:
2024-11-26 17:39:10 -05:00
category, created = Category.objects.get_or_create(
user_id=self.user_id,
name='general',
defaults={
'display_name': 'General',
'icon': '🌍'
}
)
self.category = category
return super().save(force_insert, force_update, using, update_fields)
def __str__(self):
return self.name
2024-07-15 09:36:07 -04:00
class Collection(models.Model):
#id = models.AutoField(primary_key=True)
2024-08-11 09:37:12 -04:00
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)
name = models.CharField(max_length=200)
2024-07-15 09:36:07 -04:00
description = models.TextField(blank=True, null=True)
is_public = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
2024-07-27 14:26:15 -04:00
start_date = models.DateField(blank=True, null=True)
end_date = models.DateField(blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True)
2024-08-07 13:01:12 -04:00
is_archived = models.BooleanField(default=False)
2024-09-02 10:29:51 -04:00
shared_with = models.ManyToManyField(User, related_name='shared_with', blank=True)
link = models.URLField(blank=True, null=True, max_length=2083)
2024-07-15 09:36:07 -04:00
# if connected adventures are private and collection is public, raise an error
def clean(self):
if self.is_public and self.pk: # Only check if the instance has a primary key
for adventure in self.adventure_set.all():
if not adventure.is_public:
2024-07-15 09:36:07 -04:00
raise ValidationError('Public collections cannot be associated with private adventures. Collection: ' + self.name + ' Adventure: ' + adventure.name)
2024-07-08 11:44:39 -04:00
def __str__(self):
return self.name
2024-07-27 19:04:55 -04:00
class Transportation(models.Model):
#id = models.AutoField(primary_key=True)
2024-08-11 09:37:12 -04:00
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
2024-07-27 19:04:55 -04:00
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
type = models.CharField(max_length=100, choices=TRANSPORTATION_TYPES)
name = models.CharField(max_length=200)
description = models.TextField(blank=True, null=True)
rating = models.FloatField(blank=True, null=True)
link = models.URLField(blank=True, null=True)
date = models.DateTimeField(blank=True, null=True)
2024-08-19 16:32:08 -04:00
end_date = models.DateTimeField(blank=True, null=True)
2024-07-27 19:04:55 -04:00
flight_number = models.CharField(max_length=100, blank=True, null=True)
from_location = models.CharField(max_length=200, blank=True, null=True)
origin_latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
origin_longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
destination_latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
destination_longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
2024-07-27 19:04:55 -04:00
to_location = models.CharField(max_length=200, blank=True, null=True)
is_public = models.BooleanField(default=False)
collection = models.ForeignKey('Collection', on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
2024-08-19 16:32:08 -04:00
print(self.date)
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))
2024-07-27 19:04:55 -04:00
if self.collection:
if self.collection.is_public and not self.is_public:
raise ValidationError('Transportations associated with a public collection must be public. Collection: ' + self.collection.name + ' Transportation: ' + self.name)
2024-07-27 19:04:55 -04:00
if self.user_id != self.collection.user_id:
raise ValidationError('Transportations must be associated with collections owned by the same user. Collection owner: ' + self.collection.user_id.username + ' Transportation owner: ' + self.user_id.username)
def __str__(self):
return self.name
2024-08-03 21:09:49 -04:00
class Note(models.Model):
#id = models.AutoField(primary_key=True)
2024-08-11 09:37:12 -04:00
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
2024-08-03 21:09:49 -04:00
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
name = models.CharField(max_length=200)
content = models.TextField(blank=True, null=True)
2024-08-04 09:56:58 -04:00
links = ArrayField(models.URLField(), blank=True, null=True)
2024-08-03 21:09:49 -04:00
date = models.DateField(blank=True, null=True)
is_public = models.BooleanField(default=False)
collection = models.ForeignKey('Collection', on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
if self.collection:
if self.collection.is_public and not self.is_public:
raise ValidationError('Notes associated with a public collection must be public. Collection: ' + self.collection.name + ' Transportation: ' + self.name)
if self.user_id != self.collection.user_id:
raise ValidationError('Notes must be associated with collections owned by the same user. Collection owner: ' + self.collection.user_id.username + ' Transportation owner: ' + self.user_id.username)
def __str__(self):
return self.name
2024-08-05 16:09:57 -04:00
class Checklist(models.Model):
# id = models.AutoField(primary_key=True)
2024-08-11 09:37:12 -04:00
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
2024-08-05 16:09:57 -04:00
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
name = models.CharField(max_length=200)
date = models.DateField(blank=True, null=True)
is_public = models.BooleanField(default=False)
collection = models.ForeignKey('Collection', on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
if self.collection:
if self.collection.is_public and not self.is_public:
raise ValidationError('Checklists associated with a public collection must be public. Collection: ' + self.collection.name + ' Checklist: ' + self.name)
if self.user_id != self.collection.user_id:
raise ValidationError('Checklists must be associated with collections owned by the same user. Collection owner: ' + self.collection.user_id.username + ' Checklist owner: ' + self.user_id.username)
def __str__(self):
return self.name
class ChecklistItem(models.Model):
#id = models.AutoField(primary_key=True)
2024-08-11 09:37:12 -04:00
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
2024-08-05 16:09:57 -04:00
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
name = models.CharField(max_length=200)
is_checked = models.BooleanField(default=False)
checklist = models.ForeignKey('Checklist', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
if self.checklist.is_public and not self.checklist.is_public:
raise ValidationError('Checklist items associated with a public checklist must be public. Checklist: ' + self.checklist.name + ' Checklist item: ' + self.name)
if self.user_id != self.checklist.user_id:
raise ValidationError('Checklist items must be associated with checklists owned by the same user. Checklist owner: ' + self.checklist.user_id.username + ' Checklist item owner: ' + self.user_id.username)
def __str__(self):
return self.name
2024-08-15 19:36:42 -04:00
@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)
2024-08-15 19:36:42 -04:00
class AdventureImage(models.Model):
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)
image = ResizedImageField(
force_format="WEBP",
quality=75,
upload_to=PathAndRename('images/') # Use the callable class here
)
2024-08-15 19:36:42 -04:00
adventure = models.ForeignKey(Adventure, related_name='images', on_delete=models.CASCADE)
is_primary = models.BooleanField(default=False)
2024-08-15 19:36:42 -04:00
def __str__(self):
return self.image.url
class Attachment(models.Model):
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)
file = models.FileField(upload_to=PathAndRename('attachments/'))
adventure = models.ForeignKey(Adventure, related_name='attachments', on_delete=models.CASCADE)
name = models.CharField(max_length=200, null=True, blank=True)
def __str__(self):
return self.file.url
class Category(models.Model):
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)
name = models.CharField(max_length=200)
display_name = models.CharField(max_length=200)
2024-11-26 15:10:17 -05:00
icon = models.CharField(max_length=200, default='🌍')
class Meta:
verbose_name_plural = 'Categories'
unique_together = ['name', 'user_id']
def clean(self) -> None:
self.name = self.name.lower().strip()
return super().clean()
def __str__(self):
2024-11-26 17:39:10 -05:00
return self.name + ' - ' + self.display_name + ' - ' + self.icon