Merge lp:~submarine/ubuntu-scopes/openweathermap-previews into lp:~submarine/ubuntu-scopes/openweathermap
- openweathermap-previews
- Merge into 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 |
Related bugs: |
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
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']: |
From a quick test it looks and works great! :)