[silva.app.news][Sylvain Viollon] Simplify agenda viewer default...

[email protected]
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Wed Jul 31 17:05:33 2013 +0200
revision:  2034:ee7e4a0d0601 in silva.app.news
branch:    2.4
details:   https://hg.infrae.com/silva.app.news?cmd=changeset;node=ee7e4a0d0601
modified:  src/silva/app/news/tests/SilvaNewsTestCase.py src/silva/app/news/tests/test_agenda_viewer.py src/silva/app/news/viewers/AgendaViewer.py src/silva/app/news/viewers/AgendaViewer_templates/agendaviewermonthcalendar.cpt src/silva/app/news/viewers/rest/events.py
added:     
removed:   
log:       Simplify agenda viewer default view code. Fix some little bugs that
	got introduced in the latest modifications.


diffstat:

 src/silva/app/news/tests/SilvaNewsTestCase.py                                   |    1 +
 src/silva/app/news/tests/test_agenda_viewer.py                                  |   85 +++-
 src/silva/app/news/viewers/AgendaViewer.py                                      |  161 ++++-----
 src/silva/app/news/viewers/AgendaViewer_templates/agendaviewermonthcalendar.cpt |    2 +-
 src/silva/app/news/viewers/rest/events.py                                       |    1 -
 5 files changed, 129 insertions(+), 121 deletions(-)

diffs (505 lines):

diff -r 72e480f92e80 -r ee7e4a0d0601 src/silva/app/news/tests/SilvaNewsTestCase.py
--- a/src/silva/app/news/tests/SilvaNewsTestCase.py	Tue Jul 09 18:18:08 2013 +0200
+++ b/src/silva/app/news/tests/SilvaNewsTestCase.py	Wed Jul 31 17:05:33 2013 +0200
@@ -29,6 +29,7 @@
         factory.manage_addAgendaItem(id, title)
         item = parent._getOb(id)
         version = item.get_editable()
