GCal Mapping: Callback mechanism using channels
This commit is contained in:
parent
ba0cde09c1
commit
fcb04058b5
|
@ -0,0 +1 @@
|
||||||
|
{"_module": "oauth2client.client", "token_expiry": "2014-04-19T18:37:13Z", "access_token": "ya29.1.AADtN_UNi3N8wJ5AiTZ3ced1ho7-ZOhe9cV0TqJ7lByEy_h5SHGm0EuZBSWVOQ3q3g", "token_uri": "https://accounts.google.com/o/oauth2/token", "invalid": false, "token_response": {"access_token": "ya29.1.AADtN_UNi3N8wJ5AiTZ3ced1ho7-ZOhe9cV0TqJ7lByEy_h5SHGm0EuZBSWVOQ3q3g", "token_type": "Bearer", "expires_in": 3600}, "client_id": "34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com", "id_token": null, "client_secret": "y4t9XBrJdCODPTO5UvtONWWn", "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", "_class": "OAuth2Credentials", "refresh_token": "1/7-6-m_lLAKX8IeD7OuGtkcIiprty_nZUSxhMunSC5b0", "user_agent": null}
|
|
@ -183,6 +183,8 @@ GCAL_COUPLING = {
|
||||||
'clientId' : '34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com',
|
'clientId' : '34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com',
|
||||||
'client_secret' : 'y4t9XBrJdCODPTO5UvtONWWn',
|
'client_secret' : 'y4t9XBrJdCODPTO5UvtONWWn',
|
||||||
'credentials_file' : PROJECT_PATH + '/calendarCredentials.dat',
|
'credentials_file' : PROJECT_PATH + '/calendarCredentials.dat',
|
||||||
|
'push_url' : "https://test.bauer.technology/eventplanner_gcal/gcalApiCallback",
|
||||||
|
#'push_url' : "https://test.bauer.technology/callbackTest2.php",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ import musicians.urls
|
||||||
#import imagestore.urls
|
#import imagestore.urls
|
||||||
import website.urls
|
import website.urls
|
||||||
import scoremanager.urls
|
import scoremanager.urls
|
||||||
|
import eventplanner_gcal.urls
|
||||||
|
|
||||||
|
|
||||||
from eventplanner_gcal.views import *
|
|
||||||
|
|
||||||
import settings
|
import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
@ -18,13 +19,13 @@ from django.conf.urls.static import static
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^', include(website.urls) ),
|
url(r'^', include( website.urls ) ),
|
||||||
url(r'^events/', include( eventplanner.urls.urlpatterns) ),
|
url(r'^events/', include( eventplanner.urls.urlpatterns) ),
|
||||||
url(r'^musicians/', include( musicians.urls.urlpatterns) ),
|
url(r'^musicians/', include( musicians.urls.urlpatterns) ),
|
||||||
url(r'^scores/', include( scoremanager.urls.urlpatterns) ),
|
url(r'^scores/', include( scoremanager.urls.urlpatterns) ),
|
||||||
url(r'^messages/$', simpleforum.views.message_view ),
|
url(r'^messages/$', simpleforum.views.message_view ),
|
||||||
url(r'^admin/', include(admin.site.urls) ),
|
url(r'^admin/', include( admin.site.urls ) ),
|
||||||
url(r'^location_field/', include('location_field.urls')),
|
url(r'^location_field/', include( 'location_field.urls' ) ),
|
||||||
|
url(r'^eventplanner_gcal/', include( eventplanner_gcal.urls) ),
|
||||||
#url(r'^gallery/', include(imagestore.urls, namespace='imagestore') ),
|
#url(r'^gallery/', include(imagestore.urls, namespace='imagestore') ),
|
||||||
url(r'^eventSync/', runSync )
|
|
||||||
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}/css/bootstrap-overrides.css"> {% endaddtoblock %}
|
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}/css/bootstrap-overrides.css"> {% endaddtoblock %}
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}/css/theme.css" type="text/css"> {% endaddtoblock %}
|
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}/css/theme.css" type="text/css"> {% endaddtoblock %}
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}/css/index.css" type="text/css" media="screen" /> {% endaddtoblock %}
|
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}/css/index.css" type="text/css" media="screen" /> {% endaddtoblock %}
|
||||||
{% addtoblock "css" strip %} <link href='http://fonts.googleapis.com/css?family=Lato:300,400,700,900,300italic,400italic,700italic,900italic' rel='stylesheet' type='text/css'>{% endaddtoblock %}
|
{% addtoblock "css" strip %} <link href='https://fonts.googleapis.com/css?family=Lato:300,400,700,900,300italic,400italic,700italic,900italic' rel='stylesheet' type='text/css'>{% endaddtoblock %}
|
||||||
|
|
||||||
{% addtoblock "css" %}
|
{% addtoblock "css" %}
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
|
|
|
@ -116,8 +116,6 @@ class Event ( models.Model ):
|
||||||
return nextEvent
|
return nextEvent
|
||||||
|
|
||||||
|
|
||||||
before_read = Signal()
|
|
||||||
|
|
||||||
|
|
||||||
class EventParticipation( models.Model ):
|
class EventParticipation( models.Model ):
|
||||||
OPTIONS = ( ('?' , _('?' )),
|
OPTIONS = ( ('?' , _('?' )),
|
||||||
|
@ -147,10 +145,6 @@ class EventParticipation( models.Model ):
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def raiseBeforeReadSignal():
|
|
||||||
before_read.send( sender=EventParticipation )
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isMember( user ):
|
def isMember( user ):
|
||||||
return user.has_perm('eventplanner.member')
|
return user.has_perm('eventplanner.member')
|
||||||
|
|
|
@ -6,13 +6,13 @@ from eventplanner.views import events_grid, eventplanning,event_api,EventUpdate,
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', eventplanning ),
|
url(r'^$', eventplanning ),
|
||||||
url(r'^grid$', events_grid ),
|
url(r'^grid$', events_grid ),
|
||||||
url(r'^planning$', eventplanning ),
|
url(r'^planning$', eventplanning ),
|
||||||
url(r'^(?P<pk>\d+)$', permission_required('eventplanner.change_event')( EventUpdate.as_view() ) ),
|
url(r'^(?P<pk>\d+)$', permission_required('eventplanner.change_event')( EventUpdate.as_view() ) ),
|
||||||
url(r'^add$', permission_required('eventplanner.add_event')( EventCreate.as_view() ) ),
|
url(r'^add$', permission_required('eventplanner.add_event' )( EventCreate.as_view() ) ),
|
||||||
url(r'^(?P<pk>\d+)/delete$', permission_required('eventplanner.delete_event')( deleteEvent ) ),
|
url(r'^(?P<pk>\d+)/delete$', permission_required('eventplanner.delete_event')( deleteEvent ) ),
|
||||||
url(r'^api/', event_api, name="event_api" ),
|
url(r'^api/', event_api, name="event_api" ),
|
||||||
url(r'^api/(\w+)/$', event_api, name="event_api_per_user" ),
|
url(r'^api/(\w+)/$', event_api, name="event_api_per_user" ),
|
||||||
url(r'^api/(\w+)/(\d+)$', event_api, name="event_api_per_user_event"),
|
url(r'^api/(\w+)/(\d+)$', event_api, name="event_api_per_user_event" ),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ def eventplanning( request ):
|
||||||
"""
|
"""
|
||||||
View for a specific user, to edit his events
|
View for a specific user, to edit his events
|
||||||
"""
|
"""
|
||||||
EventParticipation.raiseBeforeReadSignal()
|
|
||||||
# non-members see the grid - but cannot edit anything
|
# non-members see the grid - but cannot edit anything
|
||||||
if not EventParticipation.isMember( request.user ):
|
if not EventParticipation.isMember( request.user ):
|
||||||
return events_grid(request)
|
return events_grid(request)
|
||||||
|
@ -78,8 +77,6 @@ def eventplanning( request ):
|
||||||
|
|
||||||
|
|
||||||
def events_grid( request ):
|
def events_grid( request ):
|
||||||
EventParticipation.raiseBeforeReadSignal()
|
|
||||||
|
|
||||||
usernames = [ u.username for u in EventParticipation.members() ]
|
usernames = [ u.username for u in EventParticipation.members() ]
|
||||||
|
|
||||||
all_future_events = list ( Event.objects.filter( date__gte = datetime.date.today() ).order_by( 'date') )
|
all_future_events = list ( Event.objects.filter( date__gte = datetime.date.today() ).order_by( 'date') )
|
||||||
|
@ -135,7 +132,6 @@ class EventUpdate( UpdateView ):
|
||||||
|
|
||||||
|
|
||||||
class EventCreate( CreateView ):
|
class EventCreate( CreateView ):
|
||||||
|
|
||||||
form_class = EventForm
|
form_class = EventForm
|
||||||
model = Event
|
model = Event
|
||||||
template_name_suffix = "_update_form"
|
template_name_suffix = "_update_form"
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
|
from eventplanner_gcal.signals import checkGCalSubscription
|
||||||
|
|
||||||
|
class Command(NoArgsCommand):
|
||||||
|
help = 'Checks if the GCal notification channel is still active'
|
||||||
|
def handle_noargs(self, **options):
|
||||||
|
print ( "Checking Subscription")
|
||||||
|
checkGCalSubscription()
|
|
@ -1,5 +1,5 @@
|
||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
from eventplanner_gcal.models import deleteAllGCalEvents
|
from eventplanner_gcal.signals import deleteAllGCalEvents
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = 'Delete all events in the google calendar created by this app'
|
help = 'Delete all events in the google calendar created by this app'
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
|
from eventplanner_gcal.signals import stopAllGCalSubscriptions
|
||||||
|
|
||||||
|
class Command(NoArgsCommand):
|
||||||
|
help = 'Stops all GCal subscriptions'
|
||||||
|
def handle_noargs(self, **options):
|
||||||
|
stopAllGCalSubscriptions()
|
|
@ -1,6 +1,6 @@
|
||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
from eventplanner_gcal.models import syncGCalEvents
|
from eventplanner_gcal.signals import syncGCalEvents
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = 'Synchronize Google Calendar with locally stored Events'
|
help = 'Synchronize Google Calendar with locally stored Events'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from apiclient.channel import new_webhook_channel, Channel
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from eventplanner.models import Event, EventParticipation
|
from eventplanner.models import Event, EventParticipation
|
||||||
|
|
||||||
|
@ -11,17 +12,56 @@ from django.conf import settings
|
||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GCalMapping( models.Model ):
|
class GCalMapping( models.Model ):
|
||||||
gcal_id = models.CharField( max_length=64 )
|
gcal_id = models.CharField( max_length=64 )
|
||||||
event = models.OneToOneField( Event, primary_key=True )
|
event = models.OneToOneField( Event, primary_key=True )
|
||||||
|
|
||||||
|
|
||||||
|
class GCalPushChannel( models.Model ):
|
||||||
|
"""This table has either zero or one entry. Required to store if a channel already exists,
|
||||||
|
when it expires, and how to stop (renew) the channel
|
||||||
|
"""
|
||||||
|
|
||||||
|
id = models.CharField( max_length=128, primary_key=True )
|
||||||
|
address = models.CharField( max_length=256 )
|
||||||
|
token = models.CharField( max_length=128 )
|
||||||
|
resource_id = models.CharField( max_length=128 )
|
||||||
|
expiration = models.IntegerField()
|
||||||
|
|
||||||
|
def toGChannel( self ):
|
||||||
|
return Channel( 'web_hook', self.id, self.token, self.address, self.expiration, resource_id = self.resource_id )
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fromGChannel( gChannel ):
|
||||||
|
return GCalPushChannel( id = gChannel.id,
|
||||||
|
address = gChannel.address,
|
||||||
|
token = gChannel.token,
|
||||||
|
expiration = gChannel.expiration,
|
||||||
|
resource_id= gChannel.resource_id )
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createNew( callbackUrl, service, ttl = None ):
|
||||||
|
gChannel = Channel('web_hook', str(uuid.uuid4()), 'blechreizGcal', callbackUrl, params= { 'ttl' : int(ttl) } )
|
||||||
|
response = service.events().watch( calendarId='primary', body= gChannel.body() ).execute()
|
||||||
|
gChannel.update( response )
|
||||||
|
|
||||||
|
dbChannel = GCalPushChannel.fromGChannel( gChannel )
|
||||||
|
dbChannel.save()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stop( service, gChannel ):
|
||||||
|
channelService = service.channels()
|
||||||
|
channelService.stop( body = gChannel.body() ).execute()
|
||||||
|
GCalPushChannel.fromGChannel( gChannel ).delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,8 +80,8 @@ def init( gcal_settings ):
|
||||||
http=http, developerKey=gcal_settings['developerKey'] )
|
http=http, developerKey=gcal_settings['developerKey'] )
|
||||||
|
|
||||||
|
|
||||||
service = init( settings.GCAL_COUPLING )
|
|
||||||
|
|
||||||
|
service = init( settings.GCAL_COUPLING )
|
||||||
|
|
||||||
|
|
||||||
def createAttendeesObj( event ):
|
def createAttendeesObj( event ):
|
||||||
|
@ -217,7 +257,6 @@ def syncGCalEvents():
|
||||||
|
|
||||||
def syncParticipationFromGoogleToLocal():
|
def syncParticipationFromGoogleToLocal():
|
||||||
allEvents = getAllGCalEvents(fromNow=True)
|
allEvents = getAllGCalEvents(fromNow=True)
|
||||||
|
|
||||||
for e in allEvents:
|
for e in allEvents:
|
||||||
localId = e['extendedProperties']['private']['blechreizID']
|
localId = e['extendedProperties']['private']['blechreizID']
|
||||||
localEvent = Event.objects.get( pk=localId )
|
localEvent = Event.objects.get( pk=localId )
|
||||||
|
@ -236,4 +275,47 @@ def syncParticipationFromGoogleToLocal():
|
||||||
else:
|
else:
|
||||||
logger.error("Unknown response status when mapping gcal event: " + a['responseStatus'] )
|
logger.error("Unknown response status when mapping gcal event: " + a['responseStatus'] )
|
||||||
|
|
||||||
part.save()
|
part.save()
|
||||||
|
|
||||||
|
|
||||||
|
def checkGCalSubscription():
|
||||||
|
global service
|
||||||
|
callbackUrl = settings.GCAL_COUPLING['push_url']
|
||||||
|
|
||||||
|
#timeToLive = 14*24*3600 # how long the channel should be active
|
||||||
|
#renewBeforeExpiry = 2*24*3600 # duration before expiry when channel is renewed
|
||||||
|
timeToLive = 60*5
|
||||||
|
renewBeforeExpiry = 60*3
|
||||||
|
|
||||||
|
# Test if a channel already exists for this callbackURL
|
||||||
|
try:
|
||||||
|
dbChannel = GCalPushChannel.objects.get( address=callbackUrl )
|
||||||
|
gChannel = dbChannel.toGChannel()
|
||||||
|
|
||||||
|
# if expiration time between 0 and two days: stop and create new channel
|
||||||
|
curTime = int( time.time() * 1000)
|
||||||
|
if gChannel.expiration > curTime:
|
||||||
|
# not yet expired
|
||||||
|
if curTime + renewBeforeExpiry*1000 > gChannel.expiration:
|
||||||
|
#will expire in less than "renewBeforeExpiry"
|
||||||
|
print ( "Renewing Google Calendar Subscription: " + callbackUrl )
|
||||||
|
GCalPushChannel.stop( service, gChannel )
|
||||||
|
GCalPushChannel.createNew( callbackUrl, service, timeToLive )
|
||||||
|
else:
|
||||||
|
print ("Channel active until %d " % ( gChannel.expiration, ) )
|
||||||
|
else:
|
||||||
|
print( "Google calendar subscription had expired - getting new subscription" )
|
||||||
|
syncParticipationFromGoogleToLocal()
|
||||||
|
GCalPushChannel.createNew( callbackUrl, service, timeToLive )
|
||||||
|
|
||||||
|
except GCalPushChannel.DoesNotExist:
|
||||||
|
# create new channel and save it in database
|
||||||
|
print ( "No CGalCallback Channel exists yet for: " + callbackUrl )
|
||||||
|
syncParticipationFromGoogleToLocal()
|
||||||
|
GCalPushChannel.createNew( callbackUrl, service, timeToLive )
|
||||||
|
|
||||||
|
|
||||||
|
def stopAllGCalSubscriptions():
|
||||||
|
for dbChannel in GCalPushChannel.objects.all():
|
||||||
|
print("Stopping %s expiry at %d " % ( dbChannel.id, dbChannel.expiration ) )
|
||||||
|
GCalPushChannel.stop( service, dbChannel.toGChannel() )
|
|
@ -1,10 +1,10 @@
|
||||||
from django.db.models.signals import post_save,pre_delete
|
from django.db.models.signals import post_save,pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from eventplanner.models import Event, EventParticipation, before_read
|
from eventplanner.models import Event, EventParticipation
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from eventplanner_gcal.models import createGCalEvent, updateGCalEvent
|
|
||||||
from eventplanner_gcal.models import deleteGCalEvent, syncParticipationFromGoogleToLocal
|
|
||||||
|
|
||||||
|
import eventplanner_gcal.models
|
||||||
|
|
||||||
class SignalLock:
|
class SignalLock:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -25,6 +25,12 @@ class SignalLock:
|
||||||
|
|
||||||
signalLock = SignalLock()
|
signalLock = SignalLock()
|
||||||
|
|
||||||
|
@receiver( post_save, sender=User )
|
||||||
|
def user_changed( **kwargs ):
|
||||||
|
if not signalLock.isLocked():
|
||||||
|
with signalLock:
|
||||||
|
eventplanner_gcal.models.syncGCalEvents()
|
||||||
|
|
||||||
|
|
||||||
@receiver( post_save,sender= Event)
|
@receiver( post_save,sender= Event)
|
||||||
def event_post_save_handler( **kwargs):
|
def event_post_save_handler( **kwargs):
|
||||||
|
@ -34,10 +40,10 @@ def event_post_save_handler( **kwargs):
|
||||||
created = kwargs['created']
|
created = kwargs['created']
|
||||||
if created:
|
if created:
|
||||||
print("Creating Gcal event")
|
print("Creating Gcal event")
|
||||||
createGCalEvent( event ).execute()
|
eventplanner_gcal.models.createGCalEvent( event ).execute()
|
||||||
else:
|
else:
|
||||||
print( "Updating Gcal event")
|
print( "Updating Gcal event")
|
||||||
updateGCalEvent( event ).execute()
|
eventplanner_gcal.models.updateGCalEvent( event ).execute()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +53,7 @@ def event_pre_delete_handler( **kwargs):
|
||||||
with signalLock:
|
with signalLock:
|
||||||
event = kwargs['instance']
|
event = kwargs['instance']
|
||||||
print ("Deleting GCAL event")
|
print ("Deleting GCAL event")
|
||||||
deleteGCalEvent( event ).execute()
|
eventplanner_gcal.models.deleteGCalEvent( event ).execute()
|
||||||
|
|
||||||
|
|
||||||
@receiver( post_save, sender=EventParticipation )
|
@receiver( post_save, sender=EventParticipation )
|
||||||
|
@ -56,12 +62,32 @@ def participation_post_save_handler( **kwargs):
|
||||||
with signalLock:
|
with signalLock:
|
||||||
participation = kwargs['instance']
|
participation = kwargs['instance']
|
||||||
print("Participation post save -> update gcal")
|
print("Participation post save -> update gcal")
|
||||||
updateGCalEvent( participation.event ).execute()
|
eventplanner_gcal.models.updateGCalEvent( participation.event ).execute()
|
||||||
|
|
||||||
|
|
||||||
@receiver( before_read, sender=EventParticipation )
|
|
||||||
def participation_before_read_handler( **kwargs):
|
|
||||||
|
# -------------- For management commands ------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def stopAllGCalSubscriptions():
|
||||||
if not signalLock.isLocked():
|
if not signalLock.isLocked():
|
||||||
with signalLock:
|
with signalLock:
|
||||||
print("SyncParticipation from google")
|
eventplanner_gcal.models.stopAllGCalSubscriptions()
|
||||||
syncParticipationFromGoogleToLocal()
|
|
||||||
|
def checkGCalSubscription():
|
||||||
|
if not signalLock.isLocked():
|
||||||
|
with signalLock:
|
||||||
|
eventplanner_gcal.models.checkGCalSubscription()
|
||||||
|
|
||||||
|
|
||||||
|
def deleteAllGCalEvents():
|
||||||
|
if not signalLock.isLocked():
|
||||||
|
with signalLock:
|
||||||
|
eventplanner_gcal.models.deleteAllGCalEvents()
|
||||||
|
|
||||||
|
def syncGCalEvents():
|
||||||
|
if not signalLock.isLocked():
|
||||||
|
with signalLock:
|
||||||
|
eventplanner_gcal.models.syncGCalEvents()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from views import runSync, gcalApiCallback
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^runSync$', runSync ),
|
||||||
|
url(r'^gcalApiCallback$', gcalApiCallback ),
|
||||||
|
)
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from eventplanner_gcal.models import syncGCalEvents, syncParticipationFromGoogleToLocal
|
||||||
|
|
||||||
from eventplanner_gcal.models import syncGCalEvents
|
|
||||||
|
|
||||||
def runSync( request ):
|
def runSync( request ):
|
||||||
syncGCalEvents()
|
syncGCalEvents()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
|
def gcalApiCallback( request ):
|
||||||
|
syncParticipationFromGoogleToLocal()
|
||||||
|
print ( "gcalApiCallback called" )
|
||||||
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
<li><a href="/events"> Termine</a></li>
|
<li><a href="/events"> Termine</a></li>
|
||||||
<li><a href="/messages">Forum</a></li>
|
<li><a href="/messages">Forum</a></li>
|
||||||
<li><a href="/musicians">Adressbuch</a></li>
|
<li><a href="/musicians">Adressbuch</a></li>
|
||||||
<li><a href="/scores"> Noten</a></li>
|
<!-- <li><a href="/scores"> Noten</a></li> -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
|
Loading…
Reference in New Issue