def sample_hello_world():

    farmbeats_endpoint = os.environ['FARMBEATS_ENDPOINT']

    credential = DefaultAzureCredential()

    client = FarmBeatsClient(endpoint=farmbeats_endpoint,
                             credential=credential)

    farmer_id = "contoso-farmer"
    farmer_name = "Contoso"
    farmer_description = "Contoso is hard working."

    print("Creating farmer, or updating if farmer already exists...",
          end=" ",
          flush=True)
    farmer = client.farmers.create_or_update(
        farmer_id=farmer_id,
        farmer=Farmer(name=farmer_name, description=farmer_description))
    print("Done")

    print("Here are the details of the farmer:")
    print(f"\tID: {farmer.id}")
    print(f"\tName: {farmer.name}")
    print(f"\tDescription: {farmer.description}")
    print(f"\tCreated timestamp: {farmer.created_date_time}")
    print(f"\tLast modified timestamp: {farmer.modified_date_time}")
    def test_satellite_flow(self, agrifood_endpoint):

        # Setup data
        common_id_prefix = "satellite-flow-"
        farmer_id = common_id_prefix + "test-farmer"
        boundary_id = common_id_prefix + "test-boundary"
        job_id_prefix = common_id_prefix + "job"
        job_id = self.generate_random_name(job_id_prefix)
        start_date_time = datetime(2020, 1, 1, tzinfo=Utc())
        end_date_time = datetime(2020, 1, 31, tzinfo=Utc())

        # Setup client
        client = self.create_client(agrifood_endpoint=agrifood_endpoint)

        # Create farmer
        farmer = client.farmers.create_or_update(farmer_id=farmer_id,
                                                 farmer=Farmer())

        # Create boundary if not exists
        self.create_boundary_if_not_exist(client, farmer_id, boundary_id)

        # Create satellite job
        satellite_job_poller = client.scenes.begin_create_satellite_data_ingestion_job(
            job_id=job_id,
            job=SatelliteDataIngestionJob(
                farmer_id=farmer_id,
                boundary_id=boundary_id,
                start_date_time=start_date_time,
                end_date_time=end_date_time,
                data=SatelliteData(image_names=["LAI"])),
        )

        # Get terminal job state and assert
        satellite_job = satellite_job_poller.result()

        assert satellite_job.farmer_id == farmer_id
        assert satellite_job.id == job_id
        assert satellite_job.boundary_id == boundary_id
        assert satellite_job.start_date_time == start_date_time
        assert satellite_job.end_date_time == end_date_time
        assert satellite_job.status == "Succeeded"

        # Get corresponding scenes
        scenes = client.scenes.list(
            farmer_id=farmer_id,
            boundary_id=boundary_id,
            start_date_time=start_date_time,
            end_date_time=end_date_time,
        )

        scenes_list = list(scenes)

        # Assert scenes got created
        assert len(scenes_list) > 0
        for scene in scenes_list:
            assert scene.farmer_id == farmer_id
            assert scene.boundary_id == boundary_id
 def test_boundary(self, agrifood_endpoint):
     client = self.create_client(agrifood_endpoint=agrifood_endpoint)
     farmer_id = "smoke-test-boundary-farmer"
     boundary_id = "smoke-test-boundary"
     farmer = client.farmers.create_or_update(farmer_id=farmer_id,
                                              farmer=Farmer())
     boundary = self.create_boundary_if_not_exist(client, farmer_id,
                                                  boundary_id)
     assert boundary == client.boundaries.get(farmer_id=farmer_id,
                                              boundary_id=boundary_id)
     self.delete_boundary(client, farmer_id, boundary_id)
     client.farmers.delete(farmer_id=farmer_id)
 async def test_boundary(self, agrifood_endpoint):
     client = self.create_client(agrifood_endpoint=agrifood_endpoint)
     boundary_id = "async-test-boundary"
     farmer_id = boundary_id + "-farmer"
     farmer = await client.farmers.create_or_update(farmer_id=farmer_id,
                                                    body=Farmer())
     boundary = await self.create_boundary_if_not_exist(
         client, farmer_id, boundary_id)
     assert boundary == await client.boundaries.get(farmer_id=farmer_id,
                                                    boundary_id=boundary_id)
     await self.delete_boundary(client, farmer_id, boundary_id)
     await client.farmers.delete(farmer_id=farmer_id)
    def test_farmer(self, farmbeats_endpoint, farmbeats_farmer_id):
        client = self.create_client(farmbeats_endpoint=farmbeats_endpoint)
        farmer = client.farmers.create_or_update(farmer_id=farmbeats_farmer_id,
                                                 body=Farmer())

        assert farmer.id == farmbeats_farmer_id
        assert farmer.e_tag
        assert farmer.created_date_time
        assert farmer.modified_date_time

        retrieved_farmer = client.farmers.get(farmer_id=farmbeats_farmer_id)
        assert farmer.id == retrieved_farmer.id
        assert farmer.e_tag == retrieved_farmer.e_tag
        assert farmer.created_date_time == retrieved_farmer.created_date_time
        assert farmer.modified_date_time == retrieved_farmer.modified_date_time
    async def test_farmer(self, agrifood_endpoint):
        client = self.create_client(agrifood_endpoint=agrifood_endpoint)
        farmer_id = "async-test-farmer"
        farmer = await client.farmers.create_or_update(farmer_id=farmer_id,
                                                       body=Farmer())

        assert farmer.id == farmer_id
        assert farmer.e_tag
        assert farmer.created_date_time
        assert farmer.modified_date_time

        retrieved_farmer = await client.farmers.get(farmer_id=farmer_id)
        assert farmer.id == retrieved_farmer.id
        assert farmer.e_tag == retrieved_farmer.e_tag
        assert farmer.created_date_time == retrieved_farmer.created_date_time
        assert farmer.modified_date_time == retrieved_farmer.modified_date_time

        await client.farmers.delete(farmer_id=farmer_id)
