<jupyter_start><jupyter_text>AerisWeather Python SDK----   The AerisWeather Python SDK is a coding toolkit created to streamline integrating data from the [AerisWeather API](https://www.aerisweather.com/support/docs/api/) into Python applications.  In other words, the goal of the SDK is to make it easier to get weather data into your Python app. While working towards that goal, we also tried to make the SDK as "Pythonic" as possible, embracing some of our favorite core tenets of the [Zen of Python (PEP20)](https://www.python.org/dev/peps/pep-0020/id3):- Simple is better than complex.- Readability counts.- If the implementation is hard to explain, it's a bad idea.  Thanks for checking out the AerisWeather Python SDK. We hope you find it useful, and enjoy using it as much as we enjoyed creating it!  *The AerisWeather Python Team*  ---- DownloadsThe full SDK containing the library, docs and demos is available for download from the project's GitHub page, or from the [Toolkits section](https://www.aerisweather.com/support/docs/toolkits/) of the AerisWeather site.  If you want just the library, that's available all by itself on [PyPi](https://pypi.org/). Most likely though, you'll just want to install it via pip (see [Installation] in the Getting Started section below).   Requirements- Python v3.6 or higher.- An active AerisWeather API subscription.*Don't have an active AerisWeather API client account?   Accessing the API data requires an active AerisWeather API subscription, and registration of your application or namespace. You can sign up for a free developer account at the [AerisWeather Sign Up page](https://www.aerisweather.com/signup/) to get your client ID and secret.* LicenseThis SDK is provided free of charge for AerisWeather's customers, and is available for use under the [MIT open source](https://opensource.org/licenses/MIT) license. SDK Contents- Core Library/Package  The AerisWeather Python SDK includes a core Python library that provides simplified fetching and parsing of data from the API, written in Python v3.6.  - Unit Tests  The SDK incudes a full set of unit tests, which are a great source of sample code for accessing the API endpoints. Check them out on [GitHub](https://github.com/aerisweather/python_sdk/tree/master/tests).- Docs  A full set of HTML code documentation is provided, outlining class and method structure. More documentation can be found at [aerisweather.com](https://www.aerisweather.com/docs/python/Aeris/index.html), and on the project's [GitHub page](https://github.com/aerisweather/python_sdk/tree/master/docs).  - Demo Project File  AerisWeatherPythonDemo is a Python file containing examples demonstrating a few ways to use the aerisweather package to get data from the AerisWeather API. You can find it on [GitHub](https://github.com/aerisweather/python_sdk/tree/master/AerisWeatherPythonDemo).- Jupyter Notebook  Also in the AerisWeatherPythonDemo folder is a Jupyter Notebook. This is another source of example code. The notebook allows you to experiment with various methods of accessing the API without having to run the demo project. Here it is on [GitHub](https://github.com/aerisweather/python_sdk/tree/master/AerisWeatherPythonDemo).- Readme  - License file  - setup.py    Getting Started  ---- Download the SDK  If you haven't already downloaded the SDK, see the [Downloads](Downloads) section above and get the SDK and all it's contents.   Python Demo File or Jupyter NotebookUnder the main SDK folder, you'll see the AerisWeatherPythonDemo folder. This folder contains:- aeris_demo.py- aeris_demo_notebook.ipynb  The first, aeris_demo.py is a simple Python file. The other is a Jupyter notebook. Both of these resources contain the same sample code, so you can choose to use either or both to learn more about how the aerisweather library works.  Be aware that whichever path you choose to follow, you will need to [install the aerisweather package](Install-the-aerisweather-Package) to the Python environment that supports that resource. We used PyCharm during the creation of the SDK, and used it to run the aeris_demo.py project. For the notebook, we configured the kernel to point to PyCharm's Python venv.   Install the aerisweather PackageNext, let's get the aerisweather package installed to your Python environment.  To use pip for the installation:    pip install -U --index-url https://pypi.org/simple/ aerisweather==..   Using PyCharm:  If you're using PyCharm as your IDE, go to Settings | Project Interpreter and click the green "+" in the upper right. Search for aerisweather and choose to install the package to your Python environment.  *Note: If you're also initializing the Jupyter Kernel from PyCharm, this step will cover that too.*  The Basics----In this section we'll talk about how to get set up to do basic data requests from the API, using the AerisWeather Python library. The topics covered on this page are:  - [The AerisWeather Class](The-AerisWeather-Class)- [Set The AerisWeather API Credentials](Set-The-AerisWeather-API-Credentials)- [Endpoints](Endpoints)- [Locations](Locations)- [Optimizing API Requests](Optimizing-API-Requests)- [API Responses](API-Responses)- [Examples](Examples)  The AerisWeather ClassThe first import line we see below is for our main class, aerisweather. Once we have successfully created an aerisweather object, we'll do most of the heavy lifting with it.      from aerisweather.aerisweather import AerisWeather    from aerisweather.requests.ParameterType import ParameterType    from aerisweather.requests.RequestLocation import RequestLocation    from aerisweather.requests.RequestAction import RequestAction    from aerisweather.requests.RequestFilter import RequestFilter    from keys import client_id, client_secret, app_id The last import is also important to note, and if you've completed setting up your AerisWeather API credentials, it should look familiar. We need this so we can pass our API credentials along with the requests for data.  In the code snippet below we're creating the aerisweather object that we'll use to make requests to the API. Once the aerisweather object is created, we can continue to use it throughout the rest of the code.    aeris = AerisWeather(client_id=client_id, client_secret=client_secret, app_id=app_id)       Set The AerisWeather API CredentialsNotice that in both the aeris_demo.py file and the aeris_demo_notebook Jupyter notebook file, the import section has a reference to a "keys" module.      from keys import client_id, client_secret, app_id  This import statement also references "client_id" and "client_secret". These are the AerisWeather API credentials mentioned previously in the [Requirements](Requirements) section.  The final reference in the import statement is the application id, which should be the namespace or domain of the application from which you will be accessing the API. For details on application id and namespace restrictions, see [Namespace Access Restrictions](https://www.aerisweather.com/support/docs/api/getting-started/authentication/).*If you don't have an active AerisWeather API account, check out the [Requirements](Requirements) section and get signed up before continuing.* In the keys.py file(s), replace the placeholders with your AerisWeather API client id and secret, and the appropriate namespace or domain for your Python application.          app_id = "com.aerisweather.pythonsdkdemo"    client_id = ""    client_secret = ""   Almost There...  Ok, now that we have our aerisweather object and have our credentials configured, we only need two more things (at a minimum) for every request to the database:- an endpoint (what)- a location (where)  EndpointsThe Aeris API supports data requests for many kinds of weather related data, each defined by an endpoint. Endpoints refer to the types of data to request, such as a place, observation, forecast or advisory, and will be the basis for any request made to the weather API. You can check out the full list of Aeris API endpoints on the [AerisWeather endpoints page](https://www.aerisweather.com/support/docs/api/reference/endpoints). The Python SDK has implemented type hints to allow code completion in IDEs that support it. Code completion is available for all fully implemented endpoints. For new or custom endpoints that are not yet specifically implemented in the SDK, check out the section on [Custom Endpoint requests](Custom-Endpoints).For the examples that follow, we are using two of the most popular endpoints of the Aeris API, Observations and Forecasts. LocationsOther than the type of data (endpoint) itself, the next most important piece of info we need is the location, or place, to which that data pertains. You will find that in the API, location and place are sometimes used interchangeably.* For the purpose of the AerisWeather Python SDK, we will use the term "location". *Even in the requests for the "Places" endpoint - but we'll get to that later...  To be able to send a valid location to the API, we need either:- latitude **and** longitudeor- city **and** stateor - zip/postal code  In the AerisWeather Python library, there a couple of ways to we can pass that location info to the API within a request.  - The RequestLocation class- The "p=" Parameter   The RequestLocationThe RequestLocation class is designed to be flexible enough to handle whatever location information you have, and give the most accurate location info to the API.  The Python lib will take the most accurate source of data and pass that to the API. For instance, let's say you create a RequestLocation object, and set the latitude, longitude, city and state like this:    loc = RequestLocation(city="minneapolis", state="mn", latitude="", longitude="")    Then we can use the RequestLocation object in an endpoint request, like this:    obs_list = aeris.observations(location=loc)When the lib builds the request for the API, it will use the lat and lon, since that's the most accurate of the info given.If you'd like to dig into the details of the RequestLocation class, you can check out the [code docs](https://www.aerisweather.com/docs/python/Aeris/classaerisweather_1_1requests_1_1_request_location_1_1_request_location.html). The "p" ParameterIn some cases, you may want to use request Actions like "closest" to query the API for data that is closest to the requested place or location.If we use the p parameter, it takes the place of RequestLocation we discussed earlier. For example, we can create a request like this:    obs_list = aeris.observations(action=RequestAction.OBSERVATIONS.CLOSEST, params={ParameterType.OBSERVATIONS.P: "minnneapolis,mn") API ResponsesEach request to the API, if successful, returns a list of endpoint appropriate response objects. Even if there is only one response result, it will be returned in a list.For example, the Observations request below returns a single response result. The response is returned as a list with one [ObservationsResponse](https://www.aerisweather.com/docs/python/Aeris/classaerisweather_1_1responses_1_1_observations_response_1_1_observations_response.html) object in it.    obs_list = aeris.observations(location=loc)        for obs in obs_list:         get the AerisPlace responses object        place = obs.place  Examples----In the following examples, we'll start with the basics of using the AerisWeather Python library. If you know what you're looking for, here's the list of basic examples:  - [Simple API Request, No Options](Simple-API-Request,-No-Options)- [API Request with Action, Filter and Parameters](API-Request-with-Action,-Filter-and-Parameters)- [Forecasts Example with Fields](Forecasts-Example)<jupyter_code>from aerisweather.aerisweather import AerisWeather
from aerisweather.requests.ParameterType import ParameterType
from aerisweather.requests.RequestLocation import RequestLocation
from aerisweather.requests.RequestAction import RequestAction
from aerisweather.requests.RequestFilter import RequestFilter
from keys import client_id, client_secret, app_id<jupyter_output><empty_output><jupyter_text>Simple API Request, No Options<jupyter_code># instantiate our aerisweather object
aeris = AerisWeather(client_id=client_id, client_secret=client_secret, app_id=app_id)

# create a RequestLocation object to be used with any endpoint requests
loc = RequestLocation(city="minneapolis", state="mn")

# create a simple observations request with no options
obs_list = aeris.observations(location=loc)

for obs in obs_list:
    # get the AerisPlace responses object
    place = obs.place

    # get the observations data object
    ob = obs.ob

    # get some observations data
    tempF = ob.tempF
    weather = ob.weather

    print()
    print("Observations Example 1:")
    print("The current weather for " + place.name + ", " + place.state + ":")
    print("Conditions are currently " + weather + " with a temp of " + str(tempF) + "°F")<jupyter_output>Observations Example 1:
The current weather for minneapolis, mn:
Conditions are currently Sunny with a temp of 11°F<jupyter_text>Forecasts ExampleIn the following example, we'll see how to pull some data from the [Forecasts endpoint](https://www.aerisweather.com/support/docs/api/reference/endpoints/forecasts/).Notice that in this example we are again using a [RequestLocation](The-RequestLocation) object, this time with a postal code.<jupyter_code># Let's create a new RequestLocation, this time using a postal code
loc = RequestLocation(postal_code="55124")


# we'll limit the fields returned by the API too, to just the ones we need for our example
forecast_list = aeris.forecasts(location=loc,
                                params={ParameterType.FORECASTS.FIELDS:
                                        "periods.isDay, periods.maxTempF, periods.minTempF, periods.weather"})

for forecast in forecast_list:
    
    # check to see if this is a day or night forecast
    if forecast.periods[0].isDay:
        day = forecast.periods[0]
        night = forecast.periods[1]
    else:
        day = forecast.periods[1]
        night = forecast.periods[0]

    print()
    print("Forecast Example:")
    print("Today expect " + day.weather + " with a high temp of " + str(day.maxTempF) + "°")
    print("Tonight will be " + night.weather + " with a low temp of " + str(night.minTempF) + "°")<jupyter_output>Forecast Example:
Today expect Partly Cloudy with a high temp of 17°
Tonight will be Partly Cloudy with a low temp of 10°<jupyter_text>Advanced TopicsNow that we've seen how to set up standard requests and work with the API response, let's take a look at a few of the more advanced bits of the Aeris Python library. In this section we'll talk about:- [Optimizing API Requests](Optimizing-API-Requests)- [Optimizing API Requests Example](Optimizing-API-Requests-Example)    - [Custom Endpoints](Custom-Endpoints)- [Custom Endpoints Example](Custom-Endpoints-Example)  - [Batch Requests](Batch-Requests)- [Batch Request Example](Batch-Request-Example)  Optimizing API RequestsThe Aeris API offers several request properties designed to improve response time and reduce the amount of data returned. Each Aeris API endpoint has a unique set of request properties for its specific data. To see the various request properties for a specific endpoint, check out the [corresponding endpoint docs](https://www.aerisweather.com/support/docs/api/reference/endpoints).In the Aeris Python library, we have mapped each of these properties to their own class, and reduced the available options to only those that apply to each endpoint.The endpoint request properties are broken down into the following categories:- Request Action- Request Filter- Request Parameter- Request Field- Request Query- Request SortWhen using the Aeris Python SDK, you can add these properties to your requests through the optional parameters passed to the request methods. For example:    obs_list = aeris.observations(action=RequestAction.OBSERVATIONS.CLOSEST,                                   filter_=[RequestFilter.OBSERVATIONS.ALL_STATIONS],                                  params={ParameterType.OBSERVATIONS.P: "minneapolis,mn",                                          ParameterType.OBSERVATIONS.FIELDS: "place, ob.tempF,ob.weather"})The above Observations request is using an Action property of "Closest", a Filter property of "All Stations" and two Parameter options "p" and "Fields". This will provide a very specific set of Observations data for the location.  You can check out the [code examples](Examples) provided to see different ways of using these properties in your requests.    Optimizing API Requests Example<jupyter_code># Make the API request and get a list of ObservationResponse objects from the response
# (Note: we don't need a RequestLocation object, since we're using Closest with the "p" parameter.)
obs_list = aeris.observations(action=RequestAction.OBSERVATIONS.CLOSEST,
                              filter_=[RequestFilter.OBSERVATIONS.ALL_STATIONS],
                              params={ParameterType.OBSERVATIONS.P: "minneapolis,mn",
                                      ParameterType.OBSERVATIONS.FIELDS: "place, ob.tempF,ob.weather"})
for obs in obs_list:
    place = obs.place
    ob = obs.ob
    tempF = ob.tempF
    weather = ob.weather

    print()
    print("Observations Example 2:")
    print("The current weather for " + place.name + ", " + place.state + ":")
    print("Conditions are currently " + weather + " with a temp of " + str(tempF) + "°F")<jupyter_output>Observations Example 2:
The current weather for minneapolis, mn:
Conditions are currently Sunny with a temp of 12°F<jupyter_text>Custom EndpointsAs the Aeris API continues to grow, new endpoints are added and new properties are added to existing ones. In some cases, the Aeris API may be updated ahead of the Python SDK may not yet support a new API endpoint. Or an endpoint may have been updated with new options. For cases like these, the Aeris Python library provides a special endpoint type named CUSTOM, and the CustomResponse class. In this section we'll show some examples of using these to make custom requests to the API.First we'll request data for a valid endpoint, but one that isn't yet supported by the SDK. We do custom requests a little different, in that we need to define the EndpointType and set up an Endpoint object. We then pass that Endpoint object to the aerisweather.request() method and let it generate the request based on the endpoint object's properties.    EndpointType.custom = "stormreports"        endpoint = Endpoint(EndpointType.CUSTOM, location=RequestLocation(postal_code="54660"))As you see above, we can use the endpoint object to handle all of the optional properties as well. Once we have all of our request properties added to the Endpoint object, we can pass that to the request() method.     response_list = awx.request(endpt)    The request method always returns a list of response objects. The type of response objects returns depends on the endpoint specified in the request, so we always get back a response appropriate to the endpoint we're getting data from. In this case, we will get a list of CustomResponse objects, since we requested data from an unknown (custom) endpoint type.In the example below, we're requesting data from the Aeris API [StormReports endpoint](https://www.aerisweather.com/support/docs/api/reference/endpoints/stormreports/). We know this to be a valid endpoint, it's just not fully implemented in the SDK at the time of this writing.*NOTE: If there are no storm reports for the location, try changing the postal code to a location that has had some storm activity recently.*  Custom Endpoints Example<jupyter_code># we need a couple of additional imports
from aerisweather.responses.CustomResponse import CustomResponse
from aerisweather.requests.Endpoint import Endpoint, EndpointType

# let's get data from a valid endpoint, but one that's not in our Endpoint Enum. A use case for this might be
# where we want to test a beta or pre-release endpoint
aeriswx = AerisWeather(app_id=app_id,
                       client_id=client_id,
                       client_secret=client_secret)

# define the custom endpoint type
EndpointType.custom = "stormreports"

# create an endpoint object, specifying CUSTOM as it's type, and let's go ahead and give the endpoint our location too
endpoint = Endpoint(EndpointType.CUSTOM, location=RequestLocation(postal_code="54660"))

# this will be a list of CustomResponse objects
response_list = aeriswx.request(endpoint)
response = response_list[0]

# the response should have storm report data, so let's try to pull the report type
print("The storm report type is: " + response.report.type)<jupyter_output>The storm report type is: snow<jupyter_text>Notice in the above example for storm reports, that although we don't have a specific response class type (like ForecastsResponse), dot notation works for any valid data within our CustomResponse object.  Let's do another example, this time using an endpoint that is currently implemented in the SDK. This is useful in cases where the endpoint has been added, but maybe a new field has been added since the last SDK update. Or, maybe we just forgot to add support a field or parameter... it happens... :-)In this example we'll request data from the Forecasts endpoint, using the Custom endpoint type and CustomResponse object.<jupyter_code># You can also use the custom endpoint type to request data from a known valid endpoint, for cases
# where new API data fields have not yet been added to an endpoint's response class.

EndpointType.custom = "forecasts"

forecasts_list = aeriswx.request(endpoint=Endpoint(endpoint_type=EndpointType.CUSTOM,
                             location=RequestLocation(postal_code="55344")))

forecast = forecasts_list[0]
period = forecast.periods[0]  # type: ForecastPeriod
print ("Expect the weather to be " + period.weather)<jupyter_output>Expect the weather to be Partly Cloudy<jupyter_text>Batch RequestsOne of the most common Aeris API requirements is to obtain multiple pieces of information for a location. We can do this with multiple API requests for each data type, but a batch request is a little nicer because we can query multiple endpoints with a single request. For more details on the API side of a batch request, check out the [Batch Request page](https://www.aerisweather.com/support/docs/api/getting-started/batch/). The RequestBatch requests in the Aeris Python SDK are somewhat similar to the custom endpoint request, which we learned about in the last section. To make a batch request, we define an Endpoint object for each request we want to make, then send a list containing all of the endpoint objects to the aerisweather.batch_request() method. The ResponseJust like all other requests, the batch_request method returns a list of responses. In this case however, the list will contain the responses from each endpoint, in the order that they were requested.*Note: Because some endpoints can return more than one response per request, we need to check each response type in the list to determine how to handle it. Check the example below to see how we handled it.* Global PropertiesAlong with the list of endpoints, we can also pass global properties to the batch_request() method. These properties will then be applied to each of the included endpoints, unless they override that particular property. For example, if we set up two endpoints like this:    endpoint1 = Endpoint(endpoint_type=EndpointType.OBSERVATIONS,                          location=RequestLocation(postal_code="54660"))                        endpoint2 = Endpoint(endpoint_type=EndpointType.FORECASTS)add these to a list    endpoint_list = [endpoint1, endpoint2]and then pass these to the batch_request method    response_list = awx.batch_request(endpoints=endpoints,                                      global_location=RequestLocation(postal_code="55124"))    the result will be that endpoint1 will request observations data for 54660, while endpoint2 will request forecast for 55124.Ok, let's take a look at a live example:  Batch Requests Example<jupyter_code># add some imports
from aerisweather.responses.ForecastsResponse import ForecastsResponse
from aerisweather.responses.ObservationsResponse import ObservationsResponse
from aerisweather.responses.ObservationsSummaryResponse import ObservationsSummaryResponse
# get our aerisweather instance 
awx = AerisWeather(app_id=app_id,
                   client_id=client_id,
                   client_secret=client_secret)

# define the first endpoint for our batch request
endpoint = Endpoint(endpoint_type=EndpointType.OBSERVATIONS,
                    action=RequestAction.OBSERVATIONS.CLOSEST,
                    params={ParameterType.OBSERVATIONS.P: "54601"})

# define the second endpoint of our batch request
endpoint2 = Endpoint(endpoint_type=EndpointType.FORECASTS,
                     params={ParameterType.FORECASTS.LIMIT: "1"})

# and then the third
endpoint3 = Endpoint(endpoint_type=EndpointType.OBSERVATIONS_SUMMARY)

# add all three endpoints to a list
endpoints = [endpoint, endpoint2, endpoint3]

# pass the list of endpoints to the batch_request method, along with some global properties that will apply to all of 
# the endpoints (unless they override it)
response_list = awx.batch_request(endpoints=endpoints,
                                  global_location=RequestLocation(postal_code="54660"))


for resp in response_list:  
    if type(resp) is ObservationsResponse:
        # Observations
        print ("Observations Data")
        obs = resp
        loc = obs.loc
        print("loc.latitude = " + str(loc.lat))
        place = obs.place
        print("place.state = " + place.state)
        print("")
    elif type(resp) is ForecastsResponse:
        # Forecasts
        print("Forecasts Data")
        forecast = resp
        print("forecast.loc.lat = " + str(forecast.loc.lat))
        print("forecast.interval = " + forecast.interval)
        period = forecast.periods[0]
        print("period.weather = " + period.weather)
        print("")
    elif type(resp) is ObservationsSummaryResponse:
        # ObservationsSummary
        print("Observations Summary Data")
        obs_sum = resp
        place = obs_sum.place
        print("place.state = " + place.state)
        periods = obs_sum.periods
        temp = periods[0].temp
        print("temp.avgF = " + str(temp.avgF))
        print("")<jupyter_output>Observations Data
loc.latitude = 43.883333333333
place.state = wi

Forecasts Data
forecast.loc.lat = 43.979
forecast.interval = day
period.weather = Mostly Cloudy

Observations Summary Data
place.state = wi
temp.avgF = 59.2
{'fraction_non_alphanumeric': 0.0631078856800838, 'fraction_numerical': 0.004788268741583121, 'mean_word_length': 4.248969173375221, 'pattern_counts': {'":': 2, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 20, 'lorem ipsum': 0, 'www.': 13, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '21250', 'license': 'MIT', 'n_tokens_mistral': 7125, 'n_tokens_neox': 6702, 'n_words': 3202, 'path': 'AerisWeatherPythonDemo/aeris_demo_notebook.ipynb', 'repo_name': 'jkoelndorfer/aerisweather-python-sdk', 'size': '26732'}