diff --git a/.gitignore b/.gitignore index f71ec5062..021bdc8d4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ packages/**/data/db/*.json server/src/data/expressions/classifier.json app/js/main.js package.json.backup -.python-version \ No newline at end of file +.python-version +Makefile diff --git a/package-lock.json b/package-lock.json index 64511eda0..d89a5839b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1424,7 +1424,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", @@ -3805,13 +3806,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -6178,7 +6181,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6593,7 +6597,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6649,6 +6654,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6692,12 +6698,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/packages/leonweather/LICENSE b/packages/leonweather/LICENSE new file mode 100644 index 000000000..adf6beb67 --- /dev/null +++ b/packages/leonweather/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 gnouf1 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/leonweather/README.md b/packages/leonweather/README.md new file mode 100644 index 000000000..792623b3f --- /dev/null +++ b/packages/leonweather/README.md @@ -0,0 +1,56 @@ +![Logo](https://zupimages.net/up/19/23/fn36.png) +# Leon Weather + +The Leon Weather Package contains modules related with the weather. + +## Features already available + +- Ask for the weather and temperatures in any cities in the world + + ``` + User : Can you tell me the weather in Paris ? + Leon : The weather in paris is clear andthe temperature is 28°C. + ``` + +- Ask for the weather and temperatures for 5 days in future with correct conjugation: + + ``` + User : Can you tell me the weather in Paris tomorrow? + Leon : The weather in Paris on 2019-06-06 at 12:00:00 will be rain and the temperature will be 18.4 °C. + ``` + + Default hour is 12:00:00. + +- Ask the weather every 3 hours: + + ``` + User :What's the weather in Paris in 3 hours ? + Leon : The weather in Paris on 2019-06-06 at 04:15:08 will be clear and the temperature will be 9.7°C. + ``` + + ``` + User : What's the weather in paris tomorrow at 09:00 AM? + Leon : The weather in Paris on 2019-06-07 at 09:00:00 will be rain and the temperature will be 15.7°C. + ``` + +- Ask temperatures in Celsuis, Fahrenheit and Kelvin by changing config file: + + ```json + { + "weather": { + "API_key": "0000", + "Measure":"C", <--- HERE 'C'|'F'|'K' + "options": {} + } + } + ``` + + As you can see on this file you can also change the API_Key, if you want to use default API_Key put `0000` in this field + +## Future features + +- Average temperature for each day +- Some frills such as wind speed etc... + +## Credit +Open Weather API : [Go](https://openweathermap.org/api) diff --git a/packages/leonweather/__init__.py b/packages/leonweather/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packages/leonweather/config/config.sample.json b/packages/leonweather/config/config.sample.json new file mode 100644 index 000000000..089d07849 --- /dev/null +++ b/packages/leonweather/config/config.sample.json @@ -0,0 +1,7 @@ +{ + "weather": { + "API_key": "0000", + "Measure":"C", + "options": {} + } +} diff --git a/packages/leonweather/data/answers/en.json b/packages/leonweather/data/answers/en.json new file mode 100644 index 000000000..669d4e492 --- /dev/null +++ b/packages/leonweather/data/answers/en.json @@ -0,0 +1,22 @@ +{ + "weather" :{ + "weather_t": [ + "The weather in %cit% on %date% at %hour% is %sky% and the temperature is %t%." + ], + "weather_f": [ + "The weather in %cit% on %date% at %hour% will be %sky% and the temperature will be %t%." + ], + "error":[ + "Something get wrong in leonweather package." + ], + "404_city_not_found":[ + "I can't found this city ! Maybe if you add the country code after the name, like this \"london,uk\" it's will work better !" + ], + "ezy":[ + "Easy, easy, too many people ask weather in this minute, wait 60 seconds please." + ], + "test" :[ + "City : %cit% %date_ref%." + ] + } +} diff --git a/packages/leonweather/data/answers/fr.json b/packages/leonweather/data/answers/fr.json new file mode 100644 index 000000000..0613c2dc3 --- /dev/null +++ b/packages/leonweather/data/answers/fr.json @@ -0,0 +1,22 @@ +{ + "weather" :{ + "weather_t": [ + "La météo de %cit% le %date% à %hour% est %sky% et la température est de %t%.", + ], + "weather_f": [ + "La météo de %cit% le %date% à %hour% sera %sky% et la température sera de %t%.", + ], + "error":[ + "Quelque chose a posé soucis dans le paquet." + ], + "404_city_not_found":[ + "je ne trouve pas la ville ! Essayez d'ajouter le code du pays derrière de cette façon : \"london,uk\" ça marchera peut être mieux !" + ], + "ezy":[ + "Doucement, je reçois trop de demande de trop de gens. Je suis limité à 60 par minutes." + ], + "town" :[ + "City : %cit%." + ] + } +} diff --git a/packages/leonweather/data/expressions/en.json b/packages/leonweather/data/expressions/en.json new file mode 100644 index 000000000..e7e81ad47 --- /dev/null +++ b/packages/leonweather/data/expressions/en.json @@ -0,0 +1,23 @@ +{ + "weather": { + "weather" :{ + "expressions":[ + "What's the weather in Paris ?", + "Can you tell me the weather in Paris ?", + "What's the weather for tomorrow in Paris ?" + ], + "entities": [ + { + "type": "trim", + "name": "city", + "conditions": [ + { + "type": "after_first", + "from": "in" + } + ] + } + ] + } + } +} diff --git a/packages/leonweather/data/expressions/fr.json b/packages/leonweather/data/expressions/fr.json new file mode 100644 index 000000000..43960097f --- /dev/null +++ b/packages/leonweather/data/expressions/fr.json @@ -0,0 +1,29 @@ +{ + "weather": { + "weather" :{ + "expressions":[ + "Quelle est la météo à Paris ?", + "Quel temps fait-il à Paris ?", + "Peut-tu me donne la météo de Paris ?" + ], + "entities": [ + { + "type": "trim", + "name": "city", + "conditions": [ + { + "type": "between", + "from": "à", + "to": "?" + }, + { + "type": "between", + "from": "de", + "to": "?" + } + ] + } + ] + } + } +} diff --git a/packages/leonweather/test/weather.spec.js b/packages/leonweather/test/weather.spec.js new file mode 100644 index 000000000..326c02a2a --- /dev/null +++ b/packages/leonweather/test/weather.spec.js @@ -0,0 +1,43 @@ +'use strict' + +describe('leonweather:weather', async () => { + test('Check the weather today', async () => { + global.nlu.brain.execute = jest.fn() + await global.nlu.process('What\'s the weather in Paris ?') + + const [obj] = global.nlu.brain.execute.mock.calls + await global.brain.execute(obj[0]) + + expect(global.brain.finalOutput.codes).toIncludeSameMembers(['weather_t']) + }) + + test('Check the weather in future', async () => { + global.nlu.brain.execute = jest.fn() + await global.nlu.process('What\'s the weather in Paris tomorrow ?') + + const [obj] = global.nlu.brain.execute.mock.calls + await global.brain.execute(obj[0]) + + expect(global.brain.finalOutput.codes).toIncludeSameMembers(['weather_f']) + }) + + test('Error', async () => { + global.nlu.brain.execute = jest.fn() + await global.nlu.process('What\'s the weather in Vide0 ?') + + const [obj] = global.nlu.brain.execute.mock.calls + await global.brain.execute(obj[0]) + + expect(global.brain.finalOutput.codes).toIncludeSameMembers(['error']) + }) + + test('The city does not exist', async () => { + global.nlu.brain.execute = jest.fn() + await global.nlu.process('What\'s the weather in Blublu ?') + + const [obj] = global.nlu.brain.execute.mock.calls + await global.brain.execute(obj[0]) + + expect(global.brain.finalOutput.codes).toIncludeSameMembers(['404_city_not_found']) + }) +}) diff --git a/packages/leonweather/version.txt b/packages/leonweather/version.txt new file mode 100644 index 000000000..7dea76edb --- /dev/null +++ b/packages/leonweather/version.txt @@ -0,0 +1 @@ +1.0.1 diff --git a/packages/leonweather/weather.py b/packages/leonweather/weather.py new file mode 100644 index 000000000..6116f235b --- /dev/null +++ b/packages/leonweather/weather.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +import utils +import requests +import time + + +def weather(string, entities): + """Check the weather""" + + # We init city + city = 'vide0' + # We init the time flag. + date_flag = 0 + timex_ref = 'EMPTY_REF' + date_ref = 'EMPTY_REF' + date_mem = 'EMPTY_REF' + + number_of_cities = 0 + # We init API Key & check it + API_key = utils.config('API_key') + if API_key == '0000': + API_key = '9ad6e7083f9e9c5d558ee4d7925e17ed' + # We init and check the measure ID (C or F or K) + measure = utils.config('Measure') + + # We init "lang" + language = utils.info() + language = language['lang'] + # We search in 'entities' the name of the city + for item in entities: + if item['entity'] == 'city' and number_of_cities == 0: + city = item['sourceText'].lower() + number_of_cities = 1 + if item['entity'] == 'date': + date_flag = 1 + date_ref = item['resolution']['date'] + timex_ref = item['resolution']['timex'] + date_ref = date_ref.replace('00:00:00.000', '12:00:00.000') + if item['entity'] == 'datetime': + if item['resolution']['values'][0]['timex'] != 'PRESENT_REF': + date_flag = 1 + date_ref = item['resolution']['values'][0]['value'] + date_mem = date_ref + hour = int(date_ref[11:13]) + if hour % 3 != 0: + if hour % 3 == 1: + hour = hour - 1 + if hour <= 0: + hour = 3 + elif hour % 3 == 2: + hour = hour + 1 + if hour >= 24: + hour = 21 + if hour < 10: + date_ref = date_ref[:11] + '0'+str(hour)+':00:00' + else: + date_ref = date_ref[:11] + str(hour)+':00:00' + + # If he don't found any city + if city == 'vide0': + return utils.output('end', 'error', utils.translate('error')) + + date_ref = date_ref.replace('T', ' ') + date_ref = date_ref[:19] + + if date_flag == 0 or timex_ref == 'PRESENT_REF': + url = "http://api.openweathermap.org/data/2.5/weather?appid="+API_key+"&q=" + city + "&lang=" + language + content = requests.get(url) + data = content.json() + + # Codes error test + # If it's not '200' we have trouble + if data['cod'] != 200: + # If he don't found the city he retry without symbol after the city's name + if data['cod'] == "404": + vir = city.find(',') + es = city.find(' ') + pt = city.find('.') + + if vir != -1: + city = city[:vir] + elif es != -1: + city = city[:es] + elif pt != -1: + city = city[:pt] + + url = "http://api.openweathermap.org/data/2.5/weather?appid="+API_key+"&q=" + city + "&lang=" + language + content = requests.get(url) + data = content.json() + if data['cod'] == "404": + return utils.output('end', '404_city_not_found', utils.translate('404_city_not_found')) + # If the number of request is higher than 60/min + elif data['cod'] == "429": + return utils.output('end', 'ezy', utils.translate('ezy')) + else: + return utils.output('end', 'error', utils.translate('error')) + + t = data['main']['temp'] + sky = data['weather'][0]['description'] + + date = time.strftime("%d-%m-%Y", time.localtime()) + hour = time.strftime("%H:%M:%S", time.localtime()) + + elif date_flag != 0: + url = "http://api.openweathermap.org/data/2.5/forecast?appid="+API_key+"&q=" + city + "&lang=" + language + content = requests.get(url) + data = content.json() + + # Codes error test (same) + if data['cod'] != "200": + if data['cod'] == "404": + vir = city.find(',') + es = city.find(' ') + pt = city.find('.') + + if vir != -1: + city = city[:vir] + elif es != -1: + city = city[:es] + elif pt != -1: + city = city[:pt] + url = "http://api.openweathermap.org/data/2.5/forecast?appid="+API_key+"&q=" + city + "&lang=" + language + content = requests.get(url) + data = content.json() + if data['cod'] == "404": + return utils.output('end', '404_city_not_found', utils.translate('404_city_not_found')) + elif data['cod'] == "429": + return utils.output('end', 'ezy', utils.translate('ezy')) + else: + return utils.output('end', 'error', utils.translate('error')) + + flag_w = 0 + i = 0 + while i < 40 and flag_w == 0: + if data['list'][i]['dt_txt'] == date_ref: + flag_w = 1 + t = data['list'][i]['main']['temp'] + sky = data['list'][i]['weather'][0]['main'] + datetmp = data['list'][i]['dt_txt'] + if date_mem != 'EMPTY_REF': + datetmp = date_mem + date = datetmp[:10] + hour = datetmp[10:] + i = i + 1 + if flag_w == 0: + return utils.output('end', 'error', utils.translate('error')) + + if measure == 'C': + # Convert in Celsuis + t = t-273.15 + elif measure == 'F': + # Convert in Fahrenheit + t = t*9/5 - 459.67 + t = round(t, 1) + + vir = city.find(',') + es = city.find(' ') + + if vir != -1: + city = city[:vir] + elif es != -1: + city = city[:es] + + city = city.capitalize() + sky = sky.lower() + t = str(t)+'°'+measure + + if date_flag != 0: + time_indic = '_f' + else: + time_indic = '_t' + + return utils.output('end', 'weather'+time_indic, utils.translate('weather'+time_indic, {'cit': city, 'sky': sky, 't': t, 'date': date, 'hour': hour}))