Skip to content

Mosquito Collections from Start to Finish

So your agency wants to record surveillance and testing information in VectorSurv using the VectorSurv API. What would this process look like?

Let's look at an example: A user at a local control agency, Davis Public Health Department, out of Davis, California, trapped mosquitoes at a new location in Central Park, and sent pools of those mosquitoes to a lab for arbovirus testing.

We can get these surveillance records into VectorSurv with a series of four API requests:

  1. Create a site
  2. Create a collection at that site
  3. Create mosquito pools associated with that collection
  4. Create pool results for that pool

Creating the site

Sites provide context for surveillance activity. A new site should be created for any location that will see more than one instance of surveillance activity. View the documentation for creating a new site.

Site details

The new site is located behind the carousel in Central Park, which itself is located at the intersection of 5th Street and B Street in Davis, California. In accordance with DPHD conventions, the new "Central Park Carousel" is assigned site code "000123".

Using this information and the documentation, an API user can construct an object that describes the new site. An annotated version of that object might look like this:

brandNewSite.js
const site = {
  active_date: "2023-05-05", // Date from which surveillance activity occurs in this location
  address_1: "5th Street & B Street", // Street address
  city: "Davis",
  region: 6, // Region ID corresponding to California
  postal_code: "95616", //
  code: "000123", // 6 digit site code
  comments: "Collection site behind the carousel in Central Park",
  coordinate_precision: 2, // ID corresponding to level of precision to which coordinates are known.
  name: "Central Park Carousel",
  shape: {
    type: "Point",
    coordinates: [-127.45, 38.546], // Longitude and latitude of the new site
  },
  site_population: "Suburban", // "Suburban", "Urban", or "Rural"
  site_land_usage: [5], // Land usage ID corresponding to "Park/Cemetery/Golf Course"
};

Making the Request

That same object could be used to define the request body in a POST request to create the site:

createSite.js
async function createSite() {
  const config = {
    headers: {
      Authorization: "Token DPHD_user_auth_token",
      "Content-Type": "application/json",
    },
  };

  const url = "https://api.vectorsurv.org/v1/site";
  const params = {
    active_date: "2023-05-05",
    address_1: "5th Street & B Street",
    city: "Davis",
    region: 6,
    postal_code: "95616",
    code: "000123",
    comments: "Collection site behind the carousel in Central Park",
    coordinate_precision: 2,
    name: "Central Park Carousel",
    shape: {
      type: "Point",
      coordinates: [-127.45, 38.546],
    },
    site_population: "Suburban",
    site_land_usage: [5],
  };

  try {
    const response = await axios.post(url, params, config);
    console.log("Response:", response.data);
  } catch (error) {
    console.error("Error:", error.response.data);
  }
}
createSite.js
library(httr)
library(jsonlite)

createSite <- function(token) {
  headers = c(
    Authorization = paste("Token", token),
    "Content-Type" = "application/json"
  )

  url <- "https://api.vectorsurv.org/v1/site"

  body <- list(
    active_date = "2023-05-05",
    address_1 = "5th Street & B Street",
    city = "Davis",
    region = 6,
    postal_code = "95616",
    code = "000123",
    comments = "Collection site behind the carousel in Central Park",
    coordinate_precision = 2,
    name = "Central Park Carousel",
    shape = list(
      type = "Point",
      coordinates = list(-127.45, 38.546)
    ),
    site_population = "Suburban",
    site_land_usage = list(5)
  )

  tryCatch({
    # Make the API request
    response <- POST(url, body = jsonlite::toJSON(body, auto_unbox = TRUE), add_headers(headers))

    # Check the response status code and category
    status_code <- http_status(response)$status_code
    status_category <- http_status(response)$category

    if (status_category == "Success") {
      # Response status is successful (e.g., status code 200)
      content <- httr::content(response, "text")
      # Use response data frame if needed
      content_df = fromJSON(content)
      message("Post request was successful")
    } else {
      # Response status is not successful (e.g., status code 422)
      response_content <- httr::content(response, 'text')
      message("Error response content:", response_content)
    }
  }, error = function(e) {
    # Any other exceptions that might occur during the API request
    message("An error occurred during the API request. Error message:", e$message)
  })
}

