Merge lp:~martin-borho/ubuntu-weather-app/API-features into lp:ubuntu-weather-app/obsolete.trunk

Proposed by Martin Borho
Status: Merged
Approved by: Raúl Yeguas
Approved revision: 19
Merged at revision: 14
Proposed branch: lp:~martin-borho/ubuntu-weather-app/API-features
Merge into: lp:ubuntu-weather-app/obsolete.trunk
Diff against target: 452 lines (+195/-88)
4 files modified
components/AddLocationDialog.qml (+24/-16)
components/LocationTab.qml (+2/-6)
components/WeatherApi.js (+142/-44)
ubuntu-weather-app.qml (+27/-22)
To merge this branch: bzr merge lp:~martin-borho/ubuntu-weather-app/API-features
Reviewer Review Type Date Requested Status
Raúl Yeguas Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+161290@code.launchpad.net

Commit message

Updated to new openweathermap API version 2.5, API calls now in WorkerScript, added fixes for stability

Description of the change

- updated to openweathermap API version 2.5
- now 10 instead of 7 days in daily forecast
- API calls now done in WorkerScript
- handling occasional empty API responses from openweathermap by a prompt retry

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Raúl Yeguas (neokore) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'components/AddLocationDialog.qml'
--- components/AddLocationDialog.qml 2013-04-21 18:05:01 +0000
+++ components/AddLocationDialog.qml 2013-04-27 14:57:26 +0000
@@ -2,17 +2,31 @@
2import Ubuntu.Components 0.12import Ubuntu.Components 0.1
3import Ubuntu.Components.Popups 0.13import Ubuntu.Components.Popups 0.1
4import Ubuntu.Components.ListItems 0.1 as ListItem;4import Ubuntu.Components.ListItems 0.1 as ListItem;
5import "WeatherApi.js" as Api
65
7// Dialog for adding a new location6// Dialog for adding a new location
8Component {7Component {
9 id: addLocationDialog8 id: addLocationDialog
10 Dialog {9
10 Dialog {
11 id: addLocationDialogue11 id: addLocationDialogue
12 height: units.gu(10)12 height: units.gu(10)
13 title: i18n.tr("Add location")13 title: i18n.tr("Add location")
14 text: i18n.tr("Write a city name o pick it by your location")14 text: i18n.tr("Write a city name o pick it by your location")
1515
16 WorkerScript {
17 id: searchWorker
18 source: "./WeatherApi.js"
19 onMessage: {
20 if(!messageObject.error) {
21 messageObject.result.locations.forEach(function(loc) {
22 citiesModel.append(loc);
23 });
24 } else {
25 console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
26 }
27 }
28 }
29
16 function locationSelected(index) {30 function locationSelected(index) {
17 var location = citiesModel.get(index);31 var location = citiesModel.get(index);
18 // make clone32 // make clone
@@ -21,18 +35,6 @@
21 PopupUtils.close(addLocationDialogue)35 PopupUtils.close(addLocationDialogue)
22 }36 }
2337
24 function onApiError(err) {
25 // TODO display
26 console.log("onApiError")
27 console.log(err.msg+" / "+err.request.url)
28 }
29
30 function onSearchSuccess(locations) {
31 locations.results.forEach(function(v) {
32 citiesModel.append(v);
33 })
34 }
35
36 Row {38 Row {
37 TextField {39 TextField {
38 id: locationString40 id: locationString
@@ -47,7 +49,10 @@
4749
48 onClicked: {50 onClicked: {
49 citiesModel.clear();51 citiesModel.clear();
50 Api.WeatherApi.searchLocationByName({name:locationString.text, units:"metric"},onSearchSuccess,onApiError);52 searchWorker.sendMessage({
53 action: "searchByName",
54 params: {name:locationString.text, units:"metric"}
55 })
51 }56 }
52 }57 }
53 }58 }
@@ -57,7 +62,10 @@
57 text: i18n.tr("Use my own location")62 text: i18n.tr("Use my own location")
58 onClicked: {63 onClicked: {
59 citiesModel.clear();64 citiesModel.clear();
60 Api.WeatherApi.searchLocationByPoint({coords: {"lon":10,"lat":53.549999}, units:"metric"}, onSearchSuccess,onApiError);65 searchWorker.sendMessage({
66 action: "searchByPoint",
67 params: {coords: {"lon":10,"lat":53.549999}, units:"metric"}
68 })
61 }69 }
62 }70 }
6371
6472
=== modified file 'components/LocationTab.qml'
--- components/LocationTab.qml 2013-04-24 17:37:44 +0000
+++ components/LocationTab.qml 2013-04-27 14:57:26 +0000
@@ -43,18 +43,14 @@
43 dayForecastModel.append({43 dayForecastModel.append({
44 dateRel: "",//Tomorrow",44 dateRel: "",//Tomorrow",
45 date: formatTimestamp(dailyForecasts[x].timestamp, 'dddd, dd MMMM yyyy'),45 date: formatTimestamp(dailyForecasts[x].timestamp, 'dddd, dd MMMM yyyy'),
46 temp: dailyForecasts[x].temp,46 temp: dailyForecasts[x].max,
47 tempMin: dailyForecasts[x].night,47 tempMin: dailyForecasts[x].min,
48 cond: dailyForecasts[x].condition.id,48 cond: dailyForecasts[x].condition.id,
49 condIcon: dailyForecasts[x].condition.icon49 condIcon: dailyForecasts[x].condition.icon
50 });50 });
51 }51 }
52 }52 }
5353
54 function locationAdded(location) {
55 mainView.locationAdded({"service":"openweathermap","service_id":2643743,"name":"London","country":"GB"});
56 }
57
58 // Menu for options54 // Menu for options
59 page: Page {55 page: Page {
6056
6157
=== modified file 'components/WeatherApi.js'
--- components/WeatherApi.js 2013-04-21 18:05:01 +0000
+++ components/WeatherApi.js 2013-04-27 14:57:26 +0000
@@ -4,22 +4,25 @@
4 /**4 /**
5 provides neccessary methods for requesting and preparing data from OpenWeatherMap.org5 provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
6 */6 */
7 var _baseUrl = "http://api.openweathermap.org/data/2.1/";7 var _baseUrl = "http://api.openweathermap.org/data/2.5/";
8
8 //9 //
9 function _buildSearchResult(request, data) {10 function _buildSearchResult(request, data) {
10 var searchResult = {11 var searchResult = {
11 results: [],12 locations: [],
12 request: request13 request: request
13 }14 }
14 data.list.forEach(function(r) {15 if(data.cod === "200" && data.list) {
15 searchResult.results.push({16 data.list.forEach(function(r) {
16 service: "openweathermap",17 searchResult.locations.push({
17 service_id: r.id,18 service: "openweathermap",
18 name: r.name,19 service_id: r.id,
19 coord: r.coords,20 name: r.name,
20 country: (r.sys && r.sys.country) ? r.sys.country : ""21 coord: r.coords,
21 });22 country: (r.sys && r.sys.country) ? r.sys.country : ""
22 })23 });
24 })
25 }
23 return searchResult;26 return searchResult;
24 }27 }
25 //28 //
@@ -73,10 +76,12 @@
73 // TODO date_txt76 // TODO date_txt
74 forecastResult.results.push({77 forecastResult.results.push({
75 timestamp: f.dt,78 timestamp: f.dt,
76 temp: f.temp,79 day: f.temp.day,
77 night: f.night,80 min: f.temp.min,
78 eve: f.eve,81 max: f.temp.max,
79 morn: f.morn,82 night: f.temp.night,
83 eve: f.temp.eve,
84 morn: f.temp.morn,
80 pressure: f.pressure,85 pressure: f.pressure,
81 humidity: f.humidity,86 humidity: f.humidity,
82 condition: f.weather[0],87 condition: f.weather[0],
@@ -92,14 +97,14 @@
92 getSearchByNameRequest: function(params) {97 getSearchByNameRequest: function(params) {
93 var request = {98 var request = {
94 type: "search",99 type: "search",
95 url: _baseUrl+"find/name?q="+encodeURIComponent(params.name)+"&units="+params.units,100 url: _baseUrl+"find?q="+encodeURIComponent(params.name)+"&units="+params.units,
96 formatter: _buildSearchResult101 formatter: _buildSearchResult
97 }102 }
98 return request;103 return request;
99 },104 },
100 //105 //
101 getSearchByPointRequest: function(params) {106 getSearchByPointRequest: function(params) {
102 var url = _baseUrl+"find/city?lat="+encodeURIComponent(params.coords.lat)107 var url = _baseUrl+"find?lat="+encodeURIComponent(params.coords.lat)
103 +"&lon="+encodeURIComponent(params.coords.lon)+"&units="+params.units,108 +"&lon="+encodeURIComponent(params.coords.lon)+"&units="+params.units,
104 request = {109 request = {
105 type: "search",110 type: "search",
@@ -112,7 +117,7 @@
112 getCurrentConditionRequest: function(params) {117 getCurrentConditionRequest: function(params) {
113 var request = {118 var request = {
114 type: "current",119 type: "current",
115 url: _baseUrl + "weather/city/"+params.location.service_id+"?units="+params.units,120 url: _baseUrl + "weather?id="+params.location.service_id+"&units="+params.units,
116 formatter: _buildCurrentCondition121 formatter: _buildCurrentCondition
117 }122 }
118 return request;123 return request;
@@ -121,7 +126,7 @@
121 getForecastRequest: function(params) {126 getForecastRequest: function(params) {
122 var request = {127 var request = {
123 type: "forecast",128 type: "forecast",
124 url: _baseUrl + "forecast/city/"+params.location.service_id+"?units="+params.units,129 url: _baseUrl + "forecast?id="+params.location.service_id+"&units="+params.units,
125 formatter: _buildForecast130 formatter: _buildForecast
126 }131 }
127 return request;132 return request;
@@ -130,7 +135,8 @@
130 getDailyForecastRequest: function(params) {135 getDailyForecastRequest: function(params) {
131 var request = {136 var request = {
132 type: "daily",137 type: "daily",
133 url: _baseUrl + "forecast/city/"+params.location.service_id+"?mode=daily_compact&units="+params.units,138 url: _baseUrl + "forecast/daily?"+params.location.service_id
139 +"&mode=daily_compact&cnt=10&units="+params.units,
134 formatter: _buildDailyForecast140 formatter: _buildDailyForecast
135 }141 }
136 return request;142 return request;
@@ -151,24 +157,32 @@
151 return _services["openweathermap"];157 return _services["openweathermap"];
152 }158 }
153 //159 //
154 function _sendRequest(request, onSuccess, onError) {160 function _sendRequest(request, onSuccess, onError) {
155 var xmlHttp = new XMLHttpRequest();161 var xmlHttp = new XMLHttpRequest();
156 if (xmlHttp) {162 if (xmlHttp) {
157 console.log(request.url);163 console.log(request.url);
158 xmlHttp.open('GET', request.url, true);164 xmlHttp.open('GET', request.url, true);
159 xmlHttp.onreadystatechange = function () {165 xmlHttp.onreadystatechange = function () {
160 if (xmlHttp.readyState == 4) {166 try {
161 var json = JSON.parse(xmlHttp.responseText);167 if (xmlHttp.readyState == 4) {
162 if(xmlHttp.status === 200) {168 //console.log(xmlHttp.responseText);
163 var result = request.formatter(request,json);169 var json = JSON.parse(xmlHttp.responseText);
164 onSuccess(result);170 if(xmlHttp.status === 200) {
165 } else {171 var result = request.formatter(request,json);
166 onError({error: true, msg: "something went wrong", request: request});172 onSuccess(result);
173 } else {
174 onError({
175 msg: "wrong response http code, got "+xmlHttp.status,
176 request: request
177 });
178 }
179 }
180 } catch (e) {
181 onError({msg: "wrong response data format", request: request});
167 }182 }
168 }183 };
169 };184 xmlHttp.send(null);
170 xmlHttp.send(null);185 }
171 }
172 }186 }
173 //187 //
174 return {188 return {
@@ -197,22 +211,106 @@
197 _sendRequest(request, onSuccess, onError);211 _sendRequest(request, onSuccess, onError);
198 },212 },
199 //213 //
214 search: function(mode, params, onSuccess, onError) {
215 var searchFunc = (mode === "point") ? this.searchLocationByPoint: this.searchLocationByName,
216 retryHandler = (function(err) {
217 console.log("search retry of "+err.request.url);
218 searchFunc(params, onSuccess, onError);
219 });
220 searchFunc(params, onSuccess, retryHandler);
221 },
222 //
200 getLocationData: function(params, onSuccess, onError) {223 getLocationData: function(params, onSuccess, onError) {
201 var response = {224 var retryMap = {
202 location: params.location225 current:this.getCurrentCondition,
226 daily:this.getDailyForecast,
227 forecast:this.getForecast
228 },
229 response = {
230 location: params.location,
231 db: (params.db) ? params.db : null
203 },232 },
204 addDataToResponse = (function(data) {233 addDataToResponse = (function(data) {
205 response[data.request.type] = data;234 response[data.request.type] = data;
206 if(response["current"] !== undefined && response["forecast"] !== undefined && response["daily"] !== undefined) {235 if(response["current"] !== undefined
236 && response["forecast"] !== undefined
237 && response["daily"] !== undefined) {
207 onSuccess(response);238 onSuccess(response);
208 }239 }
209 });240 }),
241 onErrorHandler = (function(err) {
242 onError(err);
243 }),
244 retryHandler = (function(err) {
245 console.log("retry of "+err.request.url);
246 var retryFunc = retryMap[err.request.type];
247 retryFunc(params, addDataToResponse, onErrorHandler);
248 })
210249
211 this.getCurrentCondition(params, addDataToResponse, addDataToResponse)250 this.getCurrentCondition(params, addDataToResponse, retryHandler);
212 this.getForecast(params, addDataToResponse, addDataToResponse);251 this.getForecast(params, addDataToResponse, retryHandler);
213 this.getDailyForecast(params, addDataToResponse, addDataToResponse);252 this.getDailyForecast(params, addDataToResponse, retryHandler);
214 }253 }
215 }254 }
216})({255})({
217 "openweathermap": OpenWeatherMapApi256 "openweathermap": OpenWeatherMapApi
218});257});
258
259
260/*
261 following WorkerScript handles the data requests against the weather API.
262 "message" requires a "params" property with the required params to perform
263 the API call and an "action" property, which will be added also to the response.
264*/
265if(typeof WorkerScript != "undefined") {
266 WorkerScript.onMessage = function(message) {
267 // handles the response data
268 var finished = function(result) {
269 WorkerScript.sendMessage({
270 action: message.action,
271 result: result
272 })
273 }
274 // handles errors
275 var onError = function(err) {
276 console.log(JSON.stringify(err, null, true));
277 WorkerScript.sendMessage({ 'error': err})
278 }
279 // keep order of locations, sort results
280 var sortDataResults = function(locA, locB) {
281 return locA.db.id - locB.db.id;
282 }
283 // perform the api calls
284 if(message.action === "searchByName") {
285 WeatherApi.search("name", message.params, finished, onError);
286 } else if(message.action === "searchByPoint") {
287 WeatherApi.search("point", message.params, finished, onError);
288 } else if(message.action === "newLocationData") {
289 WeatherApi.getLocationData(message.params, finished, onError);
290 } else if(message.action === "updateData") {
291 var locLength = message.params.locations.length,
292 locUpdated = 0,
293 result = [];
294 if(locLength > 0) {
295 message.params.locations.forEach(function(loc) {
296 var updatedHnd = function (newData) {
297 locUpdated += 1;
298 result.push(newData);
299 if(locUpdated === locLength) {
300 result.sort(sortDataResults);
301 finished(result);
302 }
303 },
304 params = {
305 location:loc.location,
306 db: loc.db,
307 units: message.params.units
308 };
309 WeatherApi.getLocationData(params, updatedHnd, onError);
310 })
311 } else {
312 finished(result);
313 }
314 }
315 }
316}
219317
=== modified file 'ubuntu-weather-app.qml'
--- ubuntu-weather-app.qml 2013-04-25 13:04:02 +0000
+++ ubuntu-weather-app.qml 2013-04-27 14:57:26 +0000
@@ -2,7 +2,6 @@
2import Ubuntu.Components 0.12import Ubuntu.Components 0.1
3import "components" as Components3import "components" as Components
4import Ubuntu.Components.Popups 0.14import Ubuntu.Components.Popups 0.1
5import "components/WeatherApi.js" as Api
65
7MainView {6MainView {
8 // objectName for functional testing purposes (autopilot-qt5)7 // objectName for functional testing purposes (autopilot-qt5)
@@ -17,6 +16,25 @@
17 property var locationsList: []16 property var locationsList: []
18 property var tabsObject: null17 property var tabsObject: null
1918
19 WorkerScript {
20 id: locationDataWorker
21 source: "components/WeatherApi.js"
22 onMessage: {
23 if(!messageObject.error) {
24 if(messageObject.action === "updateData") {
25 messageObject.result.forEach(function(loc) {
26 storage.updateLocation(loc.db.id, loc);
27 });
28 buildTabs(messageObject.result);
29 } else if(messageObject.action === "newLocationData") {
30 onNewLocationDataSuccess(messageObject.result);
31 }
32 } else {
33 console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
34 }
35 }
36 }
37
20 // see https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1124071, temporary workaround38 // see https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1124071, temporary workaround
21 function buildTabs(locations) {39 function buildTabs(locations) {
22 locationsList = locations;40 locationsList = locations;
@@ -43,13 +61,10 @@
43 }61 }
4462
45 function locationAdded(locationObj) {63 function locationAdded(locationObj) {
46 Api.WeatherApi.getLocationData({location:locationObj, units: "metric"}, onNewLocationDataSuccess, onApiError);64 locationDataWorker.sendMessage({
47 }65 action: "newLocationData",
4866 params: {location:locationObj, units:"metric"}
49 function onApiError(err) {67 })
50 // TODO show error popup
51 console.log("onApiError")
52 console.log(err.msg+" / "+err.request.url)
53 }68 }
5469
55 function onNewLocationDataSuccess(resp) {70 function onNewLocationDataSuccess(resp) {
@@ -59,20 +74,10 @@
5974
60 function refreshData() {75 function refreshData() {
61 storage.getLocations(function(locations) {76 storage.getLocations(function(locations) {
62 var locLength = locations.length,77 locationDataWorker.sendMessage({
63 locUpdated = 0;78 action: "updateData",
64 if(locLength > 0) {79 params: {locations:locations, units:"metric"}
65 locations.forEach(function(loc) {80 })
66 var updatedHnd = function (newData) {
67 locUpdated += 1;
68 storage.updateLocation(loc.db.id, newData);
69 if(locUpdated === locLength) {
70 buildTabs(locations);
71 }
72 }
73 Api.WeatherApi.getLocationData({location:loc.location, units: "metric"}, updatedHnd, onApiError);
74 })
75 } else buildTabs(locations);
76 });81 });
77 }82 }
7883

Subscribers

People subscribed via source and target branches