Allow for meeting to be scheduled every 4 weeks.

This allows for by weekly meetings to be run at alternate
times.

Change-Id: Ief437e53719c123399de8874469ef16540d3b3fd
Signed-off-by: Graham Hayes <gr@ham.ie>
This commit is contained in:
Graham Hayes 2018-05-04 15:57:07 +01:00
parent 2bc859072f
commit 3150c1a59a
No known key found for this signature in database
GPG Key ID: 1B263DC59F4AEFD5
6 changed files with 249 additions and 2 deletions

19
meetings/example3.yaml Normal file
View File

@ -0,0 +1,19 @@
project: Example alternating quadweekly meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '1700'
duration: 45
start_date: 20150801
day: Thursday
irc: openstack-meeting
frequency: quadweekly
- time: '600'
duration: 45
start_date: 20150801
day: Thursday
irc: openstack-meeting
frequency: quadweekly-alternate
chair: John Doe
description: >
quadweekly

View File

@ -136,13 +136,27 @@ class Schedule(object):
def conflicts(self, other): def conflicts(self, other):
"""Checks for conflicting schedules.""" """Checks for conflicting schedules."""
alternating = set(['biweekly-odd', 'biweekly-even'])
def _non_weekly_conflict_detection(self, other):
week = {
'weekly': set([0, 1, 2, 3]),
'biweekly-even': set([0, 2]),
'biweekly-odd': set([1, 3]),
'quadweekly': set([0]),
'quadweekly-week-1': set([1]),
'quadweekly-week-2': set([2]),
'quadweekly-week-3': set([3]),
'quadweekly-alternate': set([2]),
}
return len(week[self.freq].intersection(week[other.freq])) > 0
# NOTE(tonyb): .meeting_start also includes the day of the week. So no # NOTE(tonyb): .meeting_start also includes the day of the week. So no
# need to check .day explictly # need to check .day explictly
return ((self.irc == other.irc) and return ((self.irc == other.irc) and
((self.meeting_start < other.meeting_end) and ((self.meeting_start < other.meeting_end) and
(other.meeting_start < self.meeting_end)) and (other.meeting_start < self.meeting_end)) and
(set([self.freq, other.freq]) != alternating)) _non_weekly_conflict_detection(self, other))
class Meeting(object): class Meeting(object):

View File

@ -78,6 +78,34 @@ class BiWeeklyRecurrence(object):
return "Every two weeks (on %s weeks)" % self.style return "Every two weeks (on %s weeks)" % self.style
class QuadWeeklyRecurrence(object):
"""Meetings occuring every 4 weeks.
A week number can be supplied to offset meetings
"""
def __init__(self, week=0):
self.week = week
def next_occurence(self, current_date, day):
"""Calculate the next biweekly meeting.
:param current_date: the current date
:param day: scheduled day of the meeting
:returns: datetime object of next meeting
"""
nextweek_day = WeeklyRecurrence().next_occurence(current_date, day)
if nextweek_day.isocalendar()[1] % 4 == self.week:
return nextweek_day
# If week doesn't match rule, skip one week
return self.next_occurence(nextweek_day + datetime.timedelta(7), day)
def rrule(self):
return {'freq': 'weekly', 'interval': 4}
def __str__(self):
return "Every four weeks" % self.style
class AdhocRecurrence(object): class AdhocRecurrence(object):
"""Meetings occuring as needed. """Meetings occuring as needed.
@ -105,5 +133,10 @@ supported_recurrences = {
'weekly': WeeklyRecurrence(), 'weekly': WeeklyRecurrence(),
'biweekly-odd': BiWeeklyRecurrence(style='odd'), 'biweekly-odd': BiWeeklyRecurrence(style='odd'),
'biweekly-even': BiWeeklyRecurrence(), 'biweekly-even': BiWeeklyRecurrence(),
'quadweekly': QuadWeeklyRecurrence(week=0),
'quadweekly-week-1': QuadWeeklyRecurrence(week=1),
'quadweekly-week-2': QuadWeeklyRecurrence(week=2),
'quadweekly-week-3': QuadWeeklyRecurrence(week=3),
'quadweekly-alternate': QuadWeeklyRecurrence(week=2),
'adhoc': AdhocRecurrence(), 'adhoc': AdhocRecurrence(),
} }

View File

@ -272,3 +272,93 @@ chair: Shannon Stacker
description: > description: >
Adhoc random meeting for Subteam project. Adhoc random meeting for Subteam project.
""" """
QUADWEEKLY_MEETING_ALTERNATING = """
project: OpenStack Random Meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '2200'
day: Wednesday
irc: openstack-meeting
frequency: quadweekly
- time: '600'
duration: 45
start_date: 20150801
day: Thursday
irc: openstack-meeting
frequency: quadweekly-alternate
chair: John Doe
description: >
Example alternating quadweekly meeting
"""
QUADWEEKLY_MEETING = """
project: OpenStack Random Meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '2200'
day: Wednesday
irc: openstack-meeting
frequency: quadweekly
chair: John Doe
description: >
Example Quadweekly meeting
"""
QUADWEEKLY_MEETING_WEEK_1 = """
project: OpenStack Random Meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '2200'
day: Wednesday
irc: openstack-meeting
frequency: quadweekly-week-1
chair: John Doe
description: >
Example Quadweekly meeting on week 1
"""
QUADWEEKLY_MEETING_WEEK_2 = """
project: OpenStack Random Meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '2200'
day: Wednesday
irc: openstack-meeting
frequency: quadweekly-week-2
chair: John Doe
description: >
Example Quadweekly meeting on week 2
"""
QUADWEEKLY_MEETING_WEEK_3 = """
project: OpenStack Random Meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '2200'
day: Wednesday
irc: openstack-meeting
frequency: quadweekly-week-3
chair: John Doe
description: >
Example Quadweekly meeting on week 3
"""
QUADWEEKLY_MEETING_ALTERNATE = """
project: OpenStack Random Meeting
agenda_url: http://agenda.com/
project_url: http://project.com
schedule:
- time: '2200'
day: Wednesday
irc: openstack-meeting
frequency: quadweekly-alternate
chair: John Doe
description: >
Example Quadweekly Alternate meeting
"""