+        version.set_display_datetime(sdt)
         version.set_occurrences([
                 AgendaItemOccurrence(
                     start_datetime=sdt,
diff -r 72e480f92e80 -r ee7e4a0d0601 src/silva/app/news/tests/test_agenda_viewer.py
--- a/src/silva/app/news/tests/test_agenda_viewer.py	Tue Jul 09 18:18:08 2013 +0200
+++ b/src/silva/app/news/tests/test_agenda_viewer.py	Wed Jul 31 17:05:33 2013 +0200
@@ -26,16 +26,22 @@
 def calendar_settings(browser):
     public_settings(browser)
     browser.inspect.add(
-        'newsitems',
+        'items',
         css=".newsitemheading",
         type='text')
     browser.inspect.add(
+        'introduction',
+        css='h2.calendar_intro',
+        type='text',
+        unique=True)
+    browser.inspect.add(
         'next_link',
         css='a.nextmonth')
     browser.inspect.add(
         'prev_link',
         css='a.prevmonth')
 
+
 class AgendaViewerTestCase(SilvaNewsTestCase):
 
     def test_viewer(self):
@@ -72,30 +78,11 @@
             [b.getPath() for b in brains],
             [])
 
-    def test_get_item_with_number_option(self):
-        """Test get_items when number_is_days is false.
-        """
-        timezone = get_timezone('Europe/Amsterdam')
-        today = datetime.now().replace(tzinfo=timezone)
-        factory = self.root.manage_addProduct['silva.app.news']
-        factory.manage_addAgendaFilter('filter', 'Agenda Filter')
-        factory.manage_addAgendaViewer('viewer', 'Agenda Viewer')
-        factory.manage_addNewsPublication('news', 'News Publication')
 
-        self.root.filter.set_sources([self.root.news])
-        self.root.viewer.set_filters([self.root.filter])
-        self.root.viewer.set_timezone_name('Europe/Amsterdam')
-        self.root.viewer.set_number_is_days(False)
-        self.root.viewer.set_number_to_show(10)
+class AgendaViewerWithItemsTestCase(SilvaNewsTestCase):
 
-
-    def test_get_item_with_days_option(self):
-        """Test get_items when number_is_days is true.
-        """
-
-    def test_get_item_by_date(self):
-        """Test get_items_by_date and get_items_by_date_range.
-        """
+    def setUp(self):
+        super(AgendaViewerWithItemsTestCase, self).setUp()
         timezone = get_timezone('Europe/Amsterdam')
         factory = self.root.manage_addProduct['silva.app.news']
         factory.manage_addAgendaFilter('filter', 'Agenda Filter')
@@ -144,14 +131,47 @@
             datetime(2010, 11, 23, 12, 20, tzinfo=timezone),
             datetime(2010, 11, 23, 22, 00, tzinfo=timezone))
 
+    def test_get_item_with_number_option(self):
+        """Test get_items when number_is_days is false.
+        """
+        viewer = self.root.viewer
+        viewer.set_number_is_days(False)
+        viewer.set_number_to_show(2)
+        self.assertEqual(viewer.get_number_is_days(), False)
+        self.assertEqual(viewer.get_number_to_show(), 2)
+
+        self.assertEqual(
+            [b.getPath() for b in viewer.get_items()],
+            ['/root/news/end_after_month/0',
+             '/root/news/within_month/0'])
+
+    def test_get_item_with_days_option(self):
+        """Test get_items when number_is_days is true.
+        """
+        viewer = self.root.viewer
+        viewer.set_number_is_days(True)
+        viewer.set_number_to_show(2)
+        self.assertEqual(viewer.get_number_is_days(), True)
+        self.assertEqual(viewer.get_number_to_show(), 2)
+
+        # There are no items in the last 2 days ...
+        self.assertEqual(
+            [b.getPath() for b in viewer.get_items()],
+            [])
+
+    def test_get_item_by_date(self):
+        """Test get_items_by_date and get_items_by_date_range.
+        """
+        viewer = self.root.viewer
+        timezone = get_timezone('Europe/Amsterdam')
         self.assertEquals(
-            [b.getPath() for b in self.root.viewer.get_items_by_date(11, 2010)],
+            [b.getPath() for b in viewer.get_items_by_date(11, 2010)],
             ['/root/news/end_after_month/0',
              '/root/news/within_month/0',
              '/root/news/start_before_month/0',
              '/root/news/over_month/0'])
         self.assertEquals(
-            [b.getPath() for b in self.root.viewer.get_items_by_date_range(
+            [b.getPath() for b in viewer.get_items_by_date_range(
                     datetime(2010, 11, 1, 00, 00, tzinfo=timezone),
                     datetime(2010, 11, 30, 23, 59, 59, tzinfo=timezone)
                     )],
@@ -161,6 +181,7 @@
              '/root/news/over_month/0'])
 
 
+
 def format_date(date):
     return date.utcdatetime().strftime('%Y%m%dT%H%M%SZ')
 
@@ -199,7 +220,7 @@
         with self.layer.get_browser(calendar_settings) as browser:
             self.assertEqual(browser.open('/root/agenda'), 200)
             self.assertEqual(browser.inspect.title, ['Agenda'])
-            self.assertEqual(browser.inspect.newsitems, [])
+            self.assertEqual(browser.inspect.items, [])
 
     def test_rendering_with_news_items(self):
         """Render an agenda viewer that includes news items. This used
@@ -214,7 +235,7 @@
             browser.options.handle_errors = False
             self.assertEqual(browser.open('/root/agenda'), 200)
             self.assertEqual(browser.inspect.title, ['Agenda'])
-            self.assertEqual(browser.inspect.newsitems, [u'I am lost'])
+            self.assertEqual(browser.inspect.items, [u'I am lost'])
 
     def test_rendering_with_event(self):
         """Render an agenda viewer with events.
@@ -227,7 +248,10 @@
                 200)
             self.assertEqual(browser.inspect.title, ['Agenda'])
             self.assertEqual(
-                browser.inspect.newsitems,
+                browser.inspect.introduction,
+                'Events for Tuesday, June 4, 2013')
+            self.assertEqual(
+                browser.inspect.items,
                 [u'Saturday “π” aka Disco'])
 
     def test_functional_external_source_view(self):
@@ -341,7 +365,9 @@
     def test_invalid_and_empty_params(self):
          with self.layer.get_browser(calendar_settings) as browser:
             browser.options.handle_errors = False
-            self.assertEqual(200, browser.open('/root/agenda?year=&month=23&day=x12'))
+            self.assertEqual(
+                browser.open('/root/agenda?year=&month=23&day=x12'),
+                200)
 
     def test_31_february(self):
          with self.layer.get_browser(calendar_settings) as browser:
@@ -352,5 +378,6 @@
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(AgendaViewerTestCase))
+    suite.addTest(unittest.makeSuite(AgendaViewerWithItemsTestCase))
     suite.addTest(unittest.makeSuite(RenderAgendaViewerTestCase))
     return suite
