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

notes beta

This commit is contained in:
Sean Morley 2024-08-03 21:09:49 -04:00
parent b8994a531f
commit 4f1ad09470
8 changed files with 172 additions and 10 deletions

View file

@ -1,7 +1,7 @@
import os import os
from django.contrib import admin from django.contrib import admin
from django.utils.html import mark_safe from django.utils.html import mark_safe
from .models import Adventure, Collection, Transportation from .models import Adventure, Collection, Transportation, Note
from worldtravel.models import Country, Region, VisitedRegion from worldtravel.models import Country, Region, VisitedRegion
@ -75,6 +75,7 @@ admin.site.register(Country, CountryAdmin)
admin.site.register(Region, RegionAdmin) admin.site.register(Region, RegionAdmin)
admin.site.register(VisitedRegion) admin.site.register(VisitedRegion)
admin.site.register(Transportation) admin.site.register(Transportation)
admin.site.register(Note)
admin.site.site_header = 'AdventureLog Admin' admin.site.site_header = 'AdventureLog Admin'
admin.site.site_title = 'AdventureLog Admin Site' admin.site.site_title = 'AdventureLog Admin Site'

View file

@ -0,0 +1,35 @@
# Generated by Django 5.0.7 on 2024-08-04 01:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('adventures', '0013_alter_adventure_type_transportation'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='transportation',
name='type',
field=models.CharField(choices=[('car', 'Car'), ('plane', 'Plane'), ('train', 'Train'), ('bus', 'Bus'), ('boat', 'Boat'), ('bike', 'Bike'), ('walking', 'Walking'), ('other', 'Other')], max_length=100),
),
migrations.CreateModel(
name='Note',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=200)),
('content', models.TextField(blank=True, null=True)),
('date', models.DateTimeField(blank=True, null=True)),
('is_public', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('collection', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='adventures.collection')),
('user_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 5.0.7 on 2024-08-04 01:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('adventures', '0014_alter_transportation_type_note'),
]
operations = [
migrations.AlterField(
model_name='note',
name='date',
field=models.DateField(blank=True, null=True),
),
]

View file

@ -82,8 +82,6 @@ class Collection(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
# make a class for transportaiotn and make it linked to a collection. Make it so it can be used for different types of transportations like car, plane, train, etc.
class Transportation(models.Model): class Transportation(models.Model):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
user_id = models.ForeignKey( user_id = models.ForeignKey(
@ -111,3 +109,25 @@ class Transportation(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
class Note(models.Model):
id = models.AutoField(primary_key=True)
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)
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

View file

@ -1,5 +1,5 @@
import os import os
from .models import Adventure, Collection, Transportation from .models import Adventure, Collection, Note, Transportation
from rest_framework import serializers from rest_framework import serializers
class AdventureSerializer(serializers.ModelSerializer): class AdventureSerializer(serializers.ModelSerializer):
@ -57,15 +57,45 @@ class TransportationSerializer(serializers.ModelSerializer):
validated_data['user_id'] = self.context['request'].user validated_data['user_id'] = self.context['request'].user
return super().create(validated_data) return super().create(validated_data)
class NoteSerializer(serializers.ModelSerializer):
class Meta:
model = Note
fields = [
'id', 'user_id', 'name', 'content', 'date',
'is_public', 'collection', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def validate(self, data):
# Check if the collection is public and the transportation 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(
'Notes associated with a public collection must be public.'
)
# Check if the user owns the collection
request = self.context.get('request')
if request and collection and collection.user_id != request.user:
raise serializers.ValidationError(
'Notes must be associated with collections owned by the same user.'
)
return data
def create(self, validated_data):
# Set the user_id to the current user
validated_data['user_id'] = self.context['request'].user
return super().create(validated_data)
class CollectionSerializer(serializers.ModelSerializer): class CollectionSerializer(serializers.ModelSerializer):
adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set') adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set')
transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set') transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set')
notes = NoteSerializer(many=True, read_only=True, source='note_set')
class Meta: class Meta:
model = Collection model = Collection
# fields are all plus the adventures field # fields are all plus the adventures field
fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations'] fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes']

View file

