generate ICS with iCalendar package
This commit is contained in:
parent
f63b0b9cda
commit
0a1442b450
|
@ -320,18 +320,6 @@ class Talk(PonyConfModel):
|
|||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def dtstart(self):
|
||||
return self.start_date.strftime('%Y%m%dT%H%M%SZ')
|
||||
|
||||
@property
|
||||
def dtend(self):
|
||||
return self.end_date.strftime('%Y%m%dT%H%M%SZ')
|
||||
|
||||
@property
|
||||
def dtstamp(self):
|
||||
return self.updated.strftime('%Y%m%dT%H%M%SZ')
|
||||
|
||||
#@property
|
||||
#def materials_name(self):
|
||||
# return basename(self.materials.name)
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.utils.timezone import localtime
|
|||
from django.core.cache import cache
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template.loader import get_template
|
||||
from django.conf import settings
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from copy import deepcopy
|
||||
|
@ -12,6 +13,7 @@ from collections import OrderedDict, namedtuple
|
|||
from itertools import islice
|
||||
from zlib import adler32
|
||||
import xml.etree.ElementTree as ET
|
||||
from icalendar import Calendar as iCalendar, Event as iEvent
|
||||
|
||||
from .models import Conference, Talk, Room
|
||||
|
||||
|
@ -252,7 +254,27 @@ class Program:
|
|||
def _as_ics(self):
|
||||
if not self.initialized:
|
||||
self._lazy_init()
|
||||
return get_template('cfp/planning.ics').render({'site': self.site, 'talks': self.talks})
|
||||
cal = iCalendar()
|
||||
cal.add('prodid', '-//PonyConf.io//PonyConf//FR')
|
||||
cal.add('version', '2.0')
|
||||
cal.add('x-wr-calname', self.conference.name)
|
||||
cal.add('x-wr-timezone', settings.TIME_ZONE)
|
||||
cal.add('calscale', 'GREGORIAN')
|
||||
for talk in self.talks.all():
|
||||
event = iEvent()
|
||||
event.add('dtstart', talk.start_date)
|
||||
if not talk.end_date:
|
||||
continue
|
||||
event.add('dtend', talk.end_date)
|
||||
event.add('dtstamp', talk.updated)
|
||||
event.add('summary', talk.title)
|
||||
if talk.room:
|
||||
event.add('location', talk.room)
|
||||
event.add('status', 'CONFIRMED' if talk.accepted else 'TENTATIVE')
|
||||
event.add('description', talk.description)
|
||||
event.add('uid', '%s/%s' % (self.site.domain, talk.id))
|
||||
cal.add_component(event)
|
||||
return cal.to_ical()
|
||||
|
||||
def render(self, output='html'):
|
||||
if self.cache:
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
BEGIN:VCALENDAR
|
||||
PRODID:-//{{ site.domain }}//{{ site.name }}//FR
|
||||
X-WR-CALNAME:{{ site.name }}
|
||||
X-WR-TIMEZONE:Europe/Paris
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
{% for talk in talks %}
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:{{ talk.dtstamp }}
|
||||
DTSTART:{{ talk.dtstart }}
|
||||
DTEND:{{ talk.dtend }}
|
||||
SUMMARY:{{ talk.title }}
|
||||
LOCATION:{{ talk.room }}
|
||||
STATUS:{% if talk.accepted %}CONFIRMED{% else %}TENTATIVE{% endif %}
|
||||
DESCRIPTION:{{ talk.description|linebreaksbr }}
|
||||
UID:{{ site.domain }}/{{ talk.id }}
|
||||
END:VEVENT{% endfor %}
|
||||
END:VCALENDAR
|
11
cfp/views.py
11
cfp/views.py
|
@ -481,17 +481,8 @@ def schedule(request, program_format, pending, cache, template):
|
|||
elif program_format == 'xml':
|
||||
return HttpResponse(program.render('xml'), content_type="application/xml")
|
||||
elif program_format == 'ics':
|
||||
response = HttpResponse('', content_type='text/plain')
|
||||
response = HttpResponse(program.render('ics'), content_type='text/calendar')
|
||||
response['Content-Disposition'] = 'attachment; filename="planning.ics"'
|
||||
ics = []
|
||||
for line in program.render('ics').split('\n'):
|
||||
line = line.strip().replace('<br />', '')
|
||||
if len(line) < 50:
|
||||
ics.append(line)
|
||||
else: # https://icalendar.org/iCalendar-RFC-5545/3-1-content-lines.html
|
||||
for i in range(ceil(len(line) / 50)):
|
||||
ics.append((' ' if i > 0 else '') + line[i * 50:(i + 1) * 50])
|
||||
response.write('\r\n'.join([line for line in ics if line]).strip() + '\r\n')
|
||||
return response
|
||||
else:
|
||||
raise Http404(_("Format '%s' not available" % program_format))
|
||||
|
|
|
@ -10,3 +10,4 @@ django-colorful
|
|||
markdown
|
||||
bleach
|
||||
chardet
|
||||
icalendar
|
||||
|
|
|
@ -15,7 +15,9 @@ django-crispy-forms==1.6.1
|
|||
django-select2==5.11.1
|
||||
django==1.11.5
|
||||
html5lib==0.999999999 # via bleach
|
||||
icalendar==3.11.7
|
||||
markdown==2.6.9
|
||||
pytz==2017.2 # via django
|
||||
six==1.10.0 # via bleach, django-bower, html5lib
|
||||
python-dateutil==2.6.1 # via icalendar
|
||||
pytz==2017.2 # via django, icalendar
|
||||
six==1.10.0 # via bleach, django-bower, html5lib, python-dateutil
|
||||
webencodings==0.5.1 # via html5lib
|
||||
|
|
Loading…
Reference in New Issue