diff -r 72e480f92e80 -r ee7e4a0d0601 src/silva/app/news/viewers/AgendaViewer.py
--- a/src/silva/app/news/viewers/AgendaViewer.py	Tue Jul 09 18:18:08 2013 +0200
+++ b/src/silva/app/news/viewers/AgendaViewer.py	Wed Jul 31 17:05:33 2013 +0200
@@ -8,11 +8,12 @@
 import localdatetime
 
 from five import grok
-from zope.cachedescriptors.property import CachedProperty
+from zope.cachedescriptors.property import Lazy
 from zope.component import getMultiAdapter
+from zope.i18nmessageid import MessageFactory
+from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 from zope.traversing.browser import absoluteURL
-from zope.publisher.interfaces.browser import IBrowserRequest
 
 # Zope
 from AccessControl import ClassSecurityInfo
@@ -32,7 +33,8 @@
 
 # SilvaNews
 from silva.app.news import datetimeutils
-from silva.app.news.interfaces import IAgendaViewer, IAgendaFilter, IAgendaItemContent
+from silva.app.news.interfaces import IAgendaViewer, IAgendaFilter
+from silva.app.news.interfaces import IAgendaItemContentVersion
 from silva.app.news.interfaces import get_default_tz_name
 from silva.app.news.interfaces import make_filters_source
 from silva.app.news.viewers.NewsViewer import NewsViewer, INewsViewerFields
@@ -40,6 +42,9 @@
 from Products.SilvaExternalSources.ExternalSource import ExternalSource
 
 
+_ = MessageFactory('silva_news')
+
+
 class AgendaViewer(NewsViewer, ExternalSource):
     """
     Used to show agendaitems on a Silva site. When setting up an
@@ -95,6 +100,10 @@
     silvaconf.resource('calendar.css')
 
 
+def serialize_date(date):
+    return date.strftime('%Y-%m-%d')
+
+
 class CalendarView(object):
     """Mixin for AgendaViewer view to help building HTML calendars
 
@@ -106,10 +115,11 @@
     that allows to customize the rendering of a particular day. Events for that
     day are available in the index.
     """
-
     YEAR_MAX_DELTA = 5
 
-    _events_index = {}
+    @Lazy
+    def viewer_url(self):
+        return absoluteURL(self.context, self.request)
 
     def validate_boundaries(self, now, year):
         min_year = now.year - self.YEAR_MAX_DELTA
@@ -117,78 +127,57 @@
         if max_year < year or min_year > year:
             raise BadRequest('Invalid year.')
 
-    @staticmethod
-    def serialize_date(date):
-        return date.strftime('%Y-%m-%d')
-
-    def build_calendar(self, current_day, start, end,
-                       today=None, store_events_in_index=False):
+    def build_calendar(self, current_day, start, end, today=None):
         """ Build a HTMLCalendar where :
 
         - `current_day` (date) is the selected day
-        - `today` (date) defaults to today
         - `start` (datetime) and `end` (datetime)
            is the range for loading the events