createSite("DPHD_user_auth_token")
curl -X POST 'https://api.vectorsurv.org/v1/site' \
-H "Authorization: Token DPHD_user_auth_token" \
-H "Content-Type: application/json" \
-d '{
  "active_date": "2023-05-05",
  "address_1": "5th Street & B Street",
  "city": "Davis",
  "region": 6,
  "postal_code": "95616",
  "code": "000123",
  "comments": "Collection site behind the carousel in Central Park",
  "coordinate_precision": 2,
  "name": "Central Park Carousel",
  "shape": {
    "type": "Point",
    "coordinates": [-127.45, 38.546]
  },
  "site_population": "Suburban",
  "site_land_usage": [5]
}'

Interpreting the Response

On success, the response body details the site record that was created, including the id, which will be useful for associating collections with this location.

responseBody.json
{
  "active_date": "2023-05-05T00:00:00.000Z",
  "code": "000123",
  "name": "Central Park Carousel",
  "coordinate_precision": 2,
  "shape": {
    "crs": {
      "type": "name",
      "properties": {
        "name": "EPSG:4326"
      }
    },
    "type": "Point",
    "coordinates": [-127.45, 38.546]
  },
  "region": 6,
  "postal_code": "95616",
  "city": "Davis",
  "site_land_usage": [5],
  "address_1": "5th Street & 26 B Street",
  "agency": 123,
  "deactive_date": null,
  "elevation": null,
  "user": 456,
  "id": 90002,
  "add_date": "2023-06-01T00:00:00.000Z",
  "address_2": null,
  "comments": "Collection site behind the carousel in Central Park",
  "revision_comments": null,
  "updated": "2023-06-01T00:00:00.000Z"
}

Creating a collection at the new site

Surveillance activity can be associated with one or more sites to provide more context for the surveillance activity. View the documentation for creating a new collection.

Collection Details

The first collection at the new site happened on May 06, 2023. A CO2 Trap with a dry ice lure ran for one night with no problems, catching 13 female Cx. tarsalis and 7 female Cs. incidens. In accordance with DPHD conventions, the collection is assigned collection number 42.

Using this information and the documentation, an API user can construct an object that describes the new collection. An annotated version of that object might look like this:

newCollection.js
const collection = {
  collection_date: "2023-05-06",
  collection_num: 42,
  comments: "First collection at new site",
  identified_by: "Hector Collector",
  num_trap: 1,
  site: 90002, // ID corresponding to site where the collection occurred
  trap: 2, // ID corresponding to CO2 trap
  trap_nights: 1, // Ran for 1 night
  trap_problem_bit: false, // No issues
  arthropods: [
    {
      species: 70, // tarsalis
      sex: 4, // ID corresponding to female
      num_count: 13,
    },
    {
      species: 57, // incidens
      sex: 4, // ID corresponding to female
      num_count: 7,
    },
  ],
  lures: [32], // ID corresponding to dry ice lure
};

Making the Request

That same object could be used to define the request body in a POST request to create the collection:

createCollection.js
async function createCollection() {
  const config = {
    headers: {
      Authorization: "Token DPHD_user_auth_token",
      "Content-Type": "application/json",
    },
  };

  const url = "https://api.vectorsurv.org/v1/arthropod/collection";
  const params = {
    collection_date: "2023-05-06",
    collection_num: 42,
    comments: "First collection at new site",
    identified_by: "Hector Collector",
    num_trap: 1,
    site: 90002,
    trap: 2,
    trap_nights: 1,
    trap_problem_bit: false,
    arthropods: [
      {
        species: 70,
        sex: 4,
        num_count: 13,
      },
      {
        species: 57,
        sex: 4,
        num_count: 7,
      },
    ],
    lures: [32],
  };

  try {
    const response = await axios.post(url, params, config);
    console.log("Response:", response.data);
  } catch (error) {
    console.error("Error:", error.response.data);
  }
}
createCollection.r
library(httr)
library(jsonlite)

