Add one off events for skipped meetings
Rather than silently removing the skipped meeting from the calendar as is the default behavious of exdate add a one off calendar entry for each skipped meeting. The aim is to show that the slot is generally consumed just that the meeting has been explictly cancelled/skipped. Change-Id: Idd9eb1a04fe9c9661e87978c0128a292bb8e89a4
This commit is contained in:
parent
ddf64aef6e
commit
f02d892ea6
17
README.rst
17
README.rst
@ -258,14 +258,21 @@ meeting should be ommited from the ical schedule
|
||||
VERSION:2.0
|
||||
PRODID:-//yaml2ical agendas//EN
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:CANCELLED: Example Project Meeting (20151026T210000Z)
|
||||
DTSTART;VALUE=DATE-TIME:20151026T210000Z
|
||||
DURATION:PT1H
|
||||
DESCRIPTION:Face 2 Face meeting at some location
|
||||
LOCATION:#openstack-meeting
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:Example Project Meeting
|
||||
DTSTART;VALUE=DATE-TIME:20151005T210000Z
|
||||
DURATION:PT1H
|
||||
EXDATE:20151026T210000
|
||||
DESCRIPTION:Project: Example Project Meeting\\nChair: A. Random Developer
|
||||
\\nDescription: This meeting is a weekly gathering of developers working o
|
||||
n Example project.\\n\\nAgenda URL: https://wiki.openstack.org/wiki/Meetin
|
||||
gs/Example\\nProject URL: https://wiki.openstack.org/wiki/Example
|
||||
EXDATE:20151026T210000Z
|
||||
DESCRIPTION:Project: Example Project Meeting\nChair: A. Random Developer
|
||||
\nDescription: This meeting is a weekly gathering of developers working o
|
||||
n Example project.\n\nAgenda URL: https://wiki.openstack.org/wiki/Meeting
|
||||
s/Example\nProject URL: https://wiki.openstack.org/wiki/Example
|
||||
LOCATION:#openstack-meeting
|
||||
RRULE:FREQ=WEEKLY
|
||||
END:VEVENT
|
||||
|
@ -34,14 +34,27 @@ class Yaml2IcalCalendar(icalendar.Calendar):
|
||||
"""Add this meeting to the calendar."""
|
||||
|
||||
for sch in meeting.schedules:
|
||||
# one Event per iCal file
|
||||
self.add_schedule(meeting, sch)
|
||||
|
||||
def add_schedule(self, meeting, sch, exdate=None):
|
||||
event = icalendar.Event()
|
||||
|
||||
# NOTE(jotan): I think the summary field needs to be unique per
|
||||
# event in an ical file (at least, for it to work with
|
||||
# Google Calendar)
|
||||
|
||||
event.add('summary', meeting.project)
|
||||
summary = meeting.project
|
||||
# NOTE(tonyb): If we're adding an a place holder event for a
|
||||
# cancelled meeting make that as obvious as possible in the
|
||||
# summary.
|
||||
if exdate:
|
||||
# NOTE(tonyb): Because some iCal consumers require that the
|
||||
# summary be unique, and adding multiple "CANCELLED: $x"
|
||||
# entries would violate that rule, append the (UTC)
|
||||
# timestamp for the cancelled meeting.
|
||||
suffix = exdate.date_str
|
||||
summary = 'CANCELLED: %s (%s)' % (summary, suffix)
|
||||
event.add('summary', summary)
|
||||
event.add('location', '#' + sch.irc)
|
||||
|
||||
# add ical description
|
||||
@ -60,28 +73,38 @@ class Yaml2IcalCalendar(icalendar.Calendar):
|
||||
ical_descript = "\n".join((ical_descript,
|
||||
"Project URL: %s" %
|
||||
(meeting.extras['project_url'])))
|
||||
event.add('description', ical_descript)
|
||||
|
||||
# get starting date
|
||||
next_meeting = sch.recurrence.next_occurence(sch.start_date,
|
||||
sch.day)
|
||||
next_meeting_date = datetime.datetime(next_meeting.year,
|
||||
next_meeting.month,
|
||||
next_meeting.day,
|
||||
sch.time.hour,
|
||||
sch.time.minute,
|
||||
tzinfo=pytz.utc)
|
||||
event.add('dtstart', next_meeting_date)
|
||||
# NOTE(tonyb): If we're adding an a place holder event for a
|
||||
# cancelled meeting do not add an rrule and set dtstart to the
|
||||
# skipped date.
|
||||
if not exdate:
|
||||
# get starting date
|
||||
next_meeting = sch.recurrence.next_occurence(sch.start_date,
|
||||
sch.day)
|
||||
next_meeting_date = datetime.datetime(next_meeting.year,
|
||||
next_meeting.month,
|
||||
next_meeting.day,
|
||||
sch.time.hour,
|
||||
sch.time.minute,
|
||||
tzinfo=pytz.utc)
|
||||
event.add('dtstart', next_meeting_date)
|
||||
|
||||
# add recurrence rule
|
||||
event.add('rrule', sch.recurrence.rrule())
|
||||
# add recurrence rule
|
||||
event.add('rrule', sch.recurrence.rrule())
|
||||
event.add('description', ical_descript)
|
||||
else:
|
||||
event.add('dtstart', exdate.date)
|
||||
event.add('description', exdate.reason)
|
||||
|
||||
event.add('duration', datetime.timedelta(minutes=sch.duration))
|
||||
|
||||
# Add exdate (exclude date) if present
|
||||
if hasattr(sch, 'skip_dates'):
|
||||
if not exdate and hasattr(sch, 'skip_dates'):
|
||||
for skip_date in sch.skip_dates:
|
||||
event.add('exdate', skip_date.date)
|
||||
# NOTE(tonyb): If this is a skipped meeting add a
|
||||
# non-recurring event with an obvisous summary.
|
||||
self.add_schedule(meeting, sch, exdate=skip_date)
|
||||
|
||||
# add event to calendar
|
||||
self.add_component(event)
|
||||
|
@ -94,12 +94,27 @@ class MeetingTestCase(unittest.TestCase):
|
||||
|
||||
def test_skip_meeting(self):
|
||||
meeting_yaml = sample_data.MEETING_WITH_SKIP_DATES
|
||||
p = re.compile('.*exdate:\s*20150810T120000', re.IGNORECASE)
|
||||
# Copied from sample_data.MEETING_WITH_SKIP_DATES
|
||||
summary = 'OpenStack Subteam 8 Meeting'
|
||||
patterns = []
|
||||
# The "main" meeting should have an exdate
|
||||
patterns.append(re.compile('.*exdate:\s*20150810T120000', re.I))
|
||||
# The "main" meeting should start on 2015-08-13
|
||||
patterns.append(re.compile('.*dtstart;.*:20150803T120000Z', re.I))
|
||||
# The "main" meeting should have a simple summary
|
||||
patterns.append(re.compile('.*summary:\s*%s' % summary, re.I))
|
||||
# The "skipped" meeting should start on 20150806
|
||||
patterns.append(re.compile('.*dtstart;.*:20150810T120000Z', re.I))
|
||||
# The "skipped" meeting should say include 'CANCELLED' and the datetime
|
||||
patterns.append(re.compile('.*summary:\s*CANCELLED.*20150810T120000Z',
|
||||
re.I))
|
||||
m = meeting.load_meetings(meeting_yaml)[0]
|
||||
cal = ical.Yaml2IcalCalendar()
|
||||
cal.add_meeting(m)
|
||||
cal_str = str(cal.to_ical())
|
||||
self.assertTrue(hasattr(m.schedules[0], 'skip_dates'))
|
||||
self.assertNotEqual(None, p.match(str(cal.to_ical())))
|
||||
for p in patterns:
|
||||
self.assertNotEqual(None, p.match(cal_str))
|
||||
|
||||
def test_skip_meeting_missing_skip_date(self):
|
||||
self.assertRaises(KeyError,
|
||||
|
Loading…
Reference in New Issue
Block a user