-        - `store_events_in_index` determine if event are stored in index
-        or just the fact that a event is present.
+        - `today` (date) defaults to today
         """
-        self._store_events_in_index = store_events_in_index
         now = datetime.now(self.context.get_timezone())
         today = today or now.date()
         self.validate_boundaries(now, start.year)
         self.validate_boundaries(now, end.year)
+        self.__index = {}
 
-        acalendar = HTMLCalendar(
+        calendar = HTMLCalendar(
             self.context.get_first_weekday(),
             today=today,
             current_day=current_day or today,
             day_render_callback=self._render_day_callback)
         for brain in self.context.get_items_by_date_range(start, end):
             item = brain.getObject()
-            if IAgendaItemContent.providedBy(item):
-                self._register_event(acalendar, item, start, end)
-        return acalendar
+            if IAgendaItemContentVersion.providedBy(item):
+                self._add_event(calendar, item, start, end)
+        return calendar
 
-    def _register_event(self, acalendar, event, start, end):
+    def _add_event(self, acalendar, event, start, end):
         """ index all the days for which the event has an occurrence between
         start and end.
         """
+        timezone = self.context.get_timezone()
         for occurrence in event.get_occurrences():
             cd = occurrence.get_calendar_datetime()
             for datetime_range in cd.get_datetime_ranges(start, end):
                 for day in datetimeutils.DayWalk(
-                    datetime_range[0], datetime_range[1],
-                    self.context.get_timezone()):
-                    self._index_event(day, event)
-
-    def _index_event(self, adate, event):
-        """ (internal) actual indexing of an event.
-        """
-        serial = self.serialize_date(adate)
-        if self._store_events_in_index:
-            day_events = self._events_index.get(serial, [])
-            day_events.append(event)
-            self._events_index[serial] = day_events
-        else:
-            self._events_index[serial] = True
+                    datetime_range[0], datetime_range[1], timezone):
+                    self.__index[serialize_date(day)] = True
 
     def _render_day_callback(self, day, weekday, week, year, month):
         """Callback for the html calendar to render every day"""
         try:
             event_date = date(year, month, day)
-            events = self._events_index.get(self.serialize_date(event_date))
+            events = self.__index.get(serialize_date(event_date))
             if events:
-                return self._render_events(event_date, events)
+                return (
+                    'event',
+                    '<a href="%s?day=%d&amp;month=%d&amp;year=%d">%d</a>' % \
+                        (self.viewer_url, day, month, year, day))
         except ValueError:
             pass
-        return u'', unicode(day)
-
-    def _render_events(self, date, events):
-        """render a day for which there is events in the index"""
-        cal_url = absoluteURL(self.context, self.request)
-        return ('event',
-                '<a href="%s?day=%d&amp;month=%d&amp;year=%d">%d</a>' % \
-            (cal_url, date.day, date.month, date.year, date.day))
+        return (u'',
+                unicode(day))
 
 
 class AgendaViewerExternalSourceView(silvaviews.View, CalendarView):
@@ -231,10 +220,6 @@
     selected day"""
     grok.context(IAgendaViewer)
 
-    @CachedProperty
-    def context_absolute_url(self):
-        return absoluteURL(self.context, self.request)
-
     def get_int_param(self, name, default=None):
         try:
             value = self.request.get(name, default)
@@ -244,7 +229,7 @@
         except (TypeError, ValueError):
             return default
 
-    def get_current_day(self, now=None):
+    def get_selected_day(self, now=None):
         if now is None:
             now = datetime.now(self.context.get_timezone())
         day = self.get_int_param('day')
@@ -259,29 +244,41 @@
     def update(self):
         need(ICalendarResources)
         self.now = datetime.now(self.context.get_timezone())
-        self.month = self.get_int_param('month', self.now.month)
-        self.year = self.get_int_param('year', self.now.year)
-        self.day = self.get_current_day(self.now)
+        self.selected_month = self.get_int_param('month', self.now.month)
+        self.selected_year = self.get_int_param('year', self.now.year)
         try:
-            self.day_datetime = datetime(self.year, self.month, self.day,
-                                     tzinfo=self.context.get_timezone())
+            self.selected_day = datetime(
+                self.selected_year,
+                self.selected_month,
+                self.get_selected_day(self.now),
+                tzinfo=self.context.get_timezone())
         except ValueError:
-            self.day_datetime = self.now
-            self.year = self.now.year
-            self.month = self.now.month
-            self.day = self.now.day
+            self.selected_day = self.now
+            self.selected_year = self.now.year
+            self.selected_month = self.now.month
 
-        (first_weekday, lastday,) = calendar.monthrange(
-            self.year, self.month)
-
-        self.start = datetimeutils.start_of_month(self.day_datetime)
-        self.end = datetimeutils.end_of_month(self.day_datetime)
+        self.start = datetimeutils.start_of_month(self.selected_day)
+        self.end = datetimeutils.end_of_month(self.selected_day)
 
         self._day_events = self._selected_day_events()
         self.calendar = self.build_calendar(
-            self.day_datetime.date(), self.start, self.end, self.now.date())
+            self.selected_day.date(), self.start, self.end, self.now.date())
 