createCollection <- function(token) {
  headers = c(
    Authorization = paste("Token", token),
    "Content-Type" = "application/json"
  )

  url <- "https://api.vectorsurv.org/v1/arthropod/collection"

  body <- list(
    collection_date = "2023-05-06",
    collection_num = 42,
    comments = "First collection at new site",
    identified_by = "Hector Collector",
    num_trap = 1,
    site = 90002,
    trap = 2,
    trap_nights = 1,
    trap_problem_bit = FALSE,
    arthropods = list(
      list(
        species = 70,
        sex = 4,
        num_count = 13
      ),
      list(
        species = 57,
        sex = 4,
        num_count = 7
      )
    ),
    lures = list(32)
  )

  tryCatch({
    # Make the API request
    response <- POST(url, body = jsonlite::toJSON(body, auto_unbox = TRUE), add_headers(headers))

    # Check the response status code and category
    status_code <- http_status(response)$status_code
    status_category <- http_status(response)$category

    if (status_category == "Success") {
      # Response status is successful (e.g., status code 200)
      content <- httr::content(response, "text")
      # Use response data frame if needed
      content_df = fromJSON(content)
      message("Post request was successful")
    } else {
      # Response status is not successful (e.g., status code 422)
      response_content <- httr::content(response, 'text')
      message("Error response content:", response_content)
    }
  }, error = function(e) {
    # Any other exceptions that might occur during the API request
    message("An error occurred during the API request. Error message:", e$message)
  })
}

createCollection("DPHD_user_auth_token")
curl -X POST 'https://api.vectorsurv.org/v1/arthropod/collection' \
-H "Authorization: Token DPHD_user_auth_token" \
-H "Content-Type: application/json" \
-d '{
  "collection_date": "2023-05-06",
  "collection_num": 42,
  "comments": "First collection at new site",
  "identified_by": "Hector Collector",
  "num_trap": 1,
  "site": 90002,
  "trap": 2,
  "trap_nights": 1,
  "trap_problem_bit": false,
  "arthropods": [
    {
      "species": 70,
      "sex": 4,
      "num_count": 13
    },
    {
      "species": 57,
      "sex": 4,
      "num_count": 7
    }
  ],
  "lures": [32]
}'

Interpreting the Response

On success, the response body details the collection record that was created, including the id, which will be useful for creating pools with this collection.

responseBody.json
{
  "add_date": "2023-06-01T00:00:00.000Z",
  "updated": "2023-06-01T00:00:00.000Z",
  "id": 66001,
  "collection_date": "2023-05-06T00:00:00.000Z",
  "collection_num": 42,
  "comments": "First collection at new site",
  "identified_by": "Hector",
  "num_trap": 1,
  "site": 90002,
  "trap": 2,
  "trap_nights": 1,
  "trap_problem_bit": false,
  "agency": 123,
  "collection_date_date_only": true,
  "surv_year": 2023,
  "user": 456,
  "deactive_date": null,
  "location": {
    "table_name": "arthro_collection",
    "record_id": 66001,
    "agency": 123,
    "shape": {
      "crs": {
        "type": "name",
        "properties": {
          "name": "EPSG:4326"
        }
      },
      "type": "Point",
      "coordinates": [-127.45, 38.546]
    },
    "coordinate_precision": 2
  },
  "arthropods": [
    {
      "id": 30013,
      "species": 70,
      "sex": 4,
      "num_count": 13,
      "collection": 66001,
      "agency": 123,
      "deactive_date": null
    },
    {
      "id": 30012,
      "species": 57,
      "sex": 4,
      "num_count": 7,
      "collection": 66001,
      "agency": 123,
      "deactive_date": null
    }
  ],
  "lures": [
    {
      "add_date": "2023-06-07T18:02:50.658Z",
      "id": 30039,
      "table_name": "arthro_collection",
      "record_id": 66001,
      "agency": 123,
      "lure": 32,
      "deactive_date": null
    }
  ]
}

