From 537e5dd8a6ca8291fd794b7850352311e3f84dfe Mon Sep 17 00:00:00 2001 From: Signior-X Date: Sun, 9 Oct 2022 01:10:13 +0530 Subject: [PATCH] Create Calender Event done --- events/admin.py | 2 +- events/calender_utils.py | 172 ++++++++++++++++++ events/forms.py | 2 +- ...003_event_description_alter_event_venue.py | 23 +++ events/migrations/0004_alter_event_name.py | 18 ++ events/models.py | 16 +- events/templates/events/change_list.html | 2 +- events/urls.py | 4 +- events/views.py | 87 +++++++-- 9 files changed, 299 insertions(+), 27 deletions(-) create mode 100644 events/calender_utils.py create mode 100644 events/migrations/0003_event_description_alter_event_venue.py create mode 100644 events/migrations/0004_alter_event_name.py diff --git a/events/admin.py b/events/admin.py index 1b6f31a..0405db2 100644 --- a/events/admin.py +++ b/events/admin.py @@ -12,7 +12,7 @@ # Register your models here. class EventAdmin(admin.ModelAdmin): - list_display = ['club','name','day', 'start_time', 'end_time', 'venue'] + list_display = ['club','name', 'day', 'start_time', 'end_time', 'venue', 'description', 'event_link'] change_list_template = 'admin/events/change_list.html' def changelist_view(self, request, extra_context=None): diff --git a/events/calender_utils.py b/events/calender_utils.py new file mode 100644 index 0000000..34ba2d4 --- /dev/null +++ b/events/calender_utils.py @@ -0,0 +1,172 @@ +from google.oauth2.credentials import Credentials +from googleapiclient.discovery import build + + +class CalenderEventsUtil(): + scopes = ["https://www.googleapis.com/auth/calendar"] + + # TODO : Take these values from environment + client_id = "" + client_secret = "" + token_uri = "https://oauth2.googleapis.com/token" + + # User related OAuth tokens + access_token = None + refresh_token = None + + def __init__(self, access_token, refresh_token): + self.access_token = access_token + self.refresh_token = refresh_token + + def create_event_data( + self, + summary, + location, + description, + start_data_time, + end_date_time, + attendees_emails, + reminders_default=False, + timezone='Asia/Kolkata'): + """ + Create event data dict that allows google calender to create events + DateTime format Example - '2022-10-06T17:30:00' + attendies - emails for attendies + """ + if location is None: + location = '' + + if description is None: + description = '' + + reminders = {'useDefault': True} + + if not reminders_default: + reminders = { + 'useDefault': False, + 'overrides': [ + {'method': 'email', 'minutes': 24 * 60}, + {'method': 'popup', 'minutes': 10}, + ]} + + attendees_list = [] + if attendees_emails: + for attendee in attendees_emails: + attendees_list.append({'email': attendee}) + + return { + 'summary': summary, + 'location': location, + 'description': description, + 'start': { + 'dateTime': start_data_time, + 'timeZone': timezone, + }, + 'end': { + 'dateTime':end_date_time, + 'timeZone': timezone, + }, + 'attendees': attendees_list, + 'reminders': reminders + } + + def get_calender_service(self): + ''' Uses credentials from global to create credential service ''' + + creds = Credentials( + self.access_token, + refresh_token=self.refresh_token, + token_uri=self.token_uri, + client_id=self.client_id, + client_secret=self.client_secret, + scopes=self.scopes + ) + + service = build('calendar', 'v3', credentials=creds) + return service + + def create_calender_event(self, event_data): + ''' + Create calender event + @params event_data - The event dict as comes form `create_event_data` + ''' + + try: + service = self.get_calender_service() + event = service.events().insert(calendarId='primary', body=event_data).execute() + event_link = event.get('htmlLink') + event_id = event.get('id') + + print('Event created: ', event_link, event_id) + return { + 'success': True, + 'event_link': event_link, + 'event_id': event_id + } + + except Exception as e: + print("Exception came while creating event: ", e) + return { + 'success': False, + 'error': e + } + + def retrieve_calender_event(self, event_id): + try: + service = self.get_calender_service() + event = service.events().get(calendarId='primary', eventId=event_id).execute() + + return { + 'success': True, + 'event_link': event.get('htmlLink'), + 'event_id': event_id, + 'summary': event.get('summary') + } + + except Exception as e: + print("Exception came while creating event: ", e) + return { + 'success': False, + 'error': e + } + + def update_calender_event(self, event_id, event_data): + # First retrieve the event from the API. + try: + service = self.get_calender_service() + event = service.events().get(calendarId='primary', eventId=event_id).execute() + + for key in event_data: + event[key] = event_data[key] + + updated_event = service.events().update(calendarId='primary', eventId=event['id'], body=event).execute() + return { + 'success': True, + 'event_link': updated_event.get('htmlLink'), + 'event_id': event_id + } + + except Exception as e: + print("Exception came while creating event: ", e) + return { + 'success': False, + 'error': e + } + + def delete_calender_event(self, event_id): + ''' Deletes the specified calender event from google calender ''' + + try: + service = self.get_calender_service() + service.events().delete(calendarId='primary', eventId=event_id).execute() + + return { + 'success': True, + 'event_id': event_id + } + except Exception as e: + print("Exception came while deleting the event: ", e) + return { + 'success': False, + 'error': e + } diff --git a/events/forms.py b/events/forms.py index f41bdc7..2f0a53b 100644 --- a/events/forms.py +++ b/events/forms.py @@ -14,7 +14,7 @@ class TimeInput(forms.TimeInput): class EventForm(forms.ModelForm): class Meta: model = Event - fields = ('name', 'day', 'start_time', 'end_time', 'venue', 'overlap') + fields = ('name', 'venue', 'description', 'day', 'start_time', 'end_time', 'overlap') widgets = { 'day': DateInput(), 'start_time':TimeInput(), diff --git a/events/migrations/0003_event_description_alter_event_venue.py b/events/migrations/0003_event_description_alter_event_venue.py new file mode 100644 index 0000000..dcdd3e5 --- /dev/null +++ b/events/migrations/0003_event_description_alter_event_venue.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.2 on 2022-10-08 18:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0002_rename_event_id_event_id'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='description', + field=models.TextField(blank=True, null=True, verbose_name='Description of the Event'), + ), + migrations.AlterField( + model_name='event', + name='venue', + field=models.CharField(blank=True, default='TBA', max_length=100, null=True, verbose_name='Venue or location'), + ), + ] diff --git a/events/migrations/0004_alter_event_name.py b/events/migrations/0004_alter_event_name.py new file mode 100644 index 0000000..534db61 --- /dev/null +++ b/events/migrations/0004_alter_event_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.2 on 2022-10-08 18:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0003_event_description_alter_event_venue'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='name', + field=models.CharField(max_length=200, null=True, verbose_name='Event Details'), + ), + ] diff --git a/events/models.py b/events/models.py index 0b59f8b..5e0f95b 100644 --- a/events/models.py +++ b/events/models.py @@ -12,13 +12,25 @@ def_start=datetime.now().replace(hour=0,minute=0) class Event(models.Model): club= models.CharField(u'Club name', max_length=100, blank=False, null=True) - name = models.TextField(u'Event Details', null=True,blank=False) + + # Maps to summary + name = models.CharField(u'Event Details', max_length=200, null=True, blank=False) + + # Maps to start_date_time and end_date_time day = models.DateField(u'Day of the event') start_time = models.TimeField(u'Starting time') end_time = models.TimeField(u'Final time') - venue= models.CharField(u'Venue or link',max_length=100, blank=True, null=True, default='TBA') + + # Maps to location + venue = models.CharField(u'Venue or location',max_length=100, blank=True, null=True, default='TBA') + + # Maps to description of the event + description = models.TextField(u'Description of the Event', null=True, blank=True) + + # A check if overlap should be allowed overlap = models.BooleanField(u'Allow overlaps',default=False,blank=False,null=False) + # Maps to google calender event id = models.CharField(u'Event Id', max_length=200, primary_key=True) event_link = models.CharField(u'Google Calender Event Link', max_length=200, blank=True, null=True) diff --git a/events/templates/events/change_list.html b/events/templates/events/change_list.html index 7193151..96211eb 100644 --- a/events/templates/events/change_list.html +++ b/events/templates/events/change_list.html @@ -71,7 +71,7 @@

