Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(package/weather): Version 1.0 of weather package #108

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bridges/python/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ requests = "==2.21.0"
pytube = "==9.5.0"
tinydb = "==3.9.0"
beautifulsoup4 = "==4.7.1"
pyowm = "==2.10.0"
Copy link
Member

@theoludwig theoludwig Mar 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is quite old, before we merge, we should use the latest versions of the dependencies used in this package.
Currently the latest version of pyowm is 3.2.0 and tzlocal is 2.1.0.

tzlocal = "==1.5.1"

[dev-packages]
36 changes: 32 additions & 4 deletions bridges/python/Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/weather/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
config/configuration25.sample.py
218 changes: 218 additions & 0 deletions packages/weather/OpenWeatherMap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import functools

import utils

from tzlocal import get_localzone
from pyowm import OWM


# Decorators
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid "useless" comments.


def load_config(func):
@functools.wraps(func)
def wrapper_load_config(string, entities):
payload = dict()
payload["string"] = string
payload["entities"] = entities

api_key = utils.config("api_key")
pro = utils.config("pro")
payload["temperature_units"] = utils.config("temperature_units")
payload["wind_speed_units"] = utils.config("wind_speed_units")

if (
(payload["temperature_units"] != "celsius")
and (payload["temperature_units"] != "fahrenheit")
):
return utils.output(
"end",
"invalid_temperature_units",
utils.translate("invalid_temperature_units")
)

if payload["wind_speed_units"] == "meters per seconds":
payload["wind_speed_units_response"] = payload["wind_speed_units"]
payload["wind_speed_units"] = "meters_sec"
elif payload["wind_speed_units"] == "miles per hour":
payload["wind_speed_units_response"] = payload["wind_speed_units"]
payload["wind_speed_units"] = "miles_hour"
else:
return utils.output(
"end",
"invalid_wind_speed_units",
utils.translate("invalid_wind_speed_units")
)

if pro:
payload["owm"] = OWM(api_key, subscription_type="pro")
else:
payload["owm"] = OWM(api_key)

return func(payload)
return wrapper_load_config


def acquire_weather(func):
@functools.wraps(func)
def wrapper_acquire_weather(payload):
for item in payload["entities"]:
if item["entity"] == "city":
utils.output(
"inter",
"acquiring",
utils.translate("acquiring")
)

payload["city"] = item["sourceText"].title()
payload["observation"] = payload["owm"].weather_at_place(payload["city"])
payload["wtr"] = payload["observation"].get_weather()

return func(payload)
return utils.output(
"end",
"request_error",
utils.translate("request_error")
)

return wrapper_acquire_weather


# Methods
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we should avoid that sort of comment.


@load_config
@acquire_weather
def current_weather(payload):
"""
Get the current weather.
"""

detailed_status = payload["wtr"].get_detailed_status()
temperatures = payload["wtr"].get_temperature(payload["temperature_units"])
humidity = payload["wtr"].get_humidity()

return utils.output(
"end",
"current_weather",
utils.translate(
"current_weather",
{
"detailed_status": detailed_status.capitalize(),
"city": payload["city"],
"temperature": temperatures["temp"],
"temperature_units": payload["temperature_units"].capitalize(),
"humidity": humidity
}
)
)


@load_config
@acquire_weather
def temperature(payload):
"""
Get the current temperature.
"""

temperatures = payload["wtr"].get_temperature(payload["temperature_units"])

return utils.output(
"end",
"temperature",
utils.translate(
"temperature",
{
"city": payload["city"],
"temperature": temperatures["temp"],
"temperature_units": payload["temperature_units"].capitalize()
}
)
)


@load_config
@acquire_weather
def humidity(payload):
"""
Get the current humidity.
"""

humidity = payload["wtr"].get_humidity()

return utils.output(
"end",
"humidity",
utils.translate(
"humidity",
{
"city": payload["city"],
"humidity": humidity
}
)
)


@load_config
@acquire_weather
def wind(payload):
"""
Get the current wind speed and direction.
"""

wind = payload["wtr"].get_wind(payload["wind_speed_units"])

return utils.output(
"end",
"wind",
utils.translate(
"wind",
{
"city": payload["city"],
"wind_speed": wind["speed"],
"wind_speed_units_response": payload["wind_speed_units_response"],
"wind_direction": wind["deg"]
}
)
)


@load_config
@acquire_weather
def sunrise(payload):
"""
Get when the sun rises.
"""

dt = payload["wtr"].get_sunrise_time("date")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid abbreviations in variable names.
So instead to call it dt, we could call it date_time for example.

dt = dt.astimezone(get_localzone())

return utils.output(
"end",
"sunrise",
utils.translate(
"sunrise",
{"time": dt.strftime("%H:%M:%S"), "city": payload["city"]}
)
)


@load_config
@acquire_weather
def sunset(payload):
"""
Get when the sun sets.
"""

dt = payload["wtr"].get_sunset_time("date")
dt = dt.astimezone(get_localzone())

return utils.output(
"end",
"sunset",
utils.translate(
"sunset",
{"time": dt.strftime("%H:%M:%S"), "city": payload["city"]}
)
)
49 changes: 49 additions & 0 deletions packages/weather/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Weather Package

The weather package contains modules which include getting the latest weather forecast.

## Modules

### OpenWeatherMap

#### Requirements
- tzlocal
- PyOWM

#### Usage

1. Register a new account on [OpenWeatherMap Sign Up](https://openweathermap.org/sign_up) unless you already have one.
2. Generate a new API key on [OpenWeatherMap API keys](https://home.openweathermap.org/api_keys).
3. Duplicate the file `packages/weather/config/config.sample.json` and rename it `config.json`.
4. Copy the API key in `packages/weather/config/config.json` and set the other options to your liking.
5. This package uses PyOWM. To install it: from inside leon directory `cd bridges/python`
6. `pipenv install tzlocal pyowm`
7. Done!

```
(en-US)
- "What's the weather like in Milan?"
- "When is the sun going to set in Rome?"
- "Is it windy in Chicago?"

(it-IT)
- "Che tempo fa a Milano?"
- "Quando tramonta il sole a Roma?"
...
```

#### Options
- `pro`: Set this to `true` if you have a premium subscription.
- `temperature_units`: Choose which temperature scale to use. ["celsius", "fahrenheit"]
- `wind_speed_units`: Choose which units to use for the wind speed. ["meters per second", "miles per hour"] More units coming soon.

#### Links

- [OpenWeatherMap API](https://developers.google.com/youtube/v3/getting-started)
- [PyOWM GitHub](https://github.com/csparpa/pyowm)
- [PyOWM Docs](https://pyowm.readthedocs.io/en/latest/)

#### TODO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not use "Todo" inside markdown files, instead it would be GitHub issues or Trello Card.

What do you think?

- Implement some sort of caching mechanism. PyOWM does support caching, but it's a bit messy. It might be easier to implement it from scratch, perhaps with some sort of db like TinyDb which is already supported by leon.
- Implement `pro` functionality for OWM.
- Add more weather services and related (UV, pollution, sky events, etc.).
Empty file added packages/weather/__init__.py
Empty file.
Empty file.
9 changes: 9 additions & 0 deletions packages/weather/config/config.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"OpenWeatherMap": {
"api_key": "YOUR_OPEN_WEATHER_MAP_TOKEN",
"pro": false,
"temperature_units": "celsius",
"wind_speed_units": "meters per seconds",
"options": {}
}
}
Empty file added packages/weather/data/.gitkeep
Empty file.
Empty file.
Loading