← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~ubuntu-weather-dev/ubuntu-weather-app/new-weather-api into lp:ubuntu-weather-app

 

Victor Thompson has proposed merging lp:~ubuntu-weather-dev/ubuntu-weather-app/new-weather-api into lp:ubuntu-weather-app.

Commit message:
* Update to use new weather API
* Change the trimAPIKey function so it only trims the API Key

Requested reviews:
  Ubuntu Weather Developers (ubuntu-weather-dev)
Related bugs:
  Bug #1511070 in Ubuntu Weather App: "TWC API change"
  https://bugs.launchpad.net/ubuntu-weather-app/+bug/1511070

For more details, see:
https://code.launchpad.net/~ubuntu-weather-dev/ubuntu-weather-app/new-weather-api/+merge/289584

Move to the new weather api.

For reference, here is the commit to use the new API in the scope: http://bazaar.launchpad.net/~ubuntuone-hackers/ubuntu-rest-scopes/trunk/revision/505
-- 
Your team Ubuntu Weather Developers is requested to review the proposed merge of lp:~ubuntu-weather-dev/ubuntu-weather-app/new-weather-api into lp:ubuntu-weather-app.
=== modified file 'app/data/WeatherApi.js'
--- app/data/WeatherApi.js	2015-10-21 02:16:50 +0000
+++ app/data/WeatherApi.js	2016-05-28 00:41:30 +0000
@@ -100,15 +100,20 @@
 }
 
 
