import json
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

def requests_retry_session(
    retries=3,
    backoff_factor=0.3,
    status_forcelist=(500, 502, 504),
    session=None,
):
    session = session or requests.Session()
    retry = Retry(
        total=retries,
        read=retries,
        connect=retries,
        backoff_factor=backoff_factor,
        status_forcelist=status_forcelist,
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    return session

"""
Explanation: Model My Watershed (MMW) API Demo: Analyze land properties
Emilio Mayorga, University of Washington, Seattle. 2018-5-17 (minor updates to documentation on 2018-8-19). Demo put together using as a starting point instructions from Azavea from October 2017. See also the related, previous notebook, https://github.com/WikiWatershed/model-my-watershed/blob/develop/doc/MMW_API_watershed_demo.ipynb
Introduction
The Model My Watershed API allows you to delineate watersheds and analyze geo-data for watersheds and arbitrary areas. You can read more about the work at WikiWatershed or use the web app.
MMW users can discover their API keys through the user interface, and test the MMW geoprocessing API on either the live or staging apps. An Account page with the API key is available from either app (live or staging). To see it, go to the app, log in, and click on "Account" in the dropdown that appears when you click on your username in the top right. Your key is different between staging and production. For testing with the live (production) API and key, go to https://modelmywatershed.org/api/docs/
The API can be tested from the command line using curl. This example uses the production API to test the watershed endpoint:
bash
curl -H "Content-Type: application/json" -H "Authorization: Token YOUR_API_KEY" -X POST 
  -d '{ "location": [39.67185,-75.76743] }' https://modelmywatershed.org/api/watershed/
MMW API: Obtain land properties based on "analyze" geoprocessing on AOI (small box around a point)
1. Set up
End of explanation
"""


api_url = "https://modelmywatershed.org/api/"

"""
Explanation: MMW production API endpoint base url.
End of explanation
"""


def get_job_result(api_url, s, jobrequest):
    url_tmplt = api_url + "jobs/{job}/"
    get_url = url_tmplt.format
    
    result = ''
    while not result:
        get_req = requests_retry_session(session=s).get(get_url(job=jobrequest['job']))
        result = json.loads(get_req.content)['result']
    
    return result

s = requests.Session()

APIToken = '<YOUR API TOKEN STRING>'  # ENTER YOUR OWN API TOKEN 

s.headers.update({
    'Authorization': APIToken,
    'Content-Type': 'application/json'
})

"""
Explanation: The job is not completed instantly and the results are not returned directly by the API request that initiated the job. The user must first issue an API request to confirm that the job is complete, then fetch the results. The demo presented here performs automated retries (checks) until the server confirms the job is completed, then requests the JSON results and converts (deserializes) them into a Python dictionary.
End of explanation
"""


from shapely.geometry import box, MultiPolygon

width = 0.0004  # Looks like using a width smaller than 0.0002 causes a problem with the API?

# GOOS: (-88.5552, 40.4374) elev 240.93. Agriculture Site—Goose Creek (Corn field) Site (GOOS) at IML CZO
# SJER: (-119.7314, 37.1088) elev 403.86. San Joaquin Experimental Reserve Site (SJER) at South Sierra CZO
lon, lat = -119.7314, 37.1088

bbox = box(lon-0.5*width, lat-0.5*width, lon+0.5*width, lat+0.5*width)

payload = MultiPolygon([bbox]).__geo_interface__

json_payload = json.dumps(payload)

payload

"""
Explanation: 2. Construct AOI GeoJSON for job request
Parameters passed to the "analyze" API requests.
End of explanation
"""


# convenience function, to simplify the request calls, below
def analyze_api_request(api_name, s, api_url, json_payload):
    post_url = "{}analyze/{}/".format(api_url, api_name)
    post_req = requests_retry_session(session=s).post(post_url, data=json_payload)
    jobrequest_json = json.loads(post_req.content)
    # Fetch and examine job result
    result = get_job_result(api_url, s, jobrequest_json)
    return result

"""
Explanation: 3. Issue job requests, fetch job results when done, then examine results. Repeat for each request type
End of explanation
"""


result = analyze_api_request('land/2011_2011', s, api_url, json_payload)

"""
Explanation: Issue job request: analyze/land/2011_2011/
End of explanation
"""


type(result), result.keys()

"""
Explanation: Everything below is just exploration of the results. Examine the content of the results (as JSON, and Python dictionaries)
End of explanation
"""


result['survey'].keys()

categories = result['survey']['categories']

len(categories), categories[1]

land_categories_nonzero = [d for d in categories if d['coverage'] > 0]

land_categories_nonzero

"""
Explanation: result is a dictionary with one item, survey. This item in turn is a dictionary with 3 items: displayName, name, categories. The first two are just labels. The data are in the categories item.
End of explanation
"""


result = analyze_api_request('terrain', s, api_url, json_payload)

"""
Explanation: Issue job request: analyze/terrain/
End of explanation
"""


categories = result['survey']['categories']

len(categories), categories

[d for d in categories if d['type'] == 'average']

"""
Explanation: result is a dictionary with one item, survey. This item in turn is a dictionary with 3 items: displayName, name, categories. The first two are just labels. The data are in the categories item.
End of explanation
"""


result = analyze_api_request('climate', s, api_url, json_payload)

"""
Explanation: Issue job request: analyze/climate/
End of explanation
"""


categories = result['survey']['categories']

len(categories), categories[:2]

ppt = [d['ppt'] for d in categories]
tmean = [d['tmean'] for d in categories]

# ppt is in cm, right?
sum(ppt)

import calendar
import numpy as np

calendar.mdays

# Annual tmean needs to be weighted by the number of days per month
sum(np.asarray(tmean) * np.asarray(calendar.mdays[1:]))/365

"""
Explanation: result is a dictionary with one item, survey. This item in turn is a dictionary with 3 items: displayName, name, categories. The first two are just labels. The data are in the categories item.
End of explanation
"""


