-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 6e43c6f
Showing
15 changed files
with
1,122 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[flake8] | ||
exclude = .git,.nox,venv | ||
max-line-length = 90 | ||
select = E,F,W,C | ||
ignore=E501,W503,E203 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
venv/ | ||
*.egg-info/ | ||
.vscode/ | ||
dist/ | ||
venv |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Caiyun Weather API Python SDK | ||
|
||
## Install | ||
|
||
Python 3.6+ is required. | ||
|
||
```sh | ||
pip install cy-weather-api | ||
``` | ||
|
||
## Usage | ||
|
||
### Request Caiyun API | ||
|
||
```py | ||
from cy_weather_api import CyWeatherAPIClient | ||
|
||
client = CyWeatherAPIClient(token="TAkhjf8d1nlSlspN") | ||
apiResult = client.fetch(lng=101.8551, lat=26.6832, lang="zh_CN", alert=True) | ||
print(apiResult.result.hourly.description) | ||
apiResult = client.fetch(lng=-0.2008, lat=51.5024, lang="en_GB") | ||
print(apiResult.result.hourly.description) | ||
apiResult = client.fetch(lng=73.9808, lat=40.7648, lang="en_US") | ||
print(apiResult.result.hourly.description) | ||
``` | ||
|
||
Output sample: | ||
|
||
``` | ||
晴,今天晚间20点钟后转小雨,其后多云 | ||
clear weather over the next 24 hours | ||
clear weather, overcast after 20 o'clock this afternoon, followed by cloudy | ||
``` | ||
|
||
### Use our dataclass models | ||
|
||
The default HTTP client is requests, you can other HTTP cient to request API, | ||
and pass the response dict to our models (based on `dataclasss`): | ||
|
||
```py | ||
from cy_weather_api import initFromDict | ||
|
||
data = { | ||
"status": "ok", | ||
"api_version": "v2.5", | ||
"api_status": "active", | ||
"lang": "en_US", | ||
"unit": "metric", | ||
"tzshift": 28800, | ||
"timezone": "Asia/Shanghai", | ||
"server_time": 1589125757, | ||
"location": [39.888888, 116.674501], | ||
"result": {"forecast_keypoint": "test forecast_keypoint", "primary": 0}, | ||
} | ||
apiResult = initFromDict(data) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from cyapi.client import CyAPIClient | ||
from cyapi.models import initFromDict | ||
|
||
__all__ = ["CyAPIClient", "initFromDict"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
from dataclasses import dataclass | ||
|
||
import requests | ||
from cyapi.models import initFromDict, CyWeatherAPIResponseHandler | ||
|
||
API_BASE = ( | ||
"http://api.caiyunapp.com/v2.5/{token}/{lng},{lat}/weather.json?" | ||
"&lang={lang}" | ||
"&unit={unit}" | ||
"&dailysteps={dailysteps}" | ||
"&hourlysteps={hourlysteps}" | ||
) | ||
VALID_UNIT_OPTIONS = ["metric", "metric:v1", "metric:v2", "imperial", "SI"] | ||
VALID_GRANU_OPTIONS = ["realtime", "minutely", "hourly", "daily", "weather"] | ||
|
||
|
||
@dataclass | ||
class CyWeatherAPIClient: | ||
token: str = "TAkhjf8d1nlSlspN" | ||
session = requests.Session() | ||
|
||
def fetch( | ||
self, | ||
lng: float, | ||
lat: float, | ||
lang: str = "en_US", | ||
begin: int = None, | ||
alert: bool = False, | ||
granu: str = None, | ||
unit: str = "metric", | ||
dailysteps: int = 5, | ||
hourlysteps: int = 48, | ||
) -> CyWeatherAPIResponseHandler: | ||
|
||
if unit and unit not in VALID_UNIT_OPTIONS: | ||
raise ValueError( | ||
"Invaliad unit, got {}, expect one from {}".format( | ||
unit, VALID_UNIT_OPTIONS | ||
) | ||
) | ||
|
||
if granu and granu not in VALID_GRANU_OPTIONS: | ||
raise ValueError( | ||
"Invaliad granu, got {}, expect one from {}".format( | ||
unit, VALID_GRANU_OPTIONS | ||
) | ||
) | ||
|
||
if dailysteps < 1 or dailysteps > 15: | ||
raise ValueError("dailysteps in range [1, 15]") | ||
|
||
if hourlysteps < 1 or hourlysteps > 360: | ||
raise ValueError("hourlysteps in range [1, 360]") | ||
|
||
url = API_BASE.format( | ||
token=self.token, | ||
lng=lng, | ||
lat=lat, | ||
lang=lang, | ||
unit=unit, | ||
hourlysteps=hourlysteps, | ||
dailysteps=dailysteps, | ||
) | ||
|
||
if granu: | ||
url += "&granu={}".format(granu) | ||
|
||
if begin is not None: | ||
try: | ||
int(begin) | ||
url += "&begin={}".format(begin) | ||
except Exception: | ||
raise ValueError("Invalid begin") | ||
|
||
_data = self.session.get(url).json() | ||
if _data["status"] != "ok": | ||
_mess = ( | ||
"Calling Caiyun API Errored with {error_mess}, please check token status\n" | ||
"url: {url}\n" | ||
"data: {api_data}\n" | ||
) | ||
mess = _mess.format(url=url, api_data=_data, error_mess=_data.get("error")) | ||
raise ValueError(mess) | ||
return initFromDict(_data) | ||
|
||
|
||
if __name__ == "__main__": | ||
client = CyWeatherAPIClient(token="TAkhjf8d1nlSlspN") | ||
apiResult = client.fetch(lng=101.8551, lat=26.6832, lang="zh_CN", alert=True) | ||
print(apiResult.result.hourly.description) | ||
apiResult = client.fetch(lng=-0.2008, lat=51.5024, lang="en_GB") | ||
print(apiResult.result.hourly.description) | ||
apiResult = client.fetch(lng=73.9808, lat=40.7648, lang="en_US") | ||
print(apiResult.result.hourly.description) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
"""Dataclass for Caiyun Weather API. | ||
Below dataclasses arr based on Caiyun Weather API v2.5, the offical API docs: | ||
zh_CN: https://open.caiyunapp.com/通用预报接口/v2.5 | ||
en_US: https://open.caiyunapp.com/General_weather_interface/v2.5 | ||
We encourage use Golang's coding style: | ||
1. Use black as format tool. | ||
2. Use Camel-Case. | ||
3. Avoid complex oop tricks. | ||
""" | ||
|
||
import json | ||
from dataclasses import asdict, dataclass, is_dataclass | ||
from typing import Dict, List | ||
|
||
import orjson | ||
from dacite import from_dict | ||
|
||
from cyapi.models.result import cyWeatherAPIResponseResultStruct | ||
|
||
|
||
class EnhancedJSONEncoder(json.JSONEncoder): | ||
def default(self, o): | ||
if is_dataclass(o): | ||
return asdict(o) | ||
return super().default(o) | ||
|
||
|
||
@dataclass | ||
class CyWeatherAPIResponseHandler: | ||
status: str | ||
api_version: str | ||
api_status: str | ||
lang: str | ||
unit: str | ||
tzshift: int | ||
timezone: str | ||
server_time: int | ||
location: List[float] | ||
result: cyWeatherAPIResponseResultStruct | ||
|
||
def __post_init__(self): | ||
pass | ||
|
||
def dumps(self, ensure_text=True) -> bytes: | ||
"""Fast dumps via orjson. | ||
Check orjson docs for details: https://github.com/ijl/orjson#dataclass | ||
""" | ||
if ensure_text: | ||
return orjson.dumps(self).decode("utf-8") | ||
else: | ||
return orjson.dumps(self) | ||
|
||
def dumps_pretty(self, **kwargs) -> str: | ||
"""Dumps JSON with indent. | ||
Note: please do not change `cls` option if you don't know it. | ||
""" | ||
return json.dumps(self, cls=EnhancedJSONEncoder, **kwargs) | ||
|
||
|
||
def initFromDict(data: Dict) -> CyWeatherAPIResponseHandler: | ||
return from_dict(data_class=CyWeatherAPIResponseHandler, data=data) | ||
|
||
|
||
if __name__ == "__main__": | ||
import requests | ||
# from dacite import from_dict | ||
|
||
# NOTE: Test token, no one can ensure its availability. | ||
TOKEN = "TAkhjf8d1nlSlspN" | ||
LNG, LAT = 116.3883, 39.9289 | ||
URL = "http://api.caiyunapp.com/v2.5/{TOKEN}/{LNG},{LAT}/weather".format( | ||
TOKEN=TOKEN, LNG=LNG, LAT=LAT | ||
) | ||
|
||
apiResponseDataclass = from_dict( | ||
data_class=CyWeatherAPIResponseHandler, data=requests.get(URL).json() | ||
) | ||
print(apiResponseDataclass.dumps()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from dataclasses import dataclass | ||
from typing import List | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseAlertContentItemStruct: | ||
province: str | ||
status: str | ||
code: str | ||
description: str | ||
pubtimestamp: float | ||
city: str | ||
adcode: str | ||
regionId: str | ||
latlon: List[float] | ||
county: str | ||
alertId: str | ||
request_status: str | ||
source: str | ||
title: str | ||
location: str | ||
|
||
def __post__init(self): | ||
self.lng = self.latlon[-1] | ||
self.lat = self.latlon[0] | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseAlertStruct: | ||
status: str = None | ||
content: List[cyWeatherAPIResponseAlertContentItemStruct] = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from dataclasses import dataclass | ||
from typing import List, Any | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyAstroItemTimeStruct: | ||
time: str | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyAstroItemStruct: | ||
date: str | ||
sunrise: cyWeatherAPIResponseDailyAstroItemTimeStruct | ||
sunset: cyWeatherAPIResponseDailyAstroItemTimeStruct | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyMaxMinAvgItemStruct: | ||
date: str | ||
max: float | ||
min: float | ||
avg: float | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyWindItemPropertyStruct: | ||
speed: float | ||
direction: float | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyWindItemStruct: | ||
date: str | ||
max: cyWeatherAPIResponseDailyWindItemPropertyStruct | ||
min: cyWeatherAPIResponseDailyWindItemPropertyStruct | ||
avg: cyWeatherAPIResponseDailyWindItemPropertyStruct | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyAirQualityItemAQITypeStruct: | ||
chn: float | ||
usa: float | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyAirQualityItemAQIStruct: | ||
date: str | ||
max: cyWeatherAPIResponseDailyAirQualityItemAQITypeStruct | ||
min: cyWeatherAPIResponseDailyAirQualityItemAQITypeStruct | ||
avg: cyWeatherAPIResponseDailyAirQualityItemAQITypeStruct | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyAirQualityItemStruct: | ||
aqi: List[cyWeatherAPIResponseDailyAirQualityItemAQIStruct] | ||
pm25: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailySkyconItemStruct: | ||
date: str | ||
value: str | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyDateIndexDescItemStruct: | ||
date: str | ||
index: Any | ||
desc: str | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyLifeIndexItemStruct: | ||
ultraviolet: List[cyWeatherAPIResponseDailyDateIndexDescItemStruct] | ||
carWashing: List[cyWeatherAPIResponseDailyDateIndexDescItemStruct] | ||
dressing: List[cyWeatherAPIResponseDailyDateIndexDescItemStruct] | ||
comfort: List[cyWeatherAPIResponseDailyDateIndexDescItemStruct] | ||
coldRisk: List[cyWeatherAPIResponseDailyDateIndexDescItemStruct] | ||
|
||
|
||
@dataclass | ||
class cyWeatherAPIResponseDailyStruct: | ||
status: str | ||
astro: List[cyWeatherAPIResponseDailyAstroItemStruct] | ||
precipitation: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
temperature: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
humidity: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
cloudrate: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
pressure: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
visibility: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
dswrf: List[cyWeatherAPIResponseDailyMaxMinAvgItemStruct] | ||
wind: List[cyWeatherAPIResponseDailyWindItemStruct] | ||
skycon: List[cyWeatherAPIResponseDailySkyconItemStruct] | ||
skycon_08h_20h: List[cyWeatherAPIResponseDailySkyconItemStruct] | ||
skycon_20h_32h: List[cyWeatherAPIResponseDailySkyconItemStruct] | ||
life_index: cyWeatherAPIResponseDailyLifeIndexItemStruct |
Oops, something went wrong.