Repository :
http://git.fedorahosted.org/cgit/fedocal.git
On branch : master
---------------------------------------------------------------
commit ae518fdb4e9d2f3415498c26aaad9f48e93944e7
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Dec 13 11:53:04 2012 +0100
Back-end work
Rework the get_by_date function. It can be used in more places
giving simpler and more robust code.
Add function to expand recursive meetings, we need this at several
places so we need one central place to reuse it.
Adjust the unit-test for these changes, since we expand the recursive
meeting that does return more results in a number of places.
---------------------------------------------------------------
fedocal/fedocallib/__init__.py | 96 ++++++++-----------------
fedocal/fedocallib/model.py | 151 +++++++++++++++++++++++++++++--------
fedocal/tests/test_fedocallib.py | 5 +-
fedocal/tests/test_flask.py | 4 +-
fedocal/tests/test_flask_api.py | 4 +-
fedocal/tests/test_meeting.py | 12 +++
6 files changed, 167 insertions(+), 105 deletions(-)
diff --git a/fedocal/fedocallib/__init__.py b/fedocal/fedocallib/__init__.py
index ceedcfb..0c31269 100644
--- a/fedocal/fedocallib/__init__.py
+++ b/fedocal/fedocallib/__init__.py
@@ -20,7 +20,6 @@ import operator
from datetime import datetime
from datetime import date
-from datetime import time
from datetime import timedelta
from sqlalchemy import create_engine
@@ -325,8 +324,7 @@ def get_meetings_by_date(session, calendar_name, start_date,
end_date):
meetings (this day is excluded from the selection).
"""
calendar = Calendar.by_id(session, calendar_name)
- return Meeting.get_by_date(session, calendar, start_date,
- end_date)
+ return get_by_date(session, calendar, start_date, end_date)
def get_meetings_by_date_and_region(session, calendar, start_date,
@@ -376,26 +374,14 @@ def get_past_meeting_of_user(session, username,
tzone='UTC',
:kwarg from_date: the date from which the futur meetings should be
retrieved. Defaults to today
"""
- meetings_tmp = Meeting.get_past_meeting_of_user(session, username,
- from_date)
+ meetings_tmp = Meeting.expand_regular_meetings(
+ Meeting.get_past_meeting_of_user(session, username,
+ from_date),
+ end_date=from_date)
meetings = []
for meeting in meetings_tmp:
- if meeting.recursion_frequency and meeting.recursion_ends:
- rec_meetings = []
- cnt = 0
- meetingobj = Meeting.copy(meeting)
- while meetingobj.meeting_date < from_date:
- meetingobj = Meeting.copy(meeting)
- meetingobj.meeting_date = meetingobj.meeting_date + \
- timedelta(days=meeting.recursion_frequency * cnt)
- cnt = cnt + 1
- if meetingobj.meeting_date < from_date:
- rec_meetings.append(convert_meeting_timezone(
- meetingobj, 'UTC', tzone))
- rec_meetings.reverse()
- meetings.extend(rec_meetings)
- else:
- meetings.append(convert_meeting_timezone(meeting, 'UTC', tzone))
+ meetings.append(convert_meeting_timezone(meeting, 'UTC', tzone))
+ meetings.sort(key=operator.attrgetter('meeting_date'))
return meetings
@@ -443,10 +429,10 @@ def get_future_regular_meeting_of_user(session, username,
tzone='UTC',
return meetings
-def agenda_is_free(session, calendar, meeting_date,
+def agenda_is_free(session, calendarobj, meeting_date,
time_start, time_stop):
"""Check if there is already someting planned in this agenda at that
- time.
+ time on that day.
:arg session: the database session to use
:arg calendar: the name of the calendar of interest.
@@ -454,14 +440,12 @@ def agenda_is_free(session, calendar, meeting_date,
:arg time_start: the time at which the meeting starts (as int)
:arg time_stop: the time at which the meeting stops (as int)
"""
- meetings = Meeting.at_time(session, calendar, meeting_date,
- time_start)
- meetings.extend(Meeting.at_time(session, calendar, meeting_date,
- time_stop))
- meetings.extend(Meeting.get_by_time(session, calendar, meeting_date,
- time_start, time_stop))
+ meetings = get_by_date(session, calendarobj, meeting_date,
+ meeting_date + timedelta(days=1))
agenda_free = True
for meeting in set(meetings):
+ if meeting.meeting_date != meeting_date:
+ continue
if time_start <= meeting.meeting_time_start \
and meeting.meeting_time_start < time_stop:
agenda_free = False
@@ -477,27 +461,25 @@ def agenda_is_free(session, calendar, meeting_date,
return agenda_free
-def agenda_is_free_in_future(session, calendar, meeting_date,
- recursion_ends, time_start, time_stop):
+def agenda_is_free_in_future(session, calendarobj, meeting_date,
+ time_start, time_stop):
"""For recursive meeting, check for meetings happening at the
- specified time between the specified date and the end of the
+ specified time between the specified date and to the end of the
recursion.
:arg session: the database session to use
- :arg calendar: the name of the calendar of interest.
+ :arg calendarobj: the calendar of interest.
:arg meeting_date: the date of the meeting (as Datetime object)
:arg recursion_ends: the end date of the recursion
:arg time_start: the time at which the meeting starts (as int)
:arg time_stop: the time at which the meeting stops (as int)
"""
- meetings = Meeting.in_future_at_time(session, calendar, meeting_date,
- recursion_ends, time_start)
- meetings.extend(Meeting.in_future_at_time(session, calendar,
- meeting_date, recursion_ends, time_stop))
- meetings.extend(Meeting.get_in_future_by_time(session, calendar,
- meeting_date, recursion_ends, time_start, time_stop))
+ meetings = get_by_date(session, calendarobj, meeting_date,
+ meeting_date + timedelta(days=1))
agenda_free = True
for meeting in set(meetings):
+ if meeting.meeting_date != meeting_date:
+ continue
if time_start <= meeting.meeting_time_start \
and meeting.meeting_time_start < time_stop:
agenda_free = False
@@ -674,31 +656,13 @@ def get_by_date(session, calendarobj, start_date, end_date,
tzone='UTC'):
defaults to UTC.
"""
meetings_utc = Meeting.get_by_date(session, calendarobj, start_date,
- end_date)
- meetings_utc.extend(Meeting.get_active_regular_meeting(session,
- calendarobj, start_date))
+ end_date, no_recursive=True)
+ meetings_utc.extend(Meeting.get_regular_meeting_by_date(session,
+ calendarobj, start_date, end_date))
meetings = []
for meeting in list(set(meetings_utc)):
- if meeting.recursion_frequency and meeting.recursion_ends:
- meeting_date = meeting.meeting_date
- cnt = 0
- while meeting_date < end_date and \
- meeting_date <= meeting.recursion_ends:
- recmeeting = meeting.copy()
- if meeting_date >= start_date:
- recmeeting.meeting_id = meeting.meeting_id
- recmeeting.meeting_date = meeting.meeting_date + timedelta(
- days=meeting.recursion_frequency * cnt)
- recmeeting.meeting_date_end = meeting.meeting_date_end \
- + timedelta(days=meeting.recursion_frequency * cnt)
- meetings.append(convert_meeting_timezone(recmeeting,
- 'UTC', tzone))
- cnt = cnt + 1
- meeting_date = meeting.meeting_date + timedelta(
- days=meeting.recursion_frequency * cnt)
- else:
- meetings.append(convert_meeting_timezone(meeting, 'UTC',
- tzone))
+ meetings.append(convert_meeting_timezone(meeting, 'UTC',
+ tzone))
meetings.sort(key=operator.attrgetter('meeting_date'))
return meetings
@@ -750,8 +714,8 @@ def add_meeting(session, calendarobj, fas_user,
if frequency and end_repeats:
futur_meeting_at_time = agenda_is_free_in_future(session, calendarobj,
- meeting_date, end_repeats,
- meeting_time_start.time(), meeting_time_stop.time())
+ meeting_date, meeting_time_start.time(),
+ meeting_time_stop.time())
if not bool(calendarobj.calendar_multiple_meetings) and \
not(futur_meeting_at_time):
@@ -840,8 +804,8 @@ def edit_meeting(session, meeting, calendarobj, fas_user,
if recursion_frequency and recursion_ends:
futur_meeting_at_time = agenda_is_free_in_future(session,
- calendarobj, meeting_date, recursion_ends,
- meeting_time_start.time(), meeting_time_stop.time())
+ calendarobj, meeting_date, meeting_time_start.time(),
+ meeting_time_stop.time())
if not bool(calendarobj.calendar_multiple_meetings) and \
not(futur_meeting_at_time):
diff --git a/fedocal/fedocallib/model.py b/fedocal/fedocallib/model.py
index 9c0bfd5..645138f 100644
--- a/fedocal/fedocallib/model.py
+++ b/fedocal/fedocallib/model.py
@@ -16,6 +16,7 @@ license.
"""
__requires__ = ['SQLAlchemy >= 0.7']
import pkg_resources
+import operator
from datetime import datetime
from datetime import date
@@ -232,6 +233,7 @@ class Meeting(BASE):
meeting.meeting_time_start = self.meeting_time_start
meeting.meeting_time_stop = self.meeting_time_stop
meeting.calendar_name = self.calendar_name
+ meeting.calendar = self.calendar
meeting.reminder_id = self.reminder_id
meeting.meeting_region = self.meeting_region
meeting.recursion_frequency = self.recursion_frequency
@@ -272,23 +274,43 @@ class Meeting(BASE):
@classmethod
def get_by_date(cls, session, calendar, start_date, stop_date,
- full_day=False):
+ full_day=False, no_recursive=False):
""" Retrieve the list of meetings between two date.
We include the start date and exclude the stop date.
"""
+ if no_recursive:
+ return session.query(cls).filter(and_
+ (Meeting.calendar == calendar),
+ (Meeting.meeting_date >= start_date),
+ (Meeting.meeting_date < stop_date),
+ (Meeting.recursion_frequency == None),
+ (Meeting.full_day == full_day)
+ ).order_by(Meeting.meeting_date).all()
+ else:
+ return session.query(cls).filter(and_
+ (Meeting.calendar == calendar),
+ (Meeting.meeting_date >= start_date),
+ (Meeting.meeting_date < stop_date),
+ (Meeting.full_day == full_day)
+ ).order_by(Meeting.meeting_date).all()
+
+ @classmethod
+ def get_at_date(cls, session, calendar, meeting_date, full_day=False):
+ """ Retrieve the list of meetings happening at a given date.
+ The full_day boolean allows to specify if you want full day
+ meetings or not (defaults to False).
+ """
return session.query(cls).filter(and_
(Meeting.calendar == calendar),
- (Meeting.meeting_date >= start_date),
- (Meeting.meeting_date < stop_date),
+ (Meeting.meeting_date == meeting_date),
(Meeting.full_day == full_day)
).order_by(Meeting.meeting_date).all()
@classmethod
def get_active_regular_meeting(cls, session, calendar, end_date,
full_day=False):
- """ Retrieve the list of meetings with a recursion which
- end_date is not past the provided end_date and starting before
- the end of the time considered.
+ """ Retrieve the list of recursive meetings occuring before the
+ end_date in the specified calendar.
"""
meetings = session.query(cls).filter(and_
(Meeting.meeting_date <= end_date),
@@ -299,6 +321,45 @@ class Meeting(BASE):
).order_by(Meeting.meeting_date).all()
return meetings
+ @classmethod
+ def get_regular_meeting_at_date(cls, session, calendar, end_date,
+ full_day=False):
+ """ Retrieve the list of recursive meetings happening at the
+ specified end_date.
+ """
+ meetings = cls.expand_regular_meetings(
+ cls.get_active_regular_meeting(session, calendar,
+ end_date, full_day),
+ end_date)
+ return meetings
+
+ @classmethod
+ def get_active_regular_meeting_by_date(cls, session, calendar,
+ start_date, end_date, full_day=False):
+ """ Retrieve the list of recursive meetings occuring after the
+ start_date in the specified calendar.
+ """
+ meetings = session.query(cls).filter(and_
+ (Meeting.recursion_ends >= start_date),
+ (Meeting.calendar == calendar),
+ (Meeting.recursion_frequency != None),
+ (Meeting.full_day == full_day)
+ ).order_by(Meeting.meeting_date).all()
+ return meetings
+
+
+ @classmethod
+ def get_regular_meeting_by_date(cls, session, calendar, start_date,
+ end_date, full_day=False):
+ """ Retrieve the list of recursive meetings happening in between
+ the two specified dates.
+ """
+ meetings = cls.expand_regular_meetings(
+ cls.get_active_regular_meeting_by_date(session, calendar,
+ start_date, full_day),
+ end_date=end_date, start_date=start_date)
+ return meetings
+
# pylint: disable=R0913
@classmethod
def get_by_date_and_region(cls, session, calendar, start_date,
@@ -325,20 +386,7 @@ class Meeting(BASE):
(Meeting.meeting_time_stop < stop_time)).all()
@classmethod
- def get_in_future_by_time(cls, session, calendar, meetingdate,
- recursion_ends, start_time, stop_time):
- """ Retrieve the list of meetings for a given date and between
- two times for a specific calendar.
- """
- return session.query(cls).filter(and_
- (Meeting.calendar == calendar),
- (Meeting.meeting_date >= meetingdate),
- (Meeting.meeting_date <= recursion_ends),
- (Meeting.meeting_time_start >= start_time),
- (Meeting.meeting_time_stop < stop_time)).all()
-
- @classmethod
- def at_time(cls, session, calendar, meetingdate, t_time):
+ def get_at_time(cls, session, calendar, meetingdate, t_time):
""" Returns the meeting occuring at this specifict time point.
"""
return session.query(cls).filter(and_
@@ -348,19 +396,6 @@ class Meeting(BASE):
(Meeting.meeting_time_stop > t_time)).all()
@classmethod
- def get_in_future_at_time(cls, session, calendar, meetingdate,
- recursion_ends, t_time):
- """ Returns the meeting occuring at this specifict time point
- at any time in the future.
- """
- return session.query(cls).filter(and_
- (Meeting.calendar == calendar),
- (Meeting.meeting_date >= meetingdate),
- (Meeting.meeting_date <= recursion_ends),
- (Meeting.meeting_time_start <= t_time),
- (Meeting.meeting_time_stop > t_time)).all()
-
- @classmethod
def get_past_meeting_of_user(cls, session, username, start_date):
""" Retrieve the list of meetings which specified username
is among the managers and which date is older than the specified
@@ -433,6 +468,56 @@ class Meeting(BASE):
return meetings
+ @classmethod
+ def expand_regular_meetings(cls, meetings_in, end_date=None,
+ start_date=None):
+ """ For a given list of meetings, go through all of them and if
+ the meeting is recursive, expand the recursion as if they were
+ all different meetings.
+ The end_date keyword argument allows to stop the process earlier
+ allowing to have all recursive meeting up to a certain time
+ point.
+ The start_date keyword argument allows to only keep the meeting
+ from a certain time point.
+ """
+ meetings = []
+ for meeting in meetings_in:
+ if not end_date:
+ end_date = meeting.recursion_ends
+ if meeting.recursion_frequency and meeting.recursion_ends:
+ meeting_date = meeting.meeting_date
+ cnt = 0
+ while meeting_date <= end_date and \
+ meeting_date <= meeting.recursion_ends:
+ recmeeting = meeting.copy()
+ recmeeting.meeting_id = meeting.meeting_id
+ recmeeting.calendar = meeting.calendar
+ if start_date \
+ and meeting_date >= start_date:
+ recmeeting.meeting_date = \
+ meeting.meeting_date + timedelta(
+ days=meeting.recursion_frequency * cnt)
+ recmeeting.meeting_date_end = \
+ meeting.meeting_date_end + timedelta(
+ days=meeting.recursion_frequency * cnt)
+ meetings.append(recmeeting)
+ elif not start_date:
+ recmeeting.meeting_date = \
+ meeting.meeting_date + timedelta(
+ days=meeting.recursion_frequency * cnt)
+ recmeeting.meeting_date_end = \
+ meeting.meeting_date_end + timedelta(
+ days=meeting.recursion_frequency * cnt)
+ meetings.append(recmeeting)
+
+ cnt = cnt + 1
+ meeting_date = meeting.meeting_date + timedelta(
+ days=meeting.recursion_frequency * cnt)
+ else:
+ meetings.append(meeting)
+ meetings.sort(key=operator.attrgetter('meeting_date'))
+ return meetings
+
class Reminder(BASE):
""" Reminders table.
diff --git a/fedocal/tests/test_fedocallib.py b/fedocal/tests/test_fedocallib.py
index 601d261..3a18f43 100644
--- a/fedocal/tests/test_fedocallib.py
+++ b/fedocal/tests/test_fedocallib.py
@@ -520,10 +520,11 @@ class Fedocallibtests(Modeltests):
TODAY + timedelta(days=10),
TODAY + timedelta(days=12)
)
- self.assertEqual(len(meetings), 3)
+ self.assertEqual(len(meetings), 4)
for meeting in meetings:
self.assertTrue(meeting.meeting_name in ['test-meeting2',
- 'Another test meeting', 'Test meeting with reminder'])
+ 'Another test meeting', 'Test meeting with reminder',
+ 'Test meeting with reminder and recursion'])
self.assertEqual(meeting.meeting_manager, 'pingou,')
# pylint: disable=C0103
diff --git a/fedocal/tests/test_flask.py b/fedocal/tests/test_flask.py
index eca8fb5..40524c8 100644
--- a/fedocal/tests/test_flask.py
+++ b/fedocal/tests/test_flask.py
@@ -148,8 +148,8 @@ class Flasktests(Modeltests):
self.assertTrue('DESCRIPTION:This is a test meeting with '\
'recursion' in output.data)
self.assertTrue('ORGANIZER:pingou' in output.data)
- self.assertEqual(output.data.count('BEGIN:VEVENT'), 8)
- self.assertEqual(output.data.count('END:VEVENT'), 8)
+ self.assertEqual(output.data.count('BEGIN:VEVENT'), 45)
+ self.assertEqual(output.data.count('END:VEVENT'), 45)
def test_view_meeting(self):
""" Test the view_meeting function. """
diff --git a/fedocal/tests/test_flask_api.py b/fedocal/tests/test_flask_api.py
index 90a6afe..9f4fb7e 100644
--- a/fedocal/tests/test_flask_api.py
+++ b/fedocal/tests/test_flask_api.py
@@ -97,7 +97,7 @@ class FlaskApitests(Modeltests):
self.assertTrue(' "meeting_manager": "pingou,
shaiton,",' in \
output.data)
self.assertTrue('"meeting_name": "test-meeting2"' in
output.data)
- self.assertEqual(output.data.count('meeting_name'), 8)
+ self.assertEqual(output.data.count('meeting_name'), 45)
output = self.app.get('/api/date/test_calendar4/')
self.assertEqual(output.status_code, 200)
@@ -125,7 +125,7 @@ class FlaskApitests(Modeltests):
output.data)
self.assertTrue('"meeting_name": "Another test
meeting2",' in \
output.data)
- self.assertEqual(output.data.count('meeting_name'), 4)
+ self.assertEqual(output.data.count('meeting_name'), 6)
end_date = TODAY + timedelta(days=2)
output = self.app.get('/api/date/test_calendar4/%s/%s/' % (TODAY,
diff --git a/fedocal/tests/test_meeting.py b/fedocal/tests/test_meeting.py
index 8377fa2..b12b17c 100644
--- a/fedocal/tests/test_meeting.py
+++ b/fedocal/tests/test_meeting.py
@@ -154,6 +154,7 @@ class Meetingtests(Modeltests):
recursion_frequency=7,
recursion_ends=TODAY + timedelta(days=90))
obj.save(self.session)
+
obj = model.Meeting( # id:8
meeting_name='Another test meeting2',
meeting_manager='pingou,',
@@ -398,6 +399,17 @@ class Meetingtests(Modeltests):
'This is a test meeting with recursion2')
self.assertEqual(obj[1].reminder, None)
+ obj = model.Meeting.get_by_date(self.session, cal,
+ week_start, week_stop, no_recursive=True)
+ self.assertNotEqual(obj, None)
+ self.assertEqual(len(obj), 1)
+ self.assertEqual(obj[0].meeting_name, 'Fedora-fr-test-meeting')
+ self.assertEqual(obj[0].meeting_manager, 'pingou, shaiton,')
+ self.assertEqual(obj[0].calendar.calendar_name, 'test_calendar')
+ self.assertEqual(obj[0].meeting_information,
+ 'This is a test meeting')
+ self.assertEqual(obj[0].reminder, None)
+
week_stop = week_day + timedelta(days=12)
obj = model.Meeting.get_by_date(self.session, cal,
week_start, week_stop)