Example #7
0
    def test_farmer(self, agrifood_endpoint):
        client = self.create_client(agrifood_endpoint=agrifood_endpoint)

        farmer_id = self.generate_random_name("smoke-test-farmer")

        farmer = client.farmers.create_or_update(
            farmer_id=farmer_id,
            farmer=Farmer()
        )

        assert farmer.id == farmer_id
        assert farmer.e_tag
        assert farmer.created_date_time
        assert farmer.modified_date_time

        retrieved_farmer = client.farmers.get(farmer_id=farmer_id)
        assert farmer.id == retrieved_farmer.id
        assert farmer.e_tag == retrieved_farmer.e_tag
        assert farmer.created_date_time == retrieved_farmer.created_date_time
        assert farmer.modified_date_time == retrieved_farmer.modified_date_time

        client.farmers.delete(farmer_id=farmer_id)
Example #8
0
def sample_farm_hierarchy():

    farmbeats_endpoint = os.environ['FARMBEATS_ENDPOINT']

    credential = DefaultAzureCredential()

    client = FarmBeatsClient(
        endpoint=farmbeats_endpoint,
        credential=credential
    )

    farmer_id = "contoso-farmer"
    farmer_name = "contoso-farmer-name"
    farmer_description = "contoso-farmer-description"
    farm_id = "contoso-farm"
    farm_name = "contoso-farm-name"
    farm_description = "contoso-farm-description"
    field_id = "contoso-field"
    field_name = "contoso-field-name"
    field_description = "contoso-field-description"
    boundary_id = "contoso-boundary"
    boundary_name = "contoso-boundary-name"
    boundary_description = "contoso-boundary-description"
    multi_polygon = MultiPolygon(
        coordinates=[
            [
                [
                    [-94.05807495, 44.75916947],
                    [-94.05802487, 44.7592142],
                    [-94.05798752, 44.75921875],
                    [-94.05692697, 44.75883808],
                    [-94.05697525, 44.75861334],
                    [-94.05542493, 44.75844192],
                    [-94.05537128, 44.75891045],
                    [-94.05443251, 44.75884951],
                    [-94.05086517, 44.75856001],
                    [-94.05093491, 44.75533736],
                    [-94.05389607, 44.75516594],
                    [-94.05421793, 44.75520023],
                    [-94.05447543, 44.75534879],
                    [-94.05746988, 44.75751702],
                    [-94.05795157, 44.75824385],
                    [-94.05805349, 44.75863619],
                    [-94.05807495, 44.75916947]
                ]
            ],
            [
                [
                    [-94.05802667, 44.75929136],
                    [-94.05793598, 44.7607673],
                    [-94.05693233, 44.76072738],
                    [-94.05694842, 44.76008746],
                    [-94.05727246, 44.75988264],
                    [-94.05752903, 44.75946416],
                    [-94.05760288, 44.75923042],
                    [-94.05802667, 44.75929136]
                ]
            ]
        ]
    )

    # Step 1: Create a farmer.
    print(
        f"Creating or updating farmer with Id {farmer_id}...", end=" ", flush=True)
    farmer = client.farmers.create_or_update(
        farmer_id=farmer_id,
        farmer=Farmer(
            name=farmer_name,
            description=farmer_description
        )
    )
    print("Done")

    print("Details of farmer:")
    print("\tID:", farmer.id)
    print("\tName:", farmer.name)
    print("\tDescription:", farmer.description)

    # Step 2: Create a farm.
    print(
        f"Creating or updating farm with Id {farm_id}...", end=" ", flush=True)
    farm = client.farms.create_or_update(
        farmer_id=farmer_id,
        farm_id= farm_id,
        farm=Farm(
            name=farm_name,
            description=farm_description
        )
    )
    print("Done")

    print("Details of farm:")
    print("\tID:", farm.id)
    print("\tName:", farm.name)
    print("\tFarmer Name:", farm.farmer_id)
    print("\tDescription:", farm.description)

    # Step 3: Create a field.
    print(
        f"Creating or updating field with Id {field_id}...", end=" ", flush=True)
    field = client.fields.create_or_update(
        farmer_id=farmer_id,
        field_id= field_id,
        field=Field(
            name=field_name,
            farm_id=farm_id,
            description=field_description
        )
    )
    print("Done")

    print("Details of field:")
    print("\tID:", field.id)
    print("\tName:", field.name)
    print("\tFarmer Name:", field.farmer_id)
    print("\tFarm Name:", field.farm_id)
    print("\tName:", field.name)
    print("\tDescription:", field.description)

    # Step 4: Create a boundary.
    try:
        print(
            f"Trying to fetch boundary with id {boundary_id}...", end=" ", flush=True)
        boundary = client.boundaries.get(
            farmer_id=farmer_id,
            boundary_id=boundary_id
        )
        print("Boundary already exists.")
    except ResourceNotFoundError:
        print(
            f"Doesn't exist. Creating boundary...", end=" ", flush=True)
        boundary = client.boundaries.create_or_update(
            farmer_id=farmer_id,
            boundary_id=boundary_id,
            boundary=Boundary(
                name=boundary_name,
                geometry=multi_polygon,
                description=boundary_description
            )
        )
        print("Done")

    print("Details of boundary:")
    print("\tID:", boundary.id)
    print("\tName:", boundary.name)
    print("\tDescription:", boundary.description)
def sample_satellite_download():

    farmbeats_endpoint = os.environ['FARMBEATS_ENDPOINT']

    credential = DefaultAzureCredential()

    client = FarmBeatsClient(endpoint=farmbeats_endpoint,
                             credential=credential)

    farmer_id = "contoso-farmer"
    boundary_id = "contoso-boundary"
    job_id_prefix = "contoso-job"
    start_date_time = datetime(2020, 1, 1, tzinfo=UTC)
    end_date_time = datetime(2020, 1, 31, tzinfo=UTC)
    data_root_dir = "./data/satellite/"

    # Create or update a farmer within FarmBeats.
    print(f"Ensure farmer with id {farmer_id} exists... ", end="", flush=True)
    farmer = client.farmers.create_or_update(farmer_id=farmer_id,
                                             farmer=Farmer())
    print("Done")

    # Create a boundary if the boundary does not exist.
    try:
        print(f"Checking if boundary with id {boundary_id} exists... ",
              end="",
              flush=True)
        boundary = client.boundaries.get(farmer_id=farmer_id,
                                         boundary_id=boundary_id)
        print("Exists")

    except ResourceNotFoundError as e:
        print("Boundary doesn't exist. Creating... ", end="", flush=True)
        # Creating a boundary.
        boundary = client.boundaries.create_or_update(
            farmer_id=farmer_id,
            boundary_id=boundary_id,
            boundary=Boundary(geometry=Polygon(
                coordinates=[[[79.27057921886444, 18.042507660177698],
                              [79.26899135112762, 18.040135849620704],
                              [79.27113711833954, 18.03927382882835],
                              [79.27248358726501, 18.041069275656195],
                              [79.27057921886444, 18.042507660177698]]])))
        print("Created")

    # Queue a satellite job and wait for completion.
    job_id = f"{job_id_prefix}-{randint(0, 1000)}"
    print(f"Queuing satellite job {job_id}... ", end="", flush=True)
    satellite_job_poller = client.scenes.begin_create_satellite_data_ingestion_job(
        job_id=job_id,
        job=SatelliteDataIngestionJob(farmer_id=farmer_id,
                                      boundary_id=boundary_id,
                                      start_date_time=start_date_time,
                                      end_date_time=end_date_time,
                                      data=SatelliteData(image_names=["LAI"])))
    print("Queued. Waiting for completion... ", end="", flush=True)
    satellite_job_poller.result()
    print(f"Job completed with status {satellite_job_poller.status()}")

    # Get scenes which are available in FarmBeats for our farmer and boundary of intrest.
    print("Getting scenes list... ", end="", flush=True)
    scenes = client.scenes.list(
        boundary.farmer_id,
        boundary.id,
        start_date_time=start_date_time,
        end_date_time=end_date_time,
    )
    print("Done")

    for scene in scenes:
        safe_datetime_str = scene.scene_date_time.strftime("%Y-%m-%d %H-%M-%S")
        scene_out_path = Path(data_root_dir, scene.provider, scene.source,
                              safe_datetime_str)
        for image_file in scene.image_files:
            band_out_path = Path(
                scene_out_path,
                f"{image_file.name}-{int(image_file.resolution)}.tif")
            download_image(client, image_file.file_link, band_out_path)

    print("Downloads done")
    async def test_satellite_flow(self, agrifood_endpoint):
        # not running in playback for now because the binary body is not being scrubbed properly

        # Setup data
        common_id_prefix = "satellite-flow-async-"
        farmer_id_prefix = common_id_prefix + "test-farmer"
        boundary_id_prefix = common_id_prefix + "test-boundary"
        job_id_prefix = common_id_prefix + "job"

        job_id = self.generate_random_name(job_id_prefix)
        farmer_id = self.generate_random_name(farmer_id_prefix)
        boundary_id = self.generate_random_name(boundary_id_prefix)

        start_date_time = datetime.datetime(2020, 1, 1, tzinfo=Utc())
        end_date_time = datetime.datetime(2020, 12, 31, tzinfo=Utc())

        # Setup client
        client = self.create_client(agrifood_endpoint=agrifood_endpoint)

        # Create farmer
        farmer = await client.farmers.create_or_update(farmer_id=farmer_id,
                                                       farmer=Farmer())

        # Create boundary if not exists
        boundary = await self.create_boundary_if_not_exist(
            client, farmer_id, boundary_id)

        # Create satellite job
        satellite_job_poller = await client.scenes.begin_create_satellite_data_ingestion_job(
            job_id=job_id,
            job=SatelliteDataIngestionJob(
                farmer_id=farmer_id,
                boundary_id=boundary_id,
                start_date_time=start_date_time,
                end_date_time=end_date_time,
                data=SatelliteData(image_names=["LAI"])),
        )

        # Get terminal job state and assert
        satellite_job = await satellite_job_poller.result()

        assert satellite_job.farmer_id == farmer_id

        # in async, we're getting binary form of body, so can't scrub job id from binary
        assert job_id in satellite_job.id
        assert satellite_job.boundary_id == boundary_id
        assert satellite_job.start_date_time == start_date_time
        assert satellite_job.end_date_time == end_date_time
        assert satellite_job.status == "Succeeded"

        # Get corresponding scenes
        scenes = client.scenes.list(
            farmer_id=farmer_id,
            boundary_id=boundary_id,
            start_date_time=start_date_time,
            end_date_time=end_date_time,
        )

        scenes_list = [scene async for scene in scenes]

        # Assert scenes got created
        assert len(scenes_list) > 0
        for scene in scenes_list:
            assert scene.farmer_id == farmer_id
            assert scene.boundary_id == boundary_id
