NIAB-CUF Potato Yield and Nitrogen API Quick Start Guide

Access

To access the NIAB-CUF Potato Yield API, register by contacting us at pym@niab.com.

The NIAB-CUF Potato Yield API uses Bearer Authentication. Bearer authentication (also called token authentication) is an HTTP authentication scheme that involves security tokens called bearer tokens. The user must send this token in the Authorization header when making requests to protected resources:

Authorization: Bearer <token>

To try some API calls described below, download the Postman Collection here.

NIAB Variety API

The NIAB Variety API is an additional API service developed by NIAB in order to store and deliver variety specific crop parameters to a number of models under development.

Note: this API does not require a token to access the basic list of varieties.

To access a list of accepted varieties available for use in the NIAB-CUF Potato Yield API, send a HTTP GET requests to:

https://variety.niabdev.co.uk/api/variety/potato/pym

This will return a JSON document with a list of varieties and a link to use in model input (See section Constructing your own input).

[
    {
        "variety":"aba",
        "embeddableLink":"https://variety.niabdev.co.uk/api/variety/potato/pym/dbb35eb6-0263-4687-8bb3-c599be370907"
        
    },
    {   "variety":"abbot",
        "embeddableLink":"https://variety.niabdev.co.uk/api/variety/potato/pym/5735c523-d700-4352-9876-e736f3ac6a0f"
        
    },
    {   "variety":"abby",
        "embeddableLink":"https://variety.niabdev.co.uk/api/variety/potato/pym/b0c0407a-4946-47bd-bef4-37a35ad737f5"
    },
    ...
]

Note: Some varieties may be subject to quality control measures during the model run.

Starting a model job

To submit input to and execute the NIAB-CUF Potato Yield Model, the following endpoint of the GMS is used:

https://prod.niab.com/gms/api/v1/models/pym/jobs?persist=true&webhook=https://www.niab.com

There are 2 parameters to supply with this call: webhook and persist.

webhook is optional and can be set to a URL that will recieve a POST request with the response once the model run has completed.

persist is optional and can be set to true or false to allow or prevent the results to persist on the GMS. Set this to true for this example.

Note: if webhook is empty, persist must be set to true

Using the Postman Collection navigate to the createJob request.

If not already completed, fill out your API token in the Authorzation header. Ensure the Content-Type header is set to application/json.

The main input to the model is a structured JSON document inserted in the body of the API call. In this example, a copy the example is already in the request body of the Postman Collection.

When ready, press the send button to make the API call.

If successful you should receive a 202 Accepted status.

The important information in the response is contained in the Location header.

Location /models/pym/jobs/4RXMJedaPpcz53/status

The string 4RXMJedaPpcz53 represents the unique identifier of the model job and the URL /models/pym/jobs/4RXMJedaPpcz53/status is a link to check the status of the job.

Your location string will be different to the example above. Use your location in the following API calls.

Checking the status of a job

To check the status of a job the following endpoint is used:

https://prod.niab.com/gms/api/v1/models/pym/jobs/{{jobId}}/status

Using the Postman Collection, navigate to the getJobStatus request.

Set jobId to the unique location string in the previous call (in our case 4RXMJedaPpcz53).

If not already completed, fill out your API token in the Authorzation header. Ensure the Content-Type header is set to application/json.

When ready, press the send button to make the API call.

If successful you should receive a 200 OK status and the following response:

{
    "_links": {
        "parent": {
            "href": "/models/pym/jobs/oS7Sv7LPmbB1cB1kcaL1YJ"
        },
        "self": {
            "href": "/models/pym/jobs/oS7Sv7LPmbB1cB1kcaL1YJ/status"
        }
    },
    "status": "published"
}

There are 4 possible kinds of status returned: published, failed, requiresQC and submitted.

Getting the results of a job

To get the results of a job use the following endpoint:

https://prod.niab.com/gms/api/v1/models/results/pym/{{jobId}}

You will only be able to get results from jobs that are in the published state.

Using the Postman Collection, navigate to the getResults request.

Set jobId to the unique location string in the previous call (in our case 4RXMJedaPpcz53).

If not already completed, fill out your API token in the Authorzation header. Ensure the Content-Type header is set to application/json.

When ready, press the send button to make the API call.

If successful you should receive a 200 OK status and the following response (truncated):

