Merge lp:~submarine/ubuntu-scopes/openweathermap-previews into lp:~submarine/ubuntu-scopes/openweathermap

Proposed by David Callé
Status: Merged
Approved by: Michal Hruby
Approved revision: 24
Merged at revision: 21
Proposed branch: lp:~submarine/ubuntu-scopes/openweathermap-previews
Merge into: lp:~submarine/ubuntu-scopes/openweathermap
Diff against target: 313 lines (+187/-27)
3 files modified
data/openweathermap.scope.in (+1/-1)
src/unity_openweathermap_daemon.py (+184/-24)
tests/test_openweathermap.py (+2/-2)
To merge this branch: bzr merge lp:~submarine/ubuntu-scopes/openweathermap-previews
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Michal Hruby (community) Approve
Review via email: mp+155729@code.launchpad.net

Commit message

Initial previews (with Flickr weather project support)

Description of the change

Initial previews.
- Displays a geolocalized photo with matching weather (using Flickr weather project)
- If no photo is found, a "Submit photo" button appears, opening the Flickr weather project group.

To post a comment you must log in.
Revision history for this message
Michal Hruby (mhr3) wrote :

From a quick test it looks and works great! :)

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/openweathermap.scope.in'
2--- data/openweathermap.scope.in 2013-03-15 17:29:42 +0000
3+++ data/openweathermap.scope.in 2013-03-27 13:24:21 +0000
4@@ -4,7 +4,7 @@
5 Icon=
6 _Keywords=openweathermap;weather;sky;forecast;
7 RequiredMetadata=
8-OptionalMetadata=
9+OptionalMetadata=min_temp[s];max_temp[s];pressure[s];humidity[s];wind_speed[s];wind_direction[s];latitude[s];longitude[s];
10 Loader=/usr/share/unity-scopes/openweathermap/unity_openweathermap_daemon.py
11 RemoteContent=true
12 Type=info
13
14=== modified file 'src/unity_openweathermap_daemon.py'
15--- src/unity_openweathermap_daemon.py 2013-03-21 18:00:25 +0000
16+++ src/unity_openweathermap_daemon.py 2013-03-27 13:24:21 +0000
17@@ -21,6 +21,7 @@
18 import json
19 import datetime
20 import gettext
21+from random import randrange
22
23 APP_NAME = 'unity-scope-openweathermap'
24 LOCAL_PATH = '/usr/share/locale/'
25@@ -39,7 +40,7 @@
26 PROVIDER_ICON = SVG_DIR+'service-openweathermap.svg'
27 DEFAULT_RESULT_ICON = SVG_DIR+'result-info.svg'
28 DEFAULT_RESULT_MIMETYPE = 'text/html'
29-DEFAULT_RESULT_TYPE = Unity.ResultType.PERSONAL
30+DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT
31
32 c1 = {'id' :'weather',
33 'name' :_('Weather Forecast'),
34@@ -47,13 +48,37 @@
35 'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
36 CATEGORIES = [c1]
37 FILTERS = []
38-EXTRA_METADATA = []
39+m1 = {'id' :'min_temp',
40+ 'type' :'s',
41+ 'field':Unity.SchemaFieldType.OPTIONAL}
42+m2 = {'id' :'max_temp',
43+ 'type' :'s',
44+ 'field':Unity.SchemaFieldType.OPTIONAL}
45+m3 = {'id' :'pressure',
46+ 'type' :'s',
47+ 'field':Unity.SchemaFieldType.OPTIONAL}
48+m4 = {'id' :'humidity',
49+ 'type' :'s',
50+ 'field':Unity.SchemaFieldType.OPTIONAL}
51+m5 = {'id' :'wind_speed',
52+ 'type' :'s',
53+ 'field':Unity.SchemaFieldType.OPTIONAL}
54+m6 = {'id' :'wind_direction',
55+ 'type' :'s',
56+ 'field':Unity.SchemaFieldType.OPTIONAL}
57+m7 = {'id' :'latitude',
58+ 'type' :'s',
59+ 'field':Unity.SchemaFieldType.OPTIONAL}
60+m8 = {'id' :'longitude',
61+ 'type' :'s',
62+ 'field':Unity.SchemaFieldType.OPTIONAL}
63+EXTRA_METADATA = [m1, m2, m3, m4, m5, m6, m7, m8]
64
65 def weather_search(query, s_type):
66 print (query)
67 query = urllib.parse.quote(str(query))
68 data = None
69- if not query:
70+ if not query or len(query) <= 1:
71 return data
72 if s_type == 'forecast':
73 uri = "%s%s?mode=daily_compact&units=metric" % (SEARCH_URI[1], query)
74@@ -113,10 +138,30 @@
75 wind = place_data['list'][0]['wind']['speed']
76 temp_c = float(place_data['list'][0]['main']['temp'])
77 temp_f = temp_c*(9.0/5.0)+32
78- temp = '%i%sC/%i%sF' % (int(temp_c), u"\u00B0", int(temp_f), u"\u00B0")
79- title = _('Today') + '\n' + temp
80+ temp = '%i%sC / %i%sF' % (int(temp_c), u"\u00B0", int(temp_f), u"\u00B0")
81+ name = place_data['list'][0]['name']
82+ country = place_data['list'][0]['sys']['country']
83+ title = name + ', ' + country + '\n' + temp
84 city_id = place_data['list'][0]['id']
85 uri = place_data['list'][0]['url']
86+ min_temp_c = float(place_data['list'][0]['main']['temp_min'])
87+ min_temp_f = min_temp_c*(9.0/5.0)+32
88+ min_temp = '%i%sC / %i%sF' % (int(min_temp_c), u"\u00B0", int(min_temp_f), u"\u00B0")
89+ max_temp_c = float(place_data['list'][0]['main']['temp_max'])
90+ max_temp_f = max_temp_c*(9.0/5.0)+32
91+ max_temp = '%i%sC / %i%sF' % (int(max_temp_c), u"\u00B0", int(max_temp_f), u"\u00B0")
92+ try:
93+ pressure = place_data['list'][0]['main']['pressure']
94+ except:
95+ pressure = ''
96+ try:
97+ humidity = place_data['list'][0]['main']['humidity']
98+ except:
99+ humidity = ''
100+ wind_speed = place_data['list'][0]['wind']['speed']
101+ wind_direction = place_data['list'][0]['wind']['deg']
102+ latitude = place_data['list'][0]['coord']['lat']
103+ longitude = place_data['list'][0]['coord']['lon']
104 except Exception as error:
105 print(error)
106 return results
107@@ -125,7 +170,15 @@
108 results.append({'uri':uri,
109 'icon':icon,
110 'title':title,
111- 'comment':description})
112+ 'comment':description,
113+ 'min_temp':GLib.Variant('s', min_temp),
114+ 'max_temp':GLib.Variant('s', max_temp),
115+ 'pressure':GLib.Variant('s', str(pressure)),
116+ 'humidity':GLib.Variant('s', str(humidity)),
117+ 'wind_speed':GLib.Variant('s', str(wind_speed)),
118+ 'wind_direction':GLib.Variant('s', str(wind_direction)),
119+ 'latitude':GLib.Variant('s', str(latitude)),
120+ 'longitude':GLib.Variant('s', str(longitude))})
121 forecast = weather_search(city_id, 'forecast')
122 if not forecast or not "list" in forecast:
123 return results
124@@ -140,18 +193,35 @@
125 try:
126 description = forecast['list'][i]['weather'][0]['description']
127 icon = get_icon(forecast['list'][i]['weather'][0]['icon'])
128- wind = forecast['list'][i]['speed']
129 temp_c = float(forecast['list'][i]['temp'])
130 temp_f = temp_c*(9.0/5.0)+32
131- temp = '%i%sC/%i%sF' % (int(temp_c), u"\u00B0", int(temp_f), u"\u00B0")
132+ temp = '%i%sC / %i%sF' % (int(temp_c), u"\u00B0", int(temp_f), u"\u00B0")
133 title = day_name + '\n' + temp
134+ min_temp_c = float(forecast['list'][i]['morn'])
135+ min_temp_f = min_temp_c*(9.0/5.0)+32
136+ min_temp = '%i%sC / %i%sF' % (int(min_temp_c), u"\u00B0", int(min_temp_f), u"\u00B0")
137+ max_temp_c = float(forecast['list'][i]['eve'])
138+ max_temp_f = max_temp_c*(9.0/5.0)+32
139+ max_temp = '%i%sC / %i%sF' % (int(max_temp_c), u"\u00B0", int(max_temp_f), u"\u00B0")
140+ pressure = forecast['list'][i]['pressure']
141+ humidity = forecast['list'][i]['humidity']
142+ wind_speed = forecast['list'][i]['speed']
143+ wind_direction = forecast['list'][i]['deg']
144 except Exception as error:
145 print(error)
146 return results
147 results.append({'uri':uri+"#"+day_name,
148 'icon':icon,
149 'title':title,
150- 'comment':description})
151+ 'comment':description,
152+ 'min_temp':GLib.Variant('s', min_temp),
153+ 'max_temp':GLib.Variant('s', max_temp),
154+ 'pressure':GLib.Variant('s', str(pressure)),
155+ 'humidity':GLib.Variant('s', str(humidity)),
156+ 'wind_speed':GLib.Variant('s', str(wind_speed)),
157+ 'wind_direction':GLib.Variant('s', str(wind_direction)),
158+ 'latitude':GLib.Variant('s', str(latitude)),
159+ 'longitude':GLib.Variant('s', str(longitude))})
160 return results
161
162
163@@ -188,21 +258,97 @@
164 i['comment'] = ''
165 if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '':
166 i['dnd_uri'] = i['uri']
167- i['metadata'] = {}
168- if EXTRA_METADATA:
169- for e in i:
170- for m in EXTRA_METADATA:
171- if m['id'] == e:
172- i['metadata'][e] = i[e]
173- i['metadata']['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS)
174- result = Unity.ScopeResult.create(str(i['uri']), str(i['icon']),
175- i['category'], i['result_type'],
176- str(i['mimetype']), str(i['title']),
177- str(i['comment']), str(i['dnd_uri']),
178- i['metadata'])
179- result_set.add_result(result)
180- except Exception as error:
181- print (error)
182+ i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS)
183+ result_set.add_result(**i)
184+ except Exception as error:
185+ print (error)
186+
187+class Preview (Unity.ResultPreviewer):
188+
189+ def get_image(self,q, lat, lon):
190+ print (lat, lon)
191+ flick_map={'weather-clear':'clear',
192+ 'weather-clear-night':'clear',
193+ 'weather-few-clouds':'cloudy',
194+ 'weather-few-clouds-night':'cloudy',
195+ 'weather-clouds':'cloudy',
196+ 'weather-clouds-night':'cloudy',
197+ 'weather-overcast':'cloudy',
198+ 'weather-overcast':'cloudy',
199+ 'weather-showers-scattered':'rain',
200+ 'weather-showers-scattered':'rain',
201+ 'weather-showers':'rain',
202+ 'weather-showers':'rain',
203+ 'weather-storm':'storm',
204+ 'weather-storm':'storm',
205+ 'weather-snow':'snow',
206+ 'weather-snow':'snow',
207+ 'weather-fog':'fog',
208+ 'weather-fog':'fog'}
209+ license_list = ["All Rights Reserved",
210+ "Attribution-NonCommercial-ShareAlike License",
211+ "Attribution-NonCommercial License",
212+ "Attribution-NonCommercial-NoDerivs License",
213+ "Attribution License",
214+ "Attribution-ShareAlike License",
215+ "Attribution-NoDerivs License",
216+ "No known copyright restrictions",
217+ "United States Government Work"]
218+ image = None
219+ query = urllib.parse.quote(flick_map[q])
220+ key = 'd87224f0b467093b2a87fd788d950e27'
221+ uri = 'http://secure.flickr.com/services/rest/?method=flickr.photos.search&nojsoncallback=1&per_page=20&api_key=%s&extras=url_m,owner_name,license&group_id=1463451@N25&format=json&tag_mode=all&tags=%s&bbox=%f,%f,%f,%f' % (key,
222+ query,
223+ round(float(lon) - 0.1, 2), round(float(lat) - 0.1, 2),
224+ round(float(lon) + 0.1, 2), round(float(lat) +0.1, 2))
225+ print (uri)
226+ try:
227+ response = urllib.request.urlopen(uri).read()
228+ data = json.loads(response.decode('utf-8'))
229+ photo = randrange(len(data['photos']['photo']))
230+ image = data['photos']['photo'][photo]['url_m']
231+ owner = data['photos']['photo'][photo]['ownername']
232+ license = license_list[int(data['photos']['photo'][photo]['license'])]
233+ link = 'https://www.flickr.com/photos/%s/%s' % (data['photos']['photo'][photo]['owner'], data['photos']['photo'][photo]['id'])
234+ except Exception as error:
235+ print (error)
236+ image, owner, license, link = None, None, None, None
237+ return image, owner, license, link
238+
239+ def do_run(self):
240+
241+ preview = Unity.GenericPreview.new(self.result.title.split('\n')[0], '', None)
242+ preview.props.subtitle = self.result.comment.capitalize()
243+ image, owner, license, link = self.get_image(self.result.icon_hint,
244+ self.result.metadata['latitude'].get_string(),
245+ self.result.metadata['longitude'].get_string())
246+
247+ gfile_icon = Gio.file_new_for_path(PROVIDER_ICON)
248+ gicon = Gio.FileIcon.new (gfile_icon)
249+ open_action = Unity.PreviewAction.new("open", _("Open Weather Map"), gicon)
250+ preview.add_action(open_action)
251+ preview.add_info(Unity.InfoHint.new("min_temp", _("Minimum Temperature"), None, self.result.metadata['min_temp'].get_string()))
252+ preview.add_info(Unity.InfoHint.new("max_temp", _("Maximum Temperature"), None, self.result.metadata['max_temp'].get_string()))
253+ preview.add_info(Unity.InfoHint.new("wind_speed", _("Wind Speed"), None, self.result.metadata['wind_speed'].get_string() + ' m/s'))
254+ preview.add_info(Unity.InfoHint.new("wind_direction", _("Wind Direction"), None, self.result.metadata['wind_direction'].get_string() + u"\u00B0"))
255+ if self.result.metadata['pressure'].get_string() != '':
256+ preview.add_info(Unity.InfoHint.new("pressure", _("Pressure"), None, self.result.metadata['pressure'].get_string() + ' hPa'))
257+ if self.result.metadata['humidity'].get_string() != '':
258+ preview.add_info(Unity.InfoHint.new("humidity", _("Humidity"), None, self.result.metadata['humidity'].get_string() + '%'))
259+ if image:
260+ preview.props.image_source_uri = image
261+ gfile_icon = Gio.file_new_for_path(SVG_DIR+'service-flickr.svg')
262+ gicon = Gio.FileIcon.new (gfile_icon)
263+ preview.add_info(Unity.InfoHint.new("photo_owner", _("Photo source"), gicon, 'Flickr'))
264+ preview.add_info(Unity.InfoHint.new("photo_owner", _("Photo credit"), None, owner))
265+ preview.add_info(Unity.InfoHint.new("photo_license", _("Photo license"), None, license))
266+ else:
267+ gfile_icon = Gio.file_new_for_path(SVG_DIR+'service-flickr.svg')
268+ gicon = Gio.FileIcon.new (gfile_icon)
269+ submit_action = Unity.PreviewAction.new("submit", _("Submit Photo"), gicon)
270+ preview.add_action(submit_action)
271+ return preview
272+
273
274 class Scope (Unity.AbstractScope):
275 def __init__(self):
276@@ -255,5 +401,19 @@
277 se = MySearch (search_context)
278 return se
279
280+ def do_create_previewer(self, result, metadata):
281+ rp = Preview()
282+ rp.set_scope_result(result)
283+ rp.set_search_metadata(metadata)
284+ return rp
285+
286+ def do_activate(self, result, metadata, id):
287+ if not id:
288+ return Unity.ActivationResponse(handled=Unity.HandledType.SHOW_PREVIEW, goto_uri=result.uri)
289+ if id == 'submit':
290+ uri = 'https://www.flickr.com/groups/projectweather/'
291+ return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=uri)
292+ return
293+
294 def load_scope():
295 return Scope()
296
297=== modified file 'tests/test_openweathermap.py'
298--- tests/test_openweathermap.py 2013-03-21 18:20:20 +0000
299+++ tests/test_openweathermap.py 2013-03-27 13:24:21 +0000
300@@ -43,11 +43,11 @@
301 'file:tests/data/mock_openweathermap_city_pass#']
302 expected_results = ['http://openweathermap.org/city/3026083',
303 'weather-snow',
304- 'Today\n0°C/33°F',
305+ 'Chatillon, FR\n0°C / 33°F',
306 'heavy snow',
307 'http://openweathermap.org/city/3026083#Tomorrow',
308 'weather-clouds',
309- 'Tomorrow\n257°C/494°F',
310+ 'Tomorrow\n257°C / 494°F',
311 'scattered clouds']
312 results = []
313 for s in ['query']:

Subscribers

People subscribed via source and target branches

to all changes: