Examples
========

First, get an instance of the ``ThreediApi``:

.. code:: python

   from threedi_api_client import ThreediApi
   env_file = "<path>/.env"
   api = ThreediApi(env_file)

Now you can easily make use of the api models generated by the openapi
client. Let us create a simulation. We will use a ``Simulation`` model
instance to pass data to the API. Some fields are optional but we do
need to specify:

-  the unique organisation ID we want to run the simulation for
-  the model schema to use for the simulation by referring to the id of
   the threedimodel resource
-  datetime (in ISO 8601 (UTC) format) for the simulation start
-  either a end datetime (also in ISO 8601 (UTC) format) or the duration
   parameter in seconds

If you do not know the unique ID for your organisation you can make use
of the API to request it.

.. code:: python
       
   api.organisations_list(name__istartswith="nelen")
   {'count': 2,
   'next': None,
   'previous': None,
   'results': [{'name': 'Nelen & Schuurmans',
         'unique_id': 'b8f91de705774fe8a4e7cb2d9413bf5c',
         'url': 'https://api.3di.live/v3.0/organisations/61f5a464c35044c19bc7d4b42d7f58cb/'},
        {'name': 'Nelen & Schuurmans alleen werknemers',
         'unique_id': 'e82c74c4fb5846b3ae990c0cc69130c6',
         'url': 'https://api.3di.live/v3.0/organisations/cde64bc165644be9af023fc4fa18d098/'}]}        

Now we can create the ``Simulation`` model instance.

.. code:: python

   from threedi_api_client.openapi import Simulation

   # start date will be a datetime object
   from datetime import datetime

   my_extreme_event_simulation = Simulation(
           name="my extreme event",   # (optional)
           threedimodel=1,            # The model schema to use for the simulation by referring to the id of the threedimodel resource
           organisation='b8f91de705774fe8a4e7cb2d9413bf5c',  
           start_datetime=datetime.utcnow(),  # accepts datetime instance
           duration=7200              # in secs ==> 2 hours 
   )

The ``simulations_create`` method allows you to create a new Simulation resource.

.. code:: python

   api.simulations_create(my_extreme_event_simulation)
   {'created': 'now',
   'duration': 7200,
   'duration_humanized': '2 hours, 0 minutes, 0 seconds',
   'end_datetime': '2019-11-04T16:19:46Z',
   'id': 631,
   'name': 'my extreme event',
   'organisation': 'b8f91de705774fe8a4e7cb2d9413bf5c',
   'organisation_name': 'Nelen & Schuurmans',
   'slug': 'my-extreme-event-378f55a5-06df-4021-8fb6-65bbb70519dc',
   'start_datetime': '2019-11-04T14:19:46Z',
   'threedimodel': 'https://api.3di.live/v3.0/threedimodels/1/',
   'threedimodel_id': '1',
   'url': 'https://api.3di.live/v3.0/simulations/631/',
   'user': 'lars.claussen',
   'uuid': '378f55a5-06df-4021-8fb6-65bbb70519dc'}

Simulations allow for adding an arbitrary number of events to them like

-  rain events
-  sources and sinks
-  initial conditions
-  laterals
-  saved states
-  structure controls

All of them have their own openapi client model. To add a constant rain
event to the simulation you would do the following.

.. code:: python


   from threedi_api_client.openapi import ConstantRain
   const_rain = ConstantRain(
       simulation=631,   # the ID we got from our create call above
       offset=60,        # let the rain start after one minute
       duration=5000,    # let the rain last for 5000 secs
       value=0.0006,     # not too extreme after all...;-)
       units="m/s"       # the only unit supported for now
   )
   api.simulations_events_rain_constant_create(631, const_rain)
   {'duration': 5000,
   'offset': 60,
   'simulation': 'https://api.3di.live/v3.0/simulations/631/',
   'units': 'm/s',
   'url': 'https://api.3di.live/v3.0/simulations/631/events/rain/constant/17/',
   'value': 0.0006}

If you want to see which events are defined on a given simulation

.. code:: python

   api.simulations_events(631)
   {'boundaries': None,
   'breach': [],
   'filerasterrain': [],
   'filerastersourcessinks': [],
   'filetimeseriesrain': [],
   'filetimeseriessourcessinks': [],
   'initial_groundwaterlevel': None,
   'initial_onedwaterlevel': None,
   'initial_onedwaterlevelpredefined': None,
   'initial_savedstate': None,
   'initial_twodwaterlevel': None,
   'laterals': [],
   'lizardrasterrain': [],
   'lizardrastersourcessinks': [],
   'lizardtimeseriesrain': [],
   'lizardtimeseriessourcessinks': [],
   'savedstates': [],
   'timedstructurecontrol': [],
   'timeseriesrain': [{'constant': True,
                   'duration': 5000,
                   'interpolate': False,
                   'offset': 60,
                   'simulation': 'https://api.3di.live/v3.0/simulations/631/',
                   'units': 'm/s',
                   'url': 'https://api.3di.live/v3.0/simulations/631/events/rain/timeseries/17/',
                   'values': [[0.0, 0.0006], [5000.0, 0.0]]}],
   'timeseriessourcessinks': []}

To list all file resources, make use of the ``files_list`` method.

.. code:: python

       api.files_list()                                                                                                                
       {'count': 3064,
       'next': 'https://api.3di.live/v3.0/files/?limit=10&offset=10',
       'previous': None,
       'results': [{'bucket': '3di',
               'etag': None,
               'expiry_date': '2019-08-16',
               'filename': 'precipitation_1.nc',
               'id': 2,
               ..

Advanced usage
~~~~~~~~~~~~~~

See below for an example of uploading a rain raster.

.. code:: python

   from threedi_api_client.files import upload_file

   simulation_pk = 1
   filename = 'bergermeer_rasters_from_geotiffs.nc'
   local_file_path = './data/bergermeer_rasters_from_geotiffs.nc'

   # Create rain raster upload resource in API
   # returns a 'file_upload' instance containing a
   # put_url property which is the URL to the object
   # storage object to be uploaded with an HTTP PUT requests.
   file_upload = api.simulations_events_rain_rasters_upload(
       filename, simulation_pk)

   # Upload the file
   upload_file(local_file_path, file_upload.put_url)


Async client
~~~~~~~~~~~~

This project also provides an asynchronous api client. To use
the async-client make sure you install the optional dependencies using
``pip install threedi-api-client[aio]`` and then import from the ``aio``
submodule. The async-client works the same as the synchronous client,
except all api calls are coroutines.

For example, to asynchronously request files from the api:

.. code:: python

   import asyncio

   from threedi_api_client import ThreediApi


   config = {
       "THREEDI_API_HOST": "https://api.3di.live/v3.0",
       "THREEDI_API_USERNAME": "black.sheep",
       "THREEDI_API_PASSWORD": "myverysecretmehhh"
   }


   async def main():
       async with ThreediApi(config=config) as api_client:
           api = V3Api(api_client)
           print(await api.files_list())


   if __name__ == '__main__':
       asyncio.run(main())
