From 35ef5047900201debdd17acade1400ecd91e44ab Mon Sep 17 00:00:00 2001 From: "Marcos F. Romero" Date: Tue, 29 Oct 2013 08:00:37 -0300 Subject: [PATCH 1/9] Configurable portlet with header and city --- src/collective/weather/browser/css/style.css | 4 +- src/collective/weather/browser/js/weather.js | 2 +- .../browser/templates/current_weather.pt | 2 +- src/collective/weather/configure.zcml | 7 ++ .../weather/portlets/configure.zcml | 1 + src/collective/weather/portlets/weather.py | 64 ++++++++++++++----- src/collective/weather/vocabularies.py | 19 ++++++ 7 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 src/collective/weather/vocabularies.py diff --git a/src/collective/weather/browser/css/style.css b/src/collective/weather/browser/css/style.css index 5e0de7f..d4aa8ea 100644 --- a/src/collective/weather/browser/css/style.css +++ b/src/collective/weather/browser/css/style.css @@ -33,7 +33,7 @@ dl.actionMenu a#current-city{ right: 0; } -#top-weather-viewlet #current-weather { +#top-weather-viewlet .current-weather { float: right; padding-right: 0.5em; } @@ -59,4 +59,4 @@ dl.actionMenu a.city-not-selected { .portletweather dd { text-align: right; padding: 1em 0; -} \ No newline at end of file +} diff --git a/src/collective/weather/browser/js/weather.js b/src/collective/weather/browser/js/weather.js index b3aabf3..488e305 100644 --- a/src/collective/weather/browser/js/weather.js +++ b/src/collective/weather/browser/js/weather.js @@ -15,7 +15,7 @@ function showCityWeather(cityId){ async : true, data: data, success: function(results){ - $("div#current-weather").parent().html(results); + $("#top-weather-viewlet .current-weather").parent().html(results); createCookie("collective.weather.current_city", cityId, 30); } }); diff --git a/src/collective/weather/browser/templates/current_weather.pt b/src/collective/weather/browser/templates/current_weather.pt index acd528f..a421ff0 100644 --- a/src/collective/weather/browser/templates/current_weather.pt +++ b/src/collective/weather/browser/templates/current_weather.pt @@ -9,7 +9,7 @@ tal:attributes="data-city-id city_id"> Córdoba, Argentina -
+
+ + + diff --git a/src/collective/weather/portlets/configure.zcml b/src/collective/weather/portlets/configure.zcml index b62949e..6bdf402 100644 --- a/src/collective/weather/portlets/configure.zcml +++ b/src/collective/weather/portlets/configure.zcml @@ -12,6 +12,7 @@ edit_permission="cmf.ManagePortal" renderer=".weather.Renderer" addview=".weather.AddForm" + editview=".weather.EditForm" /> diff --git a/src/collective/weather/portlets/weather.py b/src/collective/weather/portlets/weather.py index 08cfa77..f8491d1 100644 --- a/src/collective/weather/portlets/weather.py +++ b/src/collective/weather/portlets/weather.py @@ -3,35 +3,54 @@ from collective.weather import _ from collective.weather.interfaces import IWeatherUtility from plone.app.portlets.portlets import base -from plone.app.portlets.browser.formhelper import NullAddForm from plone.portlets.interfaces import IPortletDataProvider from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile +from zope import schema from zope.component import getUtility +from zope.formlib import form from zope.interface import implements class IWeatherPortlet(IPortletDataProvider): - """A weather portlet. - """ + '''A weather portlet. + ''' + + header = schema.TextLine( + title=_(u'Portlet header'), + description=_(u'Title of the rendered portlet'), + required=True) + + city = schema.Choice( + title=_(u'City'), + description=_(u'Choose one of the preconfigured cities'), + required=True, + vocabulary='collective.weather.Cities') class Assignment(base.Assignment): - """Portlet assignment. - """ + '''Portlet assignment. + ''' implements(IWeatherPortlet) + header = u'' + city = u'' + + def __init__(self, header=u'', city=u''): + self.header = header + self.city = city + @property def title(self): - """This property is used to give the title of the portlet in the - "manage portlets" screen. - """ - return _(u'Weather portlet') + '''This property is used to give the title of the portlet in the + 'manage portlets' screen. + ''' + return self.header class Renderer(base.Renderer): - """Portlet renderer. - """ + '''Portlet renderer. + ''' render = ViewPageTemplateFile('weather.pt') @@ -40,9 +59,22 @@ def update(self): self.weather_info = weather_utility.get_weather_info() -class AddForm(NullAddForm): - """Portlet add form. - """ +class AddForm(base.AddForm): + '''Portlet add form. + ''' + + form_fields = form.Fields(IWeatherPortlet) + + label = _(u'Add Weather Portlet') + + def create(self, data): + return Assignment(**data) + + +class EditForm(base.EditForm): + '''Portlet edit form. + ''' + + form_fields = form.Fields(IWeatherPortlet) - def create(self): - return Assignment() + label = _(u'Add Weather Portlet') diff --git a/src/collective/weather/vocabularies.py b/src/collective/weather/vocabularies.py new file mode 100644 index 0000000..f7a2624 --- /dev/null +++ b/src/collective/weather/vocabularies.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from collective.weather.interfaces import IWeatherUtility +from zope.component import getUtility +from zope.schema.vocabulary import SimpleVocabulary + + +def CitiesVocabulary(context): + """ Creates a vocabulary to expose configured cities. + """ + + weather_utility = getUtility(IWeatherUtility) + cities_list = weather_utility.get_cities_list() + items = [] + for city in cities_list: + items.append(SimpleVocabulary.createTerm( + city['name'], city['location_id'], city['name'])) + + return SimpleVocabulary(items) From 0596022dddc27e9c52d44c68f9c4b0ed61707d82 Mon Sep 17 00:00:00 2001 From: "Marcos F. Romero" Date: Tue, 29 Oct 2013 10:27:51 -0300 Subject: [PATCH 2/9] Configurable weather portlet --- src/collective/weather/portlets/weather.pt | 30 ++++++++++++++++++++-- src/collective/weather/portlets/weather.py | 15 ++++++++++- src/collective/weather/vocabularies.py | 2 +- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/collective/weather/portlets/weather.pt b/src/collective/weather/portlets/weather.pt index 8df828e..980a3bb 100644 --- a/src/collective/weather/portlets/weather.pt +++ b/src/collective/weather/portlets/weather.pt @@ -2,11 +2,37 @@ i18n:domain="collective.weather" tal:define="portal context/@@plone_portal_state/portal"> +
- Weather + Weather
-
+
+ + Cordoba, Argentina + +
+
+ + + + +
+ +
+ No weather information +
+
diff --git a/src/collective/weather/portlets/weather.py b/src/collective/weather/portlets/weather.py index f8491d1..bfca53e 100644 --- a/src/collective/weather/portlets/weather.py +++ b/src/collective/weather/portlets/weather.py @@ -9,6 +9,7 @@ from zope.component import getUtility from zope.formlib import form from zope.interface import implements +from zope.schema.interfaces import IVocabularyFactory class IWeatherPortlet(IPortletDataProvider): @@ -56,7 +57,19 @@ class Renderer(base.Renderer): def update(self): weather_utility = getUtility(IWeatherUtility) - self.weather_info = weather_utility.get_weather_info() + factory = getUtility(IVocabularyFactory, name='collective.weather.Cities') + vocab = factory(self.context) + self.current_city = None + if self.data.city in vocab: + city = vocab.by_value[self.data.city] + self.current_city = {'id': city.value, 'name': city.title} + + self.weather_info = None + try: + weather_utility.update_weather_info(self.current_city['id']) + self.weather_info = weather_utility.get_weather_info(self.current_city) + except: + pass class AddForm(base.AddForm): diff --git a/src/collective/weather/vocabularies.py b/src/collective/weather/vocabularies.py index f7a2624..70595c5 100644 --- a/src/collective/weather/vocabularies.py +++ b/src/collective/weather/vocabularies.py @@ -14,6 +14,6 @@ def CitiesVocabulary(context): items = [] for city in cities_list: items.append(SimpleVocabulary.createTerm( - city['name'], city['location_id'], city['name'])) + city['id'], city['location_id'], city['name'])) return SimpleVocabulary(items) From 92f859dcd1d78c77c00540e20b2b9a2ec6b7447f Mon Sep 17 00:00:00 2001 From: "Marcos F. Romero" Date: Wed, 30 Oct 2013 22:56:23 -0300 Subject: [PATCH 3/9] Test add and render --- src/collective/weather/tests/test_weather_portlet.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/collective/weather/tests/test_weather_portlet.py b/src/collective/weather/tests/test_weather_portlet.py index e01470b..7a25027 100644 --- a/src/collective/weather/tests/test_weather_portlet.py +++ b/src/collective/weather/tests/test_weather_portlet.py @@ -46,7 +46,7 @@ def test_interfaces(self): self.assertTrue(IPortletAssignment.providedBy(portlet)) self.assertTrue(IPortletDataProvider.providedBy(portlet.data)) - @unittest.skip('Portlet has no add view... yet') + #@unittest.skip('Portlet has no add view... yet') def test_invoke_add_view(self): portlet = getUtility(IPortletType, name=self.name) mapping = self.portal.restrictedTraverse('++contextportlets++plone.leftcolumn') @@ -55,12 +55,12 @@ def test_invoke_add_view(self): del mapping[m] addview = mapping.restrictedTraverse('+/' + portlet.addview) - addview.createAndAdd(data={}) + addview.createAndAdd(data={'header': u'Weather', 'city': u'Cordoba'}) self.assertEqual(len(mapping), 1) self.assertTrue(isinstance(mapping.values()[0], weather.Assignment)) - @unittest.skip('Portlet has no edit view... yet') + #@unittest.skip('Portlet has no edit view... yet') def test_invoke_edit_view(self): mapping = PortletAssignmentMapping() @@ -101,7 +101,7 @@ def renderer(self, context=None, request=None, view=None, manager=None, assignme (context, request, view, manager, assignment), IPortletRenderer) def test_render(self): - assignment = weather.Assignment() + assignment = weather.Assignment(header=u'Weather', city=u'Cordoba') r = self.renderer(assignment=assignment) r = r.__of__(self.portal) From d5545595cae1670a4a04b82ba33194a1a75486f3 Mon Sep 17 00:00:00 2001 From: hvelarde Date: Thu, 31 Oct 2013 17:12:30 -0200 Subject: [PATCH 4/9] Add tests for locations vocabulary --- .../weather/tests/test_vocabularies.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/collective/weather/tests/test_vocabularies.py diff --git a/src/collective/weather/tests/test_vocabularies.py b/src/collective/weather/tests/test_vocabularies.py new file mode 100644 index 0000000..25bc5f4 --- /dev/null +++ b/src/collective/weather/tests/test_vocabularies.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +from collective.weather.testing import INTEGRATION_TESTING +from zope.component import queryUtility +from zope.schema.interfaces import IVocabularyFactory + +import unittest + + +class VocabulariesTestCase(unittest.TestCase): + + layer = INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] + + def test_locations_vocabulary(self): + name = 'collective.weather.Cities' + util = queryUtility(IVocabularyFactory, name) + self.assertIsNotNone(util) + locations = util(self.portal) + # as defined in the test fixture + self.assertEqual(len(locations), 2) + self.assertIn('Cordoba', locations) + self.assertIn(u'Los Angeles', locations) From 5e5079c3b9c11c2b2808b58b8196419c1ff7c8a0 Mon Sep 17 00:00:00 2001 From: hvelarde Date: Thu, 31 Oct 2013 17:33:37 -0200 Subject: [PATCH 5/9] Use more general term 'location' instead of 'city' --- src/collective/weather/configure.zcml | 4 +- src/collective/weather/portlets/weather.py | 55 ++++++++++--------- .../weather/tests/test_vocabularies.py | 2 +- .../weather/tests/test_weather_portlet.py | 6 +- src/collective/weather/vocabularies.py | 12 ++-- 5 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/collective/weather/configure.zcml b/src/collective/weather/configure.zcml index b458d9c..d7ee5c7 100644 --- a/src/collective/weather/configure.zcml +++ b/src/collective/weather/configure.zcml @@ -61,8 +61,8 @@ diff --git a/src/collective/weather/portlets/weather.py b/src/collective/weather/portlets/weather.py index bfca53e..d2a1b61 100644 --- a/src/collective/weather/portlets/weather.py +++ b/src/collective/weather/portlets/weather.py @@ -13,56 +13,59 @@ class IWeatherPortlet(IPortletDataProvider): - '''A weather portlet. - ''' + """A weather portlet. + """ header = schema.TextLine( title=_(u'Portlet header'), - description=_(u'Title of the rendered portlet'), - required=True) + description=_(u'Title of the rendered portlet.'), + required=True, + ) - city = schema.Choice( - title=_(u'City'), - description=_(u'Choose one of the preconfigured cities'), + location = schema.Choice( + title=_(u'Location'), + description=_(u'Choose one of the preconfigured locations.'), required=True, - vocabulary='collective.weather.Cities') + vocabulary='collective.weather.Locations', + ) class Assignment(base.Assignment): - '''Portlet assignment. - ''' + """Portlet assignment. + """ implements(IWeatherPortlet) header = u'' - city = u'' + location = u'' - def __init__(self, header=u'', city=u''): + def __init__(self, header=u'', location=u''): self.header = header - self.city = city + self.location = location @property def title(self): - '''This property is used to give the title of the portlet in the + """This property is used to give the title of the portlet in the 'manage portlets' screen. - ''' + """ return self.header class Renderer(base.Renderer): - '''Portlet renderer. - ''' + """Portlet renderer. + """ render = ViewPageTemplateFile('weather.pt') def update(self): weather_utility = getUtility(IWeatherUtility) - factory = getUtility(IVocabularyFactory, name='collective.weather.Cities') + factory = getUtility( + IVocabularyFactory, name='collective.weather.Locations') vocab = factory(self.context) self.current_city = None - if self.data.city in vocab: - city = vocab.by_value[self.data.city] - self.current_city = {'id': city.value, 'name': city.title} + if self.data.location in vocab: + location = vocab.by_value[self.data.location] + self.current_city = {'id': location.value, 'name': location.title} self.weather_info = None try: @@ -73,8 +76,8 @@ def update(self): class AddForm(base.AddForm): - '''Portlet add form. - ''' + """Portlet add form. + """ form_fields = form.Fields(IWeatherPortlet) @@ -85,9 +88,9 @@ def create(self, data): class EditForm(base.EditForm): - '''Portlet edit form. - ''' + """Portlet edit form. + """ form_fields = form.Fields(IWeatherPortlet) - label = _(u'Add Weather Portlet') + label = _(u'Edit Weather Portlet') diff --git a/src/collective/weather/tests/test_vocabularies.py b/src/collective/weather/tests/test_vocabularies.py index 25bc5f4..e712a6b 100644 --- a/src/collective/weather/tests/test_vocabularies.py +++ b/src/collective/weather/tests/test_vocabularies.py @@ -15,7 +15,7 @@ def setUp(self): self.portal = self.layer['portal'] def test_locations_vocabulary(self): - name = 'collective.weather.Cities' + name = 'collective.weather.Locations' util = queryUtility(IVocabularyFactory, name) self.assertIsNotNone(util) locations = util(self.portal) diff --git a/src/collective/weather/tests/test_weather_portlet.py b/src/collective/weather/tests/test_weather_portlet.py index 7a25027..2cc8be2 100644 --- a/src/collective/weather/tests/test_weather_portlet.py +++ b/src/collective/weather/tests/test_weather_portlet.py @@ -46,7 +46,6 @@ def test_interfaces(self): self.assertTrue(IPortletAssignment.providedBy(portlet)) self.assertTrue(IPortletDataProvider.providedBy(portlet.data)) - #@unittest.skip('Portlet has no add view... yet') def test_invoke_add_view(self): portlet = getUtility(IPortletType, name=self.name) mapping = self.portal.restrictedTraverse('++contextportlets++plone.leftcolumn') @@ -55,12 +54,11 @@ def test_invoke_add_view(self): del mapping[m] addview = mapping.restrictedTraverse('+/' + portlet.addview) - addview.createAndAdd(data={'header': u'Weather', 'city': u'Cordoba'}) + addview.createAndAdd(data={'header': u'Weather', 'location': u'Cordoba'}) self.assertEqual(len(mapping), 1) self.assertTrue(isinstance(mapping.values()[0], weather.Assignment)) - #@unittest.skip('Portlet has no edit view... yet') def test_invoke_edit_view(self): mapping = PortletAssignmentMapping() @@ -101,7 +99,7 @@ def renderer(self, context=None, request=None, view=None, manager=None, assignme (context, request, view, manager, assignment), IPortletRenderer) def test_render(self): - assignment = weather.Assignment(header=u'Weather', city=u'Cordoba') + assignment = weather.Assignment(header=u'Weather', location=u'Cordoba') r = self.renderer(assignment=assignment) r = r.__of__(self.portal) diff --git a/src/collective/weather/vocabularies.py b/src/collective/weather/vocabularies.py index 70595c5..9b4ed13 100644 --- a/src/collective/weather/vocabularies.py +++ b/src/collective/weather/vocabularies.py @@ -5,15 +5,15 @@ from zope.schema.vocabulary import SimpleVocabulary -def CitiesVocabulary(context): - """ Creates a vocabulary to expose configured cities. +def LocationsVocabulary(context): + """Creates a vocabulary to expose configured locations. """ weather_utility = getUtility(IWeatherUtility) - cities_list = weather_utility.get_cities_list() + locations = weather_utility.get_cities_list() items = [] - for city in cities_list: - items.append(SimpleVocabulary.createTerm( - city['id'], city['location_id'], city['name'])) + for l in locations: + items.append( + SimpleVocabulary.createTerm(l['id'], l['location_id'], l['name'])) return SimpleVocabulary(items) From fbf9ef06f32adebd2b25bdacf9cc1528bfb2b87e Mon Sep 17 00:00:00 2001 From: "Marcos F. Romero" Date: Thu, 31 Oct 2013 16:35:35 -0300 Subject: [PATCH 6/9] Updated CHANGES --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 39cf981..f215f95 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,7 +6,7 @@ There's a frood who really knows where his towel is. 1.0a4 (unreleased) ^^^^^^^^^^^^^^^^^^ -- Nothing changed yet. +- Configurable weather portlet (header and location). [marcosfromero] 1.0a3 (2013-10-29) From d89c3e32ec610f07b5c26f4f3a51f2e39ccf2fd2 Mon Sep 17 00:00:00 2001 From: "Marcos F. Romero" Date: Thu, 31 Oct 2013 16:37:19 -0300 Subject: [PATCH 7/9] Changed double by single quotes --- src/collective/weather/portlets/weather.py | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/collective/weather/portlets/weather.py b/src/collective/weather/portlets/weather.py index d2a1b61..01a6bff 100644 --- a/src/collective/weather/portlets/weather.py +++ b/src/collective/weather/portlets/weather.py @@ -13,8 +13,8 @@ class IWeatherPortlet(IPortletDataProvider): - """A weather portlet. - """ + '''A weather portlet. + ''' header = schema.TextLine( title=_(u'Portlet header'), @@ -31,8 +31,8 @@ class IWeatherPortlet(IPortletDataProvider): class Assignment(base.Assignment): - """Portlet assignment. - """ + '''Portlet assignment. + ''' implements(IWeatherPortlet) @@ -45,15 +45,15 @@ def __init__(self, header=u'', location=u''): @property def title(self): - """This property is used to give the title of the portlet in the + '''This property is used to give the title of the portlet in the 'manage portlets' screen. - """ + ''' return self.header class Renderer(base.Renderer): - """Portlet renderer. - """ + '''Portlet renderer. + ''' render = ViewPageTemplateFile('weather.pt') @@ -76,8 +76,8 @@ def update(self): class AddForm(base.AddForm): - """Portlet add form. - """ + '''Portlet add form. + ''' form_fields = form.Fields(IWeatherPortlet) @@ -88,8 +88,8 @@ def create(self, data): class EditForm(base.EditForm): - """Portlet edit form. - """ + '''Portlet edit form. + ''' form_fields = form.Fields(IWeatherPortlet) From 2b5ddaeb532b31ec9b8dad271caf0ce9df5bfe90 Mon Sep 17 00:00:00 2001 From: hvelarde Date: Thu, 31 Oct 2013 17:43:55 -0200 Subject: [PATCH 8/9] Import from unittest2 for testing compatibility with Python 2.6 --- src/collective/weather/tests/test_vocabularies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collective/weather/tests/test_vocabularies.py b/src/collective/weather/tests/test_vocabularies.py index e712a6b..f2f0aea 100644 --- a/src/collective/weather/tests/test_vocabularies.py +++ b/src/collective/weather/tests/test_vocabularies.py @@ -4,7 +4,7 @@ from zope.component import queryUtility from zope.schema.interfaces import IVocabularyFactory -import unittest +import unittest2 as unittest class VocabulariesTestCase(unittest.TestCase): From 77ca50d44f8801016b52dee60b53b41334d523f3 Mon Sep 17 00:00:00 2001 From: "Marcos F. Romero" Date: Thu, 31 Oct 2013 17:47:49 -0300 Subject: [PATCH 9/9] Portlet improvement --- src/collective/weather/portlets/weather.pt | 5 +++-- src/collective/weather/portlets/weather.py | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/collective/weather/portlets/weather.pt b/src/collective/weather/portlets/weather.pt index 4fdacf3..82dfba1 100644 --- a/src/collective/weather/portlets/weather.pt +++ b/src/collective/weather/portlets/weather.pt @@ -1,13 +1,14 @@
+ tal:define="portal context/@@plone_portal_state/portal; + current_city view/current_city|nothing" + tal:condition="current_city">
Weather