~ubuntu-weather-dev/ubuntu-weather-app/reboot

« back to all changes in this revision

Viewing changes to app/data/WeatherApi.js

  • Committer: Tarmac
  • Author(s): Andrew Hayzen, Victor Thompson
  • Date: 2016-06-05 20:16:22 UTC
  • mfrom: (228.1.9 new-weather-api)
  • Revision ID: tarmac-20160605201622-25iv6lplawmvda2f
* Update to use new weather API
* Change the trimAPIKey function so it only trims the API Key
* Release 3.2 and bump version to 3.3. Fixes: https://bugs.launchpad.net/bugs/1511070.

Approved by Victor Thompson, Jenkins Bot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
100
100
}
101
101
 
102
102
 
103
 
// Remove anything including and after APPID in the given term
 
103
// Remove just the API key
104
104
function trimAPIKey(data) {
105
 
    var owm = data.indexOf("&APPID=");
106
 
    var twc = data.indexOf("&key=");
 
105
    var owm = data.indexOf("APPID=");
 
106
    var twc = data.indexOf("apiKey=");
 
107
 
 
108
    // Key length is 32 char for both APIs
 
109
    var keySize = 32;
107
110
 
108
111
    if (owm > -1) {
109
 
        data = data.substr(0, owm);
 
112
        var replace = data.substr(owm+6, keySize);
 
113
        data = data.replace(replace,"")
110
114
    } else if (twc > -1) {
111
 
        data = data.substr(0, twc);
 
115
        var replace = data.substr(twc+7, keySize);
 
116
        data = data.replace(replace,"")
112
117
    }
113
118
 
114
119
    return data;
400
405
    /**
401
406
      provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
402
407
    */
403
 
    var _baseUrl = "http://wxdata.weather.com/wxdata/";
 
408
    var _baseUrl = "https://api.weather.com/";
404
409
    //
405
410
    var _serviceName = "weatherchannel";
406
411
    //
457
462
    };
458
463
    //