-        self._set_calendar_nav()
+        if self.should_display_prev_link():
+            self.calendar.prev_link = \
+                '<a class="prevmonth caljump" href="%s">&lt;</a>' % \
+                    self.prev_month_url()
+        if self.should_display_next_link():
+            self.calendar.next_link = \
+                '<a class="nextmonth caljump" href="%s">&gt;</a>' % \
+                    self.next_month_url()
+
+    def _selected_day_events(self):
+        return map(
+            lambda b: b.getObject().get_content(),
+            self.context.get_items_by_date_range(
+                datetimeutils.start_of_day(self.selected_day),
+                datetimeutils.end_of_day(self.selected_day)))
 
     def next_month_url(self):
         year = self.start.year
@@ -290,7 +287,7 @@
             month = 1
             year = year + 1
         return "%s?month=%d&amp;year=%d&amp;day=1" % (
-            self.context_absolute_url, month, year)
+            self.viewer_url, month, year)
 
     def prev_month_url(self):
         year = self.start.year
@@ -299,31 +296,25 @@
             month = 12
             year = year - 1
         return "%s?month=%d&amp;year=%d&amp;day=1" % (
-                self.context_absolute_url, month, year)
+                self.viewer_url, month, year)
 
-    def intro(self):
+    def introduction(self):
         # XXX Should not be done with the method of the Service (who
         # manages settings on how to display the date ?)
-        dayinfo = u"for %s" % localdatetime.get_formatted_date(
-            self.day_datetime, size="full",
+        date = localdatetime.get_formatted_date(
+            self.selected_day, size="full",
             request=self.request, display_time=False)
         if self._day_events:
-            return "Events on %s" % dayinfo
-        return u"No events on %s" % dayinfo
+            return _(u"Events for ${date}", mapping=dict(date=date))
+        return _(u"No events for ${date}", mapping=dict(date=date))
 
     @property
     def day_events(self):
         return self._day_events
 
     def render_calendar(self):
-        return self.calendar.formatmonth(self.year, self.month)
-
-    def _selected_day_events(self):
-        return map(
-            lambda b: b.getObject().get_content(),
-            self.context.get_items_by_date_range(
-                datetimeutils.start_of_day(self.day_datetime),
-                datetimeutils.end_of_day(self.day_datetime)))
+        return self.calendar.formatmonth(
+            self.selected_year, self.selected_month)
  
     def should_display_next_link(self):
         inc, month = divmod(self.start.month, 12)
@@ -338,16 +329,6 @@
             year -= 1
         return self.now.year - year <= self.YEAR_MAX_DELTA
 
-    def _set_calendar_nav(self):
-        if self.should_display_prev_link():
-            self.calendar.prev_link = \
-                '<a class="prevmonth caljump" href="%s">&lt;</a>' % \
-                    self.prev_month_url()
-        if self.should_display_next_link():
-            self.calendar.next_link = \
-                '<a class="nextmonth caljump" href="%s">&gt</a>' % \
-                    self.next_month_url()
-
 
 class AgendaViewerYearCalendar(silvaviews.Page, CalendarView):
     """ Year Calendar representation
diff -r 72e480f92e80 -r ee7e4a0d0601 src/silva/app/news/viewers/AgendaViewer_templates/agendaviewermonthcalendar.cpt
--- a/src/silva/app/news/viewers/AgendaViewer_templates/agendaviewermonthcalendar.cpt	Tue Jul 09 18:18:08 2013 +0200
+++ b/src/silva/app/news/viewers/AgendaViewer_templates/agendaviewermonthcalendar.cpt	Wed Jul 31 17:05:33 2013 +0200
@@ -13,7 +13,7 @@
   </div>
 
   <div class="calendar" tal:content="structure view.render_calendar()" />
-  <h2 class="calendar_intro" tal:content="view.intro()" />
+  <h2 class="calendar_intro" tal:content="view.introduction()" />
 
   <div class="newsitem" tal:repeat="event view.day_events">
     <div tal:attributes="class python:repeat['event'].odd and 'odd' or 'even'">
diff -r 72e480f92e80 -r ee7e4a0d0601 src/silva/app/news/viewers/rest/events.py
--- a/src/silva/app/news/viewers/rest/events.py	Tue Jul 09 18:18:08 2013 +0200
+++ b/src/silva/app/news/viewers/rest/events.py	Wed Jul 31 17:05:33 2013 +0200
@@ -31,7 +31,6 @@
         for brain in brains:
             yield brain.getObject()
 
-
     def get_events_occurrences(self, start, end):
         get_id = component.getUtility(IIntIds).register
         for event in self.get_events(start, end):
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.