Skip to content

Commit b1c2930

Browse files
authored
Merge pull request #27 from Solcast/feature/add_pv_power_sites
Add support for PV power site endpoints
2 parents 01cdd91 + 469a6ab commit b1c2930

File tree

13 files changed

+280
-40
lines changed

13 files changed

+280
-40
lines changed

.github/workflows/black.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ jobs:
99
- uses: actions/checkout@v3
1010
- uses: psf/black@stable
1111
with:
12-
options: "--check --verbose -C"
12+
options: "--check --verbose"
1313
src: "./solcast"
1414
jupyter: false

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ public
22
.idea
33
*.ipynb_checkpoints
44
.pytest_cache
5-
tests/__pycache__
5+
tests/__pycache__
6+
*.pyc

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ Don't forget to set your [account Api Key](https://toolkit.solcast.com.au/regist
4545
---
4646

4747
## Contributing
48-
Tests are run against the Solcast API, you will need a key to run them.
49-
They are executed on `unmetered locations` and as such won't consume your credits.
48+
Tests are run against the Solcast API, you will need an API key to run them.
49+
They are executed on `unmetered locations` and as such won't consume your requests.
5050

5151
```commandline
5252
pytest tests

solcast/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.1.1"
1+
__version__ = "1.2.0"
22

33
from . import api, forecast, historic, live, tmy, unmetered_locations, urls
44

solcast/api.py

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy
12
import json
23
import os
34
from dataclasses import dataclass
@@ -24,10 +25,11 @@ class Response:
2425
url: str
2526
data: Optional[bytes]
2627
success: bool
28+
method: str
2729
exception: Optional[str] = None
2830

2931
def __repr__(self):
30-
return f"status code={self.code}, url={self.url}"
32+
return f"status code={self.code}, url={self.url}, method={self.method}"
3133

3234
def to_dict(self):
3335
if self.code != 200:
@@ -78,22 +80,26 @@ def __init__(self, base_url: str, endpoint: str):
7880
self.url = self.make_url()
7981

8082
@staticmethod
81-
def check_params(params: dict) -> (dict, str):
82-
"""runs some basic checks on the parameters that will be passed in the
83-
GET request."""
83+
def _check_params(params: dict) -> (dict, str):
84+
"""Run basic checks on the parameters that will be passed to the HTTP request."""
8485
assert isinstance(params, dict), "parameters needs to be a dict"
86+
params = copy.deepcopy(params)
8587

86-
if "api_key" not in params:
87-
params.update({"api_key": os.getenv("SOLCAST_API_KEY")})
88+
if "api_key" in params:
89+
# api key in the header for secrecy
90+
key = params["api_key"]
91+
del params["api_key"]
92+
else:
93+
key = os.getenv("SOLCAST_API_KEY")
8894

89-
if params["api_key"] is None:
95+
if key is None:
9096
raise ValueError(
9197
"no API key provided. Either set it as an environment "
9298
"variable SOLCAST_API_KEY, or provide `api_key` "
9399
"as an argument. Visit https://solcast.com to get an API key."
94100
)
95101

96-
if len(params["api_key"]) <= 1:
102+
if len(key) <= 1:
97103
raise ValueError("API key is too short.")
98104

99105
if "output_parameters" in params.keys() and isinstance(
@@ -109,41 +115,105 @@ def check_params(params: dict) -> (dict, str):
109115

110116
# only json supported
111117
if "format" in params.keys():
112-
assert (
113-
params["format"] == "json"
114-
), "only json response format is currently supported."
115-
116-
# api key in the header for secrecy
117-
key = params["api_key"]
118-
del params["api_key"]
118+
if params["format"] != "json":
119+
raise NotImplementedError(
120+
"Only json response format is currently supported."
121+
)
119122

120123
return params, key
121124

122125
def make_url(self) -> str:
123-
"""composes the full URL."""
126+
"""Compose the full URL."""
124127
return "/".join([self.base_url, self.endpoint])
125128

126129
def get(self, params: dict) -> Response:
127-
"""makes the GET request.
130+
"""Wrap _make_request to make a GET request
131+
132+
Args:
133+
params: a dictionary of parameters that are passed in the GET request
134+
135+
Returns:
136+
a Response object.
137+
138+
"""
139+
return self._make_request(params, method="GET")
140+
141+
def post(self, params: dict) -> Response:
142+
"""Wrap _make_request to make a POST request
143+
144+
Args:
145+
params: a dictionary of parameters that are passed in the POST request
146+
147+
Returns:
148+
a Response object.
149+
150+
"""
151+
return self._make_request(params, method="POST")
152+
153+
def patch(self, params: dict) -> Response:
154+
"""Wrap _make_request to make a PATCH request
155+
156+
Args:
157+
params: a dictionary of parameters that are passed in the PATCH request
158+
159+
Returns:
160+
a Response object.
161+
162+
"""
163+
return self._make_request(params, method="PATCH")
164+
165+
def put(self, params: dict) -> Response:
166+
"""Wrap _make_request to make a PUT request
167+
168+
Args:
169+
params: a dictionary of parameters that are passed in the PUT request
170+
171+
Returns:
172+
a Response object.
173+
174+
"""
175+
return self._make_request(params, method="PUT")
176+
177+
def delete(self, params: dict) -> Response:
178+
"""Wrap _make_request to make a DEL request
179+
180+
Args:
181+
params: a dictionary of parameters that are passed in the DEL request
182+
183+
Returns:
184+
a Response object.
185+
186+
"""
187+
return self._make_request(params, method="DELETE")
188+
189+
def _make_request(self, params: dict, method: str) -> Response:
190+
"""Make a request using urllib with the HTTP method specified
128191
129192
Args:
130-
params: a dictionary of parameters that are passed in the get request
193+
params: a dictionary of parameters that are passed in the request
194+
method: HTTP method to use
131195
132196
Returns:
133197
a Response object.
134198
"""
135199

136-
params, key = self.check_params(params)
200+
params, key = self._check_params(params)
137201
url = self.url + "?" + urllib.parse.urlencode(params)
138202
req = Request(
139203
url,
140204
headers={"Authorization": f"Bearer {key}", "User-Agent": self.user_agent},
205+
method=method,
141206
)
142207
try:
143208
with urlopen(req) as response:
144209
body = response.read()
145210
return Response(
146-
code=response.code, url=url, data=body, success=True, exception=None
211+
code=response.code,
212+
url=url,
213+
data=body,
214+
success=True,
215+
exception=None,
216+
method=method,
147217
)
148218
except urllib.error.HTTPError as e:
149219
try:
@@ -156,4 +226,5 @@ def get(self, params: dict) -> Response:
156226
data=None,
157227
exception=exception_message,
158228
success=False,
229+
method=method,
159230
)

solcast/forecast.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def radiation_and_weather(
2222
latitude: in decimal degrees, between -90 and 90, north is positive
2323
longitude: in decimal degrees, between -180 and 180, east is positive
2424
output_parameters: list of strings with the parameters to return
25+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
2526
2627
See https://docs.solcast.com.au/ for full list of parameters.
2728
"""
@@ -51,6 +52,7 @@ def rooftop_pv_power(
5152
latitude: in decimal degrees, between -90 and 90, north is positive
5253
longitude: in decimal degrees, between -180 and 180, east is positive
5354
output_parameters: list of strings with the parameters to return
55+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
5456
5557
See https://docs.solcast.com.au/ for full list of parameters.
5658
"""
@@ -76,6 +78,7 @@ def advanced_pv_power(resource_id: int, **kwargs) -> Response:
7678
7779
Args:
7880
resource_id: a Solcast resource id
81+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
7982
8083
See https://docs.solcast.com.au/ for full list of parameters.
8184
"""

solcast/historic.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def radiation_and_weather(
2323
end: optional, datetime-like, last day of the requested period
2424
duration: optional, ISO_8601 compliant duration for the historic data.
2525
Must be within 31 days of the start_date.
26+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
2627
2728
See https://docs.solcast.com.au/ for full list of parameters.
2829
"""
@@ -65,6 +66,7 @@ def rooftop_pv_power(
6566
end: optional, datetime-like, last day of the requested period
6667
duration: optional, ISO_8601 compliant duration for the historic data.
6768
Must be within 31 days of the start_date.
69+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
6870
6971
See https://docs.solcast.com.au/ for full list of parameters.
7072
"""

solcast/live.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def radiation_and_weather(
2020
latitude: in decimal degrees, between -90 and 90, north is positive
2121
longitude: in decimal degrees, between -180 and 180, east is positive
2222
output_parameters: list of strings with the parameters to return
23+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
2324
2425
See https://docs.solcast.com.au/ for full list of parameters.
2526
"""
@@ -47,6 +48,7 @@ def rooftop_pv_power(latitude: float, longitude: float, **kwargs) -> Response:
4748
Args:
4849
latitude: in decimal degrees, between -90 and 90, north is positive
4950
longitude: in decimal degrees, between -180 and 180, east is positive
51+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
5052
5153
See https://docs.solcast.com.au/ for full list of parameters.
5254
"""
@@ -66,6 +68,7 @@ def advanced_pv_power(resource_id: int, **kwargs) -> Response:
6668
6769
Args:
6870
resource_id: a Solcast resource id
71+
**kwargs: additional keyword arguments to be passed through as URL parameters to the Solcast API
6972
7073
See https://docs.solcast.com.au/ for full list of parameters.
7174
"""

0 commit comments

Comments
 (0)