{
    "validationErrors": [
        "Date planted is not within 6 months past and now",
        "Interval between surveys should be at most 1 week",
        "A survey should exist that is within one week of the model run date",
        "Some model parameters are not within expected bounds. You may wish to have this job QC'd"
    ],
    "cropId": "0aef8c2c-3197-11e7-8ac8-f23c9118e215",
    "about": {
        "credit": {
            "text": "Modelled by NIAB-CUF",
            "url": "http://www.niab.com",
            "imageUrl": "http://pym.niabdev.co.uk/niabcuf_logo.png",
            "imageCaption": "NIAB-CUF Potato Yield Model"
        },
        "terms": {
            "termsUrl": "http://pym.niabdev.co.uk/terms/",
            "technicalUrl": "http://pym.niabdev.co.uk/technical/",
            "apiUrl": "http://pym.niabdev.co.uk/api/"
        },
        "version": "1.24"
    },
    "modelPredictionData":[...]
}

Re-submitting data to the model

As you gather more data about your potato crop (e.g. more canopy data or a new sample dig), this can be added as a new run for the same job. This helps to keep your data organised.

To submit a new run for an existing job, using the Postman Collection, navigate to the runExistingJob endpoint.

Follow the instructions for sumbitting a new job (see above). You will also need to add the existing jobId (e.g. 4RXMJedaPpcz53) in the to the endpoint.

https://prod.niab.com/gms/api/v1/models/pym/jobs/{{jobId}}

Note: when resubmitting a job, the original webhook will be used if supplied.

When ready, press the send button to make the API call.

If successful you should receive a 202 Accepted status and the same response as the createJob endpoint.

When requesting the results of a job with multiple runs, only the most recent run will be returned.

Constructing your own input

The structure of the JSON input object is defined by the JSON Schema found here.

It is strongly recommended to use a validation tool to validate constructed model inputs before submitting to the model.

Examples of full and partial model inputs can be found below:

An example of a client output can be found below:

The JSON Schema definitions for input and output can be found below:

The NIAB GMS requires the JSON input to have a top level data object. Below this is the modelInput object which in turn has 4 parameters:

{"data": {
    "modelInput": {
        "modelMetadata": {...},
        "crop": {...},
        "survey": [...],
        "sample": [...],
        "nitrogenSampleReplicates": [...]
        }
    }
}

ModelMetadata object (required)

The ‘modelMetadata’ object contains information to control the model run. It is required and the model execution will apply defaults if sub-objects are absent.

The modelMetadata object has 4 parameters:

"modelMetadata": {
    "bypassManualQC": true,
    "modelDate": "2018-10-10",
    "contact": {...},
    "gradingParameters": {...}
}

gradingParameters contains 2 parameters:

"gradingParameters": {
    "gradeSizeUnit": "mm",
    "grades": [0,10,20,30,40,50,60,70,80,90,100]
}

The contact object contains the following parameters:

"contact": {
    "email": "user@email.com"
}

Crop object (required)

The crop object contains information regarding the particular potato crop to be modelled.

The crop object contains the following parameters:

"crop": {
    "dateEmergence": "2017-05-03",
    "description": "Seed crop",
    "variety": "https://variety.niabdev.co.uk/api/variety/potato/pym/fb70f9b0-aaee-4948-ba79-85602215f955",
    "dateHarvest": "2018-09-10",
    "dateDefoliation": "2018-08-14",
    "datePlanted": "2017-03-31",
    "id": "0aef8c2c-3197-11e7-8ac8-f23c9118e215",
    "name": "PLOT-2016-17-0273",
    "org": "a59346f3-93ca-40af-af02-d7612d0d8706",
    "location": {...}
}

The location object contains geographic information about the crop.

The location object contains the following parameters:

"location": {
    "latitude": 51.123,
    "longitude": 1.0234
}

Survey list (required)

The survey list contains all data points relating to the growth and senescence of the potato canopy.

survey is a list of objects containing the following parameters:

"survey": [
    {
    "surveyDate": "2017-05-09",
    "groundcover": 1.0
    },
    {
    "surveyDate": "2017-05-09",
    "groundcover": 2.0
    }
]

Sample list (optional)

The sample list contains all data points relating to the sample yield digs performed on a crop.

sample is a list of objects containing the following parameters:

"sample": [
    {
    "rowLength": 2.0,
    "numberRows": 1.0,
    "rowUnit": "cm",
    "sampleDate": "2017-10-07",
    "rowLengthUnit": "m",
    "rowWidth": 91.44,
    "weightUnit": "g",
    "sampleReplicates": [...]
    },
    {
    "rowLength": 2.0,
    "numberRows": 1.0,
    "rowUnit": "cm",
    "sampleDate": "2017-06-23",
    "rowLengthUnit": "m",
    "rowWidth": 91.44,
    "weightUnit": "g",
    "sampleReplicates": [...]
    }
]

The sampleReplicates list contains the following parameters:

"sampleReplicates": [
    {
    "plantsHarvested": 6.0,
    "mainStems": 21.0,
    "gradeValues": [...],
    "secondaryStems": 15,
    "dryMatterPercent": 30.0
    },
    {
    "plantsHarvested": 6.0,
    "mainStems": 21.0,
    "gradeValues": [...],
    "secondaryStems": 15,
    "dryMatterPercent": 30.0
    }
]