@ -4,7 +4,7 @@ from rest_framework.decorators import action
from rest_framework import viewsets from rest_framework import viewsets
from django.db.models.functions import Lower from django.db.models.functions import Lower
from rest_framework.response import Response from rest_framework.response import Response
from .models import Adventure, Collection, Transportation from .models import Adventure, Collection, Transportation, Note
from worldtravel.models import VisitedRegion, Region, Country from worldtravel.models import VisitedRegion, Region, Country
from .serializers import AdventureSerializer, CollectionSerializer, TransportationSerializer from .serializers import AdventureSerializer, CollectionSerializer, TransportationSerializer
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
@ -279,6 +279,9 @@ class CollectionViewSet(viewsets.ModelViewSet):
# do the same for transportations # do the same for transportations
Transportation.objects.filter(collection=instance).update(is_public=new_public_status) Transportation.objects.filter(collection=instance).update(is_public=new_public_status)
# do the same for notes
Note.objects.filter(collection=instance).update(is_public=new_public_status)
# Log the action (optional) # Log the action (optional)
action = "public" if new_public_status else "private" action = "public" if new_public_status else "private"
print(f"Collection {instance.id} and its adventures were set to {action}") print(f"Collection {instance.id} and its adventures were set to {action}")
@ -313,6 +316,10 @@ class CollectionViewSet(viewsets.ModelViewSet):
Prefetch('transportation_set', queryset=Transportation.objects.filter( Prefetch('transportation_set', queryset=Transportation.objects.filter(
Q(is_public=True) | Q(user_id=self.request.user.id) Q(is_public=True) | Q(user_id=self.request.user.id)
)) ))
).prefetch_related(
Prefetch('note_set', queryset=Note.objects.filter(
Q(is_public=True) | Q(user_id=self.request.user.id)
))
) )
return self.apply_sorting(adventures) return self.apply_sorting(adventures)

View file

@ -67,6 +67,7 @@ export type Collection = {
start_date?: string; start_date?: string;
end_date?: string; end_date?: string;
transportations?: Transportation[]; transportations?: Transportation[];
notes?: Note[];
}; };
export type OpenStreetMapPlace = { export type OpenStreetMapPlace = {
@ -103,3 +104,15 @@ export type Transportation = {
created_at: string; // ISO 8601 date string created_at: string; // ISO 8601 date string
updated_at: string; // ISO 8601 date string updated_at: string; // ISO 8601 date string
}; };
export type Note = {
id: number;
user_id: number;
name: string;
content: string | null;
date: string | null; // ISO 8601 date string
is_public: boolean;
collection: Collection | null;
created_at: string; // ISO 8601 date string
updated_at: string; // ISO 8601 date string
};

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Adventure, Collection, Transportation } from '$lib/types'; import type { Adventure, Collection, Note, Transportation } from '$lib/types';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
@ -17,12 +17,14 @@
import NewTransportation from '$lib/components/NewTransportation.svelte'; import NewTransportation from '$lib/components/NewTransportation.svelte';
export let data: PageData; export let data: PageData;
console.log(data);
let collection: Collection; let collection: Collection;
let adventures: Adventure[] = []; let adventures: Adventure[] = [];
let numVisited: number = 0; let numVisited: number = 0;
let transportations: Transportation[] = []; let transportations: Transportation[] = [];
let notes: Note[] = [];
let numberOfDays: number = NaN; let numberOfDays: number = NaN;
@ -52,6 +54,9 @@
if (collection.transportations) { if (collection.transportations) {
transportations = collection.transportations; transportations = collection.transportations;
} }
if (collection.notes) {
notes = collection.notes;
}
}); });
function deleteAdventure(event: CustomEvent<number>) { function deleteAdventure(event: CustomEvent<number>) {
@ -108,6 +113,28 @@
return groupedTransportations; return groupedTransportations;
} }
function groupNotesByDate(notes: Note[], startDate: Date): Record<string, Note[]> {
const groupedNotes: Record<string, Note[]> = {};
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
currentDate.setDate(startDate.getDate() + i);
const dateString = currentDate.toISOString().split('T')[0];
groupedNotes[dateString] = [];
}
notes.forEach((note) => {
if (note.date) {
const noteDate = new Date(note.date).toISOString().split('T')[0];
if (groupedNotes[noteDate]) {
groupedNotes[noteDate].push(note);
}
}
});
return groupedNotes;
}
function createAdventure(event: CustomEvent<Adventure>) { function createAdventure(event: CustomEvent<Adventure>) {
adventures = [event.detail, ...adventures]; adventures = [event.detail, ...adventures];
isShowingCreateModal = false; isShowingCreateModal = false;
@ -454,6 +481,17 @@
/> />
{/each} {/each}
{/if} {/if}
{#if notes.length > 0}
{#each notes as note}
{#if note.date && new Date(note.date).toISOString().split('T')[0] === dateString}
<div class="bg-base-300 p-4 rounded-lg w-full">
<p class="text-lg font-semibold">{note.name}</p>
<p class="text-md">{note.date}</p>
<p>{note.content}</p>
</div>
{/if}
{/each}
{/if}
{#if dayAdventures.length == 0 && dayTransportations.length == 0} {#if dayAdventures.length == 0 && dayTransportations.length == 0}
<p class="text-center text-lg mt-2"> <p class="text-center text-lg mt-2">
No adventures or transportaions planned for this day. No adventures or transportaions planned for this day.