Creating pools associated with that collection

Pool records can be associated with existing collections to provide more context to the pool. View the documentation for creating a new mosquito pool.

Pool Details

DPHD then created a pool of 5 female Cx. tarsalis out of that collection, which they plan to send to their testing lab. In accordance with DPHD conventions, the new pool is assigned pool number 123001.

Using this information and the documentation, an API user can construct an object that describes the new pool. An annotated version of that object might look like this:

newPool.js
const pool = {
  collection: 66001, // ID corresponding to the collection from which the pool is drawn
  comments: "Culex pool from collection 123",
  num_count: 5,
  pool_num: 123001,
  primary_source_bit: true, // Indicating that the pool's contents are primarily drawn from the associated collection
  species: 70,
  sex: 4,
};

Making the Request

That same object could be used to define the request body in a POST request to create the collection:

createPool.js
async function createPool() {
  const config = {
    headers: {
      Authorization: "Token DPHD_user_auth_token",
      "Content-Type": "application/json",
    },
  };

  const url = "https://api.vectorsurv.org/v1/arthropod/pool";
  const params = {
    collection: 66001,
    comments: "Culex pool from collection 123",
    num_count: 5,
    pool_num: 123001,
    primary_source_bit: true,
    species: 70,
    sex: 4,
  };

  try {
    const response = await axios.post(url, params, config);
    console.log("Response:", response.data);
  } catch (error) {
    console.error("Error:", error.response.data);
  }
}
createPool.r
library(httr)
library(jsonlite)

createPool <- function(token) {
  headers = c(
    Authorization = paste("Token", token),
    "Content-Type" = "application/json"
  )

  url <- "https://api.vectorsurv.org/v1/arthropod/pool"

  body <- list(
    collection = 66001,
    comments = "Culex pool from collection 123",
    num_count = 5,
    pool_num = 123001,
    primary_source_bit = TRUE,
    species = 70,
    sex = 4
  )

  tryCatch({
    # Make the API request
    response <- POST(url, body = jsonlite::toJSON(body, auto_unbox = TRUE), add_headers(headers))

    # Check the response status code and category
    status_code <- http_status(response)$status_code
    status_category <- http_status(response)$category

    if (status_category == "Success") {
      # Response status is successful (e.g., status code 200)
      content <- httr::content(response, "text")
      # Use response data frame if needed
      content_df = fromJSON(content)
      message("Post request was successful")
    } else {
      # Response status is not successful (e.g., status code 422)
      response_content <- httr::content(response, 'text')
      message("Error response content:", response_content)
    }
  }, error = function(e) {
    # Any other exceptions that might occur during the API request
    message("An error occurred during the API request. Error message:", e$message)
  })
}

createPool("DPHD_user_auth_token")
curl -X POST 'https://api.vectorsurv.org/v1/arthropod/pool' \
--header 'Authorization: Token DPHD_user_auth_token' \
--header 'Content-Type: application/json' \
-d '{
  "collection": 66001,
  "comments": "Culex pool from collection 123",
  "num_count": 5,
  "pool_num": 123001,
  "primary_source_bit": true,
  "species": 70,
  "sex": 4
}'

Interpreting the Response

On success, the response body details the pool record that was created, including the id, which will be useful for associating test results with this pool.

responseBody.json
{
  "collection": 66001,
  "comments": "Culex pool from collection 123",
  "num_count": 5,
  "pool_num": 222,
  "primary_source_bit": true,
  "species": 70,
  "sex": 4,
  "agency": 123,
  "site": null,
  "surv_year": 2023,
  "user": 11,
  "location": {
    "shape": {
      "crs": {
        "type": "name",
        "properties": {
          "name": "EPSG:4326"
        }
      },
      "type": "Point",
      "coordinates": [-116, 36]
    },
    "coordinate_precision": 2
  },
  "id": 66602,
  "primary_source": 66602,
  "add_date": "2023-06-07T19:07:18.145Z"
}