The gradeValues object contains the following parameters:

"gradeValues": [
    {
    "tuberNumber": 0,
    "gradeSize": {...},
    "weight": 0.0
    },
    {
    "tuberNumber": 0,
    "gradeSize": {...},
    "weight": 0.0
    },
]

The gradeSize object contains the following parameters:

"gradeSize": {
    "lowerBound": 0.0,
    "upperBound": 15.0
}

Note: The upper bound of the final grade size should not try to accommodate a large size band into a single grade fraction.

NitrogenSampleReplicates list (optional)

The nitrogenSampleReplicates list contains all data points relating to the nitrogen sampling performed on a crop.

nitrogenSampleReplicates is a list of objects containing the following parameters:

[
      {
        "replicateNo": 1,
        "sampleDate": "2017-07-07",
        "numberOfStems": 4,
        "totalNUnits": "m",
        "totalVineFwYieldWeight": 2.5,
        "subSampleFwReplicate": 2.5,
        "subSampleDwReplicate": 2.5,
        "tuberNContentReplicate": 2.5,
        "tuberNConcentrationReplicate": 2.5
      },
      {
        "replicateNo": 2,
        "sampleDate": "2017-07-07",
        "numberOfStems": 4,
        "totalNUnits": "m",
        "totalVineFwYieldWeight": 2.5,
        "subSampleFwReplicate": 2.5,
        "subSampleDwReplicate": 2.5,
        "tuberNContentReplicate": 2.5,
        "tuberNConcentrationReplicate": 2.5
      },
      {
        "replicateNo": 3,
        "sampleDate": "2017-07-07",
        "numberOfStems": 4,
        "totalNUnits": "m",
        "totalVineFwYieldWeight": 2.5,
        "subSampleFwReplicate": 2.5,
        "subSampleDwReplicate": 2.5,
        "tuberNContentReplicate": 2.5,
        "tuberNConcentrationReplicate": 2.5
      }
    ]

Understanding the Output

The output of the PYM contains 4 top level objects:

{
    "cropId": "0aef8c2c-3197-11e7-8ac8-f23c9118e215",
    "about": {...},
    "validationErrors": {...},
    "nitrogenModel": {...},
    "modelPredictionData": {...}
}

Crop ID

This cropId is the user generated UUID submitted with the model input.

About

The about object contains information relating to terms and conditions:

{
    "terms": {
        "termsUrl": "https://niabdev.co.uk/pym/terms",
        "apiUrl": "https://niabdev.co.uk/pym/api",
        "technicalUrl": "https://niabdev.co.uk/pym/technical"
    },
    "credit": {
        "imageCaption": "NIAB-CUF Potato Yield Model",
        "imageUrl": "https://niabdev.co.uk/pym/pym_logo.png",
        "text": "Modelled by NIAB-CUF",
        "url": "http://www.niab.com"
    },
    "version": "1.18"
}

Validation Errors

Some inputs may trigger validity warnings which are provided in the validationErrors list:

"validationErrors": [
        "Date planted is not within 6 months past and now",
        "Interval between surveys should be at most 1 week",
        "A survey should exist that is within one week of the model run date"
]

Nitrogen Model

nitrogenModel is an object which contains:

{
    "variates":[
        {
            "sampleDate": "2017-06-23",
            "meanTotalNUptake": 69.1511111
        },
        {
            "sampleDate": "2017-08-15",
            "meanTotalNUptake": 88.0
        }
    ],
    "seasonLongPotentialRadiationAbsorption" : 6.319489800033206,
    "radiationAbsorptionAchievedDate" : "2017-06-25"
}

Model Prediction Data

modelPredictionData is a list of yield/ground cover objects:

[
    {...},
    {...},
    {...},
    ...
]

Yield/Ground Cover Object

The yield/ground cover object contains the observed and forecasted yield and canopy data:

{
    "date": "2017-10-06",
    "radiation": 12.07,
    "ltaRadiation": 7.603714285714285,
    "gcLower": 96.9102448357476,
    "gcUpper": 100.0,
    "gcMean": 98.9102448357476,
    "gcModelled": 0.00010777474372503093,
    "gcCombined": 98.9102448357476,
    "tuberPartitionPredictionData": [...],
    "fwYieldMean": 43.64274499419973
}

Tuber Partition Prediction Data

tuberPartitionPredictionData is a list of objects:

[
    {
        "upperSizeBound": 10,
        "weight": 0,
        "lowerSizeBound": 0
    },
    ...,
    {
        "upperSizeBound": 100,
        "weight": 0,
        "lowerSizeBound": 90
    },
    {
        "weight": 0,
        "lowerSizeBound": 100
    }
]