Add RRule parser from 2 tasks and save Recurrence Tasks

This commit is contained in:
sosokker 2023-11-14 22:28:46 +07:00
parent b47e83e5cf
commit d2f75c4fc1
7 changed files with 141 additions and 39 deletions

View File

@ -6,46 +6,22 @@ from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from tasks.utils import get_service
from tasks.utils import get_service, generate_recurrence_rule
from tasks.models import Todo, RecurrenceTask
from tasks.serializers import TodoUpdateSerializer, RecurrenceTaskUpdateSerializer
class GoogleCalendarEventViewset(viewsets.ViewSet):
"""Viewset for list or save Google Calendar Events."""
permission_classes = (IsAuthenticated,)
def __init__(self, *args, **kwargs):
super().__init__()
self.current_time = datetime.now(tz=timezone.utc).isoformat()
self.event_fields = 'items(id,summary,description,created,recurringEventId,updated,start,end)'
self.current_time = (datetime.now(tz=timezone.utc) + timedelta(days=-7)).isoformat()
self.max_time = (datetime.now(tz=timezone.utc) + timedelta(days=7)).isoformat()
self.event_fields = 'items(id,summary,description,created,recurringEventId,updated,start,end,originalStartTime)'
def _validate_serializer(self, serializer):
if serializer.is_valid():
serializer.save()
return Response("Validate Successfully", status=200)
return Response(serializer.errors, status=400)
def post(self, request):
service = get_service(request)
events = service.events().list(calendarId='primary', fields=self.event_fields).execute()
for event in events.get('items', []):
if event.get('recurringEventId'):
continue
event['start_datetime'] = event.get('start').get('dateTime')
event['end_datetime'] = event.get('end').get('dateTime')
event.pop('start')
event.pop('end')
try:
task = Todo.objects.get(google_calendar_id=event['id'])
serializer = TodoUpdateSerializer(instance=task, data=event)
return self._validate_serializer(serializer)
except Todo.DoesNotExist:
serializer = TodoUpdateSerializer(data=event, user=request.user)
return self._validate_serializer(serializer)
def list(self, request, days=7):
max_time = (datetime.now(tz=timezone.utc) + timedelta(days=days)).isoformat()
def _get_google_events(self, request):
"""Get all events from Google Calendar. """
service = get_service(request)
events = []
next_page_token = None
@ -54,21 +30,77 @@ class GoogleCalendarEventViewset(viewsets.ViewSet):
query = service.events().list(
calendarId='primary',
timeMin=self.current_time,
timeMax=max_time,
timeMax=self.max_time,
maxResults=200,
singleEvents=True,
orderBy='startTime',
pageToken=next_page_token,
fields='items(id,summary,description,created,recurringEventId,updated,start,end)',
fields=self.event_fields,
)
page_results = query.execute()
page_events = page_results.get('items', [])
events.extend(page_events)
next_page_token = page_results.get('nextPageToken')
if next_page_token is None:
break
return Response(events, status=200)
return events
def _validate_serializer(self, serializer):
"""Validate serializer and return response."""
if serializer.is_valid():
serializer.save()
return Response("Validate Successfully", status=200)
return Response(serializer.errors, status=400)
def create(self, request, *args, **kwargs):
"""Create a new Google Calendar Event."""
events = self._get_google_events(request)
responses = []
recurrence_task_ids = []
for event in events:
start_datetime = event.get('start', {}).get('dateTime')
end_datetime = event.get('end', {}).get('dateTime')
event['start_datetime'] = start_datetime
event['end_datetime'] = end_datetime
event.pop('start')
event.pop('end')
if (event.get('recurringEventId') in recurrence_task_ids):
continue
if (event.get('recurringEventId') is not None):
originalStartTime = event.get('originalStartTime', {}).get('dateTime')
rrule_text = generate_recurrence_rule(event['start_datetime'], event['end_datetime'], originalStartTime)
event['recurrence'] = rrule_text
event.pop('originalStartTime')
recurrence_task_ids.append(event['recurringEventId'])
try:
task = RecurrenceTask.objects.get(google_calendar_id=event['id'])
serializer = RecurrenceTaskUpdateSerializer(instance=task, data=event)
except RecurrenceTask.DoesNotExist:
serializer = RecurrenceTaskUpdateSerializer(data=event, user=request.user)
responses.append(self._validate_serializer(serializer))
continue
try:
task = Todo.objects.get(google_calendar_id=event['id'])
serializer = TodoUpdateSerializer(instance=task, data=event)
except Todo.DoesNotExist:
serializer = TodoUpdateSerializer(data=event, user=request.user)
responses.append(self._validate_serializer(serializer))
return responses[0] if responses else Response("No events to process", status=200)
def list(self, request):
"""List all Google Calendar Events."""
return Response(self._get_google_events(request), status=200)

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.6 on 2023-11-14 15:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tasks', '0012_habit'),
]
operations = [
migrations.AlterField(
model_name='recurrencetask',
name='recurrence_rule',
field=models.CharField(),
),
]