View File

@ -78,6 +78,77 @@ class MeetingTestCase(unittest.TestCase):
sample_data.MEETING_MONDAY_LATE, sample_data.MEETING_MONDAY_LATE,
sample_data.MEETING_TUESDAY_EARLY) sample_data.MEETING_TUESDAY_EARLY)
def test_quadweekly_conflicts(self):
self.should_be_conflicting(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING_ALTERNATING)
self.should_be_conflicting(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING)
self.should_be_conflicting(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING_ALTERNATE)
self.should_be_conflicting(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_2)
self.should_not_conflict(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_1)
self.should_not_conflict(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_3)
self.should_be_conflicting(
sample_data.BIWEEKLY_EVEN_MEETING,
sample_data.QUADWEEKLY_MEETING)
self.should_be_conflicting(
sample_data.BIWEEKLY_ODD_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_1)
self.should_be_conflicting(
sample_data.BIWEEKLY_ODD_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_3)
self.should_not_conflict(
sample_data.BIWEEKLY_ODD_MEETING,
sample_data.QUADWEEKLY_MEETING)
self.should_not_conflict(
sample_data.BIWEEKLY_ODD_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_2)
self.should_not_conflict(
sample_data.BIWEEKLY_ODD_MEETING,
sample_data.QUADWEEKLY_MEETING_ALTERNATING)
self.should_not_conflict(
sample_data.QUADWEEKLY_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_1)
self.should_not_conflict(
sample_data.QUADWEEKLY_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_2)
self.should_not_conflict(
sample_data.QUADWEEKLY_MEETING,
sample_data.QUADWEEKLY_MEETING_WEEK_3)
self.should_not_conflict(
sample_data.QUADWEEKLY_MEETING_WEEK_1,
sample_data.QUADWEEKLY_MEETING_WEEK_2)
self.should_not_conflict(
sample_data.QUADWEEKLY_MEETING_WEEK_1,
sample_data.QUADWEEKLY_MEETING_WEEK_3)
self.should_not_conflict(
sample_data.QUADWEEKLY_MEETING_WEEK_2,
sample_data.QUADWEEKLY_MEETING_WEEK_3)
self.should_be_conflicting(
sample_data.QUADWEEKLY_MEETING,
sample_data.QUADWEEKLY_MEETING)
self.should_be_conflicting(
sample_data.QUADWEEKLY_MEETING_WEEK_1,
sample_data.QUADWEEKLY_MEETING_WEEK_1)
self.should_be_conflicting(
sample_data.QUADWEEKLY_MEETING_WEEK_2,
sample_data.QUADWEEKLY_MEETING_WEEK_2)
self.should_be_conflicting(
sample_data.QUADWEEKLY_MEETING_WEEK_3,
sample_data.QUADWEEKLY_MEETING_WEEK_3)
self.should_be_conflicting(
sample_data.QUADWEEKLY_MEETING_WEEK_2,
sample_data.QUADWEEKLY_MEETING_ALTERNATE)
def test_meeting_duration(self): def test_meeting_duration(self):
m = meeting.load_meetings(sample_data.MEETING_WITH_DURATION)[0] m = meeting.load_meetings(sample_data.MEETING_WITH_DURATION)[0]
self.assertEqual(30, m.schedules[0].duration) self.assertEqual(30, m.schedules[0].duration)

View File

@ -38,6 +38,26 @@ class RecurrenceTestCase(unittest.TestCase):
datetime.datetime(2014, 10, 15, 2, 47, 28, 832666), datetime.datetime(2014, 10, 15, 2, 47, 28, 832666),
self.next_meeting(recurrence.BiWeeklyRecurrence(style='even'))) self.next_meeting(recurrence.BiWeeklyRecurrence(style='even')))
def test_next_quadweekly_week_0(self):
self.assertEqual(
datetime.datetime(2014, 10, 29, 2, 47, 28, 832666),
self.next_meeting(recurrence.QuadWeeklyRecurrence(week=0)))
def test_next_quadweekly_week_1(self):
self.assertEqual(
datetime.datetime(2014, 10, 8, 2, 47, 28, 832666),
self.next_meeting(recurrence.QuadWeeklyRecurrence(week=1)))
def test_next_quadweekly_week_2(self):
self.assertEqual(
datetime.datetime(2014, 10, 15, 2, 47, 28, 832666),
self.next_meeting(recurrence.QuadWeeklyRecurrence(week=2)))
def test_next_quadweekly_week_3(self):
self.assertEqual(
datetime.datetime(2014, 10, 22, 2, 47, 28, 832666),
self.next_meeting(recurrence.QuadWeeklyRecurrence(week=3)))
def test_next_adhoc(self): def test_next_adhoc(self):
self.assertEqual( self.assertEqual(
None, None,