async def sample_attachments_async():

    farmbeats_endpoint = os.environ['FARMBEATS_ENDPOINT']

    credential = DefaultAzureCredential()

    client = FarmBeatsClient(endpoint=farmbeats_endpoint,
                             credential=credential)

    farmer_id = "contoso-farmer"
    farm_id = "contoso-farm"
    attachment_on_farmer_id = "contoso-farmer-attachment-1"
    attachment_on_farm_id = "contoso-farm-attachment-1"
    attachment_on_farmer_file_path = "../test.txt"
    attachment_on_farm_file_path = "../test.txt"

    if not (os.path.isfile(attachment_on_farmer_file_path)
            and os.path.isfile(attachment_on_farm_file_path)):
        raise SystemExit(
            "Please provide the paths to the files you want to upload.")

    # Ensure farmer exists, create if necessary.
    print(f"Create/updating farmer with id {farmer_id}...",
          end=" ",
          flush=True)
    await client.farmers.create_or_update(farmer_id=farmer_id, farmer=Farmer())
    print("Done!")

    # Ensure farm exists, create if necessary.
    print(f"Create/updating farm with id {farm_id}...", end=" ", flush=True)
    await client.farms.create_or_update(farmer_id=farmer_id,
                                        farm_id=farm_id,
                                        farm=Farmer())
    print("Done!")

    # Create attachment on farmer
    try:
        print(
            f"Checking if attachment with id {attachment_on_farmer_id} already exists "
            f"on farmer with id {farmer_id}...",
            end=" ",
            flush=True)
        await client.attachments.get(farmer_id=farmer_id,
                                     attachment_id=attachment_on_farmer_id)
        print("Attachment already exists. Not updating file.")

    except ResourceNotFoundError:
        print("Attachment doesn't exist")
        print("Creating attachment...", end=" ", flush=True)

        # Open file with buffering set to 0, to get a IO object.
        file_to_attach_on_farmer = open(attachment_on_farmer_file_path,
                                        "rb",
                                        buffering=0)

        await client.attachments.create_or_update(
            farmer_id=farmer_id,
            attachment_id=attachment_on_farmer_id,
            resource_id=farmer_id,
            resource_type="Farmer",
            file=file_to_attach_on_farmer)

        print("Done!")

    # Create attachment with farm
    try:
        print(
            f"Checking if attachment with id {attachment_on_farm_id} already exists "
            + f"on farm with id {farm_id}...",
            end=" ",
            flush=True)
        await client.attachments.get(farmer_id=farmer_id,
                                     attachment_id=attachment_on_farm_id)
        print("Attachment already exists. Not updating file.")

    except ResourceNotFoundError:
        print("Attachment doesn't exist")
        print("Creating attachment...", end=" ", flush=True)

        # Open file with buffering set to 0, to get a IO object.
        file_to_attach_on_farm = open(attachment_on_farm_file_path,
                                      "rb",
                                      buffering=0)

        await client.attachments.create_or_update(
            farmer_id=farmer_id,
            attachment_id=attachment_on_farm_id,
            resource_id=farm_id,
            resource_type="Farm",
            file=file_to_attach_on_farm)

        print("Done!")

    print("Proceeding to download all attachments on the farmer. " +
          "Press enter to continue...")
    input()

    print("Getting a list of all attachments " +
          f"on the farmer with id {farmer_id}...",
          end=" ",
          flush=True)
    farmer_attachments = client.attachments.list_by_farmer_id(
        farmer_id=farmer_id, )
    print("Done!")

    # Using a semaphore to limit the number of concurrent downloads.
    semaphore = asyncio.Semaphore(2)

    print("Downloading attachments with a maximum concurrency " +
          "of two downloads at a time...")

    # Setting up a async function (a coroutine) to download each attachment
    async def download(attachment, semaphore):
        async with semaphore:
            downloaded_attachment = await client.attachments.download(
                farmer_id=farmer_id, attachment_id=attachment_on_farmer_id)
            out_path = Path(
                "../data/attachments/" +
                f"{attachment.resource_type}/{attachment.resource_id}" +
                f"/{attachment.id}/{attachment.original_file_name}")

            # Make sure the dirs to the output path exists
            out_path.parent.mkdir(parents=True, exist_ok=True)

            print(
                f"Saving attachment id {attachment.id} to {out_path.resolve()}"
            )
            with open(out_path, 'wb') as out_file:
                async for bits in downloaded_attachment:
                    out_file.write(bits)

    await asyncio.gather(*[
        download(attachment, semaphore)
        async for attachment in farmer_attachments
    ])

    print("Done!")

    await client.close()
    await credential.close()
    def test_farmer_operations(self, agrifood_endpoint):

        # Setup data
        farmer_id = "test-farmer-farmer-ops"
        farmer_name = "Test Farmer"
        farmer_description = "Farmer created during testing."
        farmer_status = "Sample Status"
        farmer_properties = {"foo": "bar", "numeric one": 1, 1: "numeric key"}

        # Setup client
        client = self.create_client(agrifood_endpoint=agrifood_endpoint)

        # Create
        farmer = client.farmers.create_or_update(
            farmer_id=farmer_id,
            farmer=Farmer(name=farmer_name,
                          description=farmer_description,
                          status=farmer_status,
                          properties=farmer_properties))

        # Assert on immediate response
        assert farmer.id == farmer_id
        assert farmer.name == farmer_name
        assert farmer.description == farmer_description
        assert farmer.status == farmer_status

        assert len(farmer.properties) == 3
        assert farmer.properties["foo"] == "bar"
        assert farmer.properties["numeric one"] == 1
        assert farmer.properties["1"] == "numeric key"

        assert farmer.e_tag
        assert type(farmer.created_date_time) is datetime
        assert type(farmer.modified_date_time) is datetime

        # Retrieve created object
        retrieved_farmer = client.farmers.get(farmer_id=farmer_id)

        # Assert on retrieved object
        assert farmer == retrieved_farmer

        # Setup data for update
        farmer.name += " Updated"

        # Update
        updated_farmer = client.farmers.create_or_update(farmer_id=farmer_id,
                                                         farmer=farmer)

        # Assert on immediate response
        assert farmer.name == updated_farmer.name
        assert farmer.created_date_time == updated_farmer.created_date_time
        assert farmer.modified_date_time != updated_farmer.modified_date_time

        # Retrieve updated object
        updated_retrieved_farmer = client.farmers.get(farmer_id=farmer_id)

        # Assert updated object
        assert updated_retrieved_farmer == updated_farmer

        # Delete
        client.farmers.delete(farmer_id=farmer_id)

        # Assert object doesn't exist anymore
        with pytest.raises(ResourceNotFoundError):
            client.farmers.get(farmer_id=farmer_id)
