Added weather alerts

This commit is contained in:
Martin Donnelly 2018-03-01 23:45:04 +00:00
parent 326ca1005b
commit 9f13a3e391
10 changed files with 265 additions and 21 deletions

9
package-lock.json generated
View File

@ -9588,6 +9588,15 @@
"error": "7.0.2"
}
},
"request-json": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/request-json/-/request-json-0.6.3.tgz",
"integrity": "sha512-5TVnMD3LaeK0GRCyFlsNgJf5Fjg8J8j7VEfsoJESSWZlWRgPIf7IojsBLbTHcg2798JrrRkJ6L3k1+wj4sglgw==",
"requires": {
"depd": "1.1.2",
"request": "2.83.0"
}
},
"request-promise": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",

View File

@ -34,6 +34,7 @@
"muicss": "^0.9.36",
"node-foursquare-venues": "^1.1.0",
"openweather-apis": "^3.3.5",
"request-json": "^0.6.3",
"twitter": "^1.7.1",
"uglifyify": "^4.0.5",
"underscore": "^1.8.3",

View File

@ -21,6 +21,7 @@ app.use(express.static(path.join(__dirname, sitePath)));
app.get('/weather', cache('45 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
weather.doGetOpenWeather(req.query.ll)
.then((d) => {
res.send(d);
@ -36,6 +37,25 @@ app.get('/weather', cache('45 minutes'), (req, res) => {
}
});
app.get('/weatheralert', cache('45 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
// weather.doGetOpenWeather(req.query.ll)
// doGetDarkSkyWeather
weather.doGetDarkSkyWeather(req.query.ll)
.then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
res.status(500).send('There was an error!');
});
else {
// throw new Error('Weather: LL missing');
logger.warn('Weather: LL missing');
res.status(500).send('LL Missing');
}
});
app.get('/fsexplore', cache('30 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
foursquare.doGetFourSquareExplore(req.query.ll)

View File

@ -1,4 +1,4 @@
const Client = require('request-json');
const logger = require('log4js').getLogger('Weather');
const weather = require('openweather-apis');
@ -6,6 +6,9 @@ logger.level = 'debug';
const openWeatherApiKey = process.env.openweatherAPI || '936a0ed9eb23b95cf08fc9f693c24264';
const darkskyApiKey = process.env.darkskyApiKey || '9ad2a41d420f3cf4960571bb886f710c';
const DSclient = Client.createClient(`https://api.darksky.net/forecast/${ darkskyApiKey }/`);
weather.setAPPID(openWeatherApiKey);
weather.setLang('en');
// weather.setCity('Glasgow City');
@ -38,5 +41,17 @@ function doGetOpenWeatherForecast(ll) {
});
}
function doGetDarkSkyWeather(ll) {
const query = `${ll}?units=uk2&exclude=daily,flags,minutely,hourly`;
logger.debug(query);
return new Promise((resolve, reject) => {
DSclient.get(query, function(err, res, body) {
if (err || !body || !body.currently)
return reject(err);
module.exports = { doGetOpenWeather, doGetOpenWeatherForecast };
return resolve(body);
});
});
}
module.exports = { doGetOpenWeather, doGetOpenWeatherForecast, doGetDarkSkyWeather };

View File

@ -25,10 +25,16 @@
<div class="mui-container">
<div id="greet"></div>
</div>
<div class="mui-container" id="viewFrame">
<div id="weatherAlertShell" class="mui-panel" style="display: none;">
<div id="weatherAlertTitle" class="mui--text-title cardTitle">Weather Alert</div>
<div id="weatherAlert"></div>
</div>
<div id="bymeShell" class="mui-panel" style="display: none;">
<div id="byMeTitle" class="mui--text-title cardTitle">By me</div>
<div id="byme"></div>
</div>
<div id="byMeTitle" class="mui--text-title cardTitle">By me</div>
<div id="byme"></div>
</div>
<div id="nearbyShell" class="mui-panel" style="display: none;">
@ -37,14 +43,14 @@
</div>
<div id="newsShell" class="mui-panel" style="display: none;">
<div class="mui--text-title cardTitle">Latest news</div>
<div id="news" class="scrolling-wrapper-flexbox"></div>
</div>
<div class="mui--text-title cardTitle">Latest news</div>
<div id="news" class="scrolling-wrapper-flexbox"></div>
</div>
<div id="weatherShell" class="mui-panel" style="display: none;">
<div class="mui--text-title cardTitle">Weather</div>
<div id="weather"></div>
</div>
<div class="mui--text-title cardTitle">Weather</div>
<div id="weather"></div>
</div>
</div>

View File

@ -113,7 +113,6 @@ const LocationModel = Backbone.Model.extend({
console.error(err);
this.set('location', newLocation);
});
}
else {
newLocation.city = current.city;
@ -126,16 +125,16 @@ const LocationModel = Backbone.Model.extend({
// console.log('>> distanceFromLastGeocode', distanceFromLastGeocode, TimeFormat.fromMs(timestamp - lastGeocode.timestamp, 'hh:mm:ss'));
console.log('>> distanceFromLastGeocode', distanceFromLastGeocode, TimeFormat.fromMs(currentTime - lastGeocode.timestamp, 'hh:mm:ss'));
console.log(`(currentTime:${currentTime}, timestamp:${timestamp}, lastGeocode.timestamp:${lastGeocode.timestamp})`);
console.log('(currentTime - current.timestamp > 900000) ' , (currentTime - current.timestamp > 900000));
//if ((distanceFromLast > 0.5 && distanceFromLast < 2.0) || (timestamp - current.timestamp > 900000)) {
if ((distanceFromLast > 0.5 && distanceFromLast < 2.0) || (currentTime - current.timestamp > 900000)) {
console.log('(currentTime - current.timestamp > 900000) ', (currentTime - current.timestamp > 900000));
// if ((distanceFromLast > 0.5 && distanceFromLast < 2.0) || (timestamp - current.timestamp > 900000)) {
if ((distanceFromLast > 0.5 && distanceFromLast < 2.0) || ((currentTime - current.timestamp > 900000) && (currentTime - lastGeocode.timestamp < 1.8e+6))) {
// dont bother re geocoding
console.log('Slightly moved from previous');
this.set('location', newLocation);
this.set('moving', moving);
}
else if (distanceFromLastGeocode >= 2.0 || (timestamp - lastGeocode.timestamp > 1.8e+6) ) {
console.log('Moved from previous', (timestamp - lastGeocode.timestamp > 1.8e+6));
else if (distanceFromLastGeocode >= 2.0 || (currentTime - lastGeocode.timestamp >= 1.8e+6) ) {
console.log('Moved from previous', (currentTime - lastGeocode.timestamp >= 1.8e+6));
console.info('>> Location:geocoder request');
geocoder.reverse(latlong)
.then(function(res) {

View File

@ -92,7 +92,7 @@ const WeatherModel = Backbone.Model.extend({
},
'getWeather': function() {
// const ll = this.get('llShort');
console.info('>> Weather:request');
console.log('>> Weather request');
const llFixed = this.get('llFixed');
request({
'url': `${window.loc}/weather`,
@ -175,7 +175,7 @@ const WeatherView = Backbone.View.extend({
console.log('>> Weather No location yet');
},
'render': function() {
console.info('>> Weather:Render');
console.log('>> Weather:Render');
this.$el.empty();
const item = this.wCollection.first();

161
src/v1/js/WeatherAlert.js Normal file
View File

@ -0,0 +1,161 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const fecha = require('fecha');
const { get } = require('lodash');
const { reduceOpenWeather } = require('./reducers');
const { distance, toHour } = require('./utils');
const WeatherAlertModel = Backbone.Model.extend({
'defaults': function (obj) {
// return a new object
return {
'update': new Date().getTime()
};
},
'initialize': function () {
this.run = false;
this.listenTo(this, 'change:ll', this.onChange);
this.listenTo(this, 'change:update', this.onChange);
},
'onChange': function () {
console.log('Weather LL has changed');
// if distance change > 10km
// if its been an hour since last update
if (!this.has('log')) {
console.info('First run');
this.getWeatherAlert();
}
else {
const log = this.get('log');
const timeDiff = new Date().getTime() - log.time;
const ll = this.get('ll').split(',');
const dist = distance(log.lat, log.long, ll[0], ll[1]);
console.log('Weather distance:', dist);
if ((dist > 5.0) && (timeDiff > 1.8e+6))
this.getWeatherAlert();
else if (timeDiff > 3.6e+6) {
console.log('WeatherAlert hourly update');
this.getWeatherAlert();
}
}
},
'getWeatherAlert': function () {
// const ll = this.get('llShort');
console.log('>> WeatherAlert request');
const llFixed = this.get('ll');
request({
'url': `${window.loc}/weatheralert`,
'method': 'GET',
'qs': {
'll': llFixed
}
}, function (err, res, body) {
if (err)
console.error(err);
else {
// console.log(body);
const fsJSON = JSON.parse(body);
const alerts = get(fsJSON, 'alerts', []);
const item = alerts.slice(0, 1);
this.set('alert', item);
console.log(fsJSON);
this.logUpdate();
}
}.bind(this));
},
'logUpdate': function () {
console.log('WeatherAlert logging:');
const ll = this.get('ll').split(',');
const log = { 'lat': ll[0], 'long': ll[1], 'time': new Date().getTime() };
console.log('>>WeatherAlert log', log);
this.set('log', log);
this.timerID = setTimeout(
() => this.tick(),
toHour
);
// console.log(this);
},
'tick': function () {
console.log('Set update');
this.set('update', new Date().getTime());
}
}
);
const WeatherAlertView = Backbone.View.extend({
'className': 'mui--align-middle',
'initialize': function(options) {
this.eventBus = options.eventBus;
this.location = options.location;
this.$parent = this.$el.parent();
this.$title = $('#weatherAlertTitle');
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.model.bind('change:alert', this.render, this);
},
'template': _.template(`
<div>
<div class="mui--text-body1 %>"><%=description%></div>
<div class="mui--text-dark-secondary mui--text-caption mui--text-right">
Expires <%= readdate %>
</div>
`),
'attributes': function() {
return {
'class': 'mui--align-middle'
};
},
'events': {
'click': 'doClick'
},
'updateLocation': function(l) {
console.log('>> WeatherAlert Location has changed...');
if (l.has('location')) {
const location = l.get('location');
if (location.hasOwnProperty('atHome'))
this.model.set('ll', location.llFixed);
}
else
console.log('>> Weather No location yet');
},
'render': function() {
console.log('>> WeatherAlert:Render');
this.$el.empty();
const alert = this.model.get('alert');
if (alert.length === 0)
// nothing to see, hide the alert.
this.$parent.hide();
else {
const fts = new Date(alert[0].expires * 1000);
alert[0].readdate = fecha.format(fts, 'default');
this.$title.html(alert[0].title);
this.$el.html(this.template(alert[0]));
this.$el.parent().show();
}
}
});
module.exports = { WeatherAlertModel, WeatherAlertView };

View File

@ -7,6 +7,7 @@ const { LocationModel } = require('./Location');
const { GreetModel, GreetView } = require('./Greet');
const { NearbyModel, NearbyView } = require('./Nearby');
const { WeatherModel, WeatherView } = require('./Weather');
const { WeatherAlertModel, WeatherAlertView } = require('./WeatherAlert');
const { NewsModel, NewsView } = require('./News');
const { ByMeModel, ByMeView } = require('./RightByMe');
@ -56,7 +57,9 @@ else
app.nearby = new NearbyView({ 'model': new NearbyModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#nearby' });
app.weather = new WeatherView({ 'model': new WeatherModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#weather' });
app.weather = new WeatherView({ 'model': new WeatherModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#weather', 'viewFrame':'#viewFrame' });
app.weatherAlert = new WeatherAlertView({ 'model': new WeatherAlertModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#weatherAlert', 'viewFrame':'#viewFrame' });
app.news = new NewsView({ 'model': new NewsModel(), 'eventBus': app.eventBus, 'el':'#news' });

View File

@ -1,6 +1,36 @@
const fecha = require('fecha');
const { get } = require('lodash');
function reduceDarksky(item, city) {
// Openweather returns timestamps in seconds. Moment requires them in milliseconds.
// Replaced Moment with Fecha.
// Too many issues trying to get Moment packaged into the bundle.
if (!item || item === null) return {};
const currently = get(item, 'currently');
const alerts = get(item, 'alerts', []);
const fts = new Date(currently.time * 1000);
const newItem = { 'timestamp': fts,
'icon': `wi-wu-${currently.icon}`,
'summary': currently.summary,
'temp': parseInt(currently.temperature, 10),
'feels': parseFloat(currently.apparentTemperature, 10),
'datelong': fecha.format(fts, 'YYYY-MM-DDTHH:mm:ss.SSSZZ'),
'readdate': fecha.format(fts, 'default'),
'time': fts,
'date': fecha.format(fts, 'D/M'),
'day': fecha.format(fts, 'ddd'),
'city': city
};
return {
'item' : newItem,
'alerts': (alerts.length === 0) ? alerts : alerts.slice(0, 1)
};
}
function reduceOpenWeather(item, city) {
// Openweather returns timestamps in seconds. Moment requires them in milliseconds.
// Replaced Moment with Fecha.
@ -88,4 +118,4 @@ const pubdateSrc = fecha.parse(item.pubdate, 'ddd, DD MMM YYYY HH:mm:SS ZZ');
</article>`;
*/
module.exports = { reduceOpenWeather, reduceNearby, reduceEuronews };
module.exports = { reduceOpenWeather, reduceNearby, reduceEuronews, reduceDarksky };