Events

diff --git a/events/urls.py b/events/urls.py index cea6f6a..17001b9 100644 --- a/events/urls.py +++ b/events/urls.py @@ -5,6 +5,6 @@ urlpatterns = [ path('', views.change_list, name='change_list'), path('event/new/', views.event_new, name='event_new'), - path('event//edit/', views.event_edit, name='event_edit'), - path('event//delete/',views.event_delete, name='event_delete') + path('event//edit/', views.event_edit, name='event_edit'), + path('event//delete/',views.event_delete, name='event_delete') ] diff --git a/events/views.py b/events/views.py index e910751..4a7393a 100644 --- a/events/views.py +++ b/events/views.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from http.client import HTTPResponse + +from events.calender_utils import CalenderEventsUtil from.models import Event from django.shortcuts import render, redirect, get_object_or_404 from datetime import datetime, timedelta @@ -30,6 +33,7 @@ def change_list(request): d["day"]=e.day d["name"]=e.name d["venue"]=e.venue + d["event_link"]=e.event_link b=False if i==0: b=True @@ -46,17 +50,18 @@ def change_list(request): usr = request.user.first_name + " " + request.user.last_name except: usr = request.user - return render(request, 'events/change_list.html', {'events':evs_with_changes,'time':curr_time,'date':curr_date,'tomorrow':nextday,'user':usr}) -# Create your views here. + return render(request, 'events/change_list.html', { + 'events': evs_with_changes, + 'time': curr_time, + 'date': curr_date, + 'tomorrow': nextday, + 'user':usr }) -def event_new(request): - if not request.user.is_authenticated: - return redirect('/accounts/google/login') - - result = SocialToken.objects.filter(account__user=request.user, account__provider='google') +def has_calender_access(user): + result = SocialToken.objects.filter(account__user=user, account__provider='google') if len(result) == 0: - return redirect('/accounts/google/login') + return False, None, None result = result.get() refresh_token = result.__dict__.get('token_secret') @@ -66,30 +71,71 @@ def event_new(request): if refresh_token is None: # Check if access token is valid or not if access_token is None: - return redirect('/accounts/google/login') + return False, None, None else: now = datetime.now() if now + timedelta(minutes=30) > expires_at: print("Access token expired!") - return redirect('/accounts/google/login') + return False, access_token, None + + return True, access_token, refresh_token - # We have refresh token, so we are good to go to create calender event +def handle_calender_event(event, access_token, refresh_token, method='create'): + ''' Creates the calender event and returns the values ''' + + print("EVENT OLD: ", event.__dict__) + google_calender = CalenderEventsUtil( + access_token=access_token, + refresh_token=refresh_token + ) + + start_date_time = event.day.isoformat() + "T" + event.start_time.isoformat() + end_date_time = event.day.isoformat() + "T" + event.end_time.isoformat() + + # Now use the above google_calender object to create a new event + # TODO - Allow invites to be added + event_data = google_calender.create_event_data( + str(event.name), + str(event.venue) + ', IIT Mandi', + str(event.description), + start_date_time, + end_date_time, + ['sethpriyam1@gmail.com', 'b19058@students.iitmandi.ac.in'], + ) + + calender_response = google_calender.create_calender_event(event_data) + + print("CALENDER Response", calender_response) + + if calender_response.get('success'): + event.id = calender_response.get('event_id') + event.event_link = calender_response.get('event_link') + + print("EVENT NEW", event.__dict__) + event.save() - # print("result", result.__dict__) - # print("Expires timezone", result.__dict__.get('expires_at')) - # print("RESULT token", result.__dict__.get('token')) - # print("RESULT refresh_token", result.__dict__.get('token_secret')) + # We will add the code to create event to calender + return redirect('change_list') + else: + resp = "Event creation failed on Google Calender" + str(calender_response) + print(resp) + return HTTPResponse(resp) + +def event_new(request): + if not request.user.is_authenticated: + return redirect('/accounts/google/login') + + has_access, access_token, refresh_token = has_calender_access(request.user) + if not has_access: + return redirect('/accounts/google/login') + # We have refresh token, so we are good to go to create calender event if request.method == "POST": form = EventForm(request.POST) if form.is_valid(): event = form.save(commit=False) event.club = request.user.first_name + " " + request.user.last_name - print(event.club) - # event.save() - - # We will add the code to create event to calender - return redirect('change_list') + return handle_calender_event(event, access_token, refresh_token, method='create') else: form = EventForm() return render(request, 'events/event_edit.html', {'form': form}) @@ -98,6 +144,7 @@ def event_edit(request, pk): event = get_object_or_404(Event, pk=pk) if not request.user.is_authenticated or request.user.first_name not in event.club: return redirect('/accounts/google/login') + if request.method == "POST": form = EventForm(request.POST, instance=event) if form.is_valid():