1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-23 06:49:37 +02:00

Add download adventures as ICS calendar

This commit is contained in:
Sean Morley 2024-12-14 14:37:16 -05:00
parent 4839edde7b
commit 0c27f4b8a4
14 changed files with 99 additions and 12 deletions

View file

@ -1,6 +1,6 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet, AdventureImageViewSet, ReverseGeocodeViewSet, CategoryViewSet
from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet, AdventureImageViewSet, ReverseGeocodeViewSet, CategoryViewSet, IcsCalendarGeneratorViewSet
router = DefaultRouter()
router.register(r'adventures', AdventureViewSet, basename='adventures')
@ -14,6 +14,7 @@ router.register(r'checklists', ChecklistViewSet, basename='checklists')
router.register(r'images', AdventureImageViewSet, basename='images')
router.register(r'reverse-geocode', ReverseGeocodeViewSet, basename='reverse-geocode')
router.register(r'categories', CategoryViewSet, basename='categories')
router.register(r'ics-calendar', IcsCalendarGeneratorViewSet, basename='ics-calendar')
urlpatterns = [

View file

@ -17,6 +17,9 @@ from rest_framework.pagination import PageNumberPagination
from django.shortcuts import get_object_or_404
from rest_framework import status
from django.contrib.auth import get_user_model
from icalendar import Calendar, Event, vText, vCalAddress
from django.http import HttpResponse
from datetime import datetime
User = get_user_model()
@ -1203,4 +1206,57 @@ class ReverseGeocodeViewSet(viewsets.ViewSet):
visited_region.save()
new_region_count += 1
new_regions[region.id] = region.name
return Response({"new_regions": new_region_count, "regions": new_regions})
return Response({"new_regions": new_region_count, "regions": new_regions})
from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from icalendar import Calendar, Event, vText, vCalAddress
from datetime import datetime, timedelta
class IcsCalendarGeneratorViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
@action(detail=False, methods=['get'])
def generate(self, request):
adventures = Adventure.objects.filter(user_id=request.user)
serializer = AdventureSerializer(adventures, many=True)
user = request.user
name = f"{user.first_name} {user.last_name}"
print(serializer.data)
cal = Calendar()
cal.add('prodid', '-//My Adventure Calendar//example.com//')
cal.add('version', '2.0')
for adventure in serializer.data:
if adventure['visits']:
for visit in adventure['visits']:
event = Event()
event.add('summary', adventure['name'])
start_date = datetime.strptime(visit['start_date'], '%Y-%m-%d').date()
end_date = datetime.strptime(visit['end_date'], '%Y-%m-%d').date() + timedelta(days=1) if visit['end_date'] else start_date + timedelta(days=1)
event.add('dtstart', start_date)
event.add('dtend', end_date)
event.add('dtstamp', datetime.now())
event.add('transp', 'TRANSPARENT')
event.add('class', 'PUBLIC')
event.add('created', datetime.now())
event.add('last-modified', datetime.now())
event.add('description', adventure['description'])
if adventure.get('location'):
event.add('location', adventure['location'])
if adventure.get('link'):
event.add('url', adventure['link'])
organizer = vCalAddress(f'MAILTO:{user.email}')
organizer.params['cn'] = vText(name)
event.add('organizer', organizer)
cal.add_component(event)
response = HttpResponse(cal.to_ical(), content_type='text/calendar')
response['Content-Disposition'] = 'attachment; filename=adventures.ics'
return response

View file

@ -15,4 +15,6 @@ gunicorn==23.0.0
qrcode==8.0
# slippers==0.6.2
# django-allauth-ui==1.5.1
# django-widget-tweaks==1.5.0
# django-widget-tweaks==1.5.0
django-ical==1.9.2
icalendar==6.1.0

View file

@ -194,7 +194,8 @@
"adventure_calendar": "Abenteuerkalender",
"emoji_picker": "Emoji-Picker",
"hide": "Verstecken",
"show": "Zeigen"
"show": "Zeigen",
"download_calendar": "Kalender herunterladen"
},
"home": {
"desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit",

View file

@ -217,6 +217,7 @@
"show": "Show",
"hide": "Hide",
"emoji_picker": "Emoji Picker",
"download_calendar": "Download Calendar",
"days": "days",
"activities": {
"general": "General 🌍",

View file

@ -241,7 +241,8 @@
"adventure_calendar": "Calendario de aventuras",
"emoji_picker": "Selector de emojis",
"hide": "Esconder",
"show": "Espectáculo"
"show": "Espectáculo",
"download_calendar": "Descargar Calendario"
},
"worldtravel": {
"all": "Todo",

View file

@ -194,7 +194,8 @@
"adventure_calendar": "Calendrier d'aventure",
"emoji_picker": "Sélecteur d'émoticônes",
"hide": "Cacher",
"show": "Montrer"
"show": "Montrer",
"download_calendar": "Télécharger le calendrier"
},
"home": {
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",

View file

@ -194,7 +194,8 @@
"adventure_calendar": "Calendario delle avventure",
"emoji_picker": "Selettore di emoji",
"hide": "Nascondere",
"show": "Spettacolo"
"show": "Spettacolo",
"download_calendar": "Scarica Calendario"
},
"home": {
"desc_1": "Scopri, pianifica ed esplora con facilità",

View file

@ -194,7 +194,8 @@
"adventure_calendar": "Avonturenkalender",
"emoji_picker": "Emoji-kiezer",
"hide": "Verbergen",
"show": "Show"
"show": "Show",
"download_calendar": "Agenda downloaden"
},
"home": {
"desc_1": "Ontdek, plan en verken met gemak",

View file

@ -241,7 +241,8 @@
"adventure_calendar": "Kalendarz przygód",
"emoji_picker": "Wybór emoji",
"hide": "Ukrywać",
"show": "Pokazywać"
"show": "Pokazywać",
"download_calendar": "Pobierz Kalendarz"
},
"worldtravel": {
"country_list": "Lista krajów",

View file

@ -194,7 +194,8 @@
"adventure_calendar": "Äventyrskalender",
"emoji_picker": "Emoji-väljare",
"hide": "Dölja",
"show": "Visa"
"show": "Visa",
"download_calendar": "Ladda ner kalender"
},
"home": {
"desc_1": "Upptäck, planera och utforska med lätthet",

View file

@ -194,7 +194,8 @@
"adventure_calendar": "冒险日历",
"emoji_picker": "表情符号选择器",
"hide": "隐藏",
"show": "展示"
"show": "展示",
"download_calendar": "下载日历"
},
"home": {
"desc_1": "轻松发现、规划和探索",

View file

@ -30,10 +30,18 @@ export const load = (async (event) => {
});
});
let icsFetch = await fetch(`${endpoint}/api/ics-calendar/generate`, {
headers: {
Cookie: `sessionid=${sessionId}`
}
});
let ics_calendar = await icsFetch.text();
return {
props: {
adventures,
dates
dates,
ics_calendar
}
};
}) satisfies PageServerLoad;

View file

@ -14,6 +14,10 @@
let adventures = data.props.adventures;
let dates = data.props.dates;
let icsCalendar = data.props.ics_calendar;
// turn the ics calendar into a data URL
let icsCalendarDataUrl = URL.createObjectURL(new Blob([icsCalendar], { type: 'text/calendar' }));
let plugins = [TimeGrid, DayGrid];
let options = {
view: 'dayGridMonth',
@ -24,3 +28,10 @@
<h1 class="text-center text-2xl font-bold">{$t('adventures.adventure_calendar')}</h1>
<Calendar {plugins} {options} />
<!-- download calendar -->
<div class="flex items-center justify-center mt-4">
<a href={icsCalendarDataUrl} download="adventures.ics" class="btn btn-primary"
>Download Calendar</a
>
</div>