Adding test records for those pools

Finally, DPHD received negative test results back from the lab. View the documentation for creating a new test record.

Test Record Data

Pool 123001 was RTPCR tested for WNV by a third party lab on May 08, 2023. The results were a conclusive "Negative".

Using this information and the documentation, an API user can construct an object that describes the pool test. An annotated version of that object might look like this:

newTest.js
const testResult = {
  pool: 66602, // ID corresponding to the Pool 123001, created in the last step
  method: 5, // ID corresponding to RTPCR testing
  target: 6, // ID corresponding to WNV
  status: 1, // ID corresponding to 'Negative'
  test_date: "2023-05-08",
  user: 11, // ID corresponding to the API user
  test_agency: 123, // Because the lab is outside the VectorSurv network, ID corresponding to DCPH is used
};

Making the Request

That same object could be used to define the request body in a POST request to create the test record:

createTest.js
async function createTestResult() {
  const config = {
    headers: {
      Authorization: "Token DPHD_user_auth_token",
      "Content-Type": "application/json",
    },
  };

  const url = "https://api.vectorsurv.org/v1/arthropod/pool/test";
  const params = {
    pool: 66602,
    method: 5,
    target: 6,
    status: 1,
    test_date: "2023-05-08",
    user: 11,
    test_agency: 123,
  };

  try {
    const response = await axios.post(url, params, config);
    console.log("Response:", response.data);
  } catch (error) {
    console.error("Error:", error.response.data);
  }
}
library(httr)
library(jsonlite)

createTestResult <- function(token) {
  headers = c(
    Authorization = paste("Token", token),
    "Content-Type" = "application/json"
  )

  url <- "https://api.vectorsurv.org/v1/arthropod/pool/test"

  body <- list(
    pool = 66602,
    method = 5,
    target = 6,
    status = 1,
    test_date = "2023-05-08",
    user = 11,
    test_agency = 123
  )

  tryCatch({
    # Make the API request
    response <- POST(url, body = jsonlite::toJSON(body, auto_unbox = TRUE), add_headers(headers))

    # Check the response status code and category
    status_code <- http_status(response)$status_code
    status_category <- http_status(response)$category

    if (status_category == "Success") {
      # Response status is successful (e.g., status code 200)
      content <- httr::content(response, "text")
      # Use response data frame if needed
      content_df = fromJSON(content)
      message("Post request was successful")
    } else {
      # Response status is not successful (e.g., status code 422)
      response_content <- httr::content(response, 'text')
      message("Error response content:", response_content)
    }
  }, error = function(e) {
    # Any other exceptions that might occur during the API request
    message("An error occurred during the API request. Error message:", e$message)
  })
}

createTestResult("DPHD_user_auth_token")
curl -X POST 'https://api.vectorsurv.org/v1/arthropod/pool/test' \
-H 'Authorization: Token DPHD_user_auth_token' \
-H 'Content-Type: application/json' \
-d '{
  "pool": 66602,
  "method": 5,
  "target": 6,
  "status": 1,
  "test_date": "2023-05-08",
  "user": 11,
  "test_agency": 123
}'

Interpreting the Response

As this is the last step in the process, DPHD would have little use for the response body. For completeness, it is included anyway:

responseBody.json
{
  "add_date": "2023-06-01T00:00:00.000Z",
  "id": 66602,
  "pool": 66602,
  "method": 5,
  "target": 6,
  "status": 1,
  "test_date": "2023-05-08T00:00:00.000Z",
  "test_agency": 10,
  "user": 11,
  "agency": 10,
  "deactive_date": null,
  "comments": null,
  "value": null
}