View File

@ -68,7 +68,7 @@ class Todo(Task):
return self.title
class RecurrenceTask(Task):
recurrence_rule = models.TextField()
recurrence_rule = models.CharField()
def __str__(self) -> str:
return f"{self.title} ({self.recurrence_rule})"

View File

@ -41,7 +41,7 @@ class RecurrenceTaskUpdateSerializer(serializers.ModelSerializer):
description = serializers.CharField(source="notes", required=False)
created = serializers.DateTimeField(source="creation_date")
updated = serializers.DateTimeField(source="last_update")
recurrence = serializers.DateTimeField(source="recurrence_rule")
recurrence = serializers.CharField(source="recurrence_rule")
start_datetime = serializers.DateTimeField(source="start_event", required=False)
end_datetime = serializers.DateTimeField(source="end_event", required=False)

View File

@ -14,6 +14,10 @@ class TodoViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = Todo.objects.filter(user=self.request.user)
return queryset
def get_serializer_class(self):
# Can't add ManytoMany at creation time (Tags)
if self.action == 'create':

View File

@ -1,8 +1,55 @@
from dateutil import rrule
from datetime import datetime
from googleapiclient.discovery import build
from authentications.access_token_cache import get_credential_from_cache_token
def get_service(request):
"""
Get a service that communicates to a Google API.
:param request: Http request object
:return: A Resource object with methods for interacting with the calendar service
"""
credentials = get_credential_from_cache_token(request.user.id)
return build('calendar', 'v3', credentials=credentials)
return build('calendar', 'v3', credentials=credentials)
def _determine_frequency(time_difference):
if time_difference.days >= 365:
return rrule.YEARLY
elif time_difference.days >= 30:
return rrule.MONTHLY
elif time_difference.days >= 7:
return rrule.WEEKLY
elif time_difference.days >= 1:
return rrule.DAILY
elif time_difference.seconds >= 3600:
return rrule.HOURLY
elif time_difference.seconds >= 60:
return rrule.MINUTELY
else:
return rrule.SECONDLY
def generate_recurrence_rule(datetime1: str, datetime2: str, original_start_time: str) -> str:
"""
Generate recurrence rule from
difference between two datetime string.
:param task1: A task object
:param task2: A task object
:return: A recurrence rule string according to ICAL format
"""
start_time1 = datetime.fromisoformat(datetime1)
start_time2 = datetime.fromisoformat(datetime2)
time_difference = start_time2 - start_time1
recurrence_rule = rrule.rrule(
freq=_determine_frequency(time_difference),
dtstart=datetime.fromisoformat(original_start_time),
interval=time_difference.days if time_difference.days > 0 else 1,
)
return str(recurrence_rule)

View File

@ -14,4 +14,5 @@ google_auth_oauthlib>=1.1
google-auth-httplib2>=0.1
django-storages[s3]>=1.14
Pillow>=10.1
drf-spectacular>=0.26
drf-spectacular>=0.26
python-dateutil>=2.8