Merge lp:~martin-borho/ubuntu-weather-app/reboot-data into lp:ubuntu-weather-app
- reboot-data
- Merge into reboot
Status: | Merged |
---|---|
Approved by: | Martin Borho |
Approved revision: | 7 |
Merged at revision: | 4 |
Proposed branch: | lp:~martin-borho/ubuntu-weather-app/reboot-data |
Merge into: | lp:ubuntu-weather-app |
Diff against target: |
992 lines (+927/-0) 7 files modified
CMakeLists.txt (+3/-0) app/CMakeLists.txt (+1/-0) app/data/CMakeLists.txt (+5/-0) app/data/Storage.qml (+136/-0) app/data/WeatherApi.js (+743/-0) app/ubuntu-weather-app.qml (+38/-0) key.js (+1/-0) |
To merge this branch: | bzr merge lp:~martin-borho/ubuntu-weather-app/reboot-data |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Approve | |
Victor Thompson | Approve | ||
Review via email: mp+247764@code.launchpad.net |
Commit message
Added WeatherApi.js data provider client. Also added old Storage.qml to reuse data from the old weather app until storage is reimplemented.
Description of the change
Added WeatherApi.js for data provider access. Added also old Storage.qml with LocalStorage for using locations (and settings?) from the old weather app, until storage with u1db is reimplemented!
When the app is started, locations found in LocalStorage will get updated. Except they are not older than 30mins, than the data-client will return the data which was given in for the location (cached!).
Example calls from QML:
function responseDataHan
});
}
} else {
// handle errors
}
}
function demoLoadLoactio
var payload = {
],
}
}
// request weather data from the backend with callback to call with the results,
// cache handling in backend!
}
function demoGeoLookup() {
})
var byPointPayload = {"action"
})
var byNamePayload = {"action"
})
}
Component.
}
- 6. By Martin Borho
-
fixed typo
Victor Thompson (vthompson) wrote : | # |
It might also be a good idea to rename the "KEYS_FILE" variable to be "KEY_FILE", assuming it won't need to hold multiple API keys.
- 7. By Martin Borho
-
renamed KEYS_FILE to KEY_FILE
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:7
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) : | # |
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-01-23 23:15:52 +0000 |
3 | +++ CMakeLists.txt 2015-01-29 08:55:03 +0000 |
4 | @@ -35,6 +35,7 @@ |
5 | set(DESKTOP_FILE "${APP_HARDCODE}.desktop") |
6 | set(ICON weather-app@30.png) |
7 | set(AUTOPILOT_DIR ubuntu_weather_app) |
8 | +set(KEY_FILE key.js) |
9 | |
10 | # Set install paths |
11 | if(CLICK_MODE) |
12 | @@ -99,6 +100,8 @@ |
13 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE} |
14 | DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) |
15 | |
16 | +install(FILES ${KEY_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX}) |
17 | + |
18 | add_subdirectory(app) |
19 | add_subdirectory(backend) |
20 | add_subdirectory(po) |
21 | |
22 | === modified file 'app/CMakeLists.txt' |
23 | --- app/CMakeLists.txt 2015-01-23 23:15:52 +0000 |
24 | +++ app/CMakeLists.txt 2015-01-29 08:55:03 +0000 |
25 | @@ -12,4 +12,5 @@ |
26 | install(FILES ${MAIN_QML} DESTINATION ${UBUNTU-WEATHER_APP_DIR}) |
27 | |
28 | add_subdirectory(components) |
29 | +add_subdirectory(data) |
30 | |
31 | |
32 | === added directory 'app/data' |
33 | === added file 'app/data/CMakeLists.txt' |
34 | --- app/data/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
35 | +++ app/data/CMakeLists.txt 2015-01-29 08:55:03 +0000 |
36 | @@ -0,0 +1,5 @@ |
37 | +file(GLOB DATA_QML_JS_FILES *.qml *.js) |
38 | + |
39 | +add_custom_target(ubuntu-weather-app_data_QMlFiles ALL SOURCES ${DATA_QML_JS_FILES}) |
40 | + |
41 | +install(FILES ${DATA_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/data) |
42 | |
43 | === added file 'app/data/Storage.qml' |
44 | --- app/data/Storage.qml 1970-01-01 00:00:00 +0000 |
45 | +++ app/data/Storage.qml 2015-01-29 08:55:03 +0000 |
46 | @@ -0,0 +1,136 @@ |
47 | +/* |
48 | + * Copyright (C) 2013, 2014, 2015 Canonical Ltd |
49 | + * |
50 | + * This program is free software: you can redistribute it and/or modify |
51 | + * it under the terms of the GNU General Public License version 3 as |
52 | + * published by the Free Software Foundation. |
53 | + * |
54 | + * This program is distributed in the hope that it will be useful, |
55 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
56 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
57 | + * GNU General Public License for more details. |
58 | + * |
59 | + * You should have received a copy of the GNU General Public License |
60 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
61 | + * |
62 | + * Authored by: Martin Borho <martin@borho.net> |
63 | + */ |
64 | +import QtQuick.LocalStorage 2.0 |
65 | +import QtQuick 2.3 |
66 | + |
67 | +Item { |
68 | + property var db: null |
69 | + |
70 | + function openDB() { |
71 | + if(db !== null) return; |
72 | + |
73 | + db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000); |
74 | + |
75 | + if (db.version === "") { |
76 | + db.changeVersion("", "0.1", |
77 | + function(tx) { |
78 | + tx.executeSql('CREATE TABLE IF NOT EXISTS Locations(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT, date TEXT)'); |
79 | + console.log('Database created'); |
80 | + }); |
81 | + // reopen database with new version number |
82 | + db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000); |
83 | + } |
84 | + |
85 | + if(db.version === "0.1") { |
86 | + db.changeVersion("0.1", "0.2", |
87 | + function(tx) { |
88 | + tx.executeSql('CREATE TABLE IF NOT EXISTS settings(key TEXT UNIQUE, value TEXT)'); |
89 | + console.log('Settings table added, Database upgraded to v0.2'); |
90 | + }); |
91 | + // reopen database with new version number |
92 | + db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000); |
93 | + } |
94 | + |
95 | + if(db.version === "0.2") { |
96 | + db.changeVersion("0.2", "0.3", |
97 | + function(tx) { |
98 | + tx.executeSql('DELETE FROM Locations WHERE 1'); |
99 | + console.log('Removed old locations, Database upgraded to v0.3'); |
100 | + }); |
101 | + } |
102 | + } |
103 | + |
104 | + function saveSetting(key, value) { |
105 | + openDB(); |
106 | + db.transaction( function(tx){ |
107 | + tx.executeSql('INSERT OR REPLACE INTO settings VALUES(?, ?)', [key, value]); |
108 | + }); |
109 | + } |
110 | + |
111 | + function getSettings(callback) { |
112 | + openDB(); |
113 | + var settings = {}; |
114 | + db.readTransaction( |
115 | + function(tx){ |
116 | + var rs = tx.executeSql('SELECT key, value FROM Settings'); |
117 | + for(var i = 0; i < rs.rows.length; i++) { |
118 | + var row = rs.rows.item(i); |
119 | + settings[row.key] = row.value; |
120 | + } |
121 | + callback(settings); |
122 | + } |
123 | + ); |
124 | + } |
125 | + |
126 | + function clearSetting(name) { |
127 | + openDB(); |
128 | + db.transaction(function(tx){ |
129 | + tx.executeSql('DELETE FROM Settings WHERE key = ?', [name]); |
130 | + }); |
131 | + } |
132 | + |
133 | + function insertLocation(data) { |
134 | + openDB(); |
135 | + var res; |
136 | + db.transaction( function(tx){ |
137 | + var r = tx.executeSql('INSERT INTO Locations(data, date) VALUES(?, ?)', [JSON.stringify(data), new Date().getTime()]); |
138 | + res = r.insertId; |
139 | + }); |
140 | + return res; |
141 | + } |
142 | + |
143 | + function updateLocation(dbId, data) { |
144 | + openDB(); |
145 | + db.transaction( function(tx){ |
146 | + var r = tx.executeSql('UPDATE Locations SET data = ?, date=? WHERE id = ?', [JSON.stringify(data), new Date().getTime(), dbId]) |
147 | + }); |
148 | + } |
149 | + |
150 | + function getLocations(callback) { |
151 | + openDB(); |
152 | + db.readTransaction( |
153 | + function(tx){ |
154 | + var locations = []; |
155 | + var rs = tx.executeSql('SELECT * FROM Locations'); |
156 | + for(var i = 0; i < rs.rows.length; i++) { |
157 | + var row = rs.rows.item(i), |
158 | + locData = JSON.parse(row.data); |
159 | + locData["updated"] = parseInt(row.date, 10); |
160 | + locData["db"] = {id: row.id, updated: new Date(parseInt(row.date, 10))}; |
161 | + locations.push(locData); |
162 | + } |
163 | + callback(locations); |
164 | + } |
165 | + ); |
166 | + } |
167 | + |
168 | + function clearLocation(location_id) { |
169 | + openDB(); |
170 | + db.transaction(function(tx){ |
171 | + tx.executeSql('DELETE FROM Locations WHERE id = ?', [location_id]); |
172 | + }); |
173 | + } |
174 | + |
175 | + function clearDB() { // for dev purposes |
176 | + openDB(); |
177 | + db.transaction(function(tx){ |
178 | + tx.executeSql('DELETE FROM Locations WHERE 1'); |
179 | + tx.executeSql('DELETE FROM Settings WHERE 1'); |
180 | + }); |
181 | + } |
182 | +} |
183 | |
184 | === added file 'app/data/WeatherApi.js' |
185 | --- app/data/WeatherApi.js 1970-01-01 00:00:00 +0000 |
186 | +++ app/data/WeatherApi.js 2015-01-29 08:55:03 +0000 |
187 | @@ -0,0 +1,743 @@ |
188 | +.pragma library |
189 | +/* |
190 | + * Copyright (C) 2013 Canonical Ltd |
191 | + * |
192 | + * This program is free software: you can redistribute it and/or modify |
193 | + * it under the terms of the GNU General Public License version 3 as |
194 | + * published by the Free Software Foundation. |
195 | + * |
196 | + * This program is distributed in the hope that it will be useful, |
197 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
198 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
199 | + * GNU General Public License for more details. |
200 | + * |
201 | + * You should have received a copy of the GNU General Public License |
202 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
203 | + * |
204 | + * Authored by: Raúl Yeguas <neokore@gmail.com> |
205 | + * Martin Borho <martin@borho.net> |
206 | + * Andrew Starr-Bochicchio <a.starr.b@gmail.com> |
207 | + */ |
208 | + |
209 | +/** |
210 | +* Version of the response data format. |
211 | +* Increase this number to force a refresh. |
212 | +*/ |
213 | +var RESPONSE_DATA_VERSION = 20140315; |
214 | + |
215 | +/** |
216 | +* Helper functions |
217 | +*/ |
218 | +function debug(obj) { |
219 | + print(JSON.stringify(obj)) |
220 | +} |
221 | +// |
222 | +function calcFahrenheit(celsius) { |
223 | + return celsius * 1.8 + 32; |
224 | +} |
225 | +// |
226 | +function calcMph(ms) { |
227 | + return ms*2.24; |
228 | +} |
229 | +// |
230 | +function calcInch(mm) { |
231 | + return mm/25.4; |
232 | +} |
233 | +// |
234 | +function calcKmh(ms) { |
235 | + return ms*3.6; |
236 | +} |
237 | +// |
238 | +function convertKmhToMph(kmh) { |
239 | + return kmh*0.621; |
240 | +} |
241 | +// |
242 | +function calcWindDir(degrees) { |
243 | + var direction = "?"; |
244 | + if(degrees >=0 && degrees <= 30){ |
245 | + direction = "N"; |
246 | + } else if(degrees >30 && degrees <= 60){ |
247 | + direction = "NE"; |
248 | + } else if(degrees >60 && degrees <= 120){ |
249 | + direction = "E"; |
250 | + } else if(degrees >120 && degrees <= 150){ |
251 | + direction = "SE"; |
252 | + } else if(degrees >150 && degrees <= 210){ |
253 | + direction = "S"; |
254 | + } else if(degrees >210 && degrees <= 240){ |
255 | + direction = "SW"; |
256 | + } else if(degrees >240 && degrees <= 300){ |
257 | + direction = "W"; |
258 | + } else if(degrees >300 && degrees <= 330){ |
259 | + direction = "NW"; |
260 | + } else if(degrees >330 && degrees <= 360){ |
261 | + direction = "N"; |
262 | + } |
263 | + return direction; |
264 | +} |
265 | + |
266 | +// |
267 | +function getLocationTime(tstamp) { |
268 | + var locTime = new Date(tstamp); |
269 | + return { |
270 | + year: locTime.getUTCFullYear(), |
271 | + month: locTime.getUTCMonth(), |
272 | + date: locTime.getUTCDate(), |
273 | + hours: locTime.getUTCHours(), |
274 | + minutes: locTime.getUTCMinutes() |
275 | + } |
276 | +} |
277 | +// Serialize a JavaScript object to URL parameters |
278 | +// E.g. {param1: value1, param2: value2} to "param1=value¶m2=value" |
279 | +// TODO: it'd be nice to make it work with either passing a single object |
280 | +// or several at once |
281 | +function parameterize(obj) { |
282 | + var str = []; |
283 | + for(var param in obj) { |
284 | + str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param])); |
285 | + } |
286 | + return str.join("&"); |
287 | +} |
288 | + |
289 | +var GeoipApi = (function() { |
290 | + var _baseUrl = "http://geoip.ubuntu.com/lookup"; |
291 | + return { |
292 | + getLatLong: function(params, apiCaller, onSuccess, onError) { |
293 | + var request = { type: "geolookup",url: _baseUrl}, |
294 | + resultHandler = (function(request, xmlDoc) { |
295 | + var coords = {}, |
296 | + childNodes = xmlDoc.childNodes; |
297 | + for(var i=0;i<childNodes.length;i++) { |
298 | + if(childNodes[i].nodeName === "Latitude") { |
299 | + coords.lat = childNodes[i].firstChild.nodeValue; |
300 | + } else if(childNodes[i].nodeName === "Longitude") { |
301 | + coords.lon = childNodes[i].firstChild.nodeValue; |
302 | + } |
303 | + } |
304 | + onSuccess(coords); |
305 | + }), |
306 | + retryHandler = (function(err) { |
307 | + console.log("geolookup retry of "+err.request.url); |
308 | + apiCaller(request, resultHandler, onError); |
309 | + }); |
310 | + apiCaller(request, resultHandler, retryHandler); |
311 | + } |
312 | + } |
313 | +})(); |
314 | + |
315 | +var GeonamesApi = (function() { |
316 | + /** |
317 | + provides neccessary methods for requesting and preparing data from Geonames.org |
318 | + */ |
319 | + var _baseUrl = "http://api.geonames.org/"; |
320 | + var _username = "uweatherdev" |
321 | + var _addParams = "&maxRows=25&featureClass=P" |
322 | + // |
323 | + function _buildSearchResult(request, data) { |
324 | + var searchResult = { locations: [], request: request }; |
325 | + if(data.geonames) { |
326 | + data.geonames.forEach(function(r) { |
327 | + searchResult.locations.push({ |
328 | + name: r.name, |
329 | + coord: {lat: r.lat, lon: r.lng}, |
330 | + country: r.countryCode, |
331 | + countryName: r.countryName, |
332 | + timezone: r.timezone, |
333 | + adminName1: r.adminName1, |
334 | + adminName2: r.adminName2, |
335 | + adminName3: r.adminName3, |
336 | + population: r.population, |
337 | + services: { |
338 | + "geonames": r.geonameId |
339 | + } |
340 | + }); |
341 | + }) |
342 | + } |
343 | + return searchResult; |
344 | + } |
345 | + // |
346 | + return { |
347 | + // |
348 | + search: function(mode, params, apiCaller, onSuccess, onError) { |
349 | + var request, |
350 | + retryHandler = (function(err) { |
351 | + console.log("search retry of "+err.request.url); |
352 | + apiCaller(request, searchResponseHandler, onError); |
353 | + }), |
354 | + searchResponseHandler = function(request, data) { |
355 | + onSuccess(_buildSearchResult(request, data)); |
356 | + }; |
357 | + if(mode === "point") { |
358 | + request = { type: "search", |
359 | + url: _baseUrl+ "findNearbyPlaceNameJSON?style=full&username="+encodeURIComponent(_username) |
360 | + +"&lat="+encodeURIComponent(params.coords.lat)+"&lng="+encodeURIComponent(params.coords.lon) |
361 | + +_addParams} |
362 | + } else { |
363 | + request = { type: "search", |
364 | + url: _baseUrl+ "searchJSON?style=full&username="+encodeURIComponent(_username) |
365 | + +"&name_startsWith="+encodeURIComponent(params.name)+_addParams} |
366 | + } |
367 | + apiCaller(request, searchResponseHandler, retryHandler); |
368 | + } |
369 | + } |
370 | + |
371 | +})(); |
372 | + |
373 | +var OpenWeatherMapApi = (function() { |
374 | + /** |
375 | + provides neccessary methods for requesting and preparing data from OpenWeatherMap.org |
376 | + */ |
377 | + var _baseUrl = "http://api.openweathermap.org/data/2.5/"; |
378 | + // |
379 | + var _serviceName = "openweathermap"; |
380 | + // |
381 | + var _icon_map = { |
382 | + "01d": "sun", |
383 | + "01n": "moon", |
384 | + "02d": "cloud_sun", |
385 | + "02n": "cloud_moon", |
386 | + "03d": "cloud_sun", |
387 | + "03n": "cloud_moon", |
388 | + "04d": "cloud", |
389 | + "04n": "cloud", |
390 | + "09d": "rain", |
391 | + "09n": "rain", |
392 | + "10d": "rain", |
393 | + "10n": "rain", |
394 | + "11d": "thunder", |
395 | + "11n": "thunder", |
396 | + "13d": "snow_shower", |
397 | + "13n": "snow_shower", |
398 | + "50d": "fog", |
399 | + "50n": "fog" |
400 | + } |
401 | + // |
402 | + function _buildDataPoint(date, data) { |
403 | + var result = { |
404 | + timestamp: data.dt, |
405 | + date: date, |
406 | + metric: { |
407 | + temp:data.main.temp, |
408 | + windSpeed: calcKmh(data.wind.speed), |
409 | + rain: data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0, |
410 | + snow: data.main.snow || ((data.snow) ? data.snow["3h"] : false ) || 0 |
411 | + }, |
412 | + imperial: { |
413 | + temp: calcFahrenheit(data.main.temp), |
414 | + windSpeed: calcMph(data.wind.speed), |
415 | + rain: calcInch(data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0), |
416 | + snow: calcInch(data.main.snow || ((data.snow) ? data.snow["3h"] : false ) ||0) |
417 | + }, |
418 | + humidity: data.main.humidity, |
419 | + pressure: data.main.pressure, |
420 | + windDeg: data.wind.deg, |
421 | + windDir: calcWindDir(data.wind.deg), |
422 | + icon: _icon_map[data.weather[0].icon], |
423 | + condition: data.weather[0] |
424 | + }; |
425 | + if(data.id !== undefined) { |
426 | + result["service"] = _serviceName; |
427 | + result["service_id"] = data.id; |
428 | + } |
429 | + return result; |
430 | + } |
431 | + // |
432 | + function _buildDayFormat(date, data) { |
433 | + var result = { |
434 | + date: date, |
435 | + timestamp: data.dt, |
436 | + metric: { |
437 | + tempMin: data.temp.min, |
438 | + tempMax: data.temp.max, |
439 | + windSpeed: calcKmh(data.speed), |
440 | + rain: data.rain || 0, |
441 | + snow: data.snow || 0 |
442 | + }, |
443 | + imperial: { |
444 | + tempMin: calcFahrenheit(data.temp.min), |
445 | + tempMax: calcFahrenheit(data.temp.max), |
446 | + windSpeed: calcMph(data.speed), |
447 | + rain: calcInch(data.rain || 0), |
448 | + snow: calcInch(data.snow || 0) |
449 | + }, |
450 | + pressure: data.pressure, |
451 | + humidity: data.humidity, |
452 | + icon: _icon_map[data.weather[0].icon], |
453 | + condition: data.weather[0], |
454 | + windDeg: data.deg, |
455 | + windDir: calcWindDir(data.deg), |
456 | + hourly: [] |
457 | + } |
458 | + return result; |
459 | + } |
460 | + // |
461 | + function formatResult(data, location) { |
462 | + var tmpResult = {}, |
463 | + result = [], |
464 | + day=null, |
465 | + offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0, |
466 | + localNow = getLocationTime(new Date().getTime()+offset), |
467 | + todayDate; |
468 | + print("["+location.name+"] "+JSON.stringify(localNow)) |
469 | + // add openweathermap id for faster responses |
470 | + if(location.services && !location.services[_serviceName] && data["current"].id) { |
471 | + location.services[_serviceName] = data["current"].id |
472 | + } |
473 | + // |
474 | + data["daily"]["list"].forEach(function(dayData) { |
475 | + var date = getLocationTime(((dayData.dt*1000)-1000)+offset), // minus 1 sec to handle +/-12 TZ |
476 | + day = date.year+"-"+date.month+"-"+date.date; |
477 | + if(!todayDate) { |
478 | + if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) { |
479 | + // skip "yesterday" |
480 | + return; |
481 | + } |
482 | + todayDate = date; |
483 | + } |
484 | + tmpResult[day] = _buildDayFormat(date, dayData); |
485 | + }) |
486 | + // |
487 | + var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date |
488 | + tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]); |
489 | + if(data["forecast"] !== undefined) { |
490 | + data["forecast"]["list"].forEach(function(hourData) { |
491 | + var dateData = getLocationTime((hourData.dt*1000)+offset), |
492 | + day = dateData.year+"-"+dateData.month+"-"+dateData.date; |
493 | + if(tmpResult[day]) { |
494 | + tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData)); |
495 | + } |
496 | + }) |
497 | + } |
498 | + // |
499 | + for(var d in tmpResult) { |
500 | + result.push(tmpResult[d]); |
501 | + } |
502 | + return result; |
503 | + } |
504 | + // |
505 | + function _getUrls(params) { |
506 | + var urls = { |
507 | + current: "", |
508 | + daily: "", |
509 | + forecast: "" |
510 | + }, |
511 | + latLongParams = "&lat="+encodeURIComponent(params.location.coord.lat) |
512 | + + "&lon="+encodeURIComponent(params.location.coord.lon); |
513 | + if(params.location.services && params.location.services[_serviceName]) { |
514 | + urls.current = _baseUrl + "weather?units="+params.units+"&id="+params.location.services[_serviceName]; |
515 | + urls.daily = _baseUrl + "forecast/daily?id="+params.location.services[_serviceName]+"&cnt=10&units="+params.units |
516 | + urls.forecast = _baseUrl + "forecast?id="+params.location.services[_serviceName]+"&units="+params.units |
517 | + |
518 | + } else if (params.location.coord) { |
519 | + urls.current = _baseUrl + "weather?units="+params.units+latLongParams; |
520 | + urls.daily = _baseUrl+"forecast/daily?cnt=10&units="+params.units+latLongParams; |
521 | + urls.forecast = _baseUrl+"forecast?units="+params.units+latLongParams; |
522 | + } |
523 | + return urls; |
524 | + } |
525 | + // |
526 | + return { |
527 | + // |
528 | + getData: function(params, apiCaller, onSuccess, onError) { |
529 | + var urls = _getUrls(params), |
530 | + handlerMap = { |
531 | + current: { type: "current",url: urls.current}, |
532 | + daily: { type: "daily",url: urls.daily}, |
533 | + forecast: { type: "forecast", url: urls.forecast}}, |
534 | + response = { |
535 | + location: params.location, |
536 | + db: (params.db) ? params.db : null, |
537 | + format: RESPONSE_DATA_VERSION |
538 | + }, |
539 | + respData = {}, |
540 | + addDataToResponse = (function(request, data) { |
541 | + var formattedResult; |
542 | + respData[request.type] = data; |
543 | + if(respData["current"] !== undefined |
544 | + && respData["forecast"] !== undefined |
545 | + && respData["daily"] !== undefined) { |
546 | + response["data"] = formatResult(respData, params.location) |
547 | + onSuccess(response); |
548 | + } |
549 | + }), |
550 | + onErrorHandler = (function(err) { |
551 | + onError(err); |
552 | + }), |
553 | + retryHandler = (function(err) { |
554 | + console.log("retry of "+err.request.url); |
555 | + var retryFunc = handlerMap[err.request.type]; |
556 | + apiCaller(retryFunc, addDataToResponse, onErrorHandler); |
557 | + }); |
558 | + // |
559 | + apiCaller(handlerMap.current, addDataToResponse, retryHandler); |
560 | + apiCaller(handlerMap.forecast, addDataToResponse, retryHandler); |
561 | + apiCaller(handlerMap.daily, addDataToResponse, retryHandler); |
562 | + } |
563 | + } |
564 | + |
565 | +})(); |
566 | + |
567 | +var WeatherChannelApi = (function() { |
568 | + /** |
569 | + provides neccessary methods for requesting and preparing data from OpenWeatherMap.org |
570 | + */ |
571 | + var _baseUrl = "http://wxdata.weather.com/wxdata/"; |
572 | + // |
573 | + var _serviceName = "weatherchannel"; |
574 | + // |
575 | + // see http://s.imwx.com/v.20131006.223722/img/wxicon/72/([0-9]+).png |
576 | + var _iconMap = { |
577 | + "0": "thunder", // ?? |
578 | + "1": "thunder", // ?? |
579 | + "2": "thunder", // ?? |
580 | + "3": "thunder", // ?? |
581 | + "4": "thunder", //T-Storms |
582 | + "5": "snow_rain", //Rain / Snow |
583 | + "6": "snow_rain", // ?? |
584 | + "7": "snow_rain", //Wintry Mix |
585 | + "8": "scattered", //Freezing Drizzle |
586 | + "9": "scattered", //Drizzle |
587 | + "10": "rain", // ?? |
588 | + "11": "rain", //Showers |
589 | + "12": "rain", //Rain |
590 | + "13": "snow_shower", // ?? |
591 | + "14": "snow_shower", //Snow shower/Light snow |
592 | + "15": "snow_shower", // |
593 | + "16": "snow_shower", //Snow |
594 | + "17": "thunder", // Hail?? |
595 | + "18": "snow_rain", // Rain / Snow ?? |
596 | + "19": "fog", //Fog ?? |
597 | + "20": "fog", //Fog |
598 | + "21": "fog", //Haze |
599 | + "22": "fog", // ?? |
600 | + "23": "fog", // Wind ?? |
601 | + "24": "overcast", //Partly Cloudy / Wind |
602 | + "25": "overcast", // ?? |
603 | + "26": "overcast",//Cloudy |
604 | + "27": "cloud_moon",//Mostly Cloudy |
605 | + "28": "cloud_sun", //Mostly Cloudy |
606 | + "29": "cloud_moon", //Partly Cloudy |
607 | + "30": "cloud_sun", //Partly Cloudy |
608 | + "31": "moon", //Clear |
609 | + "32": "sun", //Sunny |
610 | + "33": "cloud_moon", //Mostly Clear |
611 | + "34": "cloud_sun", //Mostly Sunny |
612 | + "35": "snow_rain", // ?? |
613 | + "36": "sun", //Sunny |
614 | + "37": "thunder", //Isolated T-Storms |
615 | + "38": "thunder", //Scattered T-Storms |
616 | + "39": "scattered", //Scattered Showers |
617 | + "40": "rain", // ?? |
618 | + "41": "snow", //Scattered Snow Showers |
619 | + "42": "snow_shower", // ?? |
620 | + "43": "snow_shower", // ?? |
621 | + "44": "fog", // ?? |
622 | + "45": "scattered", // ?? |
623 | + "46": "snow_shower", //Snow Showers Early |
624 | + "47": "thunder" //Isolated T-Storms |
625 | + }; |
626 | + // |
627 | + function _buildDataPoint(date, dataObj) { |
628 | + var data = dataObj["Observation"] || dataObj, |
629 | + result = { |
630 | + timestamp: data.date || data.dateTime, |
631 | + date: date, |
632 | + metric: { |
633 | + temp: data.temp, |
634 | + tempFeels: data.feelsLike, |
635 | + windSpeed: data.wSpeed |
636 | + }, |
637 | + imperial: { |
638 | + temp: calcFahrenheit(data.temp), |
639 | + tempFeels: calcFahrenheit(data.feelsLike), |
640 | + windSpeed: convertKmhToMph(data.wSpeed) |
641 | + }, |
642 | + precipType: (data.precip_type !== undefined) ? data.precip_type : null, |
643 | + propPrecip: (data.pop !== undefined) ? data.pop : null, |
644 | + humidity: data.humid, |
645 | + pressure: data.pressure, |
646 | + windDeg: data.wDir, |
647 | + windDir: data.wDirText, |
648 | + icon: _iconMap[(data.wxIcon||data.icon)], |
649 | + condition: data.text || data.wDesc, |
650 | + uv: data.uv |
651 | + }; |
652 | + if(_iconMap[data.wxIcon||data.icon] === undefined) { |
653 | + print("ICON MISSING POINT: "+(data.wxIcon||data.icon)+" "+result.condition) |
654 | + } |
655 | + return result; |
656 | + } |
657 | + // |
658 | + function _buildDayFormat(date, data, now) { |
659 | + var partData = (now > data.validDate || data.day === undefined) ? data.night : data.day, |
660 | + result = { |
661 | + date: date, |
662 | + timestamp: data.validDate, |
663 | + metric: { |
664 | + tempMin: data.minTemp, |
665 | + tempMax: data.maxTemp, |
666 | + windSpeed: partData.wSpeed |
667 | + }, |
668 | + imperial: { |
669 | + tempMin: calcFahrenheit(data.minTemp), |
670 | + tempMax: calcFahrenheit(data.maxTemp || data.minTemp), |
671 | + windSpeed: convertKmhToMph(partData.wSpeed) |
672 | + }, |
673 | + precipType: partData.precip_type, |
674 | + propPrecip: partData.pop, |
675 | + pressure: null, |
676 | + humidity: partData.humid, |
677 | + icon: _iconMap[partData.icon], |
678 | + condition: partData.phrase, |
679 | + windDeg: partData.wDir, |
680 | + windDir: partData.wDirText, |
681 | + uv: partData.uv, |
682 | + hourly: [] |
683 | + } |
684 | + if(_iconMap[partData.icon] === undefined) { |
685 | + print("ICON MISSING DAY: "+partData.icon+" "+result.condition) |
686 | + } |
687 | + return result; |
688 | + } |
689 | + // |
690 | + function formatResult(combinedData, location) { |
691 | + var tmpResult = {}, result = [], |
692 | + day=null, todayDate, |
693 | + offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0, |
694 | + now = new Date().getTime(), |
695 | + nowMs = parseInt(now/1000), |
696 | + localNow = getLocationTime(now+offset), |
697 | + data = { |
698 | + "location": combinedData[0]["Location"], |
699 | + "daily": combinedData[0]["DailyForecasts"], |
700 | + "forecast": combinedData[0]["HourlyForecasts"], |
701 | + "current": combinedData[0]["StandardObservation"], |
702 | + }; |
703 | + print("["+location.name+"] "+JSON.stringify(localNow)); |
704 | + // add openweathermap id for faster responses |
705 | + if(location.services && !location.services[_serviceName] && data["location"].key) { |
706 | + location.services[_serviceName] = data["location"].key |
707 | + } |
708 | + // only 5 days of forecast for TWC |
709 | + for(var x=0;x<5;x++) { |
710 | + var dayData = data["daily"][x], |
711 | + date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ |
712 | + day = date.year+"-"+date.month+"-"+date.date; |
713 | + if(!todayDate) { |
714 | + if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) { |
715 | + // skip "yesterday" |
716 | + continue; |
717 | + } |
718 | + todayDate = date; |
719 | + } |
720 | + tmpResult[day] = _buildDayFormat(date, dayData, nowMs); |
721 | + } |
722 | + // |
723 | + if(data["current"]) { |
724 | + var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date |
725 | + tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]); |
726 | + } |
727 | + if(data["forecast"] !== undefined) { |
728 | + data["forecast"].forEach(function(hourData) { |
729 | + var dateData = getLocationTime((hourData.dateTime*1000)+offset), |
730 | + day = dateData.year+"-"+dateData.month+"-"+dateData.date; |
731 | + if(tmpResult[day]) { |
732 | + tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData)); |
733 | + } |
734 | + }) |
735 | + } |
736 | + // |
737 | + for(var d in tmpResult) { |
738 | + result.push(tmpResult[d]); |
739 | + } |
740 | + return result; |
741 | + } |
742 | + // |
743 | + function _getUrl(params) { |
744 | + var url, serviceId, |
745 | + baseParams = { |
746 | + key: params.api_key, |
747 | + units: (params.units === "metric") ? "m" : "e", |
748 | + locale: Qt.locale().name, |
749 | + hours: "48", |
750 | + }, |
751 | + commands = { |
752 | + "mobileaggregation": "mobile/mobagg/", |
753 | + }; |
754 | + if(params.location.services && params.location.services[_serviceName]) { |
755 | + serviceId = encodeURIComponent(params.location.services[_serviceName]); |
756 | + url = _baseUrl+commands["mobileaggregation"]+serviceId+".js?"+parameterize(baseParams); |
757 | + } else if (params.location.coord) { |
758 | + var coord = {lat: params.location.coord.lat, lng: params.location.coord.lon}; |
759 | + url = _baseUrl+commands["mobileaggregation"]+"get.js?"+parameterize(baseParams)+"&"+ |
760 | + parameterize(coord); |
761 | + } |
762 | + return url; |
763 | + } |
764 | + // |
765 | + return { |
766 | + getData: function(params, apiCaller, onSuccess, onError) { |
767 | + var url = _getUrl(params), |
768 | + handlerMap = { |
769 | + all: { type: "all", url: url} |
770 | + }, |
771 | + response = { |
772 | + location: params.location, |
773 | + db: (params.db) ? params.db : null, |
774 | + format: RESPONSE_DATA_VERSION |
775 | + }, |
776 | + addDataToResponse = (function(request, data) { |
777 | + var formattedResult; |
778 | + response["data"] = formatResult(data, params.location); |
779 | + onSuccess(response); |
780 | + }), |
781 | + onErrorHandler = (function(err) { |
782 | + onError(err); |
783 | + }); |
784 | + apiCaller(handlerMap.all, addDataToResponse, onErrorHandler); |
785 | + } |
786 | + } |
787 | +})(); |
788 | + |
789 | +var WeatherApi = (function(_services) { |
790 | + /** |
791 | + proxy for requesting weather apis, the passed _services are providing the respective api endpoints |
792 | + and formatters to build a uniform response object |
793 | + */ |
794 | + function _getService(name) { |
795 | + if(_services[name] !== undefined) { |
796 | + return _services[name]; |
797 | + } |
798 | + return _services["weatherchannel"]; |
799 | + } |
800 | + // |
801 | + function _sendRequest(request, onSuccess, onError) { |
802 | + var xmlHttp = new XMLHttpRequest(); |
803 | + if (xmlHttp) { |
804 | + console.log("Sent request URL: " + request.url); |
805 | + xmlHttp.open('GET', request.url, true); |
806 | + xmlHttp.onreadystatechange = function () { |
807 | + try { |
808 | + if (xmlHttp.readyState == 4) { |
809 | + if(xmlHttp.status === 200) { |
810 | + if(xmlHttp.responseXML) { |
811 | + onSuccess(request, xmlHttp.responseXML.documentElement); |
812 | + } else { |
813 | + var json = JSON.parse(xmlHttp.responseText); |
814 | + onSuccess(request,json); |
815 | + } |
816 | + } else { |
817 | + onError({ |
818 | + msg: "wrong response http code, got "+xmlHttp.status, |
819 | + request: request |
820 | + }); |
821 | + } |
822 | + } |
823 | + } catch (e) { |
824 | + print("Exception: "+e) |
825 | + onError({msg: "wrong response data format", request: request}); |
826 | + } |
827 | + }; |
828 | + xmlHttp.send(null); |
829 | + } |
830 | + } |
831 | + // |
832 | + return { |
833 | + // |
834 | + geoLookup: function(params, onSuccess, onError) { |
835 | + var service = _getService('geoip'), |
836 | + geoNameService = _getService('geonames'), |
837 | + lookupHandler = function(data) { |
838 | + print("Geolookup: "+JSON.stringify(data)) |
839 | + geoNameService.search("point", {coords:data}, _sendRequest, onSuccess, onError); |
840 | + }; |
841 | + service.getLatLong(params, _sendRequest, lookupHandler, onError) |
842 | + }, |
843 | + // |
844 | + search: function(mode, params, onSuccess, onError) { |
845 | + var service = _getService('geonames'); |
846 | + service.search(mode, params, _sendRequest, onSuccess, onError); |
847 | + }, |
848 | + // |
849 | + getLocationData: function(params, onSuccess, onError) { |
850 | + var service = _getService(params.service); |
851 | + service.getData(params, _sendRequest, onSuccess, onError); |
852 | + }, |
853 | + } |
854 | +})({ |
855 | + "openweathermap": OpenWeatherMapApi, |
856 | + "weatherchannel": WeatherChannelApi, |
857 | + "geonames": GeonamesApi, |
858 | + "geoip": GeoipApi |
859 | +}); |
860 | + |
861 | +var sendRequest = function(message, responseCallback) { |
862 | + // handles the response data |
863 | + var finished = function(result) { |
864 | + // print result to get data for test json files |
865 | + // print(JSON.stringify(result)); |
866 | + //WorkerScript.sendMessage({ |
867 | + responseCallback({ |
868 | + action: message.action, |
869 | + result: result |
870 | + }) |
871 | + } |
872 | + // handles errors |
873 | + var onError = function(err) { |
874 | + console.log(JSON.stringify(err, null, true)); |
875 | + //WorkerScript.sendMessage({ 'error': err}) |
876 | + responseCallback({ 'error': err}) |
877 | + } |
878 | + // keep order of locations, sort results |
879 | + var sortDataResults = function(locA, locB) { |
880 | + return locA.db.id - locB.db.id; |
881 | + } |
882 | + // perform the api calls |
883 | + if(message.action === "searchByName") { |
884 | + WeatherApi.search("name", message.params, finished, onError); |
885 | + } else if(message.action === "searchByPoint") { |
886 | + WeatherApi.search("point", message.params, finished, onError); |
887 | + } else if(message.action === "getGeoIp") { |
888 | + WeatherApi.geoLookup(message.params, finished, onError); |
889 | + } else if(message.action === "updateData") { |
890 | + var locLength = message.params.locations.length, |
891 | + locUpdated = 0, |
892 | + result = [], |
893 | + now = new Date().getTime(); |
894 | + if(locLength > 0) { |
895 | + message.params.locations.forEach(function(loc) { |
896 | + var updatedHnd = function (newData, cached) { |
897 | + locUpdated += 1; |
898 | + if(cached === true) { |
899 | + newData["save"] = false; |
900 | + } else { |
901 | + newData["save"] = true; |
902 | + newData["updated"] = new Date().getTime(); |
903 | + } |
904 | + result.push(newData); |
905 | + if(locUpdated === locLength) { |
906 | + result.sort(sortDataResults); |
907 | + finished(result); |
908 | + } |
909 | + }, |
910 | + params = { |
911 | + location:loc.location, |
912 | + db: loc.db, |
913 | + units: 'metric', |
914 | + service: message.params.service, |
915 | + api_key: message.params.api_key |
916 | + }, |
917 | + secsFromLastFetch = (now-loc.updated)/1000; |
918 | + if( message.params.force===true || loc.format !== RESPONSE_DATA_VERSION || secsFromLastFetch > 1800){ |
919 | + // data older than 30min, location is new or data format is deprecated |
920 | + WeatherApi.getLocationData(params, updatedHnd, onError); |
921 | + } else { |
922 | + console.log("["+loc.location.name+"] returning cached data, time from last fetch: "+secsFromLastFetch) |
923 | + updatedHnd(loc, true); |
924 | + } |
925 | + }) |
926 | + } else { |
927 | + finished(result); |
928 | + } |
929 | + } |
930 | +} |
931 | |
932 | === modified file 'app/ubuntu-weather-app.qml' |
933 | --- app/ubuntu-weather-app.qml 2015-01-24 00:41:10 +0000 |
934 | +++ app/ubuntu-weather-app.qml 2015-01-29 08:55:03 +0000 |
935 | @@ -18,6 +18,9 @@ |
936 | |
937 | import QtQuick 2.3 |
938 | import Ubuntu.Components 1.1 |
939 | +import "data" as Data |
940 | +import "data/WeatherApi.js" as WeatherApi |
941 | +import "../key.js" as Key |
942 | |
943 | MainView { |
944 | id: weatherApp |
945 | @@ -36,6 +39,41 @@ |
946 | useDeprecatedToolbar: false |
947 | anchorToKeyboard: true |
948 | |
949 | + Component.onCompleted: { |
950 | + storage.getLocations(function(locations) { |
951 | + WeatherApi.sendRequest({ |
952 | + action: "updateData", |
953 | + params: { |
954 | + locations:locations, |
955 | + force:false, |
956 | + service: "weatherchannel", |
957 | + api_key: Key.twcKey |
958 | + } |
959 | + }, responseDataHandler) |
960 | + }) |
961 | + } |
962 | + |
963 | + function responseDataHandler(messageObject) { |
964 | + if(!messageObject.error) { |
965 | + if(messageObject.action === "updateData") { |
966 | + messageObject.result.forEach(function(loc) { |
967 | + // replace location data in cache with refreshed values |
968 | + if(loc["save"] === true) { |
969 | + storage.updateLocation(loc.db.id, loc); |
970 | + } |
971 | + }); |
972 | + //print(JSON.stringify(messageObject.result)); |
973 | + //buildTabs(messageObject.result); |
974 | + } |
975 | + } else { |
976 | + console.log(messageObject.error.msg+" / "+messageObject.error.request.url) |
977 | + // TODO error handling |
978 | + } |
979 | + } |
980 | + |
981 | + Data.Storage { |
982 | + id: storage |
983 | + } |
984 | |
985 | Page { |
986 | title: "Weather Reboot" |
987 | |
988 | === added file 'key.js' |
989 | --- key.js 1970-01-01 00:00:00 +0000 |
990 | +++ key.js 2015-01-29 08:55:03 +0000 |
991 | @@ -0,0 +1,1 @@ |
992 | +var twcKey = ""; |
Please change "keys.js" to "key.js", or rename the file to match the CMakeLists.txt file
Install the project... o7PpsVrGpn/ /manifest. json o7PpsVrGpn/ /ubuntu- weather- app.apparmor o7PpsVrGpn/ share/applicati ons/ubuntu- weather- app.desktop cmake:72 (FILE): victor/ Development/ reboot- data/keys. js".
-- Install configuration: ""
-- Installing: /tmp/tmp.
-- Installing: /tmp/tmp.
-- Installing: /tmp/tmp.
CMake Error at cmake_install.
file INSTALL cannot find "/home/