459
464
    function _buildDataPoint(date, dataObj) {
460
 
        var data = dataObj["Observation"] || dataObj,
 
465
        var partData = dataObj["metric"] || dataObj;
 
466
        var data = dataObj["observation"] || dataObj,
461
467
            result = {
462
 
                timestamp: data.date || data.dateTime,
 
468
                timestamp: data.fcst_valid,
463
469
                date: date,
464
470
                metric: {
465
 
                    temp: data.temp,
466
 
                    tempFeels: data.feelsLike,
467
 
                    windSpeed: data.wSpeed
 
471
                    temp: partData.temp,
 
472
                    tempFeels: partData.feels_like,
 
473
                    windSpeed: partData.wspd
468
474
                },
469
475
                imperial: {
470
 
                    temp: calcFahrenheit(data.temp),
471
 
                    tempFeels: calcFahrenheit(data.feelsLike),
472
 
                    windSpeed: convertKphToMph(data.wSpeed)
 
476
                    temp: calcFahrenheit(partData.temp),
 
477
                    tempFeels: calcFahrenheit(partData.feels_like),
 
478
                    windSpeed: convertKphToMph(partData.wspd)
473
479
                },
474
480
                precipType: (data.precip_type !== undefined) ? data.precip_type : null,
475
481
                propPrecip: (data.pop !== undefined) ? data.pop : null,
476
 
                humidity: data.humid,
477
 
                pressure: data.pressure,
478
 
                windDeg: data.wDir,
479
 
                windDir: data.wDirText,
480
 
                icon: _iconMap[(data.wxIcon||data.icon)],
481
 
                condition: data.text || data.wDesc,
482
 
                uv: data.uv
 
482
                humidity: partData.rh,
 
483
                pressure: partData.mslp,
 
484
                windDeg: data.wdir,
 
485
                windDir: data.wdir_cardinal,
 
486
                icon: _iconMap[data.icon_code],
 
487
                condition: data.phrase_32char,
 
488
                uv: data.uv_index
483
489
        };
484
 
        if(_iconMap[data.wxIcon||data.icon] === undefined) {
485
 
            print("ICON MISSING POINT: "+(data.wxIcon||data.icon)+" "+result.condition)
 
490
 
 
491
        if (_iconMap[data.icon_code] === undefined) {
 
492
            print("ICON MISSING POINT: " + data.icon_code + " " + result.condition)
486
493
        }
 
494
 
487
495
        return result;
488
496
    }
489
497
    //
490
498
    function _buildDayFormat(date, data, now) {
491
 
        var partData = (now > data.validDate || data.day === undefined) ? data.night : data.day,
 
499
        var partData = (now > data.fcst_valid || data.day === undefined) ? data.night : data.day,
492
500
            result = {
493
501
            date: date,
494
 
            timestamp: data.validDate,
 
502
            timestamp: data.fcst_valid,
495
503
            metric: {
496
 
                tempMin: data.minTemp,
497
 
                tempMax: data.maxTemp,
498
 
                windSpeed: partData.wSpeed
 
504
                tempMin: data.min_temp,
 
505
                tempMax: data.max_temp !== null ? data.max_temp : undefined,
 
506
                windSpeed: partData.wspd
499
507
            },
500
508
            imperial: {
501
 
                tempMin: calcFahrenheit(data.minTemp),
502
 
                tempMax: calcFahrenheit(data.maxTemp !== undefined ? data.maxTemp : data.minTemp),
503
 
                windSpeed: convertKphToMph(partData.wSpeed)
 
509
                tempMin: calcFahrenheit(data.min_temp),
 
510
                tempMax: data.max_temp !== null && data.max_temp !== undefined ? calcFahrenheit(data.max_temp) : undefined,
 
511
                windSpeed: convertKphToMph(partData.wspd)
504
512
            },
505
513
            precipType: partData.precip_type,
506
514
            propPrecip: partData.pop,
507
515
            pressure: null,
508
 
            humidity: partData.humid,
509
 
            icon: _iconMap[partData.icon],
510
 
            condition: partData.phrase,
511
 
            windDeg: partData.wDir,
512
 
            windDir: partData.wDirText,
513
 
            uv: partData.uv,
 
516
            humidity: partData.rh,
 
517
            icon: _iconMap[partData.icon_code],
 
518
            condition: partData.phrase_32char,
 
519
            windDeg: partData.wdir,
 
520
            windDir: partData.wdir_cardinal,
 
521
            uv: partData.uv_index,
514
522
            hourly: []
515
523
        }
516
 
        if(_iconMap[partData.icon] === undefined) {
517
 
            print("ICON MISSING  DAY: "+partData.icon+" "+result.condition)
 
524
 
 
525
        if (_iconMap[partData.icon_code] === undefined) {
 
526
            print("ICON MISSING  DAY: " + partData.icon_code + " " + result.condition)
518
527
        }
 
528
 
519
529
        return result;
520
530
    }
521
531
    //
527
537
            nowMs = parseInt(now/1000),
528
538
            localNow = getLocationTime(now+offset),
529
539
            data = {
530
 
                "location": combinedData[0]["Location"],
531
 
                "daily": combinedData[0]["DailyForecasts"],
532
 
                "forecast": combinedData[0]["HourlyForecasts"],
533
 
                "current": combinedData[0]["StandardObservation"],
534
 
                "sunRiseSet": combinedData[0]["SunRiseSet"],
 
540
                "location": combinedData["current"]["metadata"],
 
541
                "daily": combinedData["daily"]["forecasts"],
 
542
                "forecast": combinedData["forecast"]["forecasts"],
 
543
                "current": combinedData["current"]["observation"],
535
544
            };
536
545
        print("["+location.name+"] "+JSON.stringify(localNow));
537
546
        // add openweathermap id for faster responses
538
547
        if(location.services && !location.services[_serviceName] && data["location"].key) {
539
548
            location.services[_serviceName] = data["location"].key
540
 
        }                
 
549
        }
541
550
        // only 5 days of forecast for TWC
542
 
        for(var x=0;x<5;x++) {
 
551
        for(var x=0; x<5; x++) {
543
552
            var dayData = data["daily"][x],
544
 
                date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ
545
 
            var sunRiseSet = data["sunRiseSet"][x];
 
553
                date = getLocationTime(((dayData.fcst_valid * 1000) - 1000) + offset);  // minus 1 sec to handle +/-12 TZ
 
554
 
 
555
            // Sun{rise,set} is in ISOString format so use getTime() to convert
 
556
            var sunrise = new Date(dayData.sunrise).getTime(),
 
557
                sunset = new Date(dayData.sunset).getTime();
546
558
            day = date.year+"-"+date.month+"-"+date.date;
547
 
            if(!todayDate) {
548
 
                if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
 
559
 
 
560
            if (!todayDate) {
 
561
                if (localNow.year + "-" + localNow.month + "-" + localNow.date > day) {
549
562
                    // skip "yesterday"
550
563
                    continue;
551
564
                }
 
565
 
552
566
                todayDate = date;
553
567
            }
 
568
 
554
569
            tmpResult[day] = _buildDayFormat(date, dayData, nowMs);
 
570
 
555
571
            var timezoneOffset = new Date().getTimezoneOffset();
556
572
            var timesOffset = (location.timezone && location.timezone.dstOffset !== undefined) ? (location.timezone.dstOffset*60 + timezoneOffset)*60*1000: 0
557
 
            var sunrise = new Date(sunRiseSet.rise*1000 + timesOffset);
558
 
            var sunset = new Date(sunRiseSet.set*1000 + timesOffset);
 
573
 
 
574
            sunrise = new Date(sunrise + timesOffset);
 
575
            sunset = new Date(sunset + timesOffset);
 
576
 
559
577
            var options = { timeZone: location.timezone.timeZoneId, timeZoneName: 'long' };
560
578
            tmpResult[day].sunrise = sunrise.toLocaleTimeString(Qt.locale().name, options);
561
579
            tmpResult[day].sunset = sunset.toLocaleTimeString(Qt.locale().name, options);
563
581
        //
564
582
        if(data["forecast"] !== undefined) {
565
583
            data["forecast"].forEach(function(hourData) {
566
 
                var dateData = getLocationTime((hourData.dateTime*1000)+offset),
 
584
                var dateData = getLocationTime((hourData.fcst_valid * 1000) + offset),
567
585
                    day = dateData.year+"-"+dateData.month+"-"+dateData.date;
 
586
 
568
587
                if(tmpResult[day]) {
569
588
                    tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
570
589
                }
591
610
        return result;
592
611
    }
593
612
    //
594
 
    function _getUrl(params) {
595
 
        var url, serviceId,
596
 
            baseParams = {
597
 
                key: params.twc_api_key,
598
 
                units: (params.units === "metric") ? "m" : "e",
599
 
                locale: Qt.locale().name,
600
 
                hours: "48",
601
 
            },
602
 
            commands = {
603
 
                "mobileaggregation": "mobile/mobagg/",
 
613
    function _getUrls(params) {
 
614
        var baseParams = {
 
615
            units: (params.units === "metric") ? "m" : "e",
 
616
            language: Qt.locale().name.replace("_","-"),
 
617
            apiKey: params.twc_api_key,
 
618
        };
 
619
        var commands = {
 
620
            "geocode": "v1/geocode/",
 
621
        };
 
622
        var urls = {
 
623
            current: "",
 
624
            daily: "",
 
625
            hourly: "",
 
626
        };
 
627
 
 
628
        // FIXME: only use coords for now and not location codes (UKXX0085)
 
629
        if (params.location.coord) {
 
630
            var coord = {
 
631
                lat: params.location.coord.lat,
 
632
                lng: params.location.coord.lon
604
633
            };
605
 
        if(params.location.services && params.location.services[_serviceName]) {
606
 
            serviceId = encodeURIComponent(params.location.services[_serviceName]);
607
 
            url = _baseUrl+commands["mobileaggregation"]+serviceId+".js?"+parameterize(baseParams);
608
 
        } else if (params.location.coord) {
609
 
            var coord = {lat: params.location.coord.lat, lng: params.location.coord.lon};
610
 
            url = _baseUrl+commands["mobileaggregation"]+"get.js?"+parameterize(baseParams)+"&"+
611
 
                  parameterize(coord);
 
634
 
 
635
            urls.current = _baseUrl + commands["geocode"] +
 
636
                    coord.lat + "/" + coord.lng +
 
637
                    "/observations/current.json?" +
 
638
                    parameterize(baseParams);
 
639
            urls.daily = _baseUrl + commands["geocode"] +
 
640
                    coord.lat + "/" + coord.lng +
 
641
                    "/forecast/daily/5day.json?" +
 
642
                    parameterize(baseParams);
 
643
            urls.hourly = _baseUrl + commands["geocode"] +
 
644
                    coord.lat + "/" + coord.lng +
 
645
                    "/forecast/hourly/48hour.json?" +
 
646
                    parameterize(baseParams);
612
647
        }
613
 
        return url;
 
648
 
 
649
        return urls;
614
650
    }
615
651
    //
616
652
    return {
617
653
        getData: function(params, apiCaller, onSuccess, onError) {
618
 
            var url = _getUrl(params),
 
654
            var urls = _getUrls(params),
619
655
                handlerMap = {
620
 
                    all: { type: "all", url: url}
621
 
                },
622
 
                response = {
623
 
                    location: params.location,
624
 
                    db: (params.db) ? params.db : null,
625
 
                    format: RESPONSE_DATA_VERSION
626
 
                },
627
 
                addDataToResponse = (function(request, data) {
628
 
                    var formattedResult;
629
 
                    response["data"] = formatResult(data, params.location);
 
656
                current: {
 
657
                    type: "current",
 
658
                    url: urls.current
 
659
                },
 
660
                daily: {
 
661
                    type: "daily",
 
662
                    url: urls.daily
 
663
                },
 
664
                forecast: {
 
665
                    type: "forecast",
 
666
                    url: urls.hourly
 
667
                }
 
668
            },
 
669
            response = {
 
670
                location: params.location,
 
671
                db: (params.db) ? params.db : null,
 
672
                format: RESPONSE_DATA_VERSION
 
673
            },
 
674
            respData = {},
 
675
            addDataToResponse = (function(request, data) {
 
676
                var formattedResult;
 
677
                respData[request.type] = data;
 
678
 
 
679
                if (respData["current"] !== undefined &&
 
680
                        respData["forecast"] !== undefined &&
 
681
                            respData["daily"] !== undefined) {
 
682
 
 
683
                    response["data"] = formatResult(respData, params.location);
630
684
                    onSuccess(response);
631
 
                }),
632
 
                onErrorHandler = (function(err) {
633
 
                    onError(err);
634
 
                });
635
 
            apiCaller(handlerMap.all, addDataToResponse, onErrorHandler);
 
685
                }
 
686
            }),
 
687
            onErrorHandler = (function(err) {
 
688
                onError(err);
 
689
            }),
 
690
            retryHandler = (function(err) {
 
691
                console.log("retry of " + trimAPIKey(err.request.url));
 
692
                var retryFunc = handlerMap[err.request.type];
 
693
 
 
694
                apiCaller(retryFunc, addDataToResponse, onErrorHandler);
 
695
            });
 
696
 
 
697
            apiCaller(handlerMap.current, addDataToResponse, retryHandler);
 
698
            apiCaller(handlerMap.forecast, addDataToResponse, retryHandler);
 
699
            apiCaller(handlerMap.daily, addDataToResponse, retryHandler);
636
700
        }
637
701
    }
638
702
})();