async def sample_farm_hierarchy_complete_async():

    farmbeats_endpoint = os.environ['FARMBEATS_ENDPOINT']

    credential = DefaultAzureCredential()

    client = FarmBeatsClient(endpoint=farmbeats_endpoint,
                             credential=credential)

    farmer_id = "contoso-farmer"
    farmer_name = "Contoso"
    farmer_description = "Contoso is hard working."

    farmer_id = "contoso-farmer"
    farmer_name = "contoso-farmer-name"
    farmer_description = "contoso-farmer-description"
    farm_id = "contoso-farm"
    farm_name = "contoso-farm-name"
    farm_description = "contoso-farm-description"
    field_id = "contoso-field"
    field_name = "contoso-field-name"
    field_description = "contoso-field-description"
    boundary_id = "contoso-boundary"
    boundary_name = "contoso-boundary-name"
    boundary_description = "contoso-boundary-description"
    crop_id = "contoso-crop"
    crop_name = "contoso-crop-name"
    crop_description = "contoso-crop-description"
    crop_variety_id = "contoso-crop-variety"
    crop_variety_name = "contoso-crop_variety-name"
    crop_variety_description = "contoso-crop-variety-description"
    season_id = "contoso-season"
    season_name = "contoso-season-name"
    season_description = "contoso-season-description"
    seasonal_field_id = "contoso-seasonal_field"
    seasonal_field_name = "contoso-seasonal_field-name"
    seasonal_field_description = "contoso-seasonal_field-description"
    year = "2021"
    start_date_time = "2021-01-01T20:08:10.137Z"
    end_date_time = "2021-06-06T20:08:10.137Z"
    multi_polygon = MultiPolygon(coordinates=[
        [[[-94.05807495, 44.75916947], [-94.05802487, 44.7592142],
          [-94.05798752, 44.75921875], [-94.05692697, 44.75883808],
          [-94.05697525, 44.75861334], [-94.05542493, 44.75844192],
          [-94.05537128, 44.75891045], [-94.05443251, 44.75884951],
          [-94.05086517, 44.75856001], [-94.05093491, 44.75533736],
          [-94.05389607, 44.75516594], [-94.05421793, 44.75520023],
          [-94.05447543, 44.75534879], [-94.05746988, 44.75751702],
          [-94.05795157, 44.75824385], [-94.05805349, 44.75863619],
          [-94.05807495, 44.75916947]]],
        [[[-94.05802667, 44.75929136], [-94.05793598, 44.7607673],
          [-94.05693233, 44.76072738], [-94.05694842, 44.76008746],
          [-94.05727246, 44.75988264], [-94.05752903, 44.75946416],
          [-94.05760288, 44.75923042], [-94.05802667, 44.75929136]]]
    ])

    # Step 1: Create a farmer.
    print(f"Creating or updating farmer with Id {farmer_id}...",
          end=" ",
          flush=True)
    farmer = await client.farmers.create_or_update(
        farmer_id=farmer_id,
        farmer=Farmer(name=farmer_name, description=farmer_description))
    print("Done")

    print("Details of farmer:")
    print("\tID:", farmer.id)
    print("\tName:", farmer.name)
    print("\tDescription:", farmer.description)

    # Step 2: Create a farm.
    print(f"Creating or updating farm with Id {farm_id}...",
          end=" ",
          flush=True)
    farm = await client.farms.create_or_update(
        farmer_id=farmer_id,
        farm_id=farm_id,
        farm=Farm(name=farm_name, description=farm_description))
    print("Done")

    print("Details of farm:")
    print("\tID:", farm.id)
    print("\tName:", farm.name)
    print("\tFarmer Name:", farm.farmer_id)
    print("\tDescription:", farm.description)

    # Step 3: Create a field.
    print(f"Creating or updating field with Id {field_id}...",
          end=" ",
          flush=True)
    field = await client.fields.create_or_update(
        farmer_id=farmer_id,
        field_id=field_id,
        field=Field(name=field_name,
                    farm_id=farm_id,
                    description=field_description))
    print("Done")

    print("Details of field:")
    print("\tID:", field.id)
    print("\tName:", field.name)
    print("\tFarmer Name:", field.farmer_id)
    print("\tFarm Name:", field.farm_id)
    print("\tName:", field.name)
    print("\tDescription:", field.description)

    # Step 4: Create a crop.
    print(f"Creating or updating crop with Id {crop_id}...",
          end=" ",
          flush=True)
    crop = await client.crops.create_or_update(
        crop_id=crop_id,
        crop=Crop(name=crop_name, description=crop_description))
    print("Done")

    print("Details of crop:")
    print("\tID:", crop.id)
    print("\tName:", crop.name)
    print("\tDescription:", crop.description)

    # Step 5: Create a crop variety.
    print(f"Creating or updating crop variety with Id {crop_variety_id}...",
          end=" ",
          flush=True)
    crop_variety = await client.crop_varieties.create_or_update(
        crop_id=crop_id,
        crop_variety_id=crop_variety_id,
        crop_variety=CropVariety(name=crop_variety_name,
                                 description=crop_variety_description))
    print("Done")

    print("Details of crop variety:")
    print("\tID:", crop_variety.id)
    print("\tCrop ID:", crop_variety.crop_id)
    print("\tName:", crop_variety.name)
    print("\tDescription:", crop_variety.description)

    # Step 6: Create a season.
    print(f"Creating or updating season with Id {season_id}...",
          end=" ",
          flush=True)
    season = await client.seasons.create_or_update(
        season_id=season_id,
        season=Season(name=season_name,
                      year=year,
                      start_date_time=start_date_time,
                      end_date_time=end_date_time,
                      description=season_description))
    print("Done")

    print("Details of season:")
    print("\tID:", season.id)
    print("\tName:", season.name)
    print("\tDescription:", season.description)
    print("\tYear:", season.year)
    print("\tStart Date Time:", season.start_date_time)
    print("\tEnd Date Time:", season.end_date_time)

    # Step 7: Create a seasonal field.
    print(
        f"Creating or updating seasonal field with Id {seasonal_field_id}...",
        end=" ",
        flush=True)
    seasonal_field = await client.seasonal_fields.create_or_update(
        farmer_id=farmer_id,
        seasonal_field_id=seasonal_field_id,
        seasonal_field=SeasonalField(name=seasonal_field_name,
                                     farm_id=farm_id,
                                     field_id=field_id,
                                     season_id=season_id,
                                     crop_id=crop_id,
                                     crop_variety_ids=[crop_variety_id],
                                     description=seasonal_field_description))
    print("Done")

    print("Details of seasonal field:")
    print("\tID:", seasonal_field.id)
    print("\tName:", seasonal_field.name)
    print("\tFarmer Name:", seasonal_field.farmer_id)
    print("\tFarm Name:", seasonal_field.farm_id)
    print("\tCrop Name:", seasonal_field.crop_id)
    print("\tSeason Name:", seasonal_field.season_id)
    print("\tField Name:", seasonal_field.field_id)
    print("\tCrop Variety Name:", seasonal_field.crop_variety_ids)
    print("\tName:", seasonal_field.name)
    print("\tDescription:", seasonal_field.description)

    # Step 8: Create a boundary.
    try:
        print(f"Trying to fetch boundary with id {boundary_id}...",
              end=" ",
              flush=True)
        boundary = await client.boundaries.get(farmer_id=farmer_id,
                                               boundary_id=boundary_id)
        print("Boundary already exists.")
    except ResourceNotFoundError:
        print(f"Doesn't exist. Creating boundary...", end=" ", flush=True)
        boundary = client.boundaries.create_or_update(
            farmer_id=farmer_id,
            boundary_id=boundary_id,
            boundary=Boundary(name=boundary_name,
                              geometry=multi_polygon,
                              parent_id=seasonal_field_id,
                              description=boundary_description))
        print("Done")

    print("Details of boundary:")
    print("\tID:", boundary.id)
    print("\tName:", boundary.name)
    print("\tDescription:", boundary.description)
    print("\tParentId:", boundary.parent_id)

    await client.close()
    await credential.close()