-// Remove anything including and after APPID in the given term
+// Remove just the API key
 function trimAPIKey(data) {
-    var owm = data.indexOf("&APPID=");
-    var twc = data.indexOf("&key=");
+    var owm = data.indexOf("APPID=");
+    var twc = data.indexOf("apiKey=");
+
+    // Key length is 32 char for both APIs
+    var keySize = 32;
 
     if (owm > -1) {
-        data = data.substr(0, owm);
+        var replace = data.substr(owm+6, keySize);
+        data = data.replace(replace,"")
     } else if (twc > -1) {
-        data = data.substr(0, twc);
+        var replace = data.substr(twc+7, keySize);
+        data = data.replace(replace,"")
     }
 
     return data;
@@ -400,7 +405,7 @@
     /**
       provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
     */
-    var _baseUrl = "http://wxdata.weather.com/wxdata/";;
+    var _baseUrl = "https://api.weather.com/";;
     //
     var _serviceName = "weatherchannel";
     //
@@ -457,65 +462,70 @@
     };
     //
     function _buildDataPoint(date, dataObj) {
-        var data = dataObj["Observation"] || dataObj,
+        var partData = dataObj["metric"] || dataObj["imperial"] || dataObj;  // TODO: be more intelligent?
+        var data = dataObj["observation"] || dataObj,
             result = {
-                timestamp: data.date || data.dateTime,
+                timestamp: data.fcst_valid,
                 date: date,
                 metric: {
-                    temp: data.temp,
-                    tempFeels: data.feelsLike,
-                    windSpeed: data.wSpeed
+                    temp: partData.temp,
+                    tempFeels: partData.feels_like,
+                    windSpeed: partData.wspd
                 },
                 imperial: {
-                    temp: calcFahrenheit(data.temp),
-                    tempFeels: calcFahrenheit(data.feelsLike),
-                    windSpeed: convertKphToMph(data.wSpeed)
+                    temp: calcFahrenheit(partData.temp),
+                    tempFeels: calcFahrenheit(partData.feels_like),
+                    windSpeed: convertKphToMph(partData.wspd)
                 },
                 precipType: (data.precip_type !== undefined) ? data.precip_type : null,
                 propPrecip: (data.pop !== undefined) ? data.pop : null,
-                humidity: data.humid,
-                pressure: data.pressure,
-                windDeg: data.wDir,
-                windDir: data.wDirText,
-                icon: _iconMap[(data.wxIcon||data.icon)],
-                condition: data.text || data.wDesc,
-                uv: data.uv
+                humidity: partData.rh,
+                pressure: partData.mslp,
+                windDeg: data.wdir,
+                windDir: data.wdir_cardinal,
+                icon: _iconMap[data.icon_code],
+                condition: data.phrase_32char,
+                uv: data.uv_index
         };
-        if(_iconMap[data.wxIcon||data.icon] === undefined) {
-            print("ICON MISSING POINT: "+(data.wxIcon||data.icon)+" "+result.condition)
+
+        if (_iconMap[data.icon_code] === undefined) {
+            print("ICON MISSING POINT: " + data.icon_code + " " + result.condition)
         }
+
         return result;
     }
     //
     function _buildDayFormat(date, data, now) {
-        var partData = (now > data.validDate || data.day === undefined) ? data.night : data.day,
+        var partData = (now > data.fcst_valid || data.day === undefined) ? data.night : data.day,
             result = {
             date: date,
-            timestamp: data.validDate,
+            timestamp: data.fcst_valid,
             metric: {
-                tempMin: data.minTemp,
-                tempMax: data.maxTemp,
-                windSpeed: partData.wSpeed
+                tempMin: data.min_temp,
+                tempMax: data.max_temp,
+                windSpeed: partData.wspd
             },
             imperial: {
-                tempMin: calcFahrenheit(data.minTemp),
-                tempMax: calcFahrenheit(data.maxTemp !== undefined ? data.maxTemp : data.minTemp),
-                windSpeed: convertKphToMph(partData.wSpeed)
+                tempMin: calcFahrenheit(data.min_temp),
+                tempMax: calcFahrenheit(data.max_temp !== undefined ? data.max_temp : data.min_temp),
+                windSpeed: convertKphToMph(partData.wspd)
             },
             precipType: partData.precip_type,
             propPrecip: partData.pop,
             pressure: null,
-            humidity: partData.humid,
-            icon: _iconMap[partData.icon],
-            condition: partData.phrase,
-            windDeg: partData.wDir,
-            windDir: partData.wDirText,
-            uv: partData.uv,
+            humidity: partData.rh,
+            icon: _iconMap[partData.icon_code],
+            condition: partData.phrase_32char,
+            windDeg: partData.wdir,
+            windDir: partData.wdir_cardinal,
+            uv: partData.uv_index,
             hourly: []
         }
-        if(_iconMap[partData.icon] === undefined) {
-            print("ICON MISSING  DAY: "+partData.icon+" "+result.condition)
+
+        if (_iconMap[partData.icon_code] === undefined) {
+            print("ICON MISSING  DAY: " + partData.icon_code + " " + result.condition)
         }
+
         return result;
     }
     //
@@ -527,35 +537,43 @@
             nowMs = parseInt(now/1000),
             localNow = getLocationTime(now+offset),
             data = {
-                "location": combinedData[0]["Location"],
-                "daily": combinedData[0]["DailyForecasts"],
-                "forecast": combinedData[0]["HourlyForecasts"],
-                "current": combinedData[0]["StandardObservation"],
-                "sunRiseSet": combinedData[0]["SunRiseSet"],
+                "location": combinedData["current"]["metadata"],
+                "daily": combinedData["daily"]["forecasts"],
+                "forecast": combinedData["forecast"]["forecasts"],
+                "current": combinedData["current"]["observation"],
             };
         print("["+location.name+"] "+JSON.stringify(localNow));
         // add openweathermap id for faster responses
         if(location.services && !location.services[_serviceName] && data["location"].key) {
             location.services[_serviceName] = data["location"].key
-        }                
+        }
         // only 5 days of forecast for TWC
-        for(var x=0;x<5;x++) {
+        for(var x=0; x<5; x++) {
             var dayData = data["daily"][x],
-                date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ
-            var sunRiseSet = data["sunRiseSet"][x];
+                date = getLocationTime(((dayData.fcst_valid * 1000) - 1000) + offset);  // minus 1 sec to handle +/-12 TZ
+
+            // Sun{rise,set} is in ISOString format so use getTime() to convert
+            var sunrise = new Date(dayData.sunrise).getTime(),
+                sunset = new Date(dayData.sunset).getTime();
             day = date.year+"-"+date.month+"-"+date.date;
-            if(!todayDate) {
-                if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
+
+            if (!todayDate) {
+                if (localNow.year + "-" + localNow.month + "-" + localNow.date > day) {
                     // skip "yesterday"
                     continue;
                 }
+
                 todayDate = date;
             }
+
             tmpResult[day] = _buildDayFormat(date, dayData, nowMs);
+
             var timezoneOffset = new Date().getTimezoneOffset();
             var timesOffset = (location.timezone && location.timezone.dstOffset !== undefined) ? (location.timezone.dstOffset*60 + timezoneOffset)*60*1000: 0
-            var sunrise = new Date(sunRiseSet.rise*1000 + timesOffset);
-            var sunset = new Date(sunRiseSet.set*1000 + timesOffset);
+
+            sunrise = new Date(sunrise + timesOffset);
+            sunset = new Date(sunset + timesOffset);
+
             var options = { timeZone: location.timezone.timeZoneId, timeZoneName: 'long' };
             tmpResult[day].sunrise = sunrise.toLocaleTimeString(Qt.locale().name, options);
             tmpResult[day].sunset = sunset.toLocaleTimeString(Qt.locale().name, options);
@@ -563,8 +581,9 @@
         //
         if(data["forecast"] !== undefined) {
             data["forecast"].forEach(function(hourData) {
-                var dateData = getLocationTime((hourData.dateTime*1000)+offset),
+                var dateData = getLocationTime((hourData.fcst_valid * 1000) + offset),
                     day = dateData.year+"-"+dateData.month+"-"+dateData.date;
+
                 if(tmpResult[day]) {
                     tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
                 }
@@ -591,48 +610,101 @@
         return result;
     }
     //
-    function _getUrl(params) {
-        var url, serviceId,
-            baseParams = {
-                key: params.twc_api_key,
-                units: (params.units === "metric") ? "m" : "e",
-                locale: Qt.locale().name,
-                hours: "48",
-            },
-            commands = {
-                "mobileaggregation": "mobile/mobagg/",
+    function _getUrls(params) {
+        var baseParams = {
+            units: (params.units === "metric") ? "m" : "e",
+            language: Qt.locale().name.replace("_","-"),
+            apiKey: params.twc_api_key,
+        };
+        var commands = {
+            "geocode": "v1/geocode/",
+        };
+        var urls = {
+            current: "",
+            daily: "",
+            hourly: "",
+        };
+
+        if(1==2 && params.location.services && params.location.services[_serviceName]) {  // FIXME: disable for now (UKXX0085)
+            var serviceId = encodeURIComponent(params.location.services[_serviceName]);
+
+            urls.current = _baseUrl + commands["geocode"] +
+                    serviceId + ".js?" + parameterize(baseParams);
+            urls.daily = _baseUrl + commands["geocode"] +
+                    serviceId + ".js?" + parameterize(baseParams);
+            urls.hourly = _baseUrl + commands["geocode"] +
+                    serviceId + ".js?" + parameterize(baseParams);
+        } else if (params.location.coord) {
+            var coord = {
+                lat: params.location.coord.lat,
+                lng: params.location.coord.lon
             };
-        if(params.location.services && params.location.services[_serviceName]) {
-            serviceId = encodeURIComponent(params.location.services[_serviceName]);
-            url = _baseUrl+commands["mobileaggregation"]+serviceId+".js?"+parameterize(baseParams);
-        } else if (params.location.coord) {
-            var coord = {lat: params.location.coord.lat, lng: params.location.coord.lon};
-            url = _baseUrl+commands["mobileaggregation"]+"get.js?"+parameterize(baseParams)+"&"+
-                  parameterize(coord);
+
+            urls.current = _baseUrl + commands["geocode"] +
+                    coord.lat + "/" + coord.lng +
+                    "/observations/current.json?" +
+                    parameterize(baseParams);
+            urls.daily = _baseUrl + commands["geocode"] +
+                    coord.lat + "/" + coord.lng +
+                    "/forecast/daily/5day.json?" +
+                    parameterize(baseParams);
+            urls.hourly = _baseUrl + commands["geocode"] +
+                    coord.lat + "/" + coord.lng +
+                    "/forecast/hourly/48hour.json?" +
+                    parameterize(baseParams);
         }
-        return url;
+
+        return urls;
     }
     //
     return {
         getData: function(params, apiCaller, onSuccess, onError) {
-            var url = _getUrl(params),
+            var urls = _getUrls(params),
                 handlerMap = {
-                    all: { type: "all", url: url}
-                },
-                response = {
-                    location: params.location,
-                    db: (params.db) ? params.db : null,
-                    format: RESPONSE_DATA_VERSION
-                },
-                addDataToResponse = (function(request, data) {
-                    var formattedResult;
-                    response["data"] = formatResult(data, params.location);
+                current: {
+                    type: "current",
+                    url: urls.current
+                },
+                daily: {
+                    type: "daily",
+                    url: urls.daily
+                },
+                forecast: {
+                    type: "forecast",
+                    url: urls.hourly
+                }
+            },
+            response = {
+                location: params.location,
+                db: (params.db) ? params.db : null,
+                format: RESPONSE_DATA_VERSION
+            },
+            respData = {},
+            addDataToResponse = (function(request, data) {
+                var formattedResult;
+                respData[request.type] = data;
+
+                if (respData["current"] !== undefined &&
+                        respData["forecast"] !== undefined &&
+                            respData["daily"] !== undefined) {
+
+                    response["data"] = formatResult(respData, params.location);
                     onSuccess(response);
-                }),
-                onErrorHandler = (function(err) {
-                    onError(err);
-                });
-            apiCaller(handlerMap.all, addDataToResponse, onErrorHandler);
+                }
+            }),
+            onErrorHandler = (function(err) {
+                onError(err);
+            }),
+            retryHandler = (function(err) {
+                console.log("retry of " + trimAPIKey(err.request.url));
+                var retryFunc = handlerMap[err.request.type];
+
+                apiCaller(retryFunc, addDataToResponse, onErrorHandler);
+            });
+
+            apiCaller(handlerMap.current, addDataToResponse, retryHandler);
+            apiCaller(handlerMap.forecast, addDataToResponse, retryHandler);
+            apiCaller(handlerMap.daily, addDataToResponse, retryHandler);
         }
